Part: Fix toponaming issues. (#23151)
* fix split apart and splice * fix hasher gen issue in extrusion * fix broken gen in fillet/chamfer * error when elements go missing in fillet/chamfer * fix hashing in some elements * fix compiler errors * fix sweep * remove hasher from mirror * remove old import * add clarifying comment * Linter cleanup --------- Co-authored-by: Chris Hennes <chennes@pioneerlibrarysystem.org>
This commit is contained in:
@@ -49,22 +49,36 @@ App::DocumentObjectExecReturn *Chamfer::execute()
|
||||
|
||||
try {
|
||||
TopoShape baseTopoShape = Feature::getTopoShape(link, ShapeOption::ResolveLink | ShapeOption::Transform);
|
||||
auto baseShape = Feature::getShape(link, ShapeOption::ResolveLink | ShapeOption::Transform);
|
||||
const auto & baseShape = baseTopoShape.getShape();
|
||||
BRepFilletAPI_MakeChamfer mkChamfer(baseShape);
|
||||
TopTools_IndexedDataMapOfShapeListOfShape mapEdgeFace;
|
||||
TopExp::MapShapesAndAncestors(baseShape, TopAbs_EDGE, TopAbs_FACE, mapEdgeFace);
|
||||
TopTools_IndexedMapOfShape mapOfEdges;
|
||||
std::vector<Part::FilletElement> edges = Edges.getValues();
|
||||
TopExp::MapShapes(baseShape, TopAbs_EDGE, mapOfEdges);
|
||||
std::string fullErrMsg;
|
||||
|
||||
const auto &vals = EdgeLinks.getSubValues();
|
||||
const auto &subs = EdgeLinks.getShadowSubs();
|
||||
if(subs.size()!=(size_t)Edges.getSize())
|
||||
return new App::DocumentObjectExecReturn("Edge link size mismatch");
|
||||
size_t i=0;
|
||||
for(const auto &info : Edges.getValues()) {
|
||||
for(const auto &info : edges) {
|
||||
auto &sub = subs[i];
|
||||
auto &ref = sub.newName.size()?sub.newName:vals[i];
|
||||
auto &ref = sub.newName.empty() ? vals[i] : sub.newName;
|
||||
auto &oldName = sub.oldName.empty() ? "" : sub.oldName;
|
||||
++i;
|
||||
|
||||
if (Data::hasMissingElement(ref.c_str()) || Data::hasMissingElement(oldName.c_str())) {
|
||||
fullErrMsg.append("Missing edge link: ");
|
||||
fullErrMsg.append(ref);
|
||||
fullErrMsg.append("\n");
|
||||
|
||||
auto removeIt = std::remove(edges.begin(), edges.end(), info);
|
||||
edges.erase(removeIt, edges.end());
|
||||
|
||||
continue;
|
||||
}
|
||||
// Toponaming project March 2024: Replaced this code because it wouldn't work:
|
||||
// TopoDS_Shape edge;
|
||||
// try {
|
||||
@@ -80,6 +94,11 @@ App::DocumentObjectExecReturn *Chamfer::execute()
|
||||
mkChamfer.Add(radius1, radius2, TopoDS::Edge(edge), face);
|
||||
}
|
||||
|
||||
if (!fullErrMsg.empty()) {
|
||||
return new App::DocumentObjectExecReturn(fullErrMsg);
|
||||
}
|
||||
Edges.setValues(edges);
|
||||
|
||||
TopoDS_Shape shape = mkChamfer.Shape();
|
||||
if (shape.IsNull())
|
||||
return new App::DocumentObjectExecReturn("Resulting shape is null");
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
# include <TopTools_IndexedMapOfShape.hxx>
|
||||
#endif
|
||||
|
||||
#include <App/Document.h>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Tools.h>
|
||||
|
||||
@@ -374,7 +375,8 @@ App::DocumentObjectExecReturn* Extrusion::execute()
|
||||
|
||||
try {
|
||||
ExtrusionParameters params = computeFinalParameters();
|
||||
TopoShape result(0);
|
||||
TopoShape result(0, getDocument()->getStringHasher());
|
||||
|
||||
extrudeShape(result, Feature::getTopoShape(link, ShapeOption::ResolveLink | ShapeOption::Transform), params);
|
||||
this->Shape.setValue(result);
|
||||
return App::DocumentObject::StdReturn;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <App/PropertyStandard.h>
|
||||
#include <App/PropertyUnits.h>
|
||||
|
||||
#include <App/Document.h>
|
||||
#include "FaceMakerCheese.h"
|
||||
#include "PartFeature.h"
|
||||
#include "ExtrusionHelper.h"
|
||||
|
||||
@@ -54,22 +54,38 @@ App::DocumentObjectExecReturn *Fillet::execute()
|
||||
#if defined(__GNUC__) && defined (FC_OS_LINUX)
|
||||
Base::SignalException se;
|
||||
#endif
|
||||
auto baseShape = Feature::getShape(link, ShapeOption::ResolveLink | ShapeOption::Transform);
|
||||
TopoShape baseTopoShape = Feature::getTopoShape(link, ShapeOption::ResolveLink | ShapeOption::Transform);
|
||||
TopoShape baseTopoShape = Feature::getTopoShape(link, ShapeOption::ResolveLink | ShapeOption::Transform);
|
||||
auto baseShape = baseTopoShape.getShape();
|
||||
BRepFilletAPI_MakeFillet mkFillet(baseShape);
|
||||
TopTools_IndexedMapOfShape mapOfShape;
|
||||
TopExp::MapShapes(baseShape, TopAbs_EDGE, mapOfShape);
|
||||
TopTools_IndexedMapOfShape mapOfEdges;
|
||||
TopExp::MapShapes(baseShape, TopAbs_EDGE, mapOfEdges);
|
||||
const auto &vals = EdgeLinks.getSubValues();
|
||||
std::vector<Part::FilletElement> edges = Edges.getValues();
|
||||
std::string fullErrMsg;
|
||||
|
||||
const auto &vals = EdgeLinks.getSubValues(true);
|
||||
const auto &subs = EdgeLinks.getShadowSubs();
|
||||
if(subs.size()!=(size_t)Edges.getSize())
|
||||
return new App::DocumentObjectExecReturn("Edge link size mismatch");
|
||||
size_t i=0;
|
||||
for(const auto &info : Edges.getValues()) {
|
||||
for(const auto &info : edges) {
|
||||
auto &sub = subs[i];
|
||||
auto &ref = sub.newName.size()?sub.newName:vals[i];
|
||||
auto &ref = sub.newName.empty() ? vals[i] : sub.newName;
|
||||
auto &oldName = sub.oldName.empty() ? "" : sub.oldName;
|
||||
++i;
|
||||
|
||||
if (Data::hasMissingElement(ref.c_str()) || Data::hasMissingElement(oldName.c_str())) {
|
||||
fullErrMsg.append("Missing edge link: ");
|
||||
fullErrMsg.append(ref);
|
||||
fullErrMsg.append("\n");
|
||||
|
||||
auto removeIt = std::remove(edges.begin(), edges.end(), info);
|
||||
edges.erase(removeIt, edges.end());
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Toponaming project March 2024: Replaced this code because it wouldn't work:
|
||||
// TopoDS_Shape edge;
|
||||
// try {
|
||||
@@ -77,19 +93,26 @@ App::DocumentObjectExecReturn *Fillet::execute()
|
||||
// }catch(...){}
|
||||
auto id = Data::MappedName(ref.c_str()).toIndexedName().getIndex();
|
||||
const TopoDS_Edge& edge = TopoDS::Edge(mapOfEdges.FindKey(id));
|
||||
|
||||
if(edge.IsNull())
|
||||
return new App::DocumentObjectExecReturn("Invalid edge link");
|
||||
return new App::DocumentObjectExecReturn("Invalid edge link");
|
||||
|
||||
double radius1 = info.radius1;
|
||||
double radius2 = info.radius2;
|
||||
mkFillet.Add(radius1, radius2, TopoDS::Edge(edge));
|
||||
}
|
||||
|
||||
if (!fullErrMsg.empty()) {
|
||||
return new App::DocumentObjectExecReturn(fullErrMsg);
|
||||
}
|
||||
Edges.setValues(edges);
|
||||
|
||||
TopoDS_Shape shape = mkFillet.Shape();
|
||||
if (shape.IsNull())
|
||||
return new App::DocumentObjectExecReturn("Resulting shape is null");
|
||||
|
||||
TopoShape res(0);
|
||||
this->Shape.setValue(res.makeElementShape(mkFillet,baseTopoShape,Part::OpCodes::Fillet));
|
||||
this->Shape.setValue(res.makeElementShape(mkFillet, baseTopoShape, Part::OpCodes::Fillet));
|
||||
return Part::FilletBase::execute();
|
||||
}
|
||||
catch (Standard_Failure& e) {
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
# include <TopoDS.hxx>
|
||||
#endif
|
||||
|
||||
#include <App/Document.h>
|
||||
#include <Base/Tools.h>
|
||||
#include "FeatureRevolution.h"
|
||||
#include "FaceMaker.h"
|
||||
@@ -161,7 +162,7 @@ App::DocumentObjectExecReturn *Revolution::execute()
|
||||
TopLoc_Location loc(mov);
|
||||
sourceShape.setShape(sourceShape.getShape().Moved(loc));
|
||||
}
|
||||
TopoShape revolve(0);
|
||||
TopoShape revolve(0, getDocument()->getStringHasher());
|
||||
revolve.makeElementRevolve(sourceShape,
|
||||
revAx,
|
||||
angle,
|
||||
|
||||
@@ -150,7 +150,7 @@ App::DocumentObjectExecReturn* RuledSurface::execute()
|
||||
return new App::DocumentObjectExecReturn("Invalid link.");
|
||||
}
|
||||
}
|
||||
TopoShape res(0);
|
||||
TopoShape res(0, getDocument()->getStringHasher());
|
||||
res.makeElementRuledSurface(shapes, Orientation.getValue());
|
||||
this->Shape.setValue(res);
|
||||
return Part::Feature::execute();
|
||||
@@ -229,7 +229,7 @@ App::DocumentObjectExecReturn* Loft::execute()
|
||||
IsRuled isRuled = Ruled.getValue() ? IsRuled::ruled : IsRuled::notRuled;
|
||||
IsClosed isClosed = Closed.getValue() ? IsClosed::closed : IsClosed::notClosed;
|
||||
int degMax = MaxDegree.getValue();
|
||||
TopoShape result(0);
|
||||
TopoShape result(0, getDocument()->getStringHasher());
|
||||
result.makeElementLoft(shapes, isSolid, isRuled, isClosed, degMax);
|
||||
if (Linearize.getValue()) {
|
||||
result.linearize( LinearizeFace::linearizeFaces, LinearizeEdge::noEdges);
|
||||
@@ -312,7 +312,7 @@ App::DocumentObjectExecReturn* Sweep::execute()
|
||||
}
|
||||
spineShapes.push_back(shape);
|
||||
}
|
||||
spine = TopoShape().makeElementCompound(spineShapes, 0, TopoShape::SingleShapeCompoundCreationPolicy::returnShape);
|
||||
spine = TopoShape(0).makeElementCompound(spineShapes, 0, TopoShape::SingleShapeCompoundCreationPolicy::returnShape);
|
||||
}
|
||||
std::vector<TopoShape> shapes;
|
||||
shapes.push_back(spine);
|
||||
@@ -326,7 +326,7 @@ App::DocumentObjectExecReturn* Sweep::execute()
|
||||
Standard_Boolean isFrenet = Frenet.getValue() ? Standard_True : Standard_False;
|
||||
auto transMode = static_cast<TransitionMode>(Transition.getValue());
|
||||
try {
|
||||
TopoShape result(0);
|
||||
TopoShape result(0, getDocument()->getStringHasher());
|
||||
result.makeElementPipeShell(shapes, isSolid, isFrenet, transMode, Part::OpCodes::Sweep);
|
||||
if (Linearize.getValue()) {
|
||||
result.linearize(LinearizeFace::linearizeFaces, LinearizeEdge::noEdges);
|
||||
@@ -427,7 +427,7 @@ App::DocumentObjectExecReturn* Thickness::execute()
|
||||
short mode = (short)Mode.getValue();
|
||||
short join = (short)Join.getValue();
|
||||
|
||||
this->Shape.setValue(TopoShape(0,getDocument()->getStringHasher())
|
||||
this->Shape.setValue(TopoShape(0, getDocument()->getStringHasher())
|
||||
.makeElementThickSolid(base,
|
||||
shapes,
|
||||
thickness,
|
||||
|
||||
@@ -296,7 +296,7 @@ def myCustomFusionRoutine(list_of_shapes):
|
||||
new_children.append(new_piece)
|
||||
existing_pieces[hash] = (-1, new_piece)
|
||||
if changed:
|
||||
return Part.Compound(new_children)
|
||||
return Part.makeCompound(new_children)
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -431,4 +431,4 @@ class GeneralFuseReturnBuilder(FrozenClass):
|
||||
self.pieces[piece_index] = new_shape
|
||||
|
||||
def getGFReturn(self):
|
||||
return (Part.Compound(self.pieces), [[self.pieces[iPiece] for iPiece in ilist] for ilist in self._pieces_from_source])
|
||||
return (Part.makeCompound(self.pieces), [[self.pieces[iPiece] for iPiece in ilist] for ilist in self._pieces_from_source])
|
||||
|
||||
@@ -91,7 +91,7 @@ def connect(list_of_shapes, tolerance = 0.0):
|
||||
if largest is not None:
|
||||
keepers.append(largest)
|
||||
|
||||
touch_test_list = Part.Compound(keepers)
|
||||
touch_test_list = Part.makeCompound(keepers)
|
||||
#add all intersection pieces that touch danglers, triple intersection pieces that touch duals, and so on
|
||||
for ii in range(2, ao.largestOverlapCount()+1):
|
||||
list_ii_pieces = [piece for piece in ao.pieces if len(ao.sourcesOfPiece(piece)) == ii]
|
||||
@@ -102,7 +102,7 @@ def connect(list_of_shapes, tolerance = 0.0):
|
||||
if len(keepers_2_add) == 0:
|
||||
break
|
||||
keepers.extend(keepers_2_add)
|
||||
touch_test_list = Part.Compound(keepers_2_add)
|
||||
touch_test_list = Part.makeCompound(keepers_2_add)
|
||||
|
||||
|
||||
#merge, and we are done!
|
||||
@@ -160,7 +160,7 @@ def cutout_legacy(shape_base, shape_tool, tolerance = 0.0):
|
||||
result = []
|
||||
for sh in shapes_base:
|
||||
result.append(cutout(sh, shape_tool))
|
||||
return Part.Compound(result)
|
||||
return Part.makeCompound(result)
|
||||
|
||||
shape_base = shapes_base[0]
|
||||
pieces = compoundLeaves(shape_base.cut(shape_tool))
|
||||
|
||||
@@ -152,7 +152,7 @@ def mergeShells(list_of_faces_shells, flag_single = False, split_connections = [
|
||||
return Part.makeShell(faces)
|
||||
else:
|
||||
groups = splitIntoGroupsBySharing(faces, lambda sh: sh.Edges, split_connections)
|
||||
return Part.makeCompound([Part.Shell(group) for group in groups])
|
||||
return Part.makeCompound([Part.makeShell(group) for group in groups])
|
||||
|
||||
def mergeWires(list_of_edges_wires, flag_single = False, split_connections = []):
|
||||
edges = []
|
||||
|
||||
@@ -55,7 +55,7 @@ def booleanFragments(list_of_shapes, mode, tolerance = 0.0):
|
||||
elif mode == "Split":
|
||||
gr = GeneralFuseResult(list_of_shapes, (pieces,map))
|
||||
gr.splitAggregates()
|
||||
return Part.Compound(gr.pieces)
|
||||
return Part.makeCompound(gr.pieces)
|
||||
else:
|
||||
raise ValueError("Unknown mode: {mode}".format(mode= mode))
|
||||
|
||||
@@ -69,15 +69,16 @@ def slice(base_shape, tool_shapes, mode, tolerance = 0.0):
|
||||
"Split" - wires and shells will be split at intersections, too.
|
||||
"CompSolid" - slice a solid and glue it back together to make a compsolid"""
|
||||
|
||||
shapes = [base_shape] + [Part.Compound([tool_shape]) for tool_shape in tool_shapes] # hack: putting tools into compounds will prevent contamination of result with pieces of tools
|
||||
shapes = [base_shape] + [Part.makeCompound([tool_shape]) for tool_shape in tool_shapes] # hack: putting tools into compounds will prevent contamination of result with pieces of tools
|
||||
if len(shapes) < 2:
|
||||
raise ValueError("No slicing objects supplied!")
|
||||
pieces, map = shapes[0].generalFuse(shapes[1:], tolerance)
|
||||
gr = GeneralFuseResult(shapes, (pieces,map))
|
||||
result = None
|
||||
if mode == "Standard":
|
||||
result = gr.piecesFromSource(shapes[0])
|
||||
elif mode == "CompSolid":
|
||||
solids = Part.Compound(gr.piecesFromSource(shapes[0])).Solids
|
||||
solids = Part.makeCompound(gr.piecesFromSource(shapes[0])).Solids
|
||||
if len(solids) < 1:
|
||||
raise ValueError("No solids in the result. Can't make compsolid.")
|
||||
elif len(solids) == 1:
|
||||
@@ -86,7 +87,10 @@ def slice(base_shape, tool_shapes, mode, tolerance = 0.0):
|
||||
elif mode == "Split":
|
||||
gr.splitAggregates(gr.piecesFromSource(shapes[0]))
|
||||
result = gr.piecesFromSource(shapes[0])
|
||||
return result[0] if len(result) == 1 else Part.Compound(result)
|
||||
if result != None:
|
||||
return result[0] if len(result) == 1 else Part.makeCompound(result)
|
||||
else:
|
||||
return Part.Shape()
|
||||
|
||||
def xor(list_of_shapes, tolerance = 0.0):
|
||||
"""xor(list_of_shapes, tolerance = 0.0): boolean XOR operation."""
|
||||
@@ -99,4 +103,4 @@ def xor(list_of_shapes, tolerance = 0.0):
|
||||
for piece in gr.pieces:
|
||||
if len(gr.sourcesOfPiece(piece)) % 2 == 1:
|
||||
pieces_to_keep.append(piece)
|
||||
return Part.Compound(pieces_to_keep)
|
||||
return Part.makeCompound(pieces_to_keep)
|
||||
|
||||
@@ -105,11 +105,11 @@ def upgradeToAggregateIfNeeded(list_of_shapes, types = None):
|
||||
if "Wire" in types:
|
||||
list_of_shapes = [(Part.Wire([shape]) if shape.ShapeType == "Edge" else shape) for shape in list_of_shapes]
|
||||
if "Shell" in types:
|
||||
list_of_shapes = [(Part.Shell([shape]) if shape.ShapeType == "Face" else shape) for shape in list_of_shapes]
|
||||
list_of_shapes = [(Part.makeShell([shape]) if shape.ShapeType == "Face" else shape) for shape in list_of_shapes]
|
||||
if "CompSolid" in types:
|
||||
list_of_shapes = [(Part.CompSolid([shape]) if shape.ShapeType == "Solid" else shape) for shape in list_of_shapes]
|
||||
if "Compound" in types:
|
||||
list_of_shapes = [(Part.Compound(upgradeToAggregateIfNeeded(shape.childShapes(), types)) if shape.ShapeType == "Compound" else shape) for shape in list_of_shapes]
|
||||
list_of_shapes = [(Part.makeCompound(upgradeToAggregateIfNeeded(shape.childShapes(), types)) if shape.ShapeType == "Compound" else shape) for shape in list_of_shapes]
|
||||
return list_of_shapes
|
||||
|
||||
# adapted from http://stackoverflow.com/a/3603824/6285007
|
||||
|
||||
@@ -117,6 +117,9 @@ class _CompoundFilter:
|
||||
"filter: '{}'".format(obj.FilterType))
|
||||
try:
|
||||
rst.append(shps[i])
|
||||
len(shps[i].ElementMap) # this calls flushElementMap on the c++ side,
|
||||
# which allows for the element map to be usable
|
||||
# for later use.
|
||||
except IndexError:
|
||||
raise ValueError("Item index '{}' is out of range for this filter: '{}'".format(i, obj.FilterType))
|
||||
flags[i] = True
|
||||
|
||||
@@ -294,7 +294,7 @@ bool CrossSections::apply()
|
||||
}
|
||||
|
||||
Gui::Command::runCommand(Gui::Command::App, QStringLiteral(
|
||||
"comp=Part.Compound(wires)\n"
|
||||
"comp=Part.makeCompound(wires)\n"
|
||||
"slice=FreeCAD.getDocument(\"%1\").addObject(\"Part::Feature\",\"%2\")\n"
|
||||
"slice.Shape=comp\n"
|
||||
"slice.purgeTouched()\n"
|
||||
@@ -302,6 +302,7 @@ bool CrossSections::apply()
|
||||
.arg(QLatin1String(doc->getName()),
|
||||
QLatin1String(s.c_str())).toLatin1());
|
||||
}
|
||||
seq.next();
|
||||
} catch (Base::Exception& e) {
|
||||
e.reportException();
|
||||
QMessageBox::critical(Gui::getMainWindow(), tr("Cannot compute cross-sections"), QString::fromStdString(e.getMessage()));
|
||||
|
||||
@@ -61,7 +61,7 @@ def makeBottle(myWidth=50.0, myHeight=70.0, myThickness=30.0):
|
||||
myBody = myBody.makeThickness([faceToRemove],-myThickness/50 , 1.e-3)
|
||||
myThreading = Part.makeThread(myNeckHeight/10, myNeckRadius*0.06, myHeight/10, myNeckRadius*0.99)
|
||||
myThreading.translate(Base.Vector(0,0,myHeight))
|
||||
myCompound = Part.Compound([myBody, myThreading])
|
||||
myCompound = Part.makeCompound([myBody, myThreading])
|
||||
|
||||
return myCompound
|
||||
|
||||
@@ -88,7 +88,7 @@ def makeBoreHole():
|
||||
S1 = Part.Shape([C1,C2,L1,L2])
|
||||
|
||||
W=Part.Wire(S1.Edges)
|
||||
F=Part.Face(W)
|
||||
F=Part.makeFace(W)
|
||||
P=F.extrude(Base.Vector(0,0,5))
|
||||
|
||||
# add objects with the shape
|
||||
@@ -101,7 +101,7 @@ def makeBoreHole():
|
||||
|
||||
c=Part.Circle(Base.Vector(0,0,-1),Base.Vector(0,0,1),2.0)
|
||||
w=Part.Wire(c.toShape())
|
||||
f=Part.Face(w)
|
||||
f=Part.makeFace(w)
|
||||
p=f.extrude(Base.Vector(0,0,7))
|
||||
P=P.cut(p)
|
||||
|
||||
@@ -113,7 +113,7 @@ def makeBoreHole():
|
||||
|
||||
c=Part.Circle(Base.Vector(0,-11,2.5),Base.Vector(0,1,0),1.0)
|
||||
w=Part.Wire(c.toShape())
|
||||
f=Part.Face(w)
|
||||
f=Part.makeFace(w)
|
||||
p=f.extrude(Base.Vector(0,22,0))
|
||||
P=P.cut(p)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user