Package DejaVu :: Package VisionInterface :: Module DejaVuNodes
[hide private]
[frames] | no frames]

Source Code for Module DejaVu.VisionInterface.DejaVuNodes

   1  ######################################################################## 
   2  # 
   3  # Date: July 2002 Authors: Daniel Stoffler, Michel Sanner 
   4  # 
   5  #    stoffler@scripps.edu 
   6  #    sanner@scripps.edu 
   7  # 
   8  #       The Scripps Research Institute (TSRI) 
   9  #       Molecular Graphics Lab 
  10  #       La Jolla, CA 92037, USA 
  11  # 
  12  # Copyright: Daniel Stoffler, Michel Sanner and TSRI 
  13  # 
  14  # revision: Guillaume Vareille 
  15  # 
  16  ######################################################################### 
  17  # 
  18  # $Header: /opt/cvs/python/packages/share1.5/DejaVu/VisionInterface/DejaVuNodes.py,v 1.282.2.2 2007/08/29 23:09:58 vareille Exp $ 
  19  # 
  20  # $Id: DejaVuNodes.py,v 1.282.2.2 2007/08/29 23:09:58 vareille Exp $ 
  21  # 
  22   
  23  import types 
  24  import warnings 
  25  import string 
  26  import Numeric, sys, os 
  27  from math import sqrt, atan, pi 
  28   
  29  from NetworkEditor.items import NetworkNode 
  30  from NetworkEditor.macros import MacroNode 
  31  from NetworkEditor.macros import MacroOutputNode 
  32  from Vision import UserLibBuild 
  33   
  34  from DejaVu.VisionInterface.DejaVuWidgets import NEColorMap, NEColorWheel, NEColorEditor, NEDejaVuGeomOptions 
  35  from DejaVu.Geom import Geom 
  36  from DejaVu.Insert2d import Insert2d 
  37  from DejaVu.colorMap import ColorMap 
  38   
  39   
