FreeCAD

Форк
0
/
FeatureProjectOnSurface.cpp 
469 строк · 16.4 Кб
1
// SPDX-License-Identifier: LGPL-2.1-or-later
2

3
/***************************************************************************
4
 *   Copyright (c) 2019 Manuel Apeltauer, direkt cnc-systeme GmbH          *
5
 *   Copyright (c) 2024 Werner Mayer <wmayer[at]users.sourceforge.net>     *
6
 *                                                                         *
7
 *   This file is part of FreeCAD.                                         *
8
 *                                                                         *
9
 *   FreeCAD is free software: you can redistribute it and/or modify it    *
10
 *   under the terms of the GNU Lesser General Public License as           *
11
 *   published by the Free Software Foundation, either version 2.1 of the  *
12
 *   License, or (at your option) any later version.                       *
13
 *                                                                         *
14
 *   FreeCAD is distributed in the hope that it will be useful, but        *
15
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
16
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
17
 *   Lesser General Public License for more details.                       *
18
 *                                                                         *
19
 *   You should have received a copy of the GNU Lesser General Public      *
20
 *   License along with FreeCAD. If not, see                               *
21
 *   <https://www.gnu.org/licenses/>.                                      *
22
 *                                                                         *
23
 **************************************************************************/
24

25
#include "PreCompiled.h"
26
#ifndef _PreComp_
27
#include <BRep_Tool.hxx>
28
#include <BRepBuilderAPI_MakeEdge.hxx>
29
#include <BRepBuilderAPI_MakeFace.hxx>
30
#include <BRepBuilderAPI_Transform.hxx>
31
#include <BRepCheck_Analyzer.hxx>
32
#include <BRepExtrema_DistShapeShape.hxx>
33
#include <BRepPrimAPI_MakePrism.hxx>
34
#include <BRepProj_Projection.hxx>
35
#include <Precision.hxx>
36
#include <ShapeAnalysis.hxx>
37
#include <ShapeAnalysis_FreeBounds.hxx>
38
#include <ShapeFix_Face.hxx>
39
#include <ShapeFix_Wire.hxx>
40
#include <ShapeFix_Wireframe.hxx>
41
#include <Standard_Failure.hxx>
42
#include <TopExp_Explorer.hxx>
43
#include <TopoDS.hxx>
44
#include <TopoDS_Builder.hxx>
45
#include <sstream>
46
#endif
47

48
#include "FeatureProjectOnSurface.h"
49
#include <Base/Exception.h>
50

51

52
using namespace Part;
53

54
PROPERTY_SOURCE(Part::ProjectOnSurface, Part::Feature)
55
static std::array<const char*, 4> modes = {"All", "Faces", "Edges", nullptr};  // NOLINT
56

57
ProjectOnSurface::ProjectOnSurface()
58
{
59
    ADD_PROPERTY_TYPE(Mode,(0L), "Projection", App::Prop_None, "Projection mode");
60
    Mode.setEnums(modes.data());
61
    ADD_PROPERTY_TYPE(Height,(0.0), "Projection", App::Prop_None, "Extrusion height");
62
    ADD_PROPERTY_TYPE(Offset,(0.0), "Projection", App::Prop_None, "Offset of solid");
63
    ADD_PROPERTY_TYPE(Direction,(Base::Vector3d(0, 0, 1)), "Projection", App::Prop_None, "Direction of projection");
64
    ADD_PROPERTY_TYPE(SupportFace,(nullptr), "Projection", App::Prop_None, "Support faceo");
65
    ADD_PROPERTY_TYPE(Projection,(nullptr), "Projection", App::Prop_None, "Shapes to project onto support face");
66
}
67

68
App::DocumentObjectExecReturn* ProjectOnSurface::execute()
69
{
70
    try {
71
        tryExecute();
72
        return App::DocumentObject::StdReturn;
73
    }
74
    catch (const Standard_Failure& error) {
75
        throw Base::ValueError(error.GetMessageString());
76
    }
77
}
78

