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 *
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
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)
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)
42 p
.next
= _knot(60, 40, mp_open
, None, 1, mp_open
, None, 1)
44 p
.next
= _knot(40, 90, mp_open
, None, 1, mp_open
, None, 1)
46 p
.next
= _knot(10, 70, mp_open
, None, 1, mp_open
, None, 1)
48 p
.next
= _knot(30, 50, mp_curl
, 1.0, 1, mp_endpoint
, None, None)
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
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)
64 p
.next
= _knot(60, 40, mp_open
, None, 1, mp_open
, None, 1)
66 p
.next
= _knot(40, 90, mp_open
, None, 1, mp_open
, None, 1)
68 p
.next
= _knot(10, 70, mp_open
, None, 1, mp_open
, None, 1)
70 p
.next
= _knot(30, 50, mp_open
, None, 1, mp_open
, None, 1)
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
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)
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)
90 p
.next
= _knot(40, 90, mp_given
, pi
, 1, mp_given
, pi
, 1)
92 p
.next
= _knot(10, 70, mp_open
, None, 1, mp_open
, None, 1)
94 p
.next
= _knot(30, 50, mp_curl
, 1, 1, mp_endpoint
, None, None)
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
106 """Some of the curves of Figs. 7 and 8 on page 8 of mpman.pdf
109 draw (0,0){dir 45}..{dir 10a}(6cm,0);
113 refpoints
= [((None, None), (0, 0), (72.980957, 72.980957)),
114 ((170.0787, 61.772141), (170.0787, 0), (None, None))]
116 refpoints
= [((None, None), (0, 0), (44.36261, 44.36261)),
117 ((110.4153, 0), (170.0787, 0), (None, None))]
119 refpoints
= [((None, None), (0, 0), (41.195404, 41.195404)),
120 ((138.80974, -85.909775), (170.0787, 0), (None, None))]
124 p
= _knot(0, 0, mp_endpoint
, None, None, mp_given
, 0.25*pi
, 1)
126 p
.next
= _knot(170.078740157, 0, mp_given
, radians(a
), 1, mp_endpoint
, None, None)
129 return knots
, refpoints
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)
136 p
.next
= _knot(100, 20, mp_given
, 0, -1, mp_given
, 0, -1)
138 p
.next
= _knot(200, 0, mp_given
, -0.5*pi
, -1, mp_endpoint
, None, None)
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
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)
153 p
.next
= _knot(50, 50, mp_open
, None, 1, mp_open
, None, 1)
155 p
.next
= _knot(150, 50, mp_open
, None, 1, mp_open
, None, 1)
157 p
.next
= _knot(200, 0, mp_curl
, 1, 1, mp_endpoint
, None, None)
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
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)
173 p
.next
= _knot(50, 50, mp_open
, None, 1, mp_open
, None, 1.3)
175 p
.next
= _knot(150, 50, mp_open
, None, 1.3, mp_open
, None, 1)
177 p
.next
= _knot(200, 0, mp_curl
, 1, 1, mp_endpoint
, None, None)
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
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)
193 p
.next
= _knot(50, 50, mp_open
, None, 1, mp_open
, None, 2.5)
195 p
.next
= _knot(150, 50, mp_open
, None, 1, mp_open
, None, 1)
197 p
.next
= _knot(200, 0, mp_curl
, 1, 1, mp_endpoint
, None, None)
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
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
214 p
.next
= _knot(50, 50, mp_open
, None, 1, mp_open
, None, -1) # z1
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
220 p
.next
= _knot(0, -20, mp_given
, atan2(-2, -1), 1, mp_given
, atan2(-2, -1), 3) # z3
222 p
.next
= _knot(50, -20, mp_open
, None, 4, mp_explicit
, -10, -50) # z4
224 p
.next
= _knot(150, 0, mp_explicit
, 100, 50, mp_endpoint
, None, None) # z5
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
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)
243 p
.next
= _knot(100.0, 50.0, mp_open
, None, 1, mp_open
, None, 1.0)
245 p
.next
= _knot(100.0, 50.0, mp_open
, None, 1, mp_open
, None, 1.0)
247 p
.next
= _knot(200.0, 0.0, mp_curl
, 1, 1, mp_curl
, 1, 1)
249 p
.next
= _knot(100.0, -50.0, mp_open
, None, 1, mp_open
, None, 1.0)
251 p
.next
= _knot(100.0, -50.0, mp_open
, None, 1, mp_open
, None, 1.0)
253 p
.next
= _knot(0.0, 0.0, mp_curl
, 1, 1, mp_endpoint
, None, None)
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
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)
273 p
.next
= _knot(100.0, 50.0, mp_open
, None, 3, mp_open
, None, 3)
275 p
.next
= _knot(100.0, 50.0, mp_open
, None, 4, mp_open
, None, 4)
277 p
.next
= _knot(200.0, 0.0, mp_curl
, 1, 2, mp_curl
, 1, 2)
279 p
.next
= _knot(100.0, -50.0, mp_open
, None, 3, mp_open
, None, 3)
281 p
.next
= _knot(100.0, -50.0, mp_open
, None, 4, mp_open
, None, 4)
283 p
.next
= _knot(0.0, 0.0, mp_curl
, 1, 2, mp_endpoint
, None, None)
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
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)
303 p
.next
= _knot(50, 10, mp_open
, None, 1, mp_explicit
, 50, 10)
305 p
.next
= _knot(100, 0, mp_explicit
, 100, 0, mp_open
, None, 1)
307 p
.next
= _knot(150, 50, mp_explicit
, 149, 49, mp_open
, None, 1)
309 p
.next
= _knot(200.0, 0.0, mp_curl
, 1, 1, mp_endpoint
, None, None)
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
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
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
331 p
.next
= _knot(40, 90, mp_open
, None, -1, mp_open
, None, -1) # z2
333 p
.next
= _knot(10, 70, mp_open
, None, -1, mp_open
, None, -1) # z3
335 p
.next
= _knot(30, 50, mp_open
, None, -1, mp_open
, None, -1) # z4
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
348 def myprint(knots
): # <<<
351 while not p
is knots
:
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
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
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())
372 def check(knots
, refpoints
, eps
=1.0e-3, rel
=1.0e-5): # <<<
373 if refpoints
is None:
375 assert knots
.linked_len() == len(refpoints
)
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
)
388 def checkone(knots
, refpoints
): # <<<
389 print(myprint(knots
))
390 mp_make_choices(knots
, epsilon
)
391 print(myprint(knots
))
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
)
401 def checkall(): # <<<
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)
412 cc
.stroke(mypath(knots
), [deco
.shownormpath(), deco
.earrow
.normal
])
417 c
.insert(cc
, [trafo
.translate(0, c
.bbox().bottom() - cc
.bbox().top()-0.5)])
419 check(knots
, refpoints
)
425 # test of the user interface:
427 def interface(): # <<<
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
),
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
),
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
),
456 cc
.stroke(p
, [deco
.shownormpath(), deco
.earrow
.normal
])
460 c
.insert(cc
, [trafo
.translate(c
.bbox().right() - cc
.bbox().left() + 0.5, 0)])
467 if __name__
== "__main__":
468 #checkone(*curve10())
472 # vim:foldmethod=marker:foldmarker=<<<,>>>