FreeCAD

Форк
0
/
MeasureClient.cpp 
465 строк · 16.7 Кб
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>                *
5
 *                                                                         *
6
 *   This file is part of FreeCAD.                                         *
7
 *                                                                         *
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.                       *
12
 *                                                                         *
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.                       *
17
 *                                                                         *
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/>.                                      *
21
 *                                                                         *
22
 **************************************************************************/
23

24

25
#include "PreCompiled.h"
26

27
#include <Mod/Part/PartGlobal.h>
28

29
#include <string>
30

31
#include <TopoDS.hxx>
32
#include <TopoDS_Vertex.hxx>
33
#include <TopAbs.hxx>
34
#include <BRepTools.hxx>
35
#include <BRep_Tool.hxx>
36
#include <BRepGProp.hxx>
37
#include <BRepAdaptor_Curve.hxx>
38
#include <BRepAdaptor_Surface.hxx>
39
#include <TopExp.hxx>
40
#include <GProp_GProps.hxx>
41
#include <ShapeAnalysis_Edge.hxx>
42
#include <gp_Circ.hxx>
43
#include <BRepBuilderAPI_Copy.hxx>
44

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>
56

57
#include "VectorAdapter.h"
58
#include "PartFeature.h"
59

60
#include "MeasureClient.h"
61

62
using namespace Part;
63

64

65
// From: https://github.com/Celemation/FreeCAD/blob/joel_selection_summary_demo/src/Gui/SelectionSummary.cpp
66

67
// Should work with edges and wires
68
static float getLength(TopoDS_Shape& wire){
69
    GProp_GProps gprops;
70
    BRepGProp::LinearProperties(wire, gprops);
71
    return gprops.Mass();
72
}
73

74
static float getFaceArea(TopoDS_Shape& face){
75
    GProp_GProps gprops;
76
    BRepGProp::SurfaceProperties(face, gprops);
77
    return gprops.Mass();
78
}
79

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?
86
            return 0.0;
87
        }
88
        gp_Circ circle = adapt.Circle();
89
        return circle.Radius();
90
    }
91
    return 0.0;
92
}
93

94
TopoDS_Shape getLocatedShape(const App::SubObjectT& subject, Base::Matrix4D* mat = nullptr)
95
{
96
    App::DocumentObject* obj = subject.getSubObjectList().back();
97
    if (!obj) {
98
        return {};
99
    }
100

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());
104
        return {};
105
    }
106

107
    auto placement = App::GeoFeature::getGlobalPlacement(obj, subject.getObject(), subject.getSubName());
108
    shape.setPlacement(placement);
109

110
    // Don't get the subShape from datum elements
111
    if (obj->getTypeId().isDerivedFrom(Part::Datum::getClassTypeId())) {
112
        return shape.getShape();
113
    }
114

115
    if (!subject.getElementName()) {
116
        return shape.getShape();
117
    }
118
    return shape.getSubShape(subject.getElementName(), true);
119
}
120

121

122
App::MeasureElementType PartMeasureTypeCb(App::DocumentObject* ob, const char* subName)
123
{
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();
129
    }
130
    TopAbs_ShapeEnum shapeType = shape.ShapeType();
131

132
    switch (shapeType) {
133
        case TopAbs_VERTEX: {
134
            return App::MeasureElementType::POINT;
135
        }
136
        case TopAbs_EDGE: {
137
            const TopoDS_Edge& edge = TopoDS::Edge(shape);
138
            BRepAdaptor_Curve curve(edge);
139

140
            switch (curve.GetType()) {
141
                case GeomAbs_Line: {
142
                    if (ob->getTypeId().isDerivedFrom(Base::Type::fromName("Part::Datum"))) {
143
                        return App::MeasureElementType::LINE;
144
                    }
145
                    return App::MeasureElementType::LINESEGMENT;
146
                }
147
                case GeomAbs_Circle: { return App::MeasureElementType::CIRCLE; }
148
                case GeomAbs_BezierCurve:
149
                case GeomAbs_BSplineCurve: {
150
                    return App::MeasureElementType::CURVE;
151
                }
152
                default: { return App::MeasureElementType::INVALID; }
153
            }
154
        }
155
        case TopAbs_FACE: {
156
            const TopoDS_Face& face = TopoDS::Face(shape);
157
            BRepAdaptor_Surface surface(face);
158

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; }
163
            }
164
        }
165
        case TopAbs_SOLID: {
166
            return App::MeasureElementType::Volume;
167
        }
168
        default: {
169
            return App::MeasureElementType::INVALID;
170
        }
171
    }
172
}
173

174