79
void ProjectOnSurface::tryExecute()
80
{
81
    TopoDS_Face supportFace = getSupportFace();
82

83
    std::vector<TopoDS_Shape> shapes = getProjectionShapes();
84
    const auto& vec = Direction.getValue();
85
    gp_Dir dir(vec.x, vec.y, vec.z);
86

87
    std::vector<TopoDS_Shape> results;
88
    for (const auto& shape : shapes) {
89
        auto shapes = createProjectedWire(shape, supportFace, dir);
90
        results.insert(results.end(), shapes.begin(), shapes.end());
91
    }
92

93
    results = filterShapes(results);
94
    auto currentPlacement = Placement.getValue();
95
    Shape.setValue(createCompound(results));
96
    Placement.setValue(currentPlacement);
97
}
98

99
TopoDS_Face ProjectOnSurface::getSupportFace() const
100
{
101
    auto support = SupportFace.getValue<Part::Feature*>();
102
    if (!support) {
103
        throw Base::ValueError("No support face specified");
104
    }
105

106
    std::vector<std::string> subStrings = SupportFace.getSubValues();
107
    if (subStrings.size() != 1) {
108
        throw Base::ValueError("Expect exactly one support face");
109
    }
110

111
    auto topoSupport = Feature::getTopoShape(support, subStrings[0].c_str(), true);
112
    return TopoDS::Face(topoSupport.getShape());
113
}
114

115
std::vector<TopoDS_Shape> ProjectOnSurface::getProjectionShapes() const
116
{
117
    std::vector<TopoDS_Shape> shapes;
118
    auto objects = Projection.getValues();
119
    auto subvalues = Projection.getSubValues();
120
    if (objects.size() != subvalues.size()) {
121
        throw Base::ValueError("Number of objects and sub-names differ");
122
    }
123

124
    for (std::size_t index = 0; index < objects.size(); index++) {
125
        auto topoSupport = Feature::getTopoShape(objects[index], subvalues[index].c_str(), true);
126
        shapes.push_back(topoSupport.getShape());
127
    }
128

129
    return shapes;
130
}
131

132
std::vector<TopoDS_Shape>
133
ProjectOnSurface::filterShapes(const std::vector<TopoDS_Shape>& shapes) const
134
{
135
    std::vector<TopoDS_Shape> filtered;
136
    const char* mode = Mode.getValueAsString();
137
    if (strcmp(mode, "All") == 0) {
138
        for (const auto& it : shapes) {
139
            if (!it.IsNull()) {
140
                filtered.push_back(it);
141
            }
142
        }
143
    }
144
    else if (strcmp(mode, "Faces") == 0) {
145
        for (const auto& it : shapes) {
146
            if (!it.IsNull() && it.ShapeType() == TopAbs_FACE) {
147
                filtered.push_back(it);
148
            }
149
        }
150
    }
151
    else if (strcmp(mode, "Edges") == 0) {
152
        for (const auto& it : shapes) {
153
            if (it.IsNull()) {
154
                continue;
155
            }
156
            if (it.ShapeType() == TopAbs_EDGE || it.ShapeType() == TopAbs_WIRE) {
157
                filtered.push_back(it);
158
            }
159
            else if (it.ShapeType() == TopAbs_FACE) {
160
                auto wires = getWires(TopoDS::Face(it));
161
                for (const auto& jt : wires) {
162
                    filtered.push_back(jt);
163
                }
164
            }
165
        }
166
    }
167

168
    return filtered;
169
}
170

171
TopoDS_Shape ProjectOnSurface::createCompound(const std::vector<TopoDS_Shape>& shapes)
172
{
173
    TopLoc_Location loc = getOffsetPlacement();
174
    bool isIdentity = loc.IsIdentity();
175
    TopoDS_Compound aCompound;
176
    if (!shapes.empty()) {
177
        TopoDS_Builder aBuilder;
178
        aBuilder.MakeCompound(aCompound);
179
        for (const auto& it : shapes) {
180
            if (isIdentity) {
181
                aBuilder.Add(aCompound, it);
182
            }
183
            else {
184
                aBuilder.Add(aCompound, it.Moved(loc));
185
            }
186
        }
187
    }
188
    return {std::move(aCompound)};
189
}
190

