Package mmLib :: Module R3DDriver
[hide private]
[frames] | no frames]

Source Code for Module mmLib.R3DDriver

  1  ## Copyright 2002-2010 by PyMMLib Development Group (see AUTHORS file) 
  2  ## This code is part of the PyMMLib distribution and governed by 
  3  ## its license.  Please see the LICENSE file that should have been 
  4  ## included as part of this package. 
  5  """Viewer.py graphics driver for producing a output file 
  6  for the Raster3D ray tracer. 
  7  """ 
  8  import subprocess 
  9  import copy 
 10  import random 
 11  import math 
 12   
 13  try: 
 14      import numpy 
 15      try: 
 16          from numpy.oldnumeric import linear_algebra as linalg 
 17      except ImportError: 
 18          from numpy.linalg import old as linalg 
 19  except ImportError: 
 20      import NumericCompat as numpy 
 21      from NumericCompat import linalg 
 22   
 23  import ConsoleOutput 
 24  import Constants 
 25  import Gaussian 
 26  import AtomMath 
 27   
 28  ## constants 
 29  MARGIN          = 1.15 
 30  BASE_LINE_WIDTH = 0.05 
 31   
 32   
33 -class FinishMe(Exception): pass
34 35
36 -def dot43(M, x):
37 """M is a 4x4 rotation-translation matrix, x is a 3x1 vector. Returns 38 the 3x1 vector of x transformed by M. 39 """ 40 return numpy.array((M[0,0]*x[0] + M[0,1]*x[1] + M[0,2]*x[2] + M[0,3], 41 M[1,0]*x[0] + M[1,1]*x[1] + M[1,2]*x[2] + M[1,3], 42 M[2,0]*x[0] + M[2,1]*x[1] + M[2,2]*x[2] + M[2,3]), float)
43
44 -class TeeWrite(object):
45 - def __init__(self, *fils):
46 self.fils = fils
47 - def write(self, buff):
48 for fil in self.fils: 49 fil.write(buff)
50 - def close(self):
51 for fil in self.fils: 52 fil.close()
53
54 -class Raster3DDriver(object):
55 """Viewer.py graphics driver for producing a output file 56 for the Raster3D ray tracer. 57 """
58 - def __init__(self):
59 self.render_program_path = Constants.RENDER 60 self.render_png_path = "ray.png" 61 self.render_stdin = None 62 63 self.glr_init_state()
64
65 - def glr_set_render_program_path(self, render_path):
66 self.render_program_path = render_path
67
68 - def glr_set_render_stdin(self, stdin):
69 self.render_stdin = stdin
70
71 - def glr_set_render_png_path(self, path):
72 self.render_png_path = path
73
74 - def glr_init_state(self):
75 """Re-initalizes driver state variables. 76 """ 77 self.object_list = [] 78 79 self.matrix = numpy.identity(4, float) 80 self.matrix_stack = [] 81 82 self.line_width = 1.0 * BASE_LINE_WIDTH 83 84 self.normal = None 85 self.normalize = False 86 87 self.light_two_sides = False 88 89 self.width = 400 90 self.height = 400 91 self.bg_color_rgbf = (0.0, 0.0, 0.0) 92 self.ambient_light = 0.2 93 self.specular_light = 1.0 94 95 self.material_color_r = 1.0 96 self.material_color_g = 1.0 97 self.material_color_b = 1.0 98 self.material_alpha = 1.0
99
100 - def glr_compile_supported(self):
101 """Returns True if draw compiling is supported by the driver. 102 """ 103 return False
104
105 - def glr_render_begin( 106 self, 107 width = 200, 108 height = 100, 109 zoom = 50, 110 near = 0, 111 far = 0, 112 bg_color_rgbf = (0.0, 0.0, 0.0), 113 ambient_light = 0.2, 114 diffuse_light = 1.0, 115 specular_light = 1.0, 116 **args):
117 """Sets up lighting and OpenGL options before scene rendering. 118 """ 119 self.glr_init_state() 120 121 self.width = width 122 self.height = height 123 self.zoom = zoom 124 125 self.front_clip = near 126 self.back_clip = far 127 128 ## Raster3D assumes r,g,b triplits are squared 129 r, g, b = bg_color_rgbf 130 r = r*r 131 g = g*g 132 b = b*b 133 self.bg_color_rgbf = (r,g,b) 134 135 ## the lighting model for Raster3D is not quite the same as 136 ## OpenGL; this conversion gets it close 137 total_light = ambient_light + diffuse_light + specular_light 138 139 self.ambient = ambient_light / total_light 140 self.specular = specular_light / total_light 141 self.phong = 3 142 143 ## initial material state 144 self.object_list.append((8, 0.0, 0, self.front_clip, self.back_clip))
145
146 - def glr_construct_header(self):
147 """Creates the header for the render program. 148 """ 149 tsz_width = 16 150 tsz_height = 16 151 152 xtiles = int(round(self.width / float(tsz_width))) 153 ytiles = int(round(self.height / float(tsz_height))) 154 155 pixel_width = xtiles * tsz_width 156 pixel_height = ytiles * tsz_height 157 158 ## self.zoom is the horizontal number of Angstroms shown in the 159 ## image, this must be converted to the Raster3D zoom parameter 160 ## which is the number of Angstroms of the shortest dimention 161 if pixel_width > pixel_height: 162 r = float(pixel_height) / float(pixel_width) 163 z = self.zoom * r 164 else: 165 z = self.zoom 166 167 self.header_list = [ 168 "mmLib Generated Raster3D Output", 169 "%d %d tiles in x,y" % (xtiles, ytiles), 170 "%d %d pixels (x,y) per tile" % (tsz_width, tsz_height), 171 "4 anti-aliasing level 4; 3x3->2x2", 172 "%4.2f %4.2f %4.2f background(rgb)" % self.bg_color_rgbf, 173 "F no shadows cast", 174 "%2d Phong power" % (self.phong), 175 "0.20 secondary light contribution", 176 "%4.2f ambient light contribution" % (self.ambient), 177 "%4.2f specular reflection component" % (self.specular), 178 "0.0 eye position(no perspective)", 179 "1 1 1 main light source position", 180 "1 0 0 0 4x4 view matrix", 181 "0 1 0 0", 182 "0 0 1 0", 183 "0 0 0 %f" % (z), 184 "3 mixed objects", 185 "* (free format triangle and plane descriptors)", 186 "* (free format sphere descriptors", 187 "* (free format cylinder descriptors)", 188 "16", 189 "FRONTCLIP %8.3f" % (self.front_clip), 190 "16", 191 "BACKCLIP %8.3f" % (self.back_clip), 192 ]
193
194 - def glr_render_end(self):
195 """Write out the input file for the render program. 196 """ 197 ## open r3d file, write header 198 pobj = None 199 200 if self.render_stdin is not None: 201 stdin = self.render_stdin 202 else: 203 cmdlist = [self.render_program_path, "-png", self.render_png_path, "-gamma", "1.5"] 204 try: 205 pobj = subprocess.Popen(cmdlist, 206 stdin = subprocess.PIPE, 207 stdout = subprocess.PIPE, 208 stderr = subprocess.STDOUT, 209 close_fds = True, 210 bufsize = 32768) 211 except OSError: 212 ConsoleOutput.warning("the render program failed to execute from path: %s" % ( 213 self.render_program_path)) 214 return 215 216 stdin = pobj.stdin 217 218 ## XXX: debug 219 ##r3dfil = open("/tmp/raytrace.r3d","w") 220 ##stdin = TeeWrite(stdin) 221 222 ## add required hader for the render program 223 self.glr_construct_header() 224 225 try: 226 stdin.write("\n".join(self.header_list)) 227 stdin.write("\n") 228 self.glr_write_objects(stdin) 229 except IOError, err: 230 ConsoleOutput.warning("IOError while executing %s" % (self.render_program_path)) 231 ConsoleOutput.warning(str(err)) 232 return 233 234 if self.render_stdin is not None: 235 self.render_stdin = None 236 237 if pobj is not None: 238 pobj.stdin.close() 239 pobj.wait()
240
241 - def glr_write_objects(self, stdin):
242 """Write the graphic objects to the stdin file. 243 """ 244 ## write objects 245 for gob in self.object_list: 246 gob_type = gob[0] 247 248 ## triangle 249 if gob_type==1: 250 stdin.write( 251 "1\n"\ 252 "%8.3f %8.3f %8.3f "\ 253 "%8.3f %8.3f %8.3f "\ 254 "%8.3f %8.3f %8.3f "\ 255 "%4.2f %4.2f %4.2f\n" % ( 256 gob[1][0], gob[1][1], gob[1][2], 257 gob[2][0], gob[2][1], gob[2][2], 258 gob[3][0], gob[3][1], gob[3][2], 259 gob[4], gob[5], gob[6])) 260 261 ## sphere 262 elif gob_type==2: 263 stdin.write( 264 "2\n"\ 265 "%8.3f %8.3f %8.3f %8.3f %4.2f %4.2f %4.2f\n" % ( 266 gob[1][0], gob[1][1], gob[1][2], 267 gob[2], 268 gob[3], gob[4], gob[5])) 269 270 ## round-ended cylinder 271 elif gob_type==3: 272 stdin.write( 273 "3\n"\ 274 "%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f %8.3f 0 "\ 275 "%4.2f %4.2f %4.2f\n" % ( 276 gob[1][0], gob[1][1], gob[1][2], 277 gob[3], 278 gob[2][0], gob[2][1], gob[2][2], 279 gob[4], gob[5], gob[6])) 280 281 ## flat-ended cylinder 282 elif gob_type==5: 283 stdin.write( 284 "5\n"\ 285 "%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f %8.3f 0 "\ 286 "%4.2f %4.2f %4.2f\n" % ( 287 gob[1][0], gob[1][1], gob[1][2], 288 gob[3], 289 gob[2][0], gob[2][1], gob[2][2], 290 gob[4], gob[5], gob[6])) 291 292 ## normal 293 elif gob_type==7: 294 stdin.write( 295 "7\n"\ 296 "%8.3f %8.3f %8.3f "\ 297 "%8.3f %8.3f %8.3f "\ 298 "%8.3f %8.3f %8.3f\n" % ( 299 gob[1][0], gob[1][1], gob[1][2], 300 gob[2][0], gob[2][1], gob[2][2], 301 gob[3][0], gob[3][1], gob[3][2] )) 302 303 ## material properties 304 elif gob_type==8: 305 306 stdin.write( 307 "%d\n"\ 308 "-1 -1 -1 -1 -1 %4.2f %1d 0 0 2\n"\ 309 "FRONTCLIP %8.3f\n"\ 310 "BACKCLIP %8.2f\n" % gob) 311 312 ## ellipse 313 elif gob_type==14: 314 q = gob[6] 315 316 stdin.write( 317 "14\n"\ 318 "%8.3f %8.3f %8.3f "\ 319 "%8.3f "\ 320 "%4.2f %4.2f %4.2f "\ 321 "%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f 0 0 0 %8.3f\n" % ( 322 gob[1][0], gob[1][1], gob[1][2], 323 gob[2], 324 gob[3], gob[4], gob[5], 325 q[0,0], q[1,1], q[2,2], q[0,1], q[1,2], q[0,2], 326 gob[7]))
327
328 - def glr_push_matrix(self):
329 """ 330 """ 331 assert len(self.matrix_stack)<=25 332 self.matrix_stack.append(self.matrix.copy())
333
334 - def glr_pop_matrix(self):
335 """ 336 """ 337 self.matrix = self.matrix_stack[-1] 338 self.matrix_stack = self.matrix_stack[:-1]
339
340 - def glr_translate(self, t):
341 """Translates the scene by vector t. 342 """ 343 M = numpy.array( [[ 1.0, 0.0, 0.0, t[0]], 344 [ 0.0, 1.0, 0.0, t[1]], 345 [ 0.0, 0.0, 1.0, t[2]], 346 [ 0.0, 0.0, 0.0, 1.0]], float) 347 348 self.matrix = numpy.dot(self.matrix, M)
349
350 - def glr_translate3(self, x, y, z):
351 """ 352 """ 353 M = numpy.array( [[ 1.0, 0.0, 0.0, x], 354 [ 0.0, 1.0, 0.0, y], 355 [ 0.0, 0.0, 1.0, z], 356 [ 0.0, 0.0, 0.0, 1.0]], float) 357 358 self.matrix = numpy.dot(self.matrix, M)
359
360 - def glr_mult_matrix_Rt(self, R, t):
361 """Return the current matrix as a 3x3 rotation matrix R and 3x1 362 translation vector t. 363 """ 364 M = numpy.array( [[R[0,0], R[0,1], R[0,2], t[0]], 365 [R[1,0], R[1,1], R[1,2], t[1]], 366 [R[2,0], R[2,1], R[2,2], t[2]], 367 [ 0.0, 0.0, 0.0, 1.0]], float) 368 369 self.matrix = numpy.dot(self.matrix, M)
370
371 - def glr_mult_matrix_R(self, R):
372 """Multiplies the current matrix by rotation matrix R and translates 373 by t 374 """ 375 M = numpy.array( [[R[0,0], R[0,1], R[0,2], 0.0], 376 [R[1,0], R[1,1], R[1,2], 0.0], 377 [R[2,0], R[2,1], R[2,2], 0.0], 378 [ 0.0, 0.0, 0.0, 1.0]], float) 379 380 self.matrix = numpy.dot(self.matrix, M)
381
382 - def glr_rotate_axis(self, deg, axis):
383 """ 384 """ 385 R = AtomMath.rmatrixu(axis, deg*Constants.DEG2RAD) 386 self.glr_mult_matrix_R(R)
387
388 - def glr_lighting_enable(self):
389 """ 390 """ 391 pass
392
393 - def glr_lighting_disable(self):
394 """ 395 """ 396 pass
397
398 - def glr_set_line_width(self, width):
399 """ 400 """ 401 self.line_width = width * BASE_LINE_WIDTH
402
403 - def glr_set_material_rgb(self, r, g, b):
404 """Creates a stock rendering material colored according to the given 405 RGB values. 406 """ 407 self.material_color_r = r*r 408 self.material_color_g = g*g 409 self.material_color_b = b*b 410 411 if self.material_alpha<1.0: 412 self.material_alpha = 1.0 413 414 if self.light_two_sides==True: 415 self.object_list.append( 416 (8, 0.0, 2, 417 self.front_clip, self.back_clip)) 418 else: 419 self.object_list.append( 420 (8, 0.0, 0, 421 self.front_clip, self.back_clip))
422
423 - def glr_set_material_rgba(self, r, g, b, a):
424 """Creates a stock rendering material colored according to the given 425 RGB values. 426 """ 427 self.material_color_r = r*r 428 self.material_color_g = g*g 429 self.material_color_b = b*b 430 431 if self.material_alpha!=a: 432 self.material_alpha = a 433 434 if self.light_two_sides==True: 435 self.object_list.append( 436 (8, 1.0 - self.material_alpha, 2, 437 self.front_clip, self.back_clip)) 438 else: 439 self.object_list.append( 440 (8, 1.0 - self.material_alpha, 0, 441 self.front_clip, self.back_clip))
442
443 - def glr_vertex(self, vertex):
444 """ 445 """ 446 self.glr_vertex_func(vertex)
447
448 - def glr_vertex3(self, x, y, z):
449 """ 450 """ 451 self.glr_vertex_func((x,y,z))
452
453 - def glr_begin_lines(self):
454 """ 455 """ 456 raise FinishMe()
457
458 - def glr_begin_triangles(self):
459 """ 460 """ 461 raise FinishMe()
462
463 - def glr_begin_quads(self):
464 self.glr_vertex_func = self.glr_vertex_quads_1 465 self.glr_end = self.glr_end_quads 466 self.vertex_1 = None 467 self.normal_1 = None 468 self.vertex_2 = None 469 self.normal_2 = None 470 self.vertex_3 = None 471 self.normal_3 = None
472
473 - def glr_end_quads(self):
474 del self.glr_vertex_func 475 del self.glr_end 476 del self.vertex_1 477 del self.normal_1 478 del self.vertex_2 479 del self.normal_2 480 del self.vertex_3 481 del self.normal_3
482
483 - def glr_vertex_quads_1(self, vertex):
484 self.glr_vertex_func = self.glr_vertex_quads_2 485 self.normal_1 = self.normal 486 self.vertex_1 = dot43(self.matrix, vertex)
487
488 - def glr_vertex_quads_2(self, vertex):
489 self.glr_vertex_func = self.glr_vertex_quads_3 490 self.normal_2 = self.normal 491 self.vertex_2 = dot43(self.matrix, vertex)
492
493 - def glr_vertex_quads_3(self, vertex):
494 self.glr_vertex_func = self.glr_vertex_quads_4 495 self.normal_3 = self.normal 496 self.vertex_3 = dot43(self.matrix, vertex)
497
498 - def glr_vertex_quads_4(self, vertex):
499 self.glr_vertex_func = self.glr_vertex_quads_1 500 501 normal_4 = self.normal 502 vertex_4 = dot43(self.matrix, vertex) 503 504 self.object_list.append( 505 (1, 506 self.vertex_1, 507 self.vertex_2, 508 self.vertex_3, 509 self.material_color_r, 510 self.material_color_g, 511 self.material_color_b)) 512 513 self.object_list.append( 514 (7, 515 self.normal_1, 516 self.normal_2, 517 self.normal_3)) 518 519 self.object_list.append( 520 (1, 521 self.vertex_1, 522 self.vertex_3, 523 vertex_4, 524 self.material_color_r, 525 self.material_color_g, 526 self.material_color_b)) 527 528 self.object_list.append( 529 (7, 530 self.normal_1, 531 self.normal_3, 532 normal_4))
533
534 - def glr_begin_triangle_fan(self):
535 """ 536 """ 537 self.glr_vertex_func = self.glr_vertex_triangle_fan_1 538 self.glr_end = self.glr_end_triangle_fan 539 self.vertex_1 = None 540 self.vertex_2 = None 541 self.normal_1 = None 542 self.normal_2 = None
543
544 - def glr_end_triangle_fan(self):
545 """ 546 """ 547 del self.glr_vertex_func 548 del self.glr_end 549 del self.vertex_1 550 del self.vertex_2 551 del self.normal_1 552 del self.normal_2
553
554 - def glr_vertex_triangle_fan_1(self, vertex):
555 """Get (first) common fan vertex. 556 """ 557 self.glr_vertex_func = self.glr_vertex_triangle_fan_2 558 559 self.vertex_1 = dot43(self.matrix, vertex) 560 self.normal_1 = self.normal
561
562 - def glr_vertex_triangle_fan_2(self, vertex):
563 """Get second vertex. 564 """ 565 self.glr_vertex_func = self.glr_vertex_triangle_fan_3 566 567 self.vertex_2 = dot43(self.matrix, vertex) 568 self.normal_2 = self.normal
569
570 - def glr_vertex_triangle_fan_3(self, vertex):
571 """Get third vertex and beyond: construct triangles. 572 """ 573 vertex_3 = dot43(self.matrix, vertex) 574 normal_3 = self.normal 575 576 self.object_list.append( 577 (1, 578 self.vertex_1, 579 self.vertex_2, 580 vertex_3, 581 self.material_color_r, 582 self.material_color_g, 583 self.material_color_b) ) 584 585 self.object_list.append( 586 (7, 587 self.normal_1, 588 self.normal_2, 589 normal_3) ) 590 591 self.vertex_2 = vertex_3 592 self.normal_2 = normal_3
593
594 - def glr_normalize_enable(self):
595 """ 596 """ 597 self.normalize = True
598
599 - def glr_normalize_disable(self):
600 """ 601 """ 602 self.normalize = False
603
604 - def glr_normal(self, n):
605 """ 606 """ 607 ## just rotate the normal 608 R = self.matrix[:3,:3] 609 nr = numpy.dot(R, n) 610 611 if self.normalize==True: 612 self.normal = AtomMath.normalize(nr) 613 else: 614 self.normal = nr
615
616 - def glr_normal3(self, x, y, z):
617 """ 618 """ 619 ## just rotate the normal 620 R = self.matrix[:3,:3] 621 n = numpy.array([x, y, z], float) 622 nr = numpy.dot(R, n) 623 624 if self.normalize==True: 625 self.normal = AtomMath.normalize(nr) 626 else: 627 self.normal = nr
628
630 """ 631 """ 632 self.light_two_sides = True 633 self.object_list.append( 634 (8, 1.0 - self.material_alpha, 2, 635 self.front_clip, self.back_clip))
636
638 """ 639 """ 640 self.light_two_sides = False 641 self.object_list.append( 642 (8, 1.0 - self.material_alpha, 0, 643 self.front_clip, self.back_clip))
644
645 - def glr_line(self, position1, position2):
646 """Draws a single line. 647 """ 648 self.object_list.append( 649 (3, 650 dot43(self.matrix, position1), 651 dot43(self.matrix, position2), 652 self.line_width, 653 self.material_color_r, 654 self.material_color_g, 655 self.material_color_b))
656
657 - def glr_text(self, text, scale):
658 """Renders a text string. 659 """ 660 pass
661
662 - def glr_axis(self, position, axis, radius):
663 """Draw a vector axis using the current set material at position 664 with the given radius. 665 """ 666 ## don't bother redering small axes -- they look like junk 667 if numpy.allclose(AtomMath.length(axis), 0.0): 668 return 669 670 self.object_list.append( 671 (5, 672 dot43(self.matrix, position), 673 dot43(self.matrix, position + axis), 674 radius, 675 self.material_color_r, 676 self.material_color_g, 677 self.material_color_b))
678
679 - def glr_tube(self, position1, position2, radius):
680 """Draws a hollow tube beginning at pos1, and ending at pos2. 681 """ 682 self.object_list.append( 683 (5, 684 dot43(self.matrix, position1), 685 dot43(self.matrix, position2), 686 radius, 687 self.material_color_r, 688 self.material_color_g, 689 self.material_color_b))
690
691 - def glr_sphere(self, position, radius, quality):
692 """Draw a atom as a CPK sphere. 693 """ 694 self.object_list.append( 695 (2, 696 dot43(self.matrix, position), 697 radius, 698 self.material_color_r, 699 self.material_color_g, 700 self.material_color_b))
701
702 - def glr_cross(self, position, color, line_width):
703 """Draws atom with a cross of lines. 704 """ 705 pass
706
707 - def glr_Uaxes(self, position, U, prob, color, line_width):
708 """Draw the anisotropic axies of the atom at the given probability. 709 """ 710 ## rotate U 711 R = self.matrix[:3,:3] 712 Ur = numpy.dot(numpy.dot(R, U), numpy.transpose(R)) 713 714 evals, evecs = linalg.eigenvectors(Ur)
715
716 - def glr_Uellipse(self, position, U, prob):
717 """Renders the ellipsoid enclosing the given fractional probability 718 given the gaussian variance-covariance matrix U at the given position. 719 C=1.8724 = 68% 720 """ 721 ## rotate U 722 R = self.matrix[:3,:3] 723 Ur = numpy.dot(numpy.dot(R, U), numpy.transpose(R)) 724 725 Umax = max(linalg.eigenvalues(Ur)) 726 try: 727 limit_radius = Gaussian.GAUSS3C[prob] * MARGIN * math.sqrt(Umax) 728 except ValueError: 729 limit_radius = 2.0 730 731 try: 732 Q = linalg.inverse(Ur) 733 except linalg.LinAlgError: 734 return 735 736 self.object_list.append( 737 (14, 738 dot43(self.matrix, position), 739 limit_radius, 740 self.material_color_r, 741 self.material_color_g, 742 self.material_color_b, 743 Q, 744 -Gaussian.GAUSS3C[prob]**2))
745
746 - def glr_Urms(self, position, U):
747 """Renders the root mean square (one standard deviation) surface of 748 the 749 gaussian variance-covariance matrix U at the given position. This 750 is a peanut-shaped surface. (Note: reference the peanut paper!) 751 """ 752 pass
753