175
bool getShapeFromStrings(TopoDS_Shape &shapeOut, const App::SubObjectT& subject, Base::Matrix4D *mat)
176
{
177
    App::DocumentObject *obj = subject.getObject();
178
    if (!obj) {
179
        return {};
180
     }
181
    shapeOut = Part::Feature::getShape(obj, subject.getElementName(), true, mat);
182
    return !shapeOut.IsNull();
183
}
184

185

186
Part::VectorAdapter buildAdapter(const App::SubObjectT& subject)
187
{
188
    Base::Matrix4D mat;
189
    TopoDS_Shape shape = getLocatedShape(subject, &mat);
190

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();
196
    }
197
    TopAbs_ShapeEnum shapeType = shape.ShapeType();
198

199
    if (shapeType == TopAbs_EDGE)
200
    {
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()) {
206
        return {};
207
      }
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);
211
      v = mat*v;
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)
216
      {
217
        if (edge.Orientation() == TopAbs_FORWARD) {
218
          edge.Orientation(TopAbs_REVERSED);
219
        }
220
        else {
221
          edge.Orientation(TopAbs_FORWARD);
222
        }
223
      }
224
      return {edge, pickPoint};
225
    }
226
    if (shapeType == TopAbs_FACE)
227
    {
228
      TopoDS_Face face = TopoDS::Face(shape);
229
      Base::Vector3d vTemp(0.0, 0.0, 0.0); //v(current.x, current.y, current.z);
230
      vTemp = mat*vTemp;
231
      gp_Vec pickPoint(vTemp.x, vTemp.y, vTemp.z);
232
      return {face, pickPoint};
233
    }
234

235
    return {};
236
}
237

238

239
MeasureLengthInfoPtr MeasureLengthHandler(const App::SubObjectT& subject)
240
{
241
    TopoDS_Shape shape = getLocatedShape(subject);
242

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());
248
    }
249
    TopAbs_ShapeEnum sType = shape.ShapeType();
250

251
    if (sType != TopAbs_EDGE) {
252
        return std::make_shared<MeasureLengthInfo>(false, 0.0, Base::Matrix4D());
253
    }
254

255
    // Get Center of mass as the attachment point of the label
256
    GProp_GProps gprops;
257
    BRepGProp::LinearProperties(shape, gprops);
258
    auto origin = gprops.CentreOfMass();
259

260
    Base::Placement placement(Base::Vector3d(origin.X(), origin.Y(), origin.Z()), Base::Rotation());
261
    return std::make_shared<MeasureLengthInfo>(true, getLength(shape), placement);
262
}
263

264
MeasureRadiusInfoPtr MeasureRadiusHandler(const App::SubObjectT& subject)
265
{
266
    Base::Placement placement;      // curve center + orientation
267
    Base::Vector3d pointOnCurve;
268

269
    TopoDS_Shape shape = getLocatedShape(subject);
270

271
    if (shape.IsNull()) {
272
        return std::make_shared<MeasureRadiusInfo>( false, 0.0, pointOnCurve, placement);
273
    }
274
        TopAbs_ShapeEnum sType = shape.ShapeType();
275

276
    if (sType != TopAbs_EDGE) {
277
        return std::make_shared<MeasureRadiusInfo>( false, 0.0, pointOnCurve, placement);
278
    }
279

280
    // Get Center of mass as the attachment point of the label
281
    GProp_GProps gprops;
282
    BRepGProp::LinearProperties(shape, gprops);
283
    auto origin = gprops.CentreOfMass();
284

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);
293

294
    placement = Base::Placement(Base::Vector3d(origin.X(), origin.Y(), origin.Z()), rot);
295

296
    return std::make_shared<MeasureRadiusInfo>( true, getRadius(shape), pointOnCurve, placement);
297
}
298

299

300
MeasureAreaInfoPtr MeasureAreaHandler(const App::SubObjectT& subject)
301
{
302
    TopoDS_Shape shape = getLocatedShape(subject);
303

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());
309
    }
310
    TopAbs_ShapeEnum sType = shape.ShapeType();
311

312
    if (sType != TopAbs_FACE) {
313
        return std::make_shared<MeasureAreaInfo>(false, 0.0, Base::Matrix4D());
314
    }
315

316
    // Get Center of mass as the attachment point of the label
317
    GProp_GProps gprops;
318
    BRepGProp::SurfaceProperties(shape, gprops);
319
    auto origin = gprops.CentreOfMass();
320

321
    // TODO: Center of Mass might not lie on the surface, somehow snap to the closest point on the surface? 
322

323
    Base::Placement placement(Base::Vector3d(origin.X(), origin.Y(), origin.Z()), Base::Rotation());
324
    return std::make_shared<MeasureAreaInfo>(true, getFaceArea(shape), placement);
325
}
326

327

328
MeasurePositionInfoPtr MeasurePositionHandler(const App::SubObjectT& subject)
329
{
330
    TopoDS_Shape shape = getLocatedShape(subject);
331

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());
336
    }
