fix directory layout of tagging dir
[PyX.git] / test / functional / test_metapost.py
blobdb34c0dd889b03fa5afe11fb524ae45403225ffb
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)
398 check(knots, refpoints)
399 # >>>
400 def checkall(): # <<<
401 c = None
402 for knots, refpoints in [curve1(), curve2(), curve3(),
403 curve4(-90), curve4(0), curve4(70), curve5(),
404 curve6a(), curve6b(), curve6c(), curve7(),
405 curve8a(), curve8b(), curve9(), curve10()]:
406 #print myprint(knots)
407 mp_make_choices(knots, epsilon)
408 #print myprint(knots)
410 cc = canvas.canvas()
411 cc.stroke(mypath(knots), [deco.shownormpath(), deco.earrow.normal])
412 if c is None:
413 c = canvas.canvas()
414 c.insert(cc)
415 else:
416 c.insert(cc, [trafo.translate(0, c.bbox().bottom() - cc.bbox().top()-0.5)])
418 check(knots, refpoints)
419 c.writePDFfile()
420 c.writeEPSfile()
421 # >>>
423 # test of the user interface:
425 def interface(): # <<<
426 c = None
428 for p in [
429 # ordinary open path:
430 mppath.path([beginknot(0,0), curve(), knot(6,4), curve(), knot(4,9), curve(), knot(1,7), curve(), endknot(3,5)], epsilon),
431 # path containing two open subpaths:
432 mppath.path([beginknot(0,0), curve(), endknot(6,4), beginknot(4,9), curve(), knot(1,7), curve(), endknot(3,5)], epsilon),
433 # closed path:
434 mppath.path([knot(0,0), curve(), knot(6,4), curve(), knot(4,9), curve(), knot(1,7), curve(), knot(3,5), curve()], epsilon),
435 # open path, but with endpoints in the middle:
436 mppath.path([knot(0,0), curve(), knot(6,4), curve(), endknot(4,9), beginknot(1,7), curve(), knot(3,5), curve()], epsilon),
437 # the same path in the right order
438 mppath.path([beginknot(1,7), curve(), knot(3,5), curve(), knot(0,0), curve(), knot(6,4), curve(), endknot(4,9)], epsilon),
439 # include a line
440 mppath.path([knot(0,0), curve(), knot(6,4), curve(), roughknot(4,9), line(), roughknot(1,7), curve(), knot(3,5), curve()], epsilon),
441 # XXX the endpoints have "open" at their other sides, not "curl" as in the open example above
442 mppath.path([knot(0,0), curve(), knot(6,4), curve(), knot(4,9), line(), knot(1,7), curve(), knot(3,5), curve()], epsilon),
443 mppath.path([knot(0,0), curve(), knot(6,4), line(), knot(3,5), curve()], epsilon),
444 mppath.path([knot(0,0), curve(), knot(6,4), curve(), knot(3,5), curve()], epsilon),
445 # TODO the internal mp_make_choices treats this as closed, but the last curve is not plotted:
446 mppath.path([knot(0,0), curve(), knot(6,4), curve(), knot(4,9), line(), knot(1,7), curve(), knot(3,5)], epsilon),
447 # include a line with given angles
448 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),
449 # include rough knots
450 mppath.path([beginknot(0,0), curve(), roughknot(6,4,langle=90), curve(), roughknot(4,9,langle=-90),
451 line(keepangles=True), roughknot(1,7,lcurl=3), curve(), endknot(3,5,angle=0)], epsilon),
453 cc = canvas.canvas()
454 cc.stroke(p, [deco.shownormpath(), deco.earrow.normal])
455 if c is None:
456 c = cc
457 else:
458 c.insert(cc, [trafo.translate(c.bbox().right() - cc.bbox().left() + 0.5, 0)])
459 c.writePDFfile()
460 c.writeEPSfile()
461 # >>>
464 if __name__ == "__main__":
465 #checkone(*curve10())
466 checkall()
467 interface()
469 # vim:foldmethod=marker:foldmarker=<<<,>>>