1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
12 def __init__(self, x, y, matrix=numpy.matrix(numpy.identity(3, numpy.float64))):
14 self._startPoint = complex(x, y)
16 self._isClosed = False
18 def addLineTo(self, x, y):
19 self._points.append({'type': Path.LINE, 'p': complex(x, y)})
21 def addArcTo(self, x, y, rot, rx, ry, large, sweep):
26 'radius': complex(rx, ry),
31 def addCurveTo(self, x, y, cp1x, cp1y, cp2x, cp2y):
35 'cp1': complex(cp1x, cp1y),
36 'cp2': complex(cp2x, cp2y)
42 def checkClosed(self):
43 if abs(self._points[-1]['p'] - self._startPoint) < 0.001:
47 self._points.append({'type': Path.LINE, 'p': self._startPoint})
50 def getPoints(self, accuracy = 1):
51 pointList = [self._m(self._startPoint)]
53 for p in self._points:
54 if p['type'] == Path.LINE:
56 pointList.append(self._m(p1))
57 elif p['type'] == Path.ARC:
59 rot = math.radians(p['rot'])
62 #http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
64 p1alt = diff #TODO: apply rot
65 p2alt = -diff #TODO: apply rot
68 x1alt2 = p1alt.real*p1alt.real
69 y1alt2 = p1alt.imag*p1alt.imag
71 f = x1alt2 / rx2 + y1alt2 / ry2
73 r *= math.sqrt(f+0.000001)
77 f = math.sqrt((rx2*ry2 - rx2*y1alt2 - ry2*x1alt2) / (rx2*y1alt2+ry2*x1alt2))
78 if p['large'] == p['sweep']:
80 cAlt = f * complex(r.real*p1alt.imag/r.imag, -r.imag*p1alt.real/r.real)
82 c = cAlt + (p1 + p2) / 2 #TODO: apply rot
84 a1 = math.atan2((p1alt.imag - cAlt.imag) / r.imag, (p1alt.real - cAlt.real) / r.real)
85 a2 = math.atan2((p2alt.imag - cAlt.imag) / r.imag, (p2alt.real - cAlt.real) / r.real)
87 large = abs(a2 - a1) > math.pi
88 if large != p['large']:
94 pCenter = self._m(c + complex(math.cos(a1 + 0.5*(a2-a1)) * r.real, math.sin(a1 + 0.5*(a2-a1)) * r.imag))
95 dist = abs(pCenter - self._m(p1)) + abs(pCenter - self._m(p2))
96 segments = int(dist / accuracy) + 1
97 for n in xrange(1, segments):
98 pointList.append(self._m(c + complex(math.cos(a1 + n*(a2-a1)/segments) * r.real, math.sin(a1 + n*(a2-a1)/segments) * r.imag)))
100 pointList.append(self._m(p2))
102 elif p['type'] == Path.CURVE:
105 cp1 = self._m(p['cp1'])
106 cp2 = self._m(p['cp2'])
108 pCenter = p1_*0.5*0.5*0.5 + cp1*3.0*0.5*0.5*0.5 + cp2*3.0*0.5*0.5*0.5 + p2*0.5*0.5*0.5
109 dist = abs(pCenter - p1_) + abs(pCenter - p2)
110 segments = int(dist / accuracy) + 1
111 for n in xrange(1, segments):
112 f = n / float(segments)
114 point = p1_*g*g*g + cp1*3.0*g*g*f + cp2*3.0*g*f*f + p2*f*f*f
115 pointList.append(point)
122 #getSVGPath returns an SVG path string. Ths path string is not perfect when matrix transformations are involved.
123 def getSVGPath(self):
124 p0 = self._m(self._startPoint)
125 ret = 'M %f %f ' % (p0.real, p0.imag)
126 for p in self._points:
127 if p['type'] == Path.LINE:
129 ret += 'L %f %f' % (p0.real, p0.imag)
130 elif p['type'] == Path.ARC:
133 ret += 'A %f %f 0 %d %d %f %f' % (radius.real, radius.imag, 1 if p['large'] else 0, 1 if p['sweep'] else 0, p0.real, p0.imag)
134 elif p['type'] == Path.CURVE:
136 cp1 = self._m(p['cp1'])
137 cp2 = self._m(p['cp2'])
138 ret += 'C %f %f %f %f %f %f' % (cp1.real, cp1.imag, cp2.real, cp2.imag, p0.real, p0.imag)
143 tmp = numpy.matrix([p.real, p.imag, 1], numpy.float64) * self._matrix
144 return complex(tmp[0,0], tmp[0,1])
146 def saveAsHtml(paths, filename):
147 f = open(filename, "w")
149 posMax = complex(-1000, -1000)
150 posMin = complex( 1000, 1000)
151 for path in paths.paths:
152 points = path.getPoints()
154 if p.real > posMax.real:
155 posMax = complex(p.real, posMax.imag)
156 if p.imag > posMax.imag:
157 posMax = complex(posMax.real, p.imag)
158 if p.real < posMin.real:
159 posMin = complex(p.real, posMin.imag)
160 if p.imag < posMin.imag:
161 posMin = complex(posMin.real, p.imag)
163 f.write("<!DOCTYPE html><html><body>\n")
164 f.write("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style='width:%dpx;height:%dpx'>\n" % ((posMax - posMin).real, (posMax - posMin).imag))
165 f.write("<g fill-rule='evenodd' style=\"fill: gray; stroke:black;stroke-width:2\">\n")
166 f.write("<path d=\"")
167 for path in paths.paths:
168 points = path.getPoints()
169 f.write("M %f %f " % (points[0].real - posMin.real, points[0].imag - posMin.imag))
170 for point in points[1:]:
171 f.write("L %f %f " % (point.real - posMin.real, point.imag - posMin.imag))
175 f.write("<g style=\"fill: none; stroke:red;stroke-width:1\">\n")
176 f.write("<path d=\"")
177 for path in paths.paths:
178 f.write(path.getSVGPath())
183 f.write("</body></html>")