add UnicodeEngine (MultiEngineText and axis texters returning MultiEngineText), texte...
[PyX.git] / test / functional / test_metapost.py
blob0abb6c6703d1f6842685493ee8da80562b935dac
1 #!/usr/bin/env python
2 # -*- coding: ISO-8859-1 -*-
4 # Copyright (C) 2011 Michael Schindler <m-schindler@users.sourceforge.net>
6 # This file is part of PyX (http://pyx.sourceforge.net/).
8 # PyX is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # PyX is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with PyX; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
21 from math import atan2, pi, radians
22 import sys; sys.path[:0] = ["../.."]
23 from pyx import canvas, unit, deco, trafo, metapost
24 # TODO why must we change the epsilon before the import of _epsilon?
25 # does the import make a copy or a reference?
26 import pyx.metapost.path as mppath
27 mppath.set(epsilon=2.0e-5)
28 from pyx.metapost.path import *
29 from pyx import path
30 from pyx.metapost.path import _knot, _epsilon
31 from pyx.metapost.mp_path import mp_endpoint, mp_explicit, mp_given, mp_curl, mp_open, mp_make_choices
32 epsilon = _epsilon
34 # internal tests: check that the reimplementation of mp_make_choices is correct:
35 # tested with the output of a mpost binary (modified to output the knot parameters)
37 def curve1(): # <<<
38 """The open curve of Fig.3 on page 6 of mpman.pdf
39 draw z0..z1..z2..z3..z4"""
40 p = _knot(0, 0, mp_endpoint, None, None, mp_curl, 1, 1)
41 knots = p
42 p.next = _knot(60, 40, mp_open, None, 1, mp_open, None, 1)
43 p = p.next
44 p.next = _knot(40, 90, mp_open, None, 1, mp_open, None, 1)
45 p = p.next
46 p.next = _knot(10, 70, mp_open, None, 1, mp_open, None, 1)
47 p = p.next
48 p.next = _knot(30, 50, mp_curl, 1.0, 1, mp_endpoint, None, None)
49 p = p.next
50 p.next = knots
51 refpoints = [
52 ((None, None), (0, 0), (26.764633, -1.8454285)),
53 ((51.409393, 14.584412), (60, 40), (67.098755, 61.001877)),
54 ((59.762527, 84.57518), (40, 90), (25.357147, 94.01947)),
55 ((10.480637, 84.502197), (10, 70), (9.628952, 58.804214)),
56 ((18.804214, 49.628952), (30, 50), (None, None))]
57 return knots, refpoints
58 # >>>
59 def curve2(): # <<<
60 """The closed curve of Fig.4a on page 6 of mpman.pdf
61 draw z0...z1...z2...z3...z4...cycle"""
62 p = _knot(0, 0, mp_open, None, 1, mp_open, None, 1)
63 knots = p
64 p.next = _knot(60, 40, mp_open, None, 1, mp_open, None, 1)
65 p = p.next
66 p.next = _knot(40, 90, mp_open, None, 1, mp_open, None, 1)
67 p = p.next
68 p.next = _knot(10, 70, mp_open, None, 1, mp_open, None, 1)
69 p = p.next
70 p.next = _knot(30, 50, mp_open, None, 1, mp_open, None, 1)
71 p = p.next
72 p.next = knots
73 refpoints = [
74 ((-4.105545, 21.238037), (0, 0), (5.187561, -26.835297)),
75 ((60.360733, -18.40036), (60, 40), (59.877136, 59.889008)),
76 ((57.338959, 81.642029), (40, 90), (22.399872, 98.483871)),
77 ((4.7240448, 84.463684), (10, 70), (13.386368, 60.716507)),
78 ((26.355911, 59.135101), (30, 50), (39.194092, 26.951981))]
79 return knots, refpoints
80 # >>>
81 def curve3(): # <<<
82 """The open curve of Fig.6 on page 8 of mpman.pdf
83 draw z0..z1{up}..z2{left}..z3..z4"""
84 p = _knot(0, 0, mp_endpoint, None, None, mp_curl, 1, 1)
85 knots = p
86 # XXX: given angle is copied over from one side to the other:
87 # this is mimicked by the default values of roughknot
88 p.next = _knot(60, 40, mp_given, 0.5*pi, 1, mp_given, 0.5*pi, 1)
89 p = p.next
90 p.next = _knot(40, 90, mp_given, pi, 1, mp_given, pi, 1)
91 p = p.next
92 p.next = _knot(10, 70, mp_open, None, 1, mp_open, None, 1)
93 p = p.next
94 p.next = _knot(30, 50, mp_curl, 1, 1, mp_endpoint, None, None)
95 p = p.next
96 p.next = knots
97 refpoints = [
98 ((None, None), (0, 0), (28.543137, -11.892975)),
99 ((60, 9.0782776), (60, 40), (60, 63.263458)),
100 ((60.129883, 90), (40, 90), (25.690277, 90)),
101 ((11.52742, 83.230453), (10, 70), (8.666214, 58.446793)),
102 ((18.446793, 48.666214), (30, 50), (None, None))]
103 return knots, refpoints
104 # >>>
105 def curve4(a): # <<<
106 """Some of the curves of Figs. 7 and 8 on page 8 of mpman.pdf
107 beginfig(7)
108 for a=-9 upto 7:
109 draw (0,0){dir 45}..{dir 10a}(6cm,0);
110 endfor
111 endfig;"""
112 if a == -90:
113 refpoints = [((None, None), (0, 0), (72.980957, 72.980957)),
114 ((170.0787, 61.772141), (170.0787, 0), (None, None))]
115 elif a == 0:
116 refpoints = [((None, None), (0, 0), (44.36261, 44.36261)),
117 ((110.4153, 0), (170.0787, 0), (None, None))]
118 elif a == 70:
119 refpoints = [((None, None), (0, 0), (41.195404, 41.195404)),
120 ((138.80974, -85.909775), (170.0787, 0), (None, None))]
121 else:
122 refpoints = None
124 p = _knot(0, 0, mp_endpoint, None, None, mp_given, 0.25*pi, 1)
125 knots = p
126 p.next = _knot(170.078740157, 0, mp_given, radians(a), 1, mp_endpoint, None, None)
127 p = p.next
128 p.next = knots
129 return knots, refpoints
130 # >>>
131 def curve5(): # <<<
132 """The right curve of Fig. 9 on page 9 of mpman.pdf
133 draw z0{up}...z1{right}...z2{down}"""
134 p = _knot(0, 0, mp_endpoint, None, None, mp_given, 0.5*pi, -1)
135 knots = p
136 p.next = _knot(100, 20, mp_given, 0, -1, mp_given, 0, -1)
137 p = p.next
138 p.next = _knot(200, 0, mp_given, -0.5*pi, -1, mp_endpoint, None, None)
139 p = p.next
140 p.next = knots
142 refpoints = [
143 ((None, None), (0, 0), (0, 19.995117)),
144 ((56.625137, 20), (100, 20), (143.37486, 20)),
145 ((200, 19.995117), (200, 0), (None, None))]
146 return knots, refpoints
147 # >>>
148 def curve6a(): # <<<
149 """The first curve of Fig.10 on page 9 of mpman.pdf
150 draw z0..z1..tension 1 and 1..z2..z3;"""
151 p = _knot(0, 0, mp_endpoint, None, None, mp_curl, 1, 1)
152 knots = p
153 p.next = _knot(50, 50, mp_open, None, 1, mp_open, None, 1)
154 p = p.next
155 p.next = _knot(150, 50, mp_open, None, 1, mp_open, None, 1)
156 p = p.next
157 p.next = _knot(200, 0, mp_curl, 1, 1, mp_endpoint, None, None)
158 p = p.next
159 p.next = knots
161 refpoints = [
162 ((None, None), (0, 0), (10.747421, 21.688171)),
163 ((28.311829, 39.252579), (50, 50), (81.50528, 65.612213)),
164 ((118.49472, 65.612213), (150, 50), (171.68817, 39.252579)),
165 ((189.25258, 21.688171), (200, 0), (None, None))]
166 return knots, refpoints
167 # >>>
168 def curve6b(): # <<<
169 """The first curve of Fig.10 on page 9 of mpman.pdf
170 draw z0..z1..tension 1.3 and 1.3..z2..z3;"""
171 p = _knot(0, 0, mp_endpoint, None, None, mp_curl, 1, 1)
172 knots = p
173 p.next = _knot(50, 50, mp_open, None, 1, mp_open, None, 1.3)
174 p = p.next
175 p.next = _knot(150, 50, mp_open, None, 1.3, mp_open, None, 1)
176 p = p.next
177 p.next = _knot(200, 0, mp_curl, 1, 1, mp_endpoint, None, None)
178 p = p.next
179 p.next = knots
181 refpoints = [
182 ((None, None), (0, 0), (7.0810547, 24.08403)),
183 ((25.91597, 42.918945), (50, 50), (75.109573, 57.382568)),
184 ((124.89043, 57.382568), (150, 50), (174.08403, 42.918945)),
185 ((192.91895, 24.08403), (200, 0), (None, None))]
186 return knots, refpoints
187 # >>>
188 def curve6c(): # <<<
189 """The first curve of Fig.10 on page 9 of mpman.pdf
190 draw z0..z1..tension 2.5 and 1..z2..z3;"""
191 p = _knot(0, 0, mp_endpoint, None, None, mp_curl, 1, 1)
192 knots = p
193 p.next = _knot(50, 50, mp_open, None, 1, mp_open, None, 2.5)
194 p = p.next
195 p.next = _knot(150, 50, mp_open, None, 1, mp_open, None, 1)
196 p = p.next
197 p.next = _knot(200, 0, mp_curl, 1, 1, mp_endpoint, None, None)
198 p = p.next
199 p.next = knots
201 refpoints = [
202 ((None, None), (0, 0), (5.4299469, 25.023956)),
203 ((24.976044, 44.570053), (50, 50), (63.245346, 52.8741)),
204 ((117.57947, 59.957458), (150, 50), (173.92447, 42.651978)),
205 ((192.65198, 23.924469), (200, 0), (None, None))]
206 return knots, refpoints
207 # >>>
208 def curve7(): # <<<
209 """The first curve of Fig.10 on page 9 of mpman.pdf
210 draw z0..z1..tension atleast 1..{curl 2}z2..z3{-1,-2}..tension 3 and 4..z4..controls z45 and z54..z5;"""
212 p = _knot(0, 0, mp_endpoint, None, None, mp_curl, 1, 1) # z0
213 knots = p
214 p.next = _knot(50, 50, mp_open, None, 1, mp_open, None, -1) # z1
215 p = p.next
216 # XXX: curl value is copied over from one side to the other:
217 # this is mimicked by the default values of roughknot
218 p.next = _knot(80, 0, mp_curl, 2, -1, mp_curl, 2, 1) # z2
219 p = p.next
220 p.next = _knot(0, -20, mp_given, atan2(-2, -1), 1, mp_given, atan2(-2, -1), 3) # z3
221 p = p.next
222 p.next = _knot(50, -20, mp_open, None, 4, mp_explicit, -10, -50) # z4
223 p = p.next
224 p.next = _knot(150, 0, mp_explicit, 100, 50, mp_endpoint, None, None) # z5
225 p = p.next
226 p.next = knots
228 refpoints = [
229 ((None, None), (0, 0), (-2.2696381, 28.501495)),
230 ((21.498505, 52.269638), (50, 50), (78.324738, 47.744446)),
231 ((94.584061, 19.255417), (80, 0), (57.765503, 24.445801)),
232 ((16.6745, 13.348999), (0, -20), (-11.100037, -42.200073)),
233 ((80.924652, -4.537674), (50, -20), (-10, -50)),
234 ((100, 50), (150, 0), (None, None))]
235 return knots, refpoints
236 # >>>
237 def curve8a(): # <<<
238 """Testing degenerate points
239 draw (0,0)..(100,50)..(100,50)..{curl 1}(200,0)..(100,-50)..(100,-50)..(0,0)"""
241 p = _knot(0.0, 0.0, mp_endpoint, None, None, mp_curl, 1.0, 1.0)
242 knots = p
243 p.next = _knot(100.0, 50.0, mp_open, None, 1, mp_open, None, 1.0)
244 p = p.next
245 p.next = _knot(100.0, 50.0, mp_open, None, 1, mp_open, None, 1.0)
246 p = p.next
247 p.next = _knot(200.0, 0.0, mp_curl, 1, 1, mp_curl, 1, 1)
248 p = p.next
249 p.next = _knot(100.0, -50.0, mp_open, None, 1, mp_open, None, 1.0)
250 p = p.next
251 p.next = _knot(100.0, -50.0, mp_open, None, 1, mp_open, None, 1.0)
252 p = p.next
253 p.next = _knot(0.0, 0.0, mp_curl, 1, 1, mp_endpoint, None, None)
254 p = p.next
255 p.next = knots
257 refpoints = [
258 ((None, None), (0, 0), (33.333328, 16.666672)),
259 ((66.666672, 33.333328), (100, 50), (100, 50)),
260 ((100, 50), (100, 50), (133.33333, 33.333328)),
261 ((166.66667, 16.666672), (200, 0), (166.66667, -16.666672)),
262 ((133.33333, -33.333328), (100, -50), (100, -50)),
263 ((100, -50), (100, -50), (66.666672, -33.333328)),
264 ((33.333328, -16.666672), (0, 0), (None, None))]
265 return knots, refpoints
266 # >>>
267 def curve8b(): # <<<
268 """Testing degenerate points
269 draw (0,0)..tension 2 and 3..(100,50)..tension 3 and 4..(100,50)..tension 4 and 2..{curl 1}(200,0)..tension 2 and 3..(100,-50)..tension 3 and 4..(100,-50)..tension 4 and 2..(0,0)"""
271 p = _knot(0.0, 0.0, mp_endpoint, None, None, mp_curl, 1, 2)
272 knots = p
273 p.next = _knot(100.0, 50.0, mp_open, None, 3, mp_open, None, 3)
274 p = p.next
275 p.next = _knot(100.0, 50.0, mp_open, None, 4, mp_open, None, 4)
276 p = p.next
277 p.next = _knot(200.0, 0.0, mp_curl, 1, 2, mp_curl, 1, 2)
278 p = p.next
279 p.next = _knot(100.0, -50.0, mp_open, None, 3, mp_open, None, 3)
280 p = p.next
281 p.next = _knot(100.0, -50.0, mp_open, None, 4, mp_open, None, 4)
282 p = p.next
283 p.next = _knot(0.0, 0.0, mp_curl, 1, 2, mp_endpoint, None, None)
284 p = p.next
285 p.next = knots
287 refpoints = [
288 ((None, None), (0, 0), (16.666672, 8.3333282)),
289 ((88.888885, 44.444443), (100, 50), (100, 50)),
290 ((100, 50), (100, 50), (108.33333, 45.833328)),
291 ((183.33333, 8.3333282), (200, 0), (183.33333, -8.3333282)),
292 ((111.11111, -44.444443), (100, -50), (100, -50)),
293 ((100, -50), (100, -50), (91.666672, -45.833328)),
294 ((16.666672, -8.3333282), (0, 0), (None, None))]
295 return knots, refpoints
296 # >>>
297 def curve9(): # <<<
298 """Testing all parts of the code.
299 This cannot be tested on the command level."""
301 p = _knot(0, 0, mp_endpoint, None, None, mp_curl, 1, 1)
302 knots = p
303 p.next = _knot(50, 10, mp_open, None, 1, mp_explicit, 50, 10)
304 p = p.next
305 p.next = _knot(100, 0, mp_explicit, 100, 0, mp_open, None, 1)
306 p = p.next
307 p.next = _knot(150, 50, mp_explicit, 149, 49, mp_open, None, 1)
308 p = p.next
309 p.next = _knot(200.0, 0.0, mp_curl, 1, 1, mp_endpoint, None, None)
310 p = p.next
311 p.next = knots
313 refpoints = [
314 ((None, None), (0, 0), (16.666672, 3.3333282)),
315 ((33.333328, 6.6666718), (50, 10), (50, 10)),
316 ((100, 0), (100, 0), (116.66667, 16.666672)),
317 ((149.65987, 49.659866), (150, 50), (183.33333, 83.333328)),
318 ((233.33333, 33.333328), (200, 0), (None, None))]
320 return knots, refpoints
321 # >>>
322 def curve10(): # <<<
323 """Testing all parts of the code: test the "else" in item 364
324 This cannot be tested on the command level."""
325 p = _knot(0, 0, mp_open, None, -1, mp_open, None, 1) # z0
326 knots = p
327 # ltype is curl, and not both tensions are 1:
328 # This is already corrected by the parser of metapost
329 p.next = _knot(60, 40, mp_curl, 2, 2, mp_open, None, -1) # z1
330 p = p.next
331 p.next = _knot(40, 90, mp_open, None, -1, mp_open, None, -1) # z2
332 p = p.next
333 p.next = _knot(10, 70, mp_open, None, -1, mp_open, None, -1) # z3
334 p = p.next
335 p.next = _knot(30, 50, mp_open, None, -1, mp_open, None, -1) # z4
336 p = p.next
337 p.next = knots
339 refpoints = [
340 ((-10.430161, 23.006058), (0, 0), (23.677628, -52.226303)),
341 ((79.551056, 19.028336), (60, 40), (53.081604, 57.296005)),
342 ((40, 90), (40, 90), (22.69722, 100.22987)),
343 ((3.934021, 85.090485), (10, 70), (13.668121, 60.874741)),
344 ((26.782501, 59.383743), (30, 50), (37.508362, 28.102051))]
345 return knots, refpoints
346 # >>>
348 def myprint(knots): # <<<
349 str = repr(knots)
350 p = knots.next
351 while not p is knots:
352 str += " "
353 str += repr(p)
354 p = p.next
355 return str
356 # >>>
357 def mypath(knots): # <<<
358 p = path.path(path.moveto_pt(knots.x_pt, knots.y_pt))
359 cx, cy = knots.rx_pt, knots.ry_pt
360 prev = knots
361 k = knots.next
362 while not k is knots:
363 p.append(path.curveto_pt(cx, cy, k.lx_pt, k.ly_pt, k.x_pt, k.y_pt))
364 cx, cy = k.rx_pt, k.ry_pt
365 prev = k
366 k = k.next
367 if knots.ltype == mp_explicit:
368 p.append(path.curveto_pt(prev.rx_pt, prev.ry_pt, knots.lx_pt, knots.ly_pt, knots.x_pt, knots.y_pt))
369 p.append(path.closepath())
370 return p
371 # >>>
372 def check(knots, refpoints, eps=1.0e-3, rel=1.0e-5): # <<<
373 if refpoints is None:
374 return
375 assert knots.linked_len() == len(refpoints)
376 p = knots
377 for i, (left, coord, right) in enumerate(refpoints):
378 if left[0] is not None:
379 assert abs(left[0]-p.lx_pt) < rel*(abs(left[0])+eps)
380 assert abs(left[1]-p.ly_pt) < rel*(abs(left[1])+eps)
381 assert abs(coord[0]-p.x_pt) < rel*(abs(coord[0])+eps)
382 assert abs(coord[1]-p.y_pt) < rel*(abs(coord[1])+eps)
383 if right[0] is not None:
384 assert abs(right[0]-p.rx_pt) < rel*(abs(right[0])+eps)
385 assert abs(right[1]-p.ry_pt) < rel*(abs(right[1])+eps)
386 p = p.next
387 # >>>
388 def checkone(knots, refpoints): # <<<
389 print(myprint(knots))
390 mp_make_choices(knots, epsilon)
391 print(myprint(knots))
393 c = canvas.canvas()
394 c.stroke(mypath(knots), [deco.shownormpath(), deco.earrow.normal])
395 c.writePDFfile(bboxenlarge=unit.t_cm)
396 c.writeEPSfile(bboxenlarge=unit.t_cm)
397 c.writeSVGfile(bboxenlarge=unit.t_cm)
399 check(knots, refpoints)
400 # >>>
401 def checkall(): # <<<
402 c = None
403 for knots, refpoints in [curve1(), curve2(), curve3(),
404 curve4(-90), curve4(0), curve4(70), curve5(),
405 curve6a(), curve6b(), curve6c(), curve7(),
406 curve8a(), curve8b(), curve9(), curve10()]:
407 #print myprint(knots)
408 mp_make_choices(knots, epsilon)
409 #print myprint(knots)
411 cc = canvas.canvas()
412 cc.stroke(mypath(knots), [deco.shownormpath(), deco.earrow.normal])
413 if c is None:
414 c = canvas.canvas()
415 c.insert(cc)
416 else:
417 c.insert(cc, [trafo.translate(0, c.bbox().bottom() - cc.bbox().top()-0.5)])
419 check(knots, refpoints)
420 c.writePDFfile()
421 c.writeEPSfile()
422 c.writeSVGfile()
423 # >>>
425 # test of the user interface:
427 def interface(): # <<<
428 c = None
430 for p in [
431 # ordinary open path:
432 mppath.path([beginknot(0,0), curve(), knot(6,4), curve(), knot(4,9), curve(), knot(1,7), curve(), endknot(3,5)], epsilon),
433 # path containing two open subpaths:
434 mppath.path([beginknot(0,0), curve(), endknot(6,4), beginknot(4,9), curve(), knot(1,7), curve(), endknot(3,5)], epsilon),
435 # closed path:
436 mppath.path([knot(0,0), curve(), knot(6,4), curve(), knot(4,9), curve(), knot(1,7), curve(), knot(3,5), curve()], epsilon),
437 # open path, but with endpoints in the middle:
438 mppath.path([knot(0,0), curve(), knot(6,4), curve(), endknot(4,9), beginknot(1,7), curve(), knot(3,5), curve()], epsilon),
439 # the same path in the right order
440 mppath.path([beginknot(1,7), curve(), knot(3,5), curve(), knot(0,0), curve(), knot(6,4), curve(), endknot(4,9)], epsilon),
441 # include a line
442 mppath.path([knot(0,0), curve(), knot(6,4), curve(), roughknot(4,9), line(), roughknot(1,7), curve(), knot(3,5), curve()], epsilon),
443 # XXX the endpoints have "open" at their other sides, not "curl" as in the open example above
444 mppath.path([knot(0,0), curve(), knot(6,4), curve(), knot(4,9), line(), knot(1,7), curve(), knot(3,5), curve()], epsilon),
445 mppath.path([knot(0,0), curve(), knot(6,4), line(), knot(3,5), curve()], epsilon),
446 mppath.path([knot(0,0), curve(), knot(6,4), curve(), knot(3,5), curve()], epsilon),
447 # TODO the internal mp_make_choices treats this as closed, but the last curve is not plotted:
448 mppath.path([knot(0,0), curve(), knot(6,4), curve(), knot(4,9), line(), knot(1,7), curve(), knot(3,5)], epsilon),
449 # include a line with given angles
450 mppath.path([knot(0,0), curve(), knot(6,4), curve(), knot(4,9), line(keepangles=True), knot(1,7), curve(), knot(3,5), curve()], epsilon),
451 # include rough knots
452 mppath.path([beginknot(0,0), curve(), roughknot(6,4,langle=90), curve(), roughknot(4,9,langle=-90),
453 line(keepangles=True), roughknot(1,7,lcurl=3), curve(), endknot(3,5,angle=0)], epsilon),
455 cc = canvas.canvas()
456 cc.stroke(p, [deco.shownormpath(), deco.earrow.normal])
457 if c is None:
458 c = cc
459 else:
460 c.insert(cc, [trafo.translate(c.bbox().right() - cc.bbox().left() + 0.5, 0)])
461 c.writePDFfile()
462 c.writeEPSfile()
463 c.writeSVGfile()
464 # >>>
467 if __name__ == "__main__":
468 #checkone(*curve10())
469 checkall()
470 interface()
472 # vim:foldmethod=marker:foldmarker=<<<,>>>