1
/***************************************************************************
2
* Copyright (c) 2010 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"
25
# include <BRepBuilderAPI_Transform.hxx>
26
# include <BRep_Tool.hxx>
28
# include <gp_Circ.hxx>
31
# include <gp_Trsf.hxx>
32
# include <BRepAdaptor_Curve.hxx>
33
# include <BRepAdaptor_Surface.hxx>
35
# include <Geom_Plane.hxx>
37
# include <TopoDS_Face.hxx>
38
# include <TopExp_Explorer.hxx>
41
#include <Mod/Part/App/PrimitiveFeature.h>
43
#include <App/OriginFeature.h>
45
#include "FeatureMirroring.h"
46
#include "DatumFeature.h"
52
PROPERTY_SOURCE(Part::Mirroring, Part::Feature)
56
ADD_PROPERTY(Source,(nullptr));
57
ADD_PROPERTY_TYPE(Base,(Base::Vector3d()),"Plane",App::Prop_None,"The base point of the plane");
58
ADD_PROPERTY_TYPE(Normal,(Base::Vector3d(0,0,1)),"Plane",App::Prop_None,"The normal of the plane");
59
ADD_PROPERTY_TYPE(MirrorPlane,(nullptr),"Plane",App::Prop_None,"A reference for the mirroring plane, overrides Base and Normal if set, can be face or circle");
62
short Mirroring::mustExecute() const
64
if (Source.isTouched())
68
if (Normal.isTouched())
70
if (MirrorPlane.isTouched())
75
void Mirroring::onChanged(const App::Property* prop)
78
In the case the user has a reference plane object, then
79
Base and Normal are computed based on that object. We must
80
handle this by setting the changes to Base and Normal to not
81
trigger a recompute when they are computed and changed. We
82
should also set Base and Normal to readonly so not to confuse
83
the user, who might try to change them despite having a reference
84
object. We could also hide them, but they contain useful information.
87
bool needsRecompute = false;
88
App::DocumentObject* refObject = MirrorPlane.getValue();
90
Base.setStatus(App::Property::ReadOnly, false);
91
Normal.setStatus(App::Property::ReadOnly, false);
92
if (prop == &Base || prop == &Normal) {
93
needsRecompute = true;
96
if (prop == &MirrorPlane){
97
Base.setStatus(App::Property::ReadOnly, true);
98
Normal.setStatus(App::Property::ReadOnly, true);
99
needsRecompute = true;
104
App::DocumentObjectExecReturn *ret = recompute();
111
Part::Feature::onChanged(prop);
114
void Mirroring::handleChangedPropertyType(Base::XMLReader &reader, const char *TypeName, App::Property *prop)
116
if (prop == &Base && strcmp(TypeName, "App::PropertyVector") == 0) {
117
App::PropertyVector v;
121
Base.setValue(v.getValue());
123
else if (prop == &Normal && strcmp(TypeName, "App::PropertyVector") == 0) {
124
App::PropertyVector v;
128
Normal.setValue(v.getValue());
131
Part::Feature::handleChangedPropertyType(reader, TypeName, prop);
135
App::DocumentObjectExecReturn *Mirroring::execute()
137
App::DocumentObject* link = Source.getValue();
139
return new App::DocumentObjectExecReturn("No object linked");
141
App::DocumentObject* refObject = MirrorPlane.getValue();
143
std::vector<std::string> subStrings = MirrorPlane.getSubValues();
148
Support mirror plane reference objects:
149
DatumPlanes, Part::Planes, Origin planes, Faces, Circles
150
Can also be App::Links to such objects
153
if (refObject->isDerivedFrom(Part::Plane::getClassTypeId()) || refObject->isDerivedFrom<App::Plane>() || (strstr(refObject->getNameInDocument(), "Plane")
154
&& refObject->isDerivedFrom(Part::Datum::getClassTypeId()))) {
155
Part::Feature* plane = static_cast<Part::Feature*>(refObject);
156
Base::Vector3d base = plane->Placement.getValue().getPosition();
157
axbase = gp_Pnt(base.x, base.y, base.z);
158
Base::Rotation rot = plane->Placement.getValue().getRotation();
160
rot.multVec(Base::Vector3d(0,0,1), dir);
161
axdir = gp_Dir(dir.x, dir.y, dir.z);
162
// reference is an app::link or a part::feature or some subobject
163
} else if (refObject->isDerivedFrom<Part::Feature>() || refObject->isDerivedFrom<App::Link>()) {
164
if (subStrings.size() > 1){
165
throw Base::ValueError(std::string(this->getFullLabel()) + ": Only 1 subobject is supported for Mirror Plane reference, either a plane face or a circle edge.");
168
auto linked = MirrorPlane.getValue();
169
bool isFace = false; //will be true if user selected face subobject or if object only has 1 face
170
bool isEdge = false; //will be true if user selected edge subobject or if object only has 1 edge
172
if (!subStrings.empty() && subStrings[0].length() > 0){
173
shape = Feature::getTopoShape(linked, subStrings[0].c_str(), true).getShape();
174
if (strstr(subStrings[0].c_str(), "Face")){
175
isFace = true; //was face subobject, e.g. Face3
177
if (strstr(subStrings[0].c_str(), "Edge")){
178
isEdge = true; //was edge subobject, e.g. Edge7
182
shape = Feature::getShape(linked); //no subobjects were selected, so this is entire shape of feature
185
// if there is only 1 face or 1 edge, then we don't need to force the user to select that face or edge
186
// instead we can infer what was intended
187
int faceCount = Part::TopoShape(shape).countSubShapes(TopAbs_FACE);
188
int edgeCount = Part::TopoShape(shape).countSubShapes(TopAbs_EDGE);
193
if (isFace) { //user selected a face, so use shape to get the TopoDS::Face
194
face = TopoDS::Face(shape);
196
if (faceCount == 1) { //entire feature selected, but it only has 1 face, so get that face
197
TopoDS_Shape tdface = Part::TopoShape(shape).getSubShape(std::string("Face1").c_str());
198
face = TopoDS::Face(tdface);
202
if (!isFace && isEdge){ //don't bother with edge if we already have a face to work with
203
edge = TopoDS::Edge(shape); //isEdge means an edge was selected
205
if (edgeCount == 1){ //we don't have a face yet and there were no edges in the subobject selection
206
//but since this object only has 1 edge, we use it
207
TopoDS_Shape tdedge = Part::TopoShape(shape).getSubShape(std::string("Edge1").c_str());
208
edge = TopoDS::Edge(tdedge);
213
if (isFace && face.IsNull()) { //ensure we have a good face to work with
214
throw Base::ValueError(std::string(this->getFullLabel()) + ": Failed to extract mirror plane because face is null");
216
if (isEdge && edge.IsNull()){ //ensure we have a good edge to work with
217
throw Base::ValueError(std::string(this->getFullLabel()) + ": Failed to extract mirror plane because edge is null");
219
if (!isFace && !isEdge){
220
throw Base::ValueError(std::string(this->getFullLabel()) + ": Failed to extract mirror plane, unable to determine which face or edge to use.");
224
BRepAdaptor_Surface adapt(face);
225
if (adapt.GetType() != GeomAbs_Plane)
226
throw Base::TypeError(std::string(this->getFullLabel()) + ": Mirror plane face must be planar");
228
exp.Init(face, TopAbs_VERTEX);
230
axbase = BRep_Tool::Pnt(TopoDS::Vertex(exp.Current()));
232
axdir = adapt.Plane().Axis().Direction();
235
BRepAdaptor_Curve curve(edge);
236
if (!(curve.GetType() == GeomAbs_Circle)) {
237
throw Base::TypeError(std::string(this->getFullLabel()) + ": Only circle edge types are supported");
239
gp_Circ circle = curve.Circle();
240
axdir = circle.Axis().Direction();
241
axbase = circle.Location();
245
throw Base::ValueError(std::string(this->getFullLabel()) + ": Mirror plane reference must be a face of a feature or a plane object or a circle");
247
Base.setValue(axbase.X(), axbase.Y(), axbase.Z());
248
Normal.setValue(axdir.X(), axdir.Y(), axdir.Z());
251
Base::Vector3d base = Base.getValue();
252
Base::Vector3d norm = Normal.getValue();
255
gp_Ax2 ax2(gp_Pnt(base.x,base.y,base.z), gp_Dir(norm.x,norm.y,norm.z));
256
auto shape = Feature::getTopoShape(link);
258
Standard_Failure::Raise("Cannot mirror empty shape");
259
this->Shape.setValue(TopoShape(0).makeElementMirror(shape,ax2));
260
return Part::Feature::execute();
262
catch (Standard_Failure& e) {
263
return new App::DocumentObjectExecReturn(e.GetMessageString());