/*************************************************************************** * Copyright (c) Victor Titov (DeepSOIC) * * (vv.titov@gmail.com) 2015 * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include #endif #include #include #include "Attacher.h" #include #include using namespace Part; using namespace Attacher; //These strings are for mode list enum property. const char* AttachEngine::eMapModeStrings[]= { "Deactivated", "Translate", "ObjectXY", "ObjectXZ", "ObjectYZ", "FlatFace", "TangentPlane", "NormalToEdge", "FrenetNB", "FrenetTN", "FrenetTB", "Concentric", "SectionOfRevolution", "ThreePointsPlane", "ThreePointsNormal", "Folding", "ObjectX", "ObjectY", "ObjectZ", "AxisOfCurvature", "Directrix1", "Directrix2", "Asymptote1", "Asymptote2", "Tangent", "Normal", "Binormal", "TangentU", "TangentV", "TwoPointLine", "IntersectionLine", "ProximityLine", "ObjectOrigin", "Focus1", "Focus2", "OnEdge", "CenterOfCurvature", "CenterOfMass", "IntersectionPoint", "Vertex", "ProximityPoint1", "ProximityPoint2", NULL}; TYPESYSTEM_SOURCE_ABSTRACT(Attacher::AttachEngine, Base::BaseClass); AttachEngine::AttachEngine() { } void AttachEngine::setUp(const App::PropertyLinkSubList &references, eMapMode mapMode, bool mapReverse, double attachParameter, double surfU, double surfV, const Base::Placement &superPlacement) { this->references.Paste(references); this->mapMode = mapMode; this->mapReverse = mapReverse; this->attachParameter = attachParameter; this->surfU = surfU; this->surfV = surfV; this->superPlacement = superPlacement; } void AttachEngine::setUp(const AttachEngine &another) { setUp(another.references, another.mapMode, another.mapReverse, another.attachParameter, another.surfU, another.surfV, another.superPlacement); } Base::Placement AttachEngine::placementFactory(const gp_Dir &ZAxis, gp_Vec XAxis, gp_Pnt Origin, gp_Pnt refOrg, bool useRefOrg_Line, bool useRefOrg_Plane, bool makeYVertical, bool makeLegacyFlatFaceOrientation, Base::Placement* placeOfRef) const { if(useRefOrg_Line){ //move Origin to projection of refOrg onto ZAxis gp_Vec refOrgV = gp_Vec(refOrg.XYZ()); gp_Vec OriginV = gp_Vec(Origin.XYZ()); gp_Vec ZAxisV = gp_Vec(ZAxis); Origin = gp_Pnt(( OriginV + ZAxisV*ZAxisV.Dot(refOrgV-OriginV) ).XYZ()); } if(useRefOrg_Plane){ //move Origin to projection of refOrg onto plane (ZAxis, Origin) gp_Vec refOrgV = gp_Vec(refOrg.XYZ()); gp_Vec OriginV = gp_Vec(Origin.XYZ()); gp_Vec ZAxisV = gp_Vec(ZAxis); Origin = gp_Pnt(( refOrgV + ZAxisV*ZAxisV.Dot(OriginV-refOrgV) ).XYZ()); } if (XAxis.Magnitude() < Precision::Confusion()) makeYVertical = true; gp_Ax3 ax3;//OCC representation of the final placement if (!makeYVertical) { ax3 = gp_Ax3(Origin, ZAxis, XAxis); } else if (makeYVertical && !makeLegacyFlatFaceOrientation) { //align Y along Z, if possible gp_Vec YAxis(0.0,0.0,1.0); XAxis = YAxis.Crossed(gp_Vec(ZAxis)); if (XAxis.Magnitude() < Precision::Confusion()){ //ZAxis is along true ZAxis XAxis = (gp_Vec(1,0,0)*ZAxis.Z()).Normalized(); } ax3 = gp_Ax3(Origin, ZAxis, XAxis); } else if (makeLegacyFlatFaceOrientation) { //find out, to which axis of support Normal is closest to. //The result will be written into pos variable (0..2 = X..Z) if (!placeOfRef) throw Base::Exception("AttachEngine::placementFactory: for Legacy mode, placement of the reference must be supplied. Got null instead!"); Base::Placement &Place = *placeOfRef; Base::Vector3d dX,dY,dZ;//internal axes of support object, as they are in global space Place.getRotation().multVec(Base::Vector3d(1,0,0),dX); Place.getRotation().multVec(Base::Vector3d(0,1,0),dY); Place.getRotation().multVec(Base::Vector3d(0,0,1),dZ); gp_Dir dirX(dX.x, dX.y, dX.z); gp_Dir dirY(dY.x, dY.y, dY.z); gp_Dir dirZ(dZ.x, dZ.y, dZ.z); double cosNX = ZAxis.Dot(dirX); double cosNY = ZAxis.Dot(dirY); double cosNZ = ZAxis.Dot(dirZ); std::vector cosXYZ; cosXYZ.push_back(fabs(cosNX)); cosXYZ.push_back(fabs(cosNY)); cosXYZ.push_back(fabs(cosNZ)); int pos = std::max_element(cosXYZ.begin(), cosXYZ.end()) - cosXYZ.begin(); // +X/-X if (pos == 0) { if (cosNX > 0) ax3 = gp_Ax3(Origin, ZAxis, dirY); else ax3 = gp_Ax3(Origin, ZAxis, -dirY); } // +Y/-Y else if (pos == 1) { if (cosNY > 0) ax3 = gp_Ax3(Origin, ZAxis, -dirX); else ax3 = gp_Ax3(Origin, ZAxis, dirX); } // +Z/-Z else { ax3 = gp_Ax3(Origin, ZAxis, dirX); } } if(this->mapReverse){ ax3.ZReverse(); ax3.XReverse(); } //convert ax3 into Base::Placement gp_Trsf Trf; Trf.SetTransformation(ax3); Trf.Invert(); Trf.SetScaleFactor(Standard_Real(1.0)); Base::Matrix4D mtrx; TopoShape::convertToMatrix(Trf,mtrx); return Base::Placement(mtrx); } eMapMode AttachEngine::listMapModes(eSuggestResult& msg, std::vector* allApplicableModes, std::set* nextRefTypeHint, std::map* reachableModes) const { //replace a pointer with a valid reference, to avoid checks for zero pointer everywhere std::vector buf; if (allApplicableModes == 0) allApplicableModes = &buf; std::vector &mlist = *allApplicableModes; mlist.clear(); mlist.reserve(mmDummy_NumberOfModes); std::set buf2; if (nextRefTypeHint == 0) nextRefTypeHint = &buf2; std::set &hints = *nextRefTypeHint; hints.clear(); std::map buf3; if (reachableModes == 0) reachableModes = &buf3; std::map &mlist_reachable = *reachableModes; mlist_reachable.clear(); std::vector parts; std::vector shapes; std::vector shapeStorage; std::vector typeStr; try{ readLinks(this->references, parts, shapes, shapeStorage, typeStr); } catch (Base::Exception) { msg = srLinkBroken; return mmDeactivated; } //search valid modes. eMapMode bestMatchType = mmDeactivated; int bestMatchScore = -1; msg = srNoModesFit; for (std::size_t iMode = 0; iMode < this->modeRefTypes.size(); ++iMode) { if (! this->modeEnabled[iMode]) continue; const refTypeStringList &listStrings = modeRefTypes[iMode]; for (std::size_t iStr = 0; iStr < listStrings.size(); ++iStr) { int score = 1; //-1 = topo incompatible, 0 = topo compatible, geom incompatible; 1+ = compatible (the higher - the more specific is the mode for the support) const refTypeString &str = listStrings[iStr]; for (std::size_t iChr = 0; iChr < str.size() && iChr < typeStr.size(); ++iChr) { int match = AttachEngine::isShapeOfType(typeStr[iChr], str[iChr]); switch(match){ case -1: score = -1; break; case 0: score = 0; break; case 1: //keep score break; default: //2 and above if (score > 0) score += match; break; } } if (score > 0 && str.size() > typeStr.size()){ //mode does not fit, but adding more references will make this mode fit. hints.insert(str[typeStr.size()]); //build string of references to be added to fit this mode refTypeString extraRefs; extraRefs.resize(str.size() - typeStr.size()); for (int iChr = typeStr.size() ; iChr < str.size() ; iChr++){ extraRefs[iChr - typeStr.size()] = str[iChr]; } //add reachable mode auto it_r = mlist_reachable.find(eMapMode(iMode)); if (it_r == mlist_reachable.end()){ it_r = mlist_reachable.insert(std::pair(eMapMode(iMode),refTypeStringList())).first; } refTypeStringList &list = it_r->second; list.push_back(extraRefs); } //size check is last, because we needed to collect hints if (str.size() != typeStr.size()) score = -1; if (score > -1){//still output a best match, even if it is not completely compatible if (score > bestMatchScore){ bestMatchScore = score; bestMatchType = eMapMode(iMode); msg = score > 0 ? srOK : srIncompatibleGeometry; } } if (score > 0){ if(mlist.size() == 0) mlist.push_back(eMapMode(iMode)); else if (mlist.back() != eMapMode(iMode)) mlist.push_back(eMapMode(iMode)); } } } return bestMatchType; } const std::set AttachEngine::getHint(bool forCurrentModeOnly) const { eSuggestResult msg; std::set ret; this->listMapModes(msg, 0, &ret); return ret; } void AttachEngine::EnableAllSupportedModes() { this->modeEnabled.resize(mmDummy_NumberOfModes,false); assert(modeRefTypes.size() > 0); for (std::size_t i = 0; i < this->modeEnabled.size(); i++) { modeEnabled[i] = modeRefTypes[i].size() > 0; } } eRefType AttachEngine::getShapeType(const TopoDS_Shape& sh) { if(sh.IsNull()) return rtAnything; switch (sh.ShapeType()){ case TopAbs_SHAPE: return rtAnything; //note: there's no rtPart detection here - not enough data! break; case TopAbs_SOLID: return rtSolid; break; case TopAbs_COMPOUND:{ const TopoDS_Compound &cmpd = TopoDS::Compound(sh); TopoDS_Iterator it (cmpd, Standard_False, Standard_False);//don't mess with placements, to hopefully increase speed if (! it.More()) return rtAnything;//empty compound const TopoDS_Shape &sh1 = it.Value(); it.Next(); if (it.More()){ //more than one object, a true compound return rtAnything; } else { //just one object, let's take a look inside return getShapeType(sh1); } }break; case TopAbs_COMPSOLID: case TopAbs_SHELL: return rtAnything; break; case TopAbs_FACE:{ const TopoDS_Face &f = TopoDS::Face(sh); BRepAdaptor_Surface surf(f, /*restriction=*/Standard_False); switch(surf.GetType()) { case GeomAbs_Plane: return rtFlatFace; case GeomAbs_Cylinder: return rtCylindricalFace; case GeomAbs_Cone: return rtConicalFace; case GeomAbs_Sphere: return rtSphericalFace; case GeomAbs_Torus: return rtToroidalFace; case GeomAbs_BezierSurface: break; case GeomAbs_BSplineSurface: break; case GeomAbs_SurfaceOfRevolution: return rtSurfaceRev; case GeomAbs_SurfaceOfExtrusion: break; case GeomAbs_OffsetSurface: break; case GeomAbs_OtherSurface: break; } return rtFace; }break; case TopAbs_EDGE:{ const TopoDS_Edge &e = TopoDS::Edge(sh); BRepAdaptor_Curve crv(e); switch (crv.GetType()){ case GeomAbs_Line: return rtLine; case GeomAbs_Circle: return rtCircle; case GeomAbs_Ellipse: return rtEllipse; case GeomAbs_Hyperbola: return rtHyperbola; case GeomAbs_Parabola: return rtParabola; case GeomAbs_BezierCurve: case GeomAbs_BSplineCurve: case GeomAbs_OtherCurve: return rtCurve; } }break; case TopAbs_WIRE: return rtWire; case TopAbs_VERTEX: return rtVertex; default: throw Base::Exception("AttachEngine::getShapeType: unexpected TopoDS_Shape::ShapeType"); }//switch shapetype return rtAnything;//shouldn't happen, it's here to shut up compiler warning } eRefType AttachEngine::getShapeType(const App::DocumentObject *obj, const std::string &subshape) { App::PropertyLinkSubList tmpLink; //const_cast is worth here, to keep obj argument const. We are not going to write anything to obj through this temporary link. tmpLink.setValue(const_cast(obj), subshape.c_str()); std::vector parts; std::vector shapes; std::vector copiedShapeStorage; std::vector types; readLinks(tmpLink, parts, shapes, copiedShapeStorage, types); assert(types.size() == 1); return types[0]; } eRefType AttachEngine::downgradeType(eRefType type) { //get rid of hasplacement flags, to simplify the rest type = eRefType(type & (rtFlagHasPlacement - 1)); //FIXME: reintroduce the flag when returning a value. switch(type){ case rtVertex: case rtEdge: case rtFace: return rtAnything; case rtAnything: return rtAnything; case rtLine: case rtCurve: return rtEdge; case rtConic: case rtCircle: return rtCurve; case rtEllipse: case rtParabola: case rtHyperbola: return rtConic; case rtFlatFace: case rtSphericalFace: case rtSurfaceRev: return rtFace; case rtCylindricalFace: case rtToroidalFace: case rtConicalFace: return rtSurfaceRev; case rtSolid: case rtWire: return rtPart; case rtPart: return rtAnything; default: throw Base::Exception("AttachEngine::downgradeType: unknown type"); } } int AttachEngine::getTypeRank(eRefType type) { //get rid of hasplacement flags, to simplify the rest type = eRefType(type & (rtFlagHasPlacement - 1)); int rank = 0; while (type != rtAnything) { type = downgradeType(type); rank++; assert(rank<8);//downgrading never yeilds rtAnything, something's wrong with downgrader. } return rank; } int AttachEngine::isShapeOfType(eRefType shapeType, eRefType requirement) { //first up, check for hasplacement flag if (requirement & rtFlagHasPlacement) { if(! (shapeType & rtFlagHasPlacement)) return -1; } //get rid of hasplacement flags, to simplify the rest shapeType = eRefType(shapeType & (rtFlagHasPlacement - 1)); requirement = eRefType(requirement & (rtFlagHasPlacement - 1)); if (requirement == rtAnything) return 1; int reqRank = getTypeRank(requirement); //test for valid match eRefType shDeg = shapeType; while(shDeg != rtAnything){ if (shDeg == requirement) return reqRank; shDeg = downgradeType(shDeg); } //test for slightly invalid match (e.g. requirement==line, shapeType == curve) requirement = downgradeType(requirement); if (requirement != rtAnything) { eRefType shDeg = shapeType; while(shDeg != rtAnything){ if (shDeg == requirement) return 0; shDeg = downgradeType(shDeg); } } //complete mismatch! return -1; } std::string AttachEngine::getModeName(eMapMode mmode) { if(mmode < 0 || mmode >= mmDummy_NumberOfModes) throw Base::Exception("AttachEngine::getModeName: Attachment Mode index is out of range"); return std::string(AttachEngine::eMapModeStrings[mmode]); } GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector &shapes) { //explode compounds TopTools_HSequenceOfShape totalSeq; for (const TopoDS_Shape* pSh: shapes){ ShapeExtend_Explorer xp; totalSeq.Append( xp.SeqFromCompound(*pSh, /*recursive=*/true)); } if (totalSeq.Length() == 0) throw Base::Exception("AttachEngine::getInertialPropsOfShape: no geometry provided"); const TopoDS_Shape &sh0 = totalSeq.Value(1); switch (sh0.ShapeType()){ case TopAbs_VERTEX:{ GProp_PGProps gpr; for (int i = 0 ; i < totalSeq.Length() ; i++){ const TopoDS_Shape &sh = totalSeq.Value(i+1); if (sh.ShapeType() != TopAbs_VERTEX) throw Base::Exception("AttachEngine::getInertialPropsOfShape: provided shapes are incompatible (not only vertices)"); gpr.AddPoint(BRep_Tool::Pnt(TopoDS::Vertex(sh))); } return gpr; } break; case TopAbs_EDGE: case TopAbs_WIRE:{ GProp_GProps gpr_acc; GProp_GProps gpr; for (int i = 0 ; i < totalSeq.Length() ; i++){ const TopoDS_Shape &sh = totalSeq.Value(i+1); if (sh.ShapeType() != TopAbs_EDGE && sh.ShapeType() != TopAbs_WIRE) throw Base::Exception("AttachEngine::getInertialPropsOfShape: provided shapes are incompatible (not only edges/wires)"); if (sh.Infinite()) throw Base::Exception("AttachEngine::getInertialPropsOfShape: infinite shape provided"); BRepGProp::LinearProperties(sh,gpr); gpr_acc.Add(gpr); } return gpr_acc; } break; case TopAbs_FACE: case TopAbs_SHELL:{ GProp_GProps gpr_acc; GProp_GProps gpr; for (int i = 0 ; i < totalSeq.Length() ; i++){ const TopoDS_Shape &sh = totalSeq.Value(i+1); if (sh.ShapeType() != TopAbs_FACE && sh.ShapeType() != TopAbs_SHELL) throw Base::Exception("AttachEngine::getInertialPropsOfShape: provided shapes are incompatible (not only faces/shells)"); if (sh.Infinite()) throw Base::Exception("AttachEngine::getInertialPropsOfShape: infinite shape provided"); BRepGProp::SurfaceProperties(sh,gpr); gpr_acc.Add(gpr); } return gpr_acc; } break; case TopAbs_SOLID: case TopAbs_COMPSOLID:{ GProp_GProps gpr_acc; GProp_GProps gpr; for (int i = 0 ; i < totalSeq.Length() ; i++){ const TopoDS_Shape &sh = totalSeq.Value(i+1); if (sh.ShapeType() != TopAbs_SOLID && sh.ShapeType() != TopAbs_COMPSOLID) throw Base::Exception("AttachEngine::getInertialPropsOfShape: provided shapes are incompatible (not only solids/compsolids)"); if (sh.Infinite()) throw Base::Exception("AttachEngine::getInertialPropsOfShape: infinite shape provided"); BRepGProp::SurfaceProperties(sh,gpr); gpr_acc.Add(gpr); } return gpr_acc; } break; default: throw Base::Exception("AttachEngine::getInertialPropsOfShape: unexpected shape type"); } assert(false);//exec shouldn't ever get here return GProp_GProps(); } /*! * \brief AttachEngine3D::readLinks * \param parts * \param shapes * \param storage is a buffer storing what some of the pointers in shapes point to. It is needed, since * subshapes are copied in the process (but copying a whole shape of an object can potentially be slow). */ void AttachEngine::readLinks(const App::PropertyLinkSubList &references, std::vector &geofs, std::vector &shapes, std::vector &storage, std::vector &types) { const std::vector &objs = references.getValues(); const std::vector &sub = references.getSubValues(); geofs.resize(objs.size()); storage.reserve(objs.size()); shapes.resize(objs.size()); types.resize(objs.size()); for (std::size_t i = 0; i < objs.size(); i++) { if (!objs[i]->getTypeId().isDerivedFrom(App::GeoFeature::getClassTypeId())) { throw Base::Exception("AttachEngine3D: link points to something that is not App::GeoFeature"); } App::GeoFeature* geof = static_cast(objs[i]); geofs[i] = geof; const Part::TopoShape* shape; if (geof->isDerivedFrom(Part::Feature::getClassTypeId())){ shape = &(static_cast(geof)->Shape.getShape()); if (shape->isNull()){ throw Base::Exception("AttachEngine3D: Part has null shape"); } if (sub[i].length()>0){ try{ storage.push_back(shape->getSubShape(sub[i].c_str())); } catch (Standard_Failure){ throw Base::Exception("AttachEngine3D: subshape not found"); } if(storage[storage.size()-1].IsNull()) throw Base::Exception("AttachEngine3D: null subshape"); shapes[i] = &(storage[storage.size()-1]); } else { shapes[i] = &(shape->_Shape); } } else if ( geof->isDerivedFrom(App::Plane::getClassTypeId()) ){ //obtain Z axis and origin of placement Base::Vector3d norm; geof->Placement.getValue().getRotation().multVec(Base::Vector3d(0.0,0.0,1.0),norm); Base::Vector3d org; geof->Placement.getValue().multVec(Base::Vector3d(),org); //make shape - an local-XY plane infinite face gp_Pln pl = gp_Pln(gp_Pnt(org.x, org.y, org.z), gp_Dir(norm.x, norm.y, norm.z)); BRepBuilderAPI_MakeFace builder(pl); storage.push_back( builder.Shape() ); shapes[i] = &(storage[storage.size()-1]); } else if ( geof->isDerivedFrom(App::Line::getClassTypeId()) ){ //obtain X axis and origin of placement //note an inconsistency: App::Line is along local X, PartDesign::DatumLine is along local Z. Base::Vector3d dir; geof->Placement.getValue().getRotation().multVec(Base::Vector3d(1.0,0.0,0.0),dir); Base::Vector3d org; geof->Placement.getValue().multVec(Base::Vector3d(),org); //make shape - an infinite line along local X axis gp_Lin l = gp_Lin(gp_Pnt(org.x, org.y, org.z), gp_Dir(dir.x, dir.y, dir.z)); BRepBuilderAPI_MakeEdge builder(l); storage.push_back( builder.Shape() ); shapes[i] = &(storage[storage.size()-1]); } else { Base::Console().Warning("Attacher: linked object %s is unexpected, assuming it has no shape.\n",geof->getNameInDocument()); storage.push_back(TopoDS_Shape()); shapes[i] = &(storage[storage.size()-1]); } //FIXME: unpack single-child compounds here? Compounds are not used so far, so it should be considered later, when the need arises. types[i] = getShapeType(*(shapes[i])); if (sub[i].length() == 0) types[i] = eRefType(types[i] | rtFlagHasPlacement); } } void AttachEngine::throwWrongMode(eMapMode mmode) { std::stringstream errmsg; if (mmode >= 0 && mmodeEnableAllSupportedModes(); } AttachEngine3D* AttachEngine3D::copy() const { AttachEngine3D* p = new AttachEngine3D; p->setUp(*this); return p; } Base::Placement AttachEngine3D::calculateAttachedPlacement(Base::Placement origPlacement) const { const eMapMode mmode = this->mapMode; if (mmode == mmDeactivated) throw ExceptionCancel();//to be handled in positionBySupport, to not do anything if disabled std::vector parts; std::vector shapes; std::vector copiedShapeStorage; std::vector types; readLinks(this->references, parts, shapes, copiedShapeStorage, types); if (parts.size() == 0) throw ExceptionCancel(); //common stuff for all map modes gp_Pnt refOrg (0.0,0.0,0.0);//origin of linked object Base::Placement Place = parts[0]->Placement.getValue(); refOrg = gp_Pnt(Place.getPosition().x, Place.getPosition().y, Place.getPosition().z); //variables to derive the actual placement. //They are to be set, depending on the mode: //to the sketch gp_Dir SketchNormal;//points at the user gp_Vec SketchXAxis; //if left zero, a guess will be made gp_Pnt SketchBasePoint; //where to put the origin of the sketch switch (mmode) { case mmDeactivated: //should have been filtered out already! break; case mmTranslate:{ if (shapes.size() < 1) throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: no subobjects specified (need one vertex)."); const TopoDS_Shape &sh = *shapes[0]; if (sh.IsNull()) throw Base::Exception("Null face in AttachEngine3D::calculateAttachedPlacement()!"); if (sh.ShapeType() != TopAbs_VERTEX) throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: no subobjects specified (need one vertex)."); gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(sh)); Base::Placement plm = Base::Placement(); plm.setPosition(Base::Vector3d(p.X(), p.Y(), p.Z())); plm.setPosition(plm.getPosition() + this->superPlacement.getPosition()); plm.setRotation(origPlacement.getRotation()); return plm; } break; case mmObjectXY: case mmObjectXZ: case mmObjectYZ:{ //DeepSOIC: could have been done much more efficiently, but I'm lazy... gp_Dir dirX, dirY, dirZ; if (types[0] & rtFlagHasPlacement) { Base::Vector3d dX,dY,dZ;//internal axes of support object, as they are in global space Place.getRotation().multVec(Base::Vector3d(1,0,0),dX); Place.getRotation().multVec(Base::Vector3d(0,1,0),dY); Place.getRotation().multVec(Base::Vector3d(0,0,1),dZ); dirX = gp_Dir(dX.x, dX.y, dX.z); dirY = gp_Dir(dY.x, dY.y, dY.z); dirZ = gp_Dir(dZ.x, dZ.y, dZ.z); SketchBasePoint = gp_Pnt(Place.getPosition().x,Place.getPosition().y,Place.getPosition().z); } else if (isShapeOfType(types[0],rtConic) > 0) { const TopoDS_Edge &e = TopoDS::Edge(*shapes[0]); BRepAdaptor_Curve adapt(e); gp_Ax3 pos; switch(adapt.GetType()){ case GeomAbs_Ellipse:{ gp_Elips cc = adapt.Ellipse(); pos = gp_Ax3(cc.Position()); }break; case GeomAbs_Hyperbola:{ gp_Hypr cc = adapt.Hyperbola(); pos = gp_Ax3(cc.Position()); }break; case GeomAbs_Parabola:{ gp_Parab cc = adapt.Parabola(); pos = gp_Ax3(cc.Position()); }break; default: assert(0);//conics should have been filtered out by testing shape type in the above if. } dirX = pos.XDirection(); dirY = pos.YDirection(); dirZ = pos.Axis().Direction(); SketchBasePoint = pos.Location(); } else { throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: need either a conic section edge, or a whole object for ObjectXY-like modes."); } switch (mmode){ case mmObjectXY: SketchNormal = dirZ; SketchXAxis = gp_Vec(dirX); break; case mmObjectXZ: SketchNormal = dirY.Reversed(); SketchXAxis = gp_Vec(dirX); break; case mmObjectYZ: SketchNormal = dirX; SketchXAxis = gp_Vec(dirY); break; default: break; } } break; case mmFlatFace:{ if (shapes.size() < 1) throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: no subobjects specified (needed one planar face)."); const TopoDS_Face &face = TopoDS::Face(*(shapes[0])); if (face.IsNull()) throw Base::Exception("Null face in AttachEngine3D::calculateAttachedPlacement()!"); BRepAdaptor_Surface adapt(face); if (adapt.GetType() != GeomAbs_Plane) throw Base::Exception("No planar face in AttachEngine3D::calculateAttachedPlacement()!"); bool Reverse = false; if (face.Orientation() == TopAbs_REVERSED) Reverse = true; gp_Pln plane = adapt.Plane(); Standard_Boolean ok = plane.Direct(); if (!ok) { // toggle if plane has a left-handed coordinate system plane.UReverse(); Reverse = !Reverse; } gp_Ax1 Normal = plane.Axis(); if (Reverse) Normal.Reverse(); SketchNormal = Normal.Direction(); Handle (Geom_Plane) gPlane = new Geom_Plane(plane); GeomAPI_ProjectPointOnSurf projector(refOrg,gPlane); SketchBasePoint = projector.NearestPoint(); } break; case mmTangentPlane: { if (shapes.size() < 2) throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: not enough subshapes (need one false and one vertex)."); bool bThruVertex = false; if (shapes[0]->ShapeType() == TopAbs_VERTEX && shapes.size()>=2) { std::swap(shapes[0],shapes[1]); bThruVertex = true; } const TopoDS_Face &face = TopoDS::Face(*(shapes[0])); if (face.IsNull()) throw Base::Exception("Null face in AttachEngine3D::calculateAttachedPlacement()!"); const TopoDS_Vertex &vertex = TopoDS::Vertex(*(shapes[1])); if (vertex.IsNull()) throw Base::Exception("Null vertex in AttachEngine3D::calculateAttachedPlacement()!"); BRepAdaptor_Surface surf (face); Handle (Geom_Surface) hSurf = BRep_Tool::Surface(face); gp_Pnt p = BRep_Tool::Pnt(vertex); GeomAPI_ProjectPointOnSurf projector(p, hSurf); double u, v; if (projector.NbPoints()==0) throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: projecting point onto surface failed."); projector.LowerDistanceParameters(u, v); BRepLProp_SLProps prop(surf,u,v,1, Precision::Confusion()); SketchNormal = prop.Normal(); gp_Dir dirX; prop.TangentU(dirX); //if normal is defined, this should be defined too SketchXAxis = gp_Vec(dirX).Reversed();//yeilds upside-down sketches less often. if (face.Orientation() == TopAbs_REVERSED) { SketchNormal.Reverse(); SketchXAxis.Reverse(); } if (bThruVertex) { SketchBasePoint = p; } else { SketchBasePoint = projector.NearestPoint(); } } break; case mmNormalToPath: case mmFrenetNB: case mmFrenetTN: case mmFrenetTB: case mmRevolutionSection: case mmConcentric: {//all alignments to poing on curve if (shapes.size() < 1) throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: no subshapes specified (need one edge, and an optional vertex)."); bool bThruVertex = false; if (shapes[0]->ShapeType() == TopAbs_VERTEX && shapes.size()>=2) { std::swap(shapes[0],shapes[1]); bThruVertex = true; } const TopoDS_Edge &path = TopoDS::Edge(*(shapes[0])); if (path.IsNull()) throw Base::Exception("Null path in AttachEngine3D::calculateAttachedPlacement()!"); BRepAdaptor_Curve adapt(path); double u = 0.0; double u1 = adapt.FirstParameter(); double u2 = adapt.LastParameter(); if(Precision::IsInfinite(u1) || Precision::IsInfinite(u2)){ //prevent attachment to infinities in case of infinite shape. //example of an infinite shape is a datum line. u1 = 0.0; u2 = 1.0; } //if a point is specified, use the point as a point of mapping, otherwise use parameter value from properties gp_Pnt p_in; if (shapes.size() >= 2) { TopoDS_Vertex vertex = TopoDS::Vertex(*(shapes[1])); if (vertex.IsNull()) throw Base::Exception("Null vertex in AttachEngine3D::calculateAttachedPlacement()!"); p_in = BRep_Tool::Pnt(vertex); Handle (Geom_Curve) hCurve = BRep_Tool::Curve(path, u1, u2); GeomAPI_ProjectPointOnCurve projector (p_in, hCurve); u = projector.LowerDistanceParameter(); } else { u = u1 + this->attachParameter * (u2 - u1); } gp_Pnt p; gp_Vec d; //point and derivative adapt.D1(u,p,d); if (d.Magnitude() Precision::SquareConfusion()) { N.Normalize(); B = T.Crossed(N); } else { Base::Console().Warning("AttachEngine3D::calculateAttachedPlacement: path curve second derivative is below 1e-14, can't align x axis.\n"); N = gp_Vec(0.,0.,0.); B = gp_Vec(0.,0.,0.);//redundant, just for consistency } //Set origin. Note that it will be overridden later for mmConcentric and mmRevolutionSection if (bThruVertex) { SketchBasePoint = p_in; } else { SketchBasePoint = p; } switch (mmode){ case mmFrenetNB: case mmRevolutionSection: SketchNormal = T.Reversed();//to avoid sketches upside-down for regular curves like circles SketchXAxis = N.Reversed(); break; case mmFrenetTN: case mmConcentric: if (N.Magnitude() == 0.0) throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: Frenet-Serret normal is undefined. Can't align to TN plane."); SketchNormal = B; SketchXAxis = T; break; case mmFrenetTB: if (N.Magnitude() == 0.0) throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: Frenet-Serret normal is undefined. Can't align to TB plane."); SketchNormal = N.Reversed();//it is more convenient to sketch on something looking it it so it is convex. SketchXAxis = T; break; default: assert(0);//mode forgotten? } if (mmode == mmRevolutionSection || mmode == mmConcentric) { //make sketch origin be at center of osculating circle if (N.Magnitude() == 0.0) throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: path has infinite radius of curvature at the point. Can't align for revolving."); double curvature = dd.Dot(N) / pow(d.Magnitude(), 2); gp_Vec pv (p.XYZ()); pv.Add(N.Multiplied(1/curvature));//shift the point along curvature by radius of curvature SketchBasePoint = gp_Pnt(pv.XYZ()); //it would have been cool to have the curve attachment point available inside sketch... Leave for future. } } else if (mmode == mmNormalToPath){//mmNormalToPath //align sketch origin to the origin of support SketchNormal = gp_Dir(d.Reversed());//sketch normal looks at user. It is natural to have the curve directed away from user, so reversed. SketchBasePoint = p; } } break; case mmThreePointsPlane: case mmThreePointsNormal: { std::vector points; for (std::size_t i = 0; i < shapes.size(); i++) { const TopoDS_Shape &sh = *shapes[i]; if (sh.IsNull()) throw Base::Exception("Null shape in AttachEngine3D::calculateAttachedPlacement()!"); if (sh.ShapeType() == TopAbs_VERTEX){ const TopoDS_Vertex &v = TopoDS::Vertex(sh); points.push_back(BRep_Tool::Pnt(v)); } else if (sh.ShapeType() == TopAbs_EDGE) { const TopoDS_Edge &e = TopoDS::Edge(sh); BRepAdaptor_Curve crv(e); double u1 = crv.FirstParameter(); double u2 = crv.LastParameter(); if ( Precision::IsInfinite(u1) || Precision::IsInfinite(u2) ){ u1 = 0.0; u2 = 1.0; } points.push_back(crv.Value(u1)); points.push_back(crv.Value(u2)); } if (points.size() >= 3) break; } if(points.size()<3) throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: less than 3 points are specified, cannot derive the plane."); gp_Pnt p0 = points[0]; gp_Pnt p1 = points[1]; gp_Pnt p2 = points[2]; gp_Vec vec01 (p0,p1); gp_Vec vec02 (p0,p2); if (vec01.Magnitude() < Precision::Confusion() || vec02.Magnitude() < Precision::Confusion()) throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: some of 3 points are coincident. Can't make a plane"); vec01.Normalize(); vec02.Normalize(); gp_Vec norm ; if (mmode == mmThreePointsPlane) { norm = vec01.Crossed(vec02); if (norm.Magnitude() < Precision::Confusion()) throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: points are collinear. Can't make a plane"); //SketchBasePoint = (p0+p1+p2)/3.0 SketchBasePoint = gp_Pnt(gp_Vec(p0.XYZ()).Added(p1.XYZ()).Added(p2.XYZ()).Multiplied(1.0/3.0).XYZ()); } else if (mmode == mmThreePointsNormal) { norm = vec02.Subtracted(vec01.Multiplied(vec02.Dot(vec01))).Reversed();//norm = vec02 forced perpendicular to vec01. if (norm.Magnitude() < Precision::Confusion()) throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: points are collinear. Can't make a plane"); //SketchBasePoint = (p0+p1)/2.0 Handle (Geom_Plane) gPlane = new Geom_Plane(p0, gp_Dir(norm)); GeomAPI_ProjectPointOnSurf projector(p2,gPlane); SketchBasePoint = projector.NearestPoint(); } norm.Normalize(); SketchNormal = gp_Dir(norm); } break; case mmFolding: { // Expected selection: four edges in order: edgeA, fold axis A, // fold axis B, edgeB. The sketch will be placed angled so as to join // edgeA to edgeB by folding the sheet along axes. All edges are // expected to be in one plane. if (shapes.size()<4) throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: not enough shapes (need 4 lines: edgeA, axisA, axisB, edgeB)."); //extract the four lines const TopoDS_Edge* (edges[4]); BRepAdaptor_Curve adapts[4]; gp_Lin lines[4]; for(int i=0 ; i<4 ; i++){ edges[i] = &TopoDS::Edge(*(shapes[i])); if (edges[i]->IsNull()) throw Base::Exception("Null edge in AttachEngine3D::calculateAttachedPlacement()!"); adapts[i] = BRepAdaptor_Curve(*(edges[i])); if (adapts[i].GetType() != GeomAbs_Line) throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: Folding - non-straight edge."); lines[i] = adapts[i].Line(); } //figure out the common starting point (variable p) gp_Pnt p, p1, p2, p3, p4; double signs[4] = {0,0,0,0};//flags whether to reverse line directions, for all directions to point away from the common vertex p1 = adapts[0].Value(adapts[0].FirstParameter()); p2 = adapts[0].Value(adapts[0].LastParameter()); p3 = adapts[1].Value(adapts[1].FirstParameter()); p4 = adapts[1].Value(adapts[1].LastParameter()); p = p1; if (p1.Distance(p3) < Precision::Confusion()){ p = p3; signs[0] = +1.0; signs[1] = +1.0; } else if (p1.Distance(p4) < Precision::Confusion()){ p = p4; signs[0] = +1.0; signs[1] = -1.0; } else if (p2.Distance(p3) < Precision::Confusion()){ p = p3; signs[0] = -1.0; signs[1] = +1.0; } else if (p2.Distance(p4) < Precision::Confusion()){ p = p4; signs[0] = -1.0; signs[1] = -1.0; } else { throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: Folding - edges to not share a vertex."); } for (int i = 2 ; i<4 ; i++){ p1 = adapts[i].Value(adapts[i].FirstParameter()); p2 = adapts[i].Value(adapts[i].LastParameter()); if (p.Distance(p1) < Precision::Confusion()) signs[i] = +1.0; else if (p.Distance(p2) < Precision::Confusion()) signs[i] = -1.0; else throw Base::Exception("AttachEngine3D::calculateAttachedPlacement: Folding - edges to not share a vertex."); } gp_Vec dirs[4]; for(int i=0 ; i<4 ; i++){ assert(fabs(signs[i]) == 1.0); dirs[i] = gp_Vec(lines[i].Direction()).Multiplied(signs[i]); } double ang = this->calculateFoldAngle( dirs[1], dirs[2], dirs[0], dirs[3] ); gp_Vec norm = dirs[1].Crossed(dirs[2]); //rotation direction: when angle is positive, rotation is CCW when observing the vector so //that the axis is pointing at you. Hence angle is negated here. norm.Rotate(gp_Ax1(gp_Pnt(),gp_Dir(dirs[1])),-ang); SketchNormal = norm.Reversed(); SketchXAxis = dirs[1]; SketchBasePoint = p; } break; default: throwWrongMode(mmode); }//switch (MapMode) //----------calculate placement, based on point and vector Base::Placement plm = this->placementFactory(SketchNormal, SketchXAxis, SketchBasePoint, gp_Pnt(), /*useRefOrg_Line = */ false, /*useRefOrg_Plane = */ false, /*makeYVertical = */ false, /*makeLegacyFlatFaceOrientation = */ mmode == mmFlatFace, &Place); plm *= this->superPlacement; return plm; } double AttachEngine3D::calculateFoldAngle(gp_Vec axA, gp_Vec axB, gp_Vec edA, gp_Vec edB) const { //DeepSOIC: this hardcore math can probably be replaced with a couple of //clever OCC calls... See forum thread "Sketch mapping enhancement" for a //picture on how this math was derived. //http://forum.freecadweb.org/viewtopic.php?f=8&t=10511&sid=007946a934530ff2a6c9259fb32624ec&start=40#p87584 axA.Normalize(); axB.Normalize(); edA.Normalize(); edB.Normalize(); gp_Vec norm = axA.Crossed(axB); if (norm.Magnitude() < Precision::Confusion()) throw Base::Exception("calculateFoldAngle: Folding axes are parallel, folding angle cannot be computed."); norm.Normalize(); double a = edA.Dot(axA); double ra = edA.Crossed(axA).Magnitude(); if (fabs(ra) < Precision::Confusion()) throw Base::Exception("calculateFoldAngle: axisA and edgeA are parallel, folding can't be computed."); double b = edB.Dot(axB); double costheta = axB.Dot(axA); double sintheta = axA.Crossed(axB).Dot(norm); double singama = -costheta; double cosgama = sintheta; double k = b*cosgama; double l = a + b*singama; double xa = k + l*singama/cosgama; double cos_unfold = -xa/ra; if (fabs(cos_unfold)>0.999) throw Base::Exception("calculateFoldAngle: cosine of folding angle is too close to or above 1."); return acos(cos_unfold); } //================================================================================= TYPESYSTEM_SOURCE(Attacher::AttachEnginePlane, Attacher::AttachEngine); AttachEnginePlane::AttachEnginePlane() { //re-used 3d modes: all of Attacher3d AttachEngine3D attacher3D; this->modeRefTypes = attacher3D.modeRefTypes; this->EnableAllSupportedModes(); } AttachEnginePlane *AttachEnginePlane::copy() const { AttachEnginePlane* p = new AttachEnginePlane; p->setUp(*this); return p; } Base::Placement AttachEnginePlane::calculateAttachedPlacement(Base::Placement origPlacement) const { //re-use Attacher3d Base::Placement plm; AttachEngine3D attacher3D; attacher3D.setUp(*this); plm = attacher3D.calculateAttachedPlacement(origPlacement); return plm; } //================================================================================= TYPESYSTEM_SOURCE(Attacher::AttachEngineLine, Attacher::AttachEngine); AttachEngineLine::AttachEngineLine() { //fill type lists for modes modeRefTypes.resize(mmDummy_NumberOfModes); refTypeString s; //re-used 3d modes AttachEngine3D attacher3D; modeRefTypes[mm1AxisX] = attacher3D.modeRefTypes[mmObjectYZ]; modeRefTypes[mm1AxisY] = attacher3D.modeRefTypes[mmObjectXZ]; modeRefTypes[mm1AxisZ] = attacher3D.modeRefTypes[mmObjectXY]; modeRefTypes[mm1AxisCurv] = attacher3D.modeRefTypes[mmRevolutionSection]; modeRefTypes[mm1Binormal] = attacher3D.modeRefTypes[mmFrenetTN]; modeRefTypes[mm1Normal] = attacher3D.modeRefTypes[mmFrenetTB]; modeRefTypes[mm1Tangent] = attacher3D.modeRefTypes[mmNormalToPath]; modeRefTypes[mm1TwoPoints].push_back(cat(rtVertex,rtVertex)); modeRefTypes[mm1TwoPoints].push_back(cat(rtLine)); modeRefTypes[mm1Asymptote1].push_back(cat(rtHyperbola)); modeRefTypes[mm1Asymptote2].push_back(cat(rtHyperbola)); modeRefTypes[mm1Directrix1].push_back(cat(rtConic)); modeRefTypes[mm1Directrix2].push_back(cat(rtEllipse)); modeRefTypes[mm1Directrix2].push_back(cat(rtHyperbola)); modeRefTypes[mm1Proximity].push_back(cat(rtAnything, rtAnything)); this->EnableAllSupportedModes(); } AttachEngineLine *AttachEngineLine::copy() const { AttachEngineLine* p = new AttachEngineLine; p->setUp(*this); return p; } Base::Placement AttachEngineLine::calculateAttachedPlacement(Base::Placement origPlacement) const { eMapMode mmode = this->mapMode; if (mmode == mmDeactivated) throw ExceptionCancel();//to be handled in positionBySupport, to not do anything if disabled //modes that are mirrors of attacher3D: bool bReUsed = true; Base::Placement presuperPlacement; switch(mmode){ case mm1AxisX: mmode = mmObjectYZ; break; case mm1AxisY: mmode = mmObjectXZ; break; case mm1AxisZ: mmode = mmObjectXY; break; case mm1AxisCurv: mmode = mmRevolutionSection; //the line should go along Y, not Z presuperPlacement.setRotation( Base::Rotation( Base::Vector3d(0.0,0.0,1.0), Base::Vector3d(0.0,1.0,0.0) ) ); break; case mm1Binormal: mmode = mmFrenetTN; break; case mm1Normal: mmode = mmFrenetTB; break; case mm1Tangent: mmode = mmNormalToPath; break; default: bReUsed = false; } Base::Placement plm; if (!bReUsed){ std::vector parts; std::vector shapes; std::vector copiedShapeStorage; std::vector types; readLinks(this->references, parts, shapes, copiedShapeStorage, types); if (parts.size() == 0) throw ExceptionCancel(); //common stuff for all map modes gp_Pnt refOrg (0.0,0.0,0.0); Base::Placement Place = parts[0]->Placement.getValue(); refOrg = gp_Pnt(Place.getPosition().x, Place.getPosition().y, Place.getPosition().z); //variables to derive the actual placement. //They are to be set, depending on the mode: gp_Dir LineDir; gp_Pnt LineBasePoint; //the point the line goes through switch (mmode) { case mmDeactivated: //should have been filtered out already! break; case mm1TwoPoints:{ std::vector points; for (std::size_t i = 0; i < shapes.size(); i++) { const TopoDS_Shape &sh = *shapes[i]; if (sh.IsNull()) throw Base::Exception("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); if (sh.ShapeType() == TopAbs_VERTEX){ const TopoDS_Vertex &v = TopoDS::Vertex(sh); points.push_back(BRep_Tool::Pnt(v)); } else if (sh.ShapeType() == TopAbs_EDGE) { const TopoDS_Edge &e = TopoDS::Edge(sh); BRepAdaptor_Curve crv(e); double u1 = crv.FirstParameter(); double u2 = crv.LastParameter(); if ( Precision::IsInfinite(u1) || Precision::IsInfinite(u2) ){ u1 = 0.0; u2 = 1.0; } points.push_back(crv.Value(u1)); points.push_back(crv.Value(u2)); } if (points.size() >= 2) break; } if(points.size()<2) throw Base::Exception("AttachEngineLine::calculateAttachedPlacement: less than 2 points are specified, cannot derive the line."); gp_Pnt p0 = points[0]; gp_Pnt p1 = points[1]; LineDir = gp_Dir(gp_Vec(p0,p1)); LineBasePoint = p0; }break; case mm1Asymptote1: case mm1Asymptote2:{ if (shapes[0]->IsNull()) throw Base::Exception("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); const TopoDS_Edge &e = TopoDS::Edge(*(shapes[0])); BRepAdaptor_Curve adapt (e); if (adapt.GetType() != GeomAbs_Hyperbola) throw Base::Exception("AttachEngineLine::calculateAttachedPlacement: Asymptotes are available only for hyperbola-shaped edges, the one supplied is not."); gp_Hypr hyp = adapt.Hyperbola(); if (mmode == mm1Asymptote1) LineDir = hyp.Asymptote1().Direction(); else LineDir = hyp.Asymptote2().Direction(); LineBasePoint = hyp.Location(); }break; case mm1Directrix1: case mm1Directrix2:{ if (shapes[0]->IsNull()) throw Base::Exception("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); const TopoDS_Edge &e = TopoDS::Edge(*(shapes[0])); BRepAdaptor_Curve adapt (e); gp_Ax1 dx1, dx2;//vars to recieve directrices switch(adapt.GetType()){ case GeomAbs_Ellipse:{ gp_Elips cc = adapt.Ellipse(); dx1 = cc.Directrix1(); dx2 = cc.Directrix2(); }break; case GeomAbs_Hyperbola:{ gp_Hypr cc = adapt.Hyperbola(); dx1 = cc.Directrix1(); dx2 = cc.Directrix2(); }break; case GeomAbs_Parabola:{ gp_Parab cc = adapt.Parabola(); dx1 = cc.Directrix(); if (mmode == mm1Directrix2) throw Base::Exception("AttachEngineLine::calculateAttachedPlacement: Parabola has no second directrix"); }break; default: throw Base::Exception("AttachEngineLine::calculateAttachedPlacement: referenced edge is not a conic section with a directrix"); } if (mmode == mm1Directrix1){ LineDir = dx1.Direction(); LineBasePoint = dx1.Location(); } else { LineDir = dx2.Direction(); LineBasePoint = dx2.Location(); } }break; case mm1Proximity:{ if (shapes.size() < 2) throw Base::Exception("AttachEngineLine::calculateAttachedPlacement: Proximity mode requires two shapes; only one is supplied"); if (shapes[0]->IsNull()) throw Base::Exception("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); if (shapes[1]->IsNull()) throw Base::Exception("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); BRepExtrema_DistShapeShape distancer (*(shapes[0]), *(shapes[1])); if (!distancer.IsDone()) throw Base::Exception("AttachEngineLine::calculateAttachedPlacement: proximity calculation failed."); if (distancer.NbSolution()>1) Base::Console().Warning("AttachEngineLine::calculateAttachedPlacement: proximity calculation gave %i solutions, ambiguous.\n",int(distancer.NbSolution())); gp_Pnt p1 = distancer.PointOnShape1(1); gp_Pnt p2 = distancer.PointOnShape2(1); LineBasePoint = p1; gp_Vec dist = gp_Vec(p1,p2); if (dist.Magnitude() < Precision::Confusion()) throw Base::Exception("AttachEngineLine::calculateAttachedPlacement: can't make proximity line, because shapes touch or intersect"); LineDir = gp_Dir(dist); }break; default: throwWrongMode(mmode); } plm = this->placementFactory(LineDir, gp_Vec(), LineBasePoint, refOrg, /*useRefOrg_Line = */ true); } else {//re-use 3d mode AttachEngine3D attacher3D; attacher3D.setUp(*this); attacher3D.mapMode = mmode; attacher3D.superPlacement = Base::Placement(); //superplacement is applied separately here, afterwards. So we are resetting it in sub-attacher to avoid applying it twice! plm = attacher3D.calculateAttachedPlacement(origPlacement); plm *= presuperPlacement; } plm *= this->superPlacement; return plm; } //================================================================================= TYPESYSTEM_SOURCE(Attacher::AttachEnginePoint, Attacher::AttachEngine); AttachEnginePoint::AttachEnginePoint() { //fill type lists for modes modeRefTypes.resize(mmDummy_NumberOfModes); refTypeString s; //re-used 3d modes AttachEngine3D attacher3D; modeRefTypes[mm0Origin] = attacher3D.modeRefTypes[mmObjectXY]; modeRefTypes[mm0CenterOfCurvature] = attacher3D.modeRefTypes[mmRevolutionSection]; modeRefTypes[mm0OnEdge] = attacher3D.modeRefTypes[mmNormalToPath]; modeRefTypes[mm0Vertex].push_back(cat(rtVertex)); modeRefTypes[mm0Vertex].push_back(cat(rtLine)); modeRefTypes[mm0Focus1].push_back(cat(rtConic)); modeRefTypes[mm0Focus2].push_back(cat(rtEllipse)); modeRefTypes[mm0Focus2].push_back(cat(rtHyperbola)); s = cat(rtAnything, rtAnything); modeRefTypes[mm0ProximityPoint1].push_back(s); modeRefTypes[mm0ProximityPoint2].push_back(s); modeRefTypes[mm0CenterOfMass].push_back(cat(rtAnything)); modeRefTypes[mm0CenterOfMass].push_back(cat(rtAnything,rtAnything)); modeRefTypes[mm0CenterOfMass].push_back(cat(rtAnything,rtAnything,rtAnything)); modeRefTypes[mm0CenterOfMass].push_back(cat(rtAnything,rtAnything,rtAnything,rtAnything)); this->EnableAllSupportedModes(); } AttachEnginePoint *AttachEnginePoint::copy() const { AttachEnginePoint* p = new AttachEnginePoint; p->setUp(*this); return p; } Base::Placement AttachEnginePoint::calculateAttachedPlacement(Base::Placement origPlacement) const { eMapMode mmode = this->mapMode; if (mmode == mmDeactivated) throw ExceptionCancel();//to be handled in positionBySupport, to not do anything if disabled //modes that are mirrors of attacher3D: bool bReUsed = true; switch(mmode){ case mm0Origin: mmode = mmObjectXY; break; case mm0CenterOfCurvature: mmode = mmRevolutionSection; break; case mm0OnEdge: //todo: prevent thruPoint mmode = mmNormalToPath; break; default: bReUsed = false; } Base::Placement plm; if (!bReUsed){ std::vector parts; std::vector shapes; std::vector copiedShapeStorage; std::vector types; readLinks(this->references, parts, shapes, copiedShapeStorage, types); if (parts.size() == 0) throw ExceptionCancel(); //variables to derive the actual placement. //They are to be set, depending on the mode: gp_Pnt BasePoint; //where to put the point switch (mmode) { case mmDeactivated: //should have been filtered out already! break; case mm0Vertex:{ std::vector points; assert(shapes.size()>0); const TopoDS_Shape &sh = *shapes[0]; if (sh.IsNull()) throw Base::Exception("Null shape in AttachEnginePoint::calculateAttachedPlacement()!"); if (sh.ShapeType() == TopAbs_VERTEX){ const TopoDS_Vertex &v = TopoDS::Vertex(sh); BasePoint = BRep_Tool::Pnt(v); } else if (sh.ShapeType() == TopAbs_EDGE) { const TopoDS_Edge &e = TopoDS::Edge(sh); BRepAdaptor_Curve crv(e); BasePoint = crv.Value(crv.FirstParameter()); } }break; case mm0Focus1: case mm0Focus2:{ if (shapes[0]->IsNull()) throw Base::Exception("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); const TopoDS_Edge &e = TopoDS::Edge(*(shapes[0])); BRepAdaptor_Curve adapt (e); gp_Pnt f1, f2; switch(adapt.GetType()){ case GeomAbs_Ellipse:{ gp_Elips cc = adapt.Ellipse(); f1 = cc.Focus1(); f2 = cc.Focus2(); }break; case GeomAbs_Hyperbola:{ gp_Hypr cc = adapt.Hyperbola(); f1 = cc.Focus1(); f2 = cc.Focus2(); }break; case GeomAbs_Parabola:{ gp_Parab cc = adapt.Parabola(); f1 = cc.Focus(); if (mmode == mm0Focus2) throw Base::Exception("AttachEnginePoint::calculateAttachedPlacement: Parabola has no second focus"); }break; default: throw Base::Exception("AttachEngineLine::calculateAttachedPlacement: referenced edge is not a conic section with a directrix"); } if (mmode == mm0Focus1) BasePoint = f1; else BasePoint = f2; }break; case mm0ProximityPoint1: case mm0ProximityPoint2:{ if (shapes.size() < 2) throw Base::Exception("AttachEngineLine::calculateAttachedPlacement: Proximity mode requires two shapes; only one is supplied"); if (shapes[0]->IsNull()) throw Base::Exception("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); if (shapes[1]->IsNull()) throw Base::Exception("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); BRepExtrema_DistShapeShape distancer (*(shapes[0]), *(shapes[1])); if (!distancer.IsDone()) throw Base::Exception("AttachEngineLine::calculateAttachedPlacement: proximity calculation failed."); if (distancer.NbSolution()>1) Base::Console().Warning("AttachEngineLine::calculateAttachedPlacement: proximity calculation gave %i solutions, ambiguous.\n",int(distancer.NbSolution())); gp_Pnt p1 = distancer.PointOnShape1(1); gp_Pnt p2 = distancer.PointOnShape2(1); if (mmode == mm0ProximityPoint1) BasePoint = p1; else BasePoint = p2; }break; case mm0CenterOfMass:{ GProp_GProps gpr = AttachEngine::getInertialPropsOfShape(shapes); BasePoint = gpr.CentreOfMass(); }break; default: throwWrongMode(mmode); } plm = this->placementFactory(gp_Vec(0.0,0.0,1.0), gp_Vec(1.0,0.0,0.0), BasePoint, gp_Pnt()); } else {//re-use 3d mode AttachEngine3D attacher3D; attacher3D.setUp(*this); attacher3D.mapMode = mmode; attacher3D.superPlacement = Base::Placement(); //superplacement is applied separately here, afterwards. So we are resetting it in sub-attacher to avoid applying it twice! plm = attacher3D.calculateAttachedPlacement(origPlacement); } plm *= this->superPlacement; return plm; }