1
/***************************************************************************
2
* Copyright (c) 2023 Wandererfan <wandererfan@gmail.com> *
3
* Copyright (c) 2023 Joel Meijering (EDG5000) <joel@meijering.email> *
4
* Copyright (c) 2023 David Friedli <david@friedli-be.ch> *
6
* This file is part of FreeCAD. *
8
* FreeCAD is free software: you can redistribute it and/or modify it *
9
* under the terms of the GNU Lesser General Public License as *
10
* published by the Free Software Foundation, either version 2.1 of the *
11
* License, or (at your option) any later version. *
13
* FreeCAD is distributed in the hope that it will be useful, but *
14
* WITHOUT ANY WARRANTY; without even the implied warranty of *
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
16
* Lesser General Public License for more details. *
18
* You should have received a copy of the GNU Lesser General Public *
19
* License along with FreeCAD. If not, see *
20
* <https://www.gnu.org/licenses/>. *
22
**************************************************************************/
25
#include "PreCompiled.h"
27
#include <Mod/Part/PartGlobal.h>
32
#include <TopoDS_Vertex.hxx>
34
#include <BRepTools.hxx>
35
#include <BRep_Tool.hxx>
36
#include <BRepGProp.hxx>
37
#include <BRepAdaptor_Curve.hxx>
38
#include <BRepAdaptor_Surface.hxx>
40
#include <GProp_GProps.hxx>
41
#include <ShapeAnalysis_Edge.hxx>
43
#include <BRepBuilderAPI_Copy.hxx>
45
#include <DatumFeature.h>
46
#include <App/Application.h>
47
#include <App/Document.h>
48
#include <App/DocumentObject.h>
49
#include <App/MeasureManager.h>
50
#include <App/DocumentObserver.h>
51
#include <App/GeoFeature.h>
52
#include <Base/Console.h>
53
#include <Base/Matrix.h>
54
#include <Base/Rotation.h>
55
#include <Base/Vector3D.h>
57
#include "VectorAdapter.h"
58
#include "PartFeature.h"
60
#include "MeasureClient.h"
65
// From: https://github.com/Celemation/FreeCAD/blob/joel_selection_summary_demo/src/Gui/SelectionSummary.cpp
67
// Should work with edges and wires
68
static float getLength(TopoDS_Shape& wire){
70
BRepGProp::LinearProperties(wire, gprops);
74
static float getFaceArea(TopoDS_Shape& face){
76
BRepGProp::SurfaceProperties(face, gprops);
80
static float getRadius(TopoDS_Shape& edge){
81
// gprops.Mass() would be the circumference (length) of the circle (arc)
82
if (edge.ShapeType() == TopAbs_EDGE) {
83
BRepAdaptor_Curve adapt(TopoDS::Edge(edge));
84
if (adapt.GetType() != GeomAbs_Circle) {
85
// TODO: not sure what the error handling here should be. nan? 0.0?
88
gp_Circ circle = adapt.Circle();
89
return circle.Radius();
94
TopoDS_Shape getLocatedShape(const App::SubObjectT& subject, Base::Matrix4D* mat = nullptr)
96
App::DocumentObject* obj = subject.getSubObjectList().back();
101
Part::TopoShape shape = Part::Feature::getTopoShape(obj, subject.getElementName(), false, mat, nullptr, true);
102
if (shape.isNull()) {
103
Base::Console().Log("Part::MeasureClient::getLocatedShape: Did not retrieve shape for %s, %s\n", obj->getNameInDocument(), subject.getElementName());
107
auto placement = App::GeoFeature::getGlobalPlacement(obj, subject.getObject(), subject.getSubName());
108
shape.setPlacement(placement);
110
// Don't get the subShape from datum elements
111
if (obj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())) {
112
return shape.getShape();
115
if (!subject.getElementName()) {
116
return shape.getShape();
118
return shape.getSubShape(subject.getElementName(), true);
122
App::MeasureElementType PartMeasureTypeCb(App::DocumentObject* ob, const char* subName)
124
TopoDS_Shape shape = Part::Feature::getShape(ob, subName, true);
125
if (shape.IsNull()) {
126
// failure here on loading document with existing measurement.
127
Base::Console().Message("Part::PartMeasureTypeCb did not retrieve shape for %s, %s\n", ob->getNameInDocument(), subName);
128
return App::MeasureElementType();
130
TopAbs_ShapeEnum shapeType = shape.ShapeType();
133
case TopAbs_VERTEX: {
134
return App::MeasureElementType::POINT;
137
const TopoDS_Edge& edge = TopoDS::Edge(shape);
138
BRepAdaptor_Curve curve(edge);
140
switch (curve.GetType()) {
142
if (ob->getTypeId().isDerivedFrom(Base::Type::fromName("Part::Datum"))) {
143
return App::MeasureElementType::LINE;
145
return App::MeasureElementType::LINESEGMENT;
147
case GeomAbs_Circle: { return App::MeasureElementType::CIRCLE; }
148
case GeomAbs_BezierCurve:
149
case GeomAbs_BSplineCurve: {
150
return App::MeasureElementType::CURVE;
152
default: { return App::MeasureElementType::INVALID; }
156
const TopoDS_Face& face = TopoDS::Face(shape);
157
BRepAdaptor_Surface surface(face);
159
switch (surface.GetType()) {
160
case GeomAbs_Cylinder: { return App::MeasureElementType::CYLINDER; }
161
case GeomAbs_Plane: { return App::MeasureElementType::PLANE; }
162
default: { return App::MeasureElementType::INVALID; }
166
return App::MeasureElementType::Volume;
169
return App::MeasureElementType::INVALID;
175
bool getShapeFromStrings(TopoDS_Shape &shapeOut, const App::SubObjectT& subject, Base::Matrix4D *mat)
177
App::DocumentObject *obj = subject.getObject();
181
shapeOut = Part::Feature::getShape(obj, subject.getElementName(), true, mat);
182
return !shapeOut.IsNull();
186
Part::VectorAdapter buildAdapter(const App::SubObjectT& subject)
189
TopoDS_Shape shape = getLocatedShape(subject, &mat);
191
if (shape.IsNull()) {
192
// failure here on loading document with existing measurement.
193
Base::Console().Message("Part::buildAdapter did not retrieve shape for %s, %s\n",
194
subject.getObjectName(), subject.getElementName());
195
return Part::VectorAdapter();
197
TopAbs_ShapeEnum shapeType = shape.ShapeType();
199
if (shapeType == TopAbs_EDGE)
201
TopoDS_Edge edge = TopoDS::Edge(shape);
202
// make edge orientation so that end of edge closest to pick is head of vector.
203
TopoDS_Vertex firstVertex = TopExp::FirstVertex(edge, Standard_True);
204
TopoDS_Vertex lastVertex = TopExp::LastVertex(edge, Standard_True);
205
if (firstVertex.IsNull() || lastVertex.IsNull()) {
208
gp_Vec firstPoint = Part::VectorAdapter::convert(firstVertex);
209
gp_Vec lastPoint = Part::VectorAdapter::convert(lastVertex);
210
Base::Vector3d v(0.0, 0.0, 0.0); //v(current.x,current.y,current.z);
212
gp_Vec pickPoint(v.x, v.y, v.z);
213
double firstDistance = (firstPoint - pickPoint).Magnitude();
214
double lastDistance = (lastPoint - pickPoint).Magnitude();
215
if (lastDistance > firstDistance)
217
if (edge.Orientation() == TopAbs_FORWARD) {
218
edge.Orientation(TopAbs_REVERSED);
221
edge.Orientation(TopAbs_FORWARD);
224
return {edge, pickPoint};
226
if (shapeType == TopAbs_FACE)
228
TopoDS_Face face = TopoDS::Face(shape);
229
Base::Vector3d vTemp(0.0, 0.0, 0.0); //v(current.x, current.y, current.z);
231
gp_Vec pickPoint(vTemp.x, vTemp.y, vTemp.z);
232
return {face, pickPoint};
239
MeasureLengthInfoPtr MeasureLengthHandler(const App::SubObjectT& subject)
241
TopoDS_Shape shape = getLocatedShape(subject);
243
if (shape.IsNull()) {
244
// failure here on loading document with existing measurement.
245
Base::Console().Message("MeasureLengthHandler did not retrieve shape for %s, %s\n",
246
subject.getObjectName(), subject.getElementName());
247
return std::make_shared<MeasureLengthInfo>(false, 0.0, Base::Matrix4D());
249
TopAbs_ShapeEnum sType = shape.ShapeType();
251
if (sType != TopAbs_EDGE) {
252
return std::make_shared<MeasureLengthInfo>(false, 0.0, Base::Matrix4D());
255
// Get Center of mass as the attachment point of the label
257
BRepGProp::LinearProperties(shape, gprops);
258
auto origin = gprops.CentreOfMass();
260
Base::Placement placement(Base::Vector3d(origin.X(), origin.Y(), origin.Z()), Base::Rotation());
261
return std::make_shared<MeasureLengthInfo>(true, getLength(shape), placement);
264
MeasureRadiusInfoPtr MeasureRadiusHandler(const App::SubObjectT& subject)
266
Base::Placement placement; // curve center + orientation
267
Base::Vector3d pointOnCurve;
269
TopoDS_Shape shape = getLocatedShape(subject);
271
if (shape.IsNull()) {
272
return std::make_shared<MeasureRadiusInfo>( false, 0.0, pointOnCurve, placement);
274
TopAbs_ShapeEnum sType = shape.ShapeType();
276
if (sType != TopAbs_EDGE) {
277
return std::make_shared<MeasureRadiusInfo>( false, 0.0, pointOnCurve, placement);
280
// Get Center of mass as the attachment point of the label
282
BRepGProp::LinearProperties(shape, gprops);
283
auto origin = gprops.CentreOfMass();
285
TopoDS_Edge edge = TopoDS::Edge(shape);
286
gp_Pnt firstPoint = BRep_Tool::Pnt(TopExp::FirstVertex(edge));
287
pointOnCurve = Base::Vector3d(firstPoint.X(), firstPoint.Y(), firstPoint.Z());
288
// a somewhat arbitrary radius from center -> point on curve
289
auto dir = (firstPoint.XYZ() - origin.XYZ()).Normalized();
290
Base::Vector3d elementDirection(dir.X(), dir.Y(), dir.Z());
291
Base::Vector3d axisUp(0.0, 0.0, 1.0);
292
Base::Rotation rot(axisUp, elementDirection);
294
placement = Base::Placement(Base::Vector3d(origin.X(), origin.Y(), origin.Z()), rot);
296
return std::make_shared<MeasureRadiusInfo>( true, getRadius(shape), pointOnCurve, placement);
300
MeasureAreaInfoPtr MeasureAreaHandler(const App::SubObjectT& subject)
302
TopoDS_Shape shape = getLocatedShape(subject);
304
if (shape.IsNull()) {
305
// failure here on loading document with existing measurement.
306
Base::Console().Message("MeasureAreaHandler did not retrieve shape for %s, %s\n",
307
subject.getObjectName(), subject.getElementName());
308
return std::make_shared<MeasureAreaInfo>(false, 0.0, Base::Matrix4D());
310
TopAbs_ShapeEnum sType = shape.ShapeType();
312
if (sType != TopAbs_FACE) {
313
return std::make_shared<MeasureAreaInfo>(false, 0.0, Base::Matrix4D());
316
// Get Center of mass as the attachment point of the label
318
BRepGProp::SurfaceProperties(shape, gprops);
319
auto origin = gprops.CentreOfMass();
321
// TODO: Center of Mass might not lie on the surface, somehow snap to the closest point on the surface?
323
Base::Placement placement(Base::Vector3d(origin.X(), origin.Y(), origin.Z()), Base::Rotation());
324
return std::make_shared<MeasureAreaInfo>(true, getFaceArea(shape), placement);
328
MeasurePositionInfoPtr MeasurePositionHandler(const App::SubObjectT& subject)
330
TopoDS_Shape shape = getLocatedShape(subject);
332
if (shape.IsNull()) {
333
Base::Console().Message("MeasurePositionHandler did not retrieve shape for %s, %s\n",
334
subject.getObjectName(), subject.getElementName());
335
return std::make_shared<MeasurePositionInfo>(false, Base::Vector3d());
337
TopAbs_ShapeEnum sType = shape.ShapeType();
339
if (sType != TopAbs_VERTEX) {
340
return std::make_shared<MeasurePositionInfo>(false, Base::Vector3d());
343
TopoDS_Vertex vertex = TopoDS::Vertex(shape);
344
auto point = BRep_Tool::Pnt(vertex);
345
return std::make_shared<MeasurePositionInfo>( true, Base::Vector3d(point.X(), point.Y(), point.Z()));
349
MeasureAngleInfoPtr MeasureAngleHandler(const App::SubObjectT& subject)
351
TopoDS_Shape shape = getLocatedShape(subject);
353
if (shape.IsNull()) {
354
// failure here on loading document with existing measurement.
355
Base::Console().Message("MeasureAngleHandler did not retrieve shape for %s, %s\n",
356
subject.getObjectName(), subject.getElementName());
357
return std::make_shared<MeasureAngleInfo>();
360
TopAbs_ShapeEnum sType = shape.ShapeType();
362
Part::VectorAdapter vAdapt = buildAdapter(subject);
365
Base::Vector3d position;
366
if (sType == TopAbs_FACE) {
367
TopoDS_Face face = TopoDS::Face(shape);
370
BRepGProp::SurfaceProperties(face, gprops);
371
vec = gprops.CentreOfMass();
373
} else if (sType == TopAbs_EDGE) {
374
TopoDS_Edge edge = TopoDS::Edge(shape);
377
BRepGProp::LinearProperties(edge, gprops);
378
vec = gprops.CentreOfMass();
381
position.Set(vec.X(), vec.Y(), vec.Z());
383
auto info = std::make_shared<MeasureAngleInfo>(vAdapt.isValid(), (Base::Vector3d)vAdapt, position);
388
MeasureDistanceInfoPtr MeasureDistanceHandler(const App::SubObjectT& subject)
390
TopoDS_Shape shape = getLocatedShape(subject);
392
if (shape.IsNull()) {
393
// failure here on loading document with existing measurement.
394
Base::Console().Message("MeasureDistanceHandler did not retrieve shape for %s, %s\n",
395
subject.getObjectName(), subject.getElementName());
396
return std::make_shared<MeasureDistanceInfo>();
399
// return a persistent copy of the TopoDS_Shape here as shape will go out of scope at end
400
BRepBuilderAPI_Copy copy(shape);
401
return std::make_shared<MeasureDistanceInfo>(true, copy.Shape());
405
void Part::MeasureClient::initialize() {
406
App::MeasureManager::addMeasureHandler("Part", PartMeasureTypeCb);
410
Part::CallbackRegistrationList Part::MeasureClient::reportLengthCB()
412
CallbackRegistrationList callbacks;
413
callbacks.emplace_back("Part", "Length", MeasureLengthHandler);
414
callbacks.emplace_back("PartDesign", "Length", MeasureLengthHandler);
415
callbacks.emplace_back("Sketcher", "Length", MeasureLengthHandler);
419
Part::CallbackRegistrationList Part::MeasureClient::reportPositionCB()
421
CallbackRegistrationList callbacks;
422
callbacks.emplace_back("Part", "Position", MeasurePositionHandler);
423
callbacks.emplace_back("PartDesign", "Position", MeasurePositionHandler);
424
callbacks.emplace_back("Sketcher", "Position", MeasurePositionHandler);
428
Part::CallbackRegistrationList Part::MeasureClient::reportAreaCB()
430
CallbackRegistrationList callbacks;
431
callbacks.emplace_back("Part", "Area", MeasureAreaHandler);
432
callbacks.emplace_back("PartDesign", "Area", MeasureAreaHandler);
433
callbacks.emplace_back("Sketcher", "Area", MeasureAreaHandler);
438
Part::CallbackRegistrationList Part::MeasureClient::reportAngleCB()
440
CallbackRegistrationList callbacks;
441
callbacks.emplace_back("Part", "Angle", MeasureAngleHandler);
442
callbacks.emplace_back("PartDesign", "Angle", MeasureAngleHandler);
443
callbacks.emplace_back("Sketcher", "Angle", MeasureAngleHandler);
448
Part::CallbackRegistrationList Part::MeasureClient::reportDistanceCB()
450
CallbackRegistrationList callbacks;
451
callbacks.emplace_back("Part", "Distance", MeasureDistanceHandler);
452
callbacks.emplace_back("PartDesign", "Distance", MeasureDistanceHandler);
453
callbacks.emplace_back("Sketcher", "Distance", MeasureDistanceHandler);
458
Part::CallbackRegistrationList Part::MeasureClient::reportRadiusCB()
460
CallbackRegistrationList callbacks;
461
callbacks.emplace_back("Part", "Radius", MeasureRadiusHandler);
462
callbacks.emplace_back("PartDesign", "Radius", MeasureRadiusHandler);
463
callbacks.emplace_back("Sketcher", "Radius", MeasureRadiusHandler);