Download src/Mod/Measure/App/Measurement.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 41.9 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Measure/App/Measurement.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/Measure/App/Measurement.cpp
-
curl -L -o Measurement.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Measure/App/Measurement.cpp
41.9 kB
| // SPDX-License-Identifier: LGPL-2.1-or-later | |
| /*************************************************************************** | |
| * Copyright (c) 2013 Luke Parry <l.parry@warwick.ac.uk> * | |
| * * | |
| * This file is part of the FreeCAD CAx development system. * | |
| * * | |
| * This library is free software; you can redistribute it and/or * | |
| * modify it under the terms of the GNU Library General Public * | |
| * License as published by the Free Software Foundation; either * | |
| * version 2 of the License, or (at your option) any later version. * | |
| * * | |
| * This library is distributed in the hope that it will be useful, * | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
| * GNU Library General Public License for more details. * | |
| * * | |
| * You should have received a copy of the GNU Library General Public * | |
| * License along with this library; see the file COPYING.LIB. If not, * | |
| * write to the Free Software Foundation, Inc., 59 Temple Place, * | |
| * Suite 330, Boston, MA 02111-1307, USA * | |
| * * | |
| ***************************************************************************/ | |
| using namespace Measure; | |
| using namespace Base; | |
| using namespace Part; | |
| TYPESYSTEM_SOURCE(Measure::Measurement, Base::BaseClass) | |
| Measurement::Measurement() | |
| { | |
| measureType = MeasureType::Invalid; | |
| References3D.setScope(App::LinkScope::Global); | |
| } | |
| Measurement::~Measurement() = default; | |
| void Measurement::clear() | |
| { | |
| std::vector<App::DocumentObject*> Objects; | |
| std::vector<std::string> SubElements; | |
| References3D.setValues(Objects, SubElements); | |
| measureType = MeasureType::Invalid; | |
| } | |
| bool Measurement::has3DReferences() | |
| { | |
| return (References3D.getSize() > 0); | |
| } | |
| // add a 3D reference (obj+sub) to end of list | |
| int Measurement::addReference3D(App::DocumentObject* obj, const std::string& subName) | |
| { | |
| return addReference3D(obj, subName.c_str()); | |
| } | |
| /// add a 3D reference (obj+sub) to end of list | |
| int Measurement::addReference3D(App::DocumentObject* obj, const char* subName) | |
| { | |
| std::vector<App::DocumentObject*> objects = References3D.getValues(); | |
| std::vector<std::string> subElements = References3D.getSubValues(); | |
| objects.push_back(obj); | |
| subElements.emplace_back(subName); | |
| References3D.setValues(objects, subElements); | |
| measureType = findType(); | |
| return References3D.getSize(); | |
| } | |
| MeasureType Measurement::findType() | |
| { | |
| const std::vector<App::DocumentObject*>& objects = References3D.getValues(); | |
| const std::vector<std::string>& subElements = References3D.getSubValues(); | |
| std::vector<App::DocumentObject*>::const_iterator obj = objects.begin(); | |
| std::vector<std::string>::const_iterator subEl = subElements.begin(); | |
| MeasureType mode; | |
| int verts = 0; | |
| int edges = 0; | |
| int lines = 0; | |
| int circles = 0; | |
| int circleArcs = 0; | |
| int faces = 0; | |
| int planes = 0; | |
| int cylinders = 0; | |
| int cylinderSections = 0; | |
| int cones = 0; | |
| int torus = 0; | |
| int spheres = 0; | |
| int vols = 0; | |
| int other = 0; | |
| for (; obj != objects.end(); ++obj, ++subEl) { | |
| TopoDS_Shape refSubShape; | |
| try { | |
| refSubShape = Part::Feature::getShape( | |
| *obj, | |
| Part::ShapeOption::NeedSubElement | Part::ShapeOption::ResolveLink | |
| | Part::ShapeOption::Transform, | |
| (*subEl).c_str() | |
| ); | |
| if (refSubShape.IsNull()) { | |
| return MeasureType::Invalid; | |
| } | |
| } | |
| catch (Standard_Failure& e) { | |
| std::stringstream errorMsg; | |
| errorMsg << "Measurement - getType - " << e.GetMessageString() << std::endl; | |
| throw Base::CADKernelError(e.GetMessageString()); | |
| } | |
| switch (refSubShape.ShapeType()) { | |
| case TopAbs_VERTEX: { | |
| verts++; | |
| } break; | |
| case TopAbs_EDGE: { | |
| edges++; | |
| TopoDS_Edge edge = TopoDS::Edge(refSubShape); | |
| BRepAdaptor_Curve sf(edge); | |
| if (sf.GetType() == GeomAbs_Line) { | |
| lines++; | |
| } | |
| else if (sf.GetType() == GeomAbs_Circle) { | |
| if (sf.IsClosed()) { | |
| circles++; | |
| } | |
| else { | |
| circleArcs++; | |
| } | |
| } | |
| } break; | |
| case TopAbs_FACE: { | |
| faces++; | |
| TopoDS_Face face = TopoDS::Face(refSubShape); | |
| BRepAdaptor_Surface sf(face); | |
| if (sf.GetType() == GeomAbs_Plane) { | |
| planes++; | |
| } | |
| else if (sf.GetType() == GeomAbs_Cylinder) { | |
| if (sf.IsUClosed() || sf.IsVClosed()) { | |
| cylinders++; | |
| } | |
| else { | |
| cylinderSections++; | |
| } | |
| } | |
| else if (sf.GetType() == GeomAbs_Sphere) { | |
| spheres++; | |
| } | |
| else if (sf.GetType() == GeomAbs_Cone) { | |
| cones++; | |
| } | |
| else if (sf.GetType() == GeomAbs_Torus) { | |
| torus++; | |
| } | |
| } break; | |
| case TopAbs_SOLID: { | |
| vols++; | |
| } break; | |
| default: | |
| other++; | |
| break; | |
| } | |
| } | |
| if (other > 0) { | |
| mode = MeasureType::Invalid; | |
| } | |
| else if (vols > 0) { | |
| if (verts > 0 || edges > 0 || faces > 0) { | |
| mode = MeasureType::Invalid; | |
| } | |
| else { | |
| mode = MeasureType::Volumes; | |
| } | |
| } | |
| else if (faces > 0) { | |
| if (verts > 0 || edges > 0) { | |
| if (faces == 1 && (cylinders + cylinderSections) == 1 && verts == 1 && edges == 0) { | |
| mode = MeasureType::PointToCylinder; | |
| } | |
| else if (faces == 1 && verts == 1) { | |
| mode = MeasureType::PointToSurface; | |
| } | |
| else if (faces == 1 && (cylinders + cylinderSections) == 1 | |
| && (circles + circleArcs) == 1 && edges == 1) { | |
| mode = MeasureType::CircleToCylinder; | |
| } | |
| else if (faces == 1 && (circles + circleArcs) == 1 && edges == 1) { | |
| mode = MeasureType::CircleToSurface; | |
| } | |
| else { | |
| mode = MeasureType::Invalid; | |
| } | |
| } | |
| else { | |
| if (planes == 1 && faces == 1) { | |
| mode = MeasureType::Plane; | |
| } | |
| else if (planes == 2 && faces == 2) { | |
| if (planesAreParallel()) { | |
| mode = MeasureType::TwoPlanes; | |
| } | |
| else { | |
| mode = MeasureType::Surfaces; | |
| } | |
| } | |
| else if (cylinders == 1 && faces == 1) { | |
| mode = MeasureType::Cylinder; | |
| } | |
| else if (cylinderSections == 1 && faces == 1) { | |
| mode = MeasureType::CylinderSection; | |
| } | |
| else if ((cylinders + cylinderSections) == 2 && faces == 2) { | |
| mode = MeasureType::TwoCylinders; | |
| } | |
| else if (cones == 1 && faces == 1) { | |
| mode = MeasureType::Cone; | |
| } | |
| else if (spheres == 1 && faces == 1) { | |
| mode = MeasureType::Sphere; | |
| } | |
| else if (torus == 1 && faces == 1) { | |
| mode = MeasureType::Torus; | |
| } | |
| else { | |
| mode = MeasureType::Surfaces; | |
| } | |
| } | |
| } | |
| else if (edges > 0) { | |
| if (verts > 0) { | |
| if (verts > 1) { | |
| mode = MeasureType::Invalid; | |
| } | |
| else if ((circles + circleArcs) == 1) { | |
| mode = MeasureType::PointToCircle; | |
| } | |
| else { | |
| mode = MeasureType::PointToEdge; | |
| } | |
| } | |
| else if (lines == 1 && edges == 1) { | |
| mode = MeasureType::Line; | |
| } | |
| else if (lines == 2 && edges == 2) { | |
| if (linesAreParallel()) { | |
| mode = MeasureType::TwoParallelLines; | |
| } | |
| else { | |
| mode = MeasureType::TwoLines; | |
| } | |
| } | |
| else if (circles == 1 && edges == 1) { | |
| mode = MeasureType::Circle; | |
| } | |
| else if (circleArcs == 1 && edges == 1) { | |
| mode = MeasureType::CircleArc; | |
| } | |
| else if ((circles + circleArcs) == 2 && edges == 2) { | |
| mode = MeasureType::TwoCircles; | |
| } | |
| else if ((circles + circleArcs == 1) && edges == 2) { | |
| mode = MeasureType::CircleToEdge; | |
| } | |
| else { | |
| mode = MeasureType::Edges; | |
| } | |
| } | |
| else if (verts > 0) { | |
| if (verts == 2) { | |
| mode = MeasureType::PointToPoint; | |
| } | |
| else { | |
| mode = MeasureType::Points; | |
| } | |
| } | |
| else { | |
| mode = MeasureType::Invalid; | |
| } | |
| return mode; | |
| } | |
| MeasureType Measurement::getType() | |
| { | |
| return measureType; | |
| } | |
| TopoDS_Shape Measurement::getShape(App::DocumentObject* obj, const char* subName, TopAbs_ShapeEnum hint) const | |
| { | |
| (void)hint; | |
| return Part::Feature::getShape( | |
| obj, | |
| Part::ShapeOption::NeedSubElement | Part::ShapeOption::ResolveLink | |
| | Part::ShapeOption::Transform, | |
| subName | |
| ); | |
| } | |
| // TODO:: add lengthX, lengthY (and lengthZ??) support | |
| // Methods for distances (edge length, two points, edge and a point | |
| double Measurement::length() const | |
| { | |
| double result = 0.0; | |
| int numRefs = References3D.getSize(); | |
| if (numRefs == 0) { | |
| Base::Console().error("Measurement::length - No 3D references available\n"); | |
| } | |
| else if (measureType == MeasureType::Invalid) { | |
| Base::Console().error("Measurement::length - measureType is Invalid\n"); | |
| } | |
| else { | |
| const std::vector<App::DocumentObject*>& objects = References3D.getValues(); | |
| const std::vector<std::string>& subElements = References3D.getSubValues(); | |
| if (measureType == MeasureType::Points || measureType == MeasureType::PointToPoint | |
| || measureType == MeasureType::PointToEdge || measureType == MeasureType::PointToSurface | |
| || measureType == MeasureType::PointToCircle | |
| || measureType == MeasureType::PointToCylinder) { | |
| Base::Vector3d diff = this->delta(); | |
| result = diff.Length(); | |
| } | |
| else if (measureType == MeasureType::Edges || measureType == MeasureType::Line | |
| || measureType == MeasureType::TwoLines || measureType == MeasureType::Circle | |
| || measureType == MeasureType::CircleArc || measureType == MeasureType::TwoCircles | |
| || measureType == MeasureType::CircleToEdge) { | |
| // Iterate through edges and calculate each length | |
| std::vector<App::DocumentObject*>::const_iterator obj = objects.begin(); | |
| std::vector<std::string>::const_iterator subEl = subElements.begin(); | |
| for (; obj != objects.end(); ++obj, ++subEl) { | |
| // Get the length of one edge | |
| TopoDS_Shape shape = getShape(*obj, (*subEl).c_str(), TopAbs_EDGE); | |
| if (shape.IsNull() || shape.Infinite()) { | |
| continue; | |
| } | |
| const TopoDS_Edge& edge = TopoDS::Edge(shape); | |
| BRepAdaptor_Curve curve(edge); | |
| switch (curve.GetType()) { | |
| case GeomAbs_Line: { | |
| gp_Pnt P1 = curve.Value(curve.FirstParameter()); | |
| gp_Pnt P2 = curve.Value(curve.LastParameter()); | |
| gp_XYZ diff = P2.XYZ() - P1.XYZ(); | |
| result += diff.Modulus(); | |
| break; | |
| } | |
| case GeomAbs_Circle: { | |
| double u = curve.FirstParameter(); | |
| double v = curve.LastParameter(); | |
| double radius = curve.Circle().Radius(); | |
| if (u > v) { // if arc is reversed | |
| std::swap(u, v); | |
| } | |
| double range = v - u; | |
| result += radius * range; | |
| break; | |
| } | |
| case GeomAbs_Ellipse: | |
| case GeomAbs_BSplineCurve: | |
| case GeomAbs_Hyperbola: | |
| case GeomAbs_Parabola: | |
| case GeomAbs_BezierCurve: { | |
| result += GCPnts_AbscissaPoint::Length(curve); | |
| break; | |
| } | |
| default: { | |
| throw Base::RuntimeError( | |
| "Measurement - length - Curve type not currently handled" | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return result; | |
| } | |
| double Measurement::lineLineDistance() const | |
| { | |
| // We don't use delta() because BRepExtrema_DistShapeShape return minimum length between line | |
| // segment. Here we get the nominal distance between the infinite lines. | |
| double distance = 0.0; | |
| if (measureType != MeasureType::TwoParallelLines || References3D.getSize() != 2) { | |
| return distance; | |
| } | |
| const std::vector<App::DocumentObject*>& objects = References3D.getValues(); | |
| const std::vector<std::string>& subElements = References3D.getSubValues(); | |
| // Get the first line | |
| TopoDS_Shape shape1 = getShape(objects[0], subElements[0].c_str(), TopAbs_EDGE); | |
| const TopoDS_Edge& edge1 = TopoDS::Edge(shape1); | |
| BRepAdaptor_Curve curve1(edge1); | |
| // Get the second line | |
| TopoDS_Shape shape2 = getShape(objects[1], subElements[1].c_str(), TopAbs_EDGE); | |
| const TopoDS_Edge& edge2 = TopoDS::Edge(shape2); | |
| BRepAdaptor_Curve curve2(edge2); | |
| if (curve1.GetType() == GeomAbs_Line && curve2.GetType() == GeomAbs_Line) { | |
| gp_Lin line1 = curve1.Line(); | |
| gp_Lin line2 = curve2.Line(); | |
| gp_Pnt p1 = line1.Location(); | |
| gp_Pnt p2 = line2.Location(); | |
| // Create a vector from a point on line1 to a point on line2 | |
| gp_Vec lineVec(p1, p2); | |
| // The direction vector of one of the lines | |
| gp_Dir lineDir = line1.Direction(); | |
| // Project lineVec onto lineDir | |
| gp_Vec parallelComponent = lineVec.Dot(lineDir) * lineDir; | |
| // Compute the perpendicular component | |
| gp_Vec perpendicularComponent = lineVec - parallelComponent; | |
| // Distance is the magnitude of the perpendicular component | |
| distance = perpendicularComponent.Magnitude(); | |
| } | |
| else { | |
| Base::Console().error("Measurement::length - TwoLines measureType requires two lines\n"); | |
| } | |
| return distance; | |
| } | |
| double Measurement::circleCenterDistance() const | |
| { | |
| double distance = 0.0; | |
| if (References3D.getSize() != 2) { | |
| return distance; | |
| } | |
| const std::vector<App::DocumentObject*>& objects = References3D.getValues(); | |
| const std::vector<std::string>& subElements = References3D.getSubValues(); | |
| // Get the first circle | |
| TopoDS_Shape shape1 = getShape(objects[0], subElements[0].c_str()); | |
| TopoDS_Shape shape2 = getShape(objects[1], subElements[1].c_str()); | |
| if (shape1.ShapeType() != TopAbs_EDGE) { | |
| std::swap(shape1, shape2); | |
| } | |
| if (measureType == MeasureType::TwoCircles) { | |
| const TopoDS_Edge& edge1 = TopoDS::Edge(shape1); | |
| BRepAdaptor_Curve curve1(edge1); | |
| const TopoDS_Edge& edge2 = TopoDS::Edge(shape2); | |
| BRepAdaptor_Curve curve2(edge2); | |
| if (curve1.GetType() == GeomAbs_Circle && curve2.GetType() == GeomAbs_Circle) { | |
| gp_Circ circle1 = curve1.Circle(); | |
| gp_Circ circle2 = curve2.Circle(); | |
| distance = circle1.Location().Distance(circle2.Location()); | |
| } | |
| } | |
| else if (measureType == MeasureType::CircleToEdge || measureType == MeasureType::CircleToSurface | |
| || measureType == MeasureType::CircleToCylinder) { | |
| const TopoDS_Edge& edge1 = TopoDS::Edge(shape1); | |
| BRepAdaptor_Curve curve1(edge1); | |
| TopoDS_Vertex circleCenter; | |
| const TopoDS_Shape* otherShape; | |
| if (curve1.GetType() == GeomAbs_Circle) { | |
| circleCenter = BRepBuilderAPI_MakeVertex(curve1.Circle().Location()); | |
| otherShape = &shape2; | |
| } | |
| else { | |
| const TopoDS_Edge& edge2 = TopoDS::Edge(shape2); | |
| BRepAdaptor_Curve curve2(edge2); | |
| circleCenter = BRepBuilderAPI_MakeVertex(curve2.Circle().Location()); | |
| otherShape = &shape1; | |
| } | |
| BRepExtrema_DistShapeShape extrema(circleCenter, *otherShape); | |
| if (extrema.IsDone()) { | |
| // Found the nearest point between point and curve | |
| // NOTE we will assume there is only 1 solution (cyclic topology will create | |
| // multiple solutions. | |
| gp_Pnt P1 = extrema.PointOnShape1(1); | |
| gp_Pnt P2 = extrema.PointOnShape2(1); | |
| gp_XYZ diff = P2.XYZ() - P1.XYZ(); | |
| distance = Base::Vector3d(diff.X(), diff.Y(), diff.Z()).Length(); | |
| } | |
| } | |
| else if (measureType == MeasureType::PointToCircle) { | |
| const TopoDS_Edge& edge1 = TopoDS::Edge(shape1); | |
| BRepAdaptor_Curve curve1(edge1); | |
| TopoDS_Vertex& vert1 = TopoDS::Vertex(shape2); | |
| gp_Circ circle1 = curve1.Circle(); | |
| gp_Pnt pt = BRep_Tool::Pnt(vert1); | |
| distance = circle1.Location().Distance(pt); | |
| } | |
| return distance; | |
| } | |
| double Measurement::planePlaneDistance() const | |
| { | |
| if (measureType != MeasureType::TwoPlanes || References3D.getSize() != 2) { | |
| return 0.0; | |
| } | |
| const auto& objects = References3D.getValues(); | |
| const auto& subElements = References3D.getSubValues(); | |
| // Get the first plane | |
| TopoDS_Shape shape1 = getShape(objects[0], subElements[0].c_str(), TopAbs_FACE); | |
| const TopoDS_Face& face1 = TopoDS::Face(shape1); | |
| BRepAdaptor_Surface surface1(face1); | |
| const gp_Pln& plane1 = surface1.Plane(); | |
| // Get the second plane | |
| TopoDS_Shape shape2 = getShape(objects[1], subElements[1].c_str(), TopAbs_FACE); | |
| const TopoDS_Face& face2 = TopoDS::Face(shape2); | |
| BRepAdaptor_Surface surface2(face2); | |
| const gp_Pln& plane2 = surface2.Plane(); | |
| // Distance between two parallel planes | |
| gp_Pnt pointOnPlane1 = plane1.Location(); | |
| gp_Dir normalToPlane1 = plane1.Axis().Direction(); | |
| gp_Pnt pointOnPlane2 = plane2.Location(); | |
| // Create a vector from a point on plane1 to a point on plane2 | |
| gp_Vec vectorBetweenPlanes(pointOnPlane1, pointOnPlane2); | |
| // Project this vector onto the plane normal | |
| double distance = Abs(vectorBetweenPlanes.Dot(normalToPlane1)); | |
| return distance; | |
| } | |
| double Measurement::cylinderAxisDistance() const | |
| { | |
| double distance = 0.0; | |
| if (References3D.getSize() != 2) { | |
| return distance; | |
| } | |
| const std::vector<App::DocumentObject*>& objects = References3D.getValues(); | |
| const std::vector<std::string>& subElements = References3D.getSubValues(); | |
| // Get the first circle | |
| TopoDS_Shape shape1 = getShape(objects[0], subElements[0].c_str()); | |
| TopoDS_Shape shape2 = getShape(objects[1], subElements[1].c_str()); | |
| if (shape1.ShapeType() != TopAbs_FACE) { | |
| std::swap(shape1, shape2); | |
| } | |
| if (measureType == MeasureType::PointToCylinder) { | |
| TopoDS_Face face = TopoDS::Face(shape1); | |
| BRepAdaptor_Surface cylinderFace(face); | |
| const TopoDS_Vertex& vert1 = TopoDS::Vertex(shape2); | |
| gp_Pnt pt = BRep_Tool::Pnt(vert1); | |
| if (cylinderFace.GetType() == GeomAbs_Cylinder) { | |
| distance = gp_Lin(cylinderFace.Cylinder().Axis()).Distance(pt); | |
| } | |
| } | |
| else if (measureType == MeasureType::TwoCylinders) { | |
| TopoDS_Face face1 = TopoDS::Face(shape1); | |
| BRepAdaptor_Surface surface1(face1); | |
| TopoDS_Face face2 = TopoDS::Face(shape2); | |
| BRepAdaptor_Surface surface2(face2); | |
| if (surface1.GetType() == GeomAbs_Cylinder && surface2.GetType() == GeomAbs_Cylinder) { | |
| distance = gp_Lin(surface1.Cylinder().Axis()).Distance(gp_Lin(surface2.Cylinder().Axis())); | |
| } | |
| } | |
| else if (measureType == MeasureType::CircleToCylinder) { | |
| TopoDS_Face face1 = TopoDS::Face(shape1); | |
| BRepAdaptor_Surface surface1(face1); | |
| TopoDS_Edge edge1 = TopoDS::Edge(shape2); | |
| BRepAdaptor_Curve curve1(edge1); | |
| if (surface1.GetType() == GeomAbs_Cylinder && curve1.GetType() == GeomAbs_Circle) { | |
| distance = gp_Lin(surface1.Cylinder().Axis()).Distance(curve1.Circle().Location()); | |
| } | |
| } | |
| return distance; | |
| } | |
| double Measurement::angle(const Base::Vector3d& /*param*/) const | |
| { | |
| // TODO: do these references arrive as obj+sub pairs or as a struct of obj + [subs]? | |
| const std::vector<App::DocumentObject*>& objects = References3D.getValues(); | |
| const std::vector<std::string>& subElements = References3D.getSubValues(); | |
| int numRefs = objects.size(); | |
| if (numRefs == 0) { | |
| throw Base::RuntimeError("No references available for angle measurement"); | |
| } | |
| else if (measureType == MeasureType::Invalid) { | |
| throw Base::RuntimeError("MeasureType is Invalid for angle measurement"); | |
| } | |
| else if (measureType == MeasureType::TwoLines) { | |
| // Only case that is supported is edge to edge | |
| // The angle between two skew lines is measured by the angle between one line (A) | |
| // and a line (B) with the direction of the second through a point on the first line. | |
| // Since we don't know if the directions of the lines point in the same general direction | |
| // we could get the angle we want or the supplementary angle. | |
| if (numRefs == 2) { | |
| TopoDS_Shape shape1 = getShape(objects.at(0), subElements.at(0).c_str(), TopAbs_EDGE); | |
| TopoDS_Shape shape2 = getShape(objects.at(1), subElements.at(1).c_str(), TopAbs_EDGE); | |
| BRepAdaptor_Curve curve1(TopoDS::Edge(shape1)); | |
| BRepAdaptor_Curve curve2(TopoDS::Edge(shape2)); | |
| if (curve1.GetType() == GeomAbs_Line && curve2.GetType() == GeomAbs_Line) { | |
| gp_Pnt pnt1First = curve1.Value(curve1.FirstParameter()); | |
| gp_Dir dir1 = curve1.Line().Direction(); | |
| gp_Dir dir2 = curve2.Line().Direction(); | |
| gp_Dir dir2r = curve2.Line().Direction().Reversed(); | |
| gp_Lin l1 = gp_Lin(pnt1First, dir1); // (A) | |
| gp_Lin l2 = gp_Lin(pnt1First, dir2); // (B) | |
| gp_Lin l2r = gp_Lin(pnt1First, dir2r); // (B') | |
| Standard_Real aRad = l1.Angle(l2); | |
| double aRadr = l1.Angle(l2r); | |
| return Base::toDegrees<double>(std::min(aRad, aRadr)); | |
| } | |
| else { | |
| throw Base::RuntimeError("Measurement references must both be lines"); | |
| } | |
| } | |
| else { | |
| throw Base::RuntimeError("Can not compute angle measurement - too many references"); | |
| } | |
| } | |
| else if (measureType == MeasureType::Points) { | |
| // NOTE: we are calculating the 3d angle here, not the projected angle | |
| // ASSUMPTION: the references are in end-apex-end order | |
| if (numRefs == 3) { | |
| TopoDS_Shape shape0 = getShape(objects.at(0), subElements.at(0).c_str(), TopAbs_VERTEX); | |
| TopoDS_Shape shape1 = getShape(objects.at(1), subElements.at(1).c_str(), TopAbs_VERTEX); | |
| TopoDS_Shape shape2 = getShape(objects.at(1), subElements.at(2).c_str(), TopAbs_VERTEX); | |
| if (shape0.ShapeType() != TopAbs_VERTEX || shape1.ShapeType() != TopAbs_VERTEX | |
| || shape2.ShapeType() != TopAbs_VERTEX) { | |
| throw Base::RuntimeError("Measurement references for 3 point angle are not Vertex"); | |
| } | |
| gp_Pnt gEnd0 = BRep_Tool::Pnt(TopoDS::Vertex(shape0)); | |
| gp_Pnt gApex = BRep_Tool::Pnt(TopoDS::Vertex(shape1)); | |
| gp_Pnt gEnd1 = BRep_Tool::Pnt(TopoDS::Vertex(shape2)); | |
| gp_Dir gDir0 = gp_Dir(gEnd0.XYZ() - gApex.XYZ()); | |
| gp_Dir gDir1 = gp_Dir(gEnd1.XYZ() - gApex.XYZ()); | |
| gp_Lin line0 = gp_Lin(gEnd0, gDir0); | |
| gp_Lin line1 = gp_Lin(gEnd1, gDir1); | |
| double radians = line0.Angle(line1); | |
| return Base::toDegrees<double>(radians); | |
| } | |
| } | |
| else if (measureType == MeasureType::TwoCylinders || measureType == MeasureType::TwoCircles | |
| || measureType == MeasureType::CircleToCylinder) { | |
| if (numRefs == 2) { | |
| TopoDS_Shape shape1 = getShape(objects.at(0), subElements.at(0).c_str(), TopAbs_EDGE); | |
| TopoDS_Shape shape2 = getShape(objects.at(1), subElements.at(1).c_str(), TopAbs_EDGE); | |
| gp_Ax1 axis1; | |
| gp_Ax1 axis2; | |
| if (measureType == MeasureType::TwoCylinders) { | |
| BRepAdaptor_Surface surface1(TopoDS::Face(shape1)); | |
| BRepAdaptor_Surface surface2(TopoDS::Face(shape2)); | |
| axis1 = surface1.Cylinder().Axis(); | |
| axis2 = surface2.Cylinder().Axis(); | |
| } | |
| else if (measureType == MeasureType::TwoCircles) { | |
| BRepAdaptor_Curve curve1(TopoDS::Edge(shape1)); | |
| BRepAdaptor_Curve curve2(TopoDS::Edge(shape2)); | |
| axis1 = curve1.Circle().Axis(); | |
| axis2 = curve2.Circle().Axis(); | |
| } | |
| else if (measureType == MeasureType::CircleToCylinder) { | |
| if (shape1.ShapeType() == TopAbs_FACE) { | |
| std::swap(shape1, shape2); | |
| } | |
| BRepAdaptor_Curve curve1(TopoDS::Edge(shape1)); | |
| BRepAdaptor_Surface surface2(TopoDS::Face(shape2)); | |
| axis1 = curve1.Circle().Axis(); | |
| axis2 = surface2.Cylinder().Axis(); | |
| } | |
| double aRad = axis1.Angle(axis2); | |
| return Base::toDegrees<double>( | |
| std::min(aRad, std::fmod(aRad + std::numbers::pi, 2.0 * std::numbers::pi)) | |
| ); | |
| } | |
| } | |
| throw Base::RuntimeError("Unexpected error for angle measurement"); | |
| } | |
| double Measurement::radius() const | |
| { | |
| const std::vector<App::DocumentObject*>& objects = References3D.getValues(); | |
| const std::vector<std::string>& subElements = References3D.getSubValues(); | |
| int numRefs = References3D.getSize(); | |
| if (numRefs == 0) { | |
| Base::Console().error("Measurement::radius - No 3D references available\n"); | |
| } | |
| else if (measureType == MeasureType::Circle || measureType == MeasureType::CircleArc) { | |
| TopoDS_Shape shape = getShape(objects.at(0), subElements.at(0).c_str(), TopAbs_EDGE); | |
| const TopoDS_Edge& edge = TopoDS::Edge(shape); | |
| BRepAdaptor_Curve curve(edge); | |
| if (curve.GetType() == GeomAbs_Circle) { | |
| return (double)curve.Circle().Radius(); | |
| } | |
| } | |
| else if (measureType == MeasureType::Cylinder || measureType == MeasureType::CylinderSection | |
| || measureType == MeasureType::Sphere || measureType == MeasureType::Torus) { | |
| TopoDS_Shape shape = getShape(objects.at(0), subElements.at(0).c_str(), TopAbs_FACE); | |
| TopoDS_Face face = TopoDS::Face(shape); | |
| BRepAdaptor_Surface sf(face); | |
| if (sf.GetType() == GeomAbs_Cylinder) { | |
| return sf.Cylinder().Radius(); | |
| } | |
| else if (sf.GetType() == GeomAbs_Sphere) { | |
| return sf.Sphere().Radius(); | |
| } | |
| else if (sf.GetType() == GeomAbs_Torus) { | |
| return sf.Torus().MinorRadius(); | |
| } | |
| } | |
| Base::Console().error("Measurement::radius - Invalid References3D Provided\n"); | |
| return 0.0; | |
| } | |
| double Measurement::diameter() const | |
| { | |
| const std::vector<App::DocumentObject*>& objects = References3D.getValues(); | |
| const std::vector<std::string>& subElements = References3D.getSubValues(); | |
| int numRefs = References3D.getSize(); | |
| if (numRefs == 0) { | |
| Base::Console().error("Measurement::diameter - No 3D references available\n"); | |
| } | |
| else if (measureType == MeasureType::Circle) { | |
| TopoDS_Shape shape = getShape(objects.at(0), subElements.at(0).c_str(), TopAbs_EDGE); | |
| const TopoDS_Edge& edge = TopoDS::Edge(shape); | |
| BRepAdaptor_Curve curve(edge); | |
| if (curve.GetType() == GeomAbs_Circle) { | |
| return (double)curve.Circle().Radius() * 2.0; | |
| } | |
| } | |
| else if (measureType == MeasureType::Cylinder) { | |
| TopoDS_Shape shape = getShape(objects.at(0), subElements.at(0).c_str(), TopAbs_FACE); | |
| TopoDS_Face face = TopoDS::Face(shape); | |
| BRepAdaptor_Surface sf(face); | |
| if (sf.GetType() == GeomAbs_Cylinder) { | |
| return sf.Cylinder().Radius() * 2.0; | |
| } | |
| } | |
| Base::Console().error("Measurement::diameter - Invalid References3D Provided\n"); | |
| return 0.0; | |
| } | |
| Base::Vector3d Measurement::delta() const | |
| { | |
| Base::Vector3d result; | |
| int numRefs = References3D.getSize(); | |
| if (numRefs == 0) { | |
| Base::Console().error("Measurement::delta - No 3D references available\n"); | |
| } | |
| else if (measureType == MeasureType::Invalid) { | |
| Base::Console().error("Measurement::delta - measureType is Invalid\n"); | |
| } | |
| else { | |
| const std::vector<App::DocumentObject*>& objects = References3D.getValues(); | |
| const std::vector<std::string>& subElements = References3D.getSubValues(); | |
| if (measureType == MeasureType::PointToPoint) { | |
| if (numRefs == 2) { | |
| // Keep separate case for two points to reduce need for complex algorithm | |
| TopoDS_Shape shape1 = getShape(objects.at(0), subElements.at(0).c_str(), TopAbs_VERTEX); | |
| TopoDS_Shape shape2 = getShape(objects.at(1), subElements.at(1).c_str(), TopAbs_VERTEX); | |
| const TopoDS_Vertex& vert1 = TopoDS::Vertex(shape1); | |
| const TopoDS_Vertex& vert2 = TopoDS::Vertex(shape2); | |
| gp_Pnt P1 = BRep_Tool::Pnt(vert1); | |
| gp_Pnt P2 = BRep_Tool::Pnt(vert2); | |
| gp_XYZ diff = P2.XYZ() - P1.XYZ(); | |
| return Base::Vector3d(diff.X(), diff.Y(), diff.Z()); | |
| } | |
| } | |
| else if (measureType == MeasureType::PointToEdge || measureType == MeasureType::PointToSurface | |
| || measureType == MeasureType::PointToCircle | |
| || measureType == MeasureType::PointToCylinder) { | |
| // BrepExtema can calculate minimum distance between any set of topology sets. | |
| if (numRefs == 2) { | |
| TopoDS_Shape shape1 = getShape(objects.at(0), subElements.at(0).c_str()); | |
| TopoDS_Shape shape2 = getShape(objects.at(1), subElements.at(1).c_str()); | |
| BRepExtrema_DistShapeShape extrema(shape1, shape2); | |
| if (extrema.IsDone()) { | |
| // Found the nearest point between point and curve | |
| // NOTE we will assume there is only 1 solution (cyclic topology will create | |
| // multiple solutions. | |
| gp_Pnt P1 = extrema.PointOnShape1(1); | |
| gp_Pnt P2 = extrema.PointOnShape2(1); | |
| gp_XYZ diff = P2.XYZ() - P1.XYZ(); | |
| result = Base::Vector3d(diff.X(), diff.Y(), diff.Z()); | |
| } | |
| } | |
| } | |
| else if (measureType == MeasureType::Edges) { | |
| // Only case that is supported is straight line edge | |
| if (numRefs == 1) { | |
| TopoDS_Shape shape = getShape(objects.at(0), subElements.at(0).c_str(), TopAbs_EDGE); | |
| const TopoDS_Edge& edge = TopoDS::Edge(shape); | |
| BRepAdaptor_Curve curve(edge); | |
| if (curve.GetType() == GeomAbs_Line) { | |
| gp_Pnt P1 = curve.Value(curve.FirstParameter()); | |
| gp_Pnt P2 = curve.Value(curve.LastParameter()); | |
| gp_XYZ diff = P2.XYZ() - P1.XYZ(); | |
| result = Base::Vector3d(diff.X(), diff.Y(), diff.Z()); | |
| } | |
| } | |
| else if (numRefs == 2) { | |
| TopoDS_Shape shape1 = getShape(objects.at(0), subElements.at(0).c_str(), TopAbs_EDGE); | |
| TopoDS_Shape shape2 = getShape(objects.at(1), subElements.at(1).c_str(), TopAbs_EDGE); | |
| BRepAdaptor_Curve curve1(TopoDS::Edge(shape1)); | |
| BRepAdaptor_Curve curve2(TopoDS::Edge(shape2)); | |
| // Only permit line to line distance | |
| if (curve1.GetType() == GeomAbs_Line && curve2.GetType() == GeomAbs_Line) { | |
| BRepExtrema_DistShapeShape extrema(shape1, shape2); | |
| if (extrema.IsDone()) { | |
| // Found the nearest point between point and curve | |
| // NOTE we will assume there is only 1 solution (cyclic topology will create | |
| // multiple solutions. | |
| gp_Pnt P1 = extrema.PointOnShape1(1); | |
| gp_Pnt P2 = extrema.PointOnShape2(1); | |
| gp_XYZ diff = P2.XYZ() - P1.XYZ(); | |
| result = Base::Vector3d(diff.X(), diff.Y(), diff.Z()); | |
| } | |
| } | |
| } | |
| } | |
| else { | |
| Base::Console().error("Measurement::delta - measureType is not recognized\n"); | |
| } | |
| } | |
| return result; | |
| } | |
| double Measurement::volume() const | |
| { | |
| double result = 0.0; | |
| if (References3D.getSize() == 0) { | |
| Base::Console().error("Measurement::volume - No 3D references available\n"); | |
| } | |
| else if (measureType != MeasureType::Volumes) { | |
| Base::Console().error("Measurement::volume - measureType is not Volumes\n"); | |
| } | |
| else { | |
| const std::vector<App::DocumentObject*>& objects = References3D.getValues(); | |
| const std::vector<std::string>& subElements = References3D.getSubValues(); | |
| for (size_t i = 0; i < objects.size(); ++i) { | |
| GProp_GProps props = GProp_GProps(); | |
| TopoDS_Shape shape = getShape(objects[i], subElements[i].c_str()); | |
| if (shape.IsNull() || shape.Infinite()) { | |
| continue; | |
| } | |
| BRepGProp::VolumeProperties(shape, props); | |
| result += props.Mass(); | |
| } | |
| } | |
| return result; | |
| } | |
| double Measurement::area() const | |
| { | |
| double result = 0.0; | |
| if (References3D.getSize() == 0) { | |
| Base::Console().error("Measurement::area - No 3D references available\n"); | |
| } | |
| else if (measureType == MeasureType::Volumes || measureType == MeasureType::Surfaces | |
| || measureType == MeasureType::Cylinder || measureType == MeasureType::CylinderSection | |
| || measureType == MeasureType::TwoCylinders || measureType == MeasureType::Cone | |
| || measureType == MeasureType::Sphere || measureType == MeasureType::Torus | |
| || measureType == MeasureType::Plane) { | |
| const std::vector<App::DocumentObject*>& objects = References3D.getValues(); | |
| const std::vector<std::string>& subElements = References3D.getSubValues(); | |
| for (size_t i = 0; i < objects.size(); ++i) { | |
| GProp_GProps props; | |
| TopoDS_Shape shape = getShape(objects[i], subElements[i].c_str()); | |
| if (shape.IsNull() || shape.Infinite()) { | |
| continue; | |
| } | |
| BRepGProp::SurfaceProperties(shape, props); | |
| result += props.Mass(); // Area is obtained using Mass method for surface properties | |
| } | |
| } | |
| else { | |
| Base::Console().error("Measurement::area - measureType is not valid\n"); | |
| } | |
| return result; | |
| } | |
| Base::Vector3d Measurement::massCenter() const | |
| { | |
| Base::Vector3d result; | |
| int numRefs = References3D.getSize(); | |
| if (numRefs == 0) { | |
| Base::Console().error("Measurement::massCenter - No 3D references available\n"); | |
| } | |
| else if (measureType == MeasureType::Invalid) { | |
| Base::Console().error("Measurement::massCenter - measureType is Invalid\n"); | |
| } | |
| else { | |
| const std::vector<App::DocumentObject*>& objects = References3D.getValues(); | |
| const std::vector<std::string>& subElements = References3D.getSubValues(); | |
| GProp_GProps gprops = GProp_GProps(); | |
| if (measureType == MeasureType::Volumes) { | |
| // Iterate through edges and calculate each length | |
| std::vector<App::DocumentObject*>::const_iterator obj = objects.begin(); | |
| std::vector<std::string>::const_iterator subEl = subElements.begin(); | |
| for (; obj != objects.end(); ++obj, ++subEl) { | |
| // Compute inertia properties | |
| GProp_GProps props = GProp_GProps(); | |
| TopoDS_Shape shape = ShapeFinder::getLocatedShape(*(*obj), ""); | |
| if (shape.IsNull()) { | |
| continue; | |
| } | |
| BRepGProp::VolumeProperties(shape, props); | |
| gprops.Add(props); | |
| // Get inertia properties | |
| } | |
| gp_Pnt cog = gprops.CentreOfMass(); | |
| return Base::Vector3d(cog.X(), cog.Y(), cog.Z()); | |
| } | |
| else { | |
| Base::Console().error("Measurement::massCenter - measureType is not recognized\n"); | |
| } | |
| } | |
| return result; | |
| } | |
| bool Measurement::planesAreParallel() const | |
| { | |
| const std::vector<App::DocumentObject*>& objects = References3D.getValues(); | |
| const std::vector<std::string>& subElements = References3D.getSubValues(); | |
| std::vector<gp_Dir> planeNormals; | |
| for (size_t i = 0; i < objects.size(); ++i) { | |
| TopoDS_Shape refSubShape; | |
| try { | |
| refSubShape = Part::Feature::getShape( | |
| objects[i], | |
| Part::ShapeOption::NeedSubElement | Part::ShapeOption::ResolveLink | |
| | Part::ShapeOption::Transform, | |
| subElements[i].c_str() | |
| ); | |
| if (refSubShape.IsNull()) { | |
| return false; | |
| } | |
| } | |
| catch (Standard_Failure& e) { | |
| std::stringstream errorMsg; | |
| errorMsg << "Measurement - planesAreParallel - " << e.GetMessageString() << std::endl; | |
| throw Base::CADKernelError(e.GetMessageString()); | |
| } | |
| if (refSubShape.ShapeType() == TopAbs_FACE) { | |
| TopoDS_Face face = TopoDS::Face(refSubShape); | |
| BRepAdaptor_Surface sf(face); | |
| if (sf.GetType() == GeomAbs_Plane) { | |
| gp_Pln plane = sf.Plane(); | |
| gp_Dir normal = plane.Axis().Direction(); | |
| planeNormals.push_back(normal); | |
| } | |
| } | |
| } | |
| if (planeNormals.size() != 2) { | |
| return false; // Ensure exactly two planes are considered | |
| } | |
| // Check if normals are parallel (either identical or opposite) | |
| const gp_Dir& normal1 = planeNormals[0]; | |
| const gp_Dir& normal2 = planeNormals[1]; | |
| return normal1.IsParallel(normal2, Precision::Angular()); | |
| } | |
| bool Measurement::linesAreParallel() const | |
| { | |
| const std::vector<App::DocumentObject*>& objects = References3D.getValues(); | |
| const std::vector<std::string>& subElements = References3D.getSubValues(); | |
| if (References3D.getSize() != 2) { | |
| return false; | |
| } | |
| // Get the first line | |
| TopoDS_Shape shape1 = getShape(objects[0], subElements[0].c_str(), TopAbs_EDGE); | |
| if (shape1.IsNull()) { | |
| return false; | |
| } | |
| const TopoDS_Edge& edge1 = TopoDS::Edge(shape1); | |
| BRepAdaptor_Curve curve1(edge1); | |
| // Get the second line | |
| TopoDS_Shape shape2 = getShape(objects[1], subElements[1].c_str(), TopAbs_EDGE); | |
| if (shape2.IsNull()) { | |
| return false; | |
| } | |
| const TopoDS_Edge& edge2 = TopoDS::Edge(shape2); | |
| BRepAdaptor_Curve curve2(edge2); | |
| if (curve1.GetType() == GeomAbs_Line && curve2.GetType() == GeomAbs_Line) { | |
| gp_Lin line1 = curve1.Line(); | |
| gp_Lin line2 = curve2.Line(); | |
| gp_Dir dir1 = line1.Direction(); | |
| gp_Dir dir2 = line2.Direction(); | |
| // Check if lines are parallel | |
| if (dir1.IsParallel(dir2, Precision::Angular())) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| unsigned int Measurement::getMemSize() const | |
| { | |
| return 0; | |
| } | |
| PyObject* Measurement::getPyObject() | |
| { | |
| if (PythonObject.is(Py::_None())) { | |
| // ref counter is set to 1 | |
| PythonObject = Py::Object(new MeasurementPy(this), true); | |
| } | |
| return Py::new_reference_to(PythonObject); | |
| } | |