40 -def importSymLib(net):
41 try: 42 from symserv.VisionInterface.SymservNodes import symlib 43 net.editor.addLibraryInstance( 44 symlib, 'symserv.VisionInterface.SymservNodes', 'symlib') 45 except: 46 warnings.warn( 47 'Warning! Could not import symlib from symserv.VisionInterface.SymservNodes.py', stacklevel=2)
48 49
50 -def importImageLib(net):
51 try: 52 from Vision.PILNodes import imagelib 53 net.editor.addLibraryInstance(imagelib, 'Vision.PILNodes', 'imagelib') 54 except: 55 warnings.warn( 56 'Warning! Could not import imagelib from Vision.PILNodes.py', stacklevel=2)
57 58
59 -def importVolLib(net):
60 try: 61 from Volume.VisionInterface.VolumeNodes import vollib 62 net.editor.addLibraryInstance( 63 vollib, 'Volume.VisionInterface.VolumeNodes', 'vollib') 64 except: 65 warnings.warn( 66 'Warning! Could not import vollib from Volume.VisionInterface.VolumeNodes.py')
67 68
69 -class GeomOptions(NetworkNode):
70 - def __init__(self, name='Set Geom Options', viewer=None, **kw):
71 kw['name'] = name 72 apply( NetworkNode.__init__, (self,), kw ) 73 74 ip = self.inputPortsDescr 75 ip.append(name='geomOptions', datatype='dict') 76 77 self.widgetDescr['geomOptions'] = { 78 'class':'NEDejaVuGeomOptions', 'lockedOnPort':True 79 } 80 81 op = self.outputPortsDescr 82 op.append(datatype='dict', name='geomOptions') 83 84 code = """def doit(self, geomOptions): 85 self.outputData(geomOptions=geomOptions) 86 """ 87 88 self.setFunction(code)
89 90
91 -class RestoreState(NetworkNode):
92 """Set a Viewer's state from a file 93 94 Input Ports: 95 Viewer: (required) the Viewer for which to restore the state 96 filename: (required) the file containing the state 97 mode: (optional) can be 'Viewer', 'objects' or 'both' 98 defaults to 'both'. When this value is 'viewer', 99 the state is restored only for objects always present in the 100 viewer (i.e. root, lights, clipping planes, fog, etc). 101 'objects': means restoring the state of geometries in the viewer. 102 'both': does both. 103 104 Output Ports: 105 """ 106 from DejaVu.Geom import Geom 107
108 - def __init__(self, name='RestoreState', viewer=None, **kw):
109 kw['name'] = name 110 apply( NetworkNode.__init__, (self,), kw ) 111 112 ip = self.inputPortsDescr 113 ip.append(name='viewer', datatype='viewer') 114 ip.append(name='filename', datatype='string') 115 ip.append(name='mode', datatype='string', required=False) 116 117 self.widgetDescr['filename'] = { 118 'class':'NEEntryWithFileBrowser', 'master':'node', 'width':10, 119 'initialValue':'', 120 'filetypes':[('state files','*_state.py'),('all','*')], 121 'labelCfg':{'text':'Filename: '} 122 } 123 124 self.widgetDescr['mode'] = { 125 'class':'NEComboBox', 'master':'node', 126 'choices':['viewer', 'objects', 'both'], 127 'initialValue': 'both', 128 'entryfield_entry_width':8, 129 'labelCfg':{'text':'mode:'}, 130 } 131 132 code = """def doit(self, viewer, filename, mode='both'): 133 execfile(filename, {'vi':viewer, 'mode':mode}) 134 viewer.Redraw() 135 """ 136 self.setFunction(code)
137 138
139 -class CenterOnPickedVertex(NetworkNode):
140 """Set the rotation center of for the scene to the picked vertex 141 142 Input Ports: 143 Viewer: (required) the Viewer for which to restore the state 144 145 Output Ports: 146 """ 147 from DejaVu.Geom import Geom 148
149 - def __init__(self, name='CenterOnPick', viewer=None, **kw):
150 kw['name'] = name 151 apply( NetworkNode.__init__, (self,), kw ) 152 153 ip = self.inputPortsDescr 154 ip.append(name='viewer', datatype='viewer') 155 156 code = """def doit(self, viewer): 157 if viewer.lastPick is None: 158 return 159 hits = viewer.lastPick.hits 160 if len(hits)==1: 161 g = hits.keys()[0] 162 index = hits[g][0][0] 163 point = g.vertexSet.vertices.array[index] 164 viewer.rootObject.SetPivot(point)""" 165 166 self.setFunction(code)
167 168
169 -class CenterOnVertex(NetworkNode):
170 """Set the rotation center of for the scene to a vertex 171 172 Input Ports: 173 Viewer: (required) the Viewer for which to restore the state 174 vertex: 175 176 Output Ports: 177 """ 178 from DejaVu.Geom import Geom 179
180 - def __init__(self, name='CenterOnPick', viewer=None, **kw):
181 kw['name'] = name 182 apply( NetworkNode.__init__, (self,), kw ) 183 184 ip = self.inputPortsDescr 185 ip.append(name='viewer', datatype='viewer') 186 ip.append(name='point', datatype='coord3') 187 188 code = """def doit(self, viewer, point): 189 viewer.rootObject.SetPivot(point)""" 190 191 self.setFunction(code)
192 193
194 -class Viewer(NetworkNode):
195 """Create an instance of a DejaVu Viewer object. 196 This object provides a full fledged 3D geometry viewer with support for 197 the OpenGL material and lighting model with multiple light source, 198 arbitrary clipping planes, a hierarchy of geometries with property 199 inheritance, a material editor, etc... . 200 201 Input Ports: 202 Geometries: (required) Accepts any object that is a DejaVu.Geom instance 203 or a list thereof from each parent port and adds the geometry 204 objects to the viewer 205 Output Ports: 206 lastPick: outputs a DejaVu.Camera.PickObject 207 DejaVu: outputs the DejaVu Viewer instance 208 Redraw: fires at each redraw event 209 """ 210 from DejaVu.Geom import Geom 211
212 - def __init__(self, name='Viewer', viewer=None, **kw):
213 kw['name'] = name 214 apply( NetworkNode.__init__, (self,), kw ) 215 #self.readOnly = 1 216 self.vi = viewer 217 218 # code for input port callback "beforeDisconnect" 219 # this will delete geometries added to the viewer 220 codeBeforeDisconnect = """def beforeDisconnect(self, c): 221 #print "Viewer Node beforeDisconnect" 222 self.node.deleteGeometries(c) 223 """ 224 # add an entry so the show parma. panel will be enabled 225 ip = self.inputPortsDescr 226 ip.append(name='geometries', datatype='geom(0)', required=False, 227 singleConnection=False, 228 beforeDisconnect=codeBeforeDisconnect) 229 230 op = self.outputPortsDescr 231 op.append(datatype='None', name='lastPick') 232 op.append(datatype='viewer', name='dejaVuViewer') 233 op.append(datatype='None', name='redraw') 234 235 code = """def doit(self, geometries=None): 236 self.addGeometriesToViewer(geometries) 237 238 self.outputData(lastPick=self.vi.lastPick) 239 self.outputData(dejaVuViewer=self.vi) 240 self.outputData(redraw=0) 241 """ 242 self.setFunction(code)
243 244
245 - def getStateDefinitionCode(self, nodeName, indent=''):
246 #print "ViewerNode getStateDefinitionCode" 247 return self.vi.getViewerStateDefinitionCode( 248 viewerName=nodeName+'.vi', 249 indent=indent, 250 withMode=False)
251 252
253 - def addGeometriesToViewer(self, geometries):
254 """ this geometries a list, but not a list from a geom node 255 """ 256 if self.vi is None: 257 return 258 259 if geometries: 260 for g in geometries: 261 self.addGeometryiesToViewer(g)
262 263
264 - def addGeometryiesToViewer(self, geometryies):
265 """ we don't know if geometryies is a Geom or a list from a geom node 266 """ 267 if geometryies and geometryies != [None]: 268 if isinstance(geometryies, Geom) or isinstance(geometryies, Insert2d): 269 if hasattr(geometryies, 'node') and \ 270 hasattr(geometryies.node(),'geoms'): 271 self.addMultipleGeomsToViewer(geometryies.node().geoms) 272 else: 273 self.addSingleGeomToViewer(geometryies) 274 self.addGeometryiesToViewer(geometryies.parent) 275 elif isinstance(geometryies, list): 276 self.addMultipleGeomsToViewer(geometryies) 277 else: 278 assert False 279 280 self.vi.Redraw()
281 282
283 - def addMultipleGeomsToViewer(self, geometries ):
284 """ geometries is a list from a geom node 285 """ 286 #print "addMultipleGeomsToViewer", geometries 287 for g in geometries: 288 if isinstance(g, Geom) or isinstance(g, Insert2d): 289 self.addSingleGeomToViewer(g) 290 if g.parent.viewer is None: 291 self.addGeometryiesToViewer(g.parent) 292 elif isinstance(g, list): 293 self.addMultipleGeomsToViewer(g)
294 295
296 - def addSingleGeomToViewer(self, aGeom ):
297 """ geometries is a Geom 298 """ 299 #print "addSingleGeomToViewer", aGeom.name 300 301 #if aGeom is None or aGeom.viewer is not None: 302 if aGeom.viewer is not None: 303 return 304 305 # add the geom to the viewer 306 lHighestModifiedParent = \ 307 self.vi.AddObjectAndParents(aGeom, parent=aGeom.parent, local=False) 308 309 # rename node geometry in Vision and set input for name 310 # FIXME this test has to go after all Vision geoms have .node() 311 if hasattr(aGeom, 'node') : 312 aGeom.node().ensureNameOfNodeAndDescendants(lHighestModifiedParent)
313 314
315 - def deleteGeometries(self, connection):
316 """code for input port callback "beforeDisconnect" 317 this will delete geometries added to the viewer 318 """ 319 #print "Viewer Node deleteGeometries" 320 #import pdb;pdb.set_trace() 321 if self.vi is not None: 322 self.vi.SetCurrentObject(self.vi.rootObject) 323 324 if connection.port1.data: 325 geoms = connection.port1.data 326 # geoms can be packed in a list or not 327 import types 328 from DejaVu.Geom import Geom 329 if type(geoms) == types.ListType: 330 for g in geoms: 331 if self.vi is None: 332 g.viewer = None 333 else: 334 if hasattr(g,'node'): 335 g.node().removeViewerConnections(connection) 336 g.node().removeAndCleanMultipleGeometryies(g) 337 else: # not list 338 if self.vi is None: 339 geoms.viewer = None 340 else: 341 if hasattr(geoms,'node'): 342 geoms.node().removeViewerConnections(connection) 343 geoms.node().removeAndCleanMultipleGeometryies(geoms)
344 345
346 - def buildIcons(self, canvas, posx, posy):
347 apply( NetworkNode.buildIcons, (self, canvas, posx, posy) ) 348 self.paramPanel.applyButton.forget() 349 from DejaVu import Viewer 350 from DejaVu.Geom import Geom 351 352 if self.vi is None: 353 self.vi = Viewer(nogui=1, guiMaster=self.paramPanel.widgetFrame) 354 # do not allow user to kill this window 355 self.vi.master.protocol('WM_DELETE_WINDOW', self.doNothing) 356 self.ownViewer = 1 357 else: 358 #self.vi = viewer 359 self.ownViewer = 0 360 361 camera = self.vi.currentCamera 362 # add callback to output picking events 363 self.vi.AddPickingCallback(self.handlePick) 364 # add callback to output redraw event 365 self.vi.afterRedraw = self.afterRedraw 366 367 self.outputData(dejaVuViewer=self.vi)
368 369
370 - def doNothing(self):
371 # used for overwriting WM_DELETE_WINDOW of Viewer window 372 return
373 374
375 - def afterRedraw(self):
376 if self.vi: 377 apply( self.vi.__class__.afterRedraw, (self.vi,)) 378 # trigger all children of the redraw outputPort 379 if len(self.outputPorts): 380 self.outputData(redraw=1) 381 self.scheduleChildren(portList=[self.outputPorts[2]])
382 383
384 - def handlePick(self, pick):
385 if pick: 386 if self.vi: 387 self.outputData(lastPick=self.vi.lastPick) 388 # schedule children of lastPick 389 self.scheduleChildren(portList=[self.outputPorts[0]])
390 391
392 - def beforeRemovingFromNetwork(self):
393 NetworkNode.beforeRemovingFromNetwork(self) 394 395 for g in self.vi.rootObject.AllObjects(): 396 if hasattr(g,'node'): 397 g.node().removeSingleGeom(g) 398 399 if self.ownViewer: 400 self.vi.Exit() 401 self.vi = None
402 403
404 - def toggleNodeExpand_cb(self, event=None):
405 # overwrite base class method, because this panel is special 406 if self.paramPanel.visible: 407 self.paramPanel.hide() 408 else: 409 self.paramPanel.show()
410 411 412
413 -class AccumPickedVertices(NetworkNode):
414 """Class to accumulate picked vertices 415 416 Input Ports: 417 pick: DejaVu pick object instance 418 viewer: DejaVu viewer object instance 419 reset: resets the list of vertices to empty 420 remove: check button to remove vertices form the list 421 422 Output Ports: 423 pickedVertices: list of 3D vertices 424 """
425 - def __init__(self, **kw):
426 apply( NetworkNode.__init__, (self,), kw ) 427 428 self.vertices = [] 429 430 # add an entry so the show parma. panel will be enabled 431 ip = self.inputPortsDescr 432 ip.append(name='pick', datatype='None') 433 ip.append(name='viewer', datatype='viewer') 434 ip.append(name='reset', datatype='boolean') 435 ip.append(name='remove', datatype='boolean') 436 437 self.widgetDescr['reset'] = { 438 'class':'NEButton', 439 'master':'node', 440 'initialValue':False, 441 'labelGridCfg':{'sticky':'we'}, 442 'labelCfg':{'text':'empty list'}, 443 } 444 445 self.widgetDescr['remove'] = { 446 'class':'NECheckButton', 447 'master':'node', 448 'initialValue':False, 449 'labelGridCfg':{'sticky':'we'}, 450 'labelCfg':{'text':'remove'}, 451 } 452 453 op = self.outputPortsDescr 454 op.append(datatype='list', name='pickedVertices') 455 456 code = """def doit(self, pick, viewer, reset, remove): 457 if self.inputPortByName['reset'].hasNewData(): 458 self.vertices = [] 459 460 if self.inputPortByName['pick'].hasNewData(): 461 vertices = viewer.transformedCoordinatesWithInstances(pick.hits) 462 if remove: 463 for v1 in vertices: 464 vstr = '%.3f %.3f %.3f'%tuple(v1) 465 for v2 in self.vertices: 466 if '%.3f %.3f %.3f'%tuple(v2)==vstr: 467 self.vertices.remove(v2) 468 else: 469 self.vertices.extend(vertices) 470 471 self.outputData(pickedVertices=self.vertices) 472 """ 473 474 self.setFunction(code)
475 476 477
478 -class PolyhedronVolumeArea(NetworkNode):
479 """compute the numerical volume and surface area of a polyhedron 480 481 Input Ports: 482 geometry: (required) Accepts any IndexedPolygon geometry from DejaVu 483 484 Output Ports: 485 volume: float 486 area: float 487 compactness: area/volume (float) 488 .""" 489
490 - def triangleArea(self, p1, p2, p3):
491 """Compute the surface area of a triangle. 492 """ 493 x1,y1,z1 = p1 494 x2,y2,z2 = p2 495 x3,y3,z3 = p3 496 dx, dy, dz = x1-x2, y1-y2, z1-z2 497 a = sqrt( dx*dx + dy*dy + dz*dz ) 498 dx, dy, dz = x2-x3, y2-y3, z2-z3 499 b = sqrt( dx*dx + dy*dy + dz*dz ) 500 dx, dy, dz = x1-x3, y1-y3, z1-z3 501 c = sqrt( dx*dx + dy*dy + dz*dz ) 502 s = .5*(a+b+c) 503 area = s*(s-a)*(s-b)*(s-c) 504 if area <= 0.: 505 return 0. 506 return sqrt(area)
507 508
509 - def meshVolume(self, verts, tri, norm):
510 """Compute the Volume and surface area of a mesh specified by vertices, 511 indices of triangular faces and face normals 512 """ 513 assert len(tri)==len(norm) 514 volSum = 0.0 515 areaSum = 0.0 516 oneThird = 1./3. 517 for t,n in zip(tri, norm): 518 s1 = verts[t[0]] 519 s2 = verts[t[1]] 520 s3 = verts[t[2]] 521 area = self.triangleArea(s1,s2,s3) 522 areaSum += area 523 524 g = [ (s1[0]+s2[0]+s3[0])*oneThird, 525 (s1[1]+s2[1]+s3[1])*oneThird, 526 (s1[2]+s2[2]+s3[2])*oneThird ] 527 volSum += (g[0]*n[0] + g[1]*n[1] + g[2]*n[2])*area 528 return volSum*oneThird, areaSum
529 530
531 - def __init__(self, name='PolyhedronVolumeArea', **kw):
532 kw['name'] = name 533 apply( NetworkNode.__init__, (self,), kw ) 534 #self.readOnly = 1 535 536 ip = self.inputPortsDescr 537 ip.append(datatype='geom', name='geometry') 538 539 op = self.outputPortsDescr 540 op.append(datatype='float', name='volume') 541 op.append(datatype='float', name='area') 542 op.append(datatype='float', name='compactness') 543 544 code = """def doit(self, geometry): 545 vert = geometry.getVertices() 546 tri = geometry.getFaces() 547 #from opengltk.extent.utillib import glTriangleNormals 548 from geomutils.geomalgorithms import TriangleNormals 549 norm = TriangleNormals( vert, tri, 'PER_FACE') 550 # FIXME getFNormals() returns old set of normals after sendity has changed 551 # on MSMS surface 552 # norm = geometry.getFNormals() 553 554 vol, area = self.meshVolume(vert, tri, norm) 555 self.outputData(volume=vol, area=area, compactness=area/vol) 556 """ 557 558 self.setFunction(code)
559 560
561 -class writeIndexedPolygon(NetworkNode):
562 """write the vertices, normals and faces of an IndexedPolygon to a file. 563 564 Input Ports: 565 geometry: (required) Accepts any IndexedPolgon geometry from DejaVu 566 filename: (required) string, no extension. filename.vert and filename.face 567 will be created 568 Output Ports: 569 .""" 570
571 - def __init__(self, name='writeIndexedPolygon', **kw):
572 kw['name'] = name 573 apply( NetworkNode.__init__, (self,), kw ) 574 #self.readOnly = 1 575 576 self.widgetDescr['filename'] = { 577 'class':'NEEntryWithFileSaver', 'master':'node', 'width':10, 578 'initialValue':'', 579 'labelCfg':{'text':'Filename: '} 580 } 581 582 ip = self.inputPortsDescr 583 ip.append(datatype='geom', name='geometry') 584 ip.append(datatype='string', name='filename') 585 586 code = """def doit(self, geometry, filename): 587 588 from DejaVu.IndexedPolygons import IndexedPolygons 589 assert isinstance(geometry, IndexedPolygons) 590 geometry.writeToFile(filename) 591 """ 592 593 self.setFunction(code)
594 595
596 -class writeCurvPly(NetworkNode):
597 """write the vertices and triangles of an IndexedPolygon to a file in the 598 format used by the setCurvature program (which ressembles ply2) 599 600 Input Ports: 601 geometry: (required) Accepts any IndexedPolgon geometry from DejaVu 602 filename: (required) string, no extension. filename.vert and filename.face 603 will be created 604 Output Ports: 605 filename: outputs the filename after it wrote the file 606 """
607 - def writeFile(self, filename, verts, faces, neighborhoodSize, crestLines):
608 f = open(filename, 'w') 609 610 # this assert just because the test bellow commented out was weird 611 assert crestLines is True or crestLines is False 612 613 f.write('%d\n%d\n%d\n%d\n'%( len(verts), len(faces), neighborhoodSize, 614 crestLines )) 615 # crestLines==True )) 616 617 618 619 dum = map(lambda x, f=f: f.write('%f\n'%x), verts) 620 dum = map(lambda x, f=f: f.write('%d\n'%x), faces) 621 f.close()
622
623 - def __init__(self, name='writeCurvPly', **kw):
624 kw['name'] = name 625 apply( NetworkNode.__init__, (self,), kw ) 626 #self.readOnly = 1 627 628 self.widgetDescr['filename'] = { 629 'class':'NEEntryWithFileSaver', 630 'master':'node', 'width':10, 631 'initialValue':'', 632 'labelCfg':{'text':'Filename: '} 633 } 634 635 self.widgetDescr['neighborhoodSize'] = { 636 'class':'NEThumbWheel', 637 'master':'node', 638 'initialValue':4, 639 'labelCfg':{'text':'neighborhoodSize:'}, 640 'width':80, 'height':20, 641 'type':'int', 'oneTurn':10, 'lockBMin':1, 'min':1, 642 'wheelPad':2 } 643 644 self.widgetDescr['crestLines'] = { 645 'class':'NECheckButton', 646 'master':'node', 647 'initialValue':False, 648 'labelGridCfg':{'sticky':'we'}, 649 'labelCfg':{'text':'crest Lines:'}, 650 } 651 652 ip = self.inputPortsDescr 653 ip.append(datatype='geom', name='geometry') 654 ip.append(datatype='string', name='filename') 655 ip.append(datatype='int', name='neighborhoodSize') 656 ip.append(datatype='boolean', name='crestLines') 657 658 op = self.outputPortsDescr 659 op.append(datatype='string', name='filename') 660 661 code = """def doit(self, geometry, filename, neighborhoodSize, 662 crestLines): 663 664 from DejaVu.IndexedPolygons import IndexedPolygons 665 assert isinstance(geometry, IndexedPolygons) 666 verts = geometry.getVertices() 667 faces = geometry.getFaces() 668 self.writeFile(filename, verts, faces, neighborhoodSize, crestLines) 669 geometry.writeToFile(filename) 670 """ 671 672 self.setFunction(code)
673 674
675 -class Curvature(NetworkNode):
676 """run the setCurvature program 677 678 Input Ports: 679 geometry: (required) Accepts any IndexedPolgon geometry from DejaVu 680 neighborhoodSize: size of the neighborhood use to fit quadric 681 crestLines: when set to ture crest liens are traced 682 683 Output Ports: 684 maxCurv: max curvature for each vertex 685 minCurv: min curvature for each vertex 686 vmaxCurv: max curvature vector 687 vminCurv: min curvature vector 688 GaussianCurv: Gaussian curvature vector (Kmax*Kmin) 689 meanCurv: mean curvature vector for (Kmax+Kmin) 690 shapeIndex: shapeIndex -2/pi*arctan(Kmax+Kmin/Kmax-Kmin) 691 curvedness: curvedness sqrt(Kmax**2 + Kmin**2)/2 692 normals: normals to the surface 693 """
694 - def writeFile(self, filename, verts, faces, neighborhoodSize, crestLines):
695 f = open(filename, 'w') 696 697 # this assert just because the test bellow commented out was weird 698 assert crestLines is True or crestLines is False 699 700 f.write('%d\n%d\n%d\n%d\n'%( len(verts)/3, len(faces)/3, 701 neighborhoodSize, 702 crestLines )) 703 #crestLines==True )) 704 705 dum = map(lambda x, f=f: f.write('%f\n'%x), verts) 706 dum = map(lambda x, f=f: f.write('%d\n'%x), faces) 707 f.close()
708 709
710 - def readCurvature(self, filename):
711 f = open(filename) 712 data = f.readlines() 713 f.close() 714 nbv = int(data[0]) 715 nbt = int(data[1]) 716 import string 717 dataf = map(string.split, data[2:]) 718 curvData = [] 719 for l in dataf: 720 curvData.append(map(float, l)) 721 curvData = Numeric.array(curvData, 'f') 722 return curvData
723 724
725 - def __init__(self, name='curvature', **kw): #guillaume: was cruvature
726 kw['name'] = name 727 apply( NetworkNode.__init__, (self,), kw ) 728 729 self.widgetDescr['neighborhoodSize'] = { 730 'class':'NEThumbWheel', 731 'master':'node', 732 'initialValue':4, 733 'labelCfg':{'text':'neighborhoodSize:'}, 734 'width':80, 'height':20, 735 'type':'int', 'oneTurn':10, 'lockBMin':1, 'min':1, 736 'wheelPad':2 } 737 738 self.widgetDescr['crestLines'] = { 739 'class':'NECheckButton', 740 'master':'node', 741 'initialValue':False, 742 'labelGridCfg':{'sticky':'we'}, 743 'labelCfg':{'text':'crest Lines:'}, 744 } 745 746 ip = self.inputPortsDescr 747 ip.append(datatype='geom', name='geometry') 748 ip.append(datatype='int', name='neighborhoodSize') 749 ip.append(datatype='boolean', name='crestLines') 750 751 op = self.outputPortsDescr 752 op.append(datatype='list', name='maxCurv') 753 op.append(datatype='list', name='minCurv') 754 op.append(datatype='list', name='vmaxCurv') 755 op.append(datatype='list', name='vminCurv') 756 op.append(datatype='list', name='gaussian') 757 op.append(datatype='list', name='mean') 758 op.append(datatype='list', name='shapeIndex') 759 op.append(datatype='list', name='curvedness') 760 op.append(datatype='list', name='normals') 761 762 code = """def doit(self, geometry, neighborhoodSize, crestLines): 763 764 from DejaVu.IndexedPolygons import IndexedPolygons 765 assert isinstance(geometry, IndexedPolygons) 766 verts = Numeric.reshape(geometry.getVertices(), (-1,)) 767 faces = Numeric.reshape(geometry.getFaces(), (-1,)) 768 self.writeFile('curv.txt' , verts, faces, neighborhoodSize, crestLines) 769 os.system('setCurvature curv.txt curv.out') 770 curvData = self.readCurvature('curv.out') 771 #os.system('rm curv.out curv.txt') 772 Kmax = curvData[:,0] 773 Kmin = curvData[:,1] 774 gaussian = Kmax*Kmin 775 mean = (Kmax+Kmin)*0.5 776 shapeIndex = (-2/pi)*Numeric.arctan((Kmax+Kmin)/(Kmax-Kmin)) 777 curvedness = Numeric.sqrt(Kmax*Kmax+Kmin*Kmin)*0.5 778 self.outputData(maxCurv=Kmax, 779 minCurv=Kmin, 780 vmaxCurv=curvData[:,2:5].tolist(), 781 vminCurv=curvData[:,5:8].tolist(), 782 gaussian=gaussian, mean=mean, 783 shapeIndex=shapeIndex, curvedness=curvedness, 784 normals=curvData[:,8:].tolist() 785 ) 786 """ 787 self.setFunction(code)
788 789 790
791 -class readIndexedPolygon(NetworkNode):
792 """ ************* DEPRECATED, USE GeomsFromFile instead ************** 793 794 read the vertices, normals and faces of an IndexedPolygon from a file. 795 796 Input Ports: 797 filename: (required) string, no extension. filename.vert and filename.face 798 will be created 799 name: (optional) name for the geoemtry. Defaults to filename 800 801 Output Ports: 802 geometry: DejaVu.IndexedPolgons geometry 803 .""" 804
805 - def __init__(self, name='readIndexedPolygon', **kw):
806 807 #warnings.warn("read Indexed Polygon is deprecated, use 'geoms from file' instead") 808 809 kw['name'] = name 810 apply( NetworkNode.__init__, (self,), kw ) 811 #self.readOnly = 1 812 813 wdescr = self.widgetDescr 814 wdescr['filename'] = { 815 'class':'NEEntryWithFileBrowser', 'master':'node', 'width':10, 816 'initialValue':'', 817 'labelCfg':{'text':'Filename: '}, 818 'filetypes': [('vert','*.vert'), ('all', '*')]} 819 wdescr['name'] = { 820 'class':'NEEntry', 'master':'node', 'width':10, 821 'labelCfg':{'text':'name:'}, 822 } 823 824 ip = self.inputPortsDescr 825 ip.append(datatype='string', name='filename') 826 ip.append(datatype='string', required=False, name='name') 827 828 op = self.outputPortsDescr 829 op.append(datatype='geom', name='geom') 830 831 code = """def doit(self, filename, name=None): 832 if filename is None or filename == '': 833 return 834 from DejaVu.IndexedPolygons import IndexedPolygonFromFile 835 from os.path import splitext 836 if name == '': 837 name=None 838 filename = splitext(filename)[0] 839 self.outputData(geom=IndexedPolygonFromFile(filename, name)) 840 """ 841 842 self.setFunction(code)
843 844 845
846 -class RemoveDuplicatedVerticesNE(NetworkNode):
847 """Remove duplicated vertices and re-index the polygonal faces such that 848 they share vertices 849 850 input: 851 geom -- an IndexedGeom object in which vertices are suplicated 852 newGeom -- when true a new geoemtry is created, else the incomming 853 geometry is modified 854 855 output: 856 geom -- a new IndexedPolgons without duplicated vertices 857 """
858 - def __init__(self, name='removeDupVert', **kw):
859 kw['name'] = name 860 apply(NetworkNode.__init__, (self,), kw) 861 862 ip = self.inputPortsDescr = [] 863 ip.append({'datatype':'geom', 'name':'geom'}) 864 ip.append({'datatype':'boolean', 'name':'newGeom'}) 865 866 self.widgetDescr['newGeom'] = { 867 'class':'NECheckButton', 868 'initialValue':False, 869 'master':'node', 870 'labelGridCfg':{'sticky':'we'}, 871 'labelCfg':{'text':'new geometry'}, 872 } 873 874 op = self.outputPortsDescr = [] 875 op.append({'datatype':'geom', 'name':'geom'}) 876 877 code = """def doit(self, geom, newGeom): 878 results = [] 879 from DejaVu.IndexedPolygons import IndexedPolygons, Geom 880 from DejaVu.utils import RemoveDuplicatedVertices 881 882 if isinstance(geom, Geom): 883 geom = [geom] 884 885 for g in geom: 886 vertices = g.vertexSet.vertices.array 887 faces = g.faceSet.faces.array 888 vertList, faceList = RemoveDuplicatedVertices(vertices, faces) 889 if newGeom: 890 results.append( IndexedPolygons('%sMesh'%g.name, 891 vertices=vertList, faces=faceList)) 892 else: 893 g.Set(vertices=vertList, faces=faceList) 894 results.append(g) 895 896 self.outputData(geom=results) 897 """ 898 self.configure(function=code)
899 900
901 -class removeDuplicatedVerticesC(NetworkNode):
902 """Remove duplicated vertices and re-index the polygonal faces such that 903 they share vertices (uses the C++ function from opengltk.extent.utillib) 904 905 input: 906 geom -- an IndexedGeom object in which vertices are suplicated 907 newGeom -- when true a new geoemtry is created, else the incomming 908 geometry is modified 909 output: 910 geom -- a new IndexedPolygons without duplicated vertices 911 """
912 - def __init__(self, name='removeDupVertC', **kw):
913 kw['name'] = name 914 apply(NetworkNode.__init__, (self,), kw) 915 916 ip = self.inputPortsDescr = [] 917 ip.append({'datatype':'geom', 'name':'geom'}) 918 ip.append({'datatype':'boolean', 'name':'newGeom'}) 919 920 self.widgetDescr['newGeom'] = { 921 'class':'NECheckButton', 922 'initialValue':False, 923 'master':'node', 924 'labelGridCfg':{'sticky':'we'}, 925 'labelCfg':{'text':'new geometry'}, 926 } 927 928 op = self.outputPortsDescr = [] 929 op.append({'datatype':'geom', 'name':'geom'}) 930 931 code = """def doit(self, geom, newGeom): 932 results = [] 933 from DejaVu.IndexedPolygons import IndexedPolygons, Geom 934 #from opengltk.extent.utillib import removeDuplicatedVertices 935 from geomutils.geomalgorithms import removeDuplicatedVertices 936 937 if isinstance(geom, Geom): 938 geom = [geom] 939 940 for g in geom: 941 vertices = g.vertexSet.vertices.array 942 faces = g.faceSet.faces.array 943 norms = g.getVNormals() 944 if len(vertices) and len(faces): 945 vertList, faceList, normList = removeDuplicatedVertices( 946 vertices, faces, norms ) 947 if newGeom: 948 results.append( IndexedPolygons('%sMesh'%g.name, 949 vertices=vertList, faces=faceList, vnormals=normList)) 950 else: 951 g.Set(vertices=vertList, faces=faceList, vnormals=normList) 952 results.append(g) 953 self.outputData(geom=results) 954 """ 955 self.configure(function=code)
956
957 -class QslimExt(NetworkNode):
958 """Invoked the Qslim algorithm as an external process to decimate a surface. 959 Input Ports 960 geometry: Any DejaVu.IndexedPolgons geometry. (required) 961 persent: percentage of faces to be retained in decimated model 962 963 Output Ports: 964 geom: decimated geometry 965 .""" 966
967 - def __init__(self, name='write SMF', **kw):
968 kw['name'] = name 969 apply( NetworkNode.__init__, (self,), kw ) 970 971 self.widgetDescr['percent'] = { 972 'class':'NEDial', 'size':50, 973 'oneTurn':100, 'min':0., 'max':100., 'type':'float', 974 'showLabel':1, 'continuous':0, 975 'initialValue':50., 976 'labelCfg':{'text':'percent'}, 977 } 978 ip = self.inputPortsDescr 979 ip.append(name='lines', datatype='list') 980 ip.append(name='percent', datatype='float') 981 ip.append(name='nbf', datatype='int', required=False) 982 983 op = self.outputPortsDescr 984 op.append({'name':'lines', 'datatype':'list'}) 985 986 code = """def doit(self, lines, percent, nbf=None): 987 988 if nbf is None: 989 nbf = 0 990 for l in lines: 991 if l[0]=='f': 992 nbf+=1 993 994 targetFaces = int(nbf*percent/100.) 995 996 from mglutil import process 997 # start propslim process 998 p = process.Popen('propslim %d'%targetFaces, 999 stdin=process.PIPE, stdout=process.PIPE) 1000 inp, out, err = (p.stdin, p.stdout, p.stderr) 1001 1002 # send input 1003 map( lambda x, f=inp: f.write('%s'%x), lines) 1004 inp.close() 1005 1006 # FIXME we should select on out and err and handle errors 1007 # FIXME we should run popslim from the Binaries package 1008 # FIXME we should have a way to detect if the Binary package contains 1009 # popslim and if not not load the node 1010 # read results 1011 data = out.readlines() 1012 print 'propslim reduced from %d to %d faces'%(nbf,targetFaces) 1013 1014 self.outputData(lines=data) 1015 """ 1016 1017 self.setFunction(code)
1018 1019 1020
1021 -class GeomtoSMF(NetworkNode):
1022 """create the source of an SMF file from an DejaVu.IndexedPolygons 1023 geometry 1024 1025 Input Ports: 1026 geometry: Any DejaVu.IndexedPolgons geometry. (required) 1027 filename: name of the SMF file to be created (required, string) 1028 1029 Output Ports: 1030 lines: list of text lines in SMF format 1031 .""" 1032
1033 - def __init__(self, name='GeomToSMF', **kw):
1034 kw['name'] = name 1035 apply( NetworkNode.__init__, (self,), kw ) 1036 1037 ip = self.inputPortsDescr 1038 ip.append(name='geometry', datatype='geom') 1039 1040 self.outputPortsDescr.append(name='lines', datatype='list') 1041 1042 code = """def doit(self, geometry): 1043 1044 from DejaVu.IndexedPolygons import IndexedPolygons 1045 assert isinstance(geometry, IndexedPolygons) 1046 from DejaVu.DataOutput import IndexedPolgonsAsSMFString 1047 lines = IndexedPolgonsAsSMFString(geometry) 1048 self.outputData(lines = lines) 1049 """ 1050 1051 self.setFunction(code)
1052 1053
1054 -class SMFtoGeom(NetworkNode):
1055 """converts a list of strings describing an SMF file into a 1056 DejaVu.IndexedPolygons 1057 1058 Input Ports: 1059 lines: list of lines 1060 geomname: name for the geoemtry 1061 parent: parent geometry 1062 1063 Output Ports: 1064 geometry: DejaVu.IndexedPolgons geometry 1065 .""" 1066
1067 - def __init__(self, name='SMFtoGeom', **kw):
1068 kw['name'] = name 1069 apply( NetworkNode.__init__, (self,), kw ) 1070 1071 from DejaVu.IndexedPolygons import IndexedPolygons 1072 self.geom = IndexedPolygons() 1073 1074 self.widgetDescr['geomname'] = { 1075 'class':'NEEntry', 'master':'node', 'width':10, 1076 'labelCfg':{'text':'name:'}, 1077 'initialValue':'smfModel', 1078 } 1079 1080 ip = self.inputPortsDescr 1081 ip.append(name='lines', datatype='list') 1082 ip.append(name='geomname', datatype='string', required=False) 1083 ip.append(name='parent', datatype='geom', required=False) 1084 1085 op = self.outputPortsDescr 1086 op.append(name='geom', datatype='geom') 1087 1088 code = """def doit(self, lines, geomname=None, parent=None): 1089 if len(lines)==0: 1090 return 1091 1092 if not geomname: # i.e. None or '' 1093 geomname = 'smfModel' 1094 1095 from DejaVu.DataOutput import ParseSMFString 1096 v,f,n,c,r = ParseSMFString(lines) 1097 1098 self.geom.Set(name=geomname, vertices=v, faces=f, redo=0) 1099 1100 if len(n): 1101 if len(n)==len(v): 1102 self.geom.Set(vnormals=n, redo=0) 1103 elif len(n)==len(f): 1104 geom.Set(fnormals=n, redo=0) 1105 else: 1106 errstr = 'number of normals %d does not not match number of vertices (%d) or number of faces(%d)'%(len(v), len(f)) 1107 warnings.warn(errstr) 1108 1109 if len(c): 1110 self.geom.Set(materials=c, inheritMaterial=False, redo=0) 1111 1112 if parent is not None: 1113 self.geom.viewer.ReparentGeom(self.geom. parent) 1114 1115 self.geom.Set(redo=1) 1116 1117 self.outputData(geom=self.geom) 1118 """ 1119 1120 self.setFunction(code)
1121 1122 1123 from DejaVu.IndexedPolygons import IndexedPolygons 1124 import Numeric 1125 try: 1126 from QSlimLib import qslimlib 1127 except: 1128 pass 1129
1130 -class QSlim(NetworkNode):
1131 """Uses QSlim library to decimate a surface. 1132 Creates a QSlim model from the input geometry, decimates it 1133 and outputs a geometry with new number of faces, vertices, colors. 1134 Input Ports 1135 geometry: any DejaVu.IndexedPolgons geometry (required). 1136 percent: percentage of faces to be retained in decimated model (required). 1137 targetfaces: corresponding to persent number of faces. 1138 newgeom: (checkbutton) if checked - the node outputs a new IndexedPolygons 1139 geometry, if unchecked - modified input geometry is output. 1140 rebuild: (boolean), when true, rebuilds the QSlim model (takes 1141 current parameters of selected geometry). 1142 1143 Output Ports: 1144 geom: decimated geometry 1145 .""" 1146
1147 - def __init__(self, name='QSlim', **kw):
1148 1149 kw['name'] = name 1150 apply( NetworkNode.__init__, (self,), kw ) 1151 1152 self.widgetDescr['percent'] = { 1153 'class':'NEDial', 'master':'ParamPanel', 'size':50, 1154 'oneTurn':100, 'min':0., 'max':100., 'type':'float', 1155 'showLabel':1, 'continuous':0, 1156 'initialValue':50., 1157 'labelCfg':{'text':'percent'}, 1158 } 1159 1160 self.widgetDescr['targetfaces'] = { 1161 'class':'NEEntry', 'master':'ParamPanel', 'width':10, 1162 'labelCfg':{'text':'num of faces:'}, 1163 } 1164 1165 self.widgetDescr['newgeom'] = { 1166 'class':'NECheckButton', 'initialValue':0, 1167 'master':'ParamPanel', 1168 'labelGridCfg':{'sticky':'we'}, 1169 'labelCfg':{'text':'create new geometry'}, 1170 } 1171 1172 self.widgetDescr['rebuild'] = { 1173 #'class':'NECheckButton', 1174 'class':'NEButton', 1175 'master':'ParamPanel', 1176 'labelGridCfg':{'sticky':'we'}, 1177 'labelCfg':{'text':'rebuild Qslim model'} 1178 } 1179 1180 ip = self.inputPortsDescr 1181 ip.append(datatype='geom', name='geometry') 1182 ip.append(name='percent', datatype='float') 1183 ip.append(name='targetfaces', datatype = "int", required=False) 1184 ip.append (name='rebuild', datatype='boolean', required=False) 1185 ip.append(datatype='None', name='u', required=False) 1186 ip.append(datatype='None', name='v', required=False) 1187 1188 op = self.outputPortsDescr 1189 op.append(datatype='geom', name='geometry') 1190 op.append(datatype='None', name='u') 1191 op.append(datatype='None', name='v') 1192 1193 self.model = None 1194 self.geometry = None # Current geometry 1195 self.newverts = None # Vertex array of new geometry 1196 self.newfaces = None # Face array of new geometry 1197 self.newcolors = None # Color array of new geometry 1198 self.newnorms = None 1199 self.numColors = 0 # Number of valid colors and 1200 self.colorBind = None 1201 self.maxFaces = 0 # number of faces in the geometry used to build the 1202 # QSlim model 1203 1204 code = """def doit(self, geometry , percent, targetfaces=None, rebuild=None, u=None, v=None): 1205 #print 'QSlim Node Running QSlim', percent, targetfaces, rebuild, self.inputPortByName['rebuild'].hasNewData() 1206 # QSlim node can only handle 1 geom so far, so print out a message if there 1207 # are more comming in 1208 if type(geometry)==types.ListType: 1209 if len(geometry)>1: 1210 warnings.warn('Only first geometry is being processed by QSlim node') 1211 geometry = geometry[0] 1212 1213 # check that the incomming geom can be represented as IndexedPolygons 1214 if not geometry.asIndexedPolygons(run=0): 1215 warnings.warn(geometry.name , 'QSlim Node: %s can not be represented as IndexedPolygons', geometry.name) 1216 return 1217 1218 # if there is new data on the first input port (geom) rebuild the model 1219 #if self.inputPortByName['geometry'].hasNewData() or \ 1220 #(self.inputPortByName['rebuild'].hasNewData() and rebuild): 1221 if self.inputPortByName['geometry'].hasNewData() or \ 1222 self.inputPortByName['rebuild'].hasNewData(): 1223 if len(geometry.vertexSet.vertices): 1224 print 'QSlim Node BUILDING MODEL' 1225 self.build_model(geometry, u, v) 1226 else: 1227 return 1228 1229 # compute the number of triangles to be kept 1230 if self.inputPortByName['percent'].hasNewData(): #percent has changed 1231 targetfaces = int(self.maxFaces*percent/100.) 1232 elif self.inputPortByName['targetfaces'].hasNewData() and targetfaces: 1233 #targetfaces has changed 1234 targetfaces = int(targetfaces) 1235 else: # use the percentage 1236 targetfaces = int(self.maxFaces*percent/100.) 1237 print 'ELSE', self.maxFaces*percent, targetfaces 1238 1239 # make sure targetfaces is OK 1240 if targetfaces > self.maxFaces: 1241 targetfaces = self.maxFaces 1242 if self.inputPortByName['percent'].widget: 1243 self.inputPortByName['percent'].widget.set(100, 0) 1244 if self.inputPortByName['targetfaces'].widget: 1245 self.inputPortByName['targetfaces'].widget.set(self.maxFaces, 0) 1246 1247 # decimate 1248 if self.model: 1249 decimVerts, decimFaces, decimNormals, decimColors, decimTex = self.decimate(targetfaces) 1250 redo = self.newcolors is None 1251 geometry.Set(vertices=decimVerts, faces=decimFaces, vnormals=decimNormals, redo=redo) 1252 #print 'len decimated verts = %d, faces = %d, normals = %d' % (len(decimVerts), len(decimFaces), len(decimNormals)) 1253 geometry.vertexSet.vertices.array = decimVerts 1254 geometry.faceSet.faces.array = decimFaces 1255 #geometry.Set(faces=decimFaces, vertices=decimVerts, fnormals = decimNormals, redo=redo) 1256 numFaces = len(decimFaces) 1257 nvert = len(decimVerts) 1258 clen = len(decimFaces) 1259 if self.newcolors: 1260 if self.colorBind == 'vertex': 1261 clen = nvert 1262 #print 'len colors:', clen 1263 if clen > 0: 1264 geometry.Set(materials=decimColors[:clen], 1265 inheritMaterial=False, redo=True) 1266 else: 1267 geometry.Set(inheritMaterial=False, redo=True) 1268 else: 1269 geometry.Set(faces=decimFaces, vertices=decimVerts, fnormals = decimNormals) 1270 # set the target face widget to the actual number of faces after 1271 # decimation 1272 if self.inputPortByName['targetfaces'].widget: 1273 self.inputPortByName['targetfaces'].widget.set(numFaces, run=0) 1274 1275 realpercent = 100.*(float(numFaces)/self.maxFaces) 1276 if percent != realpercent and self.inputPortByName['percent'].widget: 1277 self.inputPortByName['percent'].widget.set(realpercent, run=0) 1278 if self.newtexcoords: 1279 if hasattr(geometry.vertexSet, 'texCoords'): 1280 geometry.Set(textureCoords=decimTex[:nvert]) 1281 self.outputData(geometry=geometry, u=decimTex[:nvert,0], v = decimTex[:nvert,1] ) 1282 else: 1283 self.outputData(geometry=geometry) 1284 """ 1285 self.setFunction(code)
1286 1287
1288 - def build_model(self, geometry, t1=None, t2 = None):
1289 """Build new QSlim model.""" 1290 1291 verts = geometry.getVertices() 1292 faces = geometry.getFaces() 1293 colors = None 1294 self.colorBind = None 1295 binding = geometry.materials[1028].binding[1] 1296 bind_to_face = False 1297 if binding==11: 1298 #per vertex color 1299 bind_to_face = False 1300 self.colorBind = 'vertex' 1301 elif binding==12: 1302 #per face color 1303 bind_to_face = True 1304 self.colorBind = 'face' 1305 if self.colorBind: 1306 colors = geometry.materials[1028].prop[1][:,:3].astype('f') 1307 self.newcolors = Numeric.zeros(colors.shape, 'f') 1308 else: 1309 self.newcolors = None 1310 normals = geometry.getVNormals() 1311 #print "in build_model: faces %d, normals %d " % (len(faces), len(normals)) 1312 #texcoords 1313 texcoords = None 1314 self.newtexcoords = None 1315 #t1 = Numeric.arange(len(verts)).astype('f') 1316 if t1 and t2: 1317 assert len(t1) == len(t2) 1318 texcoords = Numeric.zeros((len(t1),2), "f") 1319 texcoords[:,0] = t1[:] 1320 texcoords[:,1] = t2[:] 1321 elif t1: 1322 texcoords = Numeric.zeros((len(t1),2), "f") 1323 texcoords[:,0] = t1[:] 1324 elif t2: 1325 texcoords = Numeric.zeros((len(t2),2), "f") 1326 texcoords[:,0] = t2[:] 1327 else: 1328 if hasattr(geometry.vertexSet, 'texCoords'): 1329 texcoords = geometry.vertexSet.texCoords.array 1330 if texcoords: 1331 #print "len(texcoords): ", len(texcoords) 1332 #print "len(verts): ", len(verts) 1333 assert len(texcoords) == len(verts) 1334 self.newtexcoords = Numeric.zeros(texcoords.shape, 'f') 1335 self.model = qslimlib.QSlimModel(verts, faces, bindtoface=bind_to_face, 1336 colors=colors, norms=normals, texcoords=texcoords) 1337 nverts = self.model.get_vert_count() 1338 self.maxFaces = nfaces = self.model.get_face_count() 1339 1340 self.newverts = Numeric.zeros((nverts, 3)).astype('f') 1341 self.newfaces = Numeric.zeros((nfaces, 3)).astype('i') 1342 self.newnorms = Numeric.zeros((nverts, 3)).astype('f')
1343
1344 - def decimate(self, targetfaces):
1345 1346 print 'DECIMATING TO', targetfaces 1347 self.model.slim_to_target(targetfaces) 1348 # the number of faces we get back can be slightly different 1349 # from the taget number of faces: 1350 numFaces = self.model.num_valid_faces() 1351 1352 self.model.outmodel(self.newverts, self.newfaces, 1353 outcolors=self.newcolors, outnorms=self.newnorms,outtexcoords=self.newtexcoords ) 1354 numVertices = max(self.newfaces.flat)+1 1355 # build lookup table of vertices used in faces 1356 d = {} 1357 for t in self.newfaces[:numFaces]: 1358 d[int(t[0])] = 1 1359 d[int(t[1])] = 1 1360 d[int(t[2])] = 1 1361 vl = {} 1362 decimVerts = Numeric.zeros((numVertices, 3)).astype('f') 1363 decimFaces = Numeric.zeros((numFaces, 3)).astype('i') 1364 decimNormals = Numeric.zeros((numVertices, 3)).astype('f') 1365 decimColors = None 1366 decimTex = None 1367 1368 if self.newcolors: 1369 decimColors = {'vertex': Numeric.zeros((numVertices, 3)).astype('f'), 1370 'face':self.newcolors[:numFaces]}.get(self.colorBind) 1371 if self.newtexcoords: 1372 decimTex = Numeric.zeros((numVertices, 2)).astype('f') 1373 1374 1375 nvert = 0 1376 for j, t in enumerate(self.newfaces[:numFaces]): 1377 for i in (0,1,2): 1378 n = int(t[i]) 1379 if not vl.has_key(n): 1380 vl[n] = nvert 1381 decimVerts[nvert] = self.newverts[n,:] 1382 decimNormals[nvert] = self.newnorms[n,:] 1383 1384 if self.newcolors: 1385 if self.colorBind == 'vertex': 1386 decimColors[nvert] = self.newcolors[n, :] 1387 if self.newtexcoords: 1388 decimTex[nvert] = self.newtexcoords[n,:] 1389 nvert += 1 1390 decimFaces[j] = (vl[int(t[0])], vl[int(t[1])], vl[int(t[2])]) 1391 1392 return (decimVerts[:nvert], decimFaces, decimNormals[:nvert], decimColors, decimTex)
1393 1394 1395 1396 try: 1397 bhtreelibFound = True 1398 from bhtree import bhtreelib 1399
1400 - class decimate3DPoints(NetworkNode):
1401 """loops over a list of 3D points and removes any point to close to 1402 an already seen one 1403 1404 Input Ports: 1405 points: (required) 3D points to be decimated 1406 cutoff: distance below which a point in cut 1407 1408 Output Ports: 1409 points: decimated set of points 1410 .""" 1411
1412 - def __init__(self, name='getSurfaceVFN', **kw):
1413 kw['name'] = name 1414 apply( NetworkNode.__init__, (self,), kw ) 1415 #self.readOnly = 1 1416 1417 ip = self.inputPortsDescr 1418 ip.append(datatype='coordinates3D', name='point') 1419 ip.append(datatype='float', name='cutoff') 1420 1421 op = self.outputPortsDescr 1422 op.append(datatype='coordinates3D', name='points') 1423 1424 code = """def doit(self, points, cutoff): 1425 import Numeric 1426 ids = Numeric.arrayrange(len(points)).astype('i') 1427 bht = bhtreelib.TBHTree( points, ids, 10, 10, 9999.0 ) 1428 result = Numeric.zeros( (len(points),) ).astype('i') 1429 dist = Numeric.zeros( (len(points),) ).astype('f') 1430 pts = [] 1431 removed = {} 1432 for p in points: 1433 if removed.has_key('%9.6f,%9.6f,%9.6f'%tuple(p)): 1434 continue 1435 pts.append(p) 1436 nb = bht.ClosePointsDist2( tuple(p), cutoff, result, dist ) 1437 for close in result: 1438 removed['%9.6f,%9.6f,%9.6f'%tuple(points[close])] = True 1439 1440 self.outputData(points=pts) 1441 """ 1442 1443 self.setFunction(code)
1444 1445
1446 - class DistFromSphereToGeom(NetworkNode):
1447 """assign to every sphere the distance to the closest surface vertex. 1448 1449 Input ports: 1450 centers: a list of sphere centers 1451 radii: list of sphere radii 1452 geom: a polygonal geometry 1453 1454 OutputPort: 1455 dist: list of distances 1456 """
1457 - def __init__(self, name='DistFromSphereToGeom', **kw):
1458 kw['name'] = name 1459 apply( NetworkNode.