191
std::vector<TopoDS_Shape> ProjectOnSurface::createProjectedWire(const TopoDS_Shape& shape,
192
                                                                const TopoDS_Face& supportFace,
193
                                                                const gp_Dir& dir)
194
{
195
    if (shape.IsNull()) {
196
        return {};
197
    }
198
    if (shape.ShapeType() == TopAbs_FACE) {
199
        auto wires = projectFace(TopoDS::Face(shape), supportFace, dir);
200
        auto face = createFaceFromWire(wires, supportFace);
201
        auto face_or_solid = createSolidIfHeight(face);
202
        if (!face_or_solid.IsNull()) {
203
            return {face_or_solid};
204
        }
205
        if (!face.IsNull()) {
206
            return {face};
207
        }
208

209
        return wires;
210
    }
211
    if (shape.ShapeType() == TopAbs_WIRE || shape.ShapeType() == TopAbs_EDGE) {
212
        return projectWire(shape, supportFace, dir);
213
    }
214

215
    return {};
216
}
217

218
TopoDS_Face ProjectOnSurface::createFaceFromWire(const std::vector<TopoDS_Shape>& wires,
219
                                                 const TopoDS_Face& supportFace) const
220
{
221
    if (wires.empty()) {
222
        return {};
223
    }
224

225
    std::vector<TopoDS_Wire> wiresInParametricSpace = createWiresFromWires(wires, supportFace);
226
    return createFaceFromParametricWire(wiresInParametricSpace, supportFace);
227
}
228

229
TopoDS_Face
230
ProjectOnSurface::createFaceFromParametricWire(const std::vector<TopoDS_Wire>& wires,
231
                                               const TopoDS_Face& supportFace) const
232
{
233
    auto surface = BRep_Tool::Surface(supportFace);
234

235
    // try to create a face from the wires
236
    // the first wire is the otherwise
237
    // the following wires are the inside wires
238
    BRepBuilderAPI_MakeFace faceMaker;
239
    bool first = true;
240
    for (const auto& wire : wires) {
241
        if (first) {
242
            first = false;
243
            // change the wire direction, otherwise no face is created
244
            auto currentWire = TopoDS::Wire(wire.Reversed());
245
            if (supportFace.Orientation() == TopAbs_REVERSED) {
246
                currentWire = wire;
247
            }
248
            faceMaker = BRepBuilderAPI_MakeFace(surface, currentWire);
249
            ShapeFix_Face fix(faceMaker.Face());
250
            fix.Perform();
251
            auto aFace = fix.Face();
252
            BRepCheck_Analyzer aChecker(aFace);
253
            if (!aChecker.IsValid()) {
254
                faceMaker = BRepBuilderAPI_MakeFace(surface, TopoDS::Wire(currentWire.Reversed()));
255
            }
256
        }
257
        else {
258
            // make a copy of the current face maker
259
            // if the face fails just try again with the copy
260
            TopoDS_Face tempCopy = BRepBuilderAPI_MakeFace(faceMaker.Face()).Face();
261
            faceMaker.Add(TopoDS::Wire(wire.Reversed()));
262
            ShapeFix_Face fix(faceMaker.Face());
263
            fix.Perform();
264
            auto aFace = fix.Face();
265
            BRepCheck_Analyzer aChecker(aFace);
266
            if (!aChecker.IsValid()) {
267
                faceMaker = BRepBuilderAPI_MakeFace(tempCopy);
268
                faceMaker.Add(TopoDS::Wire(wire));
269
            }
270
        }
271
    }
272
    // auto doneFlag = faceMaker.IsDone();
273
    // auto error = faceMaker.Error();
274
    return faceMaker.Face();
275
}
276

277
std::vector<TopoDS_Wire>
278
ProjectOnSurface::createWiresFromWires(const std::vector<TopoDS_Shape>& wires,
279
                                       const TopoDS_Face& supportFace) const
