1
2
3
4
5
6
7
8
9
10
11
12
13
14 """Handle 3D perspective projection (client-side)"""
15
16 import sys, os, pickle, math, string
17 import Tkinter, tkFileDialog
18 import Pyro.core
19 import VisionEgg.PyroClient
20 import StringIO
21
22 __version__ = VisionEgg.release_name
23 __cvs__ = string.split('$Revision$')[1]
24 __date__ = string.join(string.split('$Date$')[1:3], ' ')
25 __author__ = 'Andrew Straw <astraw@users.sourceforge.net>'
26
29
30
31 self.left = -0.2
32 self.right = 0.2
33 self.top = 0.2
34 self.bottom = -0.2
35 self.near = 0.2
36 self.far = 20.0
37
38
39 self.eye = (0.0, 0.0, 0.0)
40 self.center = (0.0, 0.0, -1.0)
41 self.up = (0.0, 1.0, 0.0)
42
43 -class CallbackEntry(Tkinter.Entry):
44 - def __init__(self,master=None,callback=None,**kw):
45 Tkinter.Entry.__init__(self,master, **kw)
46 self.bind('<Return>',callback)
47 self.bind('<Tab>',callback)
48
50 - def __init__(self, master=None, auto_connect=0, server_hostname='', server_port=7766, **kw):
51 Tkinter.Frame.__init__(self,master,**kw)
52 self.pyro_client = None
53 self.entry_width = 10
54 self.connected = 0
55 self.meta_params = ScreenPositionParameters()
56 self.loopable_variables = {}
57
58 row = 0
59 Tkinter.Label(self,
60 text="3D Perspective Calibration",
61 font=("Helvetica",12,"bold")).grid(row=row,column=0,columnspan=2)
62
63 row += 1
64 Tkinter.Label(self,
65 text="This dialog allows you to enter acheive "+\
66 "the proper perspective distortion for 3D scenes."
67 ).grid(row=row,column=0,columnspan=2)
68
69 if not auto_connect:
70 row += 1
71
72 connected_frame = Tkinter.Frame(self)
73 connected_frame.grid(row=row,column=0,columnspan=2,sticky=Tkinter.W+Tkinter.E)
74 connected_frame.columnconfigure(0,weight=1)
75 connected_frame.columnconfigure(1,weight=1)
76 connected_frame.columnconfigure(2,weight=1)
77
78 self.connected_label = Tkinter.Label(connected_frame,text="Server status: Not connected")
79 self.connected_label.grid(row=0,column=0)
80 Tkinter.Button(connected_frame,text="Connect",command=self.connect).grid(row=0,column=1)
81 Tkinter.Button(connected_frame,text="Quit server",command=self.quit_server).grid(row=0,column=2)
82
83 row += 1
84 param_frame = Tkinter.Frame(self)
85 param_frame.grid(row=row,column=0,sticky=Tkinter.N)
86 param_frame.columnconfigure(0,weight=1)
87 param_frame.columnconfigure(1,weight=1)
88
89 pf_row = 0
90 frustum_frame = Tkinter.Frame(param_frame)
91 frustum_frame.grid(row=pf_row,column=0,columnspan=2,ipady=5)
92
93 ff_row = 0
94 Tkinter.Label(frustum_frame,
95 text="Viewing volume size",
96 font=("Helvetica",12,"bold")).grid(row=ff_row,column=0,columnspan=3,ipady=5)
97
98 ff_row += 1
99 Tkinter.Label(frustum_frame,text="Left:").grid(row=ff_row,column=0)
100 self.left_tk_var = Tkinter.DoubleVar()
101 self.left_tk_var.set(self.meta_params.left)
102 CallbackEntry(frustum_frame,
103 self.send_values,
104 width=self.entry_width,
105 textvariable=self.left_tk_var).grid(row=ff_row,column=1)
106
107 ff_row += 1
108 Tkinter.Label(frustum_frame,text="Right:").grid(row=ff_row,column=0)
109 self.right_tk_var = Tkinter.DoubleVar()
110 self.right_tk_var.set(self.meta_params.right)
111 CallbackEntry(frustum_frame,
112 self.send_values,
113 width=self.entry_width,
114 textvariable=self.right_tk_var).grid(row=ff_row,column=1)
115
116 ff_row += 1
117 Tkinter.Label(frustum_frame,text="Top:").grid(row=ff_row,column=0)
118 self.top_tk_var = Tkinter.DoubleVar()
119 self.top_tk_var.set(self.meta_params.top)
120 CallbackEntry(frustum_frame,
121 self.send_values,
122 width=self.entry_width,
123 textvariable=self.top_tk_var).grid(row=ff_row,column=1)
124
125 ff_row += 1
126 Tkinter.Label(frustum_frame,text="Bottom:").grid(row=ff_row,column=0)
127 self.bottom_tk_var = Tkinter.DoubleVar()
128 self.bottom_tk_var.set(self.meta_params.bottom)
129 CallbackEntry(frustum_frame,
130 self.send_values,
131 width=self.entry_width,
132 textvariable=self.bottom_tk_var).grid(row=ff_row,column=1)
133
134 ff_row += 1
135 Tkinter.Label(frustum_frame,text="Near:").grid(row=ff_row,column=0)
136 self.near_tk_var = Tkinter.DoubleVar()
137 self.near_tk_var.set(self.meta_params.near)
138 CallbackEntry(frustum_frame,
139 self.send_values,
140 width=self.entry_width,
141 textvariable=self.near_tk_var).grid(row=ff_row,column=1)
142
143 ff_row += 1
144 Tkinter.Label(frustum_frame,text="Far:").grid(row=ff_row,column=0)
145 self.far_tk_var = Tkinter.DoubleVar()
146 self.far_tk_var.set(self.meta_params.far)
147 CallbackEntry(frustum_frame,
148 self.send_values,
149 width=self.entry_width,
150 textvariable=self.far_tk_var).grid(row=ff_row,column=1)
151
152
153 qf_frame = Tkinter.Frame(frustum_frame)
154 qf_frame.grid(row=1,column=2,rowspan=ff_row)
155
156 qf_row = 0
157 Tkinter.Button(qf_frame,text="Taller",command=self.frustum_taller).grid(row=qf_row,column=0,columnspan=2)
158 qf_row += 1
159 Tkinter.Button(qf_frame,text="Narrower",command=self.frustum_narrower).grid(row=qf_row,column=0)
160 Tkinter.Button(qf_frame,text="Wider",command=self.frustum_wider).grid(row=qf_row,column=1)
161 qf_row += 1
162 Tkinter.Button(qf_frame,text="Shorter",command=self.frustum_shorter).grid(row=qf_row,column=0,columnspan=2)
163
164 qf_row = 0
165 Tkinter.Button(qf_frame,text="Up",command=self.frustum_up).grid(row=qf_row,column=2,columnspan=2)
166 qf_row += 1
167 Tkinter.Button(qf_frame,text="Left",command=self.frustum_left).grid(row=qf_row,column=2)
168 Tkinter.Button(qf_frame,text="Right",command=self.frustum_right).grid(row=qf_row,column=3)
169 qf_row += 1
170 Tkinter.Button(qf_frame,text="Down",command=self.frustum_down).grid(row=qf_row,column=2,columnspan=2)
171
172 pf_row += 1
173 lookat_frame = Tkinter.Frame(param_frame)
174 lookat_frame.grid(row=pf_row,column=0,columnspan=2,ipady=5)
175
176 la_row = 0
177 Tkinter.Label(lookat_frame,
178 text="Viewing volume orientation",
179 font=("Helvetica",12,"bold")).grid(row=la_row,column=0,columnspan=3,ipady=5)
180
181 la_row += 1
182 Tkinter.Label(lookat_frame,text="eye X:").grid(row=la_row,column=0)
183 self.eye_x_tk_var = Tkinter.DoubleVar()
184 self.eye_x_tk_var.set(self.meta_params.eye[0])
185 CallbackEntry(lookat_frame,
186 self.send_values,
187 width=self.entry_width,
188 textvariable=self.eye_x_tk_var).grid(row=la_row,column=1)
189
190 la_row += 1
191 Tkinter.Label(lookat_frame,text="eye Y:").grid(row=la_row,column=0)
192 self.eye_y_tk_var = Tkinter.DoubleVar()
193 self.eye_y_tk_var.set(self.meta_params.eye[1])
194 CallbackEntry(lookat_frame,
195 self.send_values,
196 width=self.entry_width,
197 textvariable=self.eye_y_tk_var).grid(row=la_row,column=1)
198
199 la_row += 1
200 Tkinter.Label(lookat_frame,text="eye Z:").grid(row=la_row,column=0)
201 self.eye_z_tk_var = Tkinter.DoubleVar()
202 self.eye_z_tk_var.set(self.meta_params.eye[2])
203 CallbackEntry(lookat_frame,
204 self.send_values,
205 width=self.entry_width,
206 textvariable=self.eye_z_tk_var).grid(row=la_row,column=1)
207
208 la_row += 1
209 Tkinter.Label(lookat_frame,text="look at X:").grid(row=la_row,column=0)
210 self.center_x_tk_var = Tkinter.DoubleVar()
211 self.center_x_tk_var.set(self.meta_params.center[0])
212 CallbackEntry(lookat_frame,
213 self.send_values,
214 width=self.entry_width,
215 textvariable=self.center_x_tk_var).grid(row=la_row,column=1)
216
217 quick_la_frame = Tkinter.Frame(lookat_frame)
218 quick_la_frame.grid(row=la_row,column=2,rowspan=3)
219 qla_row = 0
220 Tkinter.Button(quick_la_frame,text="Look at az -",command=self.az_decrease).grid(row=qla_row,column=0)
221 Tkinter.Button(quick_la_frame,text="Look at az +",command=self.az_increase).grid(row=qla_row,column=1)
222 self.look_at_az_str = Tkinter.StringVar()
223 Tkinter.Label(quick_la_frame,textvariable=self.look_at_az_str).grid(row=qla_row,column=2)
224
225 qla_row += 1
226 Tkinter.Button(quick_la_frame,text="Look at el -",command=self.el_decrease).grid(row=qla_row,column=0)
227 Tkinter.Button(quick_la_frame,text="Look at el +",command=self.el_increase).grid(row=qla_row,column=1)
228 self.look_at_el_str = Tkinter.StringVar()
229 Tkinter.Label(quick_la_frame,textvariable=self.look_at_el_str).grid(row=qla_row,column=2)
230
231 az,el = self.get_az_el(self.meta_params.center)
232 self.look_at_az_str.set("%.1f"%az)
233 self.look_at_el_str.set("%.1f"%el)
234
235 la_row += 1
236 Tkinter.Label(lookat_frame,text="look at Y:").grid(row=la_row,column=0)
237 self.center_y_tk_var = Tkinter.DoubleVar()
238 self.center_y_tk_var.set(self.meta_params.center[1])
239 CallbackEntry(lookat_frame,
240 self.send_values,
241 width=self.entry_width,
242 textvariable=self.center_y_tk_var).grid(row=la_row,column=1)
243
244 la_row += 1
245 Tkinter.Label(lookat_frame,text="look at Z:").grid(row=la_row,column=0)
246 self.center_z_tk_var = Tkinter.DoubleVar()
247 self.center_z_tk_var.set(self.meta_params.center[2])
248 CallbackEntry(lookat_frame,
249 self.send_values,
250 width=self.entry_width,
251 textvariable=self.center_z_tk_var).grid(row=la_row,column=1)
252
253 la_row += 1
254 Tkinter.Label(lookat_frame,text="up X:").grid(row=la_row,column=0)
255 self.up_x_tk_var = Tkinter.DoubleVar()
256 self.up_x_tk_var.set(self.meta_params.up[0])
257 CallbackEntry(lookat_frame,
258 self.send_values,
259 width=self.entry_width,
260 textvariable=self.up_x_tk_var).grid(row=la_row,column=1)
261
262 quick_up_frame = Tkinter.Frame(lookat_frame)
263 quick_up_frame.grid(row=la_row,column=2,rowspan=3)
264 qup_row = 0
265 Tkinter.Button(quick_up_frame,text="Up az -",command=self.up_az_decrease).grid(row=qup_row,column=0)
266 Tkinter.Button(quick_up_frame,text="Up az +",command=self.up_az_increase).grid(row=qup_row,column=1)
267 self.up_az_str = Tkinter.StringVar()
268 Tkinter.Label(quick_up_frame,textvariable=self.up_az_str).grid(row=qup_row,column=2)
269 qup_row += 1
270 Tkinter.Button(quick_up_frame,text="Up el -",command=self.up_el_decrease).grid(row=qup_row,column=0)
271 Tkinter.Button(quick_up_frame,text="Up el +",command=self.up_el_increase).grid(row=qup_row,column=1)
272 self.up_el_str = Tkinter.StringVar()
273 Tkinter.Label(quick_up_frame,textvariable=self.up_el_str).grid(row=qup_row,column=2)
274
275 az,el = self.get_az_el(self.meta_params.up)
276 self.up_az_str.set("%.1f"%az)
277 self.up_el_str.set("%.1f"%el)
278
279 la_row += 1
280 Tkinter.Label(lookat_frame,text="up Y:").grid(row=la_row,column=0)
281 self.up_y_tk_var = Tkinter.DoubleVar()
282 self.up_y_tk_var.set(self.meta_params.up[1])
283 CallbackEntry(lookat_frame,
284 self.send_values,
285 width=self.entry_width,
286 textvariable=self.up_y_tk_var).grid(row=la_row,column=1)
287
288 la_row += 1
289 Tkinter.Label(lookat_frame,text="up Z:").grid(row=la_row,column=0)
290 self.up_z_tk_var = Tkinter.DoubleVar()
291 self.up_z_tk_var.set(self.meta_params.up[2])
292 CallbackEntry(lookat_frame,
293 self.send_values,
294 width=self.entry_width,
295 textvariable=self.up_z_tk_var).grid(row=la_row,column=1)
296
297 row += 1
298 button_row_frame = Tkinter.Frame(self)
299 button_row_frame.grid(row=row,column=0,ipady=5)
300 Tkinter.Label(button_row_frame,
301 text="File operations",
302 font=("Helvetica",12,"bold")).grid(row=0,column=0,columnspan=2,ipady=5)
303 Tkinter.Button(button_row_frame,text="Save...",command=self.save).grid(row=1,column=0)
304 Tkinter.Button(button_row_frame,text="Load...",command=self.load).grid(row=1,column=1)
305
306 if auto_connect:
307 self.connect(server_hostname,server_port)
308
310 self.left_tk_var.set(self.meta_params.left*(1.0/1.05))
311 self.right_tk_var.set(self.meta_params.right*(1.0/1.05))
312 self.send_values()
313
315 self.left_tk_var.set(self.meta_params.left*1.05)
316 self.right_tk_var.set(self.meta_params.right*1.05)
317 self.send_values()
318
320 self.bottom_tk_var.set(self.meta_params.bottom*(1.0/1.05))
321 self.top_tk_var.set(self.meta_params.top*(1.0/1.05))
322 self.send_values()
323
325 self.bottom_tk_var.set(self.meta_params.bottom*1.05)
326 self.top_tk_var.set(self.meta_params.top*1.05)
327 self.send_values()
328
330 self.left_tk_var.set(self.meta_params.left*1.025)
331 self.right_tk_var.set(self.meta_params.right*(1.0/1.025))
332 self.send_values()
333
335 self.left_tk_var.set(self.meta_params.left*(1.0/1.025))
336 self.right_tk_var.set(self.meta_params.right*1.025)
337 self.send_values()
338
340 self.bottom_tk_var.set(self.meta_params.bottom*1.025)
341 self.top_tk_var.set(self.meta_params.top*(1.0/1.025))
342 self.send_values()
343
345 self.bottom_tk_var.set(self.meta_params.bottom*(1.0/1.025))
346 self.top_tk_var.set(self.meta_params.top*1.025)
347 self.send_values()
348
350 x,y,z = xyz_tuple
351 r = math.sqrt(x*x + y*y + z*z)
352 theta = math.acos(-y/r)
353 rh = r * math.sin(theta)
354 phi = math.atan2(-z,x)
355 az = -(phi * 180.0/math.pi - 90.0)
356 el = theta * 180.0/math.pi - 90.0
357 return az,el
358
360 az,el = az_el
361 theta = (el + 90.0) / 180.0 * math.pi
362 phi = (az + 90.0) / 180.0 * math.pi
363 y = -math.cos(theta)
364 rh = math.sin(theta)
365 x = -rh * math.cos(phi)
366 z = -rh * math.sin(phi)
367 return x,y,z
368
370 az,el = self.get_az_el(self.meta_params.center)
371 az = az + 5.0
372 self.look_at_az_str.set("%.1f"%az)
373 x,y,z = self.get_xyz((az,el))
374 self.center_x_tk_var.set("%.4f"%x)
375 self.center_y_tk_var.set("%.4f"%y)
376 self.center_z_tk_var.set("%.4f"%z)
377 self.send_values()
378
380 az,el = self.get_az_el(self.meta_params.center)
381 az = az - 5.0
382 self.look_at_az_str.set("%.1f"%az)
383 x,y,z = self.get_xyz((az,el))
384 self.center_x_tk_var.set("%.4f"%x)
385 self.center_y_tk_var.set("%.4f"%y)
386 self.center_z_tk_var.set("%.4f"%z)
387 self.send_values()
388
390 az,el = self.get_az_el(self.meta_params.center)
391 el = el + 5.0
392 el = min(el,90.0)
393 self.look_at_el_str.set("%.1f"%el)
394 x,y,z = self.get_xyz((az,el))
395 self.center_x_tk_var.set("%.4f"%x)
396 self.center_y_tk_var.set("%.4f"%y)
397 self.center_z_tk_var.set("%.4f"%z)
398 self.send_values()
399
401 az,el = self.get_az_el(self.meta_params.center)
402 el = el - 5.0
403 el = max(el,-90.0)
404 self.look_at_el_str.set("%.1f"%el)
405 x,y,z = self.get_xyz((az,el))
406 self.center_x_tk_var.set("%.4f"%x)
407 self.center_y_tk_var.set("%.4f"%y)
408 self.center_z_tk_var.set("%.4f"%z)
409 self.send_values()
410
412 az,el = self.get_az_el(self.meta_params.up)
413 az = az + 5.0
414 self.up_az_str.set("%.1f"%az)
415 x,y,z = self.get_xyz((az,el))
416 self.up_x_tk_var.set("%.4f"%x)
417 self.up_y_tk_var.set("%.4f"%y)
418 self.up_z_tk_var.set("%.4f"%z)
419 self.send_values()
420
422 az,el = self.get_az_el(self.meta_params.up)
423 az = az - 5.0
424 self.up_az_str.set("%.1f"%az)
425 x,y,z = self.get_xyz((az,el))
426 self.up_x_tk_var.set("%.4f"%x)
427 self.up_y_tk_var.set("%.4f"%y)
428 self.up_z_tk_var.set("%.4f"%z)
429 self.send_values()
430
432 az,el = self.get_az_el(self.meta_params.up)
433 el = el + 5.0
434 el = min(el,90.0)
435 self.up_el_str.set("%.1f"%el)
436 x,y,z = self.get_xyz((az,el))
437 self.up_x_tk_var.set("%.4f"%x)
438 self.up_y_tk_var.set("%.4f"%y)
439 self.up_z_tk_var.set("%.4f"%z)
440 self.send_values()
441
443 az,el = self.get_az_el(self.meta_params.up)
444 el = el - 5.0
445 el = max(el,-90.0)
446 self.up_el_str.set("%.1f"%el)
447 x,y,z = self.get_xyz((az,el))
448 self.up_x_tk_var.set("%.4f"%x)
449 self.up_y_tk_var.set("%.4f"%y)
450 self.up_z_tk_var.set("%.4f"%z)
451 self.send_values()
452
454 filename = tkFileDialog.asksaveasfilename(defaultextension=".ve_3dproj",filetypes=[('Projection file','*.ve_3dproj')])
455 fd = open(filename,"wb")
456 save_dict = self.get_param_dict()
457 pickle.dump( save_dict, fd )
458
460 filename = tkFileDialog.askopenfilename(defaultextension=".ve_3dproj",filetypes=[('Projection file','*.ve_3dproj')])
461 if not filename:
462 return
463 fd = open(filename,"rb")
464 file_contents = fd.read()
465 file_contents = file_contents.replace('\r\n','\n')
466 memory_file = StringIO.StringIO(file_contents)
467 load_dict = pickle.load(memory_file)
468 self.set_param_dict( load_dict )
469 self.send_values()
470
472 """Used as basename for saving parameter files"""
473 return "screen_position"
474
476 result = {}
477 for param_name in dir(self.meta_params):
478 if param_name[:2] != '__' and param_name[-2:] != '__':
479 result[param_name] = getattr(self.meta_params,param_name)
480 return result
481
483 return "screenPositionGUI"
484
486 orig_params = dir(self.meta_params)
487 for new_param_name in new_param_dict.keys():
488 if new_param_name[:2] != '__' and new_param_name[-2:] != '__':
489 if new_param_name not in orig_params:
490 raise ValueError('Gave parameter "%s", which I do not know about.'%(new_param_name,))
491 setattr(self.meta_params,new_param_name,new_param_dict[new_param_name])
492 self.left_tk_var.set( self.meta_params.left )
493 self.right_tk_var.set( self.meta_params.right )
494 self.top_tk_var.set( self.meta_params.top )
495 self.bottom_tk_var.set( self.meta_params.bottom )
496 self.near_tk_var.set( self.meta_params.near )
497 self.far_tk_var.set( self.meta_params.far )
498 self.eye_x_tk_var.set( self.meta_params.eye[0] )
499 self.eye_y_tk_var.set( self.meta_params.eye[1] )
500 self.eye_z_tk_var.set( self.meta_params.eye[2] )
501 self.center_x_tk_var.set( self.meta_params.center[0] )
502 self.center_y_tk_var.set( self.meta_params.center[1] )
503 self.center_z_tk_var.set( self.meta_params.center[2] )
504 self.up_x_tk_var.set( self.meta_params.up[0] )
505 self.up_y_tk_var.set( self.meta_params.up[1] )
506 self.up_z_tk_var.set( self.meta_params.up[2] )
507
509 result = []
510 for param_name in dir(self.meta_params):
511 if param_name[:2] != '__' and param_name[-2:] != '__':
512 value = getattr(self.meta_params,param_name)
513 value_string = str(value)
514 result.append((param_name,value_string))
515 return result
516
518 return self.loopable_variables.keys()
519
521 meta_param_var_name,tk_var = self.loopable_variables[easy_name]
522 setattr(self.meta_params,meta_param_var_name,value)
523 tk_var.set(value)
524 self.update()
525
527 self.meta_params.left = self.left_tk_var.get()
528 self.meta_params.right = self.right_tk_var.get()
529 self.meta_params.top = self.top_tk_var.get()
530 self.meta_params.bottom = self.bottom_tk_var.get()
531 self.meta_params.near = self.near_tk_var.get()
532 self.meta_params.far = self.far_tk_var.get()
533 self.meta_params.eye = (self.eye_x_tk_var.get(),
534 self.eye_y_tk_var.get(),
535 self.eye_z_tk_var.get())
536 self.meta_params.center = (self.center_x_tk_var.get(),
537 self.center_y_tk_var.get(),
538 self.center_z_tk_var.get())
539 self.meta_params.up = (self.up_x_tk_var.get(),
540 self.up_y_tk_var.get(),
541 self.up_z_tk_var.get())
542
543 if self.connected:
544 self.projection_controller.set_parameters( self.meta_params )
545
546 - def connect(self,server_hostname='',server_port=7766):
547 self.pyro_client = VisionEgg.PyroClient.PyroClient(server_hostname,server_port)
548
549 self.projection_controller = self.pyro_client.get("projection_controller")
550
551 self.connected = 1
552 if hasattr(self, 'connected_label'):
553 self.connected_label.config(text="Server status: Connected")
554 self.send_values()
555 else:
556 self.meta_params = self.projection_controller.get_parameters()
557 self.set_param_dict( {} )
558
560 self.projection_controller.quit_server()
561 self.connected = 0
562 self.connected_label.config(text="Server status: Not connected")
563
564 if __name__=='__main__':
565 frame = ScreenPositionControlFrame()
566 frame.pack(expand=1,fill=Tkinter.BOTH)
567 frame.winfo_toplevel().title("%s"%(os.path.basename(os.path.splitext(sys.argv[0])[0]),))
568 frame.mainloop()
569