svg2gcode/svg/geometry.py

Tue, 26 Sep 2017 19:08:02 +0200

author
mdd
date
Tue, 26 Sep 2017 19:08:02 +0200
changeset 36
f4730ef55ca8
parent 2
660ce16822a9
permissions
-rw-r--r--

SVG options: offset, original scale
SVG Bugfix: "scale" does now really fit to dimensions

2
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
1 # Copyright (C) 2013 -- CJlano < cjlano @ free.fr >
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
2
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
3 # This program is free software; you can redistribute it and/or modify
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
4 # it under the terms of the GNU General Public License as published by
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
5 # the Free Software Foundation; either version 2 of the License, or
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
6 # (at your option) any later version.
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
7 #
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
8 # This program is distributed in the hope that it will be useful,
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
11 # GNU General Public License for more details.
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
12 #
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
13 # You should have received a copy of the GNU General Public License along
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
14 # with this program; if not, write to the Free Software Foundation, Inc.,
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
16
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
17 '''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
18 This module contains all the geometric classes and functions not directly
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
19 related to SVG parsing. It can be reused outside the scope of SVG.
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
20 '''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
21
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
22 import math
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
23 import numbers
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
24 import operator
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
25
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
26 class Point:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
27 def __init__(self, x=None, y=None):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
28 '''A Point is defined either by a tuple/list of length 2 or
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
29 by 2 coordinates
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
30 >>> Point(1,2)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
31 (1.000,2.000)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
32 >>> Point((1,2))
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
33 (1.000,2.000)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
34 >>> Point([1,2])
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
35 (1.000,2.000)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
36 >>> Point('1', '2')
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
37 (1.000,2.000)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
38 >>> Point(('1', None))
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
39 (1.000,0.000)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
40 '''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
41 if (isinstance(x, tuple) or isinstance(x, list)) and len(x) == 2:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
42 x,y = x
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
43
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
44 # Handle empty parameter(s) which should be interpreted as 0
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
45 if x is None: x = 0
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
46 if y is None: y = 0
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
47
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
48 try:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
49 self.x = float(x)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
50 self.y = float(y)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
51 except:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
52 raise TypeError("A Point is defined by 2 numbers or a tuple")
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
53
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
54 def __add__(self, other):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
55 '''Add 2 points by adding coordinates.
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
56 Try to convert other to Point if necessary
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
57 >>> Point(1,2) + Point(3,2)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
58 (4.000,4.000)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
59 >>> Point(1,2) + (3,2)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
60 (4.000,4.000)'''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
61 if not isinstance(other, Point):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
62 try: other = Point(other)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
63 except: return NotImplemented
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
64 return Point(self.x + other.x, self.y + other.y)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
65
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
66 def __sub__(self, other):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
67 '''Substract two Points.
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
68 >>> Point(1,2) - Point(3,2)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
69 (-2.000,0.000)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
70 '''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
71 if not isinstance(other, Point):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
72 try: other = Point(other)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
73 except: return NotImplemented
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
74 return Point(self.x - other.x, self.y - other.y)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
75
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
76 def __mul__(self, other):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
77 '''Multiply a Point with a constant.
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
78 >>> 2 * Point(1,2)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
79 (2.000,4.000)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
80 >>> Point(1,2) * Point(1,2) #doctest:+IGNORE_EXCEPTION_DETAIL
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
81 Traceback (most recent call last):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
82 ...
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
83 TypeError:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
84 '''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
85 if not isinstance(other, numbers.Real):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
86 return NotImplemented
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
87 return Point(self.x * other, self.y * other)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
88 def __rmul__(self, other):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
89 return self.__mul__(other)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
90
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
91 def __eq__(self, other):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
92 '''Test equality
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
93 >>> Point(1,2) == (1,2)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
94 True
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
95 >>> Point(1,2) == Point(2,1)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
96 False
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
97 '''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
98 if not isinstance(other, Point):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
99 try: other = Point(other)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
100 except: return NotImplemented
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
101 return (self.x == other.x) and (self.y == other.y)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
102
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
103 def __repr__(self):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
104 return '(' + format(self.x,'.3f') + ',' + format( self.y,'.3f') + ')'
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
105
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
106 def __str__(self):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
107 return self.__repr__();
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
108
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
109 def coord(self):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
110 '''Return the point tuple (x,y)'''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
111 return (self.x, self.y)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
112
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
113 def length(self):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
114 '''Vector length, Pythagoras theorem'''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
115 return math.sqrt(self.x ** 2 + self.y ** 2)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
116
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
117 def rot(self, angle):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
118 '''Rotate vector [Origin,self] '''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
119 if not isinstance(angle, Angle):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
120 try: angle = Angle(angle)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
121 except: return NotImplemented
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
122 x = self.x * angle.cos - self.y * angle.sin
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
123 y = self.x * angle.sin + self.y * angle.cos
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
124 return Point(x,y)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
125
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
126
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
127 class Angle:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
128 '''Define a trigonometric angle [of a vector] '''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
129 def __init__(self, arg):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
130 if isinstance(arg, numbers.Real):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
131 # We precompute sin and cos for rotations
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
132 self.angle = arg
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
133 self.cos = math.cos(self.angle)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
134 self.sin = math.sin(self.angle)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
135 elif isinstance(arg, Point):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
136 # Point angle is the trigonometric angle of the vector [origin, Point]
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
137 pt = arg
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
138 try:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
139 self.cos = pt.x/pt.length()
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
140 self.sin = pt.y/pt.length()
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
141 except ZeroDivisionError:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
142 self.cos = 1
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
143 self.sin = 0
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
144
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
145 self.angle = math.acos(self.cos)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
146 if self.sin < 0:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
147 self.angle = -self.angle
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
148 else:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
149 raise TypeError("Angle is defined by a number or a Point")
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
150
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
151 def __neg__(self):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
152 return Angle(Point(self.cos, -self.sin))
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
153
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
154 class Segment:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
155 '''A segment is an object defined by 2 points'''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
156 def __init__(self, start, end):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
157 self.start = start
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
158 self.end = end
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
159
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
160 def __str__(self):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
161 return 'Segment from ' + str(self.start) + ' to ' + str(self.end)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
162
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
163 def segments(self, precision=0):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
164 ''' Segments is simply the segment start -> end'''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
165 return [self.start, self.end]
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
166
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
167 def length(self):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
168 '''Segment length, Pythagoras theorem'''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
169 s = self.end - self.start
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
170 return math.sqrt(s.x ** 2 + s.y ** 2)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
171
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
172 def pdistance(self, p):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
173 '''Perpendicular distance between this Segment and a given Point p'''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
174 if not isinstance(p, Point):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
175 return NotImplemented
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
176
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
177 if self.start == self.end:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
178 # Distance from a Point to another Point is length of a segment
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
179 return Segment(self.start, p).length()
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
180
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
181 s = self.end - self.start
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
182 if s.x == 0:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
183 # Vertical Segment => pdistance is the difference of abscissa
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
184 return abs(self.start.x - p.x)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
185 else:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
186 # That's 2-D perpendicular distance formulae (ref: Wikipedia)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
187 slope = s.y/s.x
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
188 # intercept: Crossing with ordinate y-axis
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
189 intercept = self.start.y - (slope * self.start.x)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
190 return abs(slope * p.x - p.y + intercept) / math.sqrt(slope ** 2 + 1)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
191
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
192
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
193 def bbox(self):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
194 xmin = min(self.start.x, self.end.x)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
195 xmax = max(self.start.x, self.end.x)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
196 ymin = min(self.start.y, self.end.y)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
197 ymax = max(self.start.y, self.end.y)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
198
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
199 return (Point(xmin,ymin),Point(xmax,ymax))
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
200
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
201 def transform(self, matrix):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
202 self.start = matrix * self.start
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
203 self.end = matrix * self.end
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
204
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
205 def scale(self, ratio):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
206 self.start *= ratio
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
207 self.end *= ratio
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
208 def translate(self, offset):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
209 self.start += offset
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
210 self.end += offset
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
211 def rotate(self, angle):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
212 self.start = self.start.rot(angle)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
213 self.end = self.end.rot(angle)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
214
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
215 class Bezier:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
216 '''Bezier curve class
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
217 A Bezier curve is defined by its control points
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
218 Its dimension is equal to the number of control points
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
219 Note that SVG only support dimension 3 and 4 Bezier curve, respectively
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
220 Quadratic and Cubic Bezier curve'''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
221 def __init__(self, pts):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
222 self.pts = list(pts)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
223 self.dimension = len(pts)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
224
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
225 def __str__(self):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
226 return 'Bezier' + str(self.dimension) + \
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
227 ' : ' + ", ".join([str(x) for x in self.pts])
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
228
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
229 def control_point(self, n):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
230 if n >= self.dimension:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
231 raise LookupError('Index is larger than Bezier curve dimension')
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
232 else:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
233 return self.pts[n]
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
234
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
235 def rlength(self):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
236 '''Rough Bezier length: length of control point segments'''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
237 pts = list(self.pts)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
238 l = 0.0
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
239 p1 = pts.pop()
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
240 while pts:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
241 p2 = pts.pop()
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
242 l += Segment(p1, p2).length()
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
243 p1 = p2
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
244 return l
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
245
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
246 def bbox(self):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
247 return self.rbbox()
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
248
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
249 def rbbox(self):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
250 '''Rough bounding box: return the bounding box (P1,P2) of the Bezier
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
251 _control_ points'''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
252 xmin = min([p.x for p in self.pts])
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
253 xmax = max([p.x for p in self.pts])
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
254 ymin = min([p.y for p in self.pts])
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
255 ymax = max([p.y for p in self.pts])
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
256
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
257 return (Point(xmin,ymin), Point(xmax,ymax))
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
258
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
259 def segments(self, precision=0):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
260 '''Return a polyline approximation ("segments") of the Bezier curve
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
261 precision is the minimum significative length of a segment'''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
262 segments = []
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
263 # n is the number of Bezier points to draw according to precision
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
264 if precision != 0:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
265 n = int(self.rlength() / precision) + 1
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
266 else:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
267 n = 1000
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
268 if n < 10: n = 10
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
269 if n > 1000 : n = 1000
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
270
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
271 for t in range(0, n+1):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
272 segments.append(self._bezierN(float(t)/n))
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
273 return segments
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
274
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
275 def _bezier1(self, p0, p1, t):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
276 '''Bezier curve, one dimension
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
277 Compute the Point corresponding to a linear Bezier curve between
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
278 p0 and p1 at "time" t '''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
279 pt = p0 + t * (p1 - p0)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
280 return pt
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
281
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
282 def _bezierN(self, t):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
283 '''Bezier curve, Nth dimension
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
284 Compute the point of the Nth dimension Bezier curve at "time" t'''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
285 # We reduce the N Bezier control points by computing the linear Bezier
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
286 # point of each control point segment, creating N-1 control points
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
287 # until we reach one single point
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
288 res = list(self.pts)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
289 # We store the resulting Bezier points in res[], recursively
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
290 for n in range(self.dimension, 1, -1):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
291 # For each control point of nth dimension,
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
292 # compute linear Bezier point a t
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
293 for i in range(0,n-1):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
294 res[i] = self._bezier1(res[i], res[i+1], t)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
295 return res[0]
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
296
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
297 def transform(self, matrix):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
298 self.pts = [matrix * x for x in self.pts]
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
299
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
300 def scale(self, ratio):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
301 self.pts = [x * ratio for x in self.pts]
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
302 def translate(self, offset):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
303 self.pts = [x + offset for x in self.pts]
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
304 def rotate(self, angle):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
305 self.pts = [x.rot(angle) for x in self.pts]
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
306
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
307 class MoveTo:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
308 def __init__(self, dest):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
309 self.dest = dest
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
310
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
311 def bbox(self):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
312 return (self.dest, self.dest)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
313
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
314 def transform(self, matrix):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
315 self.dest = matrix * self.dest
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
316
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
317 def scale(self, ratio):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
318 self.dest *= ratio
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
319 def translate(self, offset):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
320 self.dest += offset
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
321 def rotate(self, angle):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
322 self.dest = self.dest.rot(angle)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
323
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
324
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
325 def simplify_segment(segment, epsilon):
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
326 '''Ramer-Douglas-Peucker algorithm'''
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
327 if len(segment) < 3 or epsilon <= 0:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
328 return segment[:]
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
329
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
330 l = Segment(segment[0], segment[-1]) # Longest segment
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
331
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
332 # Find the furthest point from the segment
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
333 index, maxDist = max([(i, l.pdistance(p)) for i,p in enumerate(segment)],
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
334 key=operator.itemgetter(1))
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
335
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
336 if maxDist > epsilon:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
337 # Recursively call with segment splited in 2 on its furthest point
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
338 r1 = simplify_segment(segment[:index+1], epsilon)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
339 r2 = simplify_segment(segment[index:], epsilon)
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
340 # Remove redundant 'middle' Point
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
341 return r1[:-1] + r2
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
342 else:
660ce16822a9 added basic svg parser library
mbayer
parents:
diff changeset
343 return [segment[0], segment[-1]]

mercurial