comparison utils/geometry_twister.py @ 0:4a0efb7baf70

* Datasets becomes the new trunk and retires after that :-)
author mvbarracuda@33b003aa-7bff-0310-803a-e67f0ece8222
date Sun, 29 Jun 2008 18:44:17 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4a0efb7baf70
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()