diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index 69e269e57b..16056a36a6 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -24,7 +24,6 @@ #*************************************************************************** #from __future__ import division - __title__="FreeCAD Draft Workbench" __author__ = "Yorik van Havre, Werner Mayer, Martin Burbaum, Ken Cline, Dmitry Chigrin, Daniel Falck" __url__ = "http://www.freecadweb.org" @@ -1985,811 +1984,6 @@ def getDXF(obj,direction=None): return result -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): - '''getSVG(object,[scale], [linewidth],[fontsize],[fillstyle],[direction],[linestyle],[color],[linespacing]): - returns a string containing a SVG representation of the given object, - with the given linewidth and fontsize (used if the given object contains - any text). You can also supply an arbitrary projection vector. the - scale parameter allows to scale linewidths down, so they are resolution-independant.''' - - # if this is a group, gather all the svg views of its children - if hasattr(obj,"isDerivedFrom"): - if obj.isDerivedFrom("App::DocumentObjectGroup"): - svg = "" - for child in obj.Group: - svg += getSVG(child,scale,linewidth,fontsize,fillstyle,direction,linestyle,color,linespacing,techdraw) - return svg - - import Part, DraftGeomUtils - pathdata = [] - svg = "" - linewidth = float(linewidth)/scale - fontsize = (float(fontsize)/scale)/2 - if linespacing: - linespacing = float(linespacing)/scale - else: - linespacing = 0.5 - #print obj.Label," line spacing ",linespacing,"scale ",scale - pointratio = .75 # the number of times the dots are smaller than the arrow size - plane = None - if direction: - if isinstance(direction,FreeCAD.Vector): - if direction != Vector(0,0,0): - plane = WorkingPlane.plane() - plane.alignToPointAndAxis_SVG(Vector(0,0,0),direction.negative().negative(),0) - elif isinstance(direction,WorkingPlane.plane): - plane = direction - stroke = "#000000" - if color: - if "#" in color: - stroke = color - else: - stroke = getrgb(color) - elif gui: - if hasattr(obj,"ViewObject"): - if hasattr(obj.ViewObject,"LineColor"): - stroke = getrgb(obj.ViewObject.LineColor) - - def getLineStyle(): - "returns a linestyle" - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft") - l = None - if linestyle == "Dashed": - l = p.GetString("svgDashedLine","0.09,0.05") - elif linestyle == "Dashdot": - l = p.GetString("svgDashdotLine","0.09,0.05,0.02,0.05") - elif linestyle == "Dotted": - l = p.GetString("svgDottedLine","0.02,0.02") - elif linestyle: - if "," in linestyle: - l = linestyle - if l: - l = l.split(",") - try: - # scale dashes - l = ",".join([str(float(d)/scale) for d in l]) - #print "lstyle ",l - except: - return "none" - else: - return l - return "none" - - def getProj(vec): - if not plane: return vec - nx = DraftVecUtils.project(vec,plane.u) - lx = nx.Length - if abs(nx.getAngle(plane.u)) > 0.1: lx = -lx - ny = DraftVecUtils.project(vec,plane.v) - ly = ny.Length - if abs(ny.getAngle(plane.v)) > 0.1: ly = -ly - #if techdraw: buggy - we now simply do it at the end - # ly = -ly - return Vector(lx,ly,0) - - def getDiscretized(edge): - ml = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetFloat("svgDiscretization",10.0) - if ml == 0: - ml = 10 - d = int(edge.Length/ml) - if d == 0: - d = 1 - edata = "" - for i in range(d+1): - v = getProj(edge.valueAt(edge.FirstParameter+((float(i)/d)*(edge.LastParameter-edge.FirstParameter)))) - if not edata: - edata += 'M ' + str(v.x) +' '+ str(v.y) + ' ' - else: - edata += 'L ' + str(v.x) +' '+ str(v.y) + ' ' - return edata - - def getPattern(pat): - if pat in svgpatterns(): - return svgpatterns()[pat][0] - return '' - - def getPath(edges=[],wires=[],pathname=None): - import Part,DraftGeomUtils - svg = " 1e-6: - vs.reverse() - if edgeindex == 0: - v = getProj(vs[0].Point) - edata += 'M '+ str(v.x) +' '+ str(v.y) + ' ' - else: - if (vs[0].Point-previousvs[-1].Point).Length > 1e-6: - raise ValueError('edges not ordered') - iscircle = DraftGeomUtils.geomType(e) == "Circle" - isellipse = DraftGeomUtils.geomType(e) == "Ellipse" - if iscircle or isellipse: - import math - if hasattr(FreeCAD,"DraftWorkingPlane"): - drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis - else: - drawing_plane_normal = FreeCAD.Vector(0,0,1) - if plane: drawing_plane_normal = plane.axis - c = e.Curve - if round(c.Axis.getAngle(drawing_plane_normal),2) in [0,3.14]: - occversion = Part.OCC_VERSION.split(".") - done = False - if (int(occversion[0]) >= 7) and (int(occversion[1]) >= 1): - # if using occ >= 7.1, use HLR algorithm - import Drawing - snip = Drawing.projectToSVG(e,drawing_plane_normal) - if snip: - try: - a = "A " + snip.split("path d=\"")[1].split("\"")[0].split("A")[1] - except: - pass - else: - edata += a - done = True - if not done: - if len(e.Vertexes) == 1 and iscircle: #complete curve - svg = getCircle(e) - return svg - elif len(e.Vertexes) == 1 and isellipse: - #svg = getEllipse(e) - #return svg - endpoints = (getProj(c.value((c.LastParameter-\ - c.FirstParameter)/2.0)), \ - getProj(vs[-1].Point)) - else: - endpoints = (getProj(vs[-1].Point),) - # arc - if iscircle: - rx = ry = c.Radius - rot = 0 - else: #ellipse - rx = c.MajorRadius - ry = c.MinorRadius - rot = math.degrees(c.AngleXU * (c.Axis * \ - FreeCAD.Vector(0,0,1))) - if rot > 90: - rot -=180 - if rot < -90: - rot += 180 - #be careful with the sweep flag - flag_large_arc = (((e.ParameterRange[1] - \ - e.ParameterRange[0]) / math.pi) % 2) > 1 - #flag_sweep = (c.Axis * drawing_plane_normal >= 0) \ - # == (e.LastParameter > e.FirstParameter) - # == (e.Orientation == "Forward") - # other method: check the direction of the angle between tangents - t1 = e.tangentAt(e.FirstParameter) - t2 = e.tangentAt(e.FirstParameter + (e.LastParameter-e.FirstParameter)/10) - flag_sweep = (DraftVecUtils.angle(t1,t2,drawing_plane_normal) < 0) - for v in endpoints: - edata += 'A %s %s %s %s %s %s %s ' % \ - (str(rx),str(ry),str(rot),\ - str(int(flag_large_arc)),\ - str(int(flag_sweep)),str(v.x),str(v.y)) - else: - edata += getDiscretized(e) - elif DraftGeomUtils.geomType(e) == "Line": - v = getProj(vs[-1].Point) - edata += 'L '+ str(v.x) +' '+ str(v.y) + ' ' - else: - bspline=e.Curve.toBSpline(e.FirstParameter,e.LastParameter) - if bspline.Degree > 3 or bspline.isRational(): - try: - bspline=bspline.approximateBSpline(0.05,50, 3,'C0') - except RuntimeError: - print("Debug: unable to approximate bspline") - if bspline.Degree <= 3 and not bspline.isRational(): - for bezierseg in bspline.toBezier(): - if bezierseg.Degree>3: #should not happen - raise AssertionError - elif bezierseg.Degree==1: - edata +='L ' - elif bezierseg.Degree==2: - edata +='Q ' - elif bezierseg.Degree==3: - edata +='C ' - for pole in bezierseg.getPoles()[1:]: - v = getProj(pole) - edata += str(v.x) +' '+ str(v.y) + ' ' - else: - print("Debug: one edge (hash ",e.hashCode(),\ - ") has been discretized with parameter 0.1") - for linepoint in bspline.discretize(0.1)[1:]: - v = getProj(linepoint) - edata += 'L '+ str(v.x) +' '+ str(v.y) + ' ' - if fill != 'none': - edata += 'Z ' - if edata in pathdata: - # do not draw a path on another identical path - return "" - else: - svg += edata - pathdata.append(edata) - svg += '" ' - svg += 'stroke="' + stroke + '" ' - svg += 'stroke-width="' + str(linewidth) + ' px" ' - svg += 'style="stroke-width:'+ str(linewidth) - svg += ';stroke-miterlimit:4' - svg += ';stroke-dasharray:' + lstyle - svg += ';fill:' + fill - try: - svg += ';fill-opacity:' + str(fill_opacity) - except NameError: - pass - svg += ';fill-rule: evenodd "' - svg += '/>\n' - return svg - - def getCircle(edge): - cen = getProj(edge.Curve.Center) - rad = edge.Curve.Radius - if hasattr(FreeCAD,"DraftWorkingPlane"): - drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis - else: - drawing_plane_normal = FreeCAD.Vector(0,0,1) - if plane: drawing_plane_normal = plane.axis - if round(edge.Curve.Axis.getAngle(drawing_plane_normal),2) == 0: - # perpendicular projection: circle - svg = '\n' - else: - print("getSVG: arrow type not implemented") - return svg - - def getOvershoot(point,shootsize,color,linewidth,angle=0): - svg = '\n' - return svg - - def getText(color,fontsize,fontname,angle,base,text,linespacing=0.5,align="center",flip=True): - if isinstance(angle,FreeCAD.Rotation): - if not plane: - angle = angle.Angle - else: - if plane.axis.getAngle(angle.Axis) < 0.001: - angle = angle.Angle - elif abs(plane.axis.getAngle(angle.Axis)-math.pi) < 0.001: - return "" # text is perpendicular to view, so it shouldn't appear - else: - angle = 0 #TODO maybe there is something better to do here? - if not isinstance(text,list): - text = text.split("\n") - if align.lower() == "center": - anchor = "middle" - elif align.lower() == "left": - anchor = "start" - else: - anchor = "end" - if techdraw: - svg = "" - for i in range(len(text)): - t = text[i] - if sys.version_info.major < 3 and (not isinstance(t,unicode)): - t = t.decode("utf8") - # possible workaround if UTF8 is unsupported - # import unicodedata - # t = u"".join([c for c in unicodedata.normalize("NFKD",t) if not unicodedata.combining(c)]).encode("utf8") - svg += '\n' - else: - svg = '' - try: - svg += text[i] - except: - svg += text[i].decode("utf8") - svg += '\n' - svg += '\n' - return svg - - - if not obj: - pass - - elif isinstance(obj,Part.Shape): - if "#" in fillstyle: - fill = fillstyle - elif fillstyle == "shape color": - fill = "#888888" - else: - fill = 'url(#'+fillstyle+')' - lstyle = getLineStyle() - svg += getPath(obj.Edges,pathname="") - - - elif getType(obj) == "Dimension": - if gui: - if not obj.ViewObject: - print ("export of dimensions to SVG is only available in GUI mode") - elif obj.ViewObject.Proxy: - if hasattr(obj.ViewObject.Proxy,"p1"): - prx = obj.ViewObject.Proxy - ts = (len(prx.string)*obj.ViewObject.FontSize.Value)/4.0 - rm = ((prx.p3.sub(prx.p2)).Length/2.0)-ts - p2a = getProj(prx.p2.add(DraftVecUtils.scaleTo(prx.p3.sub(prx.p2),rm))) - p2b = getProj(prx.p3.add(DraftVecUtils.scaleTo(prx.p2.sub(prx.p3),rm))) - p1 = getProj(prx.p1) - p2 = getProj(prx.p2) - p3 = getProj(prx.p3) - p4 = getProj(prx.p4) - tbase = getProj(prx.tbase) - r = prx.textpos.rotation.getValue().getValue() - rv = FreeCAD.Rotation(r[0],r[1],r[2],r[3]).multVec(FreeCAD.Vector(1,0,0)) - angle = -DraftVecUtils.angle(getProj(rv)) - #angle = -DraftVecUtils.angle(p3.sub(p2)) - - # drawing lines - svg = ' math.pi/2: - tangle = tangle-math.pi - #elif (tangle <= -math.pi/2) or (tangle > math.pi/2): - # tangle = tangle+math.pi - #tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2/scale,0),tangle)) - if rotation != 0: - #print "dim: tangle:",tangle," rot: ",rotation," text: ",prx.string - if abs(tangle+math.radians(rotation)) < 0.0001: - tangle += math.pi - tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2/scale,0),tangle)) - 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.add(Vector(0,-2.0/scale,0)) - 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)+'" ' - - 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" ' - 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(obj.ViewObject,"DimOvershoot") and obj.ViewObject.DimOvershoot.Value: - shootsize = obj.ViewObject.DimOvershoot.Value/pointratio - svg += getOvershoot(p2,shootsize,stroke,linewidth,angle) - svg += getOvershoot(p3,shootsize,stroke,linewidth,angle+math.pi) - if hasattr(obj.ViewObject,"ExtOvershoot") and obj.ViewObject.ExtOvershoot.Value: - shootsize = obj.ViewObject.ExtOvershoot.Value/pointratio - shootangle = -DraftVecUtils.angle(p1.sub(p2)) - svg += getOvershoot(p2,shootsize,stroke,linewidth,shootangle) - svg += getOvershoot(p3,shootsize,stroke,linewidth,shootangle) - - # drawing arrows - if hasattr(obj.ViewObject,"ArrowType"): - arrowsize = obj.ViewObject.ArrowSize.Value/pointratio - if hasattr(obj.ViewObject,"FlipArrows"): - if obj.ViewObject.FlipArrows: - angle = angle+math.pi - svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle) - svg += getArrow(obj.ViewObject.ArrowType,p3,arrowsize,stroke,linewidth,angle+math.pi) - - # drawing text - svg += getText(stroke,fontsize,obj.ViewObject.FontName,tangle,tbase,prx.string) - - elif getType(obj) == "AngularDimension": - if gui: - if not obj.ViewObject: - print ("export of dimensions to SVG is only available in GUI mode") - elif obj.ViewObject.Proxy: - if hasattr(obj.ViewObject.Proxy,"circle"): - prx = obj.ViewObject.Proxy - - # drawing arc - fill= "none" - lstyle = getLineStyle() - if obj.ViewObject.DisplayMode == "2D": - svg += getPath([prx.circle]) - else: - if hasattr(prx,"circle1"): - svg += getPath([prx.circle1]) - svg += getPath([prx.circle2]) - else: - svg += getPath([prx.circle]) - - # drawing arrows - if hasattr(obj.ViewObject,"ArrowType"): - p2 = getProj(prx.p2) - p3 = getProj(prx.p3) - arrowsize = obj.ViewObject.ArrowSize.Value/pointratio - arrowlength = 4*obj.ViewObject.ArrowSize.Value - u1 = getProj((prx.circle.valueAt(prx.circle.FirstParameter+arrowlength)).sub(prx.circle.valueAt(prx.circle.FirstParameter))) - u2 = getProj((prx.circle.valueAt(prx.circle.LastParameter-arrowlength)).sub(prx.circle.valueAt(prx.circle.LastParameter))) - angle1 = -DraftVecUtils.angle(u1) - angle2 = -DraftVecUtils.angle(u2) - if hasattr(obj.ViewObject,"FlipArrows"): - if obj.ViewObject.FlipArrows: - angle1 = angle1+math.pi - angle2 = angle2+math.pi - svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle1) - svg += getArrow(obj.ViewObject.ArrowType,p3,arrowsize,stroke,linewidth,angle2) - - # drawing text - if obj.ViewObject.DisplayMode == "2D": - t = prx.circle.tangentAt(prx.circle.FirstParameter+(prx.circle.LastParameter-prx.circle.FirstParameter)/2.0) - t = getProj(t) - tangle = DraftVecUtils.angle(t) - if (tangle <= -math.pi/2) or (tangle > math.pi/2): - tangle = tangle + math.pi - tbase = getProj(prx.circle.valueAt(prx.circle.FirstParameter+(prx.circle.LastParameter-prx.circle.FirstParameter)/2.0)) - tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2.0/scale,0),tangle)) - #print(tbase) - else: - tangle = 0 - tbase = getProj(prx.tbase) - svg += getText(stroke,fontsize,obj.ViewObject.FontName,tangle,tbase,prx.string) - - elif getType(obj) == "Label": - if getattr(obj.ViewObject, "Line", True): # some Labels may have no Line property - def format_point(coords, action='L'): - return "{action}{x},{y}".format( - x=coords.x, y=coords.y, action=action - ) - - # Draw multisegment line - proj_points = list(map(getProj, 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 = ''.format( - stroke=stroke, - linewidth=linewidth, - directions=path_dir_str - ) - svg += svg_path - - # Draw arrow. - # We are different here from 3D view - # if Line is set to 'off', no arrow is drawn - if hasattr(obj.ViewObject, "ArrowType") and len(obj.Points) >= 2: - last_segment = FreeCAD.Vector(obj.Points[-1] - obj.Points[-2]) - angle = -DraftVecUtils.angle(getProj(last_segment)) + math.pi - svg += getArrow( - arrowtype=obj.ViewObject.ArrowType, - point=proj_points[-1], - arrowsize=obj.ViewObject.ArrowSize.Value/pointratio, - color=stroke, - linewidth=linewidth, - angle=angle - ) - - # print text - if gui: - if not obj.ViewObject: - print("export of texts to SVG is only available in GUI mode") - else: - fontname = obj.ViewObject.TextFont - position = getProj(obj.Placement.Base) - rotation = obj.Placement.Rotation - justification = obj.ViewObject.TextAlignment - text = obj.Text - svg += getText(stroke, fontsize, fontname, rotation, position, - text, linespacing, justification) - - elif getType(obj) in ["Annotation","DraftText"]: - "returns an svg representation of a document annotation" - if gui: - if not obj.ViewObject: - print ("export of texts to SVG is only available in GUI mode") - else: - n = obj.ViewObject.FontName - if getType(obj) == "Annotation": - p = getProj(obj.Position) - r = obj.ViewObject.Rotation.getValueAs("rad") - t = obj.LabelText - else: # DraftText - p = getProj(obj.Placement.Base) - r = obj.Placement.Rotation - t = obj.Text - j = obj.ViewObject.Justification - svg += getText(stroke,fontsize,n,r,p,t,linespacing,j) - - elif getType(obj) == "Axis": - "returns the SVG representation of an Arch Axis system" - if gui: - if not obj.ViewObject: - print ("export of axes to SVG is only available in GUI mode") - else: - vobj = obj.ViewObject - lorig = getLineStyle() - fill = 'none' - rad = vobj.BubbleSize.Value/2 - n = 0 - for e in obj.Shape.Edges: - lstyle = lorig - svg += getPath([e]) - lstyle = "none" - pos = ["Start"] - if hasattr(vobj,"BubblePosition"): - if vobj.BubblePosition == "Both": - pos = ["Start","End"] - else: - pos = [vobj.BubblePosition] - for p in pos: - if p == "Start": - p1 = e.Vertexes[0].Point - p2 = e.Vertexes[1].Point - else: - p1 = e.Vertexes[1].Point - p2 = e.Vertexes[0].Point - dv = p2.sub(p1) - dv.normalize() - center = p2.add(dv.scale(rad,rad,rad)) - svg += getCircle(Part.makeCircle(rad,center)) - if hasattr(vobj.Proxy,"bubbletexts"): - if len (vobj.Proxy.bubbletexts) >= n: - svg += '\n' - svg += '\n' - n += 1 - - elif getType(obj) == "Pipe": - fill = stroke - lstyle = getLineStyle() - if obj.Base and obj.Diameter: - svg += getPath(obj.Base.Shape.Edges) - for f in obj.Shape.Faces: - if len(f.Edges) == 1: - if isinstance(f.Edges[0].Curve,Part.Circle): - svg += getCircle(f.Edges[0]) - - elif getType(obj) == "Rebar": - fill = "none" - lstyle = getLineStyle() - if obj.Proxy: - if not hasattr(obj.Proxy,"wires"): - obj.Proxy.execute(obj) - if hasattr(obj.Proxy,"wires"): - svg += getPath(wires=obj.Proxy.wires) - - elif getType(obj) == "PipeConnector": - pass - - elif getType(obj) == "Space": - "returns an SVG fragment for the text of a space" - if gui: - if not obj.ViewObject: - print ("export of spaces to SVG is only available in GUI mode") - else: - c = getrgb(obj.ViewObject.TextColor) - n = obj.ViewObject.FontName - a = 0 - if rotation != 0: - a = math.radians(rotation) - t1 = obj.ViewObject.Proxy.text1.string.getValues() - t2 = obj.ViewObject.Proxy.text2.string.getValues() - scale = obj.ViewObject.FirstLine.Value/obj.ViewObject.FontSize.Value - f1 = fontsize*scale - p2 = FreeCAD.Vector(obj.ViewObject.Proxy.coords.translation.getValue().getValue()) - lspc = FreeCAD.Vector(obj.ViewObject.Proxy.header.translation.getValue().getValue()) - p1 = p2.add(lspc) - j = obj.ViewObject.TextAlign - svg += getText(c,f1,n,a,getProj(p1),t1,linespacing,j,flip=True) - if t2: - ofs = FreeCAD.Vector(0,lspc.Length,0) - if a: - ofs = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),-rotation).multVec(ofs) - svg += getText(c,fontsize,n,a,getProj(p1).add(ofs),t2,linespacing,j,flip=True) - - elif obj.isDerivedFrom('Part::Feature'): - if obj.Shape.isNull(): - return '' - # setting fill - if obj.Shape.Faces: - if gui: - try: - m = obj.ViewObject.DisplayMode - except AttributeError: - m = None - if (m != "Wireframe"): - if fillstyle == "shape color": - fill = getrgb(obj.ViewObject.ShapeColor,testbw=False) - fill_opacity = 1 - (obj.ViewObject.Transparency / 100.0) - else: - fill = 'url(#'+fillstyle+')' - svg += getPattern(fillstyle) - else: - fill = "none" - else: - fill = "#888888" - else: - fill = 'none' - lstyle = getLineStyle() - - if len(obj.Shape.Vertexes) > 1: - wiredEdges = [] - if obj.Shape.Faces: - for i,f in enumerate(obj.Shape.Faces): - svg += getPath(wires=f.Wires,pathname='%s_f%04d' % \ - (obj.Name,i)) - wiredEdges.extend(f.Edges) - else: - for i,w in enumerate(obj.Shape.Wires): - svg += getPath(w.Edges,pathname='%s_w%04d' % \ - (obj.Name,i)) - wiredEdges.extend(w.Edges) - if len(wiredEdges) != len(obj.Shape.Edges): - for i,e in enumerate(obj.Shape.Edges): - if (DraftGeomUtils.findEdge(e,wiredEdges) == None): - svg += getPath([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 = getCircle(obj.Shape.Edges[0]) - else: - svg = getPath(obj.Shape.Edges) - if FreeCAD.GuiUp: - if hasattr(obj.ViewObject,"EndArrow") and hasattr(obj.ViewObject,"ArrowType") and (len(obj.Shape.Vertexes) > 1): - if obj.ViewObject.EndArrow: - p1 = getProj(obj.Shape.Vertexes[-2].Point) - p2 = getProj(obj.Shape.Vertexes[-1].Point) - angle = -DraftVecUtils.angle(p2.sub(p1)) - arrowsize = obj.ViewObject.ArrowSize.Value/pointratio - svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle) - - # techdraw expects bottom-to-top coordinates - if techdraw: - svg = ''+svg+'' - return svg def getrgb(color,testbw=True): """getRGB(color,[testbw]): returns a rgb value #000000 from a freecad color @@ -2805,6 +1999,13 @@ def getrgb(color,testbw=True): col = "#000000" return col + +import getSVG as svg + + +getSVG = svg.getSVG + + def makeDrawingView(obj,page,lwmod=None,tmod=None,otherProjection=None): ''' makeDrawingView(object,page,[lwmod,tmod]) - adds a View of the given object to the diff --git a/src/Mod/Draft/getSVG.py b/src/Mod/Draft/getSVG.py new file mode 100644 index 0000000000..9c229841b0 --- /dev/null +++ b/src/Mod/Draft/getSVG.py @@ -0,0 +1,810 @@ +import FreeCAD, math, sys, os, DraftVecUtils, WorkingPlane +from FreeCAD import Vector +from Draft import getType, getrgb + + +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): + '''getSVG(object,[scale], [linewidth],[fontsize],[fillstyle],[direction],[linestyle],[color],[linespacing]): + returns a string containing a SVG representation of the given object, + with the given linewidth and fontsize (used if the given object contains + any text). You can also supply an arbitrary projection vector. the + scale parameter allows to scale linewidths down, so they are resolution-independant.''' + + # if this is a group, gather all the svg views of its children + if hasattr(obj,"isDerivedFrom"): + if obj.isDerivedFrom("App::DocumentObjectGroup"): + svg = "" + for child in obj.Group: + svg += getSVG(child,scale,linewidth,fontsize,fillstyle,direction,linestyle,color,linespacing,techdraw) + return svg + + import Part, DraftGeomUtils + pathdata = [] + svg = "" + linewidth = float(linewidth)/scale + fontsize = (float(fontsize)/scale)/2 + if linespacing: + linespacing = float(linespacing)/scale + else: + linespacing = 0.5 + #print obj.Label," line spacing ",linespacing,"scale ",scale + pointratio = .75 # the number of times the dots are smaller than the arrow size + plane = None + if direction: + if isinstance(direction,FreeCAD.Vector): + if direction != Vector(0,0,0): + plane = WorkingPlane.plane() + plane.alignToPointAndAxis_SVG(Vector(0,0,0),direction.negative().negative(),0) + elif isinstance(direction,WorkingPlane.plane): + plane = direction + stroke = "#000000" + if color: + if "#" in color: + stroke = color + else: + stroke = getrgb(color) + elif gui: + if hasattr(obj,"ViewObject"): + if hasattr(obj.ViewObject,"LineColor"): + stroke = getrgb(obj.ViewObject.LineColor) + + def getLineStyle(): + "returns a linestyle" + p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft") + l = None + if linestyle == "Dashed": + l = p.GetString("svgDashedLine","0.09,0.05") + elif linestyle == "Dashdot": + l = p.GetString("svgDashdotLine","0.09,0.05,0.02,0.05") + elif linestyle == "Dotted": + l = p.GetString("svgDottedLine","0.02,0.02") + elif linestyle: + if "," in linestyle: + l = linestyle + if l: + l = l.split(",") + try: + # scale dashes + l = ",".join([str(float(d)/scale) for d in l]) + #print "lstyle ",l + except: + return "none" + else: + return l + return "none" + + def getProj(vec): + if not plane: return vec + nx = DraftVecUtils.project(vec,plane.u) + lx = nx.Length + if abs(nx.getAngle(plane.u)) > 0.1: lx = -lx + ny = DraftVecUtils.project(vec,plane.v) + ly = ny.Length + if abs(ny.getAngle(plane.v)) > 0.1: ly = -ly + #if techdraw: buggy - we now simply do it at the end + # ly = -ly + return Vector(lx,ly,0) + + def getDiscretized(edge): + ml = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetFloat("svgDiscretization",10.0) + if ml == 0: + ml = 10 + d = int(edge.Length/ml) + if d == 0: + d = 1 + edata = "" + for i in range(d+1): + v = getProj(edge.valueAt(edge.FirstParameter+((float(i)/d)*(edge.LastParameter-edge.FirstParameter)))) + if not edata: + edata += 'M ' + str(v.x) +' '+ str(v.y) + ' ' + else: + edata += 'L ' + str(v.x) +' '+ str(v.y) + ' ' + return edata + + def getPattern(pat): + if pat in svgpatterns(): + return svgpatterns()[pat][0] + return '' + + def getPath(edges=[],wires=[],pathname=None): + import Part,DraftGeomUtils + svg = " 1e-6: + vs.reverse() + if edgeindex == 0: + v = getProj(vs[0].Point) + edata += 'M '+ str(v.x) +' '+ str(v.y) + ' ' + else: + if (vs[0].Point-previousvs[-1].Point).Length > 1e-6: + raise ValueError('edges not ordered') + iscircle = DraftGeomUtils.geomType(e) == "Circle" + isellipse = DraftGeomUtils.geomType(e) == "Ellipse" + if iscircle or isellipse: + import math + if hasattr(FreeCAD,"DraftWorkingPlane"): + drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis + else: + drawing_plane_normal = FreeCAD.Vector(0,0,1) + if plane: drawing_plane_normal = plane.axis + c = e.Curve + if round(c.Axis.getAngle(drawing_plane_normal),2) in [0,3.14]: + occversion = Part.OCC_VERSION.split(".") + done = False + if (int(occversion[0]) >= 7) and (int(occversion[1]) >= 1): + # if using occ >= 7.1, use HLR algorithm + import Drawing + snip = Drawing.projectToSVG(e,drawing_plane_normal) + if snip: + try: + a = "A " + snip.split("path d=\"")[1].split("\"")[0].split("A")[1] + except: + pass + else: + edata += a + done = True + if not done: + if len(e.Vertexes) == 1 and iscircle: #complete curve + svg = getCircle(e) + return svg + elif len(e.Vertexes) == 1 and isellipse: + #svg = getEllipse(e) + #return svg + endpoints = (getProj(c.value((c.LastParameter-\ + c.FirstParameter)/2.0)), \ + getProj(vs[-1].Point)) + else: + endpoints = (getProj(vs[-1].Point),) + # arc + if iscircle: + rx = ry = c.Radius + rot = 0 + else: #ellipse + rx = c.MajorRadius + ry = c.MinorRadius + rot = math.degrees(c.AngleXU * (c.Axis * \ + FreeCAD.Vector(0,0,1))) + if rot > 90: + rot -=180 + if rot < -90: + rot += 180 + #be careful with the sweep flag + flag_large_arc = (((e.ParameterRange[1] - \ + e.ParameterRange[0]) / math.pi) % 2) > 1 + #flag_sweep = (c.Axis * drawing_plane_normal >= 0) \ + # == (e.LastParameter > e.FirstParameter) + # == (e.Orientation == "Forward") + # other method: check the direction of the angle between tangents + t1 = e.tangentAt(e.FirstParameter) + t2 = e.tangentAt(e.FirstParameter + (e.LastParameter-e.FirstParameter)/10) + flag_sweep = (DraftVecUtils.angle(t1,t2,drawing_plane_normal) < 0) + for v in endpoints: + edata += 'A %s %s %s %s %s %s %s ' % \ + (str(rx),str(ry),str(rot),\ + str(int(flag_large_arc)),\ + str(int(flag_sweep)),str(v.x),str(v.y)) + else: + edata += getDiscretized(e) + elif DraftGeomUtils.geomType(e) == "Line": + v = getProj(vs[-1].Point) + edata += 'L '+ str(v.x) +' '+ str(v.y) + ' ' + else: + bspline=e.Curve.toBSpline(e.FirstParameter,e.LastParameter) + if bspline.Degree > 3 or bspline.isRational(): + try: + bspline=bspline.approximateBSpline(0.05,50, 3,'C0') + except RuntimeError: + print("Debug: unable to approximate bspline") + if bspline.Degree <= 3 and not bspline.isRational(): + for bezierseg in bspline.toBezier(): + if bezierseg.Degree>3: #should not happen + raise AssertionError + elif bezierseg.Degree==1: + edata +='L ' + elif bezierseg.Degree==2: + edata +='Q ' + elif bezierseg.Degree==3: + edata +='C ' + for pole in bezierseg.getPoles()[1:]: + v = getProj(pole) + edata += str(v.x) +' '+ str(v.y) + ' ' + else: + print("Debug: one edge (hash ",e.hashCode(),\ + ") has been discretized with parameter 0.1") + for linepoint in bspline.discretize(0.1)[1:]: + v = getProj(linepoint) + edata += 'L '+ str(v.x) +' '+ str(v.y) + ' ' + if fill != 'none': + edata += 'Z ' + if edata in pathdata: + # do not draw a path on another identical path + return "" + else: + svg += edata + pathdata.append(edata) + svg += '" ' + svg += 'stroke="' + stroke + '" ' + svg += 'stroke-width="' + str(linewidth) + ' px" ' + svg += 'style="stroke-width:'+ str(linewidth) + svg += ';stroke-miterlimit:4' + svg += ';stroke-dasharray:' + lstyle + svg += ';fill:' + fill + try: + svg += ';fill-opacity:' + str(fill_opacity) + except NameError: + pass + svg += ';fill-rule: evenodd "' + svg += '/>\n' + return svg + + def getCircle(edge): + cen = getProj(edge.Curve.Center) + rad = edge.Curve.Radius + if hasattr(FreeCAD,"DraftWorkingPlane"): + drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis + else: + drawing_plane_normal = FreeCAD.Vector(0,0,1) + if plane: drawing_plane_normal = plane.axis + if round(edge.Curve.Axis.getAngle(drawing_plane_normal),2) == 0: + # perpendicular projection: circle + svg = '\n' + else: + print("getSVG: arrow type not implemented") + return svg + + def getOvershoot(point,shootsize,color,linewidth,angle=0): + svg = '\n' + return svg + + def getText(color,fontsize,fontname,angle,base,text,linespacing=0.5,align="center",flip=True): + if isinstance(angle,FreeCAD.Rotation): + if not plane: + angle = angle.Angle + else: + if plane.axis.getAngle(angle.Axis) < 0.001: + angle = angle.Angle + elif abs(plane.axis.getAngle(angle.Axis)-math.pi) < 0.001: + return "" # text is perpendicular to view, so it shouldn't appear + else: + angle = 0 #TODO maybe there is something better to do here? + if not isinstance(text,list): + text = text.split("\n") + if align.lower() == "center": + anchor = "middle" + elif align.lower() == "left": + anchor = "start" + else: + anchor = "end" + if techdraw: + svg = "" + for i in range(len(text)): + t = text[i] + if sys.version_info.major < 3 and (not isinstance(t,unicode)): + t = t.decode("utf8") + # possible workaround if UTF8 is unsupported + # import unicodedata + # t = u"".join([c for c in unicodedata.normalize("NFKD",t) if not unicodedata.combining(c)]).encode("utf8") + svg += '\n' + else: + svg = '' + try: + svg += text[i] + except: + svg += text[i].decode("utf8") + svg += '\n' + svg += '\n' + return svg + + + if not obj: + pass + + elif isinstance(obj,Part.Shape): + if "#" in fillstyle: + fill = fillstyle + elif fillstyle == "shape color": + fill = "#888888" + else: + fill = 'url(#'+fillstyle+')' + lstyle = getLineStyle() + svg += getPath(obj.Edges,pathname="") + + + elif getType(obj) == "Dimension": + if gui: + if not obj.ViewObject: + print ("export of dimensions to SVG is only available in GUI mode") + elif obj.ViewObject.Proxy: + if hasattr(obj.ViewObject.Proxy,"p1"): + prx = obj.ViewObject.Proxy + ts = (len(prx.string)*obj.ViewObject.FontSize.Value)/4.0 + rm = ((prx.p3.sub(prx.p2)).Length/2.0)-ts + p2a = getProj(prx.p2.add(DraftVecUtils.scaleTo(prx.p3.sub(prx.p2),rm))) + p2b = getProj(prx.p3.add(DraftVecUtils.scaleTo(prx.p2.sub(prx.p3),rm))) + p1 = getProj(prx.p1) + p2 = getProj(prx.p2) + p3 = getProj(prx.p3) + p4 = getProj(prx.p4) + tbase = getProj(prx.tbase) + r = prx.textpos.rotation.getValue().getValue() + rv = FreeCAD.Rotation(r[0],r[1],r[2],r[3]).multVec(FreeCAD.Vector(1,0,0)) + angle = -DraftVecUtils.angle(getProj(rv)) + #angle = -DraftVecUtils.angle(p3.sub(p2)) + + # drawing lines + svg = ' math.pi/2: + tangle = tangle-math.pi + #elif (tangle <= -math.pi/2) or (tangle > math.pi/2): + # tangle = tangle+math.pi + #tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2/scale,0),tangle)) + if rotation != 0: + #print "dim: tangle:",tangle," rot: ",rotation," text: ",prx.string + if abs(tangle+math.radians(rotation)) < 0.0001: + tangle += math.pi + tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2/scale,0),tangle)) + 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.add(Vector(0,-2.0/scale,0)) + 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)+'" ' + + 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" ' + 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(obj.ViewObject,"DimOvershoot") and obj.ViewObject.DimOvershoot.Value: + shootsize = obj.ViewObject.DimOvershoot.Value/pointratio + svg += getOvershoot(p2,shootsize,stroke,linewidth,angle) + svg += getOvershoot(p3,shootsize,stroke,linewidth,angle+math.pi) + if hasattr(obj.ViewObject,"ExtOvershoot") and obj.ViewObject.ExtOvershoot.Value: + shootsize = obj.ViewObject.ExtOvershoot.Value/pointratio + shootangle = -DraftVecUtils.angle(p1.sub(p2)) + svg += getOvershoot(p2,shootsize,stroke,linewidth,shootangle) + svg += getOvershoot(p3,shootsize,stroke,linewidth,shootangle) + + # drawing arrows + if hasattr(obj.ViewObject,"ArrowType"): + arrowsize = obj.ViewObject.ArrowSize.Value/pointratio + if hasattr(obj.ViewObject,"FlipArrows"): + if obj.ViewObject.FlipArrows: + angle = angle+math.pi + svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle) + svg += getArrow(obj.ViewObject.ArrowType,p3,arrowsize,stroke,linewidth,angle+math.pi) + + # drawing text + svg += getText(stroke,fontsize,obj.ViewObject.FontName,tangle,tbase,prx.string) + + elif getType(obj) == "AngularDimension": + if gui: + if not obj.ViewObject: + print ("export of dimensions to SVG is only available in GUI mode") + elif obj.ViewObject.Proxy: + if hasattr(obj.ViewObject.Proxy,"circle"): + prx = obj.ViewObject.Proxy + + # drawing arc + fill= "none" + lstyle = getLineStyle() + if obj.ViewObject.DisplayMode == "2D": + svg += getPath([prx.circle]) + else: + if hasattr(prx,"circle1"): + svg += getPath([prx.circle1]) + svg += getPath([prx.circle2]) + else: + svg += getPath([prx.circle]) + + # drawing arrows + if hasattr(obj.ViewObject,"ArrowType"): + p2 = getProj(prx.p2) + p3 = getProj(prx.p3) + arrowsize = obj.ViewObject.ArrowSize.Value/pointratio + arrowlength = 4*obj.ViewObject.ArrowSize.Value + u1 = getProj((prx.circle.valueAt(prx.circle.FirstParameter+arrowlength)).sub(prx.circle.valueAt(prx.circle.FirstParameter))) + u2 = getProj((prx.circle.valueAt(prx.circle.LastParameter-arrowlength)).sub(prx.circle.valueAt(prx.circle.LastParameter))) + angle1 = -DraftVecUtils.angle(u1) + angle2 = -DraftVecUtils.angle(u2) + if hasattr(obj.ViewObject,"FlipArrows"): + if obj.ViewObject.FlipArrows: + angle1 = angle1+math.pi + angle2 = angle2+math.pi + svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle1) + svg += getArrow(obj.ViewObject.ArrowType,p3,arrowsize,stroke,linewidth,angle2) + + # drawing text + if obj.ViewObject.DisplayMode == "2D": + t = prx.circle.tangentAt(prx.circle.FirstParameter+(prx.circle.LastParameter-prx.circle.FirstParameter)/2.0) + t = getProj(t) + tangle = DraftVecUtils.angle(t) + if (tangle <= -math.pi/2) or (tangle > math.pi/2): + tangle = tangle + math.pi + tbase = getProj(prx.circle.valueAt(prx.circle.FirstParameter+(prx.circle.LastParameter-prx.circle.FirstParameter)/2.0)) + tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2.0/scale,0),tangle)) + #print(tbase) + else: + tangle = 0 + tbase = getProj(prx.tbase) + svg += getText(stroke,fontsize,obj.ViewObject.FontName,tangle,tbase,prx.string) + + elif getType(obj) == "Label": + if getattr(obj.ViewObject, "Line", True): # some Labels may have no Line property + def format_point(coords, action='L'): + return "{action}{x},{y}".format( + x=coords.x, y=coords.y, action=action + ) + + # Draw multisegment line + proj_points = list(map(getProj, 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 = ''.format( + stroke=stroke, + linewidth=linewidth, + directions=path_dir_str + ) + svg += svg_path + + # Draw arrow. + # We are different here from 3D view + # if Line is set to 'off', no arrow is drawn + if hasattr(obj.ViewObject, "ArrowType") and len(obj.Points) >= 2: + last_segment = FreeCAD.Vector(obj.Points[-1] - obj.Points[-2]) + angle = -DraftVecUtils.angle(getProj(last_segment)) + math.pi + svg += getArrow( + arrowtype=obj.ViewObject.ArrowType, + point=proj_points[-1], + arrowsize=obj.ViewObject.ArrowSize.Value/pointratio, + color=stroke, + linewidth=linewidth, + angle=angle + ) + + # print text + if gui: + if not obj.ViewObject: + print("export of texts to SVG is only available in GUI mode") + else: + fontname = obj.ViewObject.TextFont + position = getProj(obj.Placement.Base) + rotation = obj.Placement.Rotation + justification = obj.ViewObject.TextAlignment + text = obj.Text + svg += getText(stroke, fontsize, fontname, rotation, position, + text, linespacing, justification) + + elif getType(obj) in ["Annotation","DraftText"]: + "returns an svg representation of a document annotation" + if gui: + if not obj.ViewObject: + print ("export of texts to SVG is only available in GUI mode") + else: + n = obj.ViewObject.FontName + if getType(obj) == "Annotation": + p = getProj(obj.Position) + r = obj.ViewObject.Rotation.getValueAs("rad") + t = obj.LabelText + else: # DraftText + p = getProj(obj.Placement.Base) + r = obj.Placement.Rotation + t = obj.Text + j = obj.ViewObject.Justification + svg += getText(stroke,fontsize,n,r,p,t,linespacing,j) + + elif getType(obj) == "Axis": + "returns the SVG representation of an Arch Axis system" + if gui: + if not obj.ViewObject: + print ("export of axes to SVG is only available in GUI mode") + else: + vobj = obj.ViewObject + lorig = getLineStyle() + fill = 'none' + rad = vobj.BubbleSize.Value/2 + n = 0 + for e in obj.Shape.Edges: + lstyle = lorig + svg += getPath([e]) + lstyle = "none" + pos = ["Start"] + if hasattr(vobj,"BubblePosition"): + if vobj.BubblePosition == "Both": + pos = ["Start","End"] + else: + pos = [vobj.BubblePosition] + for p in pos: + if p == "Start": + p1 = e.Vertexes[0].Point + p2 = e.Vertexes[1].Point + else: + p1 = e.Vertexes[1].Point + p2 = e.Vertexes[0].Point + dv = p2.sub(p1) + dv.normalize() + center = p2.add(dv.scale(rad,rad,rad)) + svg += getCircle(Part.makeCircle(rad,center)) + if hasattr(vobj.Proxy,"bubbletexts"): + if len (vobj.Proxy.bubbletexts) >= n: + svg += '\n' + svg += '\n' + n += 1 + + elif getType(obj) == "Pipe": + fill = stroke + lstyle = getLineStyle() + if obj.Base and obj.Diameter: + svg += getPath(obj.Base.Shape.Edges) + for f in obj.Shape.Faces: + if len(f.Edges) == 1: + if isinstance(f.Edges[0].Curve,Part.Circle): + svg += getCircle(f.Edges[0]) + + elif getType(obj) == "Rebar": + fill = "none" + lstyle = getLineStyle() + if obj.Proxy: + if not hasattr(obj.Proxy,"wires"): + obj.Proxy.execute(obj) + if hasattr(obj.Proxy,"wires"): + svg += getPath(wires=obj.Proxy.wires) + + elif getType(obj) == "PipeConnector": + pass + + elif getType(obj) == "Space": + "returns an SVG fragment for the text of a space" + if gui: + if not obj.ViewObject: + print ("export of spaces to SVG is only available in GUI mode") + else: + c = getrgb(obj.ViewObject.TextColor) + n = obj.ViewObject.FontName + a = 0 + if rotation != 0: + a = math.radians(rotation) + t1 = obj.ViewObject.Proxy.text1.string.getValues() + t2 = obj.ViewObject.Proxy.text2.string.getValues() + scale = obj.ViewObject.FirstLine.Value/obj.ViewObject.FontSize.Value + f1 = fontsize*scale + p2 = FreeCAD.Vector(obj.ViewObject.Proxy.coords.translation.getValue().getValue()) + lspc = FreeCAD.Vector(obj.ViewObject.Proxy.header.translation.getValue().getValue()) + p1 = p2.add(lspc) + j = obj.ViewObject.TextAlign + svg += getText(c,f1,n,a,getProj(p1),t1,linespacing,j,flip=True) + if t2: + ofs = FreeCAD.Vector(0,lspc.Length,0) + if a: + ofs = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),-rotation).multVec(ofs) + svg += getText(c,fontsize,n,a,getProj(p1).add(ofs),t2,linespacing,j,flip=True) + + elif obj.isDerivedFrom('Part::Feature'): + if obj.Shape.isNull(): + return '' + # setting fill + if obj.Shape.Faces: + if gui: + try: + m = obj.ViewObject.DisplayMode + except AttributeError: + m = None + if (m != "Wireframe"): + if fillstyle == "shape color": + fill = getrgb(obj.ViewObject.ShapeColor,testbw=False) + fill_opacity = 1 - (obj.ViewObject.Transparency / 100.0) + else: + fill = 'url(#'+fillstyle+')' + svg += getPattern(fillstyle) + else: + fill = "none" + else: + fill = "#888888" + else: + fill = 'none' + lstyle = getLineStyle() + + if len(obj.Shape.Vertexes) > 1: + wiredEdges = [] + if obj.Shape.Faces: + for i,f in enumerate(obj.Shape.Faces): + svg += getPath(wires=f.Wires,pathname='%s_f%04d' % \ + (obj.Name,i)) + wiredEdges.extend(f.Edges) + else: + for i,w in enumerate(obj.Shape.Wires): + svg += getPath(w.Edges,pathname='%s_w%04d' % \ + (obj.Name,i)) + wiredEdges.extend(w.Edges) + if len(wiredEdges) != len(obj.Shape.Edges): + for i,e in enumerate(obj.Shape.Edges): + if (DraftGeomUtils.findEdge(e,wiredEdges) == None): + svg += getPath([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 = getCircle(obj.Shape.Edges[0]) + else: + svg = getPath(obj.Shape.Edges) + if FreeCAD.GuiUp: + if hasattr(obj.ViewObject,"EndArrow") and hasattr(obj.ViewObject,"ArrowType") and (len(obj.Shape.Vertexes) > 1): + if obj.ViewObject.EndArrow: + p1 = getProj(obj.Shape.Vertexes[-2].Point) + p2 = getProj(obj.Shape.Vertexes[-1].Point) + angle = -DraftVecUtils.angle(p2.sub(p1)) + arrowsize = obj.ViewObject.ArrowSize.Value/pointratio + svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle) + + # techdraw expects bottom-to-top coordinates + if techdraw: + svg = ''+svg+'' + return svg