diff --git a/src/Mod/TechDraw/App/AppTechDrawPy.cpp b/src/Mod/TechDraw/App/AppTechDrawPy.cpp index cbe054575..cf06d84e9 100644 --- a/src/Mod/TechDraw/App/AppTechDrawPy.cpp +++ b/src/Mod/TechDraw/App/AppTechDrawPy.cpp @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include #include @@ -46,8 +48,10 @@ #include +#include "DrawProjectSplit.h" #include "EdgeWalker.h" + namespace TechDraw { //module level static C++ functions go here } @@ -70,6 +74,9 @@ public: add_varargs_method("findOuterWire",&Module::findOuterWire, "wire = findOuterWire(edgeList) -- Planar graph traversal finds OuterWire in edge pile." ); + add_varargs_method("findShapeOutline",&Module::findShapeOutline, + "wire = findShapeOutline(shape,scale,direction) -- Project shape in direction and find outer wire of result." + ); initialize("This is a module for making drawings"); // register with Python } virtual ~Module() {} @@ -219,6 +226,62 @@ private: } return Py::asObject(outerWire); } + + Py::Object findShapeOutline(const Py::Tuple& args) + { + PyObject *pcObjShape; + double scale; + PyObject *pcObjDir; + if (!PyArg_ParseTuple(args.ptr(), "OdO", &pcObjShape, + &scale, + &pcObjDir)) { + throw Py::Exception(); + } + + TopoShapePy* pShape = static_cast(pcObjShape); + if (!pShape) { + Base::Console().Message("TRACE - AATDP::findShapeOutline - input shape is null\n"); + return Py::None(); + } + + const TopoDS_Shape& shape = pShape->getTopoShapePtr()->getShape(); + Base::Vector3d dir = static_cast(pcObjDir)->value(); + std::vector edgeList; + try { + edgeList = DrawProjectSplit::getEdgesForWalker(shape,scale,dir); + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + throw Py::Exception(Part::PartExceptionOCCError, e->GetMessageString()); + } + + if (edgeList.empty()) { + Base::Console().Log("LOG - ATDP::findShapeOutline: input is empty\n"); + return Py::None(); + } + + PyObject* outerWire = nullptr; + bool success = false; + try { + EdgeWalker ew; + ew.loadEdges(edgeList); + success = ew.perform(); + if (success) { + std::vector rw = ew.getResultNoDups(); + std::vector sortedWires = ew.sortStrip(rw,true); + outerWire = new TopoShapeWirePy(new TopoShape(*sortedWires.begin())); + } else { + Base::Console().Warning("ATDP::findShapeOutline: input is not planar graph. Wire detection not done\n"); + } + } + catch (Base::Exception &e) { + throw Py::Exception(Base::BaseExceptionFreeCADError, e.what()); + } + if (!success) { + return Py::None(); + } + return Py::asObject(outerWire); + } }; PyObject* initModule() diff --git a/src/Mod/TechDraw/App/CMakeLists.txt b/src/Mod/TechDraw/App/CMakeLists.txt index 92a75ed4d..14f8675ad 100644 --- a/src/Mod/TechDraw/App/CMakeLists.txt +++ b/src/Mod/TechDraw/App/CMakeLists.txt @@ -90,6 +90,8 @@ SET(TechDraw_SRCS PreCompiled.h EdgeWalker.cpp EdgeWalker.h + DrawProjectSplit.cpp + DrawProjectSplit.h ) SET(Geometry_SRCS diff --git a/src/Mod/TechDraw/App/DrawProjectSplit.cpp b/src/Mod/TechDraw/App/DrawProjectSplit.cpp new file mode 100644 index 000000000..c5b54e90a --- /dev/null +++ b/src/Mod/TechDraw/App/DrawProjectSplit.cpp @@ -0,0 +1,428 @@ +/*************************************************************************** + * Copyright (c) WandererFan (wandererfan@gmail.com) 2016 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "DrawUtil.h" +#include "Geometry.h" +#include "GeometryObject.h" +#include "DrawProjectSplit.h" +#include "DrawHatch.h" +#include "EdgeWalker.h" + + +//#include // generated from DrawProjectSplitPy.xml + +using namespace TechDraw; +using namespace std; + + +//=========================================================================== +// DrawProjectSplit +//=========================================================================== + +DrawProjectSplit::DrawProjectSplit() +{ +} + +DrawProjectSplit::~DrawProjectSplit() +{ +} + +std::vector DrawProjectSplit::getEdgesForWalker(TopoDS_Shape shape, double scale, Base::Vector3d direction) +{ + std::vector result; + if (shape.IsNull()) { + return result; + } + + BRepBuilderAPI_Copy BuilderCopy(shape); + TopoDS_Shape copyShape = BuilderCopy.Shape(); + + gp_Pnt inputCenter(0,0,0); + TopoDS_Shape scaledShape; + scaledShape = TechDrawGeometry::scaleShape(copyShape, + scale); + TechDrawGeometry::GeometryObject* go = buildGeometryObject(scaledShape,inputCenter,direction); + result = getEdges(go); + + delete go; + return result; +} + + +TechDrawGeometry::GeometryObject* DrawProjectSplit::buildGeometryObject( + TopoDS_Shape shape, + gp_Pnt& inputCenter, + Base::Vector3d direction) +{ + TechDrawGeometry::GeometryObject* geometryObject = new TechDrawGeometry::GeometryObject("DrawProjectSplit"); + + geometryObject->projectShape(shape, + inputCenter, + direction); + geometryObject->extractGeometry(TechDrawGeometry::ecHARD, //always show the hard&outline visible lines + true); + geometryObject->extractGeometry(TechDrawGeometry::ecOUTLINE, + true); + return geometryObject; +} + +//! get the projected edges with all their new intersections. +std::vector DrawProjectSplit::getEdges(TechDrawGeometry::GeometryObject* geometryObject) +{ + const std::vector& goEdges = geometryObject->getVisibleFaceEdges(true,true); + std::vector::const_iterator itEdge = goEdges.begin(); + std::vector origEdges; + for (;itEdge != goEdges.end(); itEdge++) { + origEdges.push_back((*itEdge)->occEdge); + } + + std::vector faceEdges; + std::vector nonZero; + for (auto& e:origEdges) { //drop any zero edges (shouldn't be any by now!!!) + if (!DrawUtil::isZeroEdge(e)) { + nonZero.push_back(e); + } else { + Base::Console().Message("INFO - DPS::extractFaces found ZeroEdge!\n"); + } + } + faceEdges = nonZero; + origEdges = nonZero; + + //HLR algo does not provide all edge intersections for edge endpoints. + //need to split long edges touched by Vertex of another edge + std::vector splits; + std::vector::iterator itOuter = origEdges.begin(); + int iOuter = 0; + for (; itOuter != origEdges.end(); itOuter++, iOuter++) { + TopoDS_Vertex v1 = TopExp::FirstVertex((*itOuter)); + TopoDS_Vertex v2 = TopExp::LastVertex((*itOuter)); + Bnd_Box sOuter; + BRepBndLib::Add(*itOuter, sOuter); + sOuter.SetGap(0.1); + if (sOuter.IsVoid()) { + Base::Console().Message("DPS::Extract Faces - outer Bnd_Box is void\n"); + continue; + } + if (DrawUtil::isZeroEdge(*itOuter)) { + Base::Console().Message("DPS::extractFaces - outerEdge: %d is ZeroEdge\n",iOuter); //this is not finding ZeroEdges + continue; //skip zero length edges. shouldn't happen ;) + } + int iInner = 0; + std::vector::iterator itInner = faceEdges.begin(); + for (; itInner != faceEdges.end(); itInner++,iInner++) { + if (iInner == iOuter) { + continue; + } + if (DrawUtil::isZeroEdge((*itInner))) { + continue; //skip zero length edges. shouldn't happen ;) + } + + Bnd_Box sInner; + BRepBndLib::Add(*itInner, sInner); + sInner.SetGap(0.1); + if (sInner.IsVoid()) { + Base::Console().Log("INFO - DPS::Extract Faces - inner Bnd_Box is void\n"); + continue; + } + if (sOuter.IsOut(sInner)) { //bboxes of edges don't intersect, don't bother + continue; + } + + double param = -1; + if (isOnEdge((*itInner),v1,param,false)) { + gp_Pnt pnt1 = BRep_Tool::Pnt(v1); + splitPoint s1; + s1.i = iInner; + s1.v = Base::Vector3d(pnt1.X(),pnt1.Y(),pnt1.Z()); + s1.param = param; + splits.push_back(s1); + } + if (isOnEdge((*itInner),v2,param,false)) { + gp_Pnt pnt2 = BRep_Tool::Pnt(v2); + splitPoint s2; + s2.i = iInner; + s2.v = Base::Vector3d(pnt2.X(),pnt2.Y(),pnt2.Z()); + s2.param = param; + splits.push_back(s2); + } + } //inner loop + } //outer loop + + std::vector sorted = sortSplits(splits,true); + auto last = std::unique(sorted.begin(), sorted.end(), DrawProjectSplit::splitEqual); //duplicates to back + sorted.erase(last, sorted.end()); //remove dupls + std::vector newEdges = splitEdges(faceEdges,sorted); + + if (newEdges.empty()) { + Base::Console().Log("LOG - DPS::extractFaces - no newEdges\n"); + } + return newEdges; +} + + +double DrawProjectSplit::simpleMinDist(TopoDS_Shape s1, TopoDS_Shape s2) +{ + Standard_Real minDist = -1; + + BRepExtrema_DistShapeShape extss(s1, s2); + if (!extss.IsDone()) { + Base::Console().Message("FE - BRepExtrema_DistShapeShape failed"); + return -1; + } + int count = extss.NbSolution(); + if (count != 0) { + minDist = extss.Value(); + } else { + minDist = -1; + } + return minDist; +} + + +//this routine is the big time consumer. gets called many times (and is slow?)) +//note param gets modified here +bool DrawProjectSplit::isOnEdge(TopoDS_Edge e, TopoDS_Vertex v, double& param, bool allowEnds) +{ + bool result = false; + bool outOfBox = false; + param = -2; + + //eliminate obvious cases + Bnd_Box sBox; + BRepBndLib::Add(e, sBox); + sBox.SetGap(0.1); + if (sBox.IsVoid()) { + Base::Console().Message("DPS::isOnEdge - Bnd_Box is void\n"); + } else { + gp_Pnt pt = BRep_Tool::Pnt(v); + if (sBox.IsOut(pt)) { + outOfBox = true; + } + } + if (!outOfBox) { + double dist = simpleMinDist(v,e); + if (dist < 0.0) { + Base::Console().Error("DPS::isOnEdge - simpleMinDist failed: %.3f\n",dist); + result = false; + } else if (dist < Precision::Confusion()) { + const gp_Pnt pt = BRep_Tool::Pnt(v); //have to duplicate method 3 to get param + BRepAdaptor_Curve adapt(e); + const Handle_Geom_Curve c = adapt.Curve().Curve(); + double maxDist = 0.000001; //magic number. less than this gives false positives. + //bool found = + (void) GeomLib_Tool::Parameter(c,pt,maxDist,param); //already know point it on curve + result = true; + } + if (result) { + TopoDS_Vertex v1 = TopExp::FirstVertex(e); + TopoDS_Vertex v2 = TopExp::LastVertex(e); + if (DrawUtil::isSamePoint(v,v1) || DrawUtil::isSamePoint(v,v2)) { + if (!allowEnds) { + result = false; + } + } + } + } //!outofbox + return result; +} + + +std::vector DrawProjectSplit::splitEdges(std::vector edges, std::vector splits) +{ + std::vector result; + std::vector newEdges; + std::vector edgeSplits; //splits for current edge + int iEdge = 0; //current edge index + int iSplit = 0; //current splitindex + int ii = 0; //i value of current split + int endEdge = edges.size(); + int endSplit = splits.size(); + int imax = std::numeric_limits::max(); + + while ((iEdge < endEdge) ) { + if (iSplit < endSplit) { + ii = splits[iSplit].i; + } else { + ii = imax; + } + if (ii == iEdge) { + edgeSplits.push_back(splits[iSplit]); + iSplit++; + } else if (ii > iEdge) { + if (!edgeSplits.empty()) { //save *iedge's splits + newEdges = split1Edge(edges[iEdge],edgeSplits); + result.insert(result.end(), newEdges.begin(), newEdges.end()); + edgeSplits.clear(); + } else { + result.push_back(edges[iEdge]); //save *iedge + } + iEdge++; //next edge + } else if (iEdge > ii) { + iSplit++; + } + } + + if (!edgeSplits.empty()) { //handle last batch + newEdges = split1Edge(edges[iEdge],edgeSplits); + result.insert(result.end(), newEdges.begin(), newEdges.end()); + edgeSplits.clear(); + } + + return result; +} + + +std::vector DrawProjectSplit::split1Edge(TopoDS_Edge e, std::vector splits) +{ + std::vector result; + if (splits.empty()) { + return result; + } + + BRepAdaptor_Curve adapt(e); + Handle_Geom_Curve c = adapt.Curve().Curve(); + double first = BRepLProp_CurveTool::FirstParameter(adapt); + double last = BRepLProp_CurveTool::LastParameter(adapt); + if (first > last) { + //TODO parms.reverse(); + Base::Console().Message("DPS::split1Edge - edge is backwards!\n"); + return result; + } + std::vector parms; + parms.push_back(first); + for (auto& s:splits) { + parms.push_back(s.param); + } + + parms.push_back(last); + std::vector::iterator pfirst = parms.begin(); + auto parms2 = parms.begin() + 1; + std::vector::iterator psecond = parms2; + std::vector::iterator pstop = parms.end(); + for (; psecond != pstop; pfirst++,psecond++) { + try { + BRepBuilderAPI_MakeEdge mkEdge(c, *pfirst, *psecond); + if (mkEdge.IsDone()) { + TopoDS_Edge e1 = mkEdge.Edge(); + result.push_back(e1); + } + } + catch (Standard_Failure) { + Base::Console().Message("LOG - DPS::split1Edge failed building edge segment\n"); + } + } + return result; +} + +std::vector DrawProjectSplit::sortSplits(std::vector& s, bool ascend) +{ + std::vector sorted = s; + std::sort(sorted.begin(), sorted.end(), DrawProjectSplit::splitCompare); + if (ascend) { + std::reverse(sorted.begin(),sorted.end()); + } + return sorted; +} + +//return true if p1 "is greater than" p2 +/*static*/bool DrawProjectSplit::splitCompare(const splitPoint& p1, const splitPoint& p2) +{ + bool result = false; + if (p1.i > p2.i) { + result = true; + } else if (p1.i < p2.i) { + result = false; + } else if (p1.param > p2.param) { + result = true; + } else if (p1.param < p2.param) { + result = false; + } + return result; +} + +//return true if p1 "is equal to" p2 +/*static*/bool DrawProjectSplit::splitEqual(const splitPoint& p1, const splitPoint& p2) +{ + bool result = false; + if ((p1.i == p2.i) && + (fabs(p1.param - p2.param) < Precision::Confusion())) { + result = true; + } + return result; +} + + diff --git a/src/Mod/TechDraw/App/DrawProjectSplit.h b/src/Mod/TechDraw/App/DrawProjectSplit.h new file mode 100644 index 000000000..d9e708e8d --- /dev/null +++ b/src/Mod/TechDraw/App/DrawProjectSplit.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (c) WandererFan (wandererfan@gmail.com) 2016 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef _DrawProjectSplit_h_ +#define _DrawProjectSplit_h_ + + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +class gp_Pnt; + +namespace TechDrawGeometry +{ +class GeometryObject; +class Vertex; +class BaseGeom; +} + +namespace TechDraw +{ +struct splitPoint { + int i; + Base::Vector3d v; + double param; +}; + +class TechDrawExport DrawProjectSplit +{ +public: + DrawProjectSplit(); + ~DrawProjectSplit(); + +public: + static std::vector getEdgesForWalker(TopoDS_Shape shape, double scale, Base::Vector3d direction); + static TechDrawGeometry::GeometryObject* buildGeometryObject(TopoDS_Shape shape, gp_Pnt& center, Base::Vector3d direction); + + static bool isOnEdge(TopoDS_Edge e, TopoDS_Vertex v, double& param, bool allowEnds = false); + static std::vector splitEdges(std::vector orig, std::vector splits); + static std::vector split1Edge(TopoDS_Edge e, std::vector splitPoints); + static double simpleMinDist(TopoDS_Shape s1, TopoDS_Shape s2); //const; //probably sb static or DrawUtil + + static std::vector sortSplits(std::vector& s, bool ascend); + static bool splitCompare(const splitPoint& p1, const splitPoint& p2); + static bool splitEqual(const splitPoint& p1, const splitPoint& p2); + +protected: + static std::vector getEdges(TechDrawGeometry::GeometryObject* geometryObject); + + +private: + +}; + +typedef App::FeaturePythonT DrawProjectSplitPython; + +} //namespace TechDraw + +#endif // #ifndef _DrawProjectSplit_h_ diff --git a/src/Mod/TechDraw/App/DrawViewCollection.cpp b/src/Mod/TechDraw/App/DrawViewCollection.cpp index 8f5297ca9..47e6b7922 100644 --- a/src/Mod/TechDraw/App/DrawViewCollection.cpp +++ b/src/Mod/TechDraw/App/DrawViewCollection.cpp @@ -118,7 +118,6 @@ void DrawViewCollection::rebuildViewList() short DrawViewCollection::mustExecute() const { - // If Tolerance Property is touched if (Views.isTouched() || Source.isTouched()) { return 1; diff --git a/src/Mod/TechDraw/App/DrawViewPart.cpp b/src/Mod/TechDraw/App/DrawViewPart.cpp index 4a4ab5f51..7dbee7049 100644 --- a/src/Mod/TechDraw/App/DrawViewPart.cpp +++ b/src/Mod/TechDraw/App/DrawViewPart.cpp @@ -95,7 +95,6 @@ using namespace std; // DrawViewPart //=========================================================================== -App::PropertyFloatConstraint::Constraints DrawViewPart::floatRange = {0.01f,5.0f,0.05f}; PROPERTY_SOURCE(TechDraw::DrawViewPart, TechDraw::DrawView) @@ -108,17 +107,13 @@ DrawViewPart::DrawViewPart(void) : geometryObject(0) //properties that affect Geometry ADD_PROPERTY_TYPE(Source ,(0),group,App::Prop_None,"3D Shape to view"); ADD_PROPERTY_TYPE(Direction ,(0,0,1.0) ,group,App::Prop_None,"Projection direction. The direction you are looking from."); -// ADD_PROPERTY_TYPE(XAxisDirection ,(1,0,0) ,group,App::Prop_None,"Where to place projection's XAxis (rotation)"); - ADD_PROPERTY_TYPE(Tolerance,(0.05f),group,App::Prop_None,"Internal tolerance for calculations"); - Tolerance.setConstraints(&floatRange); //properties that affect Appearance //visible outline ADD_PROPERTY_TYPE(SmoothVisible ,(false),sgroup,App::Prop_None,"Visible Smooth lines on/off"); ADD_PROPERTY_TYPE(SeamVisible ,(false),sgroup,App::Prop_None,"Visible Seam lines on/off"); ADD_PROPERTY_TYPE(IsoVisible ,(false),sgroup,App::Prop_None,"Visible Iso u,v lines on/off"); - ADD_PROPERTY_TYPE(HardHidden ,(false),sgroup,App::Prop_None,"Hidden Hard lines on/off"); // and outline - //hidden outline + ADD_PROPERTY_TYPE(HardHidden ,(false),sgroup,App::Prop_None,"Hidden Hard lines on/off"); ADD_PROPERTY_TYPE(SmoothHidden ,(false),sgroup,App::Prop_None,"Hidden Smooth lines on/off"); ADD_PROPERTY_TYPE(SeamHidden ,(false),sgroup,App::Prop_None,"Hidden Seam lines on/off"); ADD_PROPERTY_TYPE(IsoHidden ,(false),sgroup,App::Prop_None,"Hidden Iso u,v lines on/off"); @@ -135,7 +130,7 @@ DrawViewPart::DrawViewPart(void) : geometryObject(0) //properties that affect Section Line ADD_PROPERTY_TYPE(ShowSectionLine ,(true) ,sgroup,App::Prop_None,"Show/hide section line if applicable"); - geometryObject = new TechDrawGeometry::GeometryObject(this); + geometryObject = nullptr; getRunControl(); } @@ -162,47 +157,20 @@ App::DocumentObjectExecReturn *DrawViewPart::execute(void) return new App::DocumentObjectExecReturn("FVP - Linked shape object is empty"); } - //Base::Console().Message("TRACE - DVP::execute - %s/%s ScaleType: %s\n",getNameInDocument(),Label.getValue(),ScaleType.getValueAsString()); (void) DrawView::execute(); //make sure Scale is up to date - geometryObject->setTolerance(Tolerance.getValue()); - geometryObject->setScale(Scale.getValue()); - - //TODO: remove these try/catch block when code is stable gp_Pnt inputCenter; - try { - inputCenter = TechDrawGeometry::findCentroid(shape, - Direction.getValue()); - shapeCentroid = Base::Vector3d(inputCenter.X(),inputCenter.Y(),inputCenter.Z()); - } - catch (Standard_Failure) { - Handle_Standard_Failure e1 = Standard_Failure::Caught(); - Base::Console().Log("LOG - DVP::execute - findCentroid failed for %s - %s **\n",getNameInDocument(),e1->GetMessageString()); - return new App::DocumentObjectExecReturn(e1->GetMessageString()); - } + inputCenter = TechDrawGeometry::findCentroid(shape, + Direction.getValue()); + shapeCentroid = Base::Vector3d(inputCenter.X(),inputCenter.Y(),inputCenter.Z()); TopoDS_Shape mirroredShape; - try { - mirroredShape = TechDrawGeometry::mirrorShape(shape, - inputCenter, - Scale.getValue()); - } - catch (Standard_Failure) { - Handle_Standard_Failure e2 = Standard_Failure::Caught(); - Base::Console().Log("LOG - DVP::execute - mirrorShape failed for %s - %s **\n",getNameInDocument(),e2->GetMessageString()); - return new App::DocumentObjectExecReturn(e2->GetMessageString()); - } + mirroredShape = TechDrawGeometry::mirrorShape(shape, + inputCenter, + Scale.getValue()); - try { - geometryObject->setIsoCount(IsoCount.getValue()); - buildGeometryObject(mirroredShape,inputCenter); - } - catch (Standard_Failure) { - Handle_Standard_Failure e3 = Standard_Failure::Caught(); - Base::Console().Log("LOG - DVP::execute - buildGeometryObject failed for %s - %s **\n",getNameInDocument(),e3->GetMessageString()); - return new App::DocumentObjectExecReturn(e3->GetMessageString()); - } + geometryObject = buildGeometryObject(mirroredShape,inputCenter); #if MOD_TECHDRAW_HANDLE_FACES if (handleFaces()) { @@ -238,64 +206,68 @@ short DrawViewPart::mustExecute() const void DrawViewPart::onChanged(const App::Property* prop) { - //Base::Console().Message("TRACE - DVP::onChanged(%s) - %s\n",prop->getName(),Label.getValue()); DrawView::onChanged(prop); //TODO: when scale changes, any Dimensions for this View sb recalculated. DVD should pick this up subject to topological naming issues. } -void DrawViewPart::buildGeometryObject(TopoDS_Shape shape, gp_Pnt& inputCenter) +//note: slightly different than routine with same name in DrawProjectSplit +TechDrawGeometry::GeometryObject* DrawViewPart::buildGeometryObject(TopoDS_Shape shape, gp_Pnt& inputCenter) { - Base::Vector3d baseProjDir = Direction.getValue(); + TechDrawGeometry::GeometryObject* go = new TechDrawGeometry::GeometryObject(getNameInDocument()); + go->setIsoCount(IsoCount.getValue()); + Base::Vector3d baseProjDir = Direction.getValue(); saveParamSpace(baseProjDir); - geometryObject->projectShape(shape, - inputCenter, - Direction.getValue()); - geometryObject->extractGeometry(TechDrawGeometry::ecHARD, //always show the hard&outline visible lines - true); - geometryObject->extractGeometry(TechDrawGeometry::ecOUTLINE, - true); + go->projectShape(shape, + inputCenter, + Direction.getValue()); + go->extractGeometry(TechDrawGeometry::ecHARD, //always show the hard&outline visible lines + true); + go->extractGeometry(TechDrawGeometry::ecOUTLINE, + true); if (SmoothVisible.getValue()) { - geometryObject->extractGeometry(TechDrawGeometry::ecSMOOTH, - true); + go->extractGeometry(TechDrawGeometry::ecSMOOTH, + true); } if (SeamVisible.getValue()) { - geometryObject->extractGeometry(TechDrawGeometry::ecSEAM, - true); + go->extractGeometry(TechDrawGeometry::ecSEAM, + true); } if ((IsoVisible.getValue()) && (IsoCount.getValue() > 0)) { - geometryObject->extractGeometry(TechDrawGeometry::ecUVISO, - true); + go->extractGeometry(TechDrawGeometry::ecUVISO, + true); } if (HardHidden.getValue()) { - geometryObject->extractGeometry(TechDrawGeometry::ecHARD, - false); - geometryObject->extractGeometry(TechDrawGeometry::ecOUTLINE, - false); + go->extractGeometry(TechDrawGeometry::ecHARD, + false); + go->extractGeometry(TechDrawGeometry::ecOUTLINE, + false); } if (SmoothHidden.getValue()) { - geometryObject->extractGeometry(TechDrawGeometry::ecSMOOTH, - false); + go->extractGeometry(TechDrawGeometry::ecSMOOTH, + false); } if (SeamHidden.getValue()) { - geometryObject->extractGeometry(TechDrawGeometry::ecSEAM, - false); + go->extractGeometry(TechDrawGeometry::ecSEAM, + false); } if (IsoHidden.getValue() && (IsoCount.getValue() > 0)) { - geometryObject->extractGeometry(TechDrawGeometry::ecUVISO, - false); + go->extractGeometry(TechDrawGeometry::ecUVISO, + false); } - bbox = geometryObject->calcBoundingBox(); + bbox = go->calcBoundingBox(); + return go; } //! make faces from the existing edge geometry void DrawViewPart::extractFaces() { geometryObject->clearFaceGeom(); - const std::vector& goEdges = geometryObject->getVisibleFaceEdges(); + const std::vector& goEdges = + geometryObject->getVisibleFaceEdges(SmoothVisible.getValue(),SeamVisible.getValue()); std::vector::const_iterator itEdge = goEdges.begin(); std::vector origEdges; for (;itEdge != goEdges.end(); itEdge++) { @@ -356,7 +328,7 @@ void DrawViewPart::extractFaces() } double param = -1; - if (isOnEdge((*itInner),v1,param,false)) { + if (DrawProjectSplit::isOnEdge((*itInner),v1,param,false)) { gp_Pnt pnt1 = BRep_Tool::Pnt(v1); splitPoint s1; s1.i = iInner; @@ -364,7 +336,7 @@ void DrawViewPart::extractFaces() s1.param = param; splits.push_back(s1); } - if (isOnEdge((*itInner),v2,param,false)) { + if (DrawProjectSplit::isOnEdge((*itInner),v2,param,false)) { gp_Pnt pnt2 = BRep_Tool::Pnt(v2); splitPoint s2; s2.i = iInner; @@ -375,10 +347,10 @@ void DrawViewPart::extractFaces() } //inner loop } //outer loop - std::vector sorted = sortSplits(splits,true); - auto last = std::unique(sorted.begin(), sorted.end(), DrawViewPart::splitEqual); //duplicates to back + std::vector sorted = DrawProjectSplit::sortSplits(splits,true); + auto last = std::unique(sorted.begin(), sorted.end(), DrawProjectSplit::splitEqual); //duplicates to back sorted.erase(last, sorted.end()); //remove dupls - std::vector newEdges = splitEdges(faceEdges,sorted); + std::vector newEdges = DrawProjectSplit::splitEdges(faceEdges,sorted); if (newEdges.empty()) { Base::Console().Log("LOG - DVP::extractFaces - no newEdges\n"); @@ -408,247 +380,6 @@ void DrawViewPart::extractFaces() } } -double DrawViewPart::simpleMinDist(TopoDS_Shape s1, TopoDS_Shape s2) -{ - Standard_Real minDist = -1; - - BRepExtrema_DistShapeShape extss(s1, s2); - if (!extss.IsDone()) { - Base::Console().Message("DVP - BRepExtrema_DistShapeShape failed"); - return -1; - } - int count = extss.NbSolution(); - if (count != 0) { - minDist = extss.Value(); - } else { - minDist = -1; - } - return minDist; -} - -//this routine is the big time consumer. gets called many times (and is slow?)) -//note param gets modified here -bool DrawViewPart::isOnEdge(TopoDS_Edge e, TopoDS_Vertex v, double& param, bool allowEnds) -{ - bool result = false; - bool outOfBox = false; - param = -2; - - //eliminate obvious cases - Bnd_Box sBox; - BRepBndLib::Add(e, sBox); - sBox.SetGap(0.1); - if (sBox.IsVoid()) { - Base::Console().Message("DVP::isOnEdge - Bnd_Box is void for %s\n",getNameInDocument()); - } else { - gp_Pnt pt = BRep_Tool::Pnt(v); - if (sBox.IsOut(pt)) { - outOfBox = true; - } - } - if (!outOfBox) { - if (m_interAlgo == 1) { - //1) using projPointOnCurve. roughly similar to dist to shape w/ bndbox. hangs(?) w/o bndbox - try { - gp_Pnt pt = BRep_Tool::Pnt(v); - BRepAdaptor_Curve adapt(e); - Handle_Geom_Curve c = adapt.Curve().Curve(); - GeomAPI_ProjectPointOnCurve proj(pt,c); - int n = proj.NbPoints(); - if (n > 0) { - if (proj.LowerDistance() < Precision::Confusion()) { - param = proj.LowerDistanceParameter(); - result = true; - } - if (result) { - TopoDS_Vertex v1 = TopExp::FirstVertex(e); - TopoDS_Vertex v2 = TopExp::LastVertex(e); - if (DrawUtil::isSamePoint(v,v1) || DrawUtil::isSamePoint(v,v2)) { - if (!allowEnds) { - result = false; - } - } - } - } - } - catch (Standard_Failure) { - Handle_Standard_Failure e = Standard_Failure::Caught(); //no perp projection - } - } else if (m_interAlgo == 2) { //can't provide param as is - double dist = simpleMinDist(v,e); - if (dist < 0.0) { - Base::Console().Error("DVP::isOnEdge - simpleMinDist failed: %.3f\n",dist); - result = false; - } else if (dist < Precision::Confusion()) { - const gp_Pnt pt = BRep_Tool::Pnt(v); //have to duplicate method 3 to get param - BRepAdaptor_Curve adapt(e); - const Handle_Geom_Curve c = adapt.Curve().Curve(); - double maxDist = 0.000001; //magic number. less than this gives false positives. - //bool found = - (void) GeomLib_Tool::Parameter(c,pt,maxDist,param); //already know point it on curve - result = true; - } - if (result) { - TopoDS_Vertex v1 = TopExp::FirstVertex(e); - TopoDS_Vertex v2 = TopExp::LastVertex(e); - if (DrawUtil::isSamePoint(v,v1) || DrawUtil::isSamePoint(v,v2)) { - if (!allowEnds) { - result = false; - } - } - } - } else if (m_interAlgo == 3) { - const gp_Pnt pt = BRep_Tool::Pnt(v); - BRepAdaptor_Curve adapt(e); - const Handle_Geom_Curve c = adapt.Curve().Curve(); - double par = -1; - double maxDist = 0.000001; //magic number. less than this gives false positives. - bool found = GeomLib_Tool::Parameter(c,pt,maxDist,par); - if (found) { - result = true; - param = par; - TopoDS_Vertex v1 = TopExp::FirstVertex(e); - TopoDS_Vertex v2 = TopExp::LastVertex(e); - if (DrawUtil::isSamePoint(v,v1) || DrawUtil::isSamePoint(v,v2)) { - if (!allowEnds) { - result = false; - } - } - } - } - } //!outofbox - return result; -} - -std::vector DrawViewPart::splitEdges(std::vector edges, std::vector splits) -{ - std::vector result; - std::vector newEdges; - std::vector edgeSplits; //splits for current edge - int iEdge = 0; //current edge index - int iSplit = 0; //current splitindex - int ii = 0; //i value of current split - int endEdge = edges.size(); - int endSplit = splits.size(); - int imax = std::numeric_limits::max(); - - while ((iEdge < endEdge) ) { - if (iSplit < endSplit) { - ii = splits[iSplit].i; - } else { - ii = imax; - } - if (ii == iEdge) { - edgeSplits.push_back(splits[iSplit]); - iSplit++; - continue; - } - - if (ii > iEdge) { - if (!edgeSplits.empty()) { //save *iedge's splits - newEdges = split1Edge(edges[iEdge],edgeSplits); - result.insert(result.end(), newEdges.begin(), newEdges.end()); - edgeSplits.clear(); - } else { - result.push_back(edges[iEdge]); //save *iedge - } - iEdge++; //next edge - continue; - } - - if (iEdge > ii) { - iSplit++; - continue; - } - } - if (!edgeSplits.empty()) { //handle last batch - newEdges = split1Edge(edges[iEdge],edgeSplits); - result.insert(result.end(), newEdges.begin(), newEdges.end()); - edgeSplits.clear(); - } - - return result; -} - -std::vector DrawViewPart::split1Edge(TopoDS_Edge e, std::vector splits) -{ - //Base::Console().Message("DVP::split1Edge - splits: %d\n",splits.size()); - std::vector result; - if (splits.empty()) { - return result; - } - - BRepAdaptor_Curve adapt(e); - Handle_Geom_Curve c = adapt.Curve().Curve(); - double first = BRepLProp_CurveTool::FirstParameter(adapt); - double last = BRepLProp_CurveTool::LastParameter(adapt); - if (first > last) { - //TODO parms.reverse(); - Base::Console().Message("DVP::split1Edge - edge is backwards!\n"); - return result; - } - std::vector parms; - parms.push_back(first); - for (auto& s:splits) { - parms.push_back(s.param); - } - - parms.push_back(last); - std::vector::iterator pfirst = parms.begin(); - auto parms2 = parms.begin() + 1; - std::vector::iterator psecond = parms2; - std::vector::iterator pstop = parms.end(); - for (; psecond != pstop; pfirst++,psecond++) { - try { - BRepBuilderAPI_MakeEdge mkEdge(c, *pfirst, *psecond); - if (mkEdge.IsDone()) { - TopoDS_Edge e1 = mkEdge.Edge(); - result.push_back(e1); - } - } - catch (Standard_Failure) { - Base::Console().Message("LOG - DVP::split1Edge failed building edge segment\n"); - } - } - return result; -} - -std::vector DrawViewPart::sortSplits(std::vector& s, bool ascend) -{ - std::vector sorted = s; - std::sort(sorted.begin(), sorted.end(), DrawViewPart::splitCompare); - if (ascend) { - std::reverse(sorted.begin(),sorted.end()); - } - return sorted; -} - -//return true if p1 "is greater than" p2 -/*static*/bool DrawViewPart::splitCompare(const splitPoint& p1, const splitPoint& p2) -{ - bool result = false; - if (p1.i > p2.i) { - result = true; - } else if (p1.i < p2.i) { - result = false; - } else if (p1.param > p2.param) { - result = true; - } else if (p1.param < p2.param) { - result = false; - } - return result; -} - -//return true if p1 "is equal to" p2 -/*static*/bool DrawViewPart::splitEqual(const splitPoint& p1, const splitPoint& p2) -{ - bool result = false; - if ((p1.i == p2.i) && - (fabs(p1.param - p2.param) < Precision::Confusion())) { - result = true; - } - return result; -} std::vector DrawViewPart::getHatches() const { @@ -763,6 +494,9 @@ Base::Vector3d DrawViewPart::projectPoint(const Base::Vector3d& pt) const bool DrawViewPart::hasGeometry(void) const { bool result = false; + if (geometryObject == nullptr) { + return result; + } const std::vector &verts = getVertexGeometry(); const std::vector &edges = getEdgeGeometry(); if (verts.empty() && @@ -802,18 +536,15 @@ std::vector DrawViewPart::getSectionRefs(void) const const std::vector DrawViewPart::getVisibleFaceEdges() const { - return geometryObject->getVisibleFaceEdges(); + return geometryObject->getVisibleFaceEdges(SmoothVisible.getValue(),SeamVisible.getValue()); } void DrawViewPart::getRunControl() { Base::Reference hGrp = App::GetApplication().GetUserParameter() .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/RunControl"); - m_interAlgo = hGrp->GetInt("InterAlgo", 2l); m_sectionEdges = hGrp->GetBool("ShowSectionEdges", 1l); m_handleFaces = hGrp->GetBool("HandleFaces", 1l); -// Base::Console().Message("TRACE - DVP::getRunControl - interAlgo: %ld sectionFaces: %ld handleFaces: %ld\n", -// m_interAlgo,m_sectionEdges,m_handleFaces); } bool DrawViewPart::handleFaces(void) diff --git a/src/Mod/TechDraw/App/DrawViewPart.h b/src/Mod/TechDraw/App/DrawViewPart.h index 93330b235..90936e0d3 100644 --- a/src/Mod/TechDraw/App/DrawViewPart.h +++ b/src/Mod/TechDraw/App/DrawViewPart.h @@ -32,12 +32,12 @@ #include #include #include -#include "DrawView.h" #include #include -//#include "GeometryObject.h" +#include "DrawView.h" +#include "DrawProjectSplit.h" class gp_Pnt; @@ -55,11 +55,7 @@ class DrawHatch; namespace TechDraw { -struct splitPoint { - int i; - Base::Vector3d v; - double param; -}; + class DrawViewSection; class TechDrawExport DrawViewPart : public DrawView @@ -72,7 +68,6 @@ public: App::PropertyLink Source; //Part Feature App::PropertyVector Direction; //TODO: Rename to YAxisDirection or whatever this actually is (ProjectionDirection) - //App::PropertyVector XAxisDirection; App::PropertyBool SeamVisible; App::PropertyBool SmoothVisible; //App::PropertyBool OutlinesVisible; @@ -90,7 +85,6 @@ public: App::PropertyFloat IsoWidth; App::PropertyBool ArcCenterMarks; App::PropertyFloat CenterScale; - App::PropertyFloatConstraint Tolerance; App::PropertyBool HorizCenterLine; App::PropertyBool VertCenterLine; App::PropertyBool ShowSectionLine; @@ -144,31 +138,21 @@ protected: Base::BoundBox3d bbox; void onChanged(const App::Property* prop); - void buildGeometryObject(TopoDS_Shape shape, gp_Pnt& center); + TechDrawGeometry::GeometryObject* buildGeometryObject(TopoDS_Shape shape, gp_Pnt& center); void extractFaces(); - bool isOnEdge(TopoDS_Edge e, TopoDS_Vertex v, double& param, bool allowEnds = false); - std::vector splitEdges(std::vector orig, std::vector splits); - std::vector split1Edge(TopoDS_Edge e, std::vector splitPoints); - double simpleMinDist(TopoDS_Shape s1, TopoDS_Shape s2); //const; //probably sb static or DrawUtil - //Projection parameter space void saveParamSpace(const Base::Vector3d& direction); Base::Vector3d uDir; //paperspace X Base::Vector3d vDir; //paperspace Y Base::Vector3d wDir; //paperspace Z Base::Vector3d shapeCentroid; - std::vector sortSplits(std::vector& s, bool ascend); - static bool splitCompare(const splitPoint& p1, const splitPoint& p2); - static bool splitEqual(const splitPoint& p1, const splitPoint& p2); void getRunControl(void); - long int m_interAlgo; bool m_sectionEdges; bool m_handleFaces; private: - static App::PropertyFloatConstraint::Constraints floatRange; }; diff --git a/src/Mod/TechDraw/App/DrawViewSection.cpp b/src/Mod/TechDraw/App/DrawViewSection.cpp index 29b94c2bd..21bf0aa14 100644 --- a/src/Mod/TechDraw/App/DrawViewSection.cpp +++ b/src/Mod/TechDraw/App/DrawViewSection.cpp @@ -74,6 +74,7 @@ #include "Geometry.h" #include "GeometryObject.h" #include "EdgeWalker.h" +#include "DrawProjectSplit.h" #include "DrawViewSection.h" using namespace TechDraw; @@ -219,9 +220,6 @@ App::DocumentObjectExecReturn *DrawViewSection::execute(void) return DrawView::execute(); } - geometryObject->setTolerance(Tolerance.getValue()); - geometryObject->setScale(Scale.getValue()); - gp_Pnt inputCenter; try { inputCenter = TechDrawGeometry::findCentroid(rawShape, @@ -229,7 +227,7 @@ App::DocumentObjectExecReturn *DrawViewSection::execute(void) TopoDS_Shape mirroredShape = TechDrawGeometry::mirrorShape(rawShape, inputCenter, Scale.getValue()); - buildGeometryObject(mirroredShape,inputCenter); //this is original shape after cut by section prism + geometryObject = buildGeometryObject(mirroredShape,inputCenter); //this is original shape after cut by section prism #if MOD_TECHDRAW_HANDLE_FACES extractFaces(); diff --git a/src/Mod/TechDraw/App/GeometryObject.cpp b/src/Mod/TechDraw/App/GeometryObject.cpp index 2b0c92b4f..879c8c893 100644 --- a/src/Mod/TechDraw/App/GeometryObject.cpp +++ b/src/Mod/TechDraw/App/GeometryObject.cpp @@ -38,10 +38,8 @@ #include #include #include -//#include #include #include -//#include #include #include #include @@ -69,8 +67,6 @@ #include "GeometryObject.h" #include "DrawViewPart.h" -//#include - using namespace TechDrawGeometry; using namespace TechDraw; using namespace std; @@ -80,11 +76,9 @@ struct EdgePoints { TopoDS_Edge edge; }; - -GeometryObject::GeometryObject(DrawViewPart* parent) : - Tolerance(0.05f), +GeometryObject::GeometryObject(std::string parent) : Scale(1.f), - m_parent(parent), + m_parentName(parent), m_isoCount(0) { } @@ -94,21 +88,18 @@ GeometryObject::~GeometryObject() clear(); } -void GeometryObject::setTolerance(double value) -{ - Tolerance = value; -} - void GeometryObject::setScale(double value) { Scale = value; } -const std::vector GeometryObject::getVisibleFaceEdges() const + +const std::vector GeometryObject::getVisibleFaceEdges(const bool smooth, const bool seam) const { std::vector result; - bool smoothOK = m_parent->SmoothVisible.getValue(); - bool seamOK = m_parent->SeamVisible.getValue(); + bool smoothOK = smooth; + bool seamOK = seam; + for (auto& e:edgeGeom) { if (e->visible) { switch (e->classOfEdge) { @@ -184,7 +175,7 @@ void GeometryObject::projectShape(const TopoDS_Shape& input, auto end = chrono::high_resolution_clock::now(); auto diff = end - start; double diffOut = chrono::duration (diff).count(); - Base::Console().Log("TIMING - %s GO spent: %.3f millisecs in HLRBRep_Algo & co\n",m_parent->getNameInDocument(),diffOut); + Base::Console().Log("TIMING - %s GO spent: %.3f millisecs in HLRBRep_Algo & co\n",m_parentName.c_str(),diffOut); try { HLRBRep_HLRToShape hlrToShape(brep_hlr); @@ -297,7 +288,6 @@ void GeometryObject::addGeomFromCompound(TopoDS_Shape edgeCompound, edgeClass ca //add vertices of new edge if not already in list if (visible) { BaseGeom* lastAdded = edgeGeom.back(); - //if (edgeGeom.empty()) {horrible_death();} //back() undefined behavior (can't happen? baseFactory always returns a Base?) bool v1Add = true, v2Add = true; bool c1Add = true; TechDrawGeometry::Vertex* v1 = new TechDrawGeometry::Vertex(lastAdded->getStartPoint()); @@ -754,24 +744,22 @@ TopoDS_Shape TechDrawGeometry::mirrorShape(const TopoDS_Shape &input, return transShape; } -/// debug functions -/* TODO: Clean this up when faces are actually working properly... - -void debugEdge(const TopoDS_Edge &e) - +//!scales a shape about a origin +TopoDS_Shape TechDrawGeometry::scaleShape(const TopoDS_Shape &input, + double scale) { + TopoDS_Shape transShape; + try { + gp_Trsf scaleTransform; + scaleTransform.SetScale(gp_Pnt(0,0,0), scale); - gp_Pnt p0 = BRep_Tool::Pnt(TopExp::FirstVertex(e)); - - gp_Pnt p1 = BRep_Tool::Pnt(TopExp::LastVertex(e)); - - qDebug()< #include -//#include #include #include @@ -33,7 +32,6 @@ #include #include "Geometry.h" -//#include "DrawViewPart.h" namespace TechDraw { @@ -48,6 +46,8 @@ namespace TechDrawGeometry TopoDS_Shape TechDrawExport mirrorShape(const TopoDS_Shape &input, const gp_Pnt& inputCenter, double scale); +TopoDS_Shape TechDrawExport scaleShape(const TopoDS_Shape &input, + double scale); //! Returns the centroid of shape, as viewed according to direction gp_Pnt TechDrawExport findCentroid(const TopoDS_Shape &shape, @@ -61,12 +61,11 @@ class TechDrawExport GeometryObject { public: /// Constructor - GeometryObject(TechDraw::DrawViewPart* parent); + GeometryObject(std::string parent); virtual ~GeometryObject(); void clear(); - void setTolerance(double value); void setScale(double value); //! Returns 2D bounding box @@ -74,7 +73,7 @@ public: const std::vector & getVertexGeometry() const { return vertexGeom; }; const std::vector & getEdgeGeometry() const { return edgeGeom; }; - const std::vector getVisibleFaceEdges() const; + const std::vector getVisibleFaceEdges(bool smooth, bool seam) const; const std::vector & getFaceGeometry() const { return faceGeom; }; void projectShape(const TopoDS_Shape &input, @@ -84,6 +83,7 @@ public: void addFaceGeom(Face * f); void clearFaceGeom(); void setIsoCount(int i) { m_isoCount = i; } + void setParentName(std::string n); //for debug messages protected: //HLR output @@ -128,10 +128,9 @@ protected: bool findVertex(Base::Vector2D v); - double Tolerance; double Scale; - TechDraw::DrawViewPart* m_parent; + std::string m_parentName; int m_isoCount; }; diff --git a/src/Mod/TechDraw/Gui/QGIViewPart.cpp b/src/Mod/TechDraw/Gui/QGIViewPart.cpp index a4c952866..f1e62ebc1 100644 --- a/src/Mod/TechDraw/Gui/QGIViewPart.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewPart.cpp @@ -250,7 +250,6 @@ void QGIViewPart::updateView(bool update) viewPart->isTouched() || viewPart->Source.isTouched() || viewPart->Direction.isTouched() || - viewPart->Tolerance.isTouched() || viewPart->Scale.isTouched() || viewPart->HardHidden.isTouched() || viewPart->SmoothVisible.isTouched() || @@ -290,6 +289,9 @@ void QGIViewPart::drawViewPart() if ( viewPart == nullptr ) { return; } + if (!viewPart->hasGeometry()) { + return; + } float lineWidth = viewPart->LineWidth.getValue() * lineScaleFactor; float lineWidthHid = viewPart->HiddenWidth.getValue() * lineScaleFactor;