forked from FreeCAD/FreeCAD
-
Notifications
You must be signed in to change notification settings - Fork 0
/
FeaturePocket.cpp
267 lines (227 loc) · 11.8 KB
/
FeaturePocket.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/***************************************************************************
* Copyright (c) 2010 Juergen Riegel <FreeCAD@juergen-riegel.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <Bnd_Box.hxx>
# include <gp_Dir.hxx>
# include <gp_Pln.hxx>
# include <BRep_Builder.hxx>
# include <BRepAdaptor_Surface.hxx>
# include <BRepBndLib.hxx>
# include <BRepFeat_MakePrism.hxx>
# include <BRepBuilderAPI_MakeFace.hxx>
# include <Geom_Plane.hxx>
# include <Geom_Surface.hxx>
# include <TopoDS.hxx>
# include <TopoDS_Face.hxx>
# include <TopoDS_Wire.hxx>
# include <TopoDS_Solid.hxx>
# include <TopExp_Explorer.hxx>
# include <BRepAlgoAPI_Cut.hxx>
# include <BRepPrimAPI_MakeHalfSpace.hxx>
# include <BRepAlgoAPI_Common.hxx>
#endif
#include <Base/Console.h>
#include <Base/Exception.h>
#include <Base/Placement.h>
#include <App/Document.h>
#include "FeaturePocket.h"
using namespace PartDesign;
/* TRANSLATOR PartDesign::Pocket */
const char* Pocket::TypeEnums[]= {"Length","ThroughAll","UpToFirst","UpToFace","TwoLengths",NULL};
PROPERTY_SOURCE(PartDesign::Pocket, PartDesign::ProfileBased)
Pocket::Pocket()
{
addSubType = FeatureAddSub::Subtractive;
ADD_PROPERTY_TYPE(Type,((long)0),"Pocket",App::Prop_None,"Pocket type");
Type.setEnums(TypeEnums);
ADD_PROPERTY_TYPE(Length,(100.0),"Pocket",App::Prop_None,"Pocket length");
ADD_PROPERTY_TYPE(Length2,(100.0),"Pocket",App::Prop_None,"P");
ADD_PROPERTY_TYPE(UpToFace,(0),"Pocket",App::Prop_None,"Face where pocket will end");
ADD_PROPERTY_TYPE(Offset,(0.0),"Pocket",App::Prop_None,"Offset from face in which pocket will end");
ADD_PROPERTY_TYPE(Outside,(false),"Pocket",App::Prop_None,"Remove outside of profile");
static const App::PropertyQuantityConstraint::Constraints signedLengthConstraint = {-DBL_MAX, DBL_MAX, 1.0};
Offset.setConstraints ( &signedLengthConstraint );
// Remove the constraints and keep the type to allow to accept negative values
// https://forum.freecadweb.org/viewtopic.php?f=3&t=52075&p=448410#p447636
Length2.setConstraints(nullptr);
}
short Pocket::mustExecute() const
{
if (Placement.isTouched() ||
Type.isTouched() ||
Length.isTouched() ||
Length2.isTouched() ||
Offset.isTouched() ||
UpToFace.isTouched())
return 1;
return ProfileBased::mustExecute();
}
App::DocumentObjectExecReturn *Pocket::execute(void)
{
// Handle legacy features, these typically have Type set to 3 (previously NULL, now UpToFace),
// empty FaceName (because it didn't exist) and a value for Length
if (std::string(Type.getValueAsString()) == "UpToFace" &&
(UpToFace.getValue() == NULL && Length.getValue() > Precision::Confusion()))
Type.setValue("Length");
// Validate parameters
double L = Length.getValue();
if ((std::string(Type.getValueAsString()) == "Length") && (L < Precision::Confusion()))
return new App::DocumentObjectExecReturn("Pocket: Length of pocket too small");
double L2 = Length2.getValue();
if ((std::string(Type.getValueAsString()) == "TwoLengths") && (L < Precision::Confusion()))
return new App::DocumentObjectExecReturn("Pocket: Second length of pocket too small");
Part::Feature* obj = 0;
TopoDS_Shape profileshape;
try {
obj = getVerifiedObject();
profileshape = getVerifiedFace();
} catch (const Base::Exception& e) {
return new App::DocumentObjectExecReturn(e.what());
}
// if the Base property has a valid shape, fuse the prism into it
TopoDS_Shape base;
try {
base = getBaseShape();
}
catch (const Base::Exception&) {
std::string text(QT_TR_NOOP("The requested feature cannot be created. The reason may be that:\n"
" - the active Body does not contain a base shape, so there is no\n"
" material to be removed;\n"
" - the selected sketch does not belong to the active Body."));
return new App::DocumentObjectExecReturn(text);
}
// get the Sketch plane
Base::Placement SketchPos = obj->Placement.getValue();
Base::Vector3d SketchVector = getProfileNormal();
// turn around for pockets
SketchVector *= -1;
try {
this->positionByPrevious();
TopLoc_Location invObjLoc = this->getLocation().Inverted();
base.Move(invObjLoc);
gp_Dir dir(SketchVector.x,SketchVector.y,SketchVector.z);
dir.Transform(invObjLoc.Transformation());
if (profileshape.IsNull())
return new App::DocumentObjectExecReturn("Pocket: Creating a face from sketch failed");
profileshape.Move(invObjLoc);
std::string method(Type.getValueAsString());
if (method == "UpToFirst" || method == "UpToFace") {
if (base.IsNull())
return new App::DocumentObjectExecReturn("Pocket: Extruding up to a face is only possible if the sketch is located on a face");
// Note: This will return an unlimited planar face if support is a datum plane
TopoDS_Face supportface = getSupportFace();
supportface.Move(invObjLoc);
if (Reversed.getValue())
dir.Reverse();
// Find a valid face or datum plane to extrude up to
TopoDS_Face upToFace;
if (method == "UpToFace") {
getUpToFaceFromLinkSub(upToFace, UpToFace);
upToFace.Move(invObjLoc);
}
getUpToFace(upToFace, base, supportface, profileshape, method, dir);
addOffsetToFace(upToFace, dir, Offset.getValue());
// BRepFeat_MakePrism(..., 2, 1) in combination with PerForm(upToFace) is buggy when the
// prism that is being created is contained completely inside the base solid
// In this case the resulting shape is empty. This is not a problem for the Pad or Pocket itself
// but it leads to an invalid SubShape
// The bug only occurs when the upToFace is limited (by a wire), not for unlimited upToFace. But
// other problems occur with unlimited concave upToFace so it is not an option to always unlimit upToFace
// Check supportface for limits, otherwise Perform() throws an exception
TopExp_Explorer Ex(supportface,TopAbs_WIRE);
if (!Ex.More())
supportface = TopoDS_Face();
#if 0
BRepFeat_MakePrism PrismMaker;
PrismMaker.Init(base, profileshape, supportface, dir, 0, 1);
PrismMaker.Perform(upToFace);
if (!PrismMaker.IsDone())
return new App::DocumentObjectExecReturn("Pocket: Up to face: Could not extrude the sketch!");
TopoDS_Shape prism = PrismMaker.Shape();
#else
TopoDS_Shape prism;
PrismMode mode = PrismMode::CutFromBase;
generatePrism(prism, method, base, profileshape, supportface, upToFace, dir, mode, Standard_True);
#endif
// And the really expensive way to get the SubShape...
BRepAlgoAPI_Cut mkCut(base, prism);
if (!mkCut.IsDone())
return new App::DocumentObjectExecReturn("Pocket: Up to face: Could not get SubShape!");
// FIXME: In some cases this affects the Shape property: It is set to the same shape as the SubShape!!!!
TopoDS_Shape result = refineShapeIfActive(mkCut.Shape());
this->AddSubShape.setValue(result);
int prismCount = countSolids(prism);
if (prismCount > 1) {
return new App::DocumentObjectExecReturn("Pocket: Result has multiple solids. This is not supported at this time.");
}
this->Shape.setValue(getSolid(prism));
} else {
TopoDS_Shape prism;
generatePrism(prism, profileshape, method, dir, L, L2,
Midplane.getValue(), Reversed.getValue());
if (prism.IsNull())
return new App::DocumentObjectExecReturn("Pocket: Resulting shape is empty");
// set the subtractive shape property for later usage in e.g. pattern
prism = refineShapeIfActive(prism);
this->AddSubShape.setValue(prism);
TopoDS_Shape result;
if (!Outside.getValue()) {
// Cut the SubShape out of the base feature
BRepAlgoAPI_Cut mkCut(base, prism);
if (!mkCut.IsDone())
return new App::DocumentObjectExecReturn("Pocket: Cut out of base feature failed");
result = mkCut.Shape();
} else {
// Cut the base feature to SubShape
BRepAlgoAPI_Common mkCommon(base, prism);
if (!mkCommon.IsDone())
return new App::DocumentObjectExecReturn("Pocket: Cut out of base feature failed");
result = mkCommon.Shape();
}
// we have to get the solids (fuse sometimes creates compounds)
TopoDS_Shape solRes = this->getSolid(result);
if (solRes.IsNull())
return new App::DocumentObjectExecReturn("Pocket: Resulting shape is not a solid");
int solidCount = countSolids(result);
if (solidCount > 1) {
return new App::DocumentObjectExecReturn("Pocket: Result has multiple solids. This is not supported at this time.");
}
solRes = refineShapeIfActive(solRes);
remapSupportShape(solRes);
this->Shape.setValue(getSolid(solRes));
}
return App::DocumentObject::StdReturn;
}
catch (Standard_Failure& e) {
if (std::string(e.GetMessageString()) == "TopoDS::Face" &&
(std::string(Type.getValueAsString()) == "UpToFirst" || std::string(Type.getValueAsString()) == "UpToFace"))
return new App::DocumentObjectExecReturn("Could not create face from sketch.\n"
"Intersecting sketch entities or multiple faces in a sketch are not allowed "
"for making a pocket up to a face.");
else
return new App::DocumentObjectExecReturn(e.GetMessageString());
}
catch (Base::Exception& e) {
return new App::DocumentObjectExecReturn(e.what());
}
}