diff --git a/src/Mod/Measure/App/Measurement.cpp b/src/Mod/Measure/App/Measurement.cpp index 308890b321..6d0e21c985 100644 --- a/src/Mod/Measure/App/Measurement.cpp +++ b/src/Mod/Measure/App/Measurement.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -113,9 +114,11 @@ MeasureType Measurement::findType() 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; @@ -155,7 +158,12 @@ MeasureType Measurement::findType() lines++; } else if (sf.GetType() == GeomAbs_Circle) { - circles++; + if (sf.IsClosed()) { + circles++; + } + else { + circleArcs++; + } } } break; case TopAbs_FACE: { @@ -167,7 +175,12 @@ MeasureType Measurement::findType() planes++; } else if (sf.GetType() == GeomAbs_Cylinder) { - cylinders++; + if (sf.IsUClosed() || sf.IsVClosed()) { + cylinders++; + } + else { + cylinderSections++; + } } else if (sf.GetType() == GeomAbs_Sphere) { spheres++; @@ -201,9 +214,19 @@ MeasureType Measurement::findType() } else if (faces > 0) { if (verts > 0 || edges > 0) { - if (faces == 1 && verts == 1) { + 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; } @@ -223,6 +246,12 @@ MeasureType Measurement::findType() 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; } @@ -242,6 +271,9 @@ MeasureType Measurement::findType() if (verts > 1) { mode = MeasureType::Invalid; } + else if ((circles + circleArcs) == 1) { + mode = MeasureType::PointToCircle; + } else { mode = MeasureType::PointToEdge; } @@ -260,6 +292,15 @@ MeasureType Measurement::findType() 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; } @@ -312,14 +353,17 @@ double Measurement::length() const const std::vector& subElements = References3D.getSubValues(); if (measureType == MeasureType::Points || measureType == MeasureType::PointToPoint - || measureType == MeasureType::PointToEdge - || measureType == MeasureType::PointToSurface) { + || 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::TwoLines || measureType == MeasureType::Circle + || measureType == MeasureType::CircleArc || measureType == MeasureType::TwoCircles + || measureType == MeasureType::CircleToEdge) { // Iterate through edges and calculate each length std::vector::const_iterator obj = objects.begin(); @@ -423,7 +467,84 @@ double Measurement::lineLineDistance() const } return distance; } +double Measurement::circleCenterDistance() const +{ + double distance = 0.0; + if (References3D.getSize() != 2) { + return distance; + } + + const std::vector& objects = References3D.getValues(); + const std::vector& 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) { @@ -459,6 +580,61 @@ double Measurement::planePlaneDistance() const return distance; } +double Measurement::cylinderAxisDistance() const +{ + double distance = 0.0; + + if (References3D.getSize() != 2) { + return distance; + } + + const std::vector& objects = References3D.getValues(); + const std::vector& 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 { @@ -529,6 +705,44 @@ double Measurement::angle(const Base::Vector3d& /*param*/) const return Base::toDegrees(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( + std::min(aRad, std::fmod(aRad + std::numbers::pi, 2.0 * std::numbers::pi))); + } + } throw Base::RuntimeError("Unexpected error for angle measurement"); } @@ -541,7 +755,7 @@ double Measurement::radius() const if (numRefs == 0) { Base::Console().error("Measurement::radius - No 3D references available\n"); } - else if (measureType == MeasureType::Circle) { + 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); @@ -550,8 +764,8 @@ double Measurement::radius() const return (double)curve.Circle().Radius(); } } - else if (measureType == MeasureType::Cylinder || measureType == MeasureType::Sphere - || measureType == MeasureType::Torus) { + 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); @@ -569,6 +783,36 @@ double Measurement::radius() const Base::Console().error("Measurement::radius - Invalid References3D Provided\n"); return 0.0; } +double Measurement::diameter() const +{ + const std::vector& objects = References3D.getValues(); + const std::vector& 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 { @@ -602,7 +846,9 @@ Base::Vector3d Measurement::delta() const } } else if (measureType == MeasureType::PointToEdge - || measureType == MeasureType::PointToSurface) { + || 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()); @@ -701,7 +947,8 @@ double Measurement::area() const Base::Console().error("Measurement::area - No 3D references available\n"); } else if (measureType == MeasureType::Volumes || measureType == MeasureType::Surfaces - || measureType == MeasureType::Cylinder || measureType == MeasureType::Cone + || measureType == MeasureType::Cylinder || measureType == MeasureType::CylinderSection + || measureType == MeasureType::TwoCylinders || measureType == MeasureType::Cone || measureType == MeasureType::Sphere || measureType == MeasureType::Torus || measureType == MeasureType::Plane) { diff --git a/src/Mod/Measure/App/Measurement.h b/src/Mod/Measure/App/Measurement.h index 5e3b619b95..4b0e866e1b 100644 --- a/src/Mod/Measure/App/Measurement.h +++ b/src/Mod/Measure/App/Measurement.h @@ -45,18 +45,27 @@ enum class MeasureType TwoLines, // Two lines TwoParallelLines, // Two parallel lines Circle, // One circle - Surfaces, // Measure the surface(s) - Cylinder, // One Cylinder - Cone, // One Cone - Sphere, // One Sphere - Torus, // One Torus - Plane, // One Plane - TwoPlanes, // One Plane + CircleArc, // One circle arc + TwoCircles, + CircleToEdge, + CircleToSurface, + CircleToCylinder, + Surfaces, // Measure the surface(s) + Cylinder, // One Cylinder + CylinderSection, // One cylinder section + TwoCylinders, + Cone, // One Cone + Sphere, // One Sphere + Torus, // One Torus + Plane, // One Plane + TwoPlanes, // One Plane Points, - PointToPoint, // Measure between TWO points - PointToEdge, // Measure between ONE point and ONE edge + PointToPoint, // Measure between TWO points + PointToEdge, // Measure between ONE point and ONE edge + PointToCircle, PointToSurface, // Measure between ONE point and ONE surface - EdgeToEdge, // Measure between TWO edges + PointToCylinder, + EdgeToEdge, // Measure between TWO edges Invalid }; @@ -89,11 +98,16 @@ public: double length() const; Base::Vector3d delta() const; // when would client use delta?? double lineLineDistance() const; + double circleCenterDistance() const; double planePlaneDistance() const; + double cylinderAxisDistance() const; // Calculates the radius for an arc or circular edge double radius() const; + // Calculates the diameter for a circle or a cylinder + double diameter() const; + // Calculates the angle between two edges double angle(const Base::Vector3d& param = Base::Vector3d(0, 0, 0)) const; // param is never used??? diff --git a/src/Mod/Measure/Gui/QuickMeasure.cpp b/src/Mod/Measure/Gui/QuickMeasure.cpp index 6ad953db88..dc521da1a4 100644 --- a/src/Mod/Measure/Gui/QuickMeasure.cpp +++ b/src/Mod/Measure/Gui/QuickMeasure.cpp @@ -218,11 +218,31 @@ void QuickMeasure::printResult() else if (mtype == MeasureType::Cone || mtype == MeasureType::Plane) { print(tr("Area: %1").arg(areaStr(measurement->area()))); } - else if (mtype == MeasureType::Cylinder || mtype == MeasureType::Sphere + else if (mtype == MeasureType::CylinderSection || mtype == MeasureType::Sphere || mtype == MeasureType::Torus) { print(tr("Area: %1, Radius: %2") .arg(areaStr(measurement->area()), lengthStr(measurement->radius()))); } + else if (mtype == MeasureType::Cylinder) { + print(tr("Area: %1, Diameter: %2") + .arg(areaStr(measurement->area()), lengthStr(measurement->diameter()))); + } + else if (mtype == MeasureType::TwoCylinders) { + + double angle = measurement->angle(); + + if (angle <= Precision::Confusion()) { + print(tr("Total area: %1, Axis distance: %2") + .arg(areaStr(measurement->area()), + lengthStr(measurement->cylinderAxisDistance()))); + } + else { + print(tr("Total area: %1, Axis distance: %2, Axis angle: %3") + .arg(areaStr(measurement->area()), + lengthStr(measurement->cylinderAxisDistance()), + angleStr(angle))); + } + } else if (mtype == MeasureType::Edges) { print(tr("Total length: %1").arg(lengthStr(measurement->length()))); } @@ -236,15 +256,62 @@ void QuickMeasure::printResult() else if (mtype == MeasureType::Line) { print(tr("Length: %1").arg(lengthStr(measurement->length()))); } - else if (mtype == MeasureType::Circle) { + else if (mtype == MeasureType::CircleArc) { print(tr("Radius: %1").arg(lengthStr(measurement->radius()))); } + else if (mtype == MeasureType::Circle) { + print(tr("Diameter: %1").arg(lengthStr(measurement->diameter()))); + } else if (mtype == MeasureType::PointToPoint) { print(tr("Distance: %1").arg(lengthStr(measurement->length()))); } else if (mtype == MeasureType::PointToEdge || mtype == MeasureType::PointToSurface) { print(tr("Minimum distance: %1").arg(lengthStr(measurement->length()))); } + else if (mtype == MeasureType::PointToCylinder) { + print(tr("Minimum distance: %1, Axis distance: %2") + .arg(lengthStr(measurement->length()), + lengthStr(measurement->cylinderAxisDistance()))); + } + else if (mtype == MeasureType::PointToCircle) { + print(tr("Minimum distance: %1, Center distance: %2") + .arg(lengthStr(measurement->length()), + lengthStr(measurement->circleCenterDistance()))); + } + else if (mtype == MeasureType::TwoCircles) { + double angle = measurement->angle(); + if (angle <= Precision::Confusion()) { + print(tr("Total length: %1, Center distance: %2") + .arg(lengthStr(measurement->length()), + lengthStr(measurement->circleCenterDistance()))); + } + else { + print(tr("Total length: %1, Center distance: %2, Axis angle: %3") + .arg(lengthStr(measurement->length()), + lengthStr(measurement->circleCenterDistance()), + angleStr(angle))); + } + } + else if (mtype == MeasureType::CircleToEdge) { + print(tr("Total length: %1, Center distance: %2") + .arg(lengthStr(measurement->length()), + lengthStr(measurement->circleCenterDistance()))); + } + else if (mtype == MeasureType::CircleToSurface) { + print( + tr("Center surface distance: %1").arg(lengthStr(measurement->circleCenterDistance()))); + } + else if (mtype == MeasureType::CircleToCylinder) { + double angle = measurement->angle(); + if (angle <= Precision::Confusion()) { + print( + tr("Center axis distance: %1").arg(lengthStr(measurement->cylinderAxisDistance()))); + } + else { + print(tr("Center axis distance: %1, Axis angle: %2") + .arg(lengthStr(measurement->cylinderAxisDistance()), angleStr(angle))); + } + } else { print(QStringLiteral("")); }