1#!/usr/bin/python
2
3import py2geom
4import toyframework
5import random,gtk
6from py2geom_glue import *
7
8def cairo_pw(cr, p):
9    for i in range(p.size()):
10        a,b = p.cuts[i], p.cuts[i+1]
11        bez = sbasis_to_bezier(p[i], 0)
12        cr.move_to(a, bez[0])
13        cr.curve_to(lerp(a, b, 1./3), bez[1],
14                    lerp(a,b, 2./3), bez[2],
15                    b, bez[3])
16
17def cairo_horiz(cr, y, ps):
18    for p in ps:
19        cr.move_to(p, y);
20        cr.rel_line_to(0, 10)
21
22def cairo_vert(cr, x, ps):
23    for p in ps:
24        cr.move_to(x, p)
25        cr.rel_line_to(10, 0)
26
27def l2s(l):
28    sb = py2geom.SBasis(l)
29    return sb
30
31def constant(l):
32    pws = py2geom.PiecewiseSBasis()
33
34    pws.push_cut(0)
35    pws.push_seg(l2s(py2geom.Linear(l,l)) )
36    pws.push_cut(1000)
37    return pws
38
39X = l2s(py2geom.Linear(0, 1))
40OmX = l2s(py2geom.Linear(1, 0))
41def bezier_to_sbasis(handles, order):
42    if(order == 0):
43        return l2s(py2geom.Linear(handles[0]))
44    elif(order == 1):
45        return l2s(py2geom.Linear(handles[0], handles[1]))
46    else:
47        return (py2geom.multiply(OmX, bezier_to_sbasis(handles[:-1], order-1)) +
48                py2geom.multiply(X, bezier_to_sbasis(handles[1:], order-1)))
49
50
51def bez_to_sbasis(handles, order):
52    return bezier_to_sbasis([h[1] for h in handles], order)
53
54class PWSBHandle(toyframework.Handle):
55    def __init__(self, cs, segs):
56        self.handles_per_curve = cs*segs
57        self.curve_size = cs
58        self.segs = segs
59        self.pts = []
60    def append(self, x, y):
61        self.pts.append(py2geom.Point(x,y))
62    def value(self, y_0=0):
63        pws = py2geom.PiecewiseSBasis()
64        for i in range(0, self.handles_per_curve, self.curve_size):
65	    pws.push_cut(self.pts[i][0]);
66	    for j in range(i, i + self.curve_size):
67		self.pts[j] = py2geom.Point(self.pts[j][0], self.pts[j][1] - y_0)
68            hnd = self.pts[i:i+self.curve_size]
69            pws.push_seg( bez_to_sbasis(hnd, self.curve_size-1));
70	    for j in range(i, i + self.curve_size):
71		self.pts[j] = py2geom.Point(self.pts[j][0], self.pts[j][1] + y_0);
72
73	pws.push_cut(self.pts[self.handles_per_curve - 1][0]);
74	assert(pws.invariants());
75	return pws
76
77    def draw(self, cr, annotes):
78        for p in self.pts:
79            toyframework.draw_circ(cr, p)
80
81    def hit(self, mouse):
82        for i,p in enumerate(self.pts):
83            if(py2geom.distance(py2geom.Point(*mouse), p) < 5):
84                return i
85        return None
86
87    def move_to(self, hit, om, m):
88        om = py2geom.Point(*om)
89        m = py2geom.Point(*m)
90        if hit != None:
91            i,hand = hit
92            self.pts[hand] = m
93            for i in range(self.curve_size, self.handles_per_curve, self.curve_size):
94                self.pts[i-1] = py2geom.Point(self.pts[i][0],self.pts[i-1][1])
95
96            for i in range(0, self.handles_per_curve, self.curve_size):
97                for j in range(1, (self.curve_size-1)):
98                    t = float(j)/(self.curve_size-1)
99                    x = lerp(self.pts[i][0], self.pts[i+self.curve_size-1][0],t)
100                    self.pts[i+j] = py2geom.Point(x, self.pts[i+j][1])
101
102
103
104class PwToy(toyframework.Toy):
105    def __init__(self):
106        toyframework.Toy.__init__(self)
107        self.segs = 2
108        self.handles_per_curve = 4 * self.segs
109        self.curves = 2
110        self.pwsbh = []
111        self.interval_test = []
112        for a in range(self.curves):
113            self.pwsbh.append(PWSBHandle(4, self.curves))
114            self.handles.append(self.pwsbh[a])
115            for i in range(self.handles_per_curve):
116                t = 150 + 300*i/(4*self.segs)
117                self.pwsbh[a].append(t, random.uniform(0,1) * 150 + 150 - 50 * a)
118        self.interval_test.append(toyframework.PointHandle(150, 400))
119        self.interval_test.append(toyframework.PointHandle(300, 400))
120        self.handles.append(self.interval_test[0])
121        self.handles.append(self.interval_test[1])
122        self.func = "pws[0] + pws[1]"
123    def gtk_ready(self):
124        import gtk
125        self.sb_entry = gtk.Entry()
126        self.sb_entry.connect("changed", self.sb_changed)
127        toyframework.get_vbox().add(self.sb_entry)
128        toyframework.get_vbox().show_all()
129        self.sb_entry.set_text(self.func)
130    def sb_changed(self, sb):
131        self.func = sb.get_text()
132        self.redraw()
133    def draw(self, cr, pos, save):
134        cr.set_source_rgba (0., 0., 0., 1)
135        cr.set_line_width (1)
136
137        pws = [self.pwsbh[i].value() for i in range(self.curves)]
138        for p in pws:
139            cairo_pw(cr, p)
140        cr.stroke()
141
142        d = locals().copy()
143        for i in dir(py2geom):
144            d[i] = py2geom.__dict__[i]
145        d['l2s'] = l2s
146        d['constant'] = constant
147        pw_out = eval(self.func, d)
148
149        bs = py2geom.bounds_local(pw_out, py2geom.OptInterval(
150                          py2geom.Interval(self.interval_test[0].pos[0],
151                                           self.interval_test[1].pos[0])));
152        if not bs.isEmpty():
153            bs = bs.toInterval()
154            for ph in self.interval_test:
155                ph.pos= py2geom.Point(ph.pos[0], bs.middle())
156            cr.save()
157            cr.set_source_rgba (.0, 0.25, 0.5, 1.)
158            cr.rectangle(self.interval_test[0].pos[0], bs.min(),
159                         self.interval_test[1].pos[0]-self.interval_test[0].pos[0], bs.extent())
160            cr.stroke()
161        bs = py2geom.bounds_exact(pw_out);
162        cr.set_source_rgba (0.25, 0.25, .5, 1.);
163        if not bs.isEmpty():
164            bs = bs.toInterval()
165            cairo_horiz(cr, bs.middle(), pw_out.cuts);
166            cr.stroke()
167        cr.restore()
168
169        cr.set_source_rgba (0., 0., .5, 1.);
170        cairo_pw(cr, pw_out)
171        cr.stroke()
172
173
174        self.notify = str(bs)
175        toyframework.Toy.draw(self, cr, pos, save)
176
177t = PwToy()
178import sys
179
180toyframework.init(sys.argv, t, 500, 500)
181