# SPDX-License-Identifier: LGPL-2.1-or-later # -*- coding: utf8 -*- # *************************************************************************** # * Copyright (c) 2009 Yorik van Havre * # * Copyright (c) 2018 George Shuklin (amarao) * # * Copyright (c) 2020 Eliud Cabrera Castillo * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * # * as published by the Free Software Foundation; either version 2 of * # * the License, or (at your option) any later version. * # * for detail see the LICENCE text file. * # * * # * This program 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 program; if not, write to the Free Software * # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * # * USA * # * * # *************************************************************************** """Provides functions to return the SVG representation of various shapes.""" ## @package svg # \ingroup draftfunctions # \brief Provides functions to return the SVG representation of shapes. import math import lazy_loader.lazy_loader as lz import FreeCAD as App import DraftVecUtils import WorkingPlane from draftfunctions import svgtext from draftfunctions.svgshapes import get_proj, get_circle, get_path from draftobjects import layer from draftutils import params from draftutils import utils from draftutils.messages import _wrn, _err # Delay import of module until first use because it is heavy Part = lz.LazyLoader("Part", globals(), "Part") DraftGeomUtils = lz.LazyLoader("DraftGeomUtils", globals(), "DraftGeomUtils") ## \addtogroup draftfunctions # @{ def get_line_style(line_style, scale): """Return a linestyle scaled by a factor.""" style = None if line_style == "Dashed": style = params.get_param("svgDashedLine") elif line_style == "Dashdot": style = params.get_param("svgDashdotLine") elif line_style == "Dotted": style = params.get_param("svgDottedLine") elif line_style: if "," in line_style: style = line_style if style: style = style.split(",") try: # scale dashes style = ",".join([str(float(d) / scale) for d in style]) # print("lstyle ", style) except Exception: # TODO: trap only specific exception; what is the problem? # Bad string specification? return "none" else: return style return "none" def getLineStyle(linestyle, scale): """Return a Line style. DEPRECATED. Use get_line_style.""" utils.use_instead("get_line_style") return get_line_style(linestyle, scale) def get_pattern(pat): """Get an SVG pattern.""" patterns = utils.svg_patterns() if pat in patterns: return patterns[pat][0] return "" def getPattern(pat): """Get an SVG pattern. DEPRECATED.""" utils.use_instead("get_pattern") return get_pattern(pat) def get_arrow(obj, arrowtype, point, arrowsize, color, linewidth, angle=0): """Get the SVG representation from an arrow.""" svg = "" vobj = _get_view_object(obj) if not App.GuiUp or vobj is None: return svg _cx_cy_r = 'cx="{}" cy="{}" r="{}"'.format(point.x, point.y, arrowsize) _rotate = "rotate({},{},{})".format(math.degrees(angle), point.x, point.y) _transl = "translate({},{})".format(point.x, point.y) _scale = "scale({size},{size})".format(size=arrowsize) _style = 'style="stroke-miterlimit:4;stroke-dasharray:none;stroke-linecap:square"' if arrowtype == "Circle": svg += " math.pi / 2: tangle = tangle - math.pi # elif (tangle <= -math.pi/2) or (tangle > math.pi/2): # tangle = tangle + math.pi if rotation != 0: # print("dim: tangle:", tangle, # " rot: ", rotation, # " text: ", prx.string) if abs(tangle + math.radians(rotation)) < 0.0001: tangle += math.pi _v = App.Vector(0, 2.0 / scale, 0) _rot = DraftVecUtils.rotate(_v, tangle) tbase = tbase + _rot if not nolines: svg += 'd="M ' + str(p1.x) + " " + str(p1.y) + " " svg += "L " + str(p2.x) + " " + str(p2.y) + " " svg += "L " + str(p3.x) + " " + str(p3.y) + " " svg += "L " + str(p4.x) + " " + str(p4.y) + '" ' else: tangle = 0 if rotation != 0: tangle = -math.radians(rotation) tbase = tbase + App.Vector(0, -2.0 / scale, 0) if not nolines: svg += 'd="M ' + str(p1.x) + " " + str(p1.y) + " " svg += "L " + str(p2.x) + " " + str(p2.y) + " " svg += "L " + str(p2a.x) + " " + str(p2a.y) + " " svg += "M " + str(p2b.x) + " " + str(p2b.y) + " " svg += "L " + str(p3.x) + " " + str(p3.y) + " " svg += "L " + str(p4.x) + " " + str(p4.y) + '" ' if not nolines: svg += 'fill="none" stroke="' svg += stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:' + str(linewidth) svg += ';stroke-miterlimit:4;stroke-dasharray:none;stroke-linecap:square" ' svg += 'freecad:basepoint1="' + str(p1.x) + " " + str(p1.y) + '" ' svg += 'freecad:basepoint2="' + str(p4.x) + " " + str(p4.y) + '" ' svg += 'freecad:dimpoint="' + str(p2.x) + " " + str(p2.y) + '"' svg += "/>\n" # drawing dimension and extension lines overshoots if hasattr(vobj, "DimOvershoot") and vobj.DimOvershoot.Value: shootsize = vobj.DimOvershoot.Value / pointratio svg += get_overshoot(p2, shootsize, stroke, linewidth, angle) svg += get_overshoot(p3, shootsize, stroke, linewidth, angle + math.pi) if hasattr(vobj, "ExtOvershoot") and vobj.ExtOvershoot.Value: shootsize = vobj.ExtOvershoot.Value / pointratio shootangle = -DraftVecUtils.angle(p1 - p2) svg += get_overshoot(p2, shootsize, stroke, linewidth, shootangle) svg += get_overshoot(p3, shootsize, stroke, linewidth, shootangle) # drawing arrows if ( hasattr(vobj, "ArrowTypeStart") and hasattr(vobj, "ArrowTypeEnd") and hasattr(vobj, "ArrowSizeStart") and hasattr(vobj, "ArrowSizeEnd") ): if getattr(vobj, "FlipArrows", False): angle = angle + math.pi if getattr(vobj, "FlipText", False): angle = angle + math.pi svg += get_arrow( obj, vobj.ArrowTypeStart, p2, vobj.ArrowSizeStart.Value / pointratio, stroke, linewidth, angle, ) svg += get_arrow( obj, vobj.ArrowTypeEnd, p3, vobj.ArrowSizeEnd.Value / pointratio, stroke, linewidth, angle + math.pi, ) # drawing text svg += svgtext.get_text( plane, techdraw, tstroke, fontsize, vobj.FontName, tangle, tbase, prx.string ) return svg def get_svg( obj, scale=1, linewidth=0.35, fontsize=12, fillstyle="shape color", direction=None, linestyle=None, color=None, linespacing=None, techdraw=False, rotation=0, fillspaces=False, override=True, ): """Return a string containing an SVG representation of the object. Paramaeters ----------- scale: float, optional It defaults to 1. It allows scaling line widths down, so they are resolution-independent. linewidth: float, optional It defaults to 0.35. fontsize: float, optional It defaults to 12, which is interpreted as `pt` unit (points). It is used if the given object contains any text. fillstyle: str, optional It defaults to 'shape color'. direction: Base::Vector3, optional It defaults to `None`. It is an arbitrary projection vector or a `WorkingPlane.PlaneBase` instance. linestyle: optional It defaults to `None`. color: optional It defaults to `None`. linespacing: float, optional It defaults to `None`. techdraw: bool, optional It defaults to `False`. If it is `True`, it sets some options for generating SVG strings for displaying inside TechDraw. rotation: float, optional It defaults to 0. fillspaces: bool, optional It defaults to `False`. override: bool, optional It defaults to `True`. """ # If this is a group, recursively call this function to gather # all the SVG strings from the contents of the group. if ( obj.isDerivedFrom("App::DocumentObjectGroup") or utils.get_type(obj) in ["Layer", "BuildingPart", "IfcGroup"] or obj.isDerivedFrom("App::LinkGroup") or ( obj.isDerivedFrom("App::Link") and obj.LinkedObject.isDerivedFrom("App::DocumentObjectGroup") ) ): hidden_doc = None if obj.isDerivedFrom("App::LinkGroup") or ( obj.isDerivedFrom("App::Link") and obj.LinkedObject.isDerivedFrom("App::DocumentObjectGroup") ): if obj.Placement.isIdentity(): if obj.isDerivedFrom("App::LinkGroup"): group = obj.ElementList else: group = obj.Group else: # Hidden doc hack: hidden_doc = App.newDocument(name="hidden", hidden=True, temp=True) new = hidden_doc.copyObject(obj, True) pla = new.Placement new.Placement = App.Placement() if new.isDerivedFrom("App::LinkGroup"): group = new.ElementList else: group = new.Group for child in group: child.Placement = pla * child.Placement else: group = obj.Group svg = "" for child in group: svg += get_svg( child, scale, linewidth, fontsize, fillstyle, direction, linestyle, color, linespacing, techdraw, rotation, fillspaces, override, ) if hidden_doc is not None: try: App.closeDocument(hidden_doc.Name) except: pass return svg # Handle Links to texts and dimensions. These Links do not have a Shape. if obj.isDerivedFrom("App::Link") and obj.LinkedObject and not hasattr(obj, "Shape"): # Hidden doc hack: hidden_doc = App.newDocument(name="hidden", hidden=True, temp=True) new = hidden_doc.copyObject(obj.LinkedObject, True) if utils.get_type(new) in ("Dimension", "LinearDimension", "AngularDimension"): new.Proxy.transform(new, obj.Placement) elif utils.get_type(new) == "Text" or obj.LinkTransform: new.Placement = obj.Placement * new.Placement else: new.Placement = obj.Placement svg = get_svg( new, scale, linewidth, fontsize, fillstyle, direction, linestyle, color, linespacing, techdraw, rotation, fillspaces, override, ) try: App.closeDocument(hidden_doc.Name) except: pass return svg vobj = _get_view_object(obj) pathdata = [] svg = "" linewidth = float(linewidth) / scale if not override: if vobj is not None and hasattr(vobj, "LineWidth"): if hasattr(vobj.LineWidth, "Value"): lw = vobj.LineWidth.Value else: lw = vobj.LineWidth linewidth = lw * linewidth fontsize = (float(fontsize) / scale) / 2 if linespacing: linespacing = float(linespacing) / scale else: linespacing = 0.5 # print(obj.Label, "line spacing", linespacing, "scale", scale) # The number of times the dots are smaller than the arrow size pointratio = 0.75 plane = None if direction: if isinstance(direction, App.Vector): if direction != App.Vector(0, 0, 0): plane = WorkingPlane.PlaneBase() plane.align_to_point_and_axis_svg( App.Vector(0, 0, 0), direction.negative().negative(), 0 ) else: raise ValueError("'direction' cannot be: Vector(0, 0, 0)") elif isinstance(direction, WorkingPlane.PlaneBase): plane = direction stroke = "#000000" tstroke = stroke if color and override: if "#" in color: stroke = color else: stroke = utils.get_rgb(color) tstroke = stroke elif App.GuiUp: # find print color pc = get_print_color(obj) if pc: stroke = utils.get_rgb(pc) # get line color elif vobj is not None: if hasattr(vobj, "LineColor"): stroke = utils.get_rgb(vobj.LineColor) elif hasattr(vobj, "TextColor"): stroke = utils.get_rgb(vobj.TextColor) if hasattr(vobj, "TextColor"): tstroke = utils.get_rgb(vobj.TextColor) lstyle = "none" if override: lstyle = get_line_style(linestyle, scale) else: if vobj is not None and hasattr(vobj, "DrawStyle"): lstyle = get_line_style(vobj.DrawStyle, scale) if not obj: pass elif isinstance(obj, Part.Shape): svg = _svg_shape(svg, obj, plane, fillstyle, pathdata, stroke, linewidth, lstyle) elif utils.get_type(obj) in ["Dimension", "LinearDimension"] or ( utils.get_type(obj) == "IfcAnnotation" and obj.ObjectType == "DIMENSION" ): svg = _svg_dimension( obj, plane, scale, linewidth, fontsize, stroke, tstroke, pointratio, techdraw, rotation ) elif utils.get_type(obj) == "AngularDimension": if not App.GuiUp: _wrn("Export of dimensions to SVG is only available in GUI mode") if App.GuiUp: if vobj.Proxy: if hasattr(vobj.Proxy, "circle"): prx = vobj.Proxy # drawing arc fill = "none" if vobj.DisplayMode == "World": svg += get_path( obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=[prx.circle], ) else: if hasattr(prx, "circle1"): svg += get_path( obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=[prx.circle1], ) svg += get_path( obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=[prx.circle2], ) else: svg += get_path( obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=[prx.circle], ) # drawing arrows if ( hasattr(vobj, "ArrowTypeStart") and hasattr(vobj, "ArrowTypeEnd") and hasattr(vobj, "ArrowSizeStart") and hasattr(vobj, "ArrowSizeEnd") ): p2 = get_proj(prx.p2, plane) p3 = get_proj(prx.p3, plane) arrowsizestart = vobj.ArrowSizeStart.Value / pointratio halfstartarrowlength = 2 * arrowsizestart startarrowangle = 2 * math.asin( halfstartarrowlength / prx.circle.Curve.Radius ) arrowsizeend = vobj.ArrowSizeEnd.Value / pointratio halfendarrowlength = 2 * arrowsizeend endarrowangle = 2 * math.asin(halfendarrowlength / prx.circle.Curve.Radius) if hasattr(vobj, "FlipArrows") and vobj.FlipArrows: startarrowangle = -startarrowangle endarrowangle = -endarrowangle _v1a = prx.circle.valueAt(prx.circle.FirstParameter + startarrowangle) _v1b = prx.circle.valueAt(prx.circle.FirstParameter) _v2a = prx.circle.valueAt(prx.circle.LastParameter - endarrowangle) _v2b = prx.circle.valueAt(prx.circle.LastParameter) u1 = get_proj(_v1a - _v1b, plane) u2 = get_proj(_v2a - _v2b, plane) angle1 = -DraftVecUtils.angle(u1) angle2 = -DraftVecUtils.angle(u2) svg += get_arrow( obj, vobj.ArrowTypeStart, p2, arrowsizestart, stroke, linewidth, angle1 ) svg += get_arrow( obj, vobj.ArrowTypeEnd, p3, arrowsizeend, stroke, linewidth, angle2 ) # drawing text if vobj.DisplayMode == "World": _diff = prx.circle.LastParameter - prx.circle.FirstParameter t = prx.circle.tangentAt(prx.circle.FirstParameter + _diff / 2.0) t = get_proj(t, plane) tangle = -DraftVecUtils.angle(t) if (tangle <= -math.pi / 2) or (tangle > math.pi / 2): tangle = tangle + math.pi _diff = prx.circle.LastParameter - prx.circle.FirstParameter _va = prx.circle.valueAt(prx.circle.FirstParameter + _diff / 2.0) tbase = get_proj(_va, plane) _v = App.Vector(0, 2.0 / scale, 0) tbase = tbase + DraftVecUtils.rotate(_v, tangle) # print(tbase) else: tangle = 0 tbase = get_proj(prx.tbase, plane) svg += svgtext.get_text( plane, techdraw, tstroke, fontsize, vobj.FontName, tangle, tbase, prx.string ) elif utils.get_type(obj) == "Label": if not App.GuiUp: _wrn("Export of texts to SVG is only available in GUI mode") if App.GuiUp: if getattr(vobj, "Line", True): # Some Labels may have no Line property # Draw multisegment line proj_points = list(map(lambda x: get_proj(x, plane), obj.Points)) path_dir_list = [format_point(proj_points[0], action="M")] path_dir_list += map(format_point, proj_points[1:]) path_dir_str = " ".join(path_dir_list) svg_path = " 1 ): last_segment = App.Vector(obj.Points[-1] - obj.Points[-2]) _v = get_proj(last_segment, plane) angle = -DraftVecUtils.angle(_v) + math.pi svg += get_arrow( obj, vobj.ArrowTypeStart, proj_points[-1], vobj.ArrowSizeStart.Value / pointratio, stroke, linewidth, angle, ) fontname = vobj.FontName position = get_proj(obj.Placement.Base, plane) rotation = obj.Placement.Rotation justification = vobj.Justification text = obj.Text svg += svgtext.get_text( plane, techdraw, tstroke, fontsize, fontname, rotation, position, text, linespacing, justification, ) elif utils.get_type(obj) in ["Annotation", "DraftText", "Text"] or ( utils.get_type(obj) == "IfcAnnotation" and obj.ObjectType == "TEXT" ): # returns an svg representation of a document annotation if not App.GuiUp: _wrn("Export of texts to SVG is only available in GUI mode") if App.GuiUp: n = vobj.FontName if utils.get_type(obj) == "Annotation": p = get_proj(obj.Position, plane) r = vobj.Rotation.getValueAs("rad") t = obj.LabelText else: # DraftText (old) or Text (new, 0.19) p = get_proj(obj.Placement.Base, plane) r = obj.Placement.Rotation t = obj.Text j = vobj.Justification svg += svgtext.get_text(plane, techdraw, tstroke, fontsize, n, r, p, t, linespacing, j) elif utils.get_type(obj) == "Axis": # returns the SVG representation of an Arch Axis system if not App.GuiUp: _wrn("Export of axes to SVG is only available in GUI mode") if App.GuiUp: fn = vobj.FontName fill = "none" rad = vobj.BubbleSize.Value / 2 n = 0 for e in obj.Shape.Edges: svg += get_path( obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=[e], ) for t in vobj.Proxy.getTextData(): pos = t[1].add(App.Vector(0, -fontsize / 2, 0)) svg += svgtext.get_text( plane, techdraw, tstroke, fontsize, fn, 0.0, pos, t[0], 1.0, "center" ) for b in vobj.Proxy.getShapeData(): if hasattr(b, "Curve") and isinstance(b.Curve, Part.Circle): svg += get_circle(plane, fill, stroke, linewidth, "none", b) else: sfill = stroke svg += get_path( obj, plane, sfill, pathdata, stroke, linewidth, "none", fill_opacity=None, edges=b.Edges, ) elif utils.get_type(obj) == "Pipe": fill = stroke if obj.Base and obj.Diameter: svg += get_path( obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, edges=obj.Base.Shape.Edges, ) for f in obj.Shape.Faces: if len(f.Edges) == 1: if isinstance(f.Edges[0].Curve, Part.Circle): svg += get_circle(plane, fill, stroke, linewidth, lstyle, f.Edges[0]) elif utils.get_type(obj) == "Rebar": fill = "none" basewire = obj.Base.Shape.Wires[0].copy() # Not applying rounding because the results are not correct # if hasattr(obj, "Rounding") and obj.Rounding: # basewire = DraftGeomUtils.filletWire( # basewire, obj.Rounding * obj.Diameter.Value # ) wires = [] for placement in obj.PlacementList: wire = basewire.copy() wire.Placement = placement.multiply(basewire.Placement) wires.append(wire) svg += get_path( obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=None, wires=wires ) elif utils.get_type(obj) == "PipeConnector": pass elif utils.get_type(obj) == "Space": fill_opacity = 1 # returns an SVG fragment for the text of a space if not App.GuiUp: _wrn("Export of spaces to SVG is only available in GUI mode") if App.GuiUp: if fillspaces and hasattr(obj, "Proxy"): if not hasattr(obj.Proxy, "face"): obj.Proxy.getArea(obj, notouch=True) if hasattr(obj.Proxy, "face"): # setting fill if App.GuiUp and hasattr(vobj, "ShapeColor"): fill = utils.get_rgb(vobj.ShapeColor, testbw=False) fill_opacity = 1 - vobj.Transparency / 100.0 else: fill = "#888888" svg += get_path( obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=fill_opacity, wires=[obj.Proxy.face.OuterWire], ) c = utils.get_rgb(vobj.TextColor) n = vobj.FontName a = 0 if rotation != 0: a = math.radians(rotation) t1 = vobj.Proxy.text1.string.getValues() t2 = vobj.Proxy.text2.string.getValues() scale = vobj.FirstLine.Value / vobj.FontSize.Value f1 = fontsize * scale if round(plane.axis.getAngle(App.Vector(0, 0, 1)), 2) not in [0, 3.14]: # if not in XY view, place the label at center p2 = obj.Shape.CenterOfMass else: _v = vobj.Proxy.coords.translation.getValue().getValue() p2 = obj.Placement.multVec(App.Vector(_v)) _h = vobj.Proxy.header.translation.getValue().getValue() lspc = App.Vector(_h) p1 = p2 + lspc j = vobj.TextAlign t3 = svgtext.get_text( plane, techdraw, c, f1, n, a, get_proj(p1, plane), t1, linespacing, j, flip=True ) svg += t3 if t2: ofs = App.Vector(0, -lspc.Length, 0) if a: Z = App.Vector(0, 0, 1) ofs = App.Rotation(Z, -rotation).multVec(ofs) t4 = svgtext.get_text( plane, techdraw, c, fontsize, n, a, get_proj(p1, plane).add(ofs), t2, linespacing, j, flip=True, ) svg += t4 elif hasattr(obj, "Shape"): # In the past we tested for a Part Feature # elif obj.isDerivedFrom('Part::Feature'): # # however, this doesn't work for App::Links; instead we # test for a 'Shape'. All Part::Features should have a Shape, # and App::Links can have one as well. if obj.Shape.isNull(): return "" fill_opacity = 1 # setting fill if obj.Shape.Faces: if App.GuiUp: try: m = vobj.DisplayMode except AttributeError: m = None if m != "Wireframe": if (fillstyle == "shape color") and hasattr(vobj, "ShapeColor"): fill = utils.get_rgb(vobj.ShapeColor, testbw=False) fill_opacity = 1 - vobj.Transparency / 100.0 elif fillstyle in ("none", None): fill = "none" else: fill = "url(#" + fillstyle + ")" svg += get_pattern(fillstyle) else: fill = "none" else: fill = "#888888" else: fill = "none" if len(obj.Shape.Vertexes) > 1: wiredEdges = [] if obj.Shape.Faces: for i, f in enumerate(obj.Shape.Faces): # place outer wire first wires = [f.OuterWire] wires.extend([w for w in f.Wires if w.hashCode() != f.OuterWire.hashCode()]) svg += get_path( obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=fill_opacity, wires=f.Wires, pathname="%s_f%04d" % (obj.Name, i), ) wiredEdges.extend(f.Edges) else: for i, w in enumerate(obj.Shape.Wires): svg += get_path( obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=fill_opacity, edges=w.Edges, pathname="%s_w%04d" % (obj.Name, i), ) wiredEdges.extend(w.Edges) if len(wiredEdges) != len(obj.Shape.Edges): fill = "none" # Required if obj has a face. Edges processed here have no face. def get_edge_descriptor(edge): return ( str(edge.Curve), str(edge.Vertexes[0].Point), str(edge.Vertexes[-1].Point), ) wiredEdgesSet = set([get_edge_descriptor(e) for e in wiredEdges]) for i, e in enumerate(obj.Shape.Edges): if get_edge_descriptor(e) not in wiredEdgesSet: svg += get_path( obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=fill_opacity, edges=[e], pathname="%s_nwe%04d" % (obj.Name, i), ) else: # closed circle or spline if obj.Shape.Edges: if isinstance(obj.Shape.Edges[0].Curve, Part.Circle): svg = get_circle(plane, fill, stroke, linewidth, lstyle, obj.Shape.Edges[0]) else: svg = get_path( obj, plane, fill, pathdata, stroke, linewidth, lstyle, fill_opacity=fill_opacity, edges=obj.Shape.Edges, ) if ( App.GuiUp and hasattr(vobj, "ArrowTypeStart") and hasattr(vobj, "ArrowTypeEnd") and hasattr(vobj, "ArrowSizeStart") and hasattr(vobj, "ArrowSizeEnd") and len(obj.Shape.Vertexes) > 1 ): # Draft_Wire p1 = get_proj(obj.Shape.Vertexes[0].Point, plane) p2 = get_proj(obj.Shape.Vertexes[1].Point, plane) svg += get_arrow( obj, vobj.ArrowTypeStart, p1, vobj.ArrowSizeStart.Value / pointratio, stroke, linewidth, -DraftVecUtils.angle(p2 - p1), ) p1 = get_proj(obj.Shape.Vertexes[-1].Point, plane) p2 = get_proj(obj.Shape.Vertexes[-2].Point, plane) svg += get_arrow( obj, vobj.ArrowTypeEnd, p1, vobj.ArrowSizeEnd.Value / pointratio, stroke, linewidth, -DraftVecUtils.angle(p2 - p1), ) # techdraw expects bottom-to-top coordinates if techdraw: svg = '\n ' + svg + "\n" return svg def _get_view_object(obj): if ( obj.isDerivedFrom("App::Link") and hasattr(obj, "ViewObject") and hasattr(obj.ViewObject, "OverrideMaterial") and not obj.ViewObject.OverrideMaterial and hasattr(obj.LinkedObject, "ViewObject") ): return obj.LinkedObject.ViewObject if hasattr(obj, "ViewObject"): return obj.ViewObject return None def get_print_color(obj): """Return the print color of the parent layer, if available.""" # Layers are not in the Inlist of obj because a layer's Group is App::PropertyLinkListHidden: lyr = layer.get_layer(obj) if lyr is None: return None if lyr.ViewObject.UsePrintColor: return lyr.ViewObject.LinePrintColor return None def getSVG( obj, scale=1, linewidth=0.35, fontsize=12, fillstyle="shape color", direction=None, linestyle=None, color=None, linespacing=None, techdraw=False, rotation=0, fillSpaces=False, override=True, ): """Return SVG string of the object. DEPRECATED. Use 'get_svg'.""" utils.use_instead("get_svg") return get_svg( obj, scale=scale, linewidth=linewidth, fontsize=fontsize, fillstyle=fillstyle, direction=direction, linestyle=linestyle, color=color, linespacing=linespacing, techdraw=techdraw, rotation=rotation, fillspaces=fillSpaces, override=override, ) ## @}