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