23
#include "PreCompiled.h"
25
#include <BRep_Tool.hxx>
26
#include <BRepAdaptor_Curve.hxx>
27
#include <BRepAdaptor_Surface.hxx>
28
#include <BRepExtrema_DistShapeShape.hxx>
29
#include <BRepGProp.hxx>
30
#include <GCPnts_AbscissaPoint.hxx>
33
#include <gp_Torus.hxx>
34
#include <gp_Cylinder.hxx>
35
#include <gp_Sphere.hxx>
37
#include <GProp_GProps.hxx>
39
#include <TopoDS_Shape.hxx>
43
#include <Base/Console.h>
44
#include <Base/Exception.h>
45
#include <Mod/Part/App/PartFeature.h>
46
#include <Mod/Part/App/TopoShape.h>
48
#include "Measurement.h"
49
#include "MeasurementPy.h"
53
#define M_PI 3.14159265358979323846
56
using namespace Measure;
60
TYPESYSTEM_SOURCE(Measure::Measurement, Base::BaseClass)
62
Measurement::Measurement()
64
measureType = MeasureType::Invalid;
65
References3D.setScope(App::LinkScope::Global);
68
Measurement::~Measurement() = default;
70
void Measurement::clear()
72
std::vector<App::DocumentObject*> Objects;
73
std::vector<std::string> SubElements;
74
References3D.setValues(Objects, SubElements);
75
measureType = MeasureType::Invalid;
78
bool Measurement::has3DReferences()
80
return (References3D.getSize() > 0);
84
int Measurement::addReference3D(App::DocumentObject* obj, const std::string& subName)
86
return addReference3D(obj, subName.c_str());
90
int Measurement::addReference3D(App::DocumentObject* obj, const char* subName)
92
std::vector<App::DocumentObject*> objects = References3D.getValues();
93
std::vector<std::string> subElements = References3D.getSubValues();
95
objects.push_back(obj);
96
subElements.emplace_back(subName);
98
References3D.setValues(objects, subElements);
100
measureType = findType();
101
return References3D.getSize();
104
MeasureType Measurement::findType()
106
const std::vector<App::DocumentObject*>& objects = References3D.getValues();
107
const std::vector<std::string>& subElements = References3D.getSubValues();
109
std::vector<App::DocumentObject*>::const_iterator obj = objects.begin();
110
std::vector<std::string>::const_iterator subEl = subElements.begin();
126
for (; obj != objects.end(); ++obj, ++subEl) {
129
if (strcmp((*subEl).c_str(), "") == 0) {
134
TopoDS_Shape refSubShape;
136
refSubShape = Part::Feature::getShape(*obj, (*subEl).c_str(), true);
137
if (refSubShape.IsNull()) {
138
return MeasureType::Invalid;
141
catch (Standard_Failure& e) {
142
std::stringstream errorMsg;
144
errorMsg << "Measurement - getType - " << e.GetMessageString() << std::endl;
145
throw Base::CADKernelError(e.GetMessageString());
148
switch (refSubShape.ShapeType()) {
149
case TopAbs_VERTEX: {
154
TopoDS_Edge edge = TopoDS::Edge(refSubShape);
155
BRepAdaptor_Curve sf(edge);
157
if (sf.GetType() == GeomAbs_Line) {
160
else if (sf.GetType() == GeomAbs_Circle) {
166
TopoDS_Face face = TopoDS::Face(refSubShape);
167
BRepAdaptor_Surface sf(face);
169
if (sf.GetType() == GeomAbs_Plane) {
172
else if (sf.GetType() == GeomAbs_Cylinder) {
175
else if (sf.GetType() == GeomAbs_Sphere) {
178
else if (sf.GetType() == GeomAbs_Cone) {
181
else if (sf.GetType() == GeomAbs_Torus) {
192
if (verts > 0 || edges > 0 || faces > 0) {
193
mode = MeasureType::Invalid;
196
mode = MeasureType::Volumes;
199
else if (faces > 0) {
200
if (verts > 0 || edges > 0) {
201
if (faces == 1 && verts == 1) {
202
mode = MeasureType::PointToSurface;
205
mode = MeasureType::Invalid;
209
if (planes == 1 && faces == 1) {
210
mode = MeasureType::Plane;
212
else if (planes == 2 && faces == 2) {
213
if (planesAreParallel()) {
214
mode = MeasureType::TwoPlanes;
217
mode = MeasureType::Surfaces;
220
else if (cylinders == 1 && faces == 1) {
221
mode = MeasureType::Cylinder;
223
else if (cones == 1 && faces == 1) {
224
mode = MeasureType::Cone;
226
else if (spheres == 1 && faces == 1) {
227
mode = MeasureType::Sphere;
229
else if (torus == 1 && faces == 1) {
230
mode = MeasureType::Torus;
233
mode = MeasureType::Surfaces;
237
else if (edges > 0) {
239
if (verts > 1 && edges > 0) {
240
mode = MeasureType::Invalid;
243
mode = MeasureType::PointToEdge;
246
else if (lines == 1 && edges == 1) {
247
mode = MeasureType::Line;
249
else if (lines == 2 && edges == 2) {
250
if (linesAreParallel()) {
251
mode = MeasureType::TwoParallelLines;
254
mode = MeasureType::TwoLines;
257
else if (circles == 1 && edges == 1) {
258
mode = MeasureType::Circle;
261
mode = MeasureType::Edges;
264
else if (verts > 0) {
266
mode = MeasureType::PointToPoint;
269
mode = MeasureType::Points;
273
mode = MeasureType::Invalid;
279
MeasureType Measurement::getType()
284
TopoDS_Shape Measurement::getShape(App::DocumentObject* obj, const char* subName) const
289
if (strcmp(subName, "") == 0) {
290
return Part::Feature::getShape(obj);
292
std::string workingSubName(subName);
293
size_t lastDot = workingSubName.rfind('.');
294
if (lastDot != std::string::npos) {
295
workingSubName = workingSubName.substr(lastDot + 1);
299
Part::TopoShape partShape = Part::Feature::getTopoShape(obj);
300
App::GeoFeature* geoFeat = dynamic_cast<App::GeoFeature*>(obj);
302
partShape.setPlacement(geoFeat->globalPlacement());
304
TopoDS_Shape shape = partShape.getSubShape(workingSubName.c_str());
305
if (shape.IsNull()) {
306
throw Part::NullShapeException("null shape in measurement");
310
catch (const Base::Exception&) {
314
catch (Standard_Failure& e) {
315
throw Base::CADKernelError(e.GetMessageString());
318
throw Base::RuntimeError("Measurement: Unknown error retrieving shape");
324
double Measurement::length() const
327
int numRefs = References3D.getSize();
329
Base::Console().Error("Measurement::length - No 3D references available\n");
331
else if (measureType == MeasureType::Invalid) {
332
Base::Console().Error("Measurement::length - measureType is Invalid\n");
335
const std::vector<App::DocumentObject*>& objects = References3D.getValues();
336
const std::vector<std::string>& subElements = References3D.getSubValues();
338
if (measureType == MeasureType::Points || measureType == MeasureType::PointToPoint
339
|| measureType == MeasureType::PointToEdge
340
|| measureType == MeasureType::PointToSurface) {
342
Base::Vector3d diff = this->delta();
343
result = diff.Length();
345
else if (measureType == MeasureType::Edges || measureType == MeasureType::Line
346
|| measureType == MeasureType::TwoLines || measureType == MeasureType::Circle) {
349
std::vector<App::DocumentObject*>::const_iterator obj = objects.begin();
350
std::vector<std::string>::const_iterator subEl = subElements.begin();
352
for (; obj != objects.end(); ++obj, ++subEl) {
355
TopoDS_Shape shape = getShape(*obj, (*subEl).c_str());
356
const TopoDS_Edge& edge = TopoDS::Edge(shape);
357
BRepAdaptor_Curve curve(edge);
359
switch (curve.GetType()) {
361
gp_Pnt P1 = curve.Value(curve.FirstParameter());
362
gp_Pnt P2 = curve.Value(curve.LastParameter());
363
gp_XYZ diff = P2.XYZ() - P1.XYZ();
364
result += diff.Modulus();
367
case GeomAbs_Circle: {
368
double u = curve.FirstParameter();
369
double v = curve.LastParameter();
370
double radius = curve.Circle().Radius();
375
double range = v - u;
376
result += radius * range;
379
case GeomAbs_Ellipse:
380
case GeomAbs_BSplineCurve:
381
case GeomAbs_Hyperbola:
382
case GeomAbs_BezierCurve: {
383
result += GCPnts_AbscissaPoint::Length(curve);
387
throw Base::RuntimeError(
388
"Measurement - length - Curve type not currently handled");
397
double Measurement::lineLineDistance() const
401
double distance = 0.0;
403
if (measureType != MeasureType::TwoParallelLines || References3D.getSize() != 2) {
407
const std::vector<App::DocumentObject*>& objects = References3D.getValues();
408
const std::vector<std::string>& subElements = References3D.getSubValues();
411
TopoDS_Shape shape1 = getShape(objects[0], subElements[0].c_str());
412
const TopoDS_Edge& edge1 = TopoDS::Edge(shape1);
413
BRepAdaptor_Curve curve1(edge1);
416
TopoDS_Shape shape2 = getShape(objects[1], subElements[1].c_str());
417
const TopoDS_Edge& edge2 = TopoDS::Edge(shape2);
418
BRepAdaptor_Curve curve2(edge2);
420
if (curve1.GetType() == GeomAbs_Line && curve2.GetType() == GeomAbs_Line) {
421
gp_Lin line1 = curve1.Line();
422
gp_Lin line2 = curve2.Line();
424
gp_Pnt p1 = line1.Location();
425
gp_Pnt p2 = line2.Location();
428
gp_Vec lineVec(p1, p2);
431
gp_Dir lineDir = line1.Direction();
434
gp_Vec parallelComponent = lineVec.Dot(lineDir) * lineDir;
437
gp_Vec perpendicularComponent = lineVec - parallelComponent;
440
distance = perpendicularComponent.Magnitude();
443
Base::Console().Error("Measurement::length - TwoLines measureType requires two lines\n");
448
double Measurement::planePlaneDistance() const
450
if (measureType != MeasureType::TwoPlanes || References3D.getSize() != 2) {
454
const auto& objects = References3D.getValues();
455
const auto& subElements = References3D.getSubValues();
457
std::vector<gp_Pln> planes;
460
TopoDS_Shape shape1 = getShape(objects[0], subElements[0].c_str());
461
const TopoDS_Face& face1 = TopoDS::Face(shape1);
462
BRepAdaptor_Surface surface1(face1);
463
const gp_Pln& plane1 = surface1.Plane();
466
TopoDS_Shape shape2 = getShape(objects[1], subElements[1].c_str());
467
const TopoDS_Face& face2 = TopoDS::Face(shape2);
468
BRepAdaptor_Surface surface2(face2);
469
const gp_Pln& plane2 = surface2.Plane();
472
gp_Pnt pointOnPlane1 = plane1.Location();
473
gp_Dir normalToPlane1 = plane1.Axis().Direction();
475
gp_Pnt pointOnPlane2 = plane2.Location();
478
gp_Vec vectorBetweenPlanes(pointOnPlane1, pointOnPlane2);
481
double distance = Abs(vectorBetweenPlanes.Dot(normalToPlane1));
486
double Measurement::angle(const Base::Vector3d& ) const
489
const std::vector<App::DocumentObject*>& objects = References3D.getValues();
490
const std::vector<std::string>& subElements = References3D.getSubValues();
491
int numRefs = objects.size();
493
throw Base::RuntimeError("No references available for angle measurement");
495
else if (measureType == MeasureType::Invalid) {
496
throw Base::RuntimeError("MeasureType is Invalid for angle measurement");
498
else if (measureType == MeasureType::TwoLines) {
505
TopoDS_Shape shape1 = getShape(objects.at(0), subElements.at(0).c_str());
506
TopoDS_Shape shape2 = getShape(objects.at(1), subElements.at(1).c_str());
508
BRepAdaptor_Curve curve1(TopoDS::Edge(shape1));
509
BRepAdaptor_Curve curve2(TopoDS::Edge(shape2));
511
if (curve1.GetType() == GeomAbs_Line && curve2.GetType() == GeomAbs_Line) {
513
gp_Pnt pnt1First = curve1.Value(curve1.FirstParameter());
514
gp_Dir dir1 = curve1.Line().Direction();
515
gp_Dir dir2 = curve2.Line().Direction();
516
gp_Dir dir2r = curve2.Line().Direction().Reversed();
518
gp_Lin l1 = gp_Lin(pnt1First, dir1);
519
gp_Lin l2 = gp_Lin(pnt1First, dir2);
520
gp_Lin l2r = gp_Lin(pnt1First, dir2r);
521
Standard_Real aRad = l1.Angle(l2);
522
double aRadr = l1.Angle(l2r);
523
return std::min(aRad, aRadr) * 180 / M_PI;
526
throw Base::RuntimeError("Measurement references must both be lines");
530
throw Base::RuntimeError("Can not compute angle measurement - too many references");
533
else if (measureType == MeasureType::Points) {
537
TopoDS_Shape shape0 = getShape(objects.at(0), subElements.at(0).c_str());
538
TopoDS_Shape shape1 = getShape(objects.at(1), subElements.at(1).c_str());
539
TopoDS_Shape shape2 = getShape(objects.at(1), subElements.at(2).c_str());
540
if (shape0.ShapeType() != TopAbs_VERTEX || shape1.ShapeType() != TopAbs_VERTEX
541
|| shape2.ShapeType() != TopAbs_VERTEX) {
542
throw Base::RuntimeError("Measurement references for 3 point angle are not Vertex");
544
gp_Pnt gEnd0 = BRep_Tool::Pnt(TopoDS::Vertex(shape0));
545
gp_Pnt gApex = BRep_Tool::Pnt(TopoDS::Vertex(shape1));
546
gp_Pnt gEnd1 = BRep_Tool::Pnt(TopoDS::Vertex(shape2));
547
gp_Dir gDir0 = gp_Dir(gEnd0.XYZ() - gApex.XYZ());
548
gp_Dir gDir1 = gp_Dir(gEnd1.XYZ() - gApex.XYZ());
549
gp_Lin line0 = gp_Lin(gEnd0, gDir0);
550
gp_Lin line1 = gp_Lin(gEnd1, gDir1);
551
double radians = line0.Angle(line1);
552
return radians * 180 / M_PI;
555
throw Base::RuntimeError("Unexpected error for angle measurement");
558
double Measurement::radius() const
560
const std::vector<App::DocumentObject*>& objects = References3D.getValues();
561
const std::vector<std::string>& subElements = References3D.getSubValues();
563
int numRefs = References3D.getSize();
565
Base::Console().Error("Measurement::radius - No 3D references available\n");
567
else if (measureType == MeasureType::Circle) {
568
TopoDS_Shape shape = getShape(objects.at(0), subElements.at(0).c_str());
569
const TopoDS_Edge& edge = TopoDS::Edge(shape);
571
BRepAdaptor_Curve curve(edge);
572
if (curve.GetType() == GeomAbs_Circle) {
573
return (double)curve.Circle().Radius();
576
else if (measureType == MeasureType::Cylinder || measureType == MeasureType::Sphere
577
|| measureType == MeasureType::Torus) {
578
TopoDS_Shape shape = getShape(objects.at(0), subElements.at(0).c_str());
579
TopoDS_Face face = TopoDS::Face(shape);
581
BRepAdaptor_Surface sf(face);
582
if (sf.GetType() == GeomAbs_Cylinder) {
583
return sf.Cylinder().Radius();
585
else if (sf.GetType() == GeomAbs_Sphere) {
586
return sf.Sphere().Radius();
588
else if (sf.GetType() == GeomAbs_Torus) {
589
return sf.Torus().MinorRadius();
592
Base::Console().Error("Measurement::radius - Invalid References3D Provided\n");
596
Base::Vector3d Measurement::delta() const
598
Base::Vector3d result;
599
int numRefs = References3D.getSize();
601
Base::Console().Error("Measurement::delta - No 3D references available\n");
603
else if (measureType == MeasureType::Invalid) {
604
Base::Console().Error("Measurement::delta - measureType is Invalid\n");
607
const std::vector<App::DocumentObject*>& objects = References3D.getValues();
608
const std::vector<std::string>& subElements = References3D.getSubValues();
610
if (measureType == MeasureType::PointToPoint) {
613
TopoDS_Shape shape1 = getShape(objects.at(0), subElements.at(0).c_str());
614
TopoDS_Shape shape2 = getShape(objects.at(1), subElements.at(1).c_str());
616
const TopoDS_Vertex& vert1 = TopoDS::Vertex(shape1);
617
const TopoDS_Vertex& vert2 = TopoDS::Vertex(shape2);
619
gp_Pnt P1 = BRep_Tool::Pnt(vert1);
620
gp_Pnt P2 = BRep_Tool::Pnt(vert2);
621
gp_XYZ diff = P2.XYZ() - P1.XYZ();
622
return Base::Vector3d(diff.X(), diff.Y(), diff.Z());
625
else if (measureType == MeasureType::PointToEdge
626
|| measureType == MeasureType::PointToSurface) {
629
TopoDS_Shape shape1 = getShape(objects.at(0), subElements.at(0).c_str());
630
TopoDS_Shape shape2 = getShape(objects.at(1), subElements.at(1).c_str());
632
BRepExtrema_DistShapeShape extrema(shape1, shape2);
634
if (extrema.IsDone()) {
638
gp_Pnt P1 = extrema.PointOnShape1(1);
639
gp_Pnt P2 = extrema.PointOnShape2(1);
640
gp_XYZ diff = P2.XYZ() - P1.XYZ();
641
result = Base::Vector3d(diff.X(), diff.Y(), diff.Z());
645
else if (measureType == MeasureType::Edges) {
648
TopoDS_Shape shape = getShape(objects.at(0), subElements.at(0).c_str());
649
const TopoDS_Edge& edge = TopoDS::Edge(shape);
650
BRepAdaptor_Curve curve(edge);
652
if (curve.GetType() == GeomAbs_Line) {
653
gp_Pnt P1 = curve.Value(curve.FirstParameter());
654
gp_Pnt P2 = curve.Value(curve.LastParameter());
655
gp_XYZ diff = P2.XYZ() - P1.XYZ();
656
result = Base::Vector3d(diff.X(), diff.Y(), diff.Z());
659
else if (numRefs == 2) {
660
TopoDS_Shape shape1 = getShape(objects.at(0), subElements.at(0).c_str());
661
TopoDS_Shape shape2 = getShape(objects.at(1), subElements.at(1).c_str());
663
BRepAdaptor_Curve curve1(TopoDS::Edge(shape1));
664
BRepAdaptor_Curve curve2(TopoDS::Edge(shape2));
667
if (curve1.GetType() == GeomAbs_Line && curve2.GetType() == GeomAbs_Line) {
668
BRepExtrema_DistShapeShape extrema(shape1, shape2);
670
if (extrema.IsDone()) {
674
gp_Pnt P1 = extrema.PointOnShape1(1);
675
gp_Pnt P2 = extrema.PointOnShape2(1);
676
gp_XYZ diff = P2.XYZ() - P1.XYZ();
677
result = Base::Vector3d(diff.X(), diff.Y(), diff.Z());
683
Base::Console().Error("Measurement::delta - measureType is not recognized\n");
689
double Measurement::volume() const
692
if (References3D.getSize() == 0) {
693
Base::Console().Error("Measurement::volume - No 3D references available\n");
695
else if (measureType != MeasureType::Volumes) {
696
Base::Console().Error("Measurement::volume - measureType is not Volumes\n");
699
const std::vector<App::DocumentObject*>& objects = References3D.getValues();
700
const std::vector<std::string>& subElements = References3D.getSubValues();
702
for (size_t i = 0; i < objects.size(); ++i) {
703
GProp_GProps props = GProp_GProps();
704
BRepGProp::VolumeProperties(getShape(objects[i], subElements[i].c_str()), props);
705
result += props.Mass();
711
double Measurement::area() const
714
if (References3D.getSize() == 0) {
715
Base::Console().Error("Measurement::area - No 3D references available\n");
717
else if (measureType == MeasureType::Volumes || measureType == MeasureType::Surfaces
718
|| measureType == MeasureType::Cylinder || measureType == MeasureType::Cone
719
|| measureType == MeasureType::Sphere || measureType == MeasureType::Torus
720
|| measureType == MeasureType::Plane) {
722
const std::vector<App::DocumentObject*>& objects = References3D.getValues();
723
const std::vector<std::string>& subElements = References3D.getSubValues();
725
for (size_t i = 0; i < objects.size(); ++i) {
727
BRepGProp::SurfaceProperties(getShape(objects[i], subElements[i].c_str()), props);
728
result += props.Mass();
732
Base::Console().Error("Measurement::area - measureType is not valid\n");
737
Base::Vector3d Measurement::massCenter() const
739
Base::Vector3d result;
740
int numRefs = References3D.getSize();
742
Base::Console().Error("Measurement::massCenter - No 3D references available\n");
744
else if (measureType == MeasureType::Invalid) {
745
Base::Console().Error("Measurement::massCenter - measureType is Invalid\n");
748
const std::vector<App::DocumentObject*>& objects = References3D.getValues();
749
const std::vector<std::string>& subElements = References3D.getSubValues();
750
GProp_GProps gprops = GProp_GProps();
752
if (measureType == MeasureType::Volumes) {
754
std::vector<App::DocumentObject*>::const_iterator obj = objects.begin();
755
std::vector<std::string>::const_iterator subEl = subElements.begin();
757
for (; obj != objects.end(); ++obj, ++subEl) {
761
GProp_GProps props = GProp_GProps();
762
BRepGProp::VolumeProperties(getShape((*obj), ""), props);
767
gp_Pnt cog = gprops.CentreOfMass();
769
return Base::Vector3d(cog.X(), cog.Y(), cog.Z());
772
Base::Console().Error("Measurement::massCenter - measureType is not recognized\n");
778
bool Measurement::planesAreParallel() const
780
const std::vector<App::DocumentObject*>& objects = References3D.getValues();
781
const std::vector<std::string>& subElements = References3D.getSubValues();
783
std::vector<gp_Dir> planeNormals;
785
for (size_t i = 0; i < objects.size(); ++i) {
786
TopoDS_Shape refSubShape;
788
refSubShape = Part::Feature::getShape(objects[i], subElements[i].c_str(), true);
789
if (refSubShape.IsNull()) {
793
catch (Standard_Failure& e) {
794
std::stringstream errorMsg;
795
errorMsg << "Measurement - planesAreParallel - " << e.GetMessageString() << std::endl;
796
throw Base::CADKernelError(e.GetMessageString());
799
if (refSubShape.ShapeType() == TopAbs_FACE) {
800
TopoDS_Face face = TopoDS::Face(refSubShape);
801
BRepAdaptor_Surface sf(face);
803
if (sf.GetType() == GeomAbs_Plane) {
804
gp_Pln plane = sf.Plane();
805
gp_Dir normal = plane.Axis().Direction();
806
planeNormals.push_back(normal);
811
if (planeNormals.size() != 2) {
816
const gp_Dir& normal1 = planeNormals[0];
817
const gp_Dir& normal2 = planeNormals[1];
819
return normal1.IsParallel(normal2, Precision::Angular());
822
bool Measurement::linesAreParallel() const
824
const std::vector<App::DocumentObject*>& objects = References3D.getValues();
825
const std::vector<std::string>& subElements = References3D.getSubValues();
827
if (References3D.getSize() != 2) {
832
TopoDS_Shape shape1 = getShape(objects[0], subElements[0].c_str());
833
const TopoDS_Edge& edge1 = TopoDS::Edge(shape1);
834
BRepAdaptor_Curve curve1(edge1);
837
TopoDS_Shape shape2 = getShape(objects[1], subElements[1].c_str());
838
const TopoDS_Edge& edge2 = TopoDS::Edge(shape2);
839
BRepAdaptor_Curve curve2(edge2);
841
if (curve1.GetType() == GeomAbs_Line && curve2.GetType() == GeomAbs_Line) {
842
gp_Lin line1 = curve1.Line();
843
gp_Lin line2 = curve2.Line();
845
gp_Dir dir1 = line1.Direction();
846
gp_Dir dir2 = line2.Direction();
849
if (dir1.IsParallel(dir2, Precision::Angular())) {
857
unsigned int Measurement::getMemSize() const
862
PyObject* Measurement::getPyObject()
864
if (PythonObject.is(Py::_None())) {
866
PythonObject = Py::Object(new MeasurementPy(this), true);
868
return Py::new_reference_to(PythonObject);