Move getSVG into a separate file to further refactoring.
All internal dependencies (getrgb, getType) are reimported.
This commit is contained in:
committed by
Yorik van Havre
parent
b530c7e576
commit
6ee6c677c6
@@ -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 = "<path "
|
||||
if pathname is None:
|
||||
svg += 'id="%s" ' % obj.Name
|
||||
elif pathname != "":
|
||||
svg += 'id="%s" ' % pathname
|
||||
svg += ' d="'
|
||||
if not wires:
|
||||
egroups = Part.sortEdges(edges)
|
||||
else:
|
||||
egroups = []
|
||||
for w in wires:
|
||||
w1=w.copy()
|
||||
w1.fixWire()
|
||||
egroups.append(Part.__sortEdges__(w1.Edges))
|
||||
for egroupindex, edges in enumerate(egroups):
|
||||
edata = ""
|
||||
vs=() #skipped for the first edge
|
||||
for edgeindex,e in enumerate(edges):
|
||||
previousvs = vs
|
||||
# vertexes of an edge (reversed if needed)
|
||||
vs = e.Vertexes
|
||||
if previousvs:
|
||||
if (vs[0].Point-previousvs[-1].Point).Length > 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 = '<circle cx="' + str(cen.x)
|
||||
svg += '" cy="' + str(cen.y)
|
||||
svg += '" r="' + str(rad)+'" '
|
||||
else:
|
||||
# any other projection: ellipse
|
||||
svg = '<path d="'
|
||||
svg += getDiscretized(edge)
|
||||
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 + '"'
|
||||
svg += '/>\n'
|
||||
return svg
|
||||
|
||||
def getEllipse(edge):
|
||||
cen = getProj(edge.Curve.Center)
|
||||
mir = edge.Curve.MinorRadius
|
||||
mar = edge.Curve.MajorRadius
|
||||
svg = '<ellipse cx="' + str(cen.x)
|
||||
svg += '" cy="' + str(cen.y)
|
||||
svg += '" rx="' + str(mar)
|
||||
svg += '" ry="' + str(mir)+'" '
|
||||
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 + '"'
|
||||
svg += '/>\n'
|
||||
return svg
|
||||
|
||||
def getArrow(arrowtype,point,arrowsize,color,linewidth,angle=0):
|
||||
svg = ""
|
||||
if gui:
|
||||
if not obj.ViewObject:
|
||||
return svg
|
||||
if obj.ViewObject.ArrowType == "Circle":
|
||||
svg += '<circle cx="'+str(point.x)+'" cy="'+str(point.y)
|
||||
svg += '" r="'+str(arrowsize)+'" '
|
||||
svg += 'fill="none" stroke="'+ color + '" '
|
||||
svg += 'style="stroke-width:'+ str(linewidth) + ';stroke-miterlimit:4;stroke-dasharray:none" '
|
||||
svg += 'freecad:skip="1"'
|
||||
svg += '/>\n'
|
||||
elif obj.ViewObject.ArrowType == "Dot":
|
||||
svg += '<circle cx="'+str(point.x)+'" cy="'+str(point.y)
|
||||
svg += '" r="'+str(arrowsize)+'" '
|
||||
svg += 'fill="'+ color +'" stroke="none" '
|
||||
svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" '
|
||||
svg += 'freecad:skip="1"'
|
||||
svg += '/>\n'
|
||||
elif obj.ViewObject.ArrowType == "Arrow":
|
||||
svg += '<path transform="rotate('+str(math.degrees(angle))
|
||||
svg += ','+ str(point.x) + ',' + str(point.y) + ') '
|
||||
svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') '
|
||||
svg += 'scale('+str(arrowsize)+','+str(arrowsize)+')" freecad:skip="1" '
|
||||
svg += 'fill="'+ color +'" stroke="none" '
|
||||
svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" '
|
||||
svg += 'd="M 0 0 L 4 1 L 4 -1 Z"/>\n'
|
||||
elif obj.ViewObject.ArrowType == "Tick":
|
||||
svg += '<path transform="rotate('+str(math.degrees(angle))
|
||||
svg += ','+ str(point.x) + ',' + str(point.y) + ') '
|
||||
svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') '
|
||||
svg += 'scale('+str(arrowsize)+','+str(arrowsize)+')" freecad:skip="1" '
|
||||
svg += 'fill="'+ color +'" stroke="none" '
|
||||
svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" '
|
||||
svg += 'd="M -1 -2 L 0 2 L 1 2 L 0 -2 Z"/>\n'
|
||||
elif obj.ViewObject.ArrowType == "Tick-2":
|
||||
svg += '<line transform="rotate('+str(math.degrees(angle)+45)
|
||||
svg += ','+ str(point.x) + ',' + str(point.y) + ') '
|
||||
svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') '
|
||||
svg += '" freecad:skip="1" '
|
||||
svg += 'fill="none" stroke="'+ color +'" '
|
||||
svg += 'style="stroke-dasharray:none;stroke-linecap:square;'
|
||||
svg += 'stroke-width:'+ str(linewidth) +'" '
|
||||
svg += 'x1="-'+ str(arrowsize*2) +'" y1="0" '
|
||||
svg += 'x2="' + str(arrowsize*2) +'" y2="0" />\n'
|
||||
else:
|
||||
print("getSVG: arrow type not implemented")
|
||||
return svg
|
||||
|
||||
def getOvershoot(point,shootsize,color,linewidth,angle=0):
|
||||
svg = '<line transform="rotate('+str(math.degrees(angle))
|
||||
svg += ','+ str(point.x) + ',' + str(point.y) + ') '
|
||||
svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') '
|
||||
svg += '" freecad:skip="1" '
|
||||
svg += 'fill="none" stroke="'+ color +'" '
|
||||
svg += 'style="stroke-dasharray:none;stroke-linecap:square;'
|
||||
svg += 'stroke-width:'+ str(linewidth) +'" '
|
||||
svg += 'x1="0" y1="0" '
|
||||
svg += 'x2="'+ str(shootsize*-1) +'" y2="0" />\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 += '<text fill="' + color +'" font-size="' + str(fontsize) + '" '
|
||||
svg += 'style="text-anchor:'+anchor+';text-align:'+align.lower()+';'
|
||||
svg += 'font-family:'+ fontname +'" '
|
||||
svg += 'transform="rotate('+str(math.degrees(angle))
|
||||
svg += ','+ str(base.x) + ',' + str(base.y-linespacing*i) + ') '
|
||||
svg += 'translate(' + str(base.x) + ',' + str(base.y-linespacing*i) + ') '
|
||||
svg += 'scale(1,-1)" '
|
||||
#svg += '" freecad:skip="1"'
|
||||
svg += '>\n' + t + '</text>\n'
|
||||
else:
|
||||
svg = '<text fill="'
|
||||
svg += color +'" font-size="'
|
||||
svg += str(fontsize) + '" '
|
||||
svg += 'style="text-anchor:'+anchor+';text-align:'+align.lower()+';'
|
||||
svg += 'font-family:'+ fontname +'" '
|
||||
svg += 'transform="rotate('+str(math.degrees(angle))
|
||||
svg += ','+ str(base.x) + ',' + str(base.y) + ') '
|
||||
if flip:
|
||||
svg += 'translate(' + str(base.x) + ',' + str(base.y) + ')'
|
||||
else:
|
||||
svg += 'translate(' + str(base.x) + ',' + str(-base.y) + ')'
|
||||
#svg += 'scale('+str(tmod/2000)+',-'+str(tmod/2000)+') '
|
||||
if flip:
|
||||
svg += ' scale(1,-1) '
|
||||
else:
|
||||
svg += ' scale(1,1) '
|
||||
svg += '" freecad:skip="1"'
|
||||
svg += '>\n'
|
||||
if len(text) == 1:
|
||||
try:
|
||||
svg += text[0]
|
||||
except:
|
||||
svg += text[0].decode("utf8")
|
||||
else:
|
||||
for i in range(len(text)):
|
||||
if i == 0:
|
||||
svg += '<tspan>'
|
||||
else:
|
||||
svg += '<tspan x="0" dy="'+str(linespacing)+'">'
|
||||
try:
|
||||
svg += text[i]
|
||||
except:
|
||||
svg += text[i].decode("utf8")
|
||||
svg += '</tspan>\n'
|
||||
svg += '</text>\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 = '<path '
|
||||
if obj.ViewObject.DisplayMode == "2D":
|
||||
tangle = angle
|
||||
if tangle > 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 = '<path fill="none" stroke="{stroke}" stroke-width="{linewidth}" d="{directions}"/>'.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 += '<text fill="' + stroke + '" '
|
||||
svg += 'font-size="' + str(rad) + '" '
|
||||
svg += 'style="text-anchor:middle;'
|
||||
svg += 'text-align:center;'
|
||||
svg += 'font-family: sans;" '
|
||||
svg += 'transform="translate(' + str(center.x+rad/4.0) + ',' + str(center.y-rad/3.0) + ') '
|
||||
svg += 'scale(1,-1)"> '
|
||||
svg += '<tspan>' + obj.ViewObject.Proxy.bubbletexts[n].string.getValues()[0] + '</tspan>\n'
|
||||
svg += '</text>\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 = '<g transform ="scale(1,-1)">'+svg+'</g>'
|
||||
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
|
||||
|
||||
810
src/Mod/Draft/getSVG.py
Normal file
810
src/Mod/Draft/getSVG.py
Normal file
@@ -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 = "<path "
|
||||
if pathname is None:
|
||||
svg += 'id="%s" ' % obj.Name
|
||||
elif pathname != "":
|
||||
svg += 'id="%s" ' % pathname
|
||||
svg += ' d="'
|
||||
if not wires:
|
||||
egroups = Part.sortEdges(edges)
|
||||
else:
|
||||
egroups = []
|
||||
for w in wires:
|
||||
w1=w.copy()
|
||||
w1.fixWire()
|
||||
egroups.append(Part.__sortEdges__(w1.Edges))
|
||||
for egroupindex, edges in enumerate(egroups):
|
||||
edata = ""
|
||||
vs=() #skipped for the first edge
|
||||
for edgeindex,e in enumerate(edges):
|
||||
previousvs = vs
|
||||
# vertexes of an edge (reversed if needed)
|
||||
vs = e.Vertexes
|
||||
if previousvs:
|
||||
if (vs[0].Point-previousvs[-1].Point).Length > 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 = '<circle cx="' + str(cen.x)
|
||||
svg += '" cy="' + str(cen.y)
|
||||
svg += '" r="' + str(rad)+'" '
|
||||
else:
|
||||
# any other projection: ellipse
|
||||
svg = '<path d="'
|
||||
svg += getDiscretized(edge)
|
||||
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 + '"'
|
||||
svg += '/>\n'
|
||||
return svg
|
||||
|
||||
def getEllipse(edge):
|
||||
cen = getProj(edge.Curve.Center)
|
||||
mir = edge.Curve.MinorRadius
|
||||
mar = edge.Curve.MajorRadius
|
||||
svg = '<ellipse cx="' + str(cen.x)
|
||||
svg += '" cy="' + str(cen.y)
|
||||
svg += '" rx="' + str(mar)
|
||||
svg += '" ry="' + str(mir)+'" '
|
||||
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 + '"'
|
||||
svg += '/>\n'
|
||||
return svg
|
||||
|
||||
def getArrow(arrowtype,point,arrowsize,color,linewidth,angle=0):
|
||||
svg = ""
|
||||
if gui:
|
||||
if not obj.ViewObject:
|
||||
return svg
|
||||
if obj.ViewObject.ArrowType == "Circle":
|
||||
svg += '<circle cx="'+str(point.x)+'" cy="'+str(point.y)
|
||||
svg += '" r="'+str(arrowsize)+'" '
|
||||
svg += 'fill="none" stroke="'+ color + '" '
|
||||
svg += 'style="stroke-width:'+ str(linewidth) + ';stroke-miterlimit:4;stroke-dasharray:none" '
|
||||
svg += 'freecad:skip="1"'
|
||||
svg += '/>\n'
|
||||
elif obj.ViewObject.ArrowType == "Dot":
|
||||
svg += '<circle cx="'+str(point.x)+'" cy="'+str(point.y)
|
||||
svg += '" r="'+str(arrowsize)+'" '
|
||||
svg += 'fill="'+ color +'" stroke="none" '
|
||||
svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" '
|
||||
svg += 'freecad:skip="1"'
|
||||
svg += '/>\n'
|
||||
elif obj.ViewObject.ArrowType == "Arrow":
|
||||
svg += '<path transform="rotate('+str(math.degrees(angle))
|
||||
svg += ','+ str(point.x) + ',' + str(point.y) + ') '
|
||||
svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') '
|
||||
svg += 'scale('+str(arrowsize)+','+str(arrowsize)+')" freecad:skip="1" '
|
||||
svg += 'fill="'+ color +'" stroke="none" '
|
||||
svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" '
|
||||
svg += 'd="M 0 0 L 4 1 L 4 -1 Z"/>\n'
|
||||
elif obj.ViewObject.ArrowType == "Tick":
|
||||
svg += '<path transform="rotate('+str(math.degrees(angle))
|
||||
svg += ','+ str(point.x) + ',' + str(point.y) + ') '
|
||||
svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') '
|
||||
svg += 'scale('+str(arrowsize)+','+str(arrowsize)+')" freecad:skip="1" '
|
||||
svg += 'fill="'+ color +'" stroke="none" '
|
||||
svg += 'style="stroke-miterlimit:4;stroke-dasharray:none" '
|
||||
svg += 'd="M -1 -2 L 0 2 L 1 2 L 0 -2 Z"/>\n'
|
||||
elif obj.ViewObject.ArrowType == "Tick-2":
|
||||
svg += '<line transform="rotate('+str(math.degrees(angle)+45)
|
||||
svg += ','+ str(point.x) + ',' + str(point.y) + ') '
|
||||
svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') '
|
||||
svg += '" freecad:skip="1" '
|
||||
svg += 'fill="none" stroke="'+ color +'" '
|
||||
svg += 'style="stroke-dasharray:none;stroke-linecap:square;'
|
||||
svg += 'stroke-width:'+ str(linewidth) +'" '
|
||||
svg += 'x1="-'+ str(arrowsize*2) +'" y1="0" '
|
||||
svg += 'x2="' + str(arrowsize*2) +'" y2="0" />\n'
|
||||
else:
|
||||
print("getSVG: arrow type not implemented")
|
||||
return svg
|
||||
|
||||
def getOvershoot(point,shootsize,color,linewidth,angle=0):
|
||||
svg = '<line transform="rotate('+str(math.degrees(angle))
|
||||
svg += ','+ str(point.x) + ',' + str(point.y) + ') '
|
||||
svg += 'translate(' + str(point.x) + ',' + str(point.y) + ') '
|
||||
svg += '" freecad:skip="1" '
|
||||
svg += 'fill="none" stroke="'+ color +'" '
|
||||
svg += 'style="stroke-dasharray:none;stroke-linecap:square;'
|
||||
svg += 'stroke-width:'+ str(linewidth) +'" '
|
||||
svg += 'x1="0" y1="0" '
|
||||
svg += 'x2="'+ str(shootsize*-1) +'" y2="0" />\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 += '<text fill="' + color +'" font-size="' + str(fontsize) + '" '
|
||||
svg += 'style="text-anchor:'+anchor+';text-align:'+align.lower()+';'
|
||||
svg += 'font-family:'+ fontname +'" '
|
||||
svg += 'transform="rotate('+str(math.degrees(angle))
|
||||
svg += ','+ str(base.x) + ',' + str(base.y-linespacing*i) + ') '
|
||||
svg += 'translate(' + str(base.x) + ',' + str(base.y-linespacing*i) + ') '
|
||||
svg += 'scale(1,-1)" '
|
||||
#svg += '" freecad:skip="1"'
|
||||
svg += '>\n' + t + '</text>\n'
|
||||
else:
|
||||
svg = '<text fill="'
|
||||
svg += color +'" font-size="'
|
||||
svg += str(fontsize) + '" '
|
||||
svg += 'style="text-anchor:'+anchor+';text-align:'+align.lower()+';'
|
||||
svg += 'font-family:'+ fontname +'" '
|
||||
svg += 'transform="rotate('+str(math.degrees(angle))
|
||||
svg += ','+ str(base.x) + ',' + str(base.y) + ') '
|
||||
if flip:
|
||||
svg += 'translate(' + str(base.x) + ',' + str(base.y) + ')'
|
||||
else:
|
||||
svg += 'translate(' + str(base.x) + ',' + str(-base.y) + ')'
|
||||
#svg += 'scale('+str(tmod/2000)+',-'+str(tmod/2000)+') '
|
||||
if flip:
|
||||
svg += ' scale(1,-1) '
|
||||
else:
|
||||
svg += ' scale(1,1) '
|
||||
svg += '" freecad:skip="1"'
|
||||
svg += '>\n'
|
||||
if len(text) == 1:
|
||||
try:
|
||||
svg += text[0]
|
||||
except:
|
||||
svg += text[0].decode("utf8")
|
||||
else:
|
||||
for i in range(len(text)):
|
||||
if i == 0:
|
||||
svg += '<tspan>'
|
||||
else:
|
||||
svg += '<tspan x="0" dy="'+str(linespacing)+'">'
|
||||
try:
|
||||
svg += text[i]
|
||||
except:
|
||||
svg += text[i].decode("utf8")
|
||||
svg += '</tspan>\n'
|
||||
svg += '</text>\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 = '<path '
|
||||
if obj.ViewObject.DisplayMode == "2D":
|
||||
tangle = angle
|
||||
if tangle > 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 = '<path fill="none" stroke="{stroke}" stroke-width="{linewidth}" d="{directions}"/>'.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 += '<text fill="' + stroke + '" '
|
||||
svg += 'font-size="' + str(rad) + '" '
|
||||
svg += 'style="text-anchor:middle;'
|
||||
svg += 'text-align:center;'
|
||||
svg += 'font-family: sans;" '
|
||||
svg += 'transform="translate(' + str(center.x+rad/4.0) + ',' + str(center.y-rad/3.0) + ') '
|
||||
svg += 'scale(1,-1)"> '
|
||||
svg += '<tspan>' + obj.ViewObject.Proxy.bubbletexts[n].string.getValues()[0] + '</tspan>\n'
|
||||
svg += '</text>\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 = '<g transform ="scale(1,-1)">'+svg+'</g>'
|
||||
return svg
|
||||
Reference in New Issue
Block a user