From dd9023e289caae9be85095893e231025e972129e Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Wed, 26 May 2021 09:00:10 +0800 Subject: [PATCH] Part: add API TopoShape::linearize() Used by Part::Extrude and PartDesign::Pad/Pocket for convert planar BSplineSurface face to plane when drafting. Related realthunder/FreeCAD_Assembly3#627 --- src/Mod/Part/App/FeatureExtrusion.cpp | 21 +++++++++++ src/Mod/Part/App/FeatureExtrusion.h | 16 +++------ src/Mod/Part/App/Geometry.cpp | 32 ++++++++++++++--- src/Mod/Part/App/PartParams.h | 1 + src/Mod/Part/App/TopoShape.h | 6 ++++ src/Mod/Part/App/TopoShapeEx.cpp | 45 ++++++++++++++++++++++++ src/Mod/PartDesign/App/FeaturePad.cpp | 4 +++ src/Mod/PartDesign/App/FeaturePad.h | 1 + src/Mod/PartDesign/App/FeaturePocket.cpp | 7 ++++ src/Mod/PartDesign/App/FeaturePocket.h | 1 + 10 files changed, 118 insertions(+), 16 deletions(-) diff --git a/src/Mod/Part/App/FeatureExtrusion.cpp b/src/Mod/Part/App/FeatureExtrusion.cpp index 7e30b6468a69..e31aba1dbca9 100644 --- a/src/Mod/Part/App/FeatureExtrusion.cpp +++ b/src/Mod/Part/App/FeatureExtrusion.cpp @@ -88,6 +88,8 @@ Extrusion::Extrusion() ADD_PROPERTY_TYPE(InnerTaperAngle,(0.0), "Extrude", App::Prop_None, "Taper angle of inner holes."); ADD_PROPERTY_TYPE(InnerTaperAngleRev,(0.0), "Extrude", App::Prop_None, "Taper angle of the reverse part for inner holes."); ADD_PROPERTY_TYPE(UsePipeForDraft,(false), "Extrude", App::Prop_None, "Use pipe (i.e. sweep) operation to create draft angles."); + ADD_PROPERTY_TYPE(Linearize,(false), "Extrude", App::Prop_None, + "Linearize the resut shape by simplify linear edge and planar face into line and plane"); ADD_PROPERTY_TYPE(FaceMakerClass,("Part::FaceMakerExtrusion"), "Extrude", App::Prop_None, "If Solid is true, this sets the facemaker class to use when converting wires to faces. Otherwise, ignored."); //default for old documents. See setupObject for default for new extrusions. } @@ -147,6 +149,7 @@ Extrusion::ExtrusionParameters Extrusion::computeFinalParameters() { Extrusion::ExtrusionParameters result; result.usepipe = this->UsePipeForDraft.getValue(); + result.linearize = this->Linearize.getValue(); Base::Vector3d dir; switch(this->DirMode.getValue()){ case dmCustom: @@ -533,6 +536,8 @@ void Extrusion::makeDraft(const ExtrusionParameters& params, const TopoShape& _s #endif if (params.usepipe) { drafts.push_back(makeDraftUsingPipe(list_of_sections, hasher)); + if (params.linearize) + drafts.back().linearize(true, false); return; } @@ -544,6 +549,8 @@ void Extrusion::makeDraft(const ExtrusionParameters& params, const TopoShape& _s mkGenerator.Build(); drafts.push_back(TopoShape(0,hasher).makEShape(mkGenerator,list_of_sections)); + if (params.linearize) + drafts.back().linearize(true, false); } catch (Standard_Failure &){ throw; @@ -629,5 +636,19 @@ void Part::Extrusion::setupObject() { Part::Feature::setupObject(); UsePipeForDraft.setValue(PartParams::UsePipeForExtrusionDraft()); + Linearize.setValue(Part::PartParams::LinearizeExtrusionDraft()); this->FaceMakerClass.setValue("Part::FaceMakerBullseye"); //default for newly created features } + +Part::Extrusion::ExtrusionParameters::ExtrusionParameters() + : lengthFwd(0) + , lengthRev(0) + , solid(false) + , innertaper(false) + , usepipe(false) + , linearize(PartParams::LinearizeExtrusionDraft()) + , taperAngleFwd(0) + , taperAngleRev(0) + , innerTaperAngleFwd(0) + , innerTaperAngleRev(0) +{}// constructor to keep garbage out diff --git a/src/Mod/Part/App/FeatureExtrusion.h b/src/Mod/Part/App/FeatureExtrusion.h index dfc67bbc2bb4..f7438102e0ce 100644 --- a/src/Mod/Part/App/FeatureExtrusion.h +++ b/src/Mod/Part/App/FeatureExtrusion.h @@ -56,35 +56,27 @@ class PartExport Extrusion : public Part::Feature App::PropertyAngle InnerTaperAngleRev; App::PropertyString FaceMakerClass; App::PropertyBool UsePipeForDraft; + App::PropertyBool Linearize; /** * @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 { + struct PartExport ExtrusionParameters { gp_Dir dir; double lengthFwd; double lengthRev; bool solid; bool innertaper; bool usepipe; + bool linearize; double taperAngleFwd; //in radians double taperAngleRev; double innerTaperAngleFwd; //in radians double innerTaperAngleRev; std::string faceMakerClass; - ExtrusionParameters() - : lengthFwd(0) - , lengthRev(0) - , solid(false) - , innertaper(false) - , usepipe(false) - , taperAngleFwd(0) - , taperAngleRev(0) - , innerTaperAngleFwd(0) - , innerTaperAngleRev(0) - {}// constructor to keep garbage out + ExtrusionParameters(); }; /** @name methods override feature */ diff --git a/src/Mod/Part/App/Geometry.cpp b/src/Mod/Part/App/Geometry.cpp index 8e20074a70e6..fd5fb4eff5d2 100644 --- a/src/Mod/Part/App/Geometry.cpp +++ b/src/Mod/Part/App/Geometry.cpp @@ -1809,8 +1809,14 @@ PyObject *GeomBSplineCurve::getPyObject(void) bool GeomBSplineCurve::isSame(const Geometry &_other, double tol, double atol) const { - if(_other.getTypeId() != getTypeId()) + if(_other.getTypeId() != getTypeId()) { + if (isLinear() && _other.isDerivedFrom(GeomCurve::getClassTypeId())) { + std::unique_ptr geo(toLineSegment()); + if (geo) + return geo->isSame(_other, tol, atol); + } return false; + } auto &other = static_cast(_other); (void)atol; @@ -4167,8 +4173,14 @@ PyObject *GeomLine::getPyObject(void) bool GeomLine::isSame(const Geometry &_other, double tol, double atol) const { - if(_other.getTypeId() != getTypeId()) + if(_other.getTypeId() != getTypeId()) { + if (_other.isDerivedFrom(GeomCurve::getClassTypeId())) { + std::unique_ptr geo(static_cast(_other).toLine()); + if (geo) + return isSame(*geo, tol, atol); + } return false; + } auto &other = static_cast(_other); @@ -4766,8 +4778,14 @@ PyObject *GeomBSplineSurface::getPyObject(void) bool GeomBSplineSurface::isSame(const Geometry &_other, double tol, double atol) const { - if(_other.getTypeId() != getTypeId()) + if(_other.getTypeId() != getTypeId()) { + if (_other.isDerivedFrom(GeomSurface::getClassTypeId()) && isPlanar()) { + std::unique_ptr geo(toPlane()); + if (geo) + return geo->isSame(_other, tol, atol); + } return false; + } auto &other = static_cast(_other); Standard_Integer uc = mySurface->NbUPoles(); @@ -5194,8 +5212,14 @@ PyObject *GeomPlane::getPyObject(void) bool GeomPlane::isSame(const Geometry &_other, double tol, double atol) const { - if(_other.getTypeId() != getTypeId()) + if(_other.getTypeId() != getTypeId()) { + if (_other.isDerivedFrom(GeomSurface::getClassTypeId())) { + std::unique_ptr geo(static_cast(_other).toPlane()); + if (geo) + return isSame(*geo, tol, atol); + } return false; + } auto &other = static_cast(_other); return GeomElementarySurface::isSame(other,tol,atol); diff --git a/src/Mod/Part/App/PartParams.h b/src/Mod/Part/App/PartParams.h index 1a3b1742a1b2..0d4797311210 100644 --- a/src/Mod/Part/App/PartParams.h +++ b/src/Mod/Part/App/PartParams.h @@ -68,6 +68,7 @@ class PartExport PartParams: public ParameterGrp::ObserverType FC_APP_PART_PARAM(AutoGroupSolids,bool,Bool,false) \ FC_APP_PART_PARAM(SingleSolid,bool,Bool,false) \ FC_APP_PART_PARAM(UsePipeForExtrusionDraft,bool,Bool,false) \ + FC_APP_PART_PARAM(LinearizeExtrusionDraft,bool,Bool,true) \ FC_APP_PART_PARAM(AutoCorrectLink,bool,Bool,false) \ #undef FC_APP_PART_PARAM diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 2ccd5f76f856..36febee4cdf7 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -668,6 +668,12 @@ class PartExport TopoShape : public Data::ComplexGeoData return TopoShape(0,Hasher).makECut({*this,shape},op,tol); } + /** Try to simplify geometry of any linear/planar subshape to line/plane + * + * @return Return true if the shape is modified + */ + bool linearize(bool face, bool edge); + static const std::string &modPostfix(); static const std::string &genPostfix(); static const std::string &modgenPostfix(); diff --git a/src/Mod/Part/App/TopoShapeEx.cpp b/src/Mod/Part/App/TopoShapeEx.cpp index e74438580d61..4f0e294fda6c 100644 --- a/src/Mod/Part/App/TopoShapeEx.cpp +++ b/src/Mod/Part/App/TopoShapeEx.cpp @@ -4579,3 +4579,48 @@ bool TopoShape::isPlanarFace(double tol) const return GeomSurface::isPlanar( BRepAdaptor_Surface(TopoDS::Face(getShape())).Surface().Surface(), nullptr, tol); } + +bool TopoShape::linearize(bool face, bool edge) +{ + bool touched = false; + BRep_Builder builder; + // Note: changing edge geometry seems to mess up with face (or shell, or solid) + // Probably need to do some fix afterwards. + if (edge) { + for (auto & edge : getSubTopoShapes(TopAbs_EDGE)) { + TopoDS_Edge e = TopoDS::Edge(edge.getShape()); + BRepAdaptor_Curve curve(e); + if (curve.GetType() == GeomAbs_Line || !edge.isLinearEdge()) + continue; + std::unique_ptr geo( + Geometry::fromShape(e.Located(TopLoc_Location()).Oriented(TopAbs_FORWARD))); + std::unique_ptr gline(static_cast(geo.get())->toLine()); + if (gline) { + touched = true; + builder.UpdateEdge(e, + Handle(Geom_Curve)::DownCast(gline->handle()), + e.Location(), + BRep_Tool::Tolerance(e)); + } + } + } + if (face) { + for (auto & face : getSubTopoShapes(TopAbs_FACE)) { + TopoDS_Face f = TopoDS::Face(face.getShape()); + BRepAdaptor_Surface surf(f); + if (surf.GetType() == GeomAbs_Plane || !face.isPlanarFace()) + continue; + std::unique_ptr geo( + Geometry::fromShape(f.Located(TopLoc_Location()).Oriented(TopAbs_FORWARD))); + std::unique_ptr gplane(static_cast(geo.get())->toPlane()); + if (gplane) { + touched = true; + builder.UpdateFace(f, + Handle(Geom_Surface)::DownCast(gplane->handle()), + f.Location(), + BRep_Tool::Tolerance(f)); + } + } + } + return touched; +} diff --git a/src/Mod/PartDesign/App/FeaturePad.cpp b/src/Mod/PartDesign/App/FeaturePad.cpp index 831bca1d70e1..3b362bff5a07 100644 --- a/src/Mod/PartDesign/App/FeaturePad.cpp +++ b/src/Mod/PartDesign/App/FeaturePad.cpp @@ -91,6 +91,8 @@ Pad::Pad() ADD_PROPERTY_TYPE(InnerTaperAngleRev,(0.0), "Pad", App::Prop_None, "Taper angle of the reverse part for inner holes."); ADD_PROPERTY_TYPE(UsePipeForDraft,(false), "Pad", App::Prop_None, "Use pipe (i.e. sweep) operation to create draft angles."); + ADD_PROPERTY_TYPE(Linearize,(false), "Pad", App::Prop_None, + "Linearize the resut shape by simplify linear edge and planar face into line and plane"); } short Pad::mustExecute() const @@ -117,6 +119,7 @@ void Pad::setupObject() { ProfileBased::setupObject(); UsePipeForDraft.setValue(Part::PartParams::UsePipeForExtrusionDraft()); + Linearize.setValue(Part::PartParams::LinearizeExtrusionDraft()); } App::DocumentObjectExecReturn *Pad::_execute(bool makeface, bool fuse) @@ -367,6 +370,7 @@ App::DocumentObjectExecReturn *Pad::_execute(bool makeface, bool fuse) params.taperAngleRev = this->TaperAngleRev.getValue() * M_PI / 180.0; params.innerTaperAngleFwd = this->InnerTaperAngle.getValue() * M_PI / 180.0; params.innerTaperAngleRev = this->InnerTaperAngleRev.getValue() * M_PI / 180.0; + params.linearize = this->Linearize.getValue(); if (L2 == 0.0 && Midplane.getValue()) { params.lengthFwd = L/2; params.lengthRev = L/2; diff --git a/src/Mod/PartDesign/App/FeaturePad.h b/src/Mod/PartDesign/App/FeaturePad.h index 9a6582a218d9..cb1137172907 100644 --- a/src/Mod/PartDesign/App/FeaturePad.h +++ b/src/Mod/PartDesign/App/FeaturePad.h @@ -51,6 +51,7 @@ class PartDesignExport Pad : public ProfileBased App::PropertyAngle InnerTaperAngle; App::PropertyAngle InnerTaperAngleRev; App::PropertyBool UsePipeForDraft; + App::PropertyBool Linearize; /** @name methods override feature */ //@{ diff --git a/src/Mod/PartDesign/App/FeaturePocket.cpp b/src/Mod/PartDesign/App/FeaturePocket.cpp index 958cda673949..9e0a94082296 100644 --- a/src/Mod/PartDesign/App/FeaturePocket.cpp +++ b/src/Mod/PartDesign/App/FeaturePocket.cpp @@ -83,6 +83,10 @@ Pocket::Pocket() ADD_PROPERTY_TYPE(TaperAngleRev,(0.0), "Pocket", App::Prop_None, "Taper angle of reverse part of pocketing."); ADD_PROPERTY_TYPE(InnerTaperAngle,(0.0), "Pocket", App::Prop_None, "Taper angle of inner holes."); ADD_PROPERTY_TYPE(InnerTaperAngleRev,(0.0), "Pocket", App::Prop_None, "Taper angle of the reverse part for inner holes."); + + ADD_PROPERTY_TYPE(UsePipeForDraft,(false), "Pocket", App::Prop_None, "Use pipe (i.e. sweep) operation to create draft angles."); + ADD_PROPERTY_TYPE(Linearize,(false), "Pocket", App::Prop_None, + "Linearize the resut shape by simplify linear edge and planar face into line and plane"); } short Pocket::mustExecute() const @@ -246,6 +250,7 @@ App::DocumentObjectExecReturn *Pocket::execute(void) params.dir.Reverse(); std::vector drafts; params.usepipe = this->UsePipeForDraft.getValue(); + params.linearize = this->Linearize.getValue(); Part::Extrusion::makeDraft(params, profileshape, drafts, getDocument()->getStringHasher()); if (drafts.empty()) return new App::DocumentObjectExecReturn("Pocket with draft angle failed"); @@ -303,5 +308,7 @@ App::DocumentObjectExecReturn *Pocket::execute(void) void Pocket::setupObject() { ProfileBased::setupObject(); + UsePipeForDraft.setValue(Part::PartParams::UsePipeForExtrusionDraft()); + Linearize.setValue(Part::PartParams::LinearizeExtrusionDraft()); } diff --git a/src/Mod/PartDesign/App/FeaturePocket.h b/src/Mod/PartDesign/App/FeaturePocket.h index 3de7f0deba13..f83e68e6afc4 100644 --- a/src/Mod/PartDesign/App/FeaturePocket.h +++ b/src/Mod/PartDesign/App/FeaturePocket.h @@ -46,6 +46,7 @@ class PartDesignExport Pocket : public ProfileBased App::PropertyAngle InnerTaperAngle; App::PropertyAngle InnerTaperAngleRev; App::PropertyBool UsePipeForDraft; + App::PropertyBool Linearize; /** @name methods override feature */ //@{