/src/addons/Display/OCCViewer.py

http://pythonocc.googlecode.com/ · Python · 496 lines · 300 code · 44 blank · 152 comment · 26 complexity · a6482fa7a0e3a95cae81750adc58e37d MD5 · raw file

  1. #!/usr/bin/env python
  2. from OCC.TopoDS import *
  3. ##Copyright 2008-2011 Thomas Paviot (tpaviot@gmail.com)
  4. ##
  5. ##This file is part of pythonOCC.
  6. ##
  7. ##pythonOCC is free software: you can redistribute it and/or modify
  8. ##it under the terms of the GNU Lesser General Public License as published by
  9. ##the Free Software Foundation, either version 3 of the License, or
  10. ##(at your option) any later version.
  11. ##
  12. ##pythonOCC is distributed in the hope that it will be useful,
  13. ##but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ##GNU Lesser General Public License for more details.
  16. ##
  17. ##You should have received a copy of the GNU Lesser General Public License
  18. ##along with pythonOCC. If not, see <http://www.gnu.org/licenses/>.
  19. # project
  20. import OCC.Visualization
  21. import OCC.V3d
  22. import OCC.V2d
  23. import OCC.AIS
  24. import OCC.AIS2D
  25. import OCC.Quantity
  26. import OCC.TopoDS
  27. import OCC.Visual3d
  28. from OCC import Quantity
  29. from OCC.Prs3d import *
  30. from OCC.Utils.Construct import gp_Dir, gp_Vec, gp_Pnt
  31. from OCC.Utils.Common import color, to_string
  32. # stdlib
  33. import os, os.path, types, sys, subprocess, math
  34. try:
  35. import OCC.NIS
  36. HAVE_NIS = False
  37. except ImportError:
  38. HAVE_NIS = False
  39. def set_CSF_GraphicShr():
  40. # First set up the CSF_GraphicShr environment variable
  41. # This variable must point to the libTKOpenGl OCC dynamic library
  42. # Under Linux, use the ldd tool to locate this library
  43. # Use otool with MacOSX
  44. # First find the dynamic library for the module V3d
  45. # on MacOSX, the following line should result something like:
  46. # '/Library/Python/2.6/site-packages/OCC/_V3d.so'
  47. v3d_module_library = sys.modules['_V3d'].__file__
  48. if sys.platform == 'darwin':
  49. # MacOSX : run the otool -L tool to check to which libTkOpenGl library it's linked
  50. # otool -L /Library/Python/2.6/site-packages/OCC/_V3d.so | grep -i
  51. p1 = subprocess.Popen(['otool','-L','%s'%v3d_module_library],stdout=subprocess.PIPE)
  52. elif sys.platform =='linux2':
  53. # Linux : run ldd
  54. # ldd /Library/Python/2.6/site-packages/OCC/_V3d.so | grep -i
  55. p1 = subprocess.Popen(['ldd','%s'%v3d_module_library],stdout=subprocess.PIPE)
  56. p2 = subprocess.Popen(['grep','-i','libTkOpenGl'],stdin=p1.stdout,stdout=subprocess.PIPE)
  57. output = p2.communicate()[0]
  58. if sys.platform == 'darwin':
  59. # MacOSX : output is something like
  60. # /Library/OpenCASCADE/6.3.0//lib/libTKOpenGl.0.dylib (compatibility version 1.0.0, current version 1.0.0)
  61. libTkOpenGl_library = output.split(' ')[0][1:]
  62. elif sys.platform == 'linux2':
  63. # Linux : output is something like
  64. # libTKOpenGl-6.3.0.so => /usr/lib/libTKOpenGl-6.3.0.so (0x0088b000)
  65. libTkOpenGl_library = output.split(' ')[2]
  66. # then set up the env var
  67. os.environ['CSF_GraphicShr'] = libTkOpenGl_library
  68. if not (sys.platform == 'win32'):
  69. set_CSF_GraphicShr()
  70. class BaseDriver(object):
  71. """
  72. The base driver class for both Driver2d and Driver3d classes
  73. """
  74. def __init__(self, window_handle):
  75. self._window_handle = window_handle
  76. self._inited = False
  77. self._local_context_opened = False
  78. self.Context_handle = None
  79. self.Viewer_handle = None
  80. self.View_handle = None
  81. self.Context = None
  82. self.Viewer = None
  83. self.View = None
  84. self.selected_shape = None
  85. self.selected_shapes = []
  86. def MoveTo(self,X,Y):
  87. self.Context.MoveTo(X,Y,self.View_handle)
  88. def FitAll(self):
  89. self.View.ZFitAll()
  90. self.View.FitAll()
  91. def SetWindow(self,window_handle):
  92. self._window_handle = window_handle
  93. def Create(self, create_default_lights = True):
  94. if sys.platform!='win32':
  95. try:
  96. os.environ['DISPLAY']
  97. except KeyError:
  98. raise AssertionError("Please set the DISPLAY environment variable.")
  99. self.Init(self._window_handle)
  100. self.Context_handle = self.GetContext()
  101. self.Viewer_handle = self.GetViewer()
  102. self.View_handle = self.GetView()
  103. self.Context = self.Context_handle.GetObject()
  104. self.Viewer = self.Viewer_handle.GetObject()
  105. if create_default_lights and isinstance(self, Viewer3d):
  106. self.Viewer.SetDefaultLights()
  107. self.Viewer.SetLightOn()
  108. self.View = self.View_handle.GetObject()
  109. self._inited = True
  110. # nessecary for text rendering
  111. try:
  112. self._struc_mgr = self.Context.MainPrsMgr().GetObject().StructureManager()
  113. self.aPresentation = Prs3d_Presentation(self._struc_mgr)
  114. except:
  115. pass
  116. class Viewer2d(BaseDriver, OCC.Visualization.Display2d):
  117. def __init__(self, window_handle ):
  118. BaseDriver.__init__(self,window_handle)
  119. OCC.Visualization.Display2d.__init__(self)
  120. def OnResize(self):
  121. self.View.MustBeResized(OCC.V2d.V2d_TOWRE_ENLARGE_SPACE)
  122. def DisplayShape(self, shapes, material=None, texture=None, angle=None, update=True, fitall=True ):
  123. '''
  124. display a TopoDS_* in the viewer
  125. @param shapes: shape or iterable of shapes
  126. ( where shape is a subclass of TopoDS_Shape )
  127. @param material: sets a custom material to the shape
  128. @param texture: add a texture to the shape
  129. @param angle: sets a custom deviation angle to the shape
  130. @param update: updates the viewer
  131. @fitall: whether the camera's viewpoint is updated or not...
  132. '''
  133. ais_shapes = []
  134. if issubclass(shapes.__class__, TopoDS_Shape):
  135. shapes = [shapes]
  136. SOLO = True
  137. else:
  138. SOLO = False
  139. for shape in shapes:
  140. if material:#careful: != operator segfaults
  141. #print 'material', material
  142. self.View.SetSurfaceDetail(OCC.V3d.V3d_TEX_ALL)
  143. shape_to_display = OCC.AIS.AIS_TexturedShape(shape)
  144. shape_to_display.SetMaterial(material)
  145. if angle:
  146. shape_to_display.SetAngleAndDeviation(angle)
  147. if texture:
  148. filename, toScaleU, toScaleV, toRepeatU, toRepeatV, originU, originV = texture.GetProperties()
  149. shape_to_display.SetTextureFileName(OCC.TCollection.TCollection_AsciiString(filename))
  150. shape_to_display.SetTextureMapOn()
  151. shape_to_display.SetTextureScale(True, toScaleU, toScaleV)
  152. shape_to_display.SetTextureRepeat(True, toRepeatU, toRepeatV)
  153. shape_to_display.SetTextureOrigin(True, originU, originV)
  154. shape_to_display.SetDisplayMode(3);
  155. ais_shapes.append(shape_to_display.GetHandle())
  156. else:
  157. shape_to_display = OCC.AIS.AIS_Shape(shape)
  158. if angle:
  159. shape_to_display.SetAngleAndDeviation(angle)
  160. ais_shapes.append(shape_to_display.GetHandle())
  161. self.Context.Display(shape_to_display.GetHandle())
  162. if fitall:
  163. self.FitAll()
  164. if SOLO:
  165. return ais_shapes[0]
  166. else:
  167. return ais_shapes
  168. if HAVE_NIS:
  169. class NISViewer3d(BaseDriver, OCC.Visualization.NISDisplay3d):
  170. def __init__(self, window_handle ):
  171. BaseDriver.__init__(self,window_handle)
  172. OCC.Visualization.NISDisplay3d.__init__(self)
  173. def OnResize(self):
  174. self.View.MustBeResized()
  175. class Viewer3d(BaseDriver, OCC.Visualization.Display3d):
  176. def __init__(self, window_handle ):
  177. BaseDriver.__init__(self,window_handle)
  178. OCC.Visualization.Display3d.__init__(self)
  179. self.selected_shape = None
  180. # useful for DisplayMessage & DisplayVector
  181. def OnResize(self):
  182. self.View.MustBeResized()
  183. def ResetView(self):
  184. self.View.Reset()
  185. def Repaint(self):
  186. self.Viewer.Redraw()
  187. def SetModeWireFrame(self):
  188. self.View.SetComputedMode(False)
  189. self.Context.SetDisplayMode(OCC.AIS.AIS_WireFrame)
  190. def SetModeShaded(self):
  191. self.View.SetComputedMode(False)
  192. if sys.platform=='win32':
  193. self.View.SetAntialiasingOff()
  194. self.Context.SetDisplayMode(OCC.AIS.AIS_Shaded)
  195. def SetModeQuickHLR(self):
  196. self.View.SetComputedMode(True)
  197. self.Context.SetDisplayMode(OCC.AIS.AIS_QuickHLR)
  198. def SetModeExactHLR(self):
  199. self.View.SetComputedMode(True)
  200. self.Context.SetDisplayMode(OCC.AIS.AIS_ExactHLR)
  201. def SetOrthographic(self, _bool):
  202. '''
  203. sets whether this view is a orthographic or perspective view
  204. @param _bool:
  205. '''
  206. pass
  207. def View_Top(self):
  208. self.View.SetProj(OCC.V3d.V3d_Zpos)
  209. def View_Bottom(self):
  210. self.View.SetProj(OCC.V3d.V3d_Zneg)
  211. def View_Left(self):
  212. self.View.SetProj(OCC.V3d.V3d_Xneg)
  213. def View_Right(self):
  214. self.View.SetProj(OCC.V3d.V3d_Xpos)
  215. def View_Front(self):
  216. self.View.SetProj(OCC.V3d.V3d_Yneg)
  217. def View_Rear(self):
  218. self.View.SetProj(OCC.V3d.V3d_Ypos)
  219. def View_Iso(self):
  220. self.View.SetProj(OCC.V3d.V3d_XposYnegZpos)
  221. def ExportToImage(self,Filename):
  222. self.View.Dump(Filename)
  223. def SetBackgroundImage(self, filename, Stretch = True):
  224. if not os.path.isfile(filename):
  225. raise IOError("image file %s not found."%filename)
  226. if (Stretch):
  227. self.View.SetBackgroundImage(filename, OCC.Aspect.Aspect_FM_STRETCH, True)
  228. else:
  229. self.View.SetBackgroundImage(filename, OCC.Aspect.Aspect_FM_NONE, True )
  230. def DisplayMessage(self,point,text_to_write, message_color=None, update=True):
  231. """
  232. :point: a gp_Pnt instance
  233. :text_to_write: a string
  234. :message_color: triple with the range 0-1
  235. """
  236. text_aspect = Prs3d_TextAspect()
  237. if message_color is not None:
  238. text_aspect.SetColor(color(*message_color))
  239. Prs3d_Text().Draw(self.aPresentation.GetHandle(),
  240. text_aspect.GetHandle(),
  241. to_string(text_to_write),
  242. point)
  243. self.aPresentation.Display()
  244. # TODO: it would be more coherent if a AIS_InteractiveObject would be returned
  245. if update:
  246. self.Repaint()
  247. return self.aPresentation
  248. def DisplayShape(self, shapes, material=None, texture=None, update=False):
  249. '''
  250. '''
  251. if issubclass(shapes.__class__, TopoDS_Shape):
  252. shapes = [shapes]
  253. SOLO = True
  254. else:
  255. SOLO = False
  256. ais_shapes = []
  257. for shape in shapes:
  258. if material or texture:#careful: != operator segfaults
  259. self.View.SetSurfaceDetail(OCC.V3d.V3d_TEX_ALL)
  260. shape_to_display = OCC.AIS.AIS_TexturedShape(shape)
  261. if material:
  262. shape_to_display.SetMaterial(material)
  263. if texture:
  264. filename, toScaleU, toScaleV, toRepeatU, toRepeatV, originU, originV = texture.GetProperties()
  265. shape_to_display.SetTextureFileName(OCC.TCollection.TCollection_AsciiString(filename))
  266. shape_to_display.SetTextureMapOn()
  267. shape_to_display.SetTextureScale(True, toScaleU, toScaleV)
  268. shape_to_display.SetTextureRepeat(True, toRepeatU, toRepeatV)
  269. shape_to_display.SetTextureOrigin(True, originU, originV)
  270. shape_to_display.SetDisplayMode(3);
  271. ais_shapes.append(shape_to_display.GetHandle())
  272. else:
  273. shape_to_display = OCC.AIS.AIS_Shape(shape)
  274. ais_shapes.append(shape_to_display.GetHandle())
  275. if update:
  276. # only update when explicitely told to do so
  277. self.Context.Display(shape_to_display.GetHandle(), True)
  278. # especially this call takes up a lot of time...
  279. self.FitAll()
  280. else:
  281. self.Context.Display(shape_to_display.GetHandle(), False)
  282. if SOLO:
  283. return ais_shapes[0]
  284. else:
  285. return ais_shapes
  286. def DisplayColoredShape(self, shapes, color='YELLOW', update=True, ):
  287. ais_shapes = []
  288. if isinstance(color, str):
  289. dict_color = {'WHITE':OCC.Quantity.Quantity_NOC_WHITE,
  290. 'BLUE':OCC.Quantity.Quantity_NOC_BLUE1,
  291. 'RED':OCC.Quantity.Quantity_NOC_RED,
  292. 'GREEN':OCC.Quantity.Quantity_NOC_GREEN,
  293. 'YELLOW':OCC.Quantity.Quantity_NOC_YELLOW,
  294. # new
  295. 'CYAN':OCC.Quantity.Quantity_NOC_CYAN1,
  296. 'WHITE':OCC.Quantity.Quantity_NOC_WHITE,
  297. 'BLACK':OCC.Quantity.Quantity_NOC_BLACK,
  298. 'ORANGE':OCC.Quantity.Quantity_NOC_ORANGE, }
  299. color = dict_color[color]
  300. elif isinstance(color, Quantity_Color):
  301. pass
  302. else:
  303. raise ValueError('color should either be a string ( "BLUE" ) or a Quantity_Color(0.1, 0.8, 0.1) got %s' % color)
  304. if issubclass(shapes.__class__, TopoDS_Shape):
  305. shapes = [shapes]
  306. SOLO = True
  307. else:
  308. SOLO = False
  309. for shape in shapes:
  310. shape_to_display = OCC.AIS.AIS_Shape(shape).GetHandle()
  311. self.Context.SetColor(shape_to_display,color,0)
  312. if update:
  313. self.Context.Display(shape_to_display, True)
  314. self.FitAll()
  315. else:
  316. # don't update the view when shape_to_display is added
  317. # comes in handy when adding lots and lots of objects
  318. self.Context.Display(shape_to_display, False)
  319. ais_shapes.append(shape_to_display)
  320. if SOLO:
  321. return ais_shapes[0]
  322. else:
  323. return ais_shapes
  324. def DisplayVector(self, vec, pnt, update=False):
  325. if self._inited:
  326. arrow = Prs3d_Arrow()
  327. arrow.Draw(
  328. self.aPresentation.GetHandle(),
  329. (pnt.as_vec() + vec).as_pnt(),
  330. gp_Dir(vec),
  331. math.radians(20),
  332. vec.Magnitude()
  333. )
  334. self.aPresentation.Display()
  335. # it would be more coherent if a AIS_InteractiveObject would be returned
  336. if update:
  337. self.Repaint()
  338. return self.aPresentation
  339. def DisplayTriedron(self):
  340. self.View.TriedronDisplay(OCC.Aspect.Aspect_TOTP_RIGHT_LOWER, OCC.Quantity.Quantity_NOC_BLACK, 0.08, OCC.V3d.V3d_WIREFRAME)
  341. self.Repaint()
  342. def EnableAntiAliasing(self):
  343. self.View.SetAntialiasingOn()
  344. self.Repaint()
  345. def DisableAntiAliasing(self):
  346. self.View.SetAntialiasingOff()
  347. self.Repaint()
  348. def EraseAll(self):
  349. self._objects_displayed = []
  350. # nessecary to remove text added by DisplayMessage
  351. self.Context.PurgeDisplay()
  352. self.Context.EraseAll()
  353. def Tumble(self,NumImages,Animation = True):
  354. self.View.Tumble(NumImages, Animation)
  355. def Pan(self,Dx,Dy):
  356. self.View.Pan(Dx,Dy)
  357. def SetSelectionMode(self,mode = OCC.TopAbs.TopAbs_FACE):
  358. self.Context.CloseAllContexts()
  359. self.Context.OpenLocalContext()
  360. self.Context.ActivateStandardMode(mode)
  361. def SetSelectionModeVertex(self):
  362. self.Context.CloseAllContexts()
  363. self.Context.OpenLocalContext()
  364. self.Context.ActivateStandardMode(OCC.TopAbs.TopAbs_VERTEX)
  365. def SetSelectionModeEdge(self):
  366. self.Context.CloseAllContexts()
  367. self.Context.OpenLocalContext()
  368. self.Context.ActivateStandardMode(OCC.TopAbs.TopAbs_EDGE)
  369. def SetSelectionModeFace(self):
  370. self.Context.CloseAllContexts()
  371. self.Context.OpenLocalContext()
  372. self.Context.ActivateStandardMode(OCC.TopAbs.TopAbs_FACE)
  373. def SetSelectionModeShape(self):
  374. self.Context.CloseAllContexts()
  375. def SetSelectionModeNeutral(self):
  376. self.Context.CloseAllContexts()
  377. def GetSelectedShapes(self):
  378. return self.selected_shapes
  379. def GetSelectedShape(self):
  380. """
  381. Returns the current selected shape
  382. """
  383. return self.selected_shape
  384. def SelectArea(self,Xmin,Ymin,Xmax,Ymax):
  385. self.Context.Select(Xmin,Ymin,Xmax,Ymax,self.View_handle)
  386. self.Context.InitSelected()
  387. # reinit the selected_shapes list
  388. self.selected_shapes = []
  389. while self.Context.MoreSelected():
  390. if self.Context.HasSelectedShape():
  391. self.selected_shapes.append(self.Context.SelectedShape())
  392. self.Context.NextSelected()
  393. print "Current selection (%i instances):"%len(self.selected_shapes),self.selected_shapes
  394. def Select(self,X,Y):
  395. self.Context.Select()
  396. self.Context.InitSelected()
  397. if self.Context.MoreSelected():
  398. if self.Context.HasSelectedShape():
  399. self.selected_shape = self.Context.SelectedShape()
  400. print "Current selection (single):",self.selected_shape
  401. else:
  402. self.selected_shape = None
  403. def Rotation(self,X,Y):
  404. self.View.Rotation(X,Y)
  405. def DynamicZoom(self,X1,Y1,X2,Y2):
  406. self.View.Zoom(X1,Y1,X2,Y2)
  407. def ZoomFactor(self,zoom_factor):
  408. self.View.SetZoom(zoom_factor)
  409. def ZoomArea(self,X1,Y1,X2,Y2):
  410. self.View.WindowFit(X1,Y1,X2,Y2)
  411. def Zoom(self,X,Y):
  412. self.View.Zoom(X,Y)
  413. def StartRotation(self,X,Y):
  414. self.View.StartRotation(X,Y)