comparison tools/geometry_twister.py @ 378:64738befdf3b

bringing in the changes from the build_system_rework branch in preparation for the 0.3.0 release. This commit will require the Jan2010 devkit. Clients will also need to be modified to the new way to import fife.
author vtchill@33b003aa-7bff-0310-803a-e67f0ece8222
date Mon, 11 Jan 2010 23:34:52 +0000
parents
children 81641655bc38
comparison
equal deleted inserted replaced
377:fe6fb0e0ed23 378:64738befdf3b
1 #!/usr/bin/env python
2 import Tkinter as TK
3 import math, time
4
5 LMB = 0x100
6 RMB = 0x400
7 DBLCLICK_TRESHOLD = 0.3 # secs
8
9 class Shape(object):
10 def __init__(self, center_pt, sidecount):
11 assert (sidecount in (4, 6))
12 self.rotation = 0
13 self.tilting = 0
14 self.zoomval = 70
15 self.center_pt = center_pt
16 startangle = 0
17 if sidecount == 6:
18 startangle = math.pi / 6.0
19 self.angles = [startangle + 2 * math.pi * (s / float(sidecount)) for s in xrange(sidecount-1)]
20 shape_pts = [(0,0,0)]
21 maxy, miny = 0, 0
22 for a in self.angles:
23 x, y, z = shape_pts[-1]
24 newx, newy = math.cos(a) + x, math.sin(a) + y
25 maxy, miny = max(maxy, newy), min(miny, newy)
26 shape_pts.append((newx, newy, 0))
27 xmove, ymove = 0.5, (abs(miny) + maxy) / 2
28 self.shape_pts = [(pt[0]-xmove, pt[1]-ymove, 0) for pt in shape_pts]
29 self.reflections = self.create_shape_reflections()
30
31 def create_shape_reflections(self):
32 reflections = []
33 sides = len(self.shape_pts)
34 if sides == 4:
35 for x,y in ((1,0), (1,1), (0,1), (-1,1), (-1,0), (-1,-1), (0,-1), (1,-1)):
36 reflections.append([(pt[0]+x, pt[1]+y, 0) for pt in self.shape_pts])
37 elif sides == 6:
38 W = math.cos(math.pi/6.0)
39 H = math.sin(math.pi/6.0)
40 for x,y in ((2*W,0), (W,1+H), (-W,1+H), (-2*W,0), (-W,-1-H)):
41 reflections.append([(pt[0]+x, pt[1]+y, 0) for pt in self.shape_pts])
42 return reflections
43
44 def tilt(self, degrees):
45 self.tilting += math.radians(degrees)
46 if self.tilting < 0:
47 self.tilting = 0
48 elif self.tilting > math.pi/2 * 0.95:
49 self.tilting = math.pi/2 * 0.95
50
51 def rotate(self, degrees):
52 self.rotation += math.radians(degrees)
53 if self.rotation < 0:
54 self.rotation = 0
55 elif self.rotation > math.pi/2 * 0.95:
56 self.rotation = math.pi/2 * 0.95
57
58 def zoom(self, amount):
59 self.zoomval += amount
60 if self.zoomval < 10:
61 self.zoomval = 10
62 if self.zoomval > 150:
63 self.zoomval = 150
64
65 def transform_pts(self, pts):
66 rotated_pts = []
67 for x, y, z in pts:
68 rotated_pts.append((x * math.cos(self.rotation) - y * math.sin(self.rotation),
69 -(x * math.sin(self.rotation) + y * math.cos(self.rotation)),
70 z))
71 tilted_pts = []
72 for x, y, z in rotated_pts:
73 tilted_pts.append((x,
74 y * math.cos(self.tilting) - z * math.sin(self.tilting),
75 z * math.sin(self.tilting) + z * math.cos(self.tilting)))
76
77 zoomed_pts = []
78 for x, y, z in tilted_pts:
79 zoomed_pts.append((int(round(self.center_pt[0] + self.zoomval * x)),
80 int(round(self.center_pt[1] + self.zoomval * y))))
81 return zoomed_pts
82
83 def get_screen_pts(self):
84 return self.transform_pts(self.shape_pts)
85
86 def get_screen_bounding_box(self, screenpts):
87 x2, y2 = x1, y1 = screenpts[0]
88 for pt in screenpts:
89 x1, y1 = min(x1, pt[0]), min(y1, pt[1])
90 x2, y2 = max(x2, pt[0]), max(y2, pt[1])
91 return (x1, y1), (x2, y2)
92
93 def get_reflections(self):
94 return [self.transform_pts(reflection) for reflection in self.reflections]
95
96 def get_tilting(self):
97 return math.degrees(self.tilting)
98
99 def get_rotation(self):
100 return math.degrees(self.rotation)
101
102 class Gui(object):
103 W = 400
104 H = 400
105 def __init__(self):
106 self.sides = (4, 6)
107 self.cur_side_index = 0
108 self.shape = Shape((Gui.W/2, Gui.H/2), self.sides[0])
109 self.prev_mouse_pt = (0, 0)
110 self.root = TK.Tk()
111 self.canvas = TK.Canvas(self.root, width=Gui.W, height=Gui.H)
112 self.canvas.create_window(285, 280, window=TK.Frame(self.canvas, relief=TK.GROOVE, borderwidth=2), anchor=TK.CENTER)
113 self.canvas.bind('<Motion>', self.on_move)
114 self.canvas.bind('<ButtonPress-1>', self.on_mb)
115 self.canvas.bind('<ButtonPress-3>', self.on_mb)
116 self.canvas.bind('<Configure>', self.on_resize)
117 self.canvas.pack()
118 self.update_view()
119 self.last_click_time = time.time()
120
121 def on_resize(self, event):
122 pass
123
124 def on_move(self, event):
125 if (event.state & RMB):
126 self.shape.zoom((self.prev_mouse_pt[1] - event.y) / 3)
127 self.update_view()
128 elif (event.state & LMB):
129 self.shape.rotate((self.prev_mouse_pt[0] - event.x) / 2)
130 self.shape.tilt((self.prev_mouse_pt[1] - event.y) / 2)
131 self.update_view()
132 self.prev_mouse_pt = (event.x, event.y)
133
134 def on_mb(self, event):
135 self.prev_mouse_pt = (event.x, event.y)
136 if (time.time() - self.last_click_time) < DBLCLICK_TRESHOLD:
137 self.cur_side_index = (self.cur_side_index+1) % len(self.sides)
138 newshape = Shape((Gui.W/2, Gui.H/2), self.sides[self.cur_side_index])
139 newshape.rotation = self.shape.rotation
140 newshape.tilting = self.shape.tilting
141 newshape.zoomval = self.shape.zoomval
142 self.shape = newshape
143 self.update_view()
144 self.last_click_time = time.time()
145
146 def draw_shape(self, shapepts, boundpts=None, fillshape=True):
147 if boundpts:
148 pt1, pt2 = boundpts
149 self.canvas.create_polygon(pt1[0], pt1[1], pt2[0], pt1[1], pt2[0], pt2[1], pt1[0], pt2[1], outline="blue", fill="", tag="boundingbox")
150 fillcolor = "white"
151 if not fillshape:
152 fillcolor = ""
153 self.canvas.create_polygon(fill=fillcolor, outline="black", tag="shape", *self.flatten_pts(shapepts))
154
155 def update_texts(self, size, transform):
156 self.canvas.create_text(10, 20, text="Bounding box size x:%d, y:%d" % (size[0], size[1]), tag="size", anchor=TK.NW)
157 self.canvas.create_text(10, 40, text="Transform x:%d, y:%d" % (transform[0], transform[1]), tag="transform", anchor=TK.NW)
158 self.canvas.create_text(10, 60, text="Tilting: %d" % self.shape.get_tilting(), tag="tilting", anchor=TK.NW)
159 self.canvas.create_text(10, 80, text="Rotation: %d" % self.shape.get_rotation(), tag="rotation", anchor=TK.NW)
160 self.canvas.create_text(10, 330, text="Doubleclick to change shape", anchor=TK.NW)
161 self.canvas.create_text(10, 350, text="Right button + drag = zoom", anchor=TK.NW)
162 self.canvas.create_text(10, 370, text="Left button + drag = tilt/rotate", anchor=TK.NW)
163
164 def flatten_pts(self, pts):
165 return [c for pt in pts for c in pt]
166
167 def update_view(self):
168 self.canvas.delete("all")
169 shapepts = self.shape.get_screen_pts()
170 boundpts = self.shape.get_screen_bounding_box(shapepts)
171 size = boundpts[1][0] - boundpts[0][0], boundpts[1][1] - boundpts[0][1]
172 reflections = self.shape.get_reflections()
173 transform = (reflections[0][0][0] - shapepts[0][0], -(reflections[0][0][1] - shapepts[0][1]))
174
175 for reflection_pts in reflections[1:]:
176 self.draw_shape(reflection_pts, fillshape=False)
177
178 self.draw_shape(shapepts, boundpts)
179 transform_boundpts = self.shape.get_screen_bounding_box(reflections[0])
180 self.draw_shape(reflections[0], transform_boundpts, fillshape=False)
181
182 self.update_texts(size, transform)
183
184 def run(self):
185 self.root.mainloop()
186
187 if __name__ == '__main__':
188 gui = Gui()
189 gui.run()