From c0c36138d7c23c8cc62b693796f64781cb1c2a8c Mon Sep 17 00:00:00 2001 From: wandererfan Date: Thu, 2 Aug 2018 20:25:03 -0400 Subject: [PATCH] Allow circular BSplines to be dimensioned --- src/Mod/TechDraw/App/DrawViewDimension.cpp | 59 ++++++++- src/Mod/TechDraw/App/Geometry.cpp | 147 ++++++++++++++++++++- src/Mod/TechDraw/App/Geometry.h | 12 ++ src/Mod/TechDraw/Gui/CommandCreateDims.cpp | 50 ++++++- 4 files changed, 259 insertions(+), 9 deletions(-) diff --git a/src/Mod/TechDraw/App/DrawViewDimension.cpp b/src/Mod/TechDraw/App/DrawViewDimension.cpp index 70d072b7bf..23be5bcc7d 100644 --- a/src/Mod/TechDraw/App/DrawViewDimension.cpp +++ b/src/Mod/TechDraw/App/DrawViewDimension.cpp @@ -268,6 +268,31 @@ App::DocumentObjectExecReturn *DrawViewDimension::execute(void) pts.onCurve.first = pts.center + Base::Vector3d(1,0,0) * circle->radius; //arbitrary point on edge pts.onCurve.second = pts.center + Base::Vector3d(-1,0,0) * circle->radius; //arbitrary point on edge } + } else if (base && base->geomType == TechDrawGeometry::GeomType::BSPLINE) { + TechDrawGeometry::BSpline* spline = static_cast (base); + if (spline->isCircle()) { + bool circ,arc; + double rad; + Base::Vector3d center; + spline->getCircleParms(circ,rad,center,arc); + pts.center = Base::Vector3d(center.x,center.y,0.0); + pts.radius = rad; + pts.arcEnds.first = Base::Vector3d(spline->startPnt.x,spline->startPnt.y,0.0); + pts.arcEnds.second = Base::Vector3d(spline->endPnt.x,spline->endPnt.y,0.0); + pts.midArc = Base::Vector3d(spline->midPnt.x,spline->midPnt.y,0.0); + pts.isArc = arc; + pts.arcCW = spline->cw; + if (arc) { + pts.onCurve.first = Base::Vector3d(spline->midPnt.x,spline->midPnt.y,0.0); + } else { + pts.onCurve.first = pts.center + Base::Vector3d(1,0,0) * rad; //arbitrary point on edge + pts.onCurve.second = pts.center + Base::Vector3d(-1,0,0) * rad; //arbitrary point on edge + } + } else { + //fubar - can't have non-circular spline as target of Diameter dimension + Base::Console().Error("Dimension %s refers to invalid BSpline\n",getNameInDocument()); + return App::DocumentObject::StdReturn; + } } else { Base::Console().Log("Error: DVD - %s - 2D references are corrupt\n",getNameInDocument()); return App::DocumentObject::StdReturn; @@ -285,7 +310,6 @@ App::DocumentObjectExecReturn *DrawViewDimension::execute(void) (base && base->geomType == TechDrawGeometry::GeomType::ARCOFCIRCLE)) { circle = static_cast (base); pts.center = Base::Vector3d(circle->center.x,circle->center.y,0.0); - pts.center = Base::Vector3d(circle->center.x,circle->center.y,0.0); pts.radius = circle->radius; if (base->geomType == TechDrawGeometry::GeomType::ARCOFCIRCLE) { TechDrawGeometry::AOC* aoc = static_cast (circle); @@ -300,6 +324,31 @@ App::DocumentObjectExecReturn *DrawViewDimension::execute(void) pts.onCurve.first = pts.center + Base::Vector3d(1,0,0) * circle->radius; //arbitrary point on edge pts.onCurve.second = pts.center + Base::Vector3d(-1,0,0) * circle->radius; //arbitrary point on edge } + } else if (base && base->geomType == TechDrawGeometry::GeomType::BSPLINE) { + TechDrawGeometry::BSpline* spline = static_cast (base); + if (spline->isCircle()) { + bool circ,arc; + double rad; + Base::Vector3d center; + spline->getCircleParms(circ,rad,center,arc); + pts.center = Base::Vector3d(center.x,center.y,0.0); + pts.radius = rad; + pts.arcEnds.first = Base::Vector3d(spline->startPnt.x,spline->startPnt.y,0.0); + pts.arcEnds.second = Base::Vector3d(spline->endPnt.x,spline->endPnt.y,0.0); + pts.midArc = Base::Vector3d(spline->midPnt.x,spline->midPnt.y,0.0); + pts.isArc = arc; + pts.arcCW = spline->cw; + if (arc) { + pts.onCurve.first = Base::Vector3d(spline->midPnt.x,spline->midPnt.y,0.0); + } else { + pts.onCurve.first = pts.center + Base::Vector3d(1,0,0) * rad; //arbitrary point on edge + pts.onCurve.second = pts.center + Base::Vector3d(-1,0,0) * rad; //arbitrary point on edge + } + } else { + //fubar - can't have non-circular spline as target of Diameter dimension + Base::Console().Error("Dimension %s refers to invalid BSpline\n",getNameInDocument()); + return App::DocumentObject::StdReturn; + } } else { Base::Console().Log("Error: DVD - %s - 2D references are corrupt\n",getNameInDocument()); return App::DocumentObject::StdReturn; @@ -760,7 +809,15 @@ bool DrawViewDimension::leaderIntersectsArc(Base::Vector3d s, Base::Vector3d poi if (aoc->intersectsArc(s,pointOnCircle)) { result = true; } + } else if( base && base->geomType == TechDrawGeometry::GeomType::BSPLINE ) { + TechDrawGeometry::BSpline* spline = static_cast (base); + if (spline->isCircle()) { + if (spline->intersectsArc(s,pointOnCircle)) { + result = true; + } + } } + return result; } diff --git a/src/Mod/TechDraw/App/Geometry.cpp b/src/Mod/TechDraw/App/Geometry.cpp index 343af36814..6471fc53cf 100644 --- a/src/Mod/TechDraw/App/Geometry.cpp +++ b/src/Mod/TechDraw/App/Geometry.cpp @@ -41,8 +41,11 @@ #include #include #include +#include +#include #include #include +#include #include #include #include @@ -58,6 +61,8 @@ #include #include +#include + #include "Geometry.h" #include "DrawUtil.h" @@ -236,7 +241,15 @@ BaseGeom* BaseGeom::baseFactory(TopoDS_Edge edge) result = gen; delete bspline; bspline = nullptr; - } else { + } else if (bspline->isCircle()) { +// bool circ,arc; +// double rad; +// Base::Vector3d center; +// bspline->getCircleParms(circ,rad,center,arc); +// Base::Console().Message("TRACE - GEO;:factory - spline is a Circle - radius: %.3f center: %s\n", +// rad, DrawUtil::formatVector(center).c_str()); + result = bspline; + }else { result = bspline; } break; @@ -470,10 +483,35 @@ BSpline::BSpline(const TopoDS_Edge &e) { geomType = BSPLINE; BRepAdaptor_Curve c(e); + isArc = !c.IsClosed(); + Handle(Geom_BSplineCurve) cSpline = c.BSpline(); occEdge = e; Handle(Geom_BSplineCurve) spline; + double f,l; - gp_Pnt s,ePt; + f = c.FirstParameter(); + l = c.LastParameter(); + gp_Pnt s = c.Value(f); + gp_Pnt m = c.Value((l+f)/2.0); + gp_Pnt ePt = c.Value(l); + startPnt = Base::Vector2d(s.X(), s.Y()); + endPnt = Base::Vector2d(ePt.X(), ePt.Y()); + midPnt = Base::Vector2d(m.X(), m.Y()); + gp_Vec v1(m,s); + gp_Vec v2(m,ePt); + gp_Vec v3(0,0,1); + double a = v3.DotCross(v1,v2); + cw = (a < 0) ? true: false; + + startAngle = atan2(startPnt.y,startPnt.x); + if (startAngle < 0) { + startAngle += 2.0 * M_PI; + } + endAngle = atan2(endPnt.y,endPnt.x); + if (endAngle < 0) { + endAngle += 2.0 * M_PI; + } + Standard_Real tol3D = 0.001; //1/1000 of a mm? screen can't resolve this Standard_Integer maxDegree = 3, maxSegment = 100; @@ -510,6 +548,8 @@ BSpline::BSpline(const TopoDS_Edge &e) if (bezier->Degree() > 3) { Base::Console().Log("Geometry::BSpline - converted curve degree > 3\n"); } +// Base::Console().Message("TRACE - Geo::BSpline - bezier degree: %d bezier poles: %d\n", +// bezier->Degree(),bezier->NbPoles()); tempSegment.poles = bezier->NbPoles(); tempSegment.degree = bezier->Degree(); for (int pole = 1; pole <= tempSegment.poles; ++pole) { @@ -533,6 +573,109 @@ bool BSpline::isLine() return result; } +bool BSpline::isCircle() +{ + bool result = false; + double radius; + Base::Vector3d center; + bool isArc = false; + getCircleParms(result,radius,center,isArc); + return result; +} + +void BSpline::getCircleParms(bool& isCircle, double& radius, Base::Vector3d& center, bool& isArc) +{ +// bool result = false; + double curveLimit = 0.0001; + BRepAdaptor_Curve c(occEdge); + Handle(Geom_BSplineCurve) spline = c.BSpline(); + double f,l; + f = c.FirstParameter(); + l = c.LastParameter(); + double parmRange = fabs(l - f); + int testCount = 6; + double parmStep = parmRange/testCount; + std::vector curvatures; + std::vector centers; + gp_Pnt curveCenter; + double sumCurvature = 0; + Base::Vector3d sumCenter, valueAt; + try { + GeomLProp_CLProps prop(spline,f,3,Precision::Confusion()); + curvatures.push_back(prop.Curvature()); + sumCurvature += prop.Curvature(); + prop.CentreOfCurvature(curveCenter); + centers.push_back(curveCenter); + sumCenter += Base::Vector3d(curveCenter.X(),curveCenter.Y(),curveCenter.Z()); + + for (int i = 1; i < (testCount - 1); i++) { + prop.SetParameter(parmStep * i); + curvatures.push_back(prop.Curvature()); + sumCurvature += prop.Curvature(); + prop.CentreOfCurvature(curveCenter); + centers.push_back(curveCenter); + sumCenter += Base::Vector3d(curveCenter.X(),curveCenter.Y(),curveCenter.Z()); + } + prop.SetParameter(l); + curvatures.push_back(prop.Curvature()); + sumCurvature += prop.Curvature(); + prop.CentreOfCurvature(curveCenter); + centers.push_back(curveCenter); + sumCenter += Base::Vector3d(curveCenter.X(),curveCenter.Y(),curveCenter.Z()); + } + catch (Standard_Failure& e) { + Base::Console().Log("TechDraw - GEO::BSpline::getCircleParms - CLProps failed\n"); + isCircle = false; + return; +// throw Base::RuntimeError(e.GetMessageString()); + } + Base::Vector3d avgCenter = sumCenter/testCount; + double errorCenter = 0; + for (auto& c: centers) { + errorCenter += (avgCenter - Base::Vector3d(c.X(), c.Y(), c.Z())).Length(); + } + errorCenter = errorCenter/testCount; + + double avgCurve = sumCurvature/testCount; + double errorCurve = 0; + for (auto& cv: curvatures) { + errorCurve += fabs(avgCurve - cv); //fabs??? + } + errorCurve = errorCurve/testCount; + + isArc = !c.IsClosed(); + isCircle = false; + if ( errorCurve < curveLimit ) { + isCircle = true; + radius = 1.0/avgCurve; + center = avgCenter; + } +} + +bool BSpline::intersectsArc(Base::Vector3d p1,Base::Vector3d p2) +{ + bool result = false; + double minDist = -1.0; + gp_Pnt pnt1(p1.x,p1.y,p1.z); + TopoDS_Vertex v1 = BRepBuilderAPI_MakeVertex(pnt1); + gp_Pnt pnt2(p2.x,p2.y,p2.z); + TopoDS_Vertex v2 = BRepBuilderAPI_MakeVertex(pnt2); + BRepBuilderAPI_MakeEdge mkEdge(v1,v2); + TopoDS_Edge line = mkEdge.Edge(); + BRepExtrema_DistShapeShape extss(occEdge, line); + if (extss.IsDone()) { + int count = extss.NbSolution(); + if (count != 0) { + minDist = extss.Value(); + if (minDist < Precision::Confusion()) { + result = true; + } + } + } + return result; +} + + BezierSegment::BezierSegment(const TopoDS_Edge &e) { geomType = BEZIER; diff --git a/src/Mod/TechDraw/App/Geometry.h b/src/Mod/TechDraw/App/Geometry.h index c6ddad278d..73495eb9d2 100644 --- a/src/Mod/TechDraw/App/Geometry.h +++ b/src/Mod/TechDraw/App/Geometry.h @@ -181,7 +181,19 @@ class TechDrawExport BSpline: public BaseGeom ~BSpline() = default; public: + Base::Vector2d startPnt; + Base::Vector2d endPnt; + Base::Vector2d midPnt; + double startAngle; + double endAngle; + /// Arc is drawn clockwise from startAngle to endAngle if true, counterclockwise if false + bool cw; + bool isArc; + bool isLine(void); + bool isCircle(void); + void getCircleParms(bool& isCircle, double& radius, Base::Vector3d& center, bool& isArc); + bool intersectsArc(Base::Vector3d p1,Base::Vector3d p2); std::vector segments; }; diff --git a/src/Mod/TechDraw/Gui/CommandCreateDims.cpp b/src/Mod/TechDraw/Gui/CommandCreateDims.cpp index 6708c8f23f..71dfc65384 100644 --- a/src/Mod/TechDraw/Gui/CommandCreateDims.cpp +++ b/src/Mod/TechDraw/Gui/CommandCreateDims.cpp @@ -75,7 +75,9 @@ enum EdgeType{ isVertical, isDiagonal, isCircle, - isCurve, + isEllipse, + isBSplineCircle, + isBSpline, isAngle }; @@ -280,6 +282,18 @@ void CmdTechDrawNewRadiusDimension::activated(int iMsg) if (edgeType == isCircle) { objs.push_back(objFeat); subs.push_back(SubNames[0]); + } else if (edgeType == isBSplineCircle) { + QMessageBox::StandardButton result = + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Selection Warning"), + QObject::tr("Selected edge is a BSpline. Radius will be approximate."), + QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Cancel); + if (result == QMessageBox::Ok) { + objs.push_back(objFeat); + subs.push_back(SubNames[0]); + } else { + return; + } } else { std::stringstream edgeMsg; edgeMsg << "Can't make a radius Dimension from this selection (edge type: " << _edgeTypeToText(edgeType) << ")"; @@ -368,6 +382,18 @@ void CmdTechDrawNewDiameterDimension::activated(int iMsg) if (edgeType == isCircle) { objs.push_back(objFeat); subs.push_back(SubNames[0]); + } else if (edgeType == isBSplineCircle) { + QMessageBox::StandardButton result = + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Selection Warning"), + QObject::tr("Selected edge is a BSpline. Diameter will be approximate."), + QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Cancel); + if (result == QMessageBox::Ok) { + objs.push_back(objFeat); + subs.push_back(SubNames[0]); + } else { + return; + } } else { std::stringstream edgeMsg; edgeMsg << "Can't make a diameter Dimension from this selection (edge type: " << _edgeTypeToText(edgeType) << ")"; @@ -1020,9 +1046,15 @@ int _isValidSingleEdge(Gui::Command* cmd) { geom->geomType == TechDrawGeometry::ARCOFCIRCLE ) { edgeType = isCircle; } else if (geom->geomType == TechDrawGeometry::ELLIPSE || - geom->geomType == TechDrawGeometry::ARCOFELLIPSE || - geom->geomType == TechDrawGeometry::BSPLINE) { - edgeType = isCurve; + geom->geomType == TechDrawGeometry::ARCOFELLIPSE) { + edgeType = isEllipse; + } else if (geom->geomType == TechDrawGeometry::BSPLINE) { + TechDrawGeometry::BSpline* spline = static_cast(geom); + if (spline->isCircle()) { + edgeType = isBSplineCircle; + } else { + edgeType = isBSpline; + } } else { edgeType = isInvalid; } @@ -1154,8 +1186,14 @@ char* _edgeTypeToText(int e) case isCircle: result = "circle"; break; - case isCurve: - result = "curve"; + case isEllipse: + result = "ellipse"; + break; + case isBSpline: + result = "bspline"; + break; + case isBSplineCircle: + result = "circular bspline"; break; case isAngle: result = "angle";