From 72669ab6a6cc2fdaba7c164c2a46d64026d5544b Mon Sep 17 00:00:00 2001 From: bgbsww Date: Tue, 12 Mar 2024 11:20:56 -0400 Subject: [PATCH] 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