280
{
281
    auto surface = BRep_Tool::Surface(supportFace);
282

283
    // create a wire of all edges in parametric space on the surface of the face to
284
    // projected
285
    //  --> otherwise BRepBuilderAPI_MakeFace can not make a face from the wire!
286
    std::vector<TopoDS_Wire> wiresInParametricSpace;
287
    for (const auto& wire : wires) {
288
        std::vector<TopoDS_Shape> edges;
289
        for (TopExp_Explorer xp(wire, TopAbs_EDGE); xp.More(); xp.Next()) {
290
            edges.push_back(TopoDS::Edge(xp.Current()));
291
        }
292
        if (edges.empty()) {
293
            continue;
294
        }
295

296
        std::vector<TopoDS_Edge> edgesInParametricSpace;
297
        for (const auto& edge : edges) {
298
            Standard_Real first {};
299
            Standard_Real last {};
300
            auto currentCurve = BRep_Tool::CurveOnSurface(TopoDS::Edge(edge),
301
                                                          supportFace,
302
                                                          first,
303
                                                          last);
304
            if (!currentCurve) {
305
                continue;
306
            }
307

308
            BRepBuilderAPI_MakeEdge mkEdge(currentCurve, surface, first, last);
309
            auto edgeInParametricSpace = mkEdge.Edge();
310
            edgesInParametricSpace.push_back(edgeInParametricSpace);
311
        }
312

313
        auto aWire = fixWire(edgesInParametricSpace, supportFace);
314
        wiresInParametricSpace.push_back(aWire);
315
    }
316

317
    return wiresInParametricSpace;
318
}
319

320
TopoDS_Shape ProjectOnSurface::createSolidIfHeight(const TopoDS_Face& face) const
321
{
322
    if (face.IsNull()) {
323
        return face;
324
    }
325
    double height = Height.getValue();
326
    if (height < Precision::Confusion() || Mode.getValue() != 0L) {
327
        return face;
328
    }
329

330
    const auto& vec = Direction.getValue();
331
    gp_Vec directionToExtrude(vec.x, vec.y, vec.z);
332
    directionToExtrude.Reverse();
333
    directionToExtrude.Multiply(height);
334

335
    BRepPrimAPI_MakePrism extrude(face, directionToExtrude);
336
    return extrude.Shape();
337
}
338

339
std::vector<TopoDS_Wire> ProjectOnSurface::getWires(const TopoDS_Face& face) const
340
{
341
    std::vector<TopoDS_Wire> wires;
342
    auto outerWire = ShapeAnalysis::OuterWire(face);
343
    wires.push_back(outerWire);
344
    for (TopExp_Explorer xp(face, TopAbs_WIRE); xp.More(); xp.Next()) {
345
        auto currentWire = TopoDS::Wire(xp.Current());
346
        if (!currentWire.IsSame(outerWire)) {
347
            wires.push_back(currentWire);
348
        }
349
    }
350

351
    return wires;
352
}
353

354
TopoDS_Wire ProjectOnSurface::fixWire(const TopoDS_Shape& shape,
355
                                      const TopoDS_Face& supportFace) const
356
{
357
    std::vector<TopoDS_Edge> edges;
358
    for (TopExp_Explorer xp(shape, TopAbs_EDGE); xp.More(); xp.Next()) {
359
        edges.push_back(TopoDS::Edge(xp.Current()));
360
    }
361
    return fixWire(edges, supportFace);
362
}
363

364
TopoDS_Wire ProjectOnSurface::fixWire(const std::vector<TopoDS_Edge>& edges,
365
                                      const TopoDS_Face& supportFace) const
