1
/***************************************************************************
2
* Copyright (c) 2016 Victor Titov (DeepSOIC) <vv.titov@gmail.com> *
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_MakeFace.hxx>
26
# include <BRepBuilderAPI_MakeWire.hxx>
28
# include <TopoDS_Builder.hxx>
29
# include <TopoDS_Iterator.hxx>
36
#include <App/MappedElement.h>
38
#include "TopoShapeOpCode.h"
41
TYPESYSTEM_SOURCE_ABSTRACT(Part::FaceMaker, Base::BaseClass)
42
TYPESYSTEM_SOURCE_ABSTRACT(Part::FaceMakerPublic, Part::FaceMaker)
44
void Part::FaceMaker::addWire(const TopoDS_Wire& w)
49
void Part::FaceMaker::addShape(const TopoDS_Shape& sh)
54
void Part::FaceMaker::addTopoShape(const TopoShape& shape) {
55
const TopoDS_Shape &sh = shape.getShape();
57
throw Base::ValueError("Input shape is null.");
58
switch(sh.ShapeType()){
60
this->myCompounds.push_back(TopoDS::Compound(sh));
63
this->myWires.push_back(TopoDS::Wire(sh));
64
this->myTopoWires.push_back(shape);
67
this->myWires.push_back(BRepBuilderAPI_MakeWire(TopoDS::Edge(sh)).Wire());
68
this->myTopoWires.push_back(shape);
69
this->myTopoWires.back().setShape(this->myWires.back(), false);
72
this->myInputFaces.push_back(sh);
75
// This is a special case, since this is generally a stand-alone point in a sketch. We
76
// need to ignore it rather than throw an error
79
throw Base::TypeError(tr("Shape must be a wire, edge or compound. Something else was supplied.").toStdString());
82
this->mySourceShapes.push_back(shape);
85
void Part::FaceMaker::useCompound(const TopoDS_Compound& comp)
87
TopoDS_Iterator it(comp);
88
for(; it.More(); it.Next()){
89
this->addShape(it.Value());
93
void Part::FaceMaker::useTopoCompound(const TopoShape& comp)
95
for(auto &s : comp.getSubTopoShapes())
96
this->addTopoShape(s);
99
const TopoDS_Face& Part::FaceMaker::Face()
101
return TopoDS::Face(TopoFace().getShape());
104
const Part::TopoShape &Part::FaceMaker::TopoFace() const{
105
if(this->myTopoShape.isNull())
106
throw NullShapeException("Part::FaceMaker: result shape is null.");
107
if (this->myTopoShape.getShape().ShapeType() != TopAbs_FACE)
108
throw Base::TypeError("Part::FaceMaker: return shape is not a single face.");
109
return this->myTopoShape;
112
const Part::TopoShape &Part::FaceMaker::getTopoShape() const{
113
if(this->myTopoShape.isNull())
114
throw NullShapeException("Part::FaceMaker: result shape is null.");
115
return this->myTopoShape;
118
#if OCC_VERSION_HEX >= 0x070600
119
void Part::FaceMaker::Build(const Message_ProgressRange&)
121
void Part::FaceMaker::Build()
125
this->myShapesToReturn = this->myInputFaces;
126
this->myGenerated.Clear();
128
this->Build_Essence();//adds stuff to myShapesToReturn
130
for(const TopoDS_Compound& cmp : this->myCompounds){
131
std::unique_ptr<FaceMaker> facemaker = Part::FaceMaker::ConstructFromType(this->getTypeId());
133
facemaker->useCompound(cmp);
136
const TopoDS_Shape &subfaces = facemaker->Shape();
137
if (subfaces.IsNull())
139
if (subfaces.ShapeType() == TopAbs_COMPOUND){
140
this->myShapesToReturn.push_back(subfaces);
142
//result is not a compound (probably, a face)... but we want to follow compounding structure of input, so wrap it into compound.
143
TopoDS_Builder builder;
144
TopoDS_Compound cmp_res;
145
builder.MakeCompound(cmp_res);
146
builder.Add(cmp_res,subfaces);
147
this->myShapesToReturn.push_back(cmp_res);
151
if(this->myShapesToReturn.empty()){
152
//nothing to do, null shape will be returned.
153
this->myShape = TopoDS_Shape();
154
} else if (this->myShapesToReturn.size() == 1){
155
this->myShape = this->myShapesToReturn[0];
157
TopoDS_Builder builder;
158
TopoDS_Compound cmp_res;
159
builder.MakeCompound(cmp_res);
160
for(TopoDS_Shape &sh: this->myShapesToReturn){
161
builder.Add(cmp_res,sh);
163
this->myShape = cmp_res;
171
Data::MappedName name;
172
Data::ElementIDRefs sids;
174
ElementName(long t, const Data::MappedName &n, const Data::ElementIDRefs &sids)
175
:tag(t),name(n), sids(sids)
178
inline bool operator<(const ElementName &other) const {
183
return Data::ElementNameComparator()(name,other.name);
187
void Part::FaceMaker::postBuild() {
188
this->myTopoShape.setShape(this->myShape);
189
this->myTopoShape.Hasher = this->MyHasher;
190
this->myTopoShape.mapSubElement(this->mySourceShapes);
192
const char *op = this->MyOp;
194
op = Part::OpCodes::Face;
195
const auto &faces = this->myTopoShape.getSubTopoShapes(TopAbs_FACE);
196
std::set<Data::MappedName> namesUsed;
197
// name the face using the edges of its outer wire
198
for(auto &face : faces) {
200
TopoShape wire = face.splitWires();
201
wire.mapSubElement(face);
202
std::set<ElementName> edgeNames;
203
int count = wire.countSubShapes(TopAbs_EDGE);
204
for (int index2 = 1; index2 <= count; ++index2) {
205
Data::ElementIDRefs sids;
206
Data::MappedName name =
207
face.getMappedName(Data::IndexedName::fromConst("Edge", index2), false, &sids);
211
edgeNames.emplace(wire.getElementHistory(name), name, sids);
213
if (edgeNames.empty()) {
217
std::vector<Data::MappedName> names;
218
Data::ElementIDRefs sids;
219
// To avoid name collision, we keep track of any used names to make sure
220
// to use at least 'minElementNames' number of unused element names to
221
// generate the face name.
223
for (const auto &e : edgeNames) {
224
names.push_back(e.name);
226
if (namesUsed.insert(e.name).second) {
227
if (++nameCount >= minElementNames)
231
this->myTopoShape.setElementComboName(
232
Data::IndexedName::fromConst("Face",index),names,op,nullptr,&sids);
234
this->myTopoShape.initCache(true);
238
std::unique_ptr<Part::FaceMaker> Part::FaceMaker::ConstructFromType(const char* className)
240
Base::Type fmType = Base::Type::fromName(className);
242
std::stringstream ss;
243
ss << "Class '"<< className <<"' not found.";
244
throw Base::TypeError(ss.str().c_str());
246
return Part::FaceMaker::ConstructFromType(fmType);
249
std::unique_ptr<Part::FaceMaker> Part::FaceMaker::ConstructFromType(Base::Type type)
251
if (!type.isDerivedFrom(Part::FaceMaker::getClassTypeId())){
252
std::stringstream ss;
253
ss << "Class '" << type.getName() << "' is not derived from Part::FaceMaker.";
254
throw Base::TypeError(ss.str().c_str());
256
std::unique_ptr<FaceMaker> instance(static_cast<Part::FaceMaker*>(type.createInstance()));
258
std::stringstream ss;
259
ss << "Cannot create FaceMaker from abstract type '" << type.getName() << "'";
260
throw Base::TypeError(ss.str().c_str());
265
void Part::FaceMaker::throwNotImplemented()
267
throw Base::NotImplementedError("Not implemented yet...");
271
//----------------------------------------------------------------------------------------
273
TYPESYSTEM_SOURCE(Part::FaceMakerSimple, Part::FaceMakerPublic)
276
std::string Part::FaceMakerSimple::getUserFriendlyName() const
278
return {tr("Simple").toStdString()};
281
std::string Part::FaceMakerSimple::getBriefExplanation() const
283
return {tr("Makes separate plane face from every wire independently. No support for holes; wires can be on different planes.").toStdString()};
287
void Part::FaceMakerSimple::Build_Essence()
289
for(TopoDS_Wire &w: myWires){
290
this->myShapesToReturn.push_back(BRepBuilderAPI_MakeFace(w).Shape());