337
    TopAbs_ShapeEnum sType = shape.ShapeType();
338

339
    if (sType != TopAbs_VERTEX) {
340
        return std::make_shared<MeasurePositionInfo>(false, Base::Vector3d());
341
    }
342

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()));
346
}
347

348

349
MeasureAngleInfoPtr MeasureAngleHandler(const App::SubObjectT& subject)
350
{
351
    TopoDS_Shape shape = getLocatedShape(subject);
352

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>();
358
    }
359

360
    TopAbs_ShapeEnum sType = shape.ShapeType();
361

362
    Part::VectorAdapter vAdapt = buildAdapter(subject);
363

364
    gp_Pnt vec;
365
    Base::Vector3d position;
366
    if (sType == TopAbs_FACE) {
367
        TopoDS_Face face = TopoDS::Face(shape);
368
        
369
        GProp_GProps gprops;
370
        BRepGProp::SurfaceProperties(face, gprops);
371
        vec = gprops.CentreOfMass();
372
        
373
    } else if (sType == TopAbs_EDGE) {
374
        TopoDS_Edge edge = TopoDS::Edge(shape);
375

376
        GProp_GProps gprops;
377
        BRepGProp::LinearProperties(edge, gprops);
378
        vec = gprops.CentreOfMass();
379
    }
380

381
    position.Set(vec.X(), vec.Y(), vec.Z());
382

383
    auto info = std::make_shared<MeasureAngleInfo>(vAdapt.isValid(), (Base::Vector3d)vAdapt, position);
384
    return info;
385
}
386

387

388
MeasureDistanceInfoPtr MeasureDistanceHandler(const App::SubObjectT& subject)
389
{
390
    TopoDS_Shape shape = getLocatedShape(subject);
391

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>();
397
    }
398

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());
402
}
403

404

405
void Part::MeasureClient::initialize() {
406
    App::MeasureManager::addMeasureHandler("Part", PartMeasureTypeCb);
407

408
}
409

410
Part::CallbackRegistrationList Part::MeasureClient::reportLengthCB()
411
{
412
    CallbackRegistrationList callbacks;
413
    callbacks.emplace_back("Part", "Length", MeasureLengthHandler);
414
    callbacks.emplace_back("PartDesign", "Length", MeasureLengthHandler);
415
    callbacks.emplace_back("Sketcher", "Length", MeasureLengthHandler);
416
    return callbacks;
417
}
418

419
Part::CallbackRegistrationList Part::MeasureClient::reportPositionCB()
420
{
421
    CallbackRegistrationList callbacks;
422
    callbacks.emplace_back("Part", "Position", MeasurePositionHandler);
423
    callbacks.emplace_back("PartDesign", "Position", MeasurePositionHandler);
424
    callbacks.emplace_back("Sketcher", "Position", MeasurePositionHandler);
425
    return callbacks;
426
}
427

428
Part::CallbackRegistrationList Part::MeasureClient::reportAreaCB()
429
{
430
    CallbackRegistrationList callbacks;
431
    callbacks.emplace_back("Part", "Area", MeasureAreaHandler);
432
    callbacks.emplace_back("PartDesign", "Area", MeasureAreaHandler);
433
    callbacks.emplace_back("Sketcher", "Area", MeasureAreaHandler);
434
    return callbacks;
435
}
436

437

438
Part::CallbackRegistrationList Part::MeasureClient::reportAngleCB()
439
{
440
    CallbackRegistrationList callbacks;
441
    callbacks.emplace_back("Part", "Angle", MeasureAngleHandler);
442
    callbacks.emplace_back("PartDesign", "Angle", MeasureAngleHandler);
443
    callbacks.emplace_back("Sketcher", "Angle", MeasureAngleHandler);
444
    return callbacks;
445
}
446

447

448
Part::CallbackRegistrationList Part::MeasureClient::reportDistanceCB()
449
{
450
    CallbackRegistrationList callbacks;
451
    callbacks.emplace_back("Part", "Distance", MeasureDistanceHandler);
452
    callbacks.emplace_back("PartDesign", "Distance", MeasureDistanceHandler);
453
    callbacks.emplace_back("Sketcher", "Distance", MeasureDistanceHandler);
454
    return callbacks;
455
}
456

457

458
Part::CallbackRegistrationList Part::MeasureClient::reportRadiusCB()
459
{
460
    CallbackRegistrationList callbacks;
461
    callbacks.emplace_back("Part", "Radius", MeasureRadiusHandler);
462
    callbacks.emplace_back("PartDesign", "Radius", MeasureRadiusHandler);
463
    callbacks.emplace_back("Sketcher", "Radius", MeasureRadiusHandler);
464
    return callbacks;
465
}
466

467

468

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.