366
{
367
    // try to sort and heal all wires
368
    // if the wires are not clean making a face will fail!
369
    ShapeAnalysis_FreeBounds shapeAnalyzer;
370
    Handle(TopTools_HSequenceOfShape) shapeList = new TopTools_HSequenceOfShape;
371
    Handle(TopTools_HSequenceOfShape) aWireHandle;
372
    Handle(TopTools_HSequenceOfShape) aWireWireHandle;
373

374
    for (const auto& it : edges) {
375
        shapeList->Append(it);
376
    }
377

378
    const double tolerance = 0.0001;
379
    ShapeAnalysis_FreeBounds::ConnectEdgesToWires(shapeList, tolerance, false, aWireHandle);
380
    ShapeAnalysis_FreeBounds::ConnectWiresToWires(aWireHandle, tolerance, false, aWireWireHandle);
381
    if (!aWireWireHandle) {
382
        return {};
383
    }
384
    for (auto it = 1; it <= aWireWireHandle->Length(); ++it) {
385
        auto aShape = TopoDS::Wire(aWireWireHandle->Value(it));
386
        ShapeFix_Wire aWireRepair(aShape, supportFace, tolerance);
387
        aWireRepair.FixAddCurve3dMode() = 1;
388
        aWireRepair.FixAddPCurveMode() = 1;
389
        aWireRepair.Perform();
390

391
        ShapeFix_Wireframe aWireFramFix(aWireRepair.Wire());
392
        aWireFramFix.FixWireGaps();
393
        aWireFramFix.FixSmallEdges();
394
        return TopoDS::Wire(aWireFramFix.Shape());
395
    }
396
    return {};
397
}
398

399
namespace {
400
TopoDS_Wire getProjectedWire(BRepProj_Projection& projection, const TopoDS_Shape& reference)
401
{
402
    double minDistance = std::numeric_limits<double>::max();
403
    TopoDS_Wire wireToTake;
404
    for (; projection.More(); projection.Next()) {
405
        auto it = projection.Current();
406
        BRepExtrema_DistShapeShape distanceMeasure(it, reference);
407
        distanceMeasure.Perform();
408
        auto currentDistance = distanceMeasure.Value();
409
        if (currentDistance > minDistance) {
410
            continue;
411
        }
412
        wireToTake = it;
413
        minDistance = currentDistance;
414
    }
415

416
    return wireToTake;
417
}
418
}
419

420
std::vector<TopoDS_Shape> ProjectOnSurface::projectFace(const TopoDS_Face& face,
421
                                                        const TopoDS_Face& supportFace,
422
                                                        const gp_Dir& dir)
423
{
424
    std::vector<TopoDS_Shape> shapes;
425
    std::vector<TopoDS_Wire> wires = getWires(face);
426
    for (const auto& wire : wires) {
427
        BRepProj_Projection aProjection(wire, supportFace, dir);
428
        TopoDS_Wire wireToTake = getProjectedWire(aProjection, face);
429
        auto aWire = fixWire(wireToTake, supportFace);
430
        shapes.push_back(aWire);
431
    }
432

433
    return shapes;
434
}
435

436
std::vector<TopoDS_Shape> ProjectOnSurface::projectWire(const TopoDS_Shape& wire,
437
                                                        const TopoDS_Face& supportFace,
438
                                                        const gp_Dir& dir)
439
{
440
    std::vector<TopoDS_Shape> shapes;
441
    BRepProj_Projection aProjection(wire, supportFace, dir);
442
    TopoDS_Wire wireToTake = getProjectedWire(aProjection, wire);
443
    for (TopExp_Explorer xp(wireToTake, TopAbs_EDGE); xp.More(); xp.Next()) {
444
        shapes.push_back(TopoDS::Edge(xp.Current()));
445
    }
446

447
    return shapes;
448
}
449

450
TopLoc_Location ProjectOnSurface::getOffsetPlacement() const
451
{
452
    double offset = Offset.getValue();
453
    if (offset == 0) {
454
        return {};
455
    }
456

457
    auto vec = Direction.getValue();
458
    vec.Normalize();
459
    vec.Scale(offset, offset, offset);
460
    Base::Matrix4D mat;
461
    mat.move(vec);
462
    gp_Trsf move = TopoShape::convert(mat);
463
    return TopLoc_Location(move);
464
}
465

466
const char* ProjectOnSurface::getViewProviderName() const
467
{
468
    return "PartGui::ViewProviderProjectOnSurface";
469
}
470

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

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

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

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