diff --git a/src/Mod/Part/App/FeatureExtrusion.cpp b/src/Mod/Part/App/FeatureExtrusion.cpp index c93533880..fbb93a68d 100644 --- a/src/Mod/Part/App/FeatureExtrusion.cpp +++ b/src/Mod/Part/App/FeatureExtrusion.cpp @@ -27,9 +27,11 @@ # include # include # include +# include # include # include # include +# include # include # include # include @@ -50,12 +52,15 @@ # include # include # include +# include +# include #endif #include "FeatureExtrusion.h" #include #include +#include "Part2DObject.h" using namespace Part; @@ -63,24 +68,283 @@ using namespace Part; PROPERTY_SOURCE(Part::Extrusion, Part::Feature) +const char* Extrusion::eDirModeStrings[]= { + "Custom", + "Edge", + "Normal", + NULL}; + Extrusion::Extrusion() { - ADD_PROPERTY(Base,(0)); - ADD_PROPERTY(Dir,(Base::Vector3d(0.0,0.0,1.0))); - ADD_PROPERTY(Solid,(false)); - ADD_PROPERTY(TaperAngle,(0.0)); + ADD_PROPERTY_TYPE(Base,(0), "Extrude", App::Prop_None, "Shape to extrude"); + ADD_PROPERTY_TYPE(Dir,(Base::Vector3d(0.0,0.0,1.0)), "Extrude", App::Prop_None, "Direction of extrusion (also magnitude, if both lengths are zero)."); + ADD_PROPERTY_TYPE(DirMode, (dmCustom), "Extrude", App::Prop_None, "Sets, how Dir is updated."); + DirMode.setEnums(eDirModeStrings); + ADD_PROPERTY_TYPE(DirLink,(nullptr), "Extrude", App::Prop_None, "Link to edge defining extrusion direction."); + ADD_PROPERTY_TYPE(LengthFwd,(0.0), "Extrude", App::Prop_None, "Length of extrusion along direction. If both LengthFwd and LengthRev are zero, magnitude of Dir is used."); + ADD_PROPERTY_TYPE(LengthRev,(0.0), "Extrude", App::Prop_None, "Length of additional extrusion, against direction."); + ADD_PROPERTY_TYPE(Solid,(false), "Extrude", App::Prop_None, "If true, extruding a wire yeilds a solid. If false, a shell."); + ADD_PROPERTY_TYPE(Reversed,(false), "Extrude", App::Prop_None, "Set to true to swap the direction of extrusion."); + ADD_PROPERTY_TYPE(Symmetric,(false), "Extrude", App::Prop_None, "If true, extrusion is done in both directions to a total of LengthFwd. LengthRev is ignored."); + ADD_PROPERTY_TYPE(TaperAngle,(0.0), "Extrude", App::Prop_None, "Sets the angle of slope (draft) to apply to the sides. The angle is for outward taper; negative value yeilds inward tapering."); + ADD_PROPERTY_TYPE(TaperAngleRev,(0.0), "Extrude", App::Prop_None, "Taper angle of reverse part of extrusion."); } short Extrusion::mustExecute() const { if (Base.isTouched() || Dir.isTouched() || + DirMode.isTouched() || + DirLink.isTouched() || + LengthFwd.isTouched() || + LengthRev.isTouched() || Solid.isTouched() || - TaperAngle.isTouched()) + Reversed.isTouched() || + Symmetric.isTouched() || + TaperAngle.isTouched() || + TaperAngleRev.isTouched()) return 1; return 0; } +bool Extrusion::fetchAxisLink(const App::PropertyLinkSub& axisLink, Base::Vector3d& basepoint, Base::Vector3d& dir) +{ + if (!axisLink.getValue()) + return false; + + if (!axisLink.getValue()->isDerivedFrom(Part::Feature::getClassTypeId())) + throw Base::TypeError("AxisLink has no OCC shape"); + + Part::Feature* linked = static_cast(axisLink.getValue()); + + TopoDS_Shape axEdge; + if (axisLink.getSubValues().size() > 0 && axisLink.getSubValues()[0].length() > 0){ + axEdge = linked->Shape.getShape().getSubShape(axisLink.getSubValues()[0].c_str()); + } else { + axEdge = linked->Shape.getValue(); + } + + if (axEdge.IsNull()) + throw Base::ValueError("DirLink shape is null"); + if (axEdge.ShapeType() != TopAbs_EDGE) + throw Base::TypeError("DirLink shape is not an edge"); + + BRepAdaptor_Curve crv(TopoDS::Edge(axEdge)); + gp_Pnt startpoint; + gp_Pnt endpoint; + if (crv.GetType() == GeomAbs_Line){ + startpoint = crv.Value(crv.FirstParameter()); + endpoint = crv.Value(crv.LastParameter()); + if (axEdge.Orientation() == TopAbs_REVERSED) + std::swap(startpoint, endpoint); + } else { + throw Base::TypeError("DirLink edge is not a line."); + } + basepoint.Set(startpoint.X(), startpoint.Y(), startpoint.Z()); + gp_Vec vec = gp_Vec(startpoint, endpoint); + dir.Set(vec.X(), vec.Y(), vec.Z()); + return true; +} + +Extrusion::ExtrusionParameters Extrusion::computeFinalParameters() +{ + Extrusion::ExtrusionParameters result; + Base::Vector3d dir; + switch(this->DirMode.getValue()){ + case dmCustom: + dir = this->Dir.getValue(); + break; + case dmEdge:{ + bool fetched; + Base::Vector3d base; + fetched = fetchAxisLink(this->DirLink, base, dir); + if (! fetched) + throw Base::Exception("DirMode is set to use edge, but no edge is linked."); + this->Dir.setValue(dir); + }break; + case dmNormal: + dir = calculateShapeNormal(this->Base); + this->Dir.setValue(dir); + break; + default: + throw Base::ValueError("Unexpected enum value"); + } + if(dir.Length() < Precision::Confusion()) + throw Base::ValueError("Direction is zero-length"); + result.dir = gp_Dir(dir.x, dir.y, dir.z); + if (this->Reversed.getValue()) + result.dir.Reverse(); + + result.lengthFwd = this->LengthFwd.getValue(); + result.lengthRev = this->LengthRev.getValue(); + if(fabs(result.lengthFwd) < Precision::Confusion() + && fabs(result.lengthRev) < Precision::Confusion() ){ + result.lengthFwd = dir.Length(); + } + + if (this->Symmetric.getValue()){ + result.lengthRev = result.lengthFwd * 0.5; + result.lengthFwd = result.lengthFwd * 0.5; + } + + if (fabs(result.lengthFwd + result.lengthRev) < Precision::Confusion()) + throw Base::ValueError("Total length of extrusion is zero."); + + result.solid = this->Solid.getValue(); + + result.taperAngleFwd = this->TaperAngle.getValue() * M_PI / 180.0; + if (fabs(result.taperAngleFwd) > M_PI * 0.5 - Precision::Angular() ) + throw Base::ValueError("Magnitude of taper angle matches or exceeds 90 degrees. That is too much."); + result.taperAngleRev = this->TaperAngleRev.getValue() * M_PI / 180.0; + if (fabs(result.taperAngleRev) > M_PI * 0.5 - Precision::Angular() ) + throw Base::ValueError("Magnitude of taper angle matches or exceeds 90 degrees. That is too much."); + + return result; +} + +Base::Vector3d Extrusion::calculateShapeNormal(const App::PropertyLink& shapeLink) +{ + if (!shapeLink.getValue()) + throw Base::Exception("calculateShapeNormal: link is empty"); + const App::DocumentObject* docobj = shapeLink.getValue(); + + //special case for sketches and the like: no matter what shape they have, use their local Z axis. + if (docobj->isDerivedFrom(Part::Part2DObject::getClassTypeId())){ + const Part::Part2DObject* p2do = static_cast(docobj); + Base::Vector3d OZ (0.0, 0.0, 1.0); + Base::Vector3d result; + p2do->Placement.getValue().getRotation().multVec(OZ, result); + return result; + } + + //extract the shape + if (! docobj->isDerivedFrom(Part::Feature::getClassTypeId())) + throw Base::TypeError("Linked object doesn't have shape."); + + const TopoShape &tsh = static_cast(docobj)->Shape.getShape(); + TopoDS_Shape sh = tsh.getShape(); + if (sh.IsNull()) + throw Base::Exception("calculateShapeNormal: link points to a valid object, but its shape is null."); + + //find plane + BRepLib_FindSurface planeFinder(sh, -1, /*OnlyPlane=*/true); + if (! planeFinder.Found()) + throw Base::ValueError("Can't find normal direction, because the shape is not on a plane."); + + //find plane normal and return result. + GeomAdaptor_Surface surf(planeFinder.Surface()); + gp_Dir normal = surf.Plane().Axis().Direction(); + + //now se know the plane. But if there are faces, the + //plane normal direction is not dependent on face orientation (because findPlane only uses egdes). + //let's fix that. + TopExp_Explorer ex(sh, TopAbs_FACE); + if(ex.More()) { + BRepAdaptor_Surface surf(TopoDS::Face(ex.Current())); + normal = surf.Plane().Axis().Direction(); + if (ex.Current().Orientation() == TopAbs_REVERSED){ + normal.Reverse(); + } + } + + return Base::Vector3d(normal.X(), normal.Y(), normal.Z()); +} + +TopoShape Extrusion::extrudeShape(const TopoShape source, Extrusion::ExtrusionParameters params) +{ + TopoDS_Shape result; + gp_Vec vec = gp_Vec(params.dir).Multiplied(params.lengthFwd+params.lengthRev);//total vector of extrusion + + if (std::fabs(params.taperAngleFwd) >= Precision::Angular() || + std::fabs(params.taperAngleRev) >= Precision::Angular() ) { + //Tapered extrusion! +#if defined(__GNUC__) && defined (FC_OS_LINUX) + Base::SignalException se; +#endif + TopoDS_Shape myShape = source.getShape(); + if (myShape.IsNull()) + Standard_Failure::Raise("Cannot extrude empty shape"); + // #0000910: Circles Extrude Only Surfaces, thus use BRepBuilderAPI_Copy + myShape = BRepBuilderAPI_Copy(myShape).Shape(); + + std::list drafts; + makeDraft(params, myShape, drafts); + if (drafts.empty()) { + Standard_Failure::Raise("Drafting shape failed"); + } + else if (drafts.size() == 1) { + result = drafts.front(); + } + else { + TopoDS_Compound comp; + BRep_Builder builder; + builder.MakeCompound(comp); + for (std::list::iterator it = drafts.begin(); it != drafts.end(); ++it) + builder.Add(comp, *it); + result = comp; + } + } + else { + //Regular (non-tapered) extrusion! + TopoDS_Shape myShape = source.getShape(); + if (myShape.IsNull()) + Standard_Failure::Raise("Cannot extrude empty shape"); + + // #0000910: Circles Extrude Only Surfaces, thus use BRepBuilderAPI_Copy + myShape = BRepBuilderAPI_Copy(myShape).Shape(); + + //apply reverse part of extrusion by shifting the source shape + if (fabs(params.lengthRev)>Precision::Confusion() ){ + gp_Trsf mov; + mov.SetTranslation(gp_Vec(params.dir)*(-params.lengthRev)); + TopLoc_Location loc(mov); + myShape.Move(loc); + } + + //make faces from wires + if (params.solid && myShape.ShapeType() != TopAbs_FACE) { + std::vector wires; + TopTools_IndexedMapOfShape mapOfWires; + TopExp::MapShapes(myShape, TopAbs_WIRE, mapOfWires); + + // if there are no wires then check also for edges + if (mapOfWires.IsEmpty()) { + TopTools_IndexedMapOfShape mapOfEdges; + TopExp::MapShapes(myShape, TopAbs_EDGE, mapOfEdges); + for (int i=1; i<=mapOfEdges.Extent(); i++) { + BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(mapOfEdges.FindKey(i))); + wires.push_back(mkWire.Wire()); + } + } + else { + wires.reserve(mapOfWires.Extent()); + for (int i=1; i<=mapOfWires.Extent(); i++) { + wires.push_back(TopoDS::Wire(mapOfWires.FindKey(i))); + } + } + + if (!wires.empty()) { + try { + TopoDS_Shape res = makeFace(wires); + if (!res.IsNull()) + myShape = res; + } + catch (...) { + } + } + } + + //extrude! + BRepPrimAPI_MakePrism mkPrism(myShape, vec); + result = mkPrism.Shape(); + } + + if (result.IsNull()) + throw Base::Exception("Result of extrusion is null shape."); + return TopoShape(result); + +} + App::DocumentObjectExecReturn *Extrusion::execute(void) { App::DocumentObject* link = Base.getValue(); @@ -90,84 +354,10 @@ App::DocumentObjectExecReturn *Extrusion::execute(void) return new App::DocumentObjectExecReturn("Linked object is not a Part object"); Part::Feature *base = static_cast(Base.getValue()); - Base::Vector3d v = Dir.getValue(); - gp_Vec vec(v.x,v.y,v.z); - double taperAngle = TaperAngle.getValue(); - bool makeSolid = Solid.getValue(); - try { - if (std::fabs(taperAngle) >= Precision::Confusion()) { -#if defined(__GNUC__) && defined (FC_OS_LINUX) - Base::SignalException se; -#endif - double distance = std::tan(Base::toRadians(taperAngle)) * vec.Magnitude(); - TopoDS_Shape myShape = base->Shape.getValue(); - if (myShape.IsNull()) - Standard_Failure::Raise("Cannot extrude empty shape"); - // #0000910: Circles Extrude Only Surfaces, thus use BRepBuilderAPI_Copy - myShape = BRepBuilderAPI_Copy(myShape).Shape(); - - std::list drafts; - makeDraft(distance, vec, makeSolid, myShape, drafts); - if (drafts.empty()) { - Standard_Failure::Raise("Drafting shape failed"); - } - else if (drafts.size() == 1) { - this->Shape.setValue(drafts.front()); - } - else { - TopoDS_Compound comp; - BRep_Builder builder; - builder.MakeCompound(comp); - for (std::list::iterator it = drafts.begin(); it != drafts.end(); ++it) - builder.Add(comp, *it); - this->Shape.setValue(comp); - } - } - else { - // Now, let's get the TopoDS_Shape - TopoDS_Shape myShape = base->Shape.getValue(); - if (myShape.IsNull()) - Standard_Failure::Raise("Cannot extrude empty shape"); - // #0000910: Circles Extrude Only Surfaces, thus use BRepBuilderAPI_Copy - myShape = BRepBuilderAPI_Copy(myShape).Shape(); - if (makeSolid && myShape.ShapeType() != TopAbs_FACE) { - std::vector wires; - TopTools_IndexedMapOfShape mapOfWires; - TopExp::MapShapes(myShape, TopAbs_WIRE, mapOfWires); - - // if there are no wires then check also for edges - if (mapOfWires.IsEmpty()) { - TopTools_IndexedMapOfShape mapOfEdges; - TopExp::MapShapes(myShape, TopAbs_EDGE, mapOfEdges); - for (int i=1; i<=mapOfEdges.Extent(); i++) { - BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(mapOfEdges.FindKey(i))); - wires.push_back(mkWire.Wire()); - } - } - else { - wires.reserve(mapOfWires.Extent()); - for (int i=1; i<=mapOfWires.Extent(); i++) { - wires.push_back(TopoDS::Wire(mapOfWires.FindKey(i))); - } - } - - if (!wires.empty()) { - try { - TopoDS_Shape res = makeFace(wires); - if (!res.IsNull()) - myShape = res; - } - catch (...) { - } - } - } - BRepPrimAPI_MakePrism mkPrism(myShape, vec); - TopoDS_Shape swept = mkPrism.Shape(); - if (swept.IsNull()) - return new App::DocumentObjectExecReturn("Resulting shape is null"); - this->Shape.setValue(swept); - } + Extrusion::ExtrusionParameters params = computeFinalParameters(); + TopoShape result = extrudeShape(base->Shape.getShape(),params); + this->Shape.setValue(result); return App::DocumentObject::StdReturn; } catch (Standard_Failure) { @@ -176,9 +366,19 @@ App::DocumentObjectExecReturn *Extrusion::execute(void) } } -void Extrusion::makeDraft(double distance, const gp_Vec& vec, bool makeSolid, const TopoDS_Shape& shape, std::list& drafts) const +void Extrusion::makeDraft(ExtrusionParameters params, const TopoDS_Shape& shape, std::list& drafts) { - std::list wire_list; + double distanceFwd = tan(params.taperAngleFwd)*params.lengthFwd; + double distanceRev = tan(params.taperAngleRev)*params.lengthRev; + + gp_Vec vecFwd = gp_Vec(params.dir)*params.lengthFwd; + gp_Vec vecRev = gp_Vec(params.dir.Reversed())*params.lengthRev; + + bool bFwd = fabs(params.lengthFwd) > Precision::Confusion(); + bool bRev = fabs(params.lengthRev) > Precision::Confusion(); + bool bMid = !bFwd || !bRev || params.lengthFwd*params.lengthRev > 0.0; //include the source shape as loft section? + + TopoDS_Wire sourceWire; if (shape.IsNull()) Standard_Failure::Raise("Not a valid shape"); if (shape.ShapeType() == TopAbs_WIRE) { @@ -187,49 +387,119 @@ void Extrusion::makeDraft(double distance, const gp_Vec& vec, bool makeSolid, co aFix.FixReorder(); aFix.FixConnected(); aFix.FixClosed(); - wire_list.push_back(aFix.Wire()); + sourceWire = aFix.Wire(); } else if (shape.ShapeType() == TopAbs_FACE) { TopoDS_Wire outerWire = ShapeAnalysis::OuterWire(TopoDS::Face(shape)); - wire_list.push_back(outerWire); + sourceWire = outerWire; } else if (shape.ShapeType() == TopAbs_COMPOUND) { TopoDS_Iterator it(shape); for (; it.More(); it.Next()) { - makeDraft(distance, vec, makeSolid, it.Value(), drafts); + makeDraft(params, it.Value(), drafts); } } else { Standard_Failure::Raise("Only a wire or a face is supported"); } - if (!wire_list.empty()) { - BRepOffsetAPI_MakeOffset mkOffset; + if (!sourceWire.IsNull()) { + std::list list_of_sections; + + //first. add wire for reversed part of extrusion + if (bRev){ + gp_Vec translation = vecRev; + double offset = distanceRev; + + BRepOffsetAPI_MakeOffset mkOffset; #if OCC_VERSION_HEX >= 0x060800 - mkOffset.Init(GeomAbs_Arc); + mkOffset.Init(GeomAbs_Arc); #endif - mkOffset.AddWire(wire_list.front()); - mkOffset.Perform(distance); +#if OCC_VERSION_HEX >= 0x070000 + mkOffset.Init(GeomAbs_Intersection); +#endif + gp_Trsf mat; + mat.SetTranslation(translation); + TopLoc_Location loc(mat); + TopoDS_Wire movedSourceWire = TopoDS::Wire(sourceWire.Moved(loc)); - gp_Trsf mat; - mat.SetTranslation(vec); - BRepBuilderAPI_Transform mkTransform(mkOffset.Shape(),mat); - if (mkTransform.Shape().IsNull()) - Standard_Failure::Raise("Tapered shape is empty"); - TopAbs_ShapeEnum type = mkTransform.Shape().ShapeType(); - if (type == TopAbs_WIRE) { - wire_list.push_back(TopoDS::Wire(mkTransform.Shape())); - } - else if (type == TopAbs_EDGE) { - BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(mkTransform.Shape())); - wire_list.push_back(mkWire.Wire()); - } - else { - Standard_Failure::Raise("Tapered shape type is not supported"); + TopoDS_Shape offsetShape; + if (fabs(offset)>Precision::Confusion()){ + mkOffset.AddWire(movedSourceWire); + mkOffset.Perform(offset); + + offsetShape = mkOffset.Shape(); + } else { + //stupid OCC doesn't understand, what to do when offset value is zero =/ + offsetShape = movedSourceWire; + } + + if (offsetShape.IsNull()) + Standard_Failure::Raise("Tapered shape is empty"); + TopAbs_ShapeEnum type = offsetShape.ShapeType(); + if (type == TopAbs_WIRE) { + list_of_sections.push_back(TopoDS::Wire(offsetShape)); + } + else if (type == TopAbs_EDGE) { + BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(offsetShape)); + list_of_sections.push_back(mkWire.Wire()); + } + else { + Standard_Failure::Raise("Tapered shape type is not supported"); + } } - BRepOffsetAPI_ThruSections mkGenerator(makeSolid ? Standard_True : Standard_False, Standard_False); - for (std::list::const_iterator it = wire_list.begin(); it != wire_list.end(); ++it) { + //next. Add source wire as middle section. Order is important. + if (bMid){ + list_of_sections.push_back(sourceWire); + } + + //finally. Forward extrusion offset wire. + if (bFwd){ + gp_Vec translation = vecFwd; + double offset = distanceFwd; + + BRepOffsetAPI_MakeOffset mkOffset; +#if OCC_VERSION_HEX >= 0x060800 + mkOffset.Init(GeomAbs_Arc); +#endif +#if OCC_VERSION_HEX >= 0x070000 + mkOffset.Init(GeomAbs_Intersection); +#endif + gp_Trsf mat; + mat.SetTranslation(translation); + TopLoc_Location loc(mat); + TopoDS_Wire movedSourceWire = TopoDS::Wire(sourceWire.Moved(loc)); + + TopoDS_Shape offsetShape; + if (fabs(offset)>Precision::Confusion()){ + mkOffset.AddWire(movedSourceWire); + mkOffset.Perform(offset); + + offsetShape = mkOffset.Shape(); + } else { + //stupid OCC doesn't understand, what to do when offset value is zero =/ + offsetShape = movedSourceWire; + } + + if (offsetShape.IsNull()) + Standard_Failure::Raise("Tapered shape is empty"); + TopAbs_ShapeEnum type = offsetShape.ShapeType(); + if (type == TopAbs_WIRE) { + list_of_sections.push_back(TopoDS::Wire(offsetShape)); + } + else if (type == TopAbs_EDGE) { + BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(offsetShape)); + list_of_sections.push_back(mkWire.Wire()); + } + else { + Standard_Failure::Raise("Tapered shape type is not supported"); + } + } + + //make loft + BRepOffsetAPI_ThruSections mkGenerator(params.solid ? Standard_True : Standard_False, /*ruled=*/Standard_True); + for (std::list::const_iterator it = list_of_sections.begin(); it != list_of_sections.end(); ++it) { const TopoDS_Wire &wire = *it; mkGenerator.AddWire(wire); } @@ -241,12 +511,16 @@ void Extrusion::makeDraft(double distance, const gp_Vec& vec, bool makeSolid, co mkGenerator.Build(); drafts.push_back(mkGenerator.Shape()); } + catch (Standard_Failure &){ + throw; + } catch (...) { + throw Base::Exception("Unknown exception from BRepOffsetAPI_ThruSections"); } } } -TopoDS_Face Extrusion::validateFace(const TopoDS_Face& face) const +TopoDS_Face Extrusion::validateFace(const TopoDS_Face& face) { BRepCheck_Analyzer aChecker(face); if (!aChecker.IsValid()) { @@ -311,7 +585,7 @@ public: } }; -bool Extrusion::isInside(const TopoDS_Wire& wire1, const TopoDS_Wire& wire2) const +bool Extrusion::isInside(const TopoDS_Wire& wire1, const TopoDS_Wire& wire2) { Bnd_Box box1; BRepBndLib::Add(wire1, box1); @@ -352,7 +626,7 @@ bool Extrusion::isInside(const TopoDS_Wire& wire1, const TopoDS_Wire& wire2) con return false; } -TopoDS_Shape Extrusion::makeFace(std::list& wires) const +TopoDS_Shape Extrusion::makeFace(std::list& wires) { BRepBuilderAPI_MakeFace mkFace(wires.front()); const TopoDS_Face& face = mkFace.Face(); @@ -384,7 +658,7 @@ TopoDS_Shape Extrusion::makeFace(std::list& wires) const return validateFace(mkFace.Face()); } -TopoDS_Shape Extrusion::makeFace(const std::vector& w) const +TopoDS_Shape Extrusion::makeFace(const std::vector& w) { if (w.empty()) return TopoDS_Shape(); diff --git a/src/Mod/Part/App/FeatureExtrusion.h b/src/Mod/Part/App/FeatureExtrusion.h index 5d6cc7a86..7cd4b46d4 100644 --- a/src/Mod/Part/App/FeatureExtrusion.h +++ b/src/Mod/Part/App/FeatureExtrusion.h @@ -32,7 +32,7 @@ namespace Part { -class Extrusion : public Part::Feature +class PartExport Extrusion : public Part::Feature { PROPERTY_HEADER(Part::Extrusion); @@ -41,8 +41,31 @@ public: App::PropertyLink Base; App::PropertyVector Dir; + App::PropertyEnumeration DirMode; + App::PropertyLinkSub DirLink; + App::PropertyDistance LengthFwd; + App::PropertyDistance LengthRev; App::PropertyBool Solid; + App::PropertyBool Reversed; + App::PropertyBool Symmetric; App::PropertyAngle TaperAngle; + App::PropertyAngle TaperAngleRev; + + + /** + * @brief The ExtrusionParameters struct is supposed to be filled with final + * extrusion parameters, after resolving links, applying mode logic, + * reversing, etc., and be passed to extrudeShape. + */ + struct ExtrusionParameters { + gp_Dir dir; + double lengthFwd; + double lengthRev; + bool solid; + double taperAngleFwd; //in radians + double taperAngleRev; + ExtrusionParameters(): lengthFwd(0), lengthRev(0), solid(false), taperAngleFwd(0), taperAngleRev(0) {}// constructor to keep garbage out + }; /** @name methods override feature */ //@{ @@ -55,12 +78,51 @@ public: } //@} + /** + * @brief extrudeShape powers the extrusion feature. + * @param source: the shape to be extruded + * @param params: extrusion parameters + * @return result of extrusion + */ + static TopoShape extrudeShape(TopoShape source, ExtrusionParameters params); + + /** + * @brief fetchAxisLink: read AxisLink to obtain the direction and + * length. Note: this routine is re-used in Extrude dialog, hence it + * is static. + * @param axisLink (input): the link + * @param basepoint (output): starting point of edge. Not used by extrude as of now. + * @param dir (output): direction of axis, with magnitude (length) + * @return true if link was fetched. false if link was empty. Throws if the + * link is wrong. + */ + static bool fetchAxisLink(const App::PropertyLinkSub& axisLink, + Base::Vector3d &basepoint, + Base::Vector3d &dir); + + /** + * @brief computeFinalParameters: applies mode logic and fetches links, to + * compute the actual parameters of extrusion. Dir property is updated in + * the process, hence the function is non-const. + */ + ExtrusionParameters computeFinalParameters(); + + static Base::Vector3d calculateShapeNormal(const App::PropertyLink &shapeLink); + +public: //mode enumerations + enum eDirMode{ + dmCustom, + dmEdge, + dmNormal + }; + static const char* eDirModeStrings[]; + private: - bool isInside(const TopoDS_Wire&, const TopoDS_Wire&) const; - TopoDS_Face validateFace(const TopoDS_Face&) const; - TopoDS_Shape makeFace(const std::vector&) const; - TopoDS_Shape makeFace(std::list&) const; // for internal use only - void makeDraft(double, const gp_Vec&, bool, const TopoDS_Shape&, std::list&) const; + static bool isInside(const TopoDS_Wire&, const TopoDS_Wire&); + static TopoDS_Face validateFace(const TopoDS_Face&); + static TopoDS_Shape makeFace(const std::vector&); + static TopoDS_Shape makeFace(std::list&); // for internal use only + static void makeDraft(ExtrusionParameters params, const TopoDS_Shape&, std::list&); private: class Wire_Compare;