/*************************************************************************** * Copyright (c) 2011 Werner Mayer * * * * 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 * * * ***************************************************************************/ //this file originally part of TechDraw workbench //migrated to TechDraw workbench 2022-01-26 by Wandererfan #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 # if OCC_VERSION_HEX < 0x070600 # include # endif #endif #include #include "TechDrawExport.h" #if OCC_VERSION_HEX >= 0x070600 using BRepAdaptor_HCurve = BRepAdaptor_Curve; #endif using namespace TechDraw; using namespace std; TopoDS_Edge TechDrawOutput::asCircle(const BRepAdaptor_Curve& c) const { double curv=0; gp_Pnt pnt, center; try { // approximate the circle center from three positions BRepLProp_CLProps prop(c, c.FirstParameter(), 2,Precision::Confusion()); curv += prop.Curvature(); prop.CentreOfCurvature(pnt); center.ChangeCoord().Add(pnt.Coord()); prop.SetParameter(0.5*(c.FirstParameter()+c.LastParameter())); curv += prop.Curvature(); prop.CentreOfCurvature(pnt); center.ChangeCoord().Add(pnt.Coord()); prop.SetParameter(c.LastParameter()); curv += prop.Curvature(); prop.CentreOfCurvature(pnt); center.ChangeCoord().Add(pnt.Coord()); center.ChangeCoord().Divide(3); curv /= 3; } catch (Standard_Failure&) { // if getting center of curvature fails, e.g. // for straight lines it raises LProp_NotDefined return TopoDS_Edge(); } // get circle from curvature information double radius = 1 / curv; TopLoc_Location location; Handle(Poly_Polygon3D) polygon = BRep_Tool::Polygon3D(c.Edge(), location); if (!polygon.IsNull()) { const TColgp_Array1OfPnt& nodes = polygon->Nodes(); for (int i = nodes.Lower(); i <= nodes.Upper(); i++) { gp_Pnt p = nodes(i); double dist = p.Distance(center); if (std::abs(dist - radius) > 0.001) return TopoDS_Edge(); } gp_Circ circ; circ.SetLocation(center); circ.SetRadius(radius); gp_Pnt p1 = nodes(nodes.Lower()); gp_Pnt p2 = nodes(nodes.Upper()); double dist = p1.Distance(p2); if (dist < Precision::Confusion()) { BRepBuilderAPI_MakeEdge mkEdge(circ); return mkEdge.Edge(); } else { gp_Vec dir1(center, p1); dir1.Normalize(); gp_Vec dir2(center, p2); dir2.Normalize(); p1 = gp_Pnt(center.XYZ() + radius * dir1.XYZ()); p2 = gp_Pnt(center.XYZ() + radius * dir2.XYZ()); BRepBuilderAPI_MakeEdge mkEdge(circ, p1, p2); return mkEdge.Edge(); } } return TopoDS_Edge(); } TopoDS_Edge TechDrawOutput::asBSpline(const BRepAdaptor_Curve& c, int maxDegree) const { Standard_Real tol3D = 0.001; Standard_Integer maxSegment = 50; Handle(BRepAdaptor_HCurve) hCurve = new BRepAdaptor_HCurve(c); // approximate the curve using a tolerance Approx_Curve3d approx(hCurve, tol3D, GeomAbs_C0, maxSegment, maxDegree); if (approx.IsDone() && approx.HasResult()) { // have the result Handle(Geom_BSplineCurve) spline = approx.Curve(); BRepBuilderAPI_MakeEdge mkEdge(spline, spline->FirstParameter(), spline->LastParameter()); return mkEdge.Edge(); } return TopoDS_Edge(); } SVGOutput::SVGOutput() { } std::string SVGOutput::exportEdges(const TopoDS_Shape& input) { std::stringstream result; TopExp_Explorer edges(input, TopAbs_EDGE); for (int i = 1 ; edges.More(); edges.Next(), i++) { const TopoDS_Edge& edge = TopoDS::Edge(edges.Current()); BRepAdaptor_Curve adapt(edge); if (adapt.GetType() == GeomAbs_Circle) { printCircle(adapt, result); } else if (adapt.GetType() == GeomAbs_Ellipse) { printEllipse(adapt, i, result); } else if (adapt.GetType() == GeomAbs_BSplineCurve) { // TopoDS_Edge circle = asCircle(adapt); // if (circle.IsNull()) { printBSpline(adapt, i, result); // } // else { // BRepAdaptor_Curve adapt_circle(circle); // printCircle(adapt_circle, result); // } } else if (adapt.GetType() == GeomAbs_BezierCurve) { printBezier(adapt, i, result); } // fallback else { printGeneric(adapt, i, result); } } return result.str(); } void SVGOutput::printCircle(const BRepAdaptor_Curve& c, std::ostream& out) { gp_Circ circ = c.Circle(); const gp_Pnt& p= circ.Location(); double r = circ.Radius(); double f = c.FirstParameter(); double l = c.LastParameter(); gp_Pnt s = c.Value(f); gp_Pnt m = c.Value((l+f)/2.0); gp_Pnt e = c.Value(l); gp_Vec v1(m, s); gp_Vec v2(m, e); gp_Vec v3(0, 0,1); double a = v3.DotCross(v1, v2); // a full circle if (fabs(l-f) > 1.0 && s.SquareDistance(e) < 0.001) { out << ""; } // arc of circle else { // See also https://developer.mozilla.org/en/SVG/Tutorial/Paths char xar = '0'; // x-axis-rotation char las = (l-f > M_PI) ? '1' : '0'; // large-arc-flag char swp = (a < 0) ? '1' : '0'; // sweep-flag, i.e. clockwise (0) or counter-clockwise (1) out << ""; } } void SVGOutput::printEllipse(const BRepAdaptor_Curve& c, int id, std::ostream& out) { gp_Elips ellp = c.Ellipse(); const gp_Pnt& p= ellp.Location(); double r1 = ellp.MajorRadius(); double r2 = ellp.MinorRadius(); double f = c.FirstParameter(); double l = c.LastParameter(); gp_Pnt s = c.Value(f); gp_Pnt m = c.Value((l+f)/2.0); gp_Pnt e = c.Value(l); // If the minor radius is very small compared to the major radius // the geometry actually degenerates to a line double ratio = std::min(r1, r2)/std::max(r1, r2); if (ratio < 0.001) { printGeneric(c, id, out); return; } gp_Vec v1(m, s); gp_Vec v2(m, e); gp_Vec v3(0, 0,1); double a = v3.DotCross(v1, v2); // a full ellipse // See also https://developer.mozilla.org/en/SVG/Tutorial/Paths gp_Dir xaxis = ellp.XAxis().Direction(); Standard_Real angle = xaxis.AngleWithRef(gp_Dir(1, 0,0), gp_Dir(0, 0,-1)); angle = Base::toDegrees(angle); if (fabs(l-f) > 1.0 && s.SquareDistance(e) < 0.001) { out << "" << std::endl; out << "" << std::endl; out << "" << std::endl; } // arc of ellipse else { char las = (l-f > M_PI) ? '1' : '0'; // large-arc-flag char swp = (a < 0) ? '1' : '0'; // sweep-flag, i.e. clockwise (0) or counter-clockwise (1) out << "" << std::endl; } } void SVGOutput::printBezier(const BRepAdaptor_Curve& c, int id, std::ostream& out) { try { std::stringstream str; str << "NbPoles(); // if its a bezier with degree higher than 3 convert it into a B-spline if (bezier->Degree() > 3 || bezier->IsRational()) { TopoDS_Edge edge = asBSpline(c, 3); if (!edge.IsNull()) { BRepAdaptor_Curve spline(edge); printBSpline(spline, id, out); } else { Standard_Failure::Raise("do it the generic way"); } return; } gp_Pnt p1 = bezier->Pole(1); str << p1.X() << ", " << p1.Y(); if (bezier->Degree() == 3) { if (poles != 4) Standard_Failure::Raise("do it the generic way"); gp_Pnt p2 = bezier->Pole(2); gp_Pnt p3 = bezier->Pole(3); gp_Pnt p4 = bezier->Pole(4); str << " C" << p2.X() << ", " << p2.Y() << " " << p3.X() << ", " << p3.Y() << " " << p4.X() << ", " << p4.Y() << " "; } else if (bezier->Degree() == 2) { if (poles != 3) Standard_Failure::Raise("do it the generic way"); gp_Pnt p2 = bezier->Pole(2); gp_Pnt p3 = bezier->Pole(3); str << " Q" << p2.X() << ", " << p2.Y() << " " << p3.X() << ", " << p3.Y() << " "; } else if (bezier->Degree() == 1) { if (poles != 2) Standard_Failure::Raise("do it the generic way"); gp_Pnt p2 = bezier->Pole(2); str << " L" << p2.X() << ", " << p2.Y() << " "; } else { Standard_Failure::Raise("do it the generic way"); } str << "\" />"; out << str.str(); } catch (Standard_Failure&) { printGeneric(c, id, out); } } void SVGOutput::printBSpline(const BRepAdaptor_Curve& c, int id, std::ostream& out) { try { std::stringstream str; Handle(Geom_BSplineCurve) spline; Standard_Real tol3D = 0.001; Standard_Integer maxDegree = 3, maxSegment = 100; Handle(BRepAdaptor_HCurve) hCurve = new BRepAdaptor_HCurve(c); // approximate the curve using a tolerance Approx_Curve3d approx(hCurve, tol3D, GeomAbs_C0, maxSegment, maxDegree); if (approx.IsDone() && approx.HasResult()) { // have the result spline = approx.Curve(); } else { printGeneric(c, id, out); return; } GeomConvert_BSplineCurveToBezierCurve crt(spline); Standard_Integer arcs = crt.NbArcs(); str << "NbPoles(); if (i == 1) { gp_Pnt p1 = bezier->Pole(1); str << p1.X() << ", " << p1.Y(); } if (bezier->Degree() == 3) { if (poles != 4) Standard_Failure::Raise("do it the generic way"); gp_Pnt p2 = bezier->Pole(2); gp_Pnt p3 = bezier->Pole(3); gp_Pnt p4 = bezier->Pole(4); str << " C" << p2.X() << ", " << p2.Y() << " " << p3.X() << ", " << p3.Y() << " " << p4.X() << ", " << p4.Y() << " "; } else if (bezier->Degree() == 2) { if (poles != 3) Standard_Failure::Raise("do it the generic way"); gp_Pnt p2 = bezier->Pole(2); gp_Pnt p3 = bezier->Pole(3); str << " Q" << p2.X() << ", " << p2.Y() << " " << p3.X() << ", " << p3.Y() << " "; } else if (bezier->Degree() == 1) { if (poles != 2) Standard_Failure::Raise("do it the generic way"); gp_Pnt p2 = bezier->Pole(2); str << " L" << p2.X() << ", " << p2.Y() << " "; } else { Standard_Failure::Raise("do it the generic way"); } } str << "\" />"; out << str.str(); } catch (Standard_Failure&) { printGeneric(c, id, out); } } void SVGOutput::printGeneric(const BRepAdaptor_Curve& bac, int id, std::ostream& out) { TopLoc_Location location; Handle(Poly_Polygon3D) polygon = BRep_Tool::Polygon3D(bac.Edge(), location); if (!polygon.IsNull()) { const TColgp_Array1OfPnt& nodes = polygon->Nodes(); char c = 'M'; out << "" << endl; } else if (bac.GetType() == GeomAbs_Line) { //BRep_Tool::Polygon3D assumes the edge has polygon representation - ie already been "tessellated" //this is not true for all edges, especially "floating edges" double f = bac.FirstParameter(); double l = bac.LastParameter(); gp_Pnt s = bac.Value(f); gp_Pnt e = bac.Value(l); char c = 'M'; out << "" << endl; } } // ---------------------------------------------------------------------------- DXFOutput::DXFOutput() { } std::string DXFOutput::exportEdges(const TopoDS_Shape& input) { std::stringstream result; TopExp_Explorer edges(input, TopAbs_EDGE); for (int i = 1 ; edges.More(); edges.Next(), i++) { const TopoDS_Edge& edge = TopoDS::Edge(edges.Current()); BRepAdaptor_Curve adapt(edge); if (adapt.GetType() == GeomAbs_Circle) { printCircle(adapt, result); } else if (adapt.GetType() == GeomAbs_Ellipse) { printEllipse(adapt, i, result); } else if (adapt.GetType() == GeomAbs_BSplineCurve) { printBSpline(adapt, i, result); } // fallback else { printGeneric(adapt, i, result); } } return result.str(); } void DXFOutput::printHeader( std::ostream& out) { out << 0 << endl; out << "SECTION" << endl; out << 2 << endl; out << "ENTITIES" << endl; } void DXFOutput::printCircle(const BRepAdaptor_Curve& c, std::ostream& out) { gp_Circ circ = c.Circle(); //const gp_Ax1& axis = c->Axis(); const gp_Pnt& p= circ.Location(); double r = circ.Radius(); double f = c.FirstParameter(); double l = c.LastParameter(); gp_Pnt s = c.Value(f); gp_Pnt m = c.Value((l+f)/2.0); gp_Pnt e = c.Value(l); gp_Vec v1(m, s); gp_Vec v2(m, e); gp_Vec v3(0, 0,1); double a = v3.DotCross(v1, v2); // a full circle if (s.SquareDistance(e) < 0.001) { //out << ""; out << 0 << endl; out << "CIRCLE" << endl; out << 8 << endl; // Group code for layer name out << "sheet_layer" << endl; // Layer number out << "100" << endl; out << "AcDbEntity" << endl; out << "100" << endl; out << "AcDbCircle" << endl; out << 10 << endl; // Centre X out << p.X() << endl; // X in WCS coordinates out << 20 << endl; out << p.Y() << endl; // Y in WCS coordinates out << 30 << endl; out << 0 << endl; // Z in WCS coordinates-leaving flat out << 40 << endl; // out << r << endl; // Radius } // arc of circle else { // See also https://developer.mozilla.org/en/SVG/Tutorial/Paths /*char xar = '0'; // x-axis-rotation char las = (l-f > M_PI) ? '1' : '0'; // large-arc-flag char swp = (a < 0) ? '1' : '0'; // sweep-flag, i.e. clockwise (0) or counter-clockwise (1) out << "";*/ double ax = s.X() - p.X(); double ay = s.Y() - p.Y(); double bx = e.X() - p.X(); double by = e.Y() - p.Y(); double start_angle = atan2(ay, ax) * 180 / M_PI; double end_angle = atan2(by, bx) * 180 / M_PI; if (a > 0) { double temp = start_angle; start_angle = end_angle; end_angle = temp;} out << 0 << endl; out << "ARC" << endl; out << 8 << endl; // Group code for layer name out << "sheet_layer" << endl; // Layer number out << "100" << endl; out << "AcDbEntity" << endl; out << "100" << endl; out << "AcDbCircle" << endl; out << 10 << endl; // Centre X out << p.X() << endl; // X in WCS coordinates out << 20 << endl; out << p.Y() << endl; // Y in WCS coordinates out << 30 << endl; out << 0 << endl; // Z in WCS coordinates out << 40 << endl; // out << r << endl; // Radius out << "100" << endl; out << "AcDbArc" << endl; out << 50 << endl; out << start_angle << endl; // Start angle out << 51 << endl; out << end_angle << endl; // End angle } } void DXFOutput::printEllipse(const BRepAdaptor_Curve& c, int /*id*/, std::ostream& out) { gp_Elips ellp = c.Ellipse(); const gp_Pnt& p= ellp.Location(); double r1 = ellp.MajorRadius(); double r2 = ellp.MinorRadius(); double dp = ellp.Axis().Direction().Dot(gp_Vec(0, 0,1)); // a full ellipse /* if (s.SquareDistance(e) < 0.001) { out << ""; } // arc of ellipse else { // See also https://developer.mozilla.org/en/SVG/Tutorial/Paths gp_Dir xaxis = ellp.XAxis().Direction(); Standard_Real angle = xaxis.Angle(gp_Dir(1, 0,0)); angle = Base::toDegrees(angle); char las = (l-f > D_PI) ? '1' : '0'; // large-arc-flag char swp = (a < 0) ? '1' : '0'; // sweep-flag, i.e. clockwise (0) or counter-clockwise (1) out << ""; }*/ gp_Dir xaxis = ellp.XAxis().Direction(); double angle = xaxis.AngleWithRef(gp_Dir(1, 0,0), gp_Dir(0, 0,-1)); //double rotation = Base::toDegrees(angle); double start_angle = c.FirstParameter(); double end_angle = c.LastParameter(); double major_x;double major_y; major_x = r1 * cos(angle); major_y = r1 * sin(angle); double ratio = r2/r1; if(dp < 0){ double temp = start_angle; start_angle = end_angle; end_angle = temp; } out << 0 << endl; out << "ELLIPSE" << endl; out << 8 << endl; // Group code for layer name out << "sheet_layer" << endl; // Layer number out << "100" << endl; out << "AcDbEntity" << endl; out << "100" << endl; out << "AcDbEllipse" << endl; out << 10 << endl; // Centre X out << p.X() << endl; // X in WCS coordinates out << 20 << endl; out << p.Y() << endl; // Y in WCS coordinates out << 30 << endl; out << 0 << endl; // Z in WCS coordinates out << 11 << endl; // out << major_x << endl; // Major X out << 21 << endl; out << major_y << endl; // Major Y out << 31 << endl; out << 0 << endl; // Major Z out << 40 << endl; // out << ratio << endl; // Ratio out << 41 << endl; out << start_angle << endl; // Start angle out << 42 << endl; out << end_angle << endl; // End angle } void DXFOutput::printBSpline(const BRepAdaptor_Curve& c, int id, std::ostream& out) //Not even close yet- DF { try { std::stringstream str; Handle(Geom_BSplineCurve) spline; Standard_Real tol3D = 0.001; Standard_Integer maxDegree = 3, maxSegment = 50; Handle(BRepAdaptor_HCurve) hCurve = new BRepAdaptor_HCurve(c); // approximate the curve using a tolerance Approx_Curve3d approx(hCurve, tol3D, GeomAbs_C0, maxSegment, maxDegree); if (approx.IsDone() && approx.HasResult()) { // have the result spline = approx.Curve(); } else { printGeneric(c, id, out); return; } //GeomConvert_BSplineCurveToBezierCurve crt(spline); //GeomConvert_BSplineCurveKnotSplitting crt(spline, 0); //Standard_Integer arcs = crt.NbArcs(); //Standard_Integer arcs = crt.NbSplits()-1; Standard_Integer m = 0; if (spline->IsPeriodic()) { m = spline->NbPoles() + 2*spline->Degree() - spline->Multiplicity(1) + 2; } else { for (int i=1; i<= spline->NbKnots(); i++) m += spline->Multiplicity(i); } TColStd_Array1OfReal knotsequence(1, m); spline->KnotSequence(knotsequence); TColgp_Array1OfPnt poles(1, spline->NbPoles()); spline->Poles(poles); str << 0 << endl << "SPLINE" << endl << 8 << endl // Group code for layer name << "sheet_layer" << endl // Layer name << "100" << endl << "AcDbEntity" << endl << "100" << endl << "AcDbSpline" << endl << 70 << endl << spline->IsRational()*4 << endl //flags << 71 << endl << spline->Degree() << endl << 72 << endl << knotsequence.Length() << endl << 73 << endl << poles.Length() << endl << 74 << endl << 0 << endl; //fitpoints for (int i = knotsequence.Lower() ; i <= knotsequence.Upper(); i++) { str << 40 << endl << knotsequence(i) << endl; } for (int i = poles.Lower(); i <= poles.Upper(); i++) { gp_Pnt pole = poles(i); str << 10 << endl << pole.X() << endl << 20 << endl << pole.Y() << endl << 30 << endl << pole.Z() << endl; if (spline->IsRational()) { str << 41 << endl << spline->Weight(i) << endl; } } //str << "\" />"; out << str.str(); } catch (Standard_Failure&) { printGeneric(c, id, out); } } void DXFOutput::printGeneric(const BRepAdaptor_Curve& c, int /*id*/, std::ostream& out) { double uStart = c.FirstParameter(); gp_Pnt PS; gp_Vec VS; c.D1(uStart, PS, VS); double uEnd = c.LastParameter(); gp_Pnt PE; gp_Vec VE; c.D1(uEnd, PE, VE); out << "0" << endl; out << "LINE" << endl; out << "8" << endl; // Group code for layer name out << "sheet_layer" << endl; // Layer name out << "100" << endl; out << "AcDbEntity" << endl; out << "100" << endl; out << "AcDbLine" << endl; out << "10" << endl; // Start point of line out << PS.X() << endl; // X in WCS coordinates out << "20" << endl; out << PS.Y() << endl; // Y in WCS coordinates out << "30" << endl; out << "0" << endl; // Z in WCS coordinates out << "11" << endl; // End point of line out << PE.X() << endl; // X in WCS coordinates out << "21" << endl; out << PE.Y() << endl; // Y in WCS coordinates out << "31" << endl; out << "0" << endl; // Z in WCS coordinates }