FreeCAD

Форк
0
/
FeatureMirroring.cpp 
265 строк · 11.6 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2010 Werner Mayer <wmayer[at]users.sourceforge.net>     *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
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.      *
10
 *                                                                         *
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.                  *
15
 *                                                                         *
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                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23
#include "PreCompiled.h"
24
#ifndef _PreComp_
25
# include <BRepBuilderAPI_Transform.hxx>
26
# include <BRep_Tool.hxx>
27
# include <gp_Ax2.hxx>
28
# include <gp_Circ.hxx>
29
# include <gp_Dir.hxx>
30
# include <gp_Pnt.hxx>
31
# include <gp_Trsf.hxx>
32
# include <BRepAdaptor_Curve.hxx>
33
# include <BRepAdaptor_Surface.hxx>
34
# include <gp_Pln.hxx>
35
# include <Geom_Plane.hxx>
36
# include <TopoDS.hxx>
37
# include <TopoDS_Face.hxx>
38
# include <TopExp_Explorer.hxx>
39
#endif
40

41
#include <Mod/Part/App/PrimitiveFeature.h>
42
#include <App/Link.h>
43
#include <App/OriginFeature.h>
44

45
#include "FeatureMirroring.h"
46
#include "DatumFeature.h"
47

48

49

50
using namespace Part;
51

52
PROPERTY_SOURCE(Part::Mirroring, Part::Feature)
53

54
Mirroring::Mirroring()
55
{
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");
60
}
61

62
short Mirroring::mustExecute() const
63
{
64
    if (Source.isTouched())
65
        return 1;
66
    if (Base.isTouched())
67
        return 1;
68
    if (Normal.isTouched())
69
        return 1;
70
    if (MirrorPlane.isTouched())
71
        return 1;
72
    return 0;
73
}
74

75
void Mirroring::onChanged(const App::Property* prop)
76
{
77
    /**
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.
85
    */
86
    if (!isRestoring()) {
87
        bool needsRecompute = false;
88
        App::DocumentObject* refObject = MirrorPlane.getValue();
89
        if (!refObject){
90
            Base.setStatus(App::Property::ReadOnly, false);
91
            Normal.setStatus(App::Property::ReadOnly, false);
92
            if (prop == &Base || prop == &Normal) {
93
                needsRecompute = true;
94
            }
95
        } else {
96
            if (prop == &MirrorPlane){
97
                Base.setStatus(App::Property::ReadOnly, true);
98
                Normal.setStatus(App::Property::ReadOnly, true);
99
                needsRecompute = true;
100
            }
101
        }
102
        if (needsRecompute){
103
            try {
104
                App::DocumentObjectExecReturn *ret = recompute();
105
                delete ret;
106
            }
107
            catch (...) {
108
            }
109
        }
110
    }
111
    Part::Feature::onChanged(prop);
112
}
113

114
void Mirroring::handleChangedPropertyType(Base::XMLReader &reader, const char *TypeName, App::Property *prop)
115
{
116
    if (prop == &Base && strcmp(TypeName, "App::PropertyVector") == 0) {
117
        App::PropertyVector v;
118

119
        v.Restore(reader);
120

121
        Base.setValue(v.getValue());
122
    }
123
    else if (prop == &Normal && strcmp(TypeName, "App::PropertyVector") == 0) {
124
        App::PropertyVector v;
125

126
        v.Restore(reader);
127

128
        Normal.setValue(v.getValue());
129
    }
130
    else {
131
        Part::Feature::handleChangedPropertyType(reader, TypeName, prop);
132
    }
133
}
134

135
App::DocumentObjectExecReturn *Mirroring::execute()
136
{
137
    App::DocumentObject* link = Source.getValue();
138
    if (!link)
139
        return new App::DocumentObjectExecReturn("No object linked");
140

141
    App::DocumentObject* refObject = MirrorPlane.getValue();
142

143
    std::vector<std::string> subStrings = MirrorPlane.getSubValues();
144

145
    gp_Pnt axbase;
146
    gp_Dir axdir;
147
    /**
148
      Support mirror plane reference objects:
149
      DatumPlanes, Part::Planes, Origin planes, Faces, Circles
150
      Can also be App::Links to such objects
151
    */
152
    if (refObject){
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();
159
            Base::Vector3d dir;
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.");
166

167
            }
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
171
            TopoDS_Shape shape;
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
176
                } else {
177
                    if (strstr(subStrings[0].c_str(), "Edge")){
178
                        isEdge = true; //was edge subobject, e.g. Edge7
179
                    }
180
                }
181
            } else {
182
                shape = Feature::getShape(linked); //no subobjects were selected, so this is entire shape of feature
183
            }
184

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

190
            TopoDS_Face face;
191
            TopoDS_Edge edge;
192

193
            if (isFace) { //user selected a face, so use shape to get the TopoDS::Face
194
                face = TopoDS::Face(shape);
195
            } else {
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);
199
                    isFace = true;
200
                }
201
            }
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
204
            } else {
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);
209
                    isEdge = true;
210
                }
211
            }
212

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");
215
            }
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");
218
            }
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.");
221
            }
222

223
            if (isFace) {
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");
227
                TopExp_Explorer exp;
228
                exp.Init(face, TopAbs_VERTEX);
229
                if (exp.More()) {
230
                    axbase = BRep_Tool::Pnt(TopoDS::Vertex(exp.Current()));
231
                }
232
                axdir = adapt.Plane().Axis().Direction();
233
            } else {
234
                if (isEdge){
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");
238
                    }
239
                    gp_Circ circle = curve.Circle();
240
                    axdir = circle.Axis().Direction();
241
                    axbase = circle.Location();
242
                }
243
            }
244
        } else {
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");
246
        }
247
        Base.setValue(axbase.X(), axbase.Y(), axbase.Z());
248
        Normal.setValue(axdir.X(), axdir.Y(), axdir.Z());
249
    }
250

251
    Base::Vector3d base = Base.getValue();
252
    Base::Vector3d norm = Normal.getValue();
253

254
    try {
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);
257
        if (shape.isNull())
258
            Standard_Failure::Raise("Cannot mirror empty shape");
259
        this->Shape.setValue(TopoShape(0).makeElementMirror(shape,ax2));
260
        return Part::Feature::execute();
261
    }
262
    catch (Standard_Failure& e) {
263
        return new App::DocumentObjectExecReturn(e.GetMessageString());
264
    }
265
}
266

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

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

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

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