From 9633c9451551ac7339e2e501aa5a04a67e0bba07 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 13 Apr 2017 17:49:36 +0200 Subject: [PATCH] ProjectionAlgos: Make the style of the lines in the SVG file configurable. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * getSVG provides new style parameters for every kind of line: V, V0, V1, H, H0, H1. * Old line width parameters are removed. The style parameters can be used instead. * A style is a map container for svg attribute keys and values (string, string). * The Python interface is updated to offer the new style parameters accordingly as a dict. * Because there are many parameters on the function call now, the Python interface supports keyword parameters. * Update ArchSectionPlane to take advantage of the new style parameters. This simplifies the code. String replacements could be removed (done in a later commit). * FeatureViewPy.cpp is – to my knowledge – the only function that used the old line width parameters. I rewrote it to use the new style parameters. --- src/Mod/Arch/ArchSectionPlane.py | 40 +++-- src/Mod/Drawing/App/AppDrawingPy.cpp | 121 ++++++++++++--- src/Mod/Drawing/App/FeatureViewPart.cpp | 8 +- src/Mod/Drawing/App/ProjectionAlgos.cpp | 195 +++++++++++------------- src/Mod/Drawing/App/ProjectionAlgos.h | 10 +- 5 files changed, 223 insertions(+), 151 deletions(-) diff --git a/src/Mod/Arch/ArchSectionPlane.py b/src/Mod/Arch/ArchSectionPlane.py index 28d24e0998..9d1b886779 100644 --- a/src/Mod/Arch/ArchSectionPlane.py +++ b/src/Mod/Arch/ArchSectionPlane.py @@ -196,21 +196,21 @@ def getSVG(section,allOn=False,renderMode="Wireframe",showHidden=False,showFill= shapes,hshapes,sshapes,cutface,cutvolume,invcutvolume = getCutShapes(objs,section,showHidden) if shapes: baseshape = Part.makeCompound(shapes) - svgf = Drawing.projectToSVG(baseshape,direction) - if svgf: - svgf = svgf.replace('stroke-width="0.35"','stroke-width="LWPlaceholder"') - svgf = svgf.replace('stroke-width="1"','stroke-width="LWPlaceholder"') - svgf = svgf.replace('stroke-width:0.01','stroke-width:LWPlaceholder') - svg += svgf + style = {'stroke-width': 'LWPlaceholder'} + svgf = Drawing.projectToSVG( + baseshape, direction, + hStyle=style, h0Style=style, h1Style=style, + vStyle=style, v0Style=style, v1Style=style) + svg += svgf if hshapes: hshapes = Part.makeCompound(hshapes) - svgh = Drawing.projectToSVG(hshapes,direction) - if svgh: - svgh = svgh.replace('stroke-width="0.35"','stroke-width="LWPlaceholder"') - svgh = svgh.replace('stroke-width="1"','stroke-width="LWPlaceholder"') - svgh = svgh.replace('stroke-width:0.01','stroke-width:LWPlaceholder') - svgh = svgh.replace('fill="none"','fill="none"\nstroke-dasharray="DAPlaceholder"') - svg += svgh + style = {'stroke-width': 'LWPlaceholder', + 'stroke-dasharray': 'DAPlaceholder'} + svgh = Drawing.projectToSVG( + hshapes, direction, + hStyle=style, h0Style=style, h1Style=style, + vStyle=style, v0Style=style, v1Style=style) + svg += svgh if sshapes: svgs = "" if showFill: @@ -224,14 +224,12 @@ def getSVG(section,allOn=False,renderMode="Wireframe",showHidden=False,showFill= svgs += f svgs += "\n" sshapes = Part.makeCompound(sshapes) - svgs += Drawing.projectToSVG(sshapes,direction) - if svgs: - svgs = svgs.replace('stroke-width="0.35"','stroke-width="SWPlaceholder"') - svgs = svgs.replace('stroke-width="1"','stroke-width="SWPlaceholder"') - svgs = svgs.replace('stroke-width:0.01','stroke-width:SWPlaceholder') - svgs = svgs.replace('stroke-width="0.35 px"','stroke-width="SWPlaceholder"') - svgs = svgs.replace('stroke-width:0.35','stroke-width:SWPlaceholder') - svg += svgs + style = {'stroke-width': 'SWPlaceholder'} + svgs += Drawing.projectToSVG( + sshapes, direction, + hStyle=style, h0Style=style, h1Style=style, + vStyle=style, v0Style=style, v1Style=style) + svg += svgs scaledlinewidth = linewidth/scale st = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetFloat("CutLineThickness",2) yt = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetFloat("SymbolLineThickness",0.6) diff --git a/src/Mod/Drawing/App/AppDrawingPy.cpp b/src/Mod/Drawing/App/AppDrawingPy.cpp index e3561d8731..c6819204ba 100644 --- a/src/Mod/Drawing/App/AppDrawingPy.cpp +++ b/src/Mod/Drawing/App/AppDrawingPy.cpp @@ -37,10 +37,39 @@ #include +using namespace std; using Part::TopoShapePy; using Part::TopoShape; namespace Drawing { + + /** Copies a Python dictionary of Python strings to a C++ container. + * + * After the function call, the key-value pairs of the Python + * dictionary are copied into the target buffer as C++ pairs + * (pair). + * + * @param sourceRange is a Python dictionary (Py::Dict). Both, the + * keys and the values must be Python strings. + * + * @param targetIt refers to where the data should be inserted. Must + * be of concept output iterator. + */ + template + void copy(Py::Dict sourceRange, OutputIt targetIt) + { + string key; + string value; + + for (auto keyPy : sourceRange.keys()) { + key = Py::String(keyPy); + value = Py::String(sourceRange[keyPy]); + *targetIt = {key, value}; + ++targetIt; + } + } + + class Module : public Py::ExtensionModule { public: @@ -54,8 +83,8 @@ public: "[V,V1,VN,VO,VI,H,H1,HN,HO,HI] = projectEx(TopoShape[,App.Vector Direction, string type])\n" " -- Project a shape and return the all parts of it." ); - add_varargs_method("projectToSVG",&Module::projectToSVG, - "string = projectToSVG(TopoShape[,App.Vector Direction, string type])\n" + add_keyword_method("projectToSVG",&Module::projectToSVG, + "string = projectToSVG(TopoShape[, App.Vector direction, string type, float tolerance, dict vStyle, dict v0Style, dict v1Style, dict hStyle, dict h0Style, dict h1Style])\n" " -- Project a shape and return the SVG representation as string." ); add_varargs_method("projectToDXF",&Module::projectToDXF, @@ -160,32 +189,76 @@ private: return list; } - Py::Object projectToSVG(const Py::Tuple& args) - { - PyObject *pcObjShape; - PyObject *pcObjDir=0; - const char *type=0; - float scale=1.0f; - float tol=0.1f; - if (!PyArg_ParseTuple(args.ptr(), "O!|O!sff", - &(TopoShapePy::Type), &pcObjShape, - &(Base::VectorPy::Type), &pcObjDir, &type, &scale, &tol)) - throw Py::Exception(); + Py::Object projectToSVG(const Py::Tuple& args, const Py::Dict& keys) + { + static char* argNames[] = {"topoShape", "direction", "type", "tolerance", "vStyle", "v0Style", "v1Style", "hStyle", "h0Style", "h1Style", NULL}; + PyObject *pcObjShape = 0; + PyObject *pcObjDir = 0; + const char *extractionTypePy = 0; + ProjectionAlgos::ExtractionType extractionType = ProjectionAlgos::Plain; + const float tol = 0.1f; + PyObject* vStylePy = 0; + ProjectionAlgos::XmlAttributes vStyle; + PyObject* v0StylePy = 0; + ProjectionAlgos::XmlAttributes v0Style; + PyObject* v1StylePy = 0; + ProjectionAlgos::XmlAttributes v1Style; + PyObject* hStylePy = 0; + ProjectionAlgos::XmlAttributes hStyle; + PyObject* h0StylePy = 0; + ProjectionAlgos::XmlAttributes h0Style; + PyObject* h1StylePy = 0; + ProjectionAlgos::XmlAttributes h1Style; + + // Get the arguments - TopoShapePy* pShape = static_cast(pcObjShape); - Base::Vector3d Vector(0,0,1); - if (pcObjDir) - Vector = static_cast(pcObjDir)->value(); - ProjectionAlgos Alg(pShape->getTopoShapePtr()->getShape(),Vector); + if (!PyArg_ParseTupleAndKeywords( + args.ptr(), keys.ptr(), + "O!|O!sfOOOOOO", + argNames, + &(TopoShapePy::Type), &pcObjShape, + &(Base::VectorPy::Type), &pcObjDir, + &extractionTypePy, &tol, + &vStylePy, &v0StylePy, &v1StylePy, + &hStylePy, &h0StylePy, &h1StylePy)) + + throw Py::Exception(); - bool hidden = false; - if (type && std::string(type) == "ShowHiddenLines") - hidden = true; + // Convert all arguments into the right format + + TopoShapePy* pShape = static_cast(pcObjShape); + + Base::Vector3d directionVector(0,0,1); + if (pcObjDir) + directionVector = static_cast(pcObjDir)->value(); + + if (extractionTypePy && string(extractionTypePy) == "ShowHiddenLines") + extractionType = ProjectionAlgos::WithHidden; + + if (vStylePy) + copy(Py::Dict(vStylePy), inserter(vStyle, vStyle.begin())); + if (v0StylePy) + copy(Py::Dict(v0StylePy), inserter(v0Style, v0Style.begin())); + if (v1StylePy) + copy(Py::Dict(v1StylePy), inserter(v1Style, v1Style.begin())); + if (hStylePy) + copy(Py::Dict(hStylePy), inserter(hStyle, hStyle.begin())); + if (h0StylePy) + copy(Py::Dict(h0StylePy), inserter(h0Style, h0Style.begin())); + if (h1StylePy) + copy(Py::Dict(h1StylePy), inserter(h1Style, h1Style.begin())); + + // Execute the SVG generation + + ProjectionAlgos Alg(pShape->getTopoShapePtr()->getShape(), + directionVector); + Py::String result(Alg.getSVG(extractionType, tol, + vStyle, v0Style, v1Style, + hStyle, h0Style, h1Style)); + return result; + } - Py::String result(Alg.getSVG(hidden?ProjectionAlgos::WithHidden:ProjectionAlgos::Plain, scale, tol)); - return result; - } Py::Object projectToDXF(const Py::Tuple& args) { PyObject *pcObjShape; diff --git a/src/Mod/Drawing/App/FeatureViewPart.cpp b/src/Mod/Drawing/App/FeatureViewPart.cpp index d58ac7dfcc..3427ffd1cb 100644 --- a/src/Mod/Drawing/App/FeatureViewPart.cpp +++ b/src/Mod/Drawing/App/FeatureViewPart.cpp @@ -120,7 +120,13 @@ App::DocumentObjectExecReturn *FeatureViewPart::execute(void) ProjectionAlgos::ExtractionType type = ProjectionAlgos::Plain; if (hidden) type = (ProjectionAlgos::ExtractionType)(type|ProjectionAlgos::WithHidden); if (smooth) type = (ProjectionAlgos::ExtractionType)(type|ProjectionAlgos::WithSmooth); - result << Alg.getSVG(type, this->LineWidth.getValue() / this->Scale.getValue(), this->Tolerance.getValue(), this->HiddenWidth.getValue() / this->Scale.getValue()); + ProjectionAlgos::XmlAttributes visible_style = { + {"stroke_width", to_string(this->LineWidth.getValue() / this->Scale.getValue())} + }; + ProjectionAlgos::XmlAttributes hidden_style = { + {"stroke_width", to_string(this->HiddenWidth.getValue() / this->Scale.getValue()) } + }; + result << Alg.getSVG(type, this->Tolerance.getValue(), visible_style, visible_style, visible_style, hidden_style, hidden_style, hidden_style); result << "" << endl; diff --git a/src/Mod/Drawing/App/ProjectionAlgos.cpp b/src/Mod/Drawing/App/ProjectionAlgos.cpp index f8335fc452..9afae7e9c0 100644 --- a/src/Mod/Drawing/App/ProjectionAlgos.cpp +++ b/src/Mod/Drawing/App/ProjectionAlgos.cpp @@ -37,7 +37,6 @@ #include #include #include -//#include #include #include #include @@ -99,29 +98,6 @@ ProjectionAlgos::~ProjectionAlgos() { } -/* -// no longer used, replaced invertY by adding -// << " transform=\"scale(1,-1)\"" << endl -// to getSVG(...) below. -// invertY, as here, wasn't right for intended purpose - always reflected in model Y direction rather -// than SVG projection Y direction. Also better to reflect about (0,0,0) rather than bbox centre - -TopoDS_Shape ProjectionAlgos::invertY(const TopoDS_Shape& shape) -{ - // make sure to have the y coordinates inverted - gp_Trsf mat; - Bnd_Box bounds; - BRepBndLib::Add(shape, bounds); - bounds.SetGap(0.0); - Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; - bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax); - mat.SetMirror(gp_Ax2(gp_Pnt((xMin+xMax)/2,(yMin+yMax)/2,(zMin+zMax)/2), gp_Dir(0,1,0))); - BRepBuilderAPI_Transform mkTrf(shape, mat); - return mkTrf.Shape(); -} -*/ - - //added by tanderson. aka blobfish. //projection algorithms build a 2d curve(pcurve) but no 3d curve. //this causes problems with meshing algorithms after save and load. @@ -159,112 +135,125 @@ void ProjectionAlgos::execute(void) HI = build3dCurves(shapes.IsoLineHCompound());// isoparamtriques invisibly } -std::string ProjectionAlgos::getSVG(ExtractionType type, double scale, double tolerance, double hiddenscale) +string ProjectionAlgos::getSVG(ExtractionType type, + double tolerance, + XmlAttributes V_style, + XmlAttributes V0_style, + XmlAttributes V1_style, + XmlAttributes H_style, + XmlAttributes H0_style, + XmlAttributes H1_style) { - std::stringstream result; + stringstream result; SVGOutput output; if (!H.IsNull() && (type & WithHidden)) { - double width = hiddenscale; + H_style.insert({"stroke", "rgb(0, 0, 0)"}); + H_style.insert({"stroke-width", "0.15"}); + H_style.insert({"stroke-linecap", "butt"}); + H_style.insert({"stroke-linejoin", "miter"}); + H_style.insert({"stroke-dasharray", "0.2,0.1)"}); + H_style.insert({"fill", "none"}); + H_style.insert({"transform", "scale(1,-1)"}); BRepMesh_IncrementalMesh(H,tolerance); - result << "" << endl - << output.exportEdges(H) - << "" << endl; + result << "" << endl + << output.exportEdges(H) + << "" << endl; } if (!HO.IsNull() && (type & WithHidden)) { - double width = hiddenscale; + H0_style.insert({"stroke", "rgb(0, 0, 0)"}); + H0_style.insert({"stroke-width", "0.15"}); + H0_style.insert({"stroke-linecap", "butt"}); + H0_style.insert({"stroke-linejoin", "miter"}); + H0_style.insert({"stroke-dasharray", "0.02,0.1)"}); + H0_style.insert({"fill", "none"}); + H0_style.insert({"transform", "scale(1,-1)"}); BRepMesh_IncrementalMesh(HO,tolerance); - result << "" << endl - << output.exportEdges(HO) - << "" << endl; + result << "" << endl + << output.exportEdges(HO) + << "" << endl; } if (!VO.IsNull()) { - double width = scale; + V0_style.insert({"stroke", "rgb(0, 0, 0)"}); + V0_style.insert({"stroke-width", "1.0"}); + V0_style.insert({"stroke-linecap", "butt"}); + V0_style.insert({"stroke-linejoin", "miter"}); + V0_style.insert({"fill", "none"}); + V0_style.insert({"transform", "scale(1,-1)"}); BRepMesh_IncrementalMesh(VO,tolerance); - result << "" << endl - << output.exportEdges(VO) - << "" << endl; + result << "" << endl + << output.exportEdges(VO) + << "" << endl; } if (!V.IsNull()) { - double width = scale; + V_style.insert({"stroke", "rgb(0, 0, 0)"}); + V_style.insert({"stroke-width", "1.0"}); + V_style.insert({"stroke-linecap", "butt"}); + V_style.insert({"stroke-linejoin", "miter"}); + V_style.insert({"fill", "none"}); + V_style.insert({"transform", "scale(1,-1)"}); BRepMesh_IncrementalMesh(V,tolerance); - result << "" << endl - << output.exportEdges(V) - << "" << endl; + result << "" << endl + << output.exportEdges(V) + << "" << endl; } if (!V1.IsNull() && (type & WithSmooth)) { - double width = scale; + V1_style.insert({"stroke", "rgb(0, 0, 0)"}); + V1_style.insert({"stroke-width", "1.0"}); + V1_style.insert({"stroke-linecap", "butt"}); + V1_style.insert({"stroke-linejoin", "miter"}); + V1_style.insert({"fill", "none"}); + V1_style.insert({"transform", "scale(1,-1)"}); BRepMesh_IncrementalMesh(V1,tolerance); - result << "" << endl - << output.exportEdges(V1) - << "" << endl; + result << "" << endl + << output.exportEdges(V1) + << "" << endl; } if (!H1.IsNull() && (type & WithSmooth) && (type & WithHidden)) { - double width = hiddenscale; + H1_style.insert({"stroke", "rgb(0, 0, 0)"}); + H1_style.insert({"stroke-width", "0.15"}); + H1_style.insert({"stroke-linecap", "butt"}); + H1_style.insert({"stroke-linejoin", "miter"}); + H1_style.insert({"stroke-dasharray", "0.09,0.05)"}); + H1_style.insert({"fill", "none"}); + H1_style.insert({"transform", "scale(1,-1)"}); BRepMesh_IncrementalMesh(H1,tolerance); - result << "" << endl - << output.exportEdges(H1) - << "" << endl; + result << "" << endl + << output.exportEdges(H1) + << "" << endl; } return result.str(); } /* dxf output section - Dan Falck 2011/09/25 */ -std::string ProjectionAlgos::getDXF(ExtractionType type, double /*scale*/, double tolerance) +string ProjectionAlgos::getDXF(ExtractionType type, double /*scale*/, double tolerance) { - std::stringstream result; + stringstream result; DXFOutput output; if (!H.IsNull() && (type & WithHidden)) { diff --git a/src/Mod/Drawing/App/ProjectionAlgos.h b/src/Mod/Drawing/App/ProjectionAlgos.h index 01cbe1e81d..180dd23072 100644 --- a/src/Mod/Drawing/App/ProjectionAlgos.h +++ b/src/Mod/Drawing/App/ProjectionAlgos.h @@ -43,15 +43,21 @@ public: virtual ~ProjectionAlgos(); void execute(void); -// static TopoDS_Shape invertY(const TopoDS_Shape&); enum ExtractionType { Plain = 0, WithHidden = 1, WithSmooth = 2 }; + typedef std::map XmlAttributes; - std::string getSVG(ExtractionType type, double scale=0.35, double tolerance=0.05, double hiddenscale=0.15); + std::string getSVG(ExtractionType type, double tolerance=0.05, + XmlAttributes V_style=XmlAttributes(), + XmlAttributes V0_style=XmlAttributes(), + XmlAttributes V1_style=XmlAttributes(), + XmlAttributes H_style=XmlAttributes(), + XmlAttributes H0_style=XmlAttributes(), + XmlAttributes H1_style=XmlAttributes()); std::string getDXF(ExtractionType type, double scale, double tolerance);//added by Dan Falck 2011/09/25