1
/***************************************************************************
2
* Copyright (c) 2011 Werner Mayer <wmayer[at]users.sourceforge.net> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
26
#include <BRepAdaptor_CompCurve.hxx>
27
#include <BRepAdaptor_Curve.hxx>
28
#include <BRepBuilderAPI_Copy.hxx>
29
#include <BRepBuilderAPI_MakeWire.hxx>
30
#include <BRepFill.hxx>
31
#include <BRepLib_MakeWire.hxx>
32
#include <BRepOffsetAPI_MakePipeShell.hxx>
33
#include <Geom_BSplineSurface.hxx>
34
#include <Precision.hxx>
35
#include <ShapeAnalysis.hxx>
36
#include <ShapeAnalysis_FreeBounds.hxx>
37
#include <TopExp_Explorer.hxx>
39
#include <TopoDS_Face.hxx>
40
#include <TopoDS_Iterator.hxx>
41
#include <TopoDS_Shell.hxx>
42
#include <TopTools_HSequenceOfShape.hxx>
43
#include <TopTools_ListIteratorOfListOfShape.hxx>
48
#include <App/Document.h>
49
#include "PartFeatures.h"
50
#include "TopoShapeOpCode.h"
54
PROPERTY_SOURCE(Part::RuledSurface, Part::Feature)
56
const char* RuledSurface::OrientationEnums[] = {"Automatic", "Forward", "Reversed", nullptr};
58
RuledSurface::RuledSurface()
60
ADD_PROPERTY_TYPE(Curve1, (nullptr), "Ruled Surface", App::Prop_None, "Curve of ruled surface");
61
ADD_PROPERTY_TYPE(Curve2, (nullptr), "Ruled Surface", App::Prop_None, "Curve of ruled surface");
62
ADD_PROPERTY_TYPE(Orientation,
66
"Orientation of ruled surface");
67
Orientation.setEnums(OrientationEnums);
70
short RuledSurface::mustExecute() const
72
if (Curve1.isTouched()) {
75
if (Curve2.isTouched()) {
78
if (Orientation.isTouched()) {
84
void RuledSurface::onChanged(const App::Property* prop)
86
Part::Feature::onChanged(prop);
89
App::DocumentObjectExecReturn* RuledSurface::getShape(const App::PropertyLinkSub& link,
90
TopoDS_Shape& shape) const
92
App::DocumentObject* obj = link.getValue();
93
const Part::TopoShape part = Part::Feature::getTopoShape(obj);
95
return new App::DocumentObjectExecReturn("No shape linked.");
98
// if no explicit sub-shape is selected use the whole part
99
const std::vector<std::string>& element = link.getSubValues();
100
if (element.empty()) {
101
shape = part.getShape();
104
else if (element.size() != 1) {
105
return new App::DocumentObjectExecReturn("Not exactly one sub-shape linked.");
108
if (!part.getShape().IsNull()) {
109
if (!element[0].empty()) {
110
// shape = Part::Feature::getTopoShape(obj, element[0].c_str(), true /*need
111
// element*/).getShape();
112
shape = part.getSubShape(element[0].c_str());
115
// the sub-element is an empty string, so use the whole part
116
shape = part.getShape();
123
App::DocumentObjectExecReturn* RuledSurface::execute()
126
std::vector<TopoShape> shapes;
127
std::array<App::PropertyLinkSub*, 2> links = {&Curve1, &Curve2};
128
for (auto link : links) {
129
const auto& subs = link->getSubValues();
131
shapes.push_back(getTopoShape(link->getValue()));
133
else if (subs.size() != 1) {
134
return new App::DocumentObjectExecReturn("Not exactly one sub-shape linked.");
137
shapes.push_back(getTopoShape(link->getValue(), subs.front().c_str(), true));
139
if (shapes.back().isNull()) {
140
return new App::DocumentObjectExecReturn("Invalid link.");
144
res.makeElementRuledSurface(shapes, Orientation.getValue());
145
this->Shape.setValue(res);
146
return Part::Feature::execute();
149
catch (Standard_Failure& e) {
151
return new App::DocumentObjectExecReturn(e.GetMessageString());
154
return new App::DocumentObjectExecReturn("General error in RuledSurface::execute()");
158
// ----------------------------------------------------------------------------
160
App::PropertyIntegerConstraint::Constraints Loft::Degrees = {2,
161
Geom_BSplineSurface::MaxDegree(),
164
PROPERTY_SOURCE(Part::Loft, Part::Feature)
168
ADD_PROPERTY_TYPE(Sections, (nullptr), "Loft", App::Prop_None, "List of sections");
170
ADD_PROPERTY_TYPE(Solid, (false), "Loft", App::Prop_None, "Create solid");
171
ADD_PROPERTY_TYPE(Ruled, (false), "Loft", App::Prop_None, "Ruled surface");
172
ADD_PROPERTY_TYPE(Closed, (false), "Loft", App::Prop_None, "Close Last to First Profile");
173
ADD_PROPERTY_TYPE(MaxDegree, (5), "Loft", App::Prop_None, "Maximum Degree");
174
ADD_PROPERTY_TYPE(Linearize,(false), "Loft", App::Prop_None,
175
"Linearize the result shape by simplifying linear edge and planar face into line and plane");
176
MaxDegree.setConstraints(&Degrees);
179
short Loft::mustExecute() const
181
if (Sections.isTouched()) {
184
if (Solid.isTouched()) {
187
if (Ruled.isTouched()) {
190
if (Closed.isTouched()) {
193
if (MaxDegree.isTouched()) {
199
void Loft::onChanged(const App::Property* prop)
201
Part::Feature::onChanged(prop);
204
App::DocumentObjectExecReturn* Loft::execute()
206
if (Sections.getSize() == 0) {
207
return new App::DocumentObjectExecReturn("No sections linked.");
211
std::vector<TopoShape> shapes;
212
for (auto& obj : Sections.getValues()) {
213
shapes.emplace_back(getTopoShape(obj));
214
if (shapes.back().isNull()) {
215
return new App::DocumentObjectExecReturn("Invalid section link");
218
IsSolid isSolid = Solid.getValue() ? IsSolid::solid : IsSolid::notSolid;
219
IsRuled isRuled = Ruled.getValue() ? IsRuled::ruled : IsRuled::notRuled;
220
IsClosed isClosed = Closed.getValue() ? IsClosed::closed : IsClosed::notClosed;
221
int degMax = MaxDegree.getValue();
223
result.makeElementLoft(shapes, isSolid, isRuled, isClosed, degMax);
224
if (Linearize.getValue()) {
225
result.linearize( LinearizeFace::linearizeFaces, LinearizeEdge::noEdges);
227
this->Shape.setValue(result);
228
return Part::Feature::execute();
230
catch (Standard_Failure& e) {
232
return new App::DocumentObjectExecReturn(e.GetMessageString());
236
void Part::Loft::setupObject()
238
Feature::setupObject();
239
// Linearize.setValue(PartParams::getLinearizeExtrusionDraft()); // TODO: Resolve after PartParams
242
// ----------------------------------------------------------------------------
244
const char* Part::Sweep::TransitionEnums[] = {"Transformed",
249
PROPERTY_SOURCE(Part::Sweep, Part::Feature)
253
ADD_PROPERTY_TYPE(Sections, (nullptr), "Sweep", App::Prop_None, "List of sections");
255
ADD_PROPERTY_TYPE(Spine, (nullptr), "Sweep", App::Prop_None, "Path to sweep along");
256
ADD_PROPERTY_TYPE(Solid, (false), "Sweep", App::Prop_None, "Create solid");
257
ADD_PROPERTY_TYPE(Frenet, (true), "Sweep", App::Prop_None, "Frenet");
258
ADD_PROPERTY_TYPE(Transition, (long(1)), "Sweep", App::Prop_None, "Transition mode");
259
ADD_PROPERTY_TYPE(Linearize,(false), "Sweep", App::Prop_None,
260
"Linearize the result shape by simplifying linear edge and planar face into line and plane");
261
Transition.setEnums(TransitionEnums);
264
short Sweep::mustExecute() const
266
if (Sections.isTouched()) {
269
if (Spine.isTouched()) {
272
if (Solid.isTouched()) {
275
if (Frenet.isTouched()) {
278
if (Transition.isTouched()) {
284
void Sweep::onChanged(const App::Property* prop)
286
Part::Feature::onChanged(prop);
289
App::DocumentObjectExecReturn* Sweep::execute()
291
if (Sections.getSize() == 0) {
292
return new App::DocumentObjectExecReturn("No sections linked.");
294
if (!Spine.getValue()) {
295
return new App::DocumentObjectExecReturn("No spine");
297
TopoShape spine = getTopoShape(Spine.getValue());
298
const auto& subs = Spine.getSubValues();
299
if (spine.isNull()) {
300
return new App::DocumentObjectExecReturn("Invalid spine");
303
std::vector<TopoShape> spineShapes;
304
for (auto sub : subs) {
305
auto shape = spine.getSubTopoShape(sub.c_str());
306
if (shape.isNull()) {
307
return new App::DocumentObjectExecReturn("Invalid spine");
309
spineShapes.push_back(shape);
311
spine = TopoShape().makeElementCompound(spineShapes, 0, TopoShape::SingleShapeCompoundCreationPolicy::returnShape);
313
std::vector<TopoShape> shapes;
314
shapes.push_back(spine);
315
for (auto& obj : Sections.getValues()) {
316
shapes.emplace_back(getTopoShape(obj));
317
if (shapes.back().isNull()) {
318
return new App::DocumentObjectExecReturn("Invalid section link");
321
MakeSolid isSolid = Solid.getValue() ? MakeSolid::makeSolid : MakeSolid::noSolid;
322
Standard_Boolean isFrenet = Frenet.getValue() ? Standard_True : Standard_False;
323
auto transMode = static_cast<TransitionMode>(Transition.getValue());
326
result.makeElementPipeShell(shapes, isSolid, isFrenet, transMode, Part::OpCodes::Sweep);
327
if (Linearize.getValue()) {
328
result.linearize(LinearizeFace::linearizeFaces, LinearizeEdge::noEdges);
330
this->Shape.setValue(result);
331
return App::DocumentObject::StdReturn;
333
catch (Standard_Failure& e) {
335
return new App::DocumentObjectExecReturn(e.GetMessageString());
338
return new App::DocumentObjectExecReturn("A fatal error occurred when making the sweep");
342
void Part::Sweep::setupObject()
344
Feature::setupObject();
345
// Linearize.setValue(PartParams::getLinearizeExtrusionDraft()); // TODO: Resolve after PartParams
348
// ----------------------------------------------------------------------------
350
const char* Part::Thickness::ModeEnums[] = {"Skin", "Pipe", "RectoVerso", nullptr};
351
const char* Part::Thickness::JoinEnums[] = {"Arc", "Tangent", "Intersection", nullptr};
353
PROPERTY_SOURCE(Part::Thickness, Part::Feature)
355
Thickness::Thickness()
357
ADD_PROPERTY_TYPE(Faces, (nullptr), "Thickness", App::Prop_None, "Faces to be removed");
358
ADD_PROPERTY_TYPE(Value, (1.0), "Thickness", App::Prop_None, "Thickness value");
359
ADD_PROPERTY_TYPE(Mode, (long(0)), "Thickness", App::Prop_None, "Mode");
360
Mode.setEnums(ModeEnums);
361
ADD_PROPERTY_TYPE(Join, (long(0)), "Thickness", App::Prop_None, "Join type");
362
Join.setEnums(JoinEnums);
363
ADD_PROPERTY_TYPE(Intersection, (false), "Thickness", App::Prop_None, "Intersection");
364
ADD_PROPERTY_TYPE(SelfIntersection, (false), "Thickness", App::Prop_None, "Self Intersection");
366
// Value should have length as unit
367
Value.setUnit(Base::Unit::Length);
370
short Thickness::mustExecute() const
372
if (Faces.isTouched()) {
375
if (Value.isTouched()) {
378
if (Mode.isTouched()) {
381
if (Join.isTouched()) {
384
if (Intersection.isTouched()) {
387
if (SelfIntersection.isTouched()) {
393
void Thickness::handleChangedPropertyType(Base::XMLReader& reader,
394
const char* TypeName,
397
if (prop == &Value && strcmp(TypeName, "App::PropertyFloat") == 0) {
398
App::PropertyFloat v;
402
Value.setValue(v.getValue());
405
Part::Feature::handleChangedPropertyType(reader, TypeName, prop);
409
App::DocumentObjectExecReturn* Thickness::execute()
411
std::vector<TopoShape> shapes;
412
auto base = getTopoShape(Faces.getValue());
414
return new App::DocumentObjectExecReturn("Invalid source shape");
416
if (base.countSubShapes(TopAbs_SOLID) != 1) {
417
return new App::DocumentObjectExecReturn("Source shape is not single solid.");
419
for (auto& sub : Faces.getSubValues(true)) {
420
shapes.push_back(base.getSubTopoShape(sub.c_str()));
421
if (shapes.back().getShape().ShapeType() != TopAbs_FACE) {
422
return new App::DocumentObjectExecReturn("Invalid face selection");
425
double thickness = Value.getValue();
426
double tol = Precision::Confusion();
427
bool inter = Intersection.getValue();
428
bool self = SelfIntersection.getValue();
429
short mode = (short)Mode.getValue();
430
short join = (short)Join.getValue();
432
this->Shape.setValue(TopoShape(0,getDocument()->getStringHasher())
433
.makeElementThickSolid(base,
440
static_cast<JoinType>(join)));
441
return Part::Feature::execute();
444
// ----------------------------------------------------------------------------
446
PROPERTY_SOURCE(Part::Refine, Part::Feature)
450
ADD_PROPERTY_TYPE(Source, (nullptr), "Refine", App::Prop_None, "Source shape");
453
App::DocumentObjectExecReturn* Refine::execute()
455
Part::Feature* source = Source.getValue<Part::Feature*>();
457
return new App::DocumentObjectExecReturn("No part object linked.");
461
TopoShape myShape = source->Shape.getShape();
462
this->Shape.setValue(myShape.removeSplitter());
463
return App::DocumentObject::StdReturn;
465
catch (Standard_Failure& e) {
466
return new App::DocumentObjectExecReturn(e.GetMessageString());
470
// ----------------------------------------------------------------------------
472
PROPERTY_SOURCE(Part::Reverse, Part::Feature)
476
ADD_PROPERTY_TYPE(Source, (nullptr), "Reverse", App::Prop_None, "Source shape");
479
App::DocumentObjectExecReturn* Reverse::execute()
481
App::DocumentObject* source = Source.getValue<App::DocumentObject*>();
482
Part::TopoShape topoShape = Part::Feature::getTopoShape(source);
483
if (topoShape.isNull()) {
484
return new App::DocumentObjectExecReturn("No part object linked.");
488
TopoDS_Shape myShape = topoShape.getShape();
489
if (!myShape.IsNull()) {
490
this->Shape.setValue(myShape.Reversed());
492
p.fromMatrix(topoShape.getTransform());
493
this->Placement.setValue(p);
494
return App::DocumentObject::StdReturn;
496
return new App::DocumentObjectExecReturn("Shape is null.");
498
catch (Standard_Failure& e) {
499
return new App::DocumentObjectExecReturn(e.GetMessageString());