From bb24c1554c34e55744fce4421a83d9cd55dee414 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Mon, 11 Mar 2024 11:13:27 -0400 Subject: [PATCH 1/3] Toponaming/Part: Bring over attacher differences --- src/Mod/Part/App/Attacher.cpp | 1522 +++++++++++++++++--------------- src/Mod/Part/App/Attacher.h | 44 +- src/Mod/Part/App/TopoShape.cpp | 49 +- src/Mod/Part/TestPartApp.py | 4 +- 4 files changed, 882 insertions(+), 737 deletions(-) diff --git a/src/Mod/Part/App/Attacher.cpp b/src/Mod/Part/App/Attacher.cpp index 70e0e19df9..f4a048e697 100644 --- a/src/Mod/Part/App/Attacher.cpp +++ b/src/Mod/Part/App/Attacher.cpp @@ -172,13 +172,69 @@ TYPESYSTEM_SOURCE_ABSTRACT(Attacher::AttachEngine, Base::BaseClass) AttachEngine::AttachEngine() = default; +void AttachEngine::setReferences(const App::PropertyLinkSubList& references) +{ + std::string docname; + std::vector names; + for (auto obj : references.getValues()) { + if (!obj->getNameInDocument()) { + throw AttachEngineException("AttachEngine::invalid object"); + } + if (docname.empty()) { + docname = obj->getDocument()->getName(); + } + else if (docname != obj->getDocument()->getName()) { + throw AttachEngineException("AttachEngine::object from multiple document"); + } + names.emplace_back(obj->getNameInDocument()); + } + this->docName = docname; + this->objNames = std::move(names); + this->subnames.clear(); + this->subnames.reserve(this->objNames.size()); + this->shadowSubs.clear(); + this->shadowSubs.reserve(this->objNames.size()); + for (auto& shadow : references.getShadowSubs()) { + this->shadowSubs.push_back(shadow.first); + this->subnames.push_back(shadow.second); + } + assert(this->objNames.size() == this->subnames.size()); +} + +void AttachEngine::setReferences(const std::vector& references) +{ + std::string docname; + std::vector names; + std::vector subnames; + std::vector shadowSubs; + for (auto& ref : references) { + if (!ref.getSubObject()) { + FC_THROWM(AttachEngineException, + "AttachEngine::invalid object " << ref.getSubObjectFullName()); + } + if (docname.empty()) { + docname = ref.getDocumentName(); + } + else if (docname != ref.getDocumentName()) { + throw AttachEngineException("AttachEngine::object from multiple document"); + } + names.push_back(ref.getObjectName()); + subnames.push_back(ref.getSubNameNoElement() + ref.getOldElementName()); + shadowSubs.push_back(ref.getSubNameNoElement() + ref.getNewElementName()); + } + this->docName = docname; + this->objNames = std::move(names); + this->subnames = std::move(subnames); + this->shadowSubs = std::move(shadowSubs); +} + void AttachEngine::setUp(const App::PropertyLinkSubList &references, eMapMode mapMode, bool mapReverse, double attachParameter, double surfU, double surfV, const Base::Placement &attachmentOffset) { - this->references.Paste(references); + setReferences(references); this->mapMode = mapMode; this->mapReverse = mapReverse; this->attachParameter = attachParameter; @@ -324,7 +380,7 @@ void AttachEngine::suggestMapModes(SuggestResult &result) const std::vector shapeStorage; std::vector typeStr; try{ - readLinks(this->references, parts, shapes, shapeStorage, typeStr); + readLinks(getRefObjects(),subnames, parts, shapes, shapeStorage, typeStr); } catch (Base::Exception &err) { result.references_Types = typeStr; result.message = SuggestResult::srLinkBroken; @@ -341,22 +397,23 @@ void AttachEngine::suggestMapModes(SuggestResult &result) const if (! this->modeEnabled[iMode]) continue; const refTypeStringList &listStrings = modeRefTypes[iMode]; - for (const auto & str : listStrings) { + 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) + 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; } @@ -394,7 +451,7 @@ void AttachEngine::suggestMapModes(SuggestResult &result) const } } if (score > 0){ - if(mlist.empty()) + if(mlist.size() == 0) mlist.push_back(eMapMode(iMode)); else if (mlist.back() != eMapMode(iMode)) mlist.push_back(eMapMode(iMode)); @@ -514,7 +571,7 @@ eRefType AttachEngine::getShapeType(const App::DocumentObject *obj, const std::s std::vector shapes; std::vector copiedShapeStorage; std::vector types; - readLinks(tmpLink, parts, shapes, copiedShapeStorage, types); + readLinks(tmpLink.getValues(),tmpLink.getSubValues(), parts, shapes, copiedShapeStorage, types); assert(types.size() == 1); return types[0]; @@ -752,22 +809,21 @@ GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector &objs, + const std::vector &sub, std::vector &geofs, std::vector &shapes, std::vector &storage, std::vector &types) { - verifyReferencesAreSafe(references); - 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]->isDerivedFrom()) { - throw AttachEngineException("AttachEngine3D: link points to something that is not App::GeoFeature"); + if (!objs[i]->getTypeId().isDerivedFrom(App::GeoFeature::getClassTypeId())) { + FC_THROWM(AttachEngineException,"AttachEngine3D: attached to a non App::GeoFeature '" + << objs[i]->getNameInDocument() << "'"); } App::GeoFeature* geof = static_cast(objs[i]); geofs[i] = geof; @@ -775,20 +831,32 @@ void AttachEngine::readLinks(const App::PropertyLinkSubList &references, if (geof->isDerivedFrom(Part::Feature::getClassTypeId())){ shape = (static_cast(geof)->Shape.getShape()); if (shape.isNull()){ - throw AttachEngineException("AttachEngine3D: Part has null shape"); + FC_THROWM(AttachEngineException,"AttachEngine3D: Part has null shape " + << objs[i]->getNameInDocument()); } if (sub[i].length()>0){ try{ - storage.push_back(shape.getSubShape(sub[i].c_str())); - } catch (Standard_Failure&){ - throw AttachEngineException("AttachEngine3D: subshape not found"); + shape = Part::Feature::getTopoShape(geof, sub[i].c_str(), true); + if (shape.isNull()) + FC_THROWM(AttachEngineException, "AttachEngine3D: subshape not found " + << objs[i]->getNameInDocument() << '.' << sub[i]); + storage.push_back(shape.getShape()); + } catch (Standard_Failure &e){ + FC_THROWM(AttachEngineException, "AttachEngine3D: subshape not found " + << objs[i]->getNameInDocument() << '.' << sub[i] + << std::endl << e.GetMessageString()); + } catch (Base::CADKernelError &e){ + FC_THROWM(AttachEngineException, "AttachEngine3D: subshape not found " + << objs[i]->getNameInDocument() << '.' << sub[i] + << std::endl << e.what()); } if(storage[storage.size()-1].IsNull()) - throw AttachEngineException("AttachEngine3D: null subshape"); - shapes[i] = &(storage[storage.size()-1]); + FC_THROWM(AttachEngineException, "AttachEngine3D: null subshape " + << objs[i]->getNameInDocument() << '.' << sub[i]); } else { - shapes[i] = &(shape.getShape()); + storage.push_back(shape.getShape()); } + shapes[i] = &(storage[storage.size()-1]); } else if ( geof->isDerivedFrom(App::Plane::getClassTypeId()) ){ //obtain Z axis and origin of placement Base::Vector3d norm; @@ -859,6 +927,81 @@ void AttachEngine::verifyReferencesAreSafe(const App::PropertyLinkSubList &refer } } +std::vector AttachEngine::getRefObjects() const +{ + std::vector objs; + if(objNames.empty()) + return objs; + auto doc = App::GetApplication().getDocument(docName.c_str()); + if(!doc) + FC_THROWM(AttachEngineException,"AttachEngine: document '" << docName << "' not found"); + objs.reserve(objNames.size()); + for(auto &name : objNames) { + objs.push_back(doc->getObject(name.c_str())); + if(!objs.back()) + FC_THROWM(AttachEngineException, + "AttachEngine: object '" << docName << "#" << name << "' not found"); + } + return objs; +} + +Base::Placement AttachEngine::calculateAttachedPlacement( + const Base::Placement &origPlacement, bool *subChanged) +{ + std::map > subChanges; + int i=-1; + auto objs = getRefObjects(); + for(auto obj : objs) { + ++i; + auto &sub = subnames[i]; + auto &shadow = shadowSubs[i]; + if(shadow.empty() || !Data::hasMissingElement(sub.c_str())) + continue; + auto related = Part::Feature::getRelatedElements(obj,shadow.c_str(),Part::HistoryTraceType::followTypeChange,false); + if(related.size()) { + auto &res = subChanges[i]; + res.first = Data::ComplexGeoData::elementMapPrefix(); + related.front().name.appendToBuffer(res.first); + res.second.clear(); + related.front().index.appendToStringBuffer(res.second); + } else { + std::string name = Data::oldElementName(shadow.c_str()); + if (name.size()) { + auto &res = subChanges[i]; + res.first.clear(); + res.second = name; + }else + subnames[i] = shadow; + } + } + if(subChanges.size()) { + // In case there is topological name changes, we only auto change the + // subname if the calculated placement stays the same. If not, just + // proceed as normal, which will throw exception and catch user's + // attention. + auto subs = subnames; + for(auto &v : subChanges) + subs[v.first] = v.second.second; + auto pla = _calculateAttachedPlacement(objs,subs,origPlacement); + // check equal placement with some tolerance + if(pla.getPosition().IsEqual(origPlacement.getPosition(),1e-7) + && pla.getRotation().isSame(origPlacement.getRotation(),1e-12)) + { + // Only make changes if the caller supplies 'subChanged', because + // otherwise it means the caller just want to do an immutable test. + // See AttachExtension::isAttacherActive(). + if(subChanged) { + *subChanged = true; + subnames = std::move(subs); + for(auto &v : subChanges) + shadowSubs[v.first] = v.second.first; + } + return pla; + } + } + return _calculateAttachedPlacement(objs,subnames,origPlacement); +} + //================================================================================= @@ -973,7 +1116,10 @@ AttachEngine3D* AttachEngine3D::copy() const return p; } -Base::Placement AttachEngine3D::calculateAttachedPlacement(const Base::Placement& origPlacement) const +Base::Placement AttachEngine3D::_calculateAttachedPlacement( + const std::vector&objs, + const std::vector &subs, + const Base::Placement &origPlacement) const { const eMapMode mmode = this->mapMode; if (mmode == mmDeactivated) @@ -982,12 +1128,11 @@ Base::Placement AttachEngine3D::calculateAttachedPlacement(const Base::Placement std::vector shapes; std::vector copiedShapeStorage; std::vector types; - readLinks(this->references, parts, shapes, copiedShapeStorage, types); + readLinks(objs,subs, parts, shapes, copiedShapeStorage, types); - if (parts.empty()) + 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(); @@ -995,469 +1140,476 @@ Base::Placement AttachEngine3D::calculateAttachedPlacement(const Base::Placement //variables to derive the actual placement. //They are to be set, depending on the mode: - //to the sketch + //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.empty()) + case mmDeactivated: + //should have been filtered out already! + break; + case mmTranslate:{ + if (shapes.size() < 1) throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: no subobjects specified (need one vertex)."); const TopoDS_Shape &sh = *shapes[0]; - if (sh.IsNull()) - throw Base::ValueError("Null face in AttachEngine3D::calculateAttachedPlacement()!"); + if (sh.IsNull()) + throw Base::ValueError("Null shape in AttachEngine3D::calculateAttachedPlacement()!"); if (sh.ShapeType() != TopAbs_VERTEX) throw Base::ValueError("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->attachmentOffset.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::ValueError("AttachEngine3D::calculateAttachedPlacement: need either a conic section edge, or a whole object for ObjectXY-like modes."); - } - - switch (mmode){ + Base::Placement plm = Base::Placement(); + plm.setPosition(Base::Vector3d(p.X(), p.Y(), p.Z())); + plm.setPosition(plm.getPosition() + this->attachmentOffset.getPosition()); + plm.setRotation(origPlacement.getRotation()); + return plm; + } break; 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; - } + 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::ValueError("AttachEngine3D::calculateAttachedPlacement: need either a conic section edge, or a whole object for ObjectXY-like modes."); + } - } break; - case mmInertialCS:{ - GProp_GProps gpr = AttachEngine::getInertialPropsOfShape(shapes); - GProp_PrincipalProps pr = gpr.PrincipalProperties(); - if (pr.HasSymmetryPoint()) + 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 mmInertialCS:{ + GProp_GProps gpr = AttachEngine::getInertialPropsOfShape(shapes); + GProp_PrincipalProps pr = gpr.PrincipalProperties(); + if (pr.HasSymmetryPoint()) throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement:InertialCS: inertia tensor is trivial, principal axes are undefined."); if (pr.HasSymmetryAxis()){ - Base::Console().Warning("AttachEngine3D::calculateAttachedPlacement:InertialCS: inertia tensor has axis of symmetry. Second and third axes of inertia are undefined.\n"); - //find defined axis, and use it as Z axis - //situation: we have two moments that are almost equal, and one - //that is substantially different. The one that is different - //corresponds to a defined axis. We'll identify the different one by - //comparing differences. - Standard_Real I1, I2, I3; - pr.Moments(I1,I2,I3); - Standard_Real d12, d23, d31; - d12 = fabs(I1-I2); - d23 = fabs(I2-I3); - d31 = fabs(I3-I1); - if(d12 < d23 && d12 < d31){ - SketchNormal = pr.ThirdAxisOfInertia(); - } else if (d23 < d31 && d23 < d12){ - SketchNormal = pr.FirstAxisOfInertia(); + Base::Console().Warning("AttachEngine3D::calculateAttachedPlacement:InertialCS: inertia tensor has axis of symmetry. Second and third axes of inertia are undefined.\n"); + //find defined axis, and use it as Z axis + //situation: we have two moments that are almost equal, and one + //that is substantially different. The one that is different + //corresponds to a defined axis. We'll identify the different one by + //comparing differences. + Standard_Real I1, I2, I3; + pr.Moments(I1,I2,I3); + Standard_Real d12, d23, d31; + d12 = fabs(I1-I2); + d23 = fabs(I2-I3); + d31 = fabs(I3-I1); + if(d12 < d23 && d12 < d31){ + SketchNormal = pr.ThirdAxisOfInertia(); + } else if (d23 < d31 && d23 < d12){ + SketchNormal = pr.FirstAxisOfInertia(); + } else { + SketchNormal = pr.SecondAxisOfInertia(); + } } else { - SketchNormal = pr.SecondAxisOfInertia(); + SketchNormal = pr.FirstAxisOfInertia(); + SketchXAxis = pr.SecondAxisOfInertia(); } - } else { - SketchNormal = pr.FirstAxisOfInertia(); - SketchXAxis = pr.SecondAxisOfInertia(); - } - SketchBasePoint = gpr.CentreOfMass(); - }break; - case mmFlatFace:{ - if (shapes.empty()) + SketchBasePoint = gpr.CentreOfMass(); + }break; + case mmFlatFace:{ + if (shapes.size() < 1) throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: no subobjects specified (needed one planar face)."); - const TopoDS_Face &face = TopoDS::Face(*(shapes[0])); - if (face.IsNull()) - throw Base::ValueError("Null face in AttachEngine3D::calculateAttachedPlacement()!"); - - gp_Pln plane; - BRepAdaptor_Surface adapt(face); - if (adapt.GetType() == GeomAbs_Plane) { - plane = adapt.Plane(); - } - else { - TopLoc_Location loc; - Handle(Geom_Surface) surf = BRep_Tool::Surface(face, loc); - GeomLib_IsPlanarSurface check(surf); - if (check.IsPlanar()) - plane = check.Plan(); - else + TopoDS_Face face; + gp_Pln plane; + bool Reverse = false; + try { face = TopoDS::Face(*(shapes[0])); } catch(...) {} + if (face.IsNull()) { + if (!TopoShape(*shapes[0]).findPlane(plane)) throw Base::ValueError("No planar face in AttachEngine3D::calculateAttachedPlacement()!"); + } else { + BRepAdaptor_Surface adapt(face); + if (adapt.GetType() == GeomAbs_Plane) { + plane = adapt.Plane(); + } + else { + TopLoc_Location loc; + Handle(Geom_Surface) surf = BRep_Tool::Surface(face, loc); + GeomLib_IsPlanarSurface check(surf); + if (check.IsPlanar()) + plane = check.Plan(); + else + throw Base::ValueError("No planar face in AttachEngine3D::calculateAttachedPlacement()!"); + } + + if (face.Orientation() == TopAbs_REVERSED) + Reverse = true; } - bool Reverse = false; - if (face.Orientation() == TopAbs_REVERSED) - Reverse = true; - - 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) + 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(); + Handle (Geom_Plane) gPlane = new Geom_Plane(plane); + GeomAPI_ProjectPointOnSurf projector(refOrg,gPlane); + SketchBasePoint = projector.NearestPoint(); - } break; - case mmTangentPlane: { - if (shapes.size() < 2) + } break; + case mmTangentPlane: { + if (shapes.size() < 2) throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: not enough subshapes (need one false and one vertex)."); bool bThruVertex = false; - if (shapes[0]->ShapeType() == TopAbs_VERTEX) { - std::swap(shapes[0],shapes[1]); - bThruVertex = true; - } + if (shapes[0]->ShapeType() == TopAbs_VERTEX) { + std::swap(shapes[0],shapes[1]); + bThruVertex = true; + } - const TopoDS_Face &face = TopoDS::Face(*(shapes[0])); - if (face.IsNull()) + TopoDS_Face face; + try { face = TopoDS::Face(*(shapes[0])); } catch(...) {} + if (face.IsNull()) throw Base::ValueError("Null face in AttachEngine3D::calculateAttachedPlacement()!"); - const TopoDS_Vertex &vertex = TopoDS::Vertex(*(shapes[1])); - if (vertex.IsNull()) + TopoDS_Vertex vertex; + try { vertex = TopoDS::Vertex(*(shapes[1])); } catch(...) {} + if (vertex.IsNull()) throw Base::ValueError("Null vertex in AttachEngine3D::calculateAttachedPlacement()!"); Handle (Geom_Surface) hSurf = BRep_Tool::Surface(face); - gp_Pnt p = BRep_Tool::Pnt(vertex); + gp_Pnt p = BRep_Tool::Pnt(vertex); - GeomAPI_ProjectPointOnSurf projector(p, hSurf); - double u, v; - if (projector.NbPoints()==0) + GeomAPI_ProjectPointOnSurf projector(p, hSurf); + double u, v; + if (projector.NbPoints()==0) throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: projecting point onto surface failed."); projector.LowerDistanceParameters(u, v); - BRepAdaptor_Surface surf(face); - BRepLProp_SLProps prop(surf, u, v, 1, Precision::Confusion()); - gp_Dir dirX; - Standard_Boolean done; + BRepAdaptor_Surface surf(face); + BRepLProp_SLProps prop(surf, u, v, 1, Precision::Confusion()); + gp_Dir dirX; + Standard_Boolean done; - Tools::getNormal(face, u, v, Precision::Confusion(), SketchNormal, done); + Tools::getNormal(face, u, v, Precision::Confusion(), SketchNormal, done); - if (!done) + if (!done) throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: finding normal to surface at projected point failed."); // if getNormal succeeds, at least one of the tangent is defined - if (prop.IsTangentUDefined()) { - prop.TangentU(dirX); - if (face.Orientation() == TopAbs_REVERSED) + if (prop.IsTangentUDefined()) { + prop.TangentU(dirX); + if (face.Orientation() == TopAbs_REVERSED) dirX.Reverse(); } - // here the orientation of dirX is managed by SketchNormal orientation - else { - gp_Dir dirY; - prop.TangentV(dirY); - dirX = dirY.Crossed(SketchNormal); - } + // here the orientation of dirX is managed by SketchNormal orientation + else { + gp_Dir dirY; + prop.TangentV(dirY); + dirX = dirY.Crossed(SketchNormal); + } - SketchXAxis = gp_Vec(dirX).Reversed();//yields upside-down sketches less often. + SketchXAxis = gp_Vec(dirX).Reversed();//yields upside-down sketches less often. - if (bThruVertex) { - SketchBasePoint = p; - } else { - SketchBasePoint = projector.NearestPoint(); - } - } break; - case mmNormalToPath: - case mmFrenetNB: - case mmFrenetTN: - case mmFrenetTB: - case mmRevolutionSection: - case mmConcentric: {//all alignments to point on curve - if (shapes.empty()) + if (bThruVertex) { + SketchBasePoint = p; + } else { + SketchBasePoint = projector.NearestPoint(); + } + } break; + case mmNormalToPath: + case mmFrenetNB: + case mmFrenetTN: + case mmFrenetTB: + case mmRevolutionSection: + case mmConcentric: {//all alignments to point on curve + if (shapes.size() < 1) throw Base::ValueError("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; - } + 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()) + TopoDS_Edge path; + try { path = TopoDS::Edge(*(shapes[0])); } catch(...) {} + if (path.IsNull()) throw Base::ValueError("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; - } + 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()) + //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; + try { TopoDS::Vertex(*(shapes[1])); } catch(...) {} + if (vertex.IsNull()) throw Base::ValueError("Null vertex in AttachEngine3D::calculateAttachedPlacement()!"); p_in = BRep_Tool::Pnt(vertex); - Handle (Geom_Curve) hCurve = BRep_Tool::Curve(path, u1, u2); + 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); + 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 - } + gp_Vec T,N,B;//Frenet?Serret axes: tangent, normal, binormal + T = d.Normalized(); + N = dd.Subtracted(T.Multiplied(dd.Dot(T)));//take away the portion of dd that is along tangent + if (N.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 + } - 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) + 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::ValueError("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) + SketchXAxis = T; + break; + case mmFrenetTB: + if (N.Magnitude() == 0.0) throw Base::ValueError("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 at 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) + 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::ValueError("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. + 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. } - } 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. - } - } break; - case mmThreePointsPlane: - case mmThreePointsNormal: { + } break; + case mmThreePointsPlane: + case mmThreePointsNormal: { - std::vector points; + std::vector points; - for (const auto & shape : shapes) { - const TopoDS_Shape &sh = *shape; - if (sh.IsNull()) + for (std::size_t i = 0; i < shapes.size(); i++) { + const TopoDS_Shape &sh = *shapes[i]; + if (sh.IsNull()) throw Base::ValueError("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; + 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)); } - points.push_back(crv.Value(u1)); - points.push_back(crv.Value(u2)); - } - if (points.size() >= 3) + if (points.size() >= 3) break; } - if(points.size()<3) + if(points.size()<3) throw Base::ValueError("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_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()) + gp_Vec vec01 (p0,p1); + gp_Vec vec02 (p0,p2); + if (vec01.Magnitude() < Precision::Confusion() || vec02.Magnitude() < Precision::Confusion()) throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: some of 3 points are coincident. Can't make a plane"); vec01.Normalize(); - vec02.Normalize(); + vec02.Normalize(); - gp_Vec norm ; - if (mmode == mmThreePointsPlane) { - norm = vec01.Crossed(vec02); - if (norm.Magnitude() < Precision::Confusion()) + gp_Vec norm ; + if (mmode == mmThreePointsPlane) { + norm = vec01.Crossed(vec02); + if (norm.Magnitude() < Precision::Confusion()) throw Base::ValueError("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()) + 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::ValueError("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(); + 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); + norm.Normalize(); + SketchNormal = gp_Dir(norm); - } break; - case mmFolding: { + } 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. + // 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) + if (shapes.size()<4) throw Base::ValueError("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()) + const TopoDS_Edge* edges[4]; + BRepAdaptor_Curve adapts[4]; + gp_Lin lines[4]; + for(int i=0 ; i<4 ; i++){ + try { edges[i] = &TopoDS::Edge(*(shapes[i])); } catch(...){} + if (edges[i]->IsNull()) throw Base::ValueError("Null edge in AttachEngine3D::calculateAttachedPlacement()!"); adapts[i] = BRepAdaptor_Curve(*(edges[i])); - if (adapts[i].GetType() != GeomAbs_Line) + if (adapts[i].GetType() != GeomAbs_Line) throw Base::ValueError("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::ValueError("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()) + //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::ValueError("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; @@ -1465,121 +1617,121 @@ Base::Placement AttachEngine3D::calculateAttachedPlacement(const Base::Placement throw Base::ValueError("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]); - } + 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] - ); + 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(); + 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]; + SketchXAxis = dirs[1]; - SketchBasePoint = p; + SketchBasePoint = p; - } break; - case mmOZX: - case mmOZY: - case mmOXY: - case mmOXZ: - case mmOYZ: - case mmOYX: { - const char orderStrings[6][4] = { - "ZXY", - "ZYX", - "XYZ", - "XZY", - "YZX", - "YXZ", - }; - const char* orderString = orderStrings[mmode - mmOZX]; + } break; + case mmOZX: + case mmOZY: + case mmOXY: + case mmOXZ: + case mmOYZ: + case mmOYX: { + const char orderStrings[6][4] = { + "ZXY", + "ZYX", + "XYZ", + "XZY", + "YZX", + "YXZ", + }; + const char* orderString = orderStrings[mmode - mmOZX]; - enum dirIndex { - X, - Y, - Z - }; - int order[3]; - for(int i = 0; i < 3; ++i){ - order[i] = orderString[i] - 'X'; - } + enum dirIndex { + X, + Y, + Z + }; + int order[3]; + for(int i = 0; i < 3; ++i){ + order[i] = orderString[i] - 'X'; + } - if (shapes.size() < 2) + if (shapes.size() < 2) THROWM(Base::ValueError, "AttachEngine3D::calculateAttachedPlacement: not enough shapes linked (at least two are required)."); gp_Vec dirs[3]; - //read out origin - if (shapes[0]->IsNull()) + //read out origin + if (shapes[0]->IsNull()) THROWM(Base::TypeError, "AttachEngine3D::calculateAttachedPlacement: null shape!") if (shapes[0]->ShapeType() != TopAbs_VERTEX) THROWM(Base::TypeError, "AttachEngine3D::calculateAttachedPlacement: first reference must be a vertex, it's not") SketchBasePoint = BRep_Tool::Pnt(TopoDS::Vertex(*(shapes[0]))); - //read out axes directions - for(size_t i = 1; i < 3 && i < shapes.size(); ++i){ - if (shapes[i]->IsNull()) + //read out axes directions + for(size_t i = 1; i < 3 && i < shapes.size(); ++i){ + if (shapes[i]->IsNull()) THROWM(Base::TypeError, "AttachEngine3D::calculateAttachedPlacement: null shape!") if (shapes[i]->ShapeType() == TopAbs_VERTEX){ - gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(*(shapes[i]))); - dirs[order[i-1]] = gp_Vec(SketchBasePoint, p); - } else if (shapes[i]->ShapeType() == TopAbs_EDGE){ - const TopoDS_Edge &e = TopoDS::Edge(*(shapes[i])); - 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; + gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(*(shapes[i]))); + dirs[order[i-1]] = gp_Vec(SketchBasePoint, p); + } else if (shapes[i]->ShapeType() == TopAbs_EDGE){ + const TopoDS_Edge &e = TopoDS::Edge(*(shapes[i])); + 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; + } + gp_Pnt p1 = crv.Value(u1); + gp_Pnt p2 = crv.Value(u2); + dirs[order[i-1]] = gp_Vec(p1,p2); } - gp_Pnt p1 = crv.Value(u1); - gp_Pnt p2 = crv.Value(u2); - dirs[order[i-1]] = gp_Vec(p1,p2); } - } - //make the placement - Base::Rotation rot = + //make the placement + Base::Rotation rot = Base::Rotation::makeRotationByAxes( Base::Vector3d(dirs[0].X(), dirs[0].Y(), dirs[0].Z()), Base::Vector3d(dirs[1].X(), dirs[1].Y(), dirs[1].Z()), Base::Vector3d(dirs[2].X(), dirs[2].Y(), dirs[2].Z()), orderString ); - if(this->mapReverse){ - rot = rot * Base::Rotation(Base::Vector3d(0,1,0),D_PI); - } + if(this->mapReverse){ + rot = rot * Base::Rotation(Base::Vector3d(0,1,0),D_PI); + } - Base::Placement plm = + Base::Placement plm = Base::Placement(Base::Vector3d(SketchBasePoint.X(), SketchBasePoint.Y(), SketchBasePoint.Z()), rot); - plm *= this->attachmentOffset; - return plm; - } break; - default: - throwWrongMode(mmode); + plm *= this->attachmentOffset; + return plm; + } 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); + this->placementFactory(SketchNormal, SketchXAxis, SketchBasePoint, gp_Pnt(), + /*useRefOrg_Line = */ false, + /*useRefOrg_Plane = */ false, + /*makeYVertical = */ false, + /*makeLegacyFlatFaceOrientation = */ mmode == mmFlatFace, + &Place); plm *= this->attachmentOffset; return plm; } @@ -1636,13 +1788,16 @@ AttachEnginePlane *AttachEnginePlane::copy() const return p; } -Base::Placement AttachEnginePlane::calculateAttachedPlacement(const Base::Placement& origPlacement) const +Base::Placement AttachEnginePlane::_calculateAttachedPlacement( + const std::vector&objs, + const std::vector &subs, + const Base::Placement &origPlacement) const { //re-use Attacher3d Base::Placement plm; AttachEngine3D attacher3D; attacher3D.setUp(*this); - plm = attacher3D.calculateAttachedPlacement(origPlacement); + plm = attacher3D._calculateAttachedPlacement(objs,subs,origPlacement); return plm; } @@ -1700,7 +1855,10 @@ AttachEngineLine *AttachEngineLine::copy() const return p; } -Base::Placement AttachEngineLine::calculateAttachedPlacement(const Base::Placement& origPlacement) const +Base::Placement AttachEngineLine::_calculateAttachedPlacement( + const std::vector&objs, + const std::vector &subs, + const Base::Placement &origPlacement) const { eMapMode mmode = this->mapMode; @@ -1708,40 +1866,40 @@ Base::Placement AttachEngineLine::calculateAttachedPlacement(const Base::Placeme bool bReUsed = true; Base::Placement presuperPlacement; switch(mmode){ - case mmDeactivated: - throw ExceptionCancel();//to be handled in positionBySupport, to not do anything if disabled - 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; - case mm1FaceNormal: - mmode = mmTangentPlane; - break; - default: - bReUsed = false; - break; + case mmDeactivated: + throw ExceptionCancel();//to be handled in positionBySupport, to not do anything if disabled + 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; + case mm1FaceNormal: + mmode = mmTangentPlane; + break; + default: + bReUsed = false; + break; } Base::Placement plm; @@ -1750,9 +1908,9 @@ Base::Placement AttachEngineLine::calculateAttachedPlacement(const Base::Placeme std::vector shapes; std::vector copiedShapeStorage; std::vector types; - readLinks(this->references, parts, shapes, copiedShapeStorage, types); + readLinks(objs,subs, parts, shapes, copiedShapeStorage, types); - if (parts.empty()) + if (parts.size() == 0) throw ExceptionCancel(); @@ -1768,185 +1926,163 @@ Base::Placement AttachEngineLine::calculateAttachedPlacement(const Base::Placeme switch (mmode) { - case mm1AxisInertia1: - case mm1AxisInertia2: - case mm1AxisInertia3:{ - GProp_GProps gpr = AttachEngine::getInertialPropsOfShape(shapes); - LineBasePoint = gpr.CentreOfMass(); - GProp_PrincipalProps pr = gpr.PrincipalProperties(); - if (pr.HasSymmetryPoint()) + case mm1AxisInertia1: + case mm1AxisInertia2: + case mm1AxisInertia3:{ + GProp_GProps gpr = AttachEngine::getInertialPropsOfShape(shapes); + LineBasePoint = gpr.CentreOfMass(); + GProp_PrincipalProps pr = gpr.PrincipalProperties(); + if (pr.HasSymmetryPoint()) throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement:AxisOfInertia: inertia tensor is trivial, principal axes are undefined."); //query moments, to use them to check if axis is defined - //See AttachEngine3D::calculateAttachedPlacement:case mmInertial for comment explaining these comparisons - Standard_Real I1, I2, I3; - pr.Moments(I1,I2,I3); - Standard_Real d12, d23, d31; - d12 = fabs(I1-I2); - d23 = fabs(I2-I3); - d31 = fabs(I3-I1); + //See AttachEngine3D::calculateAttachedPlacement:case mmInertial for comment explaining these comparisons + Standard_Real I1, I2, I3; + pr.Moments(I1,I2,I3); + Standard_Real d12, d23, d31; + d12 = fabs(I1-I2); + d23 = fabs(I2-I3); + d31 = fabs(I3-I1); - if (mmode == mm1AxisInertia1){ - LineDir = pr.FirstAxisOfInertia(); - if (pr.HasSymmetryAxis() && !(d23 < d31 && d23 < d12)) + if (mmode == mm1AxisInertia1){ + LineDir = pr.FirstAxisOfInertia(); + if (pr.HasSymmetryAxis() && !(d23 < d31 && d23 < d12)) throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement:AxisOfInertia: inertia tensor has axis of symmetry; first axis of inertia is undefined."); } else if (mmode == mm1AxisInertia2) { - LineDir = pr.SecondAxisOfInertia(); - if (pr.HasSymmetryAxis() && !(d31 < d12 && d31 < d23)) + LineDir = pr.SecondAxisOfInertia(); + if (pr.HasSymmetryAxis() && !(d31 < d12 && d31 < d23)) throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement:AxisOfInertia: inertia tensor has axis of symmetry; second axis of inertia is undefined."); } else if (mmode == mm1AxisInertia3) { - LineDir = pr.ThirdAxisOfInertia(); - if (pr.HasSymmetryAxis() && !(d12 < d23 && d12 < d31)) + LineDir = pr.ThirdAxisOfInertia(); + if (pr.HasSymmetryAxis() && !(d12 < d23 && d12 < d31)) throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement:AxisOfInertia: inertia tensor has axis of symmetry; third axis of inertia is undefined."); } - }break; - case mm1TwoPoints:{ - std::vector points; + }break; + case mm1TwoPoints:{ + std::vector points; - for (const auto & shape : shapes) { - const TopoDS_Shape &sh = *shape; - if (sh.IsNull()) + for (std::size_t i = 0; i < shapes.size(); i++) { + const TopoDS_Shape &sh = *shapes[i]; + if (sh.IsNull()) throw Base::ValueError("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; + 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)); } - points.push_back(crv.Value(u1)); - points.push_back(crv.Value(u2)); - } - if (points.size() >= 2) + if (points.size() >= 2) break; } - if(points.size()<2) + if(points.size()<2) throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: less than 2 points are specified, cannot derive the line."); gp_Pnt p0 = points[0]; - gp_Pnt p1 = points[1]; + gp_Pnt p1 = points[1]; - LineDir = gp_Dir(gp_Vec(p0,p1)); - LineBasePoint = p0; + LineDir = gp_Dir(gp_Vec(p0,p1)); + LineBasePoint = p0; - }break; - case mm1Asymptote1: - case mm1Asymptote2:{ - if (shapes[0]->IsNull()) + }break; + case mm1Asymptote1: + case mm1Asymptote2:{ + if (shapes[0]->IsNull()) throw Base::ValueError("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); - const TopoDS_Edge &e = TopoDS::Edge(*(shapes[0])); + TopoDS_Edge e; + try { e = TopoDS::Edge(*(shapes[0])); } catch(...) {} + if (e.IsNull()) + throw Base::ValueError("Null edge in AttachEngineLine::calculateAttachedPlacement()!"); BRepAdaptor_Curve adapt (e); - if (adapt.GetType() != GeomAbs_Hyperbola) + if (adapt.GetType() != GeomAbs_Hyperbola) throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: Asymptotes are available only for hyperbola-shaped edges, the one supplied is not."); gp_Hypr hyp = adapt.Hyperbola(); - if (mmode == mm1Asymptote1) + if (mmode == mm1Asymptote1) LineDir = hyp.Asymptote1().Direction(); else LineDir = hyp.Asymptote2().Direction(); LineBasePoint = hyp.Location(); - }break; - case mm1Directrix1: - case mm1Directrix2:{ - if (shapes[0]->IsNull()) + }break; + case mm1Directrix1: + case mm1Directrix2:{ + if (shapes[0]->IsNull()) throw Base::ValueError("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); - const TopoDS_Edge &e = TopoDS::Edge(*(shapes[0])); + TopoDS_Edge e; + try { e = TopoDS::Edge(*(shapes[0])); } catch(...) {} + if (e.IsNull()) + throw Base::ValueError("Null edge in AttachEngineLine::calculateAttachedPlacement()!"); BRepAdaptor_Curve adapt (e); - gp_Ax1 dx1, dx2;//vars to receive 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) + gp_Ax1 dx1, dx2;//vars to receive 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::ValueError("AttachEngineLine::calculateAttachedPlacement: Parabola has no second directrix"); }break; - default: - throw Base::ValueError("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 mm1Intersection:{ - if (shapes.size() < 2) - throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: Intersection mode requires two shapes; only one is supplied"); - if (shapes[0]->IsNull() || shapes[1]->IsNull()) - throw Base::ValueError("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); - - const TopoDS_Face &face1 = TopoDS::Face(*(shapes[0])); - const TopoDS_Face &face2 = TopoDS::Face(*(shapes[1])); - - Handle(Geom_Surface) hSurf1 = BRep_Tool::Surface(face1); - Handle(Geom_Surface) hSurf2 = BRep_Tool::Surface(face2); - GeomAPI_IntSS intersector(hSurf1, hSurf2, Precision::Confusion()); - if (!intersector.IsDone()) - throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: Intersection failed"); - - const Standard_Integer intLines = intersector.NbLines(); - if (intLines == 0) - throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: The two shapes don't intersect"); - if (intLines != 1) - throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: Intersection is not a single curve"); - - GeomAdaptor_Curve adapt(intersector.Line(1)); - if (adapt.GetType() != GeomAbs_Line) - throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: Intersection is not a straight line"); - - LineBasePoint = adapt.Line().Location(); - LineDir = adapt.Line().Direction(); - }break; - case mm1Proximity:{ - if (shapes.size() < 2) + default: + throw Base::ValueError("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::ValueError("AttachEngineLine::calculateAttachedPlacement: Proximity mode requires two shapes; only one is supplied"); if (shapes[0]->IsNull()) throw Base::ValueError("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); if (shapes[1]->IsNull()) throw Base::ValueError("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); BRepExtrema_DistShapeShape distancer (*(shapes[0]), *(shapes[1])); - if (!distancer.IsDone()) + if (!distancer.IsDone()) throw Base::ValueError("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()) + gp_Pnt p2 = distancer.PointOnShape2(1); + LineBasePoint = p1; + gp_Vec dist = gp_Vec(p1,p2); + if (dist.Magnitude() < Precision::Confusion()) throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: can't make proximity line, because shapes touch or intersect"); LineDir = gp_Dir(dist); - }break; - default: - throwWrongMode(mmode); + }break; + default: + throwWrongMode(mmode); } plm = this->placementFactory(LineDir, gp_Vec(), LineBasePoint, refOrg, - /*useRefOrg_Line = */ true); + /*useRefOrg_Line = */ true); } else {//re-use 3d mode AttachEngine3D attacher3D; attacher3D.setUp(*this); attacher3D.mapMode = mmode; attacher3D.attachmentOffset = Base::Placement(); //AttachmentOffset is applied separately here, afterwards. So we are resetting it in sub-attacher to avoid applying it twice! - plm = attacher3D.calculateAttachedPlacement(origPlacement); + plm = attacher3D._calculateAttachedPlacement(objs,subs,origPlacement); plm *= presuperPlacement; } plm *= this->attachmentOffset; @@ -1997,27 +2133,30 @@ AttachEnginePoint *AttachEnginePoint::copy() const return p; } -Base::Placement AttachEnginePoint::calculateAttachedPlacement(const Base::Placement& origPlacement) const +Base::Placement AttachEnginePoint::_calculateAttachedPlacement( + const std::vector&objs, + const std::vector &subs, + const Base::Placement &origPlacement) const { eMapMode mmode = this->mapMode; //modes that are mirrors of attacher3D: bool bReUsed = true; switch(mmode){ - case mmDeactivated: - throw ExceptionCancel();//to be handled in positionBySupport, to not do anything if disabled - case mm0Origin: - mmode = mmObjectXY; - break; - case mm0CenterOfCurvature: - mmode = mmRevolutionSection; - break; - case mm0OnEdge: - //todo: prevent thruPoint - mmode = mmNormalToPath; - break; - default: - bReUsed = false; + case mmDeactivated: + throw ExceptionCancel();//to be handled in positionBySupport, to not do anything if disabled + case mm0Origin: + mmode = mmObjectXY; + break; + case mm0CenterOfCurvature: + mmode = mmRevolutionSection; + break; + case mm0OnEdge: + //todo: prevent thruPoint + mmode = mmNormalToPath; + break; + default: + bReUsed = false; } Base::Placement plm; @@ -2026,7 +2165,7 @@ Base::Placement AttachEnginePoint::calculateAttachedPlacement(const Base::Placem std::vector shapes; std::vector copiedShapeStorage; std::vector types; - readLinks(this->references, parts, shapes, copiedShapeStorage, types); + readLinks(objs,subs, parts, shapes, copiedShapeStorage, types); if (parts.empty()) throw ExceptionCancel(); @@ -2038,61 +2177,64 @@ Base::Placement AttachEnginePoint::calculateAttachedPlacement(const Base::Placem switch (mmode) { - case mm0Vertex:{ - std::vector points; - assert(shapes.size()>0); + case mm0Vertex:{ + std::vector points; + assert(shapes.size()>0); - const TopoDS_Shape &sh = *shapes[0]; - if (sh.IsNull()) + const TopoDS_Shape &sh = *shapes[0]; + if (sh.IsNull()) throw Base::ValueError("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); - double u = crv.FirstParameter(); - if(Precision::IsInfinite(u)) + 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); + double u = crv.FirstParameter(); + if(Precision::IsInfinite(u)) throw Base::ValueError("Edge is infinite"); BasePoint = crv.Value(u); - } + } - }break; - case mm0Focus1: - case mm0Focus2:{ - if (shapes[0]->IsNull()) + }break; + case mm0Focus1: + case mm0Focus2:{ + if (shapes[0]->IsNull()) throw Base::ValueError("Null shape in AttachEnginePoint::calculateAttachedPlacement()!"); - const TopoDS_Edge &e = TopoDS::Edge(*(shapes[0])); + TopoDS_Edge e; + try { e = TopoDS::Edge(*(shapes[0])); } catch(...) {} + if (e.IsNull()) + throw Base::ValueError("Null edge in AttachEnginePoint::calculateAttachedPlacement()!"); 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) + 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::ValueError("AttachEnginePoint::calculateAttachedPlacement: Parabola has no second focus"); }break; - default: - throw Base::ValueError("AttachEnginePoint::calculateAttachedPlacement: referenced edge is not a conic section with a directrix"); - } - if (mmode == mm0Focus1) + default: + throw Base::ValueError("AttachEnginePoint::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) + case mm0ProximityPoint1: + case mm0ProximityPoint2:{ + if (shapes.size() < 2) throw Base::ValueError("AttachEnginePoint::calculateAttachedPlacement: Proximity mode requires two shapes; only one is supplied"); if (shapes[0]->IsNull()) throw Base::ValueError("Null shape in AttachEnginePoint::calculateAttachedPlacement()!"); @@ -2100,13 +2242,13 @@ Base::Placement AttachEnginePoint::calculateAttachedPlacement(const Base::Placem throw Base::ValueError("Null shape in AttachEnginePoint::calculateAttachedPlacement()!"); BasePoint = getProximityPoint(mmode, *(shapes[0]), *(shapes[1])); - }break; - case mm0CenterOfMass:{ - GProp_GProps gpr = AttachEngine::getInertialPropsOfShape(shapes); - BasePoint = gpr.CentreOfMass(); - }break; - default: - throwWrongMode(mmode); + }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()); @@ -2115,7 +2257,7 @@ Base::Placement AttachEnginePoint::calculateAttachedPlacement(const Base::Placem attacher3D.setUp(*this); attacher3D.mapMode = mmode; attacher3D.attachmentOffset = Base::Placement(); //AttachmentOffset is applied separately here, afterwards. So we are resetting it in sub-attacher to avoid applying it twice! - plm = attacher3D.calculateAttachedPlacement(origPlacement); + plm = attacher3D._calculateAttachedPlacement(objs,subs,origPlacement); } plm *= this->attachmentOffset; return plm; diff --git a/src/Mod/Part/App/Attacher.h b/src/Mod/Part/App/Attacher.h index c4526ea51a..1ed210f61a 100644 --- a/src/Mod/Part/App/Attacher.h +++ b/src/Mod/Part/App/Attacher.h @@ -30,8 +30,10 @@ #include +#include #include #include +#include #include #include @@ -222,7 +224,17 @@ public: //methods const Base::Placement &attachmentOffset = Base::Placement()); virtual void setUp(const AttachEngine &another); virtual AttachEngine* copy() const = 0; - virtual Base::Placement calculateAttachedPlacement(const Base::Placement& origPlacement) const = 0; + + Base::Placement calculateAttachedPlacement( + const Base::Placement &origPlacement, bool *subChanged=0); + + virtual Base::Placement _calculateAttachedPlacement( + const std::vector &objs, + const std::vector &subs, + const Base::Placement &origPlacement) const = 0; + + void setReferences(const App::PropertyLinkSubList &references); + void setReferences(const std::vector &references); /** * @brief placementFactory calculates placement from Z axis direction, @@ -348,6 +360,9 @@ public://helper functions that may be useful outside of the class static GProp_GProps getInertialPropsOfShape(const std::vector &shapes); + std::vector getRefObjects() const; + const std::vector &getSubValues() const {return subnames;} + /** * @brief verifyReferencesAreSafe: checks if pointers in references still * point to objects contained in open documents. This guarantees the links @@ -362,6 +377,10 @@ public: //enums public: //members App::PropertyLinkSubList references; + std::string docName; + std::vector objNames; + std::vector subnames; + std::vector shadowSubs; eMapMode mapMode = mmDeactivated; bool mapReverse = false; @@ -405,7 +424,8 @@ protected: ret.push_back(rt4); return ret; } - static void readLinks(const App::PropertyLinkSubList &references, std::vector &geofs, + static void readLinks(const std::vector &objs, + const std::vector &subs, std::vector &geofs, std::vector& shapes, std::vector &storage, std::vector &types); @@ -420,7 +440,10 @@ class PartExport AttachEngine3D : public AttachEngine public: AttachEngine3D(); AttachEngine3D* copy() const override; - Base::Placement calculateAttachedPlacement(const Base::Placement& origPlacement) const override; + virtual Base::Placement _calculateAttachedPlacement( + const std::vector &objs, + const std::vector &subs, + const Base::Placement &origPlacement) const; private: double calculateFoldAngle(gp_Vec axA, gp_Vec axB, gp_Vec edA, gp_Vec edB) const; }; @@ -432,7 +455,10 @@ class PartExport AttachEnginePlane : public AttachEngine public: AttachEnginePlane(); AttachEnginePlane* copy() const override; - Base::Placement calculateAttachedPlacement(const Base::Placement& origPlacement) const override; + virtual Base::Placement _calculateAttachedPlacement( + const std::vector &objs, + const std::vector &subs, + const Base::Placement &origPlacement) const; }; //attacher specialized for datum lines @@ -442,7 +468,10 @@ class PartExport AttachEngineLine : public AttachEngine public: AttachEngineLine(); AttachEngineLine* copy() const override; - Base::Placement calculateAttachedPlacement(const Base::Placement& origPlacement) const override; + virtual Base::Placement _calculateAttachedPlacement( + const std::vector &objs, + const std::vector &subs, + const Base::Placement &origPlacement) const; }; //attacher specialized for datum points @@ -452,7 +481,10 @@ class PartExport AttachEnginePoint : public AttachEngine public: AttachEnginePoint(); AttachEnginePoint* copy() const override; - Base::Placement calculateAttachedPlacement(const Base::Placement& origPlacement) const override; + virtual Base::Placement _calculateAttachedPlacement( + const std::vector &objs, + const std::vector &subs, + const Base::Placement &origPlacement) const; private: gp_Pnt getProximityPoint(eMapMode mode, const TopoDS_Shape& s1, const TopoDS_Shape& s2) const; diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp index 6a075b7213..62773ead82 100644 --- a/src/Mod/Part/App/TopoShape.cpp +++ b/src/Mod/Part/App/TopoShape.cpp @@ -336,50 +336,19 @@ Data::Segment* TopoShape::getSubElement(const char* Type, unsigned long n) const return new ShapeSegment(getSubShape(temp.c_str())); } -TopoDS_Shape TopoShape::getSubShape(const char* Type, bool silent) const -{ - auto res = shapeTypeAndIndex(Type); - return getSubShape(res.first,res.second,silent); +TopoDS_Shape TopoShape::getSubShape(const char* Type, bool silent) const { + TopoShape s(*this); + s.Tag = 0; + return s.getSubTopoShape(Type,silent).getShape(); } -TopoDS_Shape TopoShape::getSubShape(TopAbs_ShapeEnum type, int index, bool silent) const -{ - if(index <= 0) { - if(silent) - return {}; - // TODO: Is this message clear? Should we complain about the negative index instead - Standard_Failure::Raise("Unsupported sub-shape type"); - } - - if (this->_Shape.IsNull()) { - if(silent) - return {}; - Standard_Failure::Raise("Cannot get sub-shape from empty shape"); - } - - try { - if(type == TopAbs_SHAPE) { - int i=1; - for(TopoDS_Iterator it(_Shape);it.More();it.Next(),++i) { - if(i == index) - return it.Value(); - } - } else { - TopTools_IndexedMapOfShape anIndices; - TopExp::MapShapes(this->_Shape, type, anIndices); - if(index <= anIndices.Extent()) - return anIndices.FindKey(index); - } - } catch(Standard_Failure &) { - if(silent) - return {}; - throw; - } - if(!silent) - Standard_Failure::Raise("Index out of bound"); - return {}; +TopoDS_Shape TopoShape::getSubShape(TopAbs_ShapeEnum type, int idx, bool silent) const { + TopoShape s(*this); + s.Tag = 0; + return s.getSubTopoShape(type,idx,silent).getShape(); } + unsigned long TopoShape::countSubShapes(const char* Type) const { if(!Type) diff --git a/src/Mod/Part/TestPartApp.py b/src/Mod/Part/TestPartApp.py index 6694f26a45..1031c71b80 100644 --- a/src/Mod/Part/TestPartApp.py +++ b/src/Mod/Part/TestPartApp.py @@ -170,7 +170,9 @@ class PartTestBSplineCurve(unittest.TestCase): box.getElement("InvalidName") with self.assertRaises(ValueError): box.getElement("Face6_abc") - with self.assertRaises(Part.OCCError): + # getSubTopoShape now catches this before it gets to OCC, so the error changes: + # with self.assertRaises(Part.OCCError): + with self.assertRaises(IndexError): box.getElement("Face7") def tearDown(self): From 72669ab6a6cc2fdaba7c164c2a46d64026d5544b Mon Sep 17 00:00:00 2001 From: bgbsww Date: Tue, 12 Mar 2024 11:20:56 -0400 Subject: [PATCH 2/3] Toponaming/Part: clean and test attacher --- src/Mod/Part/App/Attacher.cpp | 1250 +++++++++++++++---------- src/Mod/Part/App/Attacher.h | 16 +- tests/src/Mod/Part/App/Attacher.cpp | 104 ++ tests/src/Mod/Part/App/CMakeLists.txt | 1 + tests/src/Mod/Part/App/TopoShape.cpp | 18 + 5 files changed, 881 insertions(+), 508 deletions(-) create mode 100644 tests/src/Mod/Part/App/Attacher.cpp diff --git a/src/Mod/Part/App/Attacher.cpp b/src/Mod/Part/App/Attacher.cpp index f4a048e697..35ede72cdc 100644 --- a/src/Mod/Part/App/Attacher.cpp +++ b/src/Mod/Part/App/Attacher.cpp @@ -930,76 +930,87 @@ void AttachEngine::verifyReferencesAreSafe(const App::PropertyLinkSubList &refer std::vector AttachEngine::getRefObjects() const { std::vector objs; - if(objNames.empty()) + if (objNames.empty()) { return objs; + } auto doc = App::GetApplication().getDocument(docName.c_str()); - if(!doc) - FC_THROWM(AttachEngineException,"AttachEngine: document '" << docName << "' not found"); + if (!doc) { + FC_THROWM(AttachEngineException, "AttachEngine: document '" << docName << "' not found"); + } objs.reserve(objNames.size()); - for(auto &name : objNames) { + for (auto& name : objNames) { objs.push_back(doc->getObject(name.c_str())); - if(!objs.back()) + if (!objs.back()) { FC_THROWM(AttachEngineException, "AttachEngine: object '" << docName << "#" << name << "' not found"); + } } return objs; } -Base::Placement AttachEngine::calculateAttachedPlacement( - const Base::Placement &origPlacement, bool *subChanged) +Base::Placement AttachEngine::calculateAttachedPlacement(const Base::Placement& origPlacement, + bool* subChanged) { - std::map > subChanges; - int i=-1; + std::map> subChanges; + int i = -1; auto objs = getRefObjects(); - for(auto obj : objs) { + for (auto obj : objs) { ++i; - auto &sub = subnames[i]; - auto &shadow = shadowSubs[i]; - if(shadow.empty() || !Data::hasMissingElement(sub.c_str())) + auto& sub = subnames[i]; + auto& shadow = shadowSubs[i]; + if (shadow.empty() || !Data::hasMissingElement(sub.c_str())) { continue; - auto related = Part::Feature::getRelatedElements(obj,shadow.c_str(),Part::HistoryTraceType::followTypeChange,false); - if(related.size()) { - auto &res = subChanges[i]; + } + auto related = Part::Feature::getRelatedElements(obj, + shadow.c_str(), + Part::HistoryTraceType::followTypeChange, + false); + if (related.size()) { + auto& res = subChanges[i]; res.first = Data::ComplexGeoData::elementMapPrefix(); related.front().name.appendToBuffer(res.first); res.second.clear(); related.front().index.appendToStringBuffer(res.second); - } else { + } + else { std::string name = Data::oldElementName(shadow.c_str()); if (name.size()) { - auto &res = subChanges[i]; + auto& res = subChanges[i]; res.first.clear(); res.second = name; - }else + } + else { subnames[i] = shadow; + } } } - if(subChanges.size()) { + if (subChanges.size()) { // In case there is topological name changes, we only auto change the // subname if the calculated placement stays the same. If not, just // proceed as normal, which will throw exception and catch user's // attention. auto subs = subnames; - for(auto &v : subChanges) + for (auto& v : subChanges) { subs[v.first] = v.second.second; - auto pla = _calculateAttachedPlacement(objs,subs,origPlacement); + } + auto pla = _calculateAttachedPlacement(objs, subs, origPlacement); // check equal placement with some tolerance - if(pla.getPosition().IsEqual(origPlacement.getPosition(),1e-7) - && pla.getRotation().isSame(origPlacement.getRotation(),1e-12)) - { + if (pla.getPosition().IsEqual(origPlacement.getPosition(), 1e-7) + && pla.getRotation().isSame(origPlacement.getRotation(), 1e-12)) { // Only make changes if the caller supplies 'subChanged', because // otherwise it means the caller just want to do an immutable test. // See AttachExtension::isAttacherActive(). - if(subChanged) { + if (subChanged) { *subChanged = true; subnames = std::move(subs); - for(auto &v : subChanges) + for (auto& v : subChanges) { shadowSubs[v.first] = v.second.first; + } } return pla; } } - return _calculateAttachedPlacement(objs,subnames,origPlacement); + return _calculateAttachedPlacement(objs, subnames, origPlacement); } @@ -1116,49 +1127,58 @@ AttachEngine3D* AttachEngine3D::copy() const return p; } -Base::Placement AttachEngine3D::_calculateAttachedPlacement( - const std::vector&objs, - const std::vector &subs, - const Base::Placement &origPlacement) const +Base::Placement +AttachEngine3D::_calculateAttachedPlacement(const std::vector& objs, + const std::vector& subs, + const 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 + 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(objs,subs, parts, shapes, copiedShapeStorage, types); + readLinks(objs, subs, parts, shapes, copiedShapeStorage, types); - if (parts.size() == 0) + 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 + // 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 + // 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! + // should have been filtered out already! break; - case mmTranslate:{ - if (shapes.size() < 1) - throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: no subobjects specified (need one vertex)."); - const TopoDS_Shape &sh = *shapes[0]; - if (sh.IsNull()) - throw Base::ValueError("Null shape in AttachEngine3D::calculateAttachedPlacement()!"); - if (sh.ShapeType() != TopAbs_VERTEX) - throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: no subobjects specified (need one vertex)."); - gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(sh)); + case mmTranslate: { + if (shapes.size() < 1) { + throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: no subobjects " + "specified (need one vertex)."); + } + const TopoDS_Shape& sh = *shapes[0]; + if (sh.IsNull()) { + throw Base::ValueError( + "Null shape in AttachEngine3D::calculateAttachedPlacement()!"); + } + if (sh.ShapeType() != TopAbs_VERTEX) { + throw Base::ValueError("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->attachmentOffset.getPosition()); @@ -1167,47 +1187,54 @@ Base::Placement AttachEngine3D::_calculateAttachedPlacement( } break; case mmObjectXY: case mmObjectXZ: - case mmObjectYZ:{ - //DeepSOIC: could have been done much more efficiently, but I'm lazy... + 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); + 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]); + 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:{ + switch (adapt.GetType()) { + case GeomAbs_Ellipse: { gp_Elips cc = adapt.Ellipse(); pos = gp_Ax3(cc.Position()); - }break; - case GeomAbs_Hyperbola:{ + } break; + case GeomAbs_Hyperbola: { gp_Hypr cc = adapt.Hyperbola(); pos = gp_Ax3(cc.Position()); - }break; - case GeomAbs_Parabola:{ + } break; + case GeomAbs_Parabola: { gp_Parab cc = adapt.Parabola(); pos = gp_Ax3(cc.Position()); - }break; + } break; default: - assert(0);//conics should have been filtered out by testing shape type in the above if. + 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::ValueError("AttachEngine3D::calculateAttachedPlacement: need either a conic section edge, or a whole object for ObjectXY-like modes."); + } + else { + throw Base::ValueError( + "AttachEngine3D::calculateAttachedPlacement: need either a conic section edge, " + "or a whole object for ObjectXY-like modes."); } - switch (mmode){ + switch (mmode) { case mmObjectXY: SketchNormal = dirZ; SketchXAxis = gp_Vec(dirX); @@ -1225,49 +1252,65 @@ Base::Placement AttachEngine3D::_calculateAttachedPlacement( } } break; - case mmInertialCS:{ + case mmInertialCS: { GProp_GProps gpr = AttachEngine::getInertialPropsOfShape(shapes); GProp_PrincipalProps pr = gpr.PrincipalProperties(); - if (pr.HasSymmetryPoint()) - throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement:InertialCS: inertia tensor is trivial, principal axes are undefined."); - if (pr.HasSymmetryAxis()){ - Base::Console().Warning("AttachEngine3D::calculateAttachedPlacement:InertialCS: inertia tensor has axis of symmetry. Second and third axes of inertia are undefined.\n"); - //find defined axis, and use it as Z axis - //situation: we have two moments that are almost equal, and one - //that is substantially different. The one that is different - //corresponds to a defined axis. We'll identify the different one by - //comparing differences. + if (pr.HasSymmetryPoint()) { + throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement:InertialCS: " + "inertia tensor is trivial, principal axes are undefined."); + } + if (pr.HasSymmetryAxis()) { + Base::Console().Warning( + "AttachEngine3D::calculateAttachedPlacement:InertialCS: inertia tensor has " + "axis of symmetry. Second and third axes of inertia are undefined.\n"); + // find defined axis, and use it as Z axis + // situation: we have two moments that are almost equal, and one + // that is substantially different. The one that is different + // corresponds to a defined axis. We'll identify the different one by + // comparing differences. Standard_Real I1, I2, I3; - pr.Moments(I1,I2,I3); + pr.Moments(I1, I2, I3); Standard_Real d12, d23, d31; - d12 = fabs(I1-I2); - d23 = fabs(I2-I3); - d31 = fabs(I3-I1); - if(d12 < d23 && d12 < d31){ + d12 = fabs(I1 - I2); + d23 = fabs(I2 - I3); + d31 = fabs(I3 - I1); + if (d12 < d23 && d12 < d31) { SketchNormal = pr.ThirdAxisOfInertia(); - } else if (d23 < d31 && d23 < d12){ + } + else if (d23 < d31 && d23 < d12) { SketchNormal = pr.FirstAxisOfInertia(); - } else { + } + else { SketchNormal = pr.SecondAxisOfInertia(); } - } else { + } + else { SketchNormal = pr.FirstAxisOfInertia(); SketchXAxis = pr.SecondAxisOfInertia(); } SketchBasePoint = gpr.CentreOfMass(); - }break; - case mmFlatFace:{ - if (shapes.size() < 1) - throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: no subobjects specified (needed one planar face)."); + } break; + case mmFlatFace: { + if (shapes.size() < 1) { + throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: no subobjects " + "specified (needed one planar face)."); + } - TopoDS_Face face; + TopoDS_Face face; gp_Pln plane; bool Reverse = false; - try { face = TopoDS::Face(*(shapes[0])); } catch(...) {} + try { + face = TopoDS::Face(*(shapes[0])); + } + catch (...) { + } if (face.IsNull()) { - if (!TopoShape(*shapes[0]).findPlane(plane)) - throw Base::ValueError("No planar face in AttachEngine3D::calculateAttachedPlacement()!"); - } else { + if (!TopoShape(*shapes[0]).findPlane(plane)) { + throw Base::ValueError( + "No planar face in AttachEngine3D::calculateAttachedPlacement()!"); + } + } + else { BRepAdaptor_Surface adapt(face); if (adapt.GetType() == GeomAbs_Plane) { plane = adapt.Plane(); @@ -1276,15 +1319,19 @@ Base::Placement AttachEngine3D::_calculateAttachedPlacement( TopLoc_Location loc; Handle(Geom_Surface) surf = BRep_Tool::Surface(face, loc); GeomLib_IsPlanarSurface check(surf); - if (check.IsPlanar()) - plane = check.Plan(); - else - throw Base::ValueError("No planar face in AttachEngine3D::calculateAttachedPlacement()!"); - } + if (check.IsPlanar()) { + plane = check.Plan(); + } + else { + throw Base::ValueError( + "No planar face in AttachEngine3D::calculateAttachedPlacement()!"); + } + } - if (face.Orientation() == TopAbs_REVERSED) - Reverse = true; - } + if (face.Orientation() == TopAbs_REVERSED) { + Reverse = true; + } + } Standard_Boolean ok = plane.Direct(); if (!ok) { @@ -1293,44 +1340,61 @@ Base::Placement AttachEngine3D::_calculateAttachedPlacement( Reverse = !Reverse; } gp_Ax1 Normal = plane.Axis(); - if (Reverse) - Normal.Reverse(); - SketchNormal = Normal.Direction(); + if (Reverse) { + Normal.Reverse(); + } + SketchNormal = Normal.Direction(); - Handle (Geom_Plane) gPlane = new Geom_Plane(plane); - GeomAPI_ProjectPointOnSurf projector(refOrg,gPlane); + 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::ValueError("AttachEngine3D::calculateAttachedPlacement: not enough subshapes (need one false and one vertex)."); + if (shapes.size() < 2) { + throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: not enough " + "subshapes (need one false and one vertex)."); + } - bool bThruVertex = false; + bool bThruVertex = false; if (shapes[0]->ShapeType() == TopAbs_VERTEX) { - std::swap(shapes[0],shapes[1]); + std::swap(shapes[0], shapes[1]); bThruVertex = true; } TopoDS_Face face; - try { face = TopoDS::Face(*(shapes[0])); } catch(...) {} - if (face.IsNull()) - throw Base::ValueError("Null face in AttachEngine3D::calculateAttachedPlacement()!"); + try { + face = TopoDS::Face(*(shapes[0])); + } + catch (...) { + } + if (face.IsNull()) { + throw Base::ValueError( + "Null face in AttachEngine3D::calculateAttachedPlacement()!"); + } - TopoDS_Vertex vertex; - try { vertex = TopoDS::Vertex(*(shapes[1])); } catch(...) {} - if (vertex.IsNull()) - throw Base::ValueError("Null vertex in AttachEngine3D::calculateAttachedPlacement()!"); + TopoDS_Vertex vertex; + try { + vertex = TopoDS::Vertex(*(shapes[1])); + } + catch (...) { + } + if (vertex.IsNull()) { + throw Base::ValueError( + "Null vertex in AttachEngine3D::calculateAttachedPlacement()!"); + } - Handle (Geom_Surface) hSurf = BRep_Tool::Surface(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::ValueError("AttachEngine3D::calculateAttachedPlacement: projecting point onto surface failed."); + if (projector.NbPoints() == 0) { + throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: projecting " + "point onto surface failed."); + } - projector.LowerDistanceParameters(u, v); + projector.LowerDistanceParameters(u, v); BRepAdaptor_Surface surf(face); BRepLProp_SLProps prop(surf, u, v, 1, Precision::Confusion()); gp_Dir dirX; @@ -1338,15 +1402,18 @@ Base::Placement AttachEngine3D::_calculateAttachedPlacement( Tools::getNormal(face, u, v, Precision::Confusion(), SketchNormal, done); - if (!done) - throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: finding normal to surface at projected point failed."); + if (!done) { + throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: finding normal " + "to surface at projected point failed."); + } - // if getNormal succeeds, at least one of the tangent is defined + // if getNormal succeeds, at least one of the tangent is defined if (prop.IsTangentUDefined()) { prop.TangentU(dirX); - if (face.Orientation() == TopAbs_REVERSED) - dirX.Reverse(); - } + if (face.Orientation() == TopAbs_REVERSED) { + dirX.Reverse(); + } + } // here the orientation of dirX is managed by SketchNormal orientation else { gp_Dir dirY; @@ -1354,11 +1421,12 @@ Base::Placement AttachEngine3D::_calculateAttachedPlacement( dirX = dirY.Crossed(SketchNormal); } - SketchXAxis = gp_Vec(dirX).Reversed();//yields upside-down sketches less often. + SketchXAxis = gp_Vec(dirX).Reversed(); // yields upside-down sketches less often. if (bThruVertex) { SketchBasePoint = p; - } else { + } + else { SketchBasePoint = projector.NearestPoint(); } } break; @@ -1367,124 +1435,165 @@ Base::Placement AttachEngine3D::_calculateAttachedPlacement( case mmFrenetTN: case mmFrenetTB: case mmRevolutionSection: - case mmConcentric: {//all alignments to point on curve - if (shapes.size() < 1) - throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: no subshapes specified (need one edge, and an optional vertex)."); + case mmConcentric: { // all alignments to point on curve + if (shapes.size() < 1) { + throw Base::ValueError("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]); + bool bThruVertex = false; + if (shapes[0]->ShapeType() == TopAbs_VERTEX && shapes.size() >= 2) { + std::swap(shapes[0], shapes[1]); bThruVertex = true; } TopoDS_Edge path; - try { path = TopoDS::Edge(*(shapes[0])); } catch(...) {} - if (path.IsNull()) - throw Base::ValueError("Null path in AttachEngine3D::calculateAttachedPlacement()!"); + try { + path = TopoDS::Edge(*(shapes[0])); + } + catch (...) { + } + if (path.IsNull()) { + throw Base::ValueError( + "Null path in AttachEngine3D::calculateAttachedPlacement()!"); + } - BRepAdaptor_Curve adapt(path); + 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. + 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 + // 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; - try { TopoDS::Vertex(*(shapes[1])); } catch(...) {} - if (vertex.IsNull()) - throw Base::ValueError("Null vertex in AttachEngine3D::calculateAttachedPlacement()!"); - p_in = BRep_Tool::Pnt(vertex); + try { + TopoDS::Vertex(*(shapes[1])); + } + catch (...) { + } + if (vertex.IsNull()) { + throw Base::ValueError( + "Null vertex in AttachEngine3D::calculateAttachedPlacement()!"); + } + p_in = BRep_Tool::Pnt(vertex); - Handle (Geom_Curve) hCurve = BRep_Tool::Curve(path, u1, u2); + Handle(Geom_Curve) hCurve = BRep_Tool::Curve(path, u1, u2); - GeomAPI_ProjectPointOnCurve projector (p_in, hCurve); + 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); + 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 + } + 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 } - switch (mmode){ + switch (mmode) { case mmFrenetNB: case mmRevolutionSection: - SketchNormal = T.Reversed();//to avoid sketches upside-down for regular curves like circles + 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::ValueError("AttachEngine3D::calculateAttachedPlacement: Frenet-Serret normal is undefined. Can't align to TN plane."); - SketchNormal = B; + if (N.Magnitude() == 0.0) { + throw Base::ValueError( + "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::ValueError("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 at it so it is convex. + if (N.Magnitude() == 0.0) { + throw Base::ValueError( + "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 at it so it is convex. SketchXAxis = T; break; default: - assert(0);//mode forgotten? + 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::ValueError("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 + // make sketch origin be at center of osculating circle + if (N.Magnitude() == 0.0) { + throw Base::ValueError( + "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. + // 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. + } + 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. } } break; @@ -1494,60 +1603,74 @@ Base::Placement AttachEngine3D::_calculateAttachedPlacement( std::vector points; for (std::size_t i = 0; i < shapes.size(); i++) { - const TopoDS_Shape &sh = *shapes[i]; - if (sh.IsNull()) - throw Base::ValueError("Null shape in AttachEngine3D::calculateAttachedPlacement()!"); - if (sh.ShapeType() == TopAbs_VERTEX){ - const TopoDS_Vertex &v = TopoDS::Vertex(sh); + const TopoDS_Shape& sh = *shapes[i]; + if (sh.IsNull()) { + throw Base::ValueError( + "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); + } + 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) ){ + 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) { + break; + } + } - if(points.size()<3) - throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: less than 3 points are specified, cannot derive the plane."); + if (points.size() < 3) { + throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: less than 3 " + "points are specified, cannot derive the plane."); + } - gp_Pnt p0 = points[0]; + 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::ValueError("AttachEngine3D::calculateAttachedPlacement: some of 3 points are coincident. Can't make a plane"); - vec01.Normalize(); + gp_Vec vec01(p0, p1); + gp_Vec vec02(p0, p2); + if (vec01.Magnitude() < Precision::Confusion() + || vec02.Magnitude() < Precision::Confusion()) { + throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: some of 3 " + "points are coincident. Can't make a plane"); + } + vec01.Normalize(); vec02.Normalize(); - gp_Vec norm ; + gp_Vec norm; if (mmode == mmThreePointsPlane) { norm = vec01.Crossed(vec02); - if (norm.Magnitude() < Precision::Confusion()) - throw Base::ValueError("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::ValueError("AttachEngine3D::calculateAttachedPlacement: points are collinear. Can't make a plane"); - //SketchBasePoint = (p0+p1)/2.0 + if (norm.Magnitude() < Precision::Confusion()) { + throw Base::ValueError("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::ValueError("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); + Handle(Geom_Plane) gPlane = new Geom_Plane(p0, gp_Dir(norm)); + GeomAPI_ProjectPointOnSurf projector(p2, gPlane); SketchBasePoint = projector.NearestPoint(); - } norm.Normalize(); @@ -1561,79 +1684,94 @@ Base::Placement AttachEngine3D::_calculateAttachedPlacement( // edgeA to edgeB by folding the sheet along axes. All edges are // expected to be in one plane. - if (shapes.size()<4) - throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: not enough shapes (need 4 lines: edgeA, axisA, axisB, edgeB)."); + if (shapes.size() < 4) { + throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: not enough " + "shapes (need 4 lines: edgeA, axisA, axisB, edgeB)."); + } - //extract the four lines + // extract the four lines const TopoDS_Edge* edges[4]; BRepAdaptor_Curve adapts[4]; gp_Lin lines[4]; - for(int i=0 ; i<4 ; i++){ - try { edges[i] = &TopoDS::Edge(*(shapes[i])); } catch(...){} - if (edges[i]->IsNull()) - throw Base::ValueError("Null edge in AttachEngine3D::calculateAttachedPlacement()!"); + for (int i = 0; i < 4; i++) { + try { + edges[i] = &TopoDS::Edge(*(shapes[i])); + } + catch (...) { + } + if (edges[i]->IsNull()) { + throw Base::ValueError( + "Null edge in AttachEngine3D::calculateAttachedPlacement()!"); + } - adapts[i] = BRepAdaptor_Curve(*(edges[i])); - if (adapts[i].GetType() != GeomAbs_Line) - throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: Folding - non-straight edge."); - lines[i] = adapts[i].Line(); + adapts[i] = BRepAdaptor_Curve(*(edges[i])); + if (adapts[i].GetType() != GeomAbs_Line) { + throw Base::ValueError( + "AttachEngine3D::calculateAttachedPlacement: Folding - non-straight edge."); + } + lines[i] = adapts[i].Line(); } - //figure out the common starting point (variable p) + // 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 + 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()){ + 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::ValueError("AttachEngine3D::calculateAttachedPlacement: Folding - edges to not share a vertex."); } - for (int i = 2 ; i<4 ; i++){ + 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::ValueError("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::ValueError("AttachEngine3D::calculateAttachedPlacement: Folding - edges to not share a vertex."); - } + if (p.Distance(p1) < Precision::Confusion()) { + signs[i] = +1.0; + } + else if (p.Distance(p2) < Precision::Confusion()) { + signs[i] = -1.0; + } + else { + throw Base::ValueError("AttachEngine3D::calculateAttachedPlacement: Folding - " + "edges to not share a vertex."); + } + } gp_Vec dirs[4]; - for(int i=0 ; i<4 ; i++){ + 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] - ); + 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); + // 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]; @@ -1657,76 +1795,88 @@ Base::Placement AttachEngine3D::_calculateAttachedPlacement( }; const char* orderString = orderStrings[mmode - mmOZX]; - enum dirIndex { + enum dirIndex + { X, Y, Z }; int order[3]; - for(int i = 0; i < 3; ++i){ + for (int i = 0; i < 3; ++i) { order[i] = orderString[i] - 'X'; } - if (shapes.size() < 2) - THROWM(Base::ValueError, "AttachEngine3D::calculateAttachedPlacement: not enough shapes linked (at least two are required)."); + if (shapes.size() < 2) { + THROWM(Base::ValueError, + "AttachEngine3D::calculateAttachedPlacement: not enough shapes linked (at " + "least two are required)."); + } - gp_Vec dirs[3]; + gp_Vec dirs[3]; - //read out origin - if (shapes[0]->IsNull()) - THROWM(Base::TypeError, "AttachEngine3D::calculateAttachedPlacement: null shape!") - if (shapes[0]->ShapeType() != TopAbs_VERTEX) - THROWM(Base::TypeError, "AttachEngine3D::calculateAttachedPlacement: first reference must be a vertex, it's not") - SketchBasePoint = BRep_Tool::Pnt(TopoDS::Vertex(*(shapes[0]))); - - //read out axes directions - for(size_t i = 1; i < 3 && i < shapes.size(); ++i){ - if (shapes[i]->IsNull()) + // read out origin + if (shapes[0]->IsNull()) { THROWM(Base::TypeError, "AttachEngine3D::calculateAttachedPlacement: null shape!") - if (shapes[i]->ShapeType() == TopAbs_VERTEX){ + } + if (shapes[0]->ShapeType() != TopAbs_VERTEX) { + THROWM(Base::TypeError, + "AttachEngine3D::calculateAttachedPlacement: first reference must be a " + "vertex, it's not") + } + SketchBasePoint = BRep_Tool::Pnt(TopoDS::Vertex(*(shapes[0]))); + + // read out axes directions + for (size_t i = 1; i < 3 && i < shapes.size(); ++i) { + if (shapes[i]->IsNull()) { + THROWM(Base::TypeError, + "AttachEngine3D::calculateAttachedPlacement: null shape!") + } + if (shapes[i]->ShapeType() == TopAbs_VERTEX) { gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(*(shapes[i]))); - dirs[order[i-1]] = gp_Vec(SketchBasePoint, p); - } else if (shapes[i]->ShapeType() == TopAbs_EDGE){ - const TopoDS_Edge &e = TopoDS::Edge(*(shapes[i])); + dirs[order[i - 1]] = gp_Vec(SketchBasePoint, p); + } + else if (shapes[i]->ShapeType() == TopAbs_EDGE) { + const TopoDS_Edge& e = TopoDS::Edge(*(shapes[i])); BRepAdaptor_Curve crv(e); double u1 = crv.FirstParameter(); double u2 = crv.LastParameter(); - if ( Precision::IsInfinite(u1) - || Precision::IsInfinite(u2) ){ + if (Precision::IsInfinite(u1) || Precision::IsInfinite(u2)) { u1 = 0.0; u2 = 1.0; } gp_Pnt p1 = crv.Value(u1); gp_Pnt p2 = crv.Value(u2); - dirs[order[i-1]] = gp_Vec(p1,p2); + dirs[order[i - 1]] = gp_Vec(p1, p2); } } - //make the placement - Base::Rotation rot = - Base::Rotation::makeRotationByAxes( - Base::Vector3d(dirs[0].X(), dirs[0].Y(), dirs[0].Z()), - Base::Vector3d(dirs[1].X(), dirs[1].Y(), dirs[1].Z()), - Base::Vector3d(dirs[2].X(), dirs[2].Y(), dirs[2].Z()), - orderString - ); - if(this->mapReverse){ - rot = rot * Base::Rotation(Base::Vector3d(0,1,0),D_PI); + // make the placement + Base::Rotation rot = Base::Rotation::makeRotationByAxes( + Base::Vector3d(dirs[0].X(), dirs[0].Y(), dirs[0].Z()), + Base::Vector3d(dirs[1].X(), dirs[1].Y(), dirs[1].Z()), + Base::Vector3d(dirs[2].X(), dirs[2].Y(), dirs[2].Z()), + orderString); + if (this->mapReverse) { + rot = rot * Base::Rotation(Base::Vector3d(0, 1, 0), D_PI); } - Base::Placement plm = - Base::Placement(Base::Vector3d(SketchBasePoint.X(), SketchBasePoint.Y(), SketchBasePoint.Z()), rot); + Base::Placement plm = Base::Placement( + Base::Vector3d(SketchBasePoint.X(), SketchBasePoint.Y(), SketchBasePoint.Z()), + rot); plm *= this->attachmentOffset; return plm; } break; default: throwWrongMode(mmode); - }//switch (MapMode) + } // switch (MapMode) //----------calculate placement, based on point and vector Base::Placement plm = - this->placementFactory(SketchNormal, SketchXAxis, SketchBasePoint, gp_Pnt(), + this->placementFactory(SketchNormal, + SketchXAxis, + SketchBasePoint, + gp_Pnt(), /*useRefOrg_Line = */ false, /*useRefOrg_Plane = */ false, /*makeYVertical = */ false, @@ -1855,19 +2005,20 @@ AttachEngineLine *AttachEngineLine::copy() const return p; } -Base::Placement AttachEngineLine::_calculateAttachedPlacement( - const std::vector&objs, - const std::vector &subs, - const Base::Placement &origPlacement) const +Base::Placement +AttachEngineLine::_calculateAttachedPlacement(const std::vector& objs, + const std::vector& subs, + const Base::Placement& origPlacement) const { eMapMode mmode = this->mapMode; - //modes that are mirrors of attacher3D: + // modes that are mirrors of attacher3D: bool bReUsed = true; Base::Placement presuperPlacement; - switch(mmode){ + switch (mmode) { case mmDeactivated: - throw ExceptionCancel();//to be handled in positionBySupport, to not do anything if disabled + throw ExceptionCancel(); // to be handled in positionBySupport, to not do anything if + // disabled case mm1AxisX: mmode = mmObjectYZ; break; @@ -1879,11 +2030,9 @@ Base::Placement AttachEngineLine::_calculateAttachedPlacement( break; case mm1AxisCurv: mmode = mmRevolutionSection; - //the line should go along Y, not Z + // 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) ) - ); + Base::Rotation(Base::Vector3d(0.0, 0.0, 1.0), Base::Vector3d(0.0, 1.0, 0.0))); break; case mm1Binormal: mmode = mmFrenetTN; @@ -1903,186 +2052,254 @@ Base::Placement AttachEngineLine::_calculateAttachedPlacement( } Base::Placement plm; - if (!bReUsed){ + if (!bReUsed) { std::vector parts; std::vector shapes; std::vector copiedShapeStorage; std::vector types; - readLinks(objs,subs, parts, shapes, copiedShapeStorage, types); + readLinks(objs, subs, parts, shapes, copiedShapeStorage, types); - if (parts.size() == 0) + if (parts.size() == 0) { throw ExceptionCancel(); + } - //common stuff for all map modes - gp_Pnt refOrg (0.0,0.0,0.0); + // 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: + // 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 + gp_Pnt LineBasePoint; // the point the line goes through switch (mmode) { case mm1AxisInertia1: case mm1AxisInertia2: - case mm1AxisInertia3:{ + case mm1AxisInertia3: { GProp_GProps gpr = AttachEngine::getInertialPropsOfShape(shapes); LineBasePoint = gpr.CentreOfMass(); GProp_PrincipalProps pr = gpr.PrincipalProperties(); - if (pr.HasSymmetryPoint()) - throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement:AxisOfInertia: inertia tensor is trivial, principal axes are undefined."); + if (pr.HasSymmetryPoint()) { + throw Base::ValueError( + "AttachEngineLine::calculateAttachedPlacement:AxisOfInertia: inertia " + "tensor is trivial, principal axes are undefined."); + } - //query moments, to use them to check if axis is defined - //See AttachEngine3D::calculateAttachedPlacement:case mmInertial for comment explaining these comparisons + // query moments, to use them to check if axis is defined + // See AttachEngine3D::calculateAttachedPlacement:case mmInertial for comment + // explaining these comparisons Standard_Real I1, I2, I3; - pr.Moments(I1,I2,I3); + pr.Moments(I1, I2, I3); Standard_Real d12, d23, d31; - d12 = fabs(I1-I2); - d23 = fabs(I2-I3); - d31 = fabs(I3-I1); + d12 = fabs(I1 - I2); + d23 = fabs(I2 - I3); + d31 = fabs(I3 - I1); - if (mmode == mm1AxisInertia1){ + if (mmode == mm1AxisInertia1) { LineDir = pr.FirstAxisOfInertia(); - if (pr.HasSymmetryAxis() && !(d23 < d31 && d23 < d12)) - throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement:AxisOfInertia: inertia tensor has axis of symmetry; first axis of inertia is undefined."); - } else if (mmode == mm1AxisInertia2) { + if (pr.HasSymmetryAxis() && !(d23 < d31 && d23 < d12)) { + throw Base::ValueError( + "AttachEngineLine::calculateAttachedPlacement:AxisOfInertia: inertia " + "tensor has axis of symmetry; first axis of inertia is undefined."); + } + } + else if (mmode == mm1AxisInertia2) { LineDir = pr.SecondAxisOfInertia(); - if (pr.HasSymmetryAxis() && !(d31 < d12 && d31 < d23)) - throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement:AxisOfInertia: inertia tensor has axis of symmetry; second axis of inertia is undefined."); - } else if (mmode == mm1AxisInertia3) { + if (pr.HasSymmetryAxis() && !(d31 < d12 && d31 < d23)) { + throw Base::ValueError( + "AttachEngineLine::calculateAttachedPlacement:AxisOfInertia: inertia " + "tensor has axis of symmetry; second axis of inertia is undefined."); + } + } + else if (mmode == mm1AxisInertia3) { LineDir = pr.ThirdAxisOfInertia(); - if (pr.HasSymmetryAxis() && !(d12 < d23 && d12 < d31)) - throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement:AxisOfInertia: inertia tensor has axis of symmetry; third axis of inertia is undefined."); - } - }break; - case mm1TwoPoints:{ + if (pr.HasSymmetryAxis() && !(d12 < d23 && d12 < d31)) { + throw Base::ValueError( + "AttachEngineLine::calculateAttachedPlacement:AxisOfInertia: inertia " + "tensor has axis of symmetry; third axis of inertia is undefined."); + } + } + } 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::ValueError("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); - if (sh.ShapeType() == TopAbs_VERTEX){ - const TopoDS_Vertex &v = TopoDS::Vertex(sh); + const TopoDS_Shape& sh = *shapes[i]; + if (sh.IsNull()) { + throw Base::ValueError( + "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); + } + 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) ){ + 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) { + break; + } + } - if(points.size()<2) - throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: less than 2 points are specified, cannot derive the line."); + if (points.size() < 2) { + throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: less " + "than 2 points are specified, cannot derive the line."); + } - gp_Pnt p0 = points[0]; + gp_Pnt p0 = points[0]; gp_Pnt p1 = points[1]; - LineDir = gp_Dir(gp_Vec(p0,p1)); + LineDir = gp_Dir(gp_Vec(p0, p1)); LineBasePoint = p0; - }break; + } break; case mm1Asymptote1: - case mm1Asymptote2:{ - if (shapes[0]->IsNull()) - throw Base::ValueError("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); - TopoDS_Edge e; - try { e = TopoDS::Edge(*(shapes[0])); } catch(...) {} - if (e.IsNull()) - throw Base::ValueError("Null edge in AttachEngineLine::calculateAttachedPlacement()!"); - BRepAdaptor_Curve adapt (e); - if (adapt.GetType() != GeomAbs_Hyperbola) - throw Base::ValueError("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 mm1Asymptote2: { + if (shapes[0]->IsNull()) { + throw Base::ValueError( + "Null shape in AttachEngineLine::calculateAttachedPlacement()!"); + } + TopoDS_Edge e; + try { + e = TopoDS::Edge(*(shapes[0])); + } + catch (...) { + } + if (e.IsNull()) { + throw Base::ValueError( + "Null edge in AttachEngineLine::calculateAttachedPlacement()!"); + } + BRepAdaptor_Curve adapt(e); + if (adapt.GetType() != GeomAbs_Hyperbola) { + throw Base::ValueError( + "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::ValueError("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); - TopoDS_Edge e; - try { e = TopoDS::Edge(*(shapes[0])); } catch(...) {} - if (e.IsNull()) - throw Base::ValueError("Null edge in AttachEngineLine::calculateAttachedPlacement()!"); - BRepAdaptor_Curve adapt (e); - gp_Ax1 dx1, dx2;//vars to receive directrices - switch(adapt.GetType()){ - case GeomAbs_Ellipse:{ + case mm1Directrix2: { + if (shapes[0]->IsNull()) { + throw Base::ValueError( + "Null shape in AttachEngineLine::calculateAttachedPlacement()!"); + } + TopoDS_Edge e; + try { + e = TopoDS::Edge(*(shapes[0])); + } + catch (...) { + } + if (e.IsNull()) { + throw Base::ValueError( + "Null edge in AttachEngineLine::calculateAttachedPlacement()!"); + } + BRepAdaptor_Curve adapt(e); + gp_Ax1 dx1, dx2; // vars to receive directrices + switch (adapt.GetType()) { + case GeomAbs_Ellipse: { gp_Elips cc = adapt.Ellipse(); dx1 = cc.Directrix1(); dx2 = cc.Directrix2(); - }break; - case GeomAbs_Hyperbola:{ + } break; + case GeomAbs_Hyperbola: { gp_Hypr cc = adapt.Hyperbola(); dx1 = cc.Directrix1(); dx2 = cc.Directrix2(); - }break; - case GeomAbs_Parabola:{ + } break; + case GeomAbs_Parabola: { gp_Parab cc = adapt.Parabola(); dx1 = cc.Directrix(); - if (mmode == mm1Directrix2) - throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: Parabola has no second directrix"); - }break; + if (mmode == mm1Directrix2) { + throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: " + "Parabola has no second directrix"); + } + } break; default: - throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: referenced edge is not a conic section with a directrix"); + throw Base::ValueError( + "AttachEngineLine::calculateAttachedPlacement: referenced edge is not " + "a conic section with a directrix"); } - if (mmode == mm1Directrix1){ + if (mmode == mm1Directrix1) { LineDir = dx1.Direction(); LineBasePoint = dx1.Location(); - } else { + } + else { LineDir = dx2.Direction(); LineBasePoint = dx2.Location(); } - }break; - case mm1Proximity:{ - if (shapes.size() < 2) - throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: Proximity mode requires two shapes; only one is supplied"); - if (shapes[0]->IsNull()) - throw Base::ValueError("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); - if (shapes[1]->IsNull()) - throw Base::ValueError("Null shape in AttachEngineLine::calculateAttachedPlacement()!"); - BRepExtrema_DistShapeShape distancer (*(shapes[0]), *(shapes[1])); - if (!distancer.IsDone()) - throw Base::ValueError("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); + } break; + case mm1Proximity: { + if (shapes.size() < 2) { + throw Base::ValueError( + "AttachEngineLine::calculateAttachedPlacement: Proximity mode requires two " + "shapes; only one is supplied"); + } + if (shapes[0]->IsNull()) { + throw Base::ValueError( + "Null shape in AttachEngineLine::calculateAttachedPlacement()!"); + } + if (shapes[1]->IsNull()) { + throw Base::ValueError( + "Null shape in AttachEngineLine::calculateAttachedPlacement()!"); + } + BRepExtrema_DistShapeShape distancer(*(shapes[0]), *(shapes[1])); + if (!distancer.IsDone()) { + throw Base::ValueError("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::ValueError("AttachEngineLine::calculateAttachedPlacement: can't make proximity line, because shapes touch or intersect"); - LineDir = gp_Dir(dist); - }break; + gp_Vec dist = gp_Vec(p1, p2); + if (dist.Magnitude() < Precision::Confusion()) { + throw Base::ValueError( + "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, + plm = this->placementFactory(LineDir, + gp_Vec(), + LineBasePoint, + refOrg, /*useRefOrg_Line = */ true); - } else {//re-use 3d mode + } + else { // re-use 3d mode AttachEngine3D attacher3D; attacher3D.setUp(*this); attacher3D.mapMode = mmode; - attacher3D.attachmentOffset = Base::Placement(); //AttachmentOffset is applied separately here, afterwards. So we are resetting it in sub-attacher to avoid applying it twice! - plm = attacher3D._calculateAttachedPlacement(objs,subs,origPlacement); + attacher3D.attachmentOffset = + Base::Placement(); // AttachmentOffset is applied separately here, afterwards. So we + // are resetting it in sub-attacher to avoid applying it twice! + plm = attacher3D._calculateAttachedPlacement(objs, subs, origPlacement); plm *= presuperPlacement; } plm *= this->attachmentOffset; @@ -2133,18 +2350,19 @@ AttachEnginePoint *AttachEnginePoint::copy() const return p; } -Base::Placement AttachEnginePoint::_calculateAttachedPlacement( - const std::vector&objs, - const std::vector &subs, - const Base::Placement &origPlacement) const +Base::Placement +AttachEnginePoint::_calculateAttachedPlacement(const std::vector& objs, + const std::vector& subs, + const Base::Placement& origPlacement) const { eMapMode mmode = this->mapMode; - //modes that are mirrors of attacher3D: + // modes that are mirrors of attacher3D: bool bReUsed = true; - switch(mmode){ + switch (mmode) { case mmDeactivated: - throw ExceptionCancel();//to be handled in positionBySupport, to not do anything if disabled + throw ExceptionCancel(); // to be handled in positionBySupport, to not do anything if + // disabled case mm0Origin: mmode = mmObjectXY; break; @@ -2152,7 +2370,7 @@ Base::Placement AttachEnginePoint::_calculateAttachedPlacement( mmode = mmRevolutionSection; break; case mm0OnEdge: - //todo: prevent thruPoint + // todo: prevent thruPoint mmode = mmNormalToPath; break; default: @@ -2160,104 +2378,136 @@ Base::Placement AttachEnginePoint::_calculateAttachedPlacement( } Base::Placement plm; - if (!bReUsed){ + if (!bReUsed) { std::vector parts; std::vector shapes; std::vector copiedShapeStorage; std::vector types; - readLinks(objs,subs, parts, shapes, copiedShapeStorage, types); + readLinks(objs, subs, parts, shapes, copiedShapeStorage, types); - if (parts.empty()) + if (parts.empty()) { 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 + // 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 mm0Vertex:{ + case mm0Vertex: { std::vector points; - assert(shapes.size()>0); + assert(shapes.size() > 0); - const TopoDS_Shape &sh = *shapes[0]; - if (sh.IsNull()) - throw Base::ValueError("Null shape in AttachEnginePoint::calculateAttachedPlacement()!"); - if (sh.ShapeType() == TopAbs_VERTEX){ - const TopoDS_Vertex &v = TopoDS::Vertex(sh); + const TopoDS_Shape& sh = *shapes[0]; + if (sh.IsNull()) { + throw Base::ValueError( + "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); + } + else if (sh.ShapeType() == TopAbs_EDGE) { + const TopoDS_Edge& e = TopoDS::Edge(sh); BRepAdaptor_Curve crv(e); double u = crv.FirstParameter(); - if(Precision::IsInfinite(u)) - throw Base::ValueError("Edge is infinite"); - BasePoint = crv.Value(u); + if (Precision::IsInfinite(u)) { + throw Base::ValueError("Edge is infinite"); + } + BasePoint = crv.Value(u); } - }break; + } break; case mm0Focus1: - case mm0Focus2:{ - if (shapes[0]->IsNull()) - throw Base::ValueError("Null shape in AttachEnginePoint::calculateAttachedPlacement()!"); - TopoDS_Edge e; - try { e = TopoDS::Edge(*(shapes[0])); } catch(...) {} - if (e.IsNull()) - throw Base::ValueError("Null edge in AttachEnginePoint::calculateAttachedPlacement()!"); - BRepAdaptor_Curve adapt (e); + case mm0Focus2: { + if (shapes[0]->IsNull()) { + throw Base::ValueError( + "Null shape in AttachEnginePoint::calculateAttachedPlacement()!"); + } + TopoDS_Edge e; + try { + e = TopoDS::Edge(*(shapes[0])); + } + catch (...) { + } + if (e.IsNull()) { + throw Base::ValueError( + "Null edge in AttachEnginePoint::calculateAttachedPlacement()!"); + } + BRepAdaptor_Curve adapt(e); gp_Pnt f1, f2; - switch(adapt.GetType()){ - case GeomAbs_Ellipse:{ + switch (adapt.GetType()) { + case GeomAbs_Ellipse: { gp_Elips cc = adapt.Ellipse(); f1 = cc.Focus1(); f2 = cc.Focus2(); - }break; - case GeomAbs_Hyperbola:{ + } break; + case GeomAbs_Hyperbola: { gp_Hypr cc = adapt.Hyperbola(); f1 = cc.Focus1(); f2 = cc.Focus2(); - }break; - case GeomAbs_Parabola:{ + } break; + case GeomAbs_Parabola: { gp_Parab cc = adapt.Parabola(); f1 = cc.Focus(); - if (mmode == mm0Focus2) - throw Base::ValueError("AttachEnginePoint::calculateAttachedPlacement: Parabola has no second focus"); - }break; + if (mmode == mm0Focus2) { + throw Base::ValueError("AttachEnginePoint::calculateAttachedPlacement: " + "Parabola has no second focus"); + } + } break; default: - throw Base::ValueError("AttachEnginePoint::calculateAttachedPlacement: referenced edge is not a conic section with a directrix"); + throw Base::ValueError( + "AttachEnginePoint::calculateAttachedPlacement: referenced edge is not " + "a conic section with a directrix"); } - if (mmode == mm0Focus1) - BasePoint = f1; - else - BasePoint = f2; - }break; + if (mmode == mm0Focus1) { + BasePoint = f1; + } + else { + BasePoint = f2; + } + } break; case mm0ProximityPoint1: - case mm0ProximityPoint2:{ - if (shapes.size() < 2) - throw Base::ValueError("AttachEnginePoint::calculateAttachedPlacement: Proximity mode requires two shapes; only one is supplied"); - if (shapes[0]->IsNull()) - throw Base::ValueError("Null shape in AttachEnginePoint::calculateAttachedPlacement()!"); - if (shapes[1]->IsNull()) - throw Base::ValueError("Null shape in AttachEnginePoint::calculateAttachedPlacement()!"); + case mm0ProximityPoint2: { + if (shapes.size() < 2) { + throw Base::ValueError( + "AttachEnginePoint::calculateAttachedPlacement: Proximity mode requires " + "two shapes; only one is supplied"); + } + if (shapes[0]->IsNull()) { + throw Base::ValueError( + "Null shape in AttachEnginePoint::calculateAttachedPlacement()!"); + } + if (shapes[1]->IsNull()) { + throw Base::ValueError( + "Null shape in AttachEnginePoint::calculateAttachedPlacement()!"); + } - BasePoint = getProximityPoint(mmode, *(shapes[0]), *(shapes[1])); - }break; - case mm0CenterOfMass:{ - GProp_GProps gpr = AttachEngine::getInertialPropsOfShape(shapes); + BasePoint = getProximityPoint(mmode, *(shapes[0]), *(shapes[1])); + } break; + case mm0CenterOfMass: { + GProp_GProps gpr = AttachEngine::getInertialPropsOfShape(shapes); BasePoint = gpr.CentreOfMass(); - }break; + } 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 + 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.attachmentOffset = Base::Placement(); //AttachmentOffset is applied separately here, afterwards. So we are resetting it in sub-attacher to avoid applying it twice! - plm = attacher3D._calculateAttachedPlacement(objs,subs,origPlacement); + attacher3D.attachmentOffset = + Base::Placement(); // AttachmentOffset is applied separately here, afterwards. So we + // are resetting it in sub-attacher to avoid applying it twice! + plm = attacher3D._calculateAttachedPlacement(objs, subs, origPlacement); } plm *= this->attachmentOffset; return plm; diff --git a/src/Mod/Part/App/Attacher.h b/src/Mod/Part/App/Attacher.h index 1ed210f61a..b2f6c04ff0 100644 --- a/src/Mod/Part/App/Attacher.h +++ b/src/Mod/Part/App/Attacher.h @@ -440,10 +440,10 @@ class PartExport AttachEngine3D : public AttachEngine public: AttachEngine3D(); AttachEngine3D* copy() const override; - virtual Base::Placement _calculateAttachedPlacement( + Base::Placement _calculateAttachedPlacement( const std::vector &objs, const std::vector &subs, - const Base::Placement &origPlacement) const; + const Base::Placement &origPlacement) const override; private: double calculateFoldAngle(gp_Vec axA, gp_Vec axB, gp_Vec edA, gp_Vec edB) const; }; @@ -455,10 +455,10 @@ class PartExport AttachEnginePlane : public AttachEngine public: AttachEnginePlane(); AttachEnginePlane* copy() const override; - virtual Base::Placement _calculateAttachedPlacement( + Base::Placement _calculateAttachedPlacement( const std::vector &objs, const std::vector &subs, - const Base::Placement &origPlacement) const; + const Base::Placement &origPlacement) const override; }; //attacher specialized for datum lines @@ -468,10 +468,10 @@ class PartExport AttachEngineLine : public AttachEngine public: AttachEngineLine(); AttachEngineLine* copy() const override; - virtual Base::Placement _calculateAttachedPlacement( + Base::Placement _calculateAttachedPlacement( const std::vector &objs, const std::vector &subs, - const Base::Placement &origPlacement) const; + const Base::Placement &origPlacement) const override; }; //attacher specialized for datum points @@ -481,10 +481,10 @@ class PartExport AttachEnginePoint : public AttachEngine public: AttachEnginePoint(); AttachEnginePoint* copy() const override; - virtual Base::Placement _calculateAttachedPlacement( + Base::Placement _calculateAttachedPlacement( const std::vector &objs, const std::vector &subs, - const Base::Placement &origPlacement) const; + const Base::Placement &origPlacement) const override; private: gp_Pnt getProximityPoint(eMapMode mode, const TopoDS_Shape& s1, const TopoDS_Shape& s2) const; diff --git a/tests/src/Mod/Part/App/Attacher.cpp b/tests/src/Mod/Part/App/Attacher.cpp new file mode 100644 index 0000000000..2e9dfd0fbe --- /dev/null +++ b/tests/src/Mod/Part/App/Attacher.cpp @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "gtest/gtest.h" +#include "PartTestHelpers.h" + +#include +#include +#include + +using namespace Part; +using namespace Attacher; +using namespace PartTestHelpers; + +/* + * Testing note: It looks like there are about 45 different attachment modes, and these tests all + * only look at one of them - to prove that adding elementMap code doesn't break anything. A + * comprehensive test of the Attacher would definitely want to try many more code paths. + */ + +class AttacherTest: public ::testing::Test, public PartTestHelpers::PartTestHelperClass +{ +protected: + static void SetUpTestSuite() + { + tests::initApplication(); + } + + void SetUp() override + { + createTestDoc(); + _boxes[1]->MapReversed.setValue(false); + _boxes[1]->AttachmentSupport.setValue(_boxes[0]); + _boxes[1]->MapPathParameter.setValue(0.0); + _boxes[1]->MapMode.setValue("ObjectXY"); // There are lots of attachment modes! + _boxes[1]->recomputeFeature(); + } + + void TearDown() override + { + App::GetApplication().closeDocument(_docName.c_str()); + } + + App::Document* getDocument() const + { + return _doc; + } + +protected: +}; + +TEST_F(AttacherTest, TestSetReferences) +{ + auto& attacher = _boxes[1]->attacher(); + EXPECT_EQ(attacher.getRefObjects().size(), 1); +} + +TEST_F(AttacherTest, TestSuggestMapModes) +{ + auto& attacher = _boxes[1]->attacher(); + SuggestResult result; + attacher.suggestMapModes(result); + EXPECT_EQ(result.allApplicableModes.size(), 4); + EXPECT_EQ(result.allApplicableModes[0], mmObjectXY); + EXPECT_EQ(result.allApplicableModes[1], mmObjectXZ); + EXPECT_EQ(result.allApplicableModes[2], mmObjectYZ); + EXPECT_EQ(result.allApplicableModes[3], mmInertialCS); +} + +TEST_F(AttacherTest, TestGetShapeType) +{ + auto& attacher = _boxes[1]->attacher(); + auto subObjects = _boxes[1]->getSubObjects(); + auto shapeType = attacher.getShapeType(_boxes[1], "Vertex2"); + EXPECT_EQ(shapeType, TopAbs_COMPSOLID); +} + +TEST_F(AttacherTest, TestGetInertialPropsOfShape) +{ + auto& attacher = _boxes[1]->attacher(); + std::vector result; + auto faces = _boxes[1]->Shape.getShape().getSubShapes(TopAbs_FACE); + result.emplace_back(&faces[0]); + auto shapeType = attacher.getInertialPropsOfShape(result); + EXPECT_EQ(result.size(), 1); + EXPECT_EQ(shapeType.Mass(), 6.0); +} + +TEST_F(AttacherTest, TestGetRefObjects) +{ + auto& attacher = _boxes[1]->attacher(); + auto docObjects = attacher.getRefObjects(); + EXPECT_EQ(docObjects.size(), 1); + EXPECT_STREQ(docObjects.front()->getNameInDocument(), "Part__Box"); +} + +TEST_F(AttacherTest, TestCalculateAttachedPlacement) +{ + auto& attacher = _boxes[1]->attacher(); + const Base::Placement orig; + auto placement = attacher.calculateAttachedPlacement(orig); + EXPECT_EQ(orig.getPosition().x, 0); + EXPECT_EQ(orig.getPosition().y, 0); + EXPECT_EQ(orig.getPosition().z, 0); +} diff --git a/tests/src/Mod/Part/App/CMakeLists.txt b/tests/src/Mod/Part/App/CMakeLists.txt index af1f3f7903..8aa815b1b9 100644 --- a/tests/src/Mod/Part/App/CMakeLists.txt +++ b/tests/src/Mod/Part/App/CMakeLists.txt @@ -2,6 +2,7 @@ target_sources( Part_tests_run PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/Attacher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BRepMesh.cpp ${CMAKE_CURRENT_SOURCE_DIR}/FeatureChamfer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/FeatureCompound.cpp diff --git a/tests/src/Mod/Part/App/TopoShape.cpp b/tests/src/Mod/Part/App/TopoShape.cpp index 0d838671d6..b7f7b754a8 100644 --- a/tests/src/Mod/Part/App/TopoShape.cpp +++ b/tests/src/Mod/Part/App/TopoShape.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include "gtest/gtest.h" +#include "PartTestHelpers.h" #include // clang-format off @@ -87,4 +88,21 @@ TEST(TopoShape, TestTypeNull) EXPECT_EQ(Part::TopoShape::getTypeAndIndex(nullptr), std::make_pair(std::string(), 0UL)); } + +TEST(TopoShape, TestGetSubshape) +{ + // Arrange + auto [cube1, cube2] = PartTestHelpers::CreateTwoTopoShapeCubes(); + // Act + auto face = cube1.getSubShape("Face2"); + auto vertex = cube2.getSubShape(TopAbs_VERTEX,2); + auto silentFail = cube1.getSubShape("NotThere", true); + // Assert + EXPECT_EQ(face.ShapeType(), TopAbs_FACE); + EXPECT_EQ(vertex.ShapeType(), TopAbs_VERTEX); + EXPECT_TRUE(silentFail.IsNull()); + EXPECT_THROW(cube1.getSubShape("Face7"), Base::IndexError); // Out of range + EXPECT_THROW(cube1.getSubShape("WOOHOO", false), Base::ValueError); // Invalid +} + // clang-format on From 0944242b25efc8b11c8d7312159050f78514a150 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Thu, 14 Mar 2024 10:24:32 -0400 Subject: [PATCH 3/3] Toponaming/Part: Review and lint cleanups --- src/Mod/Part/App/Attacher.cpp | 104 +++++++++++++++++++--------- tests/src/Mod/Part/App/Attacher.cpp | 3 + 2 files changed, 75 insertions(+), 32 deletions(-) diff --git a/src/Mod/Part/App/Attacher.cpp b/src/Mod/Part/App/Attacher.cpp index 35ede72cdc..d0ccc757fe 100644 --- a/src/Mod/Part/App/Attacher.cpp +++ b/src/Mod/Part/App/Attacher.cpp @@ -397,9 +397,8 @@ void AttachEngine::suggestMapModes(SuggestResult &result) const if (! this->modeEnabled[iMode]) continue; const refTypeStringList &listStrings = modeRefTypes[iMode]; - for (std::size_t iStr = 0; iStr < listStrings.size(); ++iStr) { + for (const auto & str : listStrings) { 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){ @@ -451,7 +450,7 @@ void AttachEngine::suggestMapModes(SuggestResult &result) const } } if (score > 0){ - if(mlist.size() == 0) + if(mlist.empty()) mlist.push_back(eMapMode(iMode)); else if (mlist.back() != eMapMode(iMode)) mlist.push_back(eMapMode(iMode)); @@ -734,7 +733,7 @@ GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector &objs, 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())) { - FC_THROWM(AttachEngineException,"AttachEngine3D: attached to a non App::GeoFeature '" - << objs[i]->getNameInDocument() << "'"); + if (!objs[i]->isDerivedFrom()) { + throw AttachEngineException("AttachEngine3D: link points to something that is not App::GeoFeature"); } App::GeoFeature* geof = static_cast(objs[i]); geofs[i] = geof; @@ -831,15 +829,13 @@ void AttachEngine::readLinks(const std::vector &objs, if (geof->isDerivedFrom(Part::Feature::getClassTypeId())){ shape = (static_cast(geof)->Shape.getShape()); if (shape.isNull()){ - FC_THROWM(AttachEngineException,"AttachEngine3D: Part has null shape " - << objs[i]->getNameInDocument()); + throw AttachEngineException("AttachEngine3D: Part has null shape"); } if (sub[i].length()>0){ try{ shape = Part::Feature::getTopoShape(geof, sub[i].c_str(), true); if (shape.isNull()) - FC_THROWM(AttachEngineException, "AttachEngine3D: subshape not found " - << objs[i]->getNameInDocument() << '.' << sub[i]); + throw AttachEngineException("AttachEngine3D: null subshape"); storage.push_back(shape.getShape()); } catch (Standard_Failure &e){ FC_THROWM(AttachEngineException, "AttachEngine3D: subshape not found " @@ -965,7 +961,7 @@ Base::Placement AttachEngine::calculateAttachedPlacement(const Base::Placement& shadow.c_str(), Part::HistoryTraceType::followTypeChange, false); - if (related.size()) { + if (!related.empty()) { auto& res = subChanges[i]; res.first = Data::ComplexGeoData::elementMapPrefix(); related.front().name.appendToBuffer(res.first); @@ -974,7 +970,7 @@ Base::Placement AttachEngine::calculateAttachedPlacement(const Base::Placement& } else { std::string name = Data::oldElementName(shadow.c_str()); - if (name.size()) { + if (!name.empty()) { auto& res = subChanges[i]; res.first.clear(); res.second = name; @@ -984,19 +980,21 @@ Base::Placement AttachEngine::calculateAttachedPlacement(const Base::Placement& } } } - if (subChanges.size()) { + if (!subChanges.empty()) { // In case there is topological name changes, we only auto change the // subname if the calculated placement stays the same. If not, just // proceed as normal, which will throw exception and catch user's // attention. auto subs = subnames; - for (auto& v : subChanges) { - subs[v.first] = v.second.second; + for (auto& change : subChanges) { + auto [subkey, namechange] =change; + auto [_oldname, newname] = namechange; + subs[subkey] = newname; } auto pla = _calculateAttachedPlacement(objs, subs, origPlacement); // check equal placement with some tolerance - if (pla.getPosition().IsEqual(origPlacement.getPosition(), 1e-7) - && pla.getRotation().isSame(origPlacement.getRotation(), 1e-12)) { + if (pla.getPosition().IsEqual(origPlacement.getPosition(), Precision::Confusion()) + && pla.getRotation().isSame(origPlacement.getRotation(), Precision::Angular())) { // Only make changes if the caller supplies 'subChanged', because // otherwise it means the caller just want to do an immutable test. // See AttachExtension::isAttacherActive(). @@ -1016,6 +1014,7 @@ Base::Placement AttachEngine::calculateAttachedPlacement(const Base::Placement& //================================================================================= + TYPESYSTEM_SOURCE(Attacher::AttachEngine3D, Attacher::AttachEngine) AttachEngine3D::AttachEngine3D() @@ -1143,7 +1142,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector types; readLinks(objs, subs, parts, shapes, copiedShapeStorage, types); - if (parts.size() == 0) { + if (parts.empty()) { throw ExceptionCancel(); } @@ -1165,7 +1164,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector points; - for (std::size_t i = 0; i < shapes.size(); i++) { - const TopoDS_Shape& sh = *shapes[i]; + for (const auto & shape : shapes) { + const TopoDS_Shape &sh = *shape; if (sh.IsNull()) { throw Base::ValueError( "Null shape in AttachEngine3D::calculateAttachedPlacement()!"); @@ -1943,7 +1942,7 @@ Base::Placement AttachEnginePlane::_calculateAttachedPlacement( const std::vector &subs, const Base::Placement &origPlacement) const { - //re-use Attacher3d + //reuse Attacher3d Base::Placement plm; AttachEngine3D attacher3D; attacher3D.setUp(*this); @@ -2059,7 +2058,7 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vector types; readLinks(objs, subs, parts, shapes, copiedShapeStorage, types); - if (parts.size() == 0) { + if (parts.empty()) { throw ExceptionCancel(); } @@ -2126,8 +2125,8 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vector points; - for (std::size_t i = 0; i < shapes.size(); i++) { - const TopoDS_Shape& sh = *shapes[i]; + for (const auto & shape : shapes) { + const TopoDS_Shape &sh = *shape; if (sh.IsNull()) { throw Base::ValueError( "Null shape in AttachEngineLine::calculateAttachedPlacement()!"); @@ -2247,6 +2246,47 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vectorIsNull() || shapes[1]->IsNull()) { + throw Base::ValueError( + "Null shape in AttachEngineLine::calculateAttachedPlacement()!"); + } + + const TopoDS_Face& face1 = TopoDS::Face(*(shapes[0])); + const TopoDS_Face& face2 = TopoDS::Face(*(shapes[1])); + + Handle(Geom_Surface) hSurf1 = BRep_Tool::Surface(face1); + Handle(Geom_Surface) hSurf2 = BRep_Tool::Surface(face2); + GeomAPI_IntSS intersector(hSurf1, hSurf2, Precision::Confusion()); + if (!intersector.IsDone()) { + throw Base::ValueError( + "AttachEngineLine::calculateAttachedPlacement: Intersection failed"); + } + + const Standard_Integer intLines = intersector.NbLines(); + if (intLines == 0) { + throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: The two " + "shapes don't intersect"); + } + if (intLines != 1) { + throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: " + "Intersection is not a single curve"); + } + + GeomAdaptor_Curve adapt(intersector.Line(1)); + if (adapt.GetType() != GeomAbs_Line) { + throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: " + "Intersection is not a straight line"); + } + + LineBasePoint = adapt.Line().Location(); + LineDir = adapt.Line().Direction(); + } break; case mm1Proximity: { if (shapes.size() < 2) { throw Base::ValueError( @@ -2292,7 +2332,7 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vector points; - assert(shapes.size() > 0); + assert(!shapes.empty()); const TopoDS_Shape& sh = *shapes[0]; if (sh.IsNull()) { @@ -2500,7 +2540,7 @@ AttachEnginePoint::_calculateAttachedPlacement(const std::vector