python-microscopy 25.6.5__cp39-cp39-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1277) hide show
  1. PYME/Acquire/ActionManager.py +450 -0
  2. PYME/Acquire/ExecTools.py +235 -0
  3. PYME/Acquire/Hardware/AAOptoelectronics/MDS.py +175 -0
  4. PYME/Acquire/Hardware/AAOptoelectronics/__init__.py +0 -0
  5. PYME/Acquire/Hardware/ARCoptix/__init__.py +0 -0
  6. PYME/Acquire/Hardware/ARCoptix/lcdriver.py +149 -0
  7. PYME/Acquire/Hardware/ARCoptix/lcserver32.py +123 -0
  8. PYME/Acquire/Hardware/AndorIXon/AndorCam.py +1095 -0
  9. PYME/Acquire/Hardware/AndorIXon/AndorControlFrame.py +312 -0
  10. PYME/Acquire/Hardware/AndorIXon/AndorIXon.py +737 -0
  11. PYME/Acquire/Hardware/AndorIXon/__init__.py +27 -0
  12. PYME/Acquire/Hardware/AndorNeo/AndorNeo.py +644 -0
  13. PYME/Acquire/Hardware/AndorNeo/AndorNeoControlFrame.py +161 -0
  14. PYME/Acquire/Hardware/AndorNeo/AndorZyla.py +761 -0
  15. PYME/Acquire/Hardware/AndorNeo/SDK3.py +307 -0
  16. PYME/Acquire/Hardware/AndorNeo/SDK3Cam.py +171 -0
  17. PYME/Acquire/Hardware/AndorNeo/ZylaControlPanel.py +105 -0
  18. PYME/Acquire/Hardware/AndorNeo/__init__.py +22 -0
  19. PYME/Acquire/Hardware/AndorNeo/parseAtdebug.py +46 -0
  20. PYME/Acquire/Hardware/AndorNeo/plotTimings.py +73 -0
  21. PYME/Acquire/Hardware/AndorNeo/testNeo.py +62 -0
  22. PYME/Acquire/Hardware/Camera.py +1199 -0
  23. PYME/Acquire/Hardware/CameraSkeleton.py +642 -0
  24. PYME/Acquire/Hardware/Coherent/OBIS.py +147 -0
  25. PYME/Acquire/Hardware/Coherent/Sapphire.py +162 -0
  26. PYME/Acquire/Hardware/Coherent/__init__.py +0 -0
  27. PYME/Acquire/Hardware/DMDGui.py +198 -0
  28. PYME/Acquire/Hardware/DigiData/DigiData.py +95 -0
  29. PYME/Acquire/Hardware/DigiData/DigiDataClient.py +30 -0
  30. PYME/Acquire/Hardware/DigiData/RemoteDigiData.py +51 -0
  31. PYME/Acquire/Hardware/DigiData/__init__.py +24 -0
  32. PYME/Acquire/Hardware/DigiData/axDD132x.py +348 -0
  33. PYME/Acquire/Hardware/EMCCDTheory.py +41 -0
  34. PYME/Acquire/Hardware/ExciterWheel.py +167 -0
  35. PYME/Acquire/Hardware/FilterWheel.py +184 -0
  36. PYME/Acquire/Hardware/FocCorr.py +106 -0
  37. PYME/Acquire/Hardware/FocCorrR.py +196 -0
  38. PYME/Acquire/Hardware/FrFilter.py +51 -0
  39. PYME/Acquire/Hardware/GCS/GCS_DLL.py +370 -0
  40. PYME/Acquire/Hardware/GCS/__init__.py +0 -0
  41. PYME/Acquire/Hardware/GCS/gcs.py +210 -0
  42. PYME/Acquire/Hardware/HamamatsuDCAM/HamamatsuDCAM.py +662 -0
  43. PYME/Acquire/Hardware/HamamatsuDCAM/HamamatsuORCA.py +602 -0
  44. PYME/Acquire/Hardware/HamamatsuDCAM/Hamamatsu_control_panel.py +54 -0
  45. PYME/Acquire/Hardware/HamamatsuDCAM/__init__.py +23 -0
  46. PYME/Acquire/Hardware/LaserControlFrame.py +160 -0
  47. PYME/Acquire/Hardware/MPBCommunications/MPBCW.py +132 -0
  48. PYME/Acquire/Hardware/MPBCommunications/__init__.py +0 -0
  49. PYME/Acquire/Hardware/Mercury/Mercury.py +269 -0
  50. PYME/Acquire/Hardware/Mercury/PI_Mercury_GCS_DLL.py +857 -0
  51. PYME/Acquire/Hardware/Mercury/__init__.py +22 -0
  52. PYME/Acquire/Hardware/Mercury/mercuryStepper.py +203 -0
  53. PYME/Acquire/Hardware/Mercury/mercuryStepperGCS.py +281 -0
  54. PYME/Acquire/Hardware/NikonTE2000.py +68 -0
  55. PYME/Acquire/Hardware/NikonTi.py +214 -0
  56. PYME/Acquire/Hardware/NikonTiGUI.py +81 -0
  57. PYME/Acquire/Hardware/OrielCornerstone.py +125 -0
  58. PYME/Acquire/Hardware/PM100USB.py +51 -0
  59. PYME/Acquire/Hardware/Piezos/__init__.py +22 -0
  60. PYME/Acquire/Hardware/Piezos/base_piezo.py +108 -0
  61. PYME/Acquire/Hardware/Piezos/offsetPiezo.py +162 -0
  62. PYME/Acquire/Hardware/Piezos/offsetPiezoREST.py +308 -0
  63. PYME/Acquire/Hardware/Piezos/piezo_c867.py +486 -0
  64. PYME/Acquire/Hardware/Piezos/piezo_e255.py +68 -0
  65. PYME/Acquire/Hardware/Piezos/piezo_e662.py +61 -0
  66. PYME/Acquire/Hardware/Piezos/piezo_e709.py +434 -0
  67. PYME/Acquire/Hardware/Piezos/piezo_e816.py +452 -0
  68. PYME/Acquire/Hardware/Piezos/piezo_e816_corr.py +70 -0
  69. PYME/Acquire/Hardware/Piezos/piezo_e816_dll.py +518 -0
  70. PYME/Acquire/Hardware/Piezos/piezo_e816b.py +70 -0
  71. PYME/Acquire/Hardware/Piezos/piezo_pipython_gcs.py +560 -0
  72. PYME/Acquire/Hardware/Piezos/piezo_test.py +55 -0
  73. PYME/Acquire/Hardware/Simulator/EmpiricalHist.py +273 -0
  74. PYME/Acquire/Hardware/Simulator/__init__.py +22 -0
  75. PYME/Acquire/Hardware/Simulator/dSimControl.py +961 -0
  76. PYME/Acquire/Hardware/Simulator/fakeCam.py +651 -0
  77. PYME/Acquire/Hardware/Simulator/fakePiezo.py +95 -0
  78. PYME/Acquire/Hardware/Simulator/fluor.py +336 -0
  79. PYME/Acquire/Hardware/Simulator/illuminate.cp39-win_amd64.dll.a +0 -0
  80. PYME/Acquire/Hardware/Simulator/illuminate.cp39-win_amd64.pyd +0 -0
  81. PYME/Acquire/Hardware/Simulator/illuminate.pyx +93 -0
  82. PYME/Acquire/Hardware/Simulator/lasersliders.py +90 -0
  83. PYME/Acquire/Hardware/Simulator/rend_im.py +446 -0
  84. PYME/Acquire/Hardware/Simulator/setup.py +69 -0
  85. PYME/Acquire/Hardware/Simulator/simcontrol.py +348 -0
  86. PYME/Acquire/Hardware/Simulator/simui_wx.py +794 -0
  87. PYME/Acquire/Hardware/Tango/__init__.py +0 -0
  88. PYME/Acquire/Hardware/Tango/marzhauser_tango.py +583 -0
  89. PYME/Acquire/Hardware/TiLightCrafter.py +388 -0
  90. PYME/Acquire/Hardware/__init__.py +22 -0
  91. PYME/Acquire/Hardware/aotf.py +271 -0
  92. PYME/Acquire/Hardware/arclampshutterpanel.py +71 -0
  93. PYME/Acquire/Hardware/cameraSoftwareBuffer.py +215 -0
  94. PYME/Acquire/Hardware/camera_noise.py +270 -0
  95. PYME/Acquire/Hardware/ccdAdjPanel.py +188 -0
  96. PYME/Acquire/Hardware/ccdCalibrator.py +161 -0
  97. PYME/Acquire/Hardware/cobaltLaser.py +108 -0
  98. PYME/Acquire/Hardware/cobaltLaser561.py +94 -0
  99. PYME/Acquire/Hardware/comports.py +26 -0
  100. PYME/Acquire/Hardware/driftTrackGUI.py +488 -0
  101. PYME/Acquire/Hardware/driftTracking.py +595 -0
  102. PYME/Acquire/Hardware/fakeShutters.py +52 -0
  103. PYME/Acquire/Hardware/focusKeys.py +204 -0
  104. PYME/Acquire/Hardware/focus_locks/__init__.py +0 -0
  105. PYME/Acquire/Hardware/focus_locks/reflection_focus_lock.py +684 -0
  106. PYME/Acquire/Hardware/frZStage.py +117 -0
  107. PYME/Acquire/Hardware/fw102.py +38 -0
  108. PYME/Acquire/Hardware/ids_peak_cam.py +561 -0
  109. PYME/Acquire/Hardware/ioslave.py +305 -0
  110. PYME/Acquire/Hardware/lasers.py +292 -0
  111. PYME/Acquire/Hardware/matchboxLaser.py +58 -0
  112. PYME/Acquire/Hardware/microscope_adapter.py +104 -0
  113. PYME/Acquire/Hardware/mpd_picosecond_delayer.py +395 -0
  114. PYME/Acquire/Hardware/multiview.py +287 -0
  115. PYME/Acquire/Hardware/olympusix81.py +59 -0
  116. PYME/Acquire/Hardware/pco/__init__.py +0 -0
  117. PYME/Acquire/Hardware/pco/pco_cam.py +348 -0
  118. PYME/Acquire/Hardware/pco/pco_edge_42_lt.py +34 -0
  119. PYME/Acquire/Hardware/pco/pco_sdk.py +1645 -0
  120. PYME/Acquire/Hardware/pco/pco_sdk_cam.py +518 -0
  121. PYME/Acquire/Hardware/pco/pco_sdk_cam_control_panel.py +54 -0
  122. PYME/Acquire/Hardware/phoxxLaser.py +199 -0
  123. PYME/Acquire/Hardware/phoxxLaserOLD.py +242 -0
  124. PYME/Acquire/Hardware/priorLumen.py +102 -0
  125. PYME/Acquire/Hardware/priorarclampshutter.py +52 -0
  126. PYME/Acquire/Hardware/setup.py +50 -0
  127. PYME/Acquire/Hardware/spacenav.py +148 -0
  128. PYME/Acquire/Hardware/splitter.py +394 -0
  129. PYME/Acquire/Hardware/thorlabsPiezo.py +98 -0
  130. PYME/Acquire/Hardware/thorlabs_cam.py +136 -0
  131. PYME/Acquire/Hardware/thorlabs_mff_flipper.py +400 -0
  132. PYME/Acquire/Hardware/toptica_ibeam.py +51 -0
  133. PYME/Acquire/Hardware/uc480/AndorControlFrame.py +308 -0
  134. PYME/Acquire/Hardware/uc480/__init__.py +27 -0
  135. PYME/Acquire/Hardware/uc480/uCam480.py +695 -0
  136. PYME/Acquire/Hardware/uc480/uc480.py +189 -0
  137. PYME/Acquire/Hardware/uc480/uc480Deprecated.py +87 -0
  138. PYME/Acquire/Hardware/uc480/uc480_h.py +767 -0
  139. PYME/Acquire/Hardware/uc480/uc480_h_gen.py +62 -0
  140. PYME/Acquire/Hardware/uc480/ucCamControlFrame.py +83 -0
  141. PYME/Acquire/Hardware/ueye.py +702 -0
  142. PYME/Acquire/PYMEAcquire.py +186 -0
  143. PYME/Acquire/Protocols/DMDMFM.py +74 -0
  144. PYME/Acquire/Protocols/__init__.py +22 -0
  145. PYME/Acquire/Protocols/darkCalibrate.py +56 -0
  146. PYME/Acquire/Protocols/dual671_470.py +45 -0
  147. PYME/Acquire/Protocols/dual671_488.py +46 -0
  148. PYME/Acquire/Protocols/gainCal.py +53 -0
  149. PYME/Acquire/Protocols/htsms-cal-psf.py +32 -0
  150. PYME/Acquire/Protocols/htsms-cal-registration.py +52 -0
  151. PYME/Acquire/Protocols/htsms-flow.py +29 -0
  152. PYME/Acquire/Protocols/htsms-staggered.py +31 -0
  153. PYME/Acquire/Protocols/htsms-tile.py +68 -0
  154. PYME/Acquire/Protocols/htsms-two-color.py +29 -0
  155. PYME/Acquire/Protocols/htsms-widefield.py +32 -0
  156. PYME/Acquire/Protocols/paint671.py +61 -0
  157. PYME/Acquire/Protocols/photoconversion.py +52 -0
  158. PYME/Acquire/Protocols/photoconversion2.py +55 -0
  159. PYME/Acquire/Protocols/prebleach488.py +70 -0
  160. PYME/Acquire/Protocols/prebleach490.py +69 -0
  161. PYME/Acquire/Protocols/prebleach561.py +72 -0
  162. PYME/Acquire/Protocols/prebleach561NeomEos2.py +70 -0
  163. PYME/Acquire/Protocols/prebleach561NeomEos2ND1.py +70 -0
  164. PYME/Acquire/Protocols/prebleach642.py +70 -0
  165. PYME/Acquire/Protocols/prebleach671.py +69 -0
  166. PYME/Acquire/Protocols/prebleach671ND1.py +58 -0
  167. PYME/Acquire/Protocols/prebleach671ND2.py +57 -0
  168. PYME/Acquire/Protocols/prebleach671Neo.py +69 -0
  169. PYME/Acquire/Protocols/recover671.py +62 -0
  170. PYME/Acquire/Protocols/sequence642_488.py +85 -0
  171. PYME/Acquire/Protocols/sequence642_561.py +87 -0
  172. PYME/Acquire/Protocols/shiftfield.py +198 -0
  173. PYME/Acquire/Protocols/simul488.py +59 -0
  174. PYME/Acquire/Protocols/simul642.py +63 -0
  175. PYME/Acquire/Protocols/simul642HTSMS.py +63 -0
  176. PYME/Acquire/Protocols/simulPA.py +37 -0
  177. PYME/Acquire/Protocols/simulSIM.py +76 -0
  178. PYME/Acquire/Protocols/simulStep.py +62 -0
  179. PYME/Acquire/Protocols/spdscan.py +178 -0
  180. PYME/Acquire/Protocols/standard470.py +45 -0
  181. PYME/Acquire/Protocols/standard488.py +47 -0
  182. PYME/Acquire/Protocols/standard671.py +48 -0
  183. PYME/Acquire/Protocols/standard671_no532.py +47 -0
  184. PYME/Acquire/Protocols/standardArclamp.py +46 -0
  185. PYME/Acquire/Protocols/strip_tile.py +204 -0
  186. PYME/Acquire/Protocols/tile.py +204 -0
  187. PYME/Acquire/Protocols/tile3D.py +192 -0
  188. PYME/Acquire/Protocols/tile671.py +190 -0
  189. PYME/Acquire/Protocols/tile_triggered.py +189 -0
  190. PYME/Acquire/Scripts/init.py +232 -0
  191. PYME/Acquire/Scripts/init_N1_Ti_Exeter_ZIx.py +374 -0
  192. PYME/Acquire/Scripts/init_N2_Ti_Zyla_lasers.py +290 -0
  193. PYME/Acquire/Scripts/init_Neo.py +266 -0
  194. PYME/Acquire/Scripts/init_NeoSim.py +159 -0
  195. PYME/Acquire/Scripts/init_TIRF.py +232 -0
  196. PYME/Acquire/Scripts/init_TIRF_Neo.py +242 -0
  197. PYME/Acquire/Scripts/init_TIRF_NeoO.py +241 -0
  198. PYME/Acquire/Scripts/init_TIRF_onecam.py +233 -0
  199. PYME/Acquire/Scripts/init_Ti.py +257 -0
  200. PYME/Acquire/Scripts/init_UOA_n.py +256 -0
  201. PYME/Acquire/Scripts/init_UOA_n2.py +246 -0
  202. PYME/Acquire/Scripts/init_Y1.py +249 -0
  203. PYME/Acquire/Scripts/init_Zyla.py +260 -0
  204. PYME/Acquire/Scripts/init_drift_tracking.py +80 -0
  205. PYME/Acquire/Scripts/init_emccd_basic.py +277 -0
  206. PYME/Acquire/Scripts/init_htsms.py +277 -0
  207. PYME/Acquire/Scripts/init_htsms_focus_lock.py +132 -0
  208. PYME/Acquire/Scripts/init_micrpi.py +45 -0
  209. PYME/Acquire/Scripts/init_orca.py +70 -0
  210. PYME/Acquire/Scripts/init_pco.py +49 -0
  211. PYME/Acquire/Scripts/init_rev.py +74 -0
  212. PYME/Acquire/Scripts/init_sim100.py +169 -0
  213. PYME/Acquire/Scripts/init_sim103.py +169 -0
  214. PYME/Acquire/Scripts/init_sim2.py +211 -0
  215. PYME/Acquire/Scripts/init_sim50.py +169 -0
  216. PYME/Acquire/Scripts/init_sim75.py +169 -0
  217. PYME/Acquire/Scripts/init_sim_drift_tracking.py +166 -0
  218. PYME/Acquire/Scripts/init_sim_htsms.py +224 -0
  219. PYME/Acquire/Scripts/init_sim_htsms_n.py +233 -0
  220. PYME/Acquire/Scripts/init_sim_main.py +229 -0
  221. PYME/Acquire/Scripts/init_sim_min.py +197 -0
  222. PYME/Acquire/Scripts/init_sim_rem.py +176 -0
  223. PYME/Acquire/Scripts/init_smi1.py +110 -0
  224. PYME/Acquire/Scripts/init_spectro.py +78 -0
  225. PYME/Acquire/Scripts/init_spim.py +116 -0
  226. PYME/Acquire/Scripts/init_thorcam.py +62 -0
  227. PYME/Acquire/Scripts/init_twophoton.py +112 -0
  228. PYME/Acquire/Scripts/init_uc480.py +262 -0
  229. PYME/Acquire/Scripts/init_ueye.py +261 -0
  230. PYME/Acquire/SpoolController.py +958 -0
  231. PYME/Acquire/Utils/MultiPointScanner.py +80 -0
  232. PYME/Acquire/Utils/__init__.py +13 -0
  233. PYME/Acquire/Utils/failsafe.py +157 -0
  234. PYME/Acquire/Utils/fastTiler.py +153 -0
  235. PYME/Acquire/Utils/pointScanner.py +496 -0
  236. PYME/Acquire/Utils/sarcSpacing.py +86 -0
  237. PYME/Acquire/Utils/strip_tiler.py +93 -0
  238. PYME/Acquire/Utils/tiler.py +464 -0
  239. PYME/Acquire/Utils/vibrationAnalysis.py +65 -0
  240. PYME/Acquire/__init__.py +44 -0
  241. PYME/Acquire/acquire_client.py +90 -0
  242. PYME/Acquire/acquire_server.py +316 -0
  243. PYME/Acquire/acquirebase.py +139 -0
  244. PYME/Acquire/acquiremainframe.py +716 -0
  245. PYME/Acquire/acquirewx.py +61 -0
  246. PYME/Acquire/acquisition_base.py +148 -0
  247. PYME/Acquire/actions.py +209 -0
  248. PYME/Acquire/autofocus.py +54 -0
  249. PYME/Acquire/eventLog.py +95 -0
  250. PYME/Acquire/event_loop.py +119 -0
  251. PYME/Acquire/frameWrangler.py +530 -0
  252. PYME/Acquire/htsms/__init__.py +8 -0
  253. PYME/Acquire/htsms/rule_ui.py +970 -0
  254. PYME/Acquire/htsms/rule_ui_v2.py +573 -0
  255. PYME/Acquire/htsms/tweeter.py +222 -0
  256. PYME/Acquire/microscope.py +1050 -0
  257. PYME/Acquire/positionTracker.py +181 -0
  258. PYME/Acquire/protocol.py +299 -0
  259. PYME/Acquire/protocol_acquisition.py +404 -0
  260. PYME/Acquire/sampleInformation.py +591 -0
  261. PYME/Acquire/sampleInformationDjangoDirect.py +446 -0
  262. PYME/Acquire/setup.py +52 -0
  263. PYME/Acquire/stackSettings.py +295 -0
  264. PYME/Acquire/stage_leveling.py +285 -0
  265. PYME/Acquire/ui/AnalysisSettingsUI.py +184 -0
  266. PYME/Acquire/ui/HDFSpoolFrame.py +584 -0
  267. PYME/Acquire/ui/__init__.py +0 -0
  268. PYME/Acquire/ui/actionUI.py +892 -0
  269. PYME/Acquire/ui/focus_lock_gui.py +149 -0
  270. PYME/Acquire/ui/intensity_trace.py +143 -0
  271. PYME/Acquire/ui/intsliders.py +222 -0
  272. PYME/Acquire/ui/lasersliders.py +329 -0
  273. PYME/Acquire/ui/mpd_picosecond_delay_panel.py +69 -0
  274. PYME/Acquire/ui/multiview_select.py +77 -0
  275. PYME/Acquire/ui/positionUI.py +519 -0
  276. PYME/Acquire/ui/preflight.py +58 -0
  277. PYME/Acquire/ui/scanner_panel.py +41 -0
  278. PYME/Acquire/ui/selectCameraPanel.py +46 -0
  279. PYME/Acquire/ui/seqdialog.py +501 -0
  280. PYME/Acquire/ui/splashScreen.py +175 -0
  281. PYME/Acquire/ui/spool_panel.py +830 -0
  282. PYME/Acquire/ui/tile_panel.py +556 -0
  283. PYME/Acquire/ui/tilesettingsui.py +91 -0
  284. PYME/Acquire/ui/voxelSizeDialog.py +149 -0
  285. PYME/Acquire/webui/__init__.py +36 -0
  286. PYME/Acquire/webui/ipy.py +361 -0
  287. PYME/Acquire/webui/static/css/pyme-bootstrap.css +9908 -0
  288. PYME/Acquire/webui/static/css/pymeacquire.css +85 -0
  289. PYME/Acquire/webui/static/js/pymeacquire.js +375 -0
  290. PYME/Acquire/webui/static/js/pzf.js +95 -0
  291. PYME/Acquire/webui/templates/PYMEAcquire.html +232 -0
  292. PYME/Acquire/webui/templates/login.html +84 -0
  293. PYME/Acquire/webui/templates/xtermpage.html +106 -0
  294. PYME/Acquire/xyztc.py +418 -0
  295. PYME/Acquire/zScanner.py +287 -0
  296. PYME/Analysis/BleachProfile/__init__.py +26 -0
  297. PYME/Analysis/BleachProfile/deMod.py +91 -0
  298. PYME/Analysis/BleachProfile/fitRecover.py +90 -0
  299. PYME/Analysis/BleachProfile/intensProf.py +519 -0
  300. PYME/Analysis/BleachProfile/kinModels.py +507 -0
  301. PYME/Analysis/BleachProfile/rawIntensity.py +227 -0
  302. PYME/Analysis/BleachProfile/rollins.py +555 -0
  303. PYME/Analysis/Colocalisation/__init__.py +27 -0
  304. PYME/Analysis/Colocalisation/colocScaleDep.py +126 -0
  305. PYME/Analysis/Colocalisation/correlationCoeffs.py +198 -0
  306. PYME/Analysis/Colocalisation/distColoc.py +110 -0
  307. PYME/Analysis/Colocalisation/edtColoc.py +191 -0
  308. PYME/Analysis/MetaData.py +278 -0
  309. PYME/Analysis/PSFEst/__init__.py +27 -0
  310. PYME/Analysis/PSFEst/dec.py +283 -0
  311. PYME/Analysis/PSFEst/extractImages.py +316 -0
  312. PYME/Analysis/PSFEst/fit_psf_zernikes.py +194 -0
  313. PYME/Analysis/PSFEst/psfQuality.py +225 -0
  314. PYME/Analysis/PSFGen/__init__.py +40 -0
  315. PYME/Analysis/PSFGen/fourier.py +138 -0
  316. PYME/Analysis/PSFGen/fourierHNA.py +1108 -0
  317. PYME/Analysis/PSFGen/fourierHNA2.py +979 -0
  318. PYME/Analysis/PSFGen/genCRBcurves.py +185 -0
  319. PYME/Analysis/PSFGen/nijboer_zernike.py +79 -0
  320. PYME/Analysis/PSFGen/ps_app.cp39-win_amd64.dll.a +0 -0
  321. PYME/Analysis/PSFGen/ps_app.cp39-win_amd64.pyd +0 -0
  322. PYME/Analysis/PSFGen/test_ps_app.py +24 -0
  323. PYME/Analysis/Tracking/__init__.py +3 -0
  324. PYME/Analysis/Tracking/msd.py +57 -0
  325. PYME/Analysis/Tracking/trackUtils.py +528 -0
  326. PYME/Analysis/Tracking/tracking.py +152 -0
  327. PYME/Analysis/__init__.py +30 -0
  328. PYME/Analysis/_fithelpers.py +161 -0
  329. PYME/Analysis/angleFilter.py +287 -0
  330. PYME/Analysis/annealThresh.py +157 -0
  331. PYME/Analysis/autoencoder_classifier.py +45 -0
  332. PYME/Analysis/binAvg.py +144 -0
  333. PYME/Analysis/composite.py +183 -0
  334. PYME/Analysis/cramerRao.py +259 -0
  335. PYME/Analysis/deTile.py +441 -0
  336. PYME/Analysis/density_filter.py +45 -0
  337. PYME/Analysis/distributed_pyramid.py +426 -0
  338. PYME/Analysis/fiducial_matching.py +220 -0
  339. PYME/Analysis/gen_sCMOS_maps.py +355 -0
  340. PYME/Analysis/graphing_filters.py +311 -0
  341. PYME/Analysis/implicitFilter.py +359 -0
  342. PYME/Analysis/keep_pairs.py +69 -0
  343. PYME/Analysis/optic_flow.py +108 -0
  344. PYME/Analysis/piecewise.py +36 -0
  345. PYME/Analysis/piecewiseMapping.py +304 -0
  346. PYME/Analysis/piezo_movement_correction.py +153 -0
  347. PYME/Analysis/points/DeClump/__init__.py +78 -0
  348. PYME/Analysis/points/DeClump/deClump.cp39-win_amd64.dll.a +0 -0
  349. PYME/Analysis/points/DeClump/deClump.cp39-win_amd64.pyd +0 -0
  350. PYME/Analysis/points/DeClump/deClumpGUI.py +87 -0
  351. PYME/Analysis/points/DeClump/pyDeClump.py +284 -0
  352. PYME/Analysis/points/DeClump/testClump.py +40 -0
  353. PYME/Analysis/points/DistHist/__init__.py +25 -0
  354. PYME/Analysis/points/DistHist/distHist.cp39-win_amd64.dll.a +0 -0
  355. PYME/Analysis/points/DistHist/distHist.cp39-win_amd64.pyd +0 -0
  356. PYME/Analysis/points/DistHist/distHistThreaded.py +71 -0
  357. PYME/Analysis/points/DistHist/setup.py +54 -0
  358. PYME/Analysis/points/EdgeDB/__init__.py +24 -0
  359. PYME/Analysis/points/EdgeDB/edgeDB.cp39-win_amd64.dll.a +0 -0
  360. PYME/Analysis/points/EdgeDB/edgeDB.cp39-win_amd64.pyd +0 -0
  361. PYME/Analysis/points/EdgeDB/edgeTest.py +66 -0
  362. PYME/Analysis/points/EdgeDB/edges.py +102 -0
  363. PYME/Analysis/points/EdgeDB/segment.py +75 -0
  364. PYME/Analysis/points/EdgeDB/setup.py +54 -0
  365. PYME/Analysis/points/QuadTree/QTrend.py +232 -0
  366. PYME/Analysis/points/QuadTree/__init__.py +25 -0
  367. PYME/Analysis/points/QuadTree/pointQT.py +227 -0
  368. PYME/Analysis/points/QuadTree/pointQTF.py +254 -0
  369. PYME/Analysis/points/QuadTree/qtUtils.py +36 -0
  370. PYME/Analysis/points/QuadTree/rendQT.py +118 -0
  371. PYME/Analysis/points/SoftRend/__init__.py +24 -0
  372. PYME/Analysis/points/SoftRend/setup.py +61 -0
  373. PYME/Analysis/points/SoftRend/testTetrahedra.py +41 -0
  374. PYME/Analysis/points/SoftRend/triRend.cp39-win_amd64.dll.a +0 -0
  375. PYME/Analysis/points/SoftRend/triRend.cp39-win_amd64.pyd +0 -0
  376. PYME/Analysis/points/__init__.py +3 -0
  377. PYME/Analysis/points/arcfit/__init__.py +1 -0
  378. PYME/Analysis/points/arcfit/arcmf.cp39-win_amd64.dll.a +0 -0
  379. PYME/Analysis/points/arcfit/arcmf.cp39-win_amd64.pyd +0 -0
  380. PYME/Analysis/points/arcfit/setup.py +51 -0
  381. PYME/Analysis/points/astigmatism/__init__.py +0 -0
  382. PYME/Analysis/points/astigmatism/astigTools.py +338 -0
  383. PYME/Analysis/points/astigmatism/astiglookup.cp39-win_amd64.dll.a +0 -0
  384. PYME/Analysis/points/astigmatism/astiglookup.cp39-win_amd64.pyd +0 -0
  385. PYME/Analysis/points/astigmatism/setup.py +54 -0
  386. PYME/Analysis/points/cluster_morphology.py +199 -0
  387. PYME/Analysis/points/coordinate_tools.py +278 -0
  388. PYME/Analysis/points/features/__init__.py +0 -0
  389. PYME/Analysis/points/features/metal.py +46 -0
  390. PYME/Analysis/points/features/shaders/vect_feat_3d.metal +49 -0
  391. PYME/Analysis/points/fiducials.py +142 -0
  392. PYME/Analysis/points/gen3DTriangs.py +796 -0
  393. PYME/Analysis/points/moments.py +143 -0
  394. PYME/Analysis/points/multiview.py +458 -0
  395. PYME/Analysis/points/objectMeasure.py +206 -0
  396. PYME/Analysis/points/qpaint.py +97 -0
  397. PYME/Analysis/points/rings.py +372 -0
  398. PYME/Analysis/points/ripleys.py +427 -0
  399. PYME/Analysis/points/segmentLL.py +134 -0
  400. PYME/Analysis/points/setup.py +56 -0
  401. PYME/Analysis/points/spherical_harmonics.py +909 -0
  402. PYME/Analysis/points/surfit.py +829 -0
  403. PYME/Analysis/points/tCluster.py +75 -0
  404. PYME/Analysis/points/traveling_salesperson/__init__.py +0 -0
  405. PYME/Analysis/points/traveling_salesperson/queue_opt.py +117 -0
  406. PYME/Analysis/points/traveling_salesperson/sectioned_two_opt.py +327 -0
  407. PYME/Analysis/points/traveling_salesperson/setup.py +40 -0
  408. PYME/Analysis/points/traveling_salesperson/sort.py +78 -0
  409. PYME/Analysis/points/traveling_salesperson/two_opt.py +189 -0
  410. PYME/Analysis/points/traveling_salesperson/two_opt_utils.cp39-win_amd64.dll.a +0 -0
  411. PYME/Analysis/points/traveling_salesperson/two_opt_utils.cp39-win_amd64.pyd +0 -0
  412. PYME/Analysis/points/traveling_salesperson/two_opt_utils.pyx +41 -0
  413. PYME/Analysis/points/twoColour.py +572 -0
  414. PYME/Analysis/points/twoColourPlot.py +107 -0
  415. PYME/Analysis/processLogger.py +129 -0
  416. PYME/Analysis/profile_extraction.py +72 -0
  417. PYME/Analysis/setup.py +60 -0
  418. PYME/Analysis/splitting.py +372 -0
  419. PYME/Analysis/svmSegment.py +214 -0
  420. PYME/Analysis/thresholding.py +192 -0
  421. PYME/Analysis/tile_pyramid.py +938 -0
  422. PYME/Analysis/widefieldify.py +54 -0
  423. PYME/DSView/DisplayOptionsPanel.py +503 -0
  424. PYME/DSView/LUT/__init__.py +33 -0
  425. PYME/DSView/LUT/lut.cp39-win_amd64.dll.a +0 -0
  426. PYME/DSView/LUT/lut.cp39-win_amd64.pyd +0 -0
  427. PYME/DSView/LUT/setup.py +54 -0
  428. PYME/DSView/OpenSequenceDialog.py +83 -0
  429. PYME/DSView/OverlaysPanel.py +111 -0
  430. PYME/DSView/__init__.py +32 -0
  431. PYME/DSView/arrayViewPanel.py +1035 -0
  432. PYME/DSView/displayOptions.py +453 -0
  433. PYME/DSView/displaySettingsPanel.py +271 -0
  434. PYME/DSView/dsviewer.py +542 -0
  435. PYME/DSView/eventLogViewer.py +805 -0
  436. PYME/DSView/fitInfo.py +486 -0
  437. PYME/DSView/htmlServe.py +72 -0
  438. PYME/DSView/logparser.py +100 -0
  439. PYME/DSView/modules/LMAnalysis.py +1053 -0
  440. PYME/DSView/modules/LMDisplay.py +194 -0
  441. PYME/DSView/modules/__init__.py +162 -0
  442. PYME/DSView/modules/_base.py +39 -0
  443. PYME/DSView/modules/annotation.py +725 -0
  444. PYME/DSView/modules/arrayView.py +71 -0
  445. PYME/DSView/modules/blobFinding.py +455 -0
  446. PYME/DSView/modules/blobMeasure.py +1093 -0
  447. PYME/DSView/modules/camera_maps.py +38 -0
  448. PYME/DSView/modules/coloc.py +414 -0
  449. PYME/DSView/modules/composite.py +715 -0
  450. PYME/DSView/modules/convnet.py +24 -0
  451. PYME/DSView/modules/cropping.py +133 -0
  452. PYME/DSView/modules/dark_and_flatfield.py +31 -0
  453. PYME/DSView/modules/deconvolution.py +499 -0
  454. PYME/DSView/modules/eventView.py +80 -0
  455. PYME/DSView/modules/extractBlobs.py +46 -0
  456. PYME/DSView/modules/fastGraphPanel.py +85 -0
  457. PYME/DSView/modules/filtering.py +335 -0
  458. PYME/DSView/modules/flowView.py +268 -0
  459. PYME/DSView/modules/fourierRingCorrelation.py +172 -0
  460. PYME/DSView/modules/gl_image_view.py +12 -0
  461. PYME/DSView/modules/graphViewPanel.py +272 -0
  462. PYME/DSView/modules/measure/__init__.py +1 -0
  463. PYME/DSView/modules/measure/filterPan.py +239 -0
  464. PYME/DSView/modules/metadataView.py +28 -0
  465. PYME/DSView/modules/multithreshold.py +98 -0
  466. PYME/DSView/modules/neuroglancer.py +129 -0
  467. PYME/DSView/modules/object_tagging.py +157 -0
  468. PYME/DSView/modules/particleTracking.py +463 -0
  469. PYME/DSView/modules/playback.py +256 -0
  470. PYME/DSView/modules/prebleachExtraction.py +75 -0
  471. PYME/DSView/modules/profileFitting.py +137 -0
  472. PYME/DSView/modules/profilePlotting.py +408 -0
  473. PYME/DSView/modules/projections.py +48 -0
  474. PYME/DSView/modules/psfExtraction.py +570 -0
  475. PYME/DSView/modules/psfGeneration.py +138 -0
  476. PYME/DSView/modules/psfTools.py +661 -0
  477. PYME/DSView/modules/pupilTools.py +204 -0
  478. PYME/DSView/modules/pupilView.py +139 -0
  479. PYME/DSView/modules/recipes.py +266 -0
  480. PYME/DSView/modules/redimension.py +75 -0
  481. PYME/DSView/modules/segmentation.py +192 -0
  482. PYME/DSView/modules/sheetView.py +24 -0
  483. PYME/DSView/modules/shell.py +39 -0
  484. PYME/DSView/modules/shiftfield_estimation.py +433 -0
  485. PYME/DSView/modules/sparkprepare.py +94 -0
  486. PYME/DSView/modules/splitter.py +148 -0
  487. PYME/DSView/modules/synchronise.py +64 -0
  488. PYME/DSView/modules/templates/astigCal.html +42 -0
  489. PYME/DSView/modules/templates/measureView.html +69 -0
  490. PYME/DSView/modules/templates/measureView2.html +48 -0
  491. PYME/DSView/modules/templates/measureView3.html +98 -0
  492. PYME/DSView/modules/templates/trackView.html +139 -0
  493. PYME/DSView/modules/templates/tracksOverview.html +21 -0
  494. PYME/DSView/modules/tiling.py +147 -0
  495. PYME/DSView/modules/unmixing.py +148 -0
  496. PYME/DSView/modules/vis3D.py +320 -0
  497. PYME/DSView/modules/visgui.py +163 -0
  498. PYME/DSView/modules/webview.py +14 -0
  499. PYME/DSView/overlays.py +295 -0
  500. PYME/DSView/scrolledImagePanel.py +188 -0
  501. PYME/DSView/setup.py +53 -0
  502. PYME/DSView/splashScreen.py +158 -0
  503. PYME/DSView/voxSizeDialog.py +84 -0
  504. PYME/Deconv/__init__.py +35 -0
  505. PYME/Deconv/beadGen.py +55 -0
  506. PYME/Deconv/dec.py +659 -0
  507. PYME/Deconv/decTask.py +65 -0
  508. PYME/Deconv/decThread.py +113 -0
  509. PYME/Deconv/dec_test.py +47 -0
  510. PYME/Deconv/deconvDialogs.py +417 -0
  511. PYME/Deconv/fftwWisdom.py +62 -0
  512. PYME/Deconv/richardsonLucy.py +141 -0
  513. PYME/Deconv/richardsonLucyMV.py +151 -0
  514. PYME/Deconv/richardsonLucyMVM.py +218 -0
  515. PYME/Deconv/test_block_dec.py +46 -0
  516. PYME/Deconv/tq_block_dec.py +331 -0
  517. PYME/Deconv/wiener.py +163 -0
  518. PYME/IO/DataSources/AlignDataSource.py +91 -0
  519. PYME/IO/DataSources/ArrayDataSource.py +260 -0
  520. PYME/IO/DataSources/BGSDataSource.py +353 -0
  521. PYME/IO/DataSources/BaseDataSource.py +457 -0
  522. PYME/IO/DataSources/BioformatsDataSource.py +178 -0
  523. PYME/IO/DataSources/BufferedDataSource.py +99 -0
  524. PYME/IO/DataSources/ClusterPZFDataSource.py +124 -0
  525. PYME/IO/DataSources/ConcatenatedDataSource.py +56 -0
  526. PYME/IO/DataSources/CropDataSource.py +243 -0
  527. PYME/IO/DataSources/DcimgDataSource.py +54 -0
  528. PYME/IO/DataSources/ElectronsPerSecondDataSource.py +61 -0
  529. PYME/IO/DataSources/FlatFieldDataSource.py +77 -0
  530. PYME/IO/DataSources/HDFDataSource.py +160 -0
  531. PYME/IO/DataSources/HTTPDataSource.py +80 -0
  532. PYME/IO/DataSources/ImageSeriesDataSource.py +123 -0
  533. PYME/IO/DataSources/MultiviewDataSource.py +99 -0
  534. PYME/IO/DataSources/OBFDataSource.py +67 -0
  535. PYME/IO/DataSources/RandomDataSource.py +23 -0
  536. PYME/IO/DataSources/SupertileDatasource.py +148 -0
  537. PYME/IO/DataSources/TQDataSource.py +41 -0
  538. PYME/IO/DataSources/TiffDataSource.py +193 -0
  539. PYME/IO/DataSources/TileDataSource.py +91 -0
  540. PYME/IO/DataSources/UnsplitDataSource.py +92 -0
  541. PYME/IO/DataSources/__init__.py +59 -0
  542. PYME/IO/FileUtils/IJLut.py +43 -0
  543. PYME/IO/FileUtils/RawToHdf5.py +105 -0
  544. PYME/IO/FileUtils/SPAD.py +54 -0
  545. PYME/IO/FileUtils/TiffSeqToHdf5.py +87 -0
  546. PYME/IO/FileUtils/__init__.py +24 -0
  547. PYME/IO/FileUtils/compHdf.py +39 -0
  548. PYME/IO/FileUtils/fileID.py +246 -0
  549. PYME/IO/FileUtils/fnlGen.py +40 -0
  550. PYME/IO/FileUtils/freeSpace.py +59 -0
  551. PYME/IO/FileUtils/h5-thumbnailer.py +85 -0
  552. PYME/IO/FileUtils/h5ExFrames.py +98 -0
  553. PYME/IO/FileUtils/h5ToTiffSeries.py +50 -0
  554. PYME/IO/FileUtils/h5r-thumbnailer.py +118 -0
  555. PYME/IO/FileUtils/h5rFormatDef.py +99 -0
  556. PYME/IO/FileUtils/h5rToTxt.py +77 -0
  557. PYME/IO/FileUtils/nameUtils.py +300 -0
  558. PYME/IO/FileUtils/obf_support.py +538 -0
  559. PYME/IO/FileUtils/ply.py +147 -0
  560. PYME/IO/FileUtils/readSpeckle.py +78 -0
  561. PYME/IO/FileUtils/readTiff.py +64 -0
  562. PYME/IO/FileUtils/saveTiffStack.py +137 -0
  563. PYME/IO/FileUtils/setup.py +36 -0
  564. PYME/IO/FileUtils/sf-thumbnailer.py +76 -0
  565. PYME/IO/FileUtils/stl.py +44 -0
  566. PYME/IO/FileUtils/thumbnailDatabase.py +104 -0
  567. PYME/IO/HTTPSpooler_v2.py +141 -0
  568. PYME/IO/MetaDataHandler.py +1072 -0
  569. PYME/IO/PZFFormat.py +375 -0
  570. PYME/IO/Spooler.py +279 -0
  571. PYME/IO/__init__.py +12 -0
  572. PYME/IO/acquisition_backends.py +353 -0
  573. PYME/IO/buffer_helpers.cp39-win_amd64.dll.a +0 -0
  574. PYME/IO/buffer_helpers.cp39-win_amd64.pyd +0 -0
  575. PYME/IO/buffer_helpers.pyx +190 -0
  576. PYME/IO/buffers.py +291 -0
  577. PYME/IO/clusterDuplication.py +204 -0
  578. PYME/IO/clusterExport.py +152 -0
  579. PYME/IO/clusterGlob.py +108 -0
  580. PYME/IO/clusterIO.py +1076 -0
  581. PYME/IO/clusterListing.py +232 -0
  582. PYME/IO/clusterResults.py +206 -0
  583. PYME/IO/clusterUpload.py +49 -0
  584. PYME/IO/cluster_directory.py +419 -0
  585. PYME/IO/cluster_streaming.py +216 -0
  586. PYME/IO/compatibility.py +12 -0
  587. PYME/IO/countdir/__init__.py +5 -0
  588. PYME/IO/countdir/setup.py +58 -0
  589. PYME/IO/csv_flavours.py +251 -0
  590. PYME/IO/dataExporter.py +470 -0
  591. PYME/IO/dataWrap.py +108 -0
  592. PYME/IO/dcimg.py +167 -0
  593. PYME/IO/events.py +161 -0
  594. PYME/IO/h5File.py +148 -0
  595. PYME/IO/h5rFile.py +323 -0
  596. PYME/IO/image.py +1447 -0
  597. PYME/IO/load_psf.py +26 -0
  598. PYME/IO/pyme_h5ls.py +58 -0
  599. PYME/IO/ragged.py +138 -0
  600. PYME/IO/rgb_image.py +94 -0
  601. PYME/IO/tabular.py +1338 -0
  602. PYME/IO/testClusterSpooling.py +199 -0
  603. PYME/IO/unifiedIO.py +301 -0
  604. PYME/LMVis/Extras/QPobjectSegment.py +220 -0
  605. PYME/LMVis/Extras/__init__.py +55 -0
  606. PYME/LMVis/Extras/animation.py +359 -0
  607. PYME/LMVis/Extras/annotation.py +78 -0
  608. PYME/LMVis/Extras/clusterAnalysis.py +460 -0
  609. PYME/LMVis/Extras/dockedPanel.py +69 -0
  610. PYME/LMVis/Extras/extra_layers.py +373 -0
  611. PYME/LMVis/Extras/fiducial.py +230 -0
  612. PYME/LMVis/Extras/flagZMotionArtifacts.py +60 -0
  613. PYME/LMVis/Extras/fusionUtils.py +9 -0
  614. PYME/LMVis/Extras/multiColourAnalysis.py +103 -0
  615. PYME/LMVis/Extras/multiviewMapping.py +489 -0
  616. PYME/LMVis/Extras/objectMeasurements.py +246 -0
  617. PYME/LMVis/Extras/particleTracking.py +272 -0
  618. PYME/LMVis/Extras/perFrameVariable.py +77 -0
  619. PYME/LMVis/Extras/photophysics.py +72 -0
  620. PYME/LMVis/Extras/pointSetGeneration.py +232 -0
  621. PYME/LMVis/Extras/pointwiseColoc.py +54 -0
  622. PYME/LMVis/Extras/saved_views.py +125 -0
  623. PYME/LMVis/Extras/scaleBox.py +115 -0
  624. PYME/LMVis/Extras/sequentialSpecies.py +125 -0
  625. PYME/LMVis/Extras/setBGImage.py +74 -0
  626. PYME/LMVis/Extras/shiftmapGenerator.py +138 -0
  627. PYME/LMVis/Extras/showShortcuts.py +69 -0
  628. PYME/LMVis/Extras/snapshot.py +135 -0
  629. PYME/LMVis/Extras/spherical_harmonics.py +98 -0
  630. PYME/LMVis/Extras/subsets.py +15 -0
  631. PYME/LMVis/Extras/surface_fitting.py +52 -0
  632. PYME/LMVis/Extras/temperaturePlot.py +53 -0
  633. PYME/LMVis/Extras/vibration.py +112 -0
  634. PYME/LMVis/VisGUI.py +506 -0
  635. PYME/LMVis/__init__.py +39 -0
  636. PYME/LMVis/colourFilterGUI.py +77 -0
  637. PYME/LMVis/colourPanel.py +491 -0
  638. PYME/LMVis/displayPane.py +462 -0
  639. PYME/LMVis/dyeRatios.py +116 -0
  640. PYME/LMVis/genImageDialog.py +298 -0
  641. PYME/LMVis/gl_offScreenHandler.py +134 -0
  642. PYME/LMVis/gl_render.py +1302 -0
  643. PYME/LMVis/gl_render3D.py +1453 -0
  644. PYME/LMVis/gl_render3D_shaders.py +1213 -0
  645. PYME/LMVis/glcanvas_core.py +1157 -0
  646. PYME/LMVis/imageView.py +752 -0
  647. PYME/LMVis/imageView2.py +372 -0
  648. PYME/LMVis/importTextDialog.py +342 -0
  649. PYME/LMVis/layer_panel.py +231 -0
  650. PYME/LMVis/layers/AxesOverlayLayer.py +126 -0
  651. PYME/LMVis/layers/LUTOverlayLayer.py +346 -0
  652. PYME/LMVis/layers/OverlayLayer.py +46 -0
  653. PYME/LMVis/layers/ScaleBarOverlayLayer.py +97 -0
  654. PYME/LMVis/layers/ScaleBoxOverlayLayer.py +181 -0
  655. PYME/LMVis/layers/SelectionOverlayLayer.py +124 -0
  656. PYME/LMVis/layers/__init__.py +53 -0
  657. PYME/LMVis/layers/base.py +319 -0
  658. PYME/LMVis/layers/image_layer.py +363 -0
  659. PYME/LMVis/layers/labels.py +186 -0
  660. PYME/LMVis/layers/layer_defaults.py +97 -0
  661. PYME/LMVis/layers/mesh.py +378 -0
  662. PYME/LMVis/layers/octree.py +194 -0
  663. PYME/LMVis/layers/pointcloud.py +372 -0
  664. PYME/LMVis/layers/quiver.py +271 -0
  665. PYME/LMVis/layers/text.py +250 -0
  666. PYME/LMVis/layers/tracks.py +309 -0
  667. PYME/LMVis/mv_math.py +78 -0
  668. PYME/LMVis/objectDarkMeasure.py +120 -0
  669. PYME/LMVis/pipeline.py +1227 -0
  670. PYME/LMVis/pipelineView.py +115 -0
  671. PYME/LMVis/pointSettingsPanel.py +135 -0
  672. PYME/LMVis/progGraph.py +59 -0
  673. PYME/LMVis/quadTreeSettings.py +90 -0
  674. PYME/LMVis/rendGauss.py +158 -0
  675. PYME/LMVis/renderers.py +576 -0
  676. PYME/LMVis/sessionpaths.py +136 -0
  677. PYME/LMVis/setup.py +39 -0
  678. PYME/LMVis/setup_exe.py +71 -0
  679. PYME/LMVis/shader_programs/DefaultShaderProgram.py +149 -0
  680. PYME/LMVis/shader_programs/GLProgram.py +119 -0
  681. PYME/LMVis/shader_programs/GouraudShaderProgram.py +157 -0
  682. PYME/LMVis/shader_programs/PointSpriteShaderProgram.py +163 -0
  683. PYME/LMVis/shader_programs/ShaderProgramFactory.py +54 -0
  684. PYME/LMVis/shader_programs/TesselShaderProgram.py +47 -0
  685. PYME/LMVis/shader_programs/WireFrameShaderProgram.py +50 -0
  686. PYME/LMVis/shader_programs/__init__.py +20 -0
  687. PYME/LMVis/shader_programs/oit_compositor.py +23 -0
  688. PYME/LMVis/shader_programs/shader_loader.py +84 -0
  689. PYME/LMVis/shader_programs/shader_program.py +168 -0
  690. PYME/LMVis/shader_programs/shaders/bigflatpoints_fs.glsl +16 -0
  691. PYME/LMVis/shader_programs/shaders/bigpoints_gs.glsl +40 -0
  692. PYME/LMVis/shader_programs/shaders/bigpointsprites_fs.glsl +20 -0
  693. PYME/LMVis/shader_programs/shaders/bigspheres_fs.glsl +53 -0
  694. PYME/LMVis/shader_programs/shaders/compose_fs.glsl +41 -0
  695. PYME/LMVis/shader_programs/shaders/compose_vs.glsl +25 -0
  696. PYME/LMVis/shader_programs/shaders/default_fs.glsl +27 -0
  697. PYME/LMVis/shader_programs/shaders/default_vs.glsl +88 -0
  698. PYME/LMVis/shader_programs/shaders/flatpoints_fs.glsl +27 -0
  699. PYME/LMVis/shader_programs/shaders/gouraud_flatpoints_fs.glsl +30 -0
  700. PYME/LMVis/shader_programs/shaders/gouraud_fs.glsl +26 -0
  701. PYME/LMVis/shader_programs/shaders/gouraud_oit_fs.glsl +68 -0
  702. PYME/LMVis/shader_programs/shaders/gouraud_vs.glsl +135 -0
  703. PYME/LMVis/shader_programs/shaders/image_fs.glsl +60 -0
  704. PYME/LMVis/shader_programs/shaders/image_vs.glsl +79 -0
  705. PYME/LMVis/shader_programs/shaders/phong_fs.glsl +43 -0
  706. PYME/LMVis/shader_programs/shaders/phong_vs.glsl +45 -0
  707. PYME/LMVis/shader_programs/shaders/pointsprites_fs.glsl +33 -0
  708. PYME/LMVis/shader_programs/shaders/pointsprites_vs.glsl +77 -0
  709. PYME/LMVis/shader_programs/shaders/spheres_fs.glsl +102 -0
  710. PYME/LMVis/shader_programs/shaders/tessel_fs.glsl +23 -0
  711. PYME/LMVis/shader_programs/shaders/tessel_vs.glsl +37 -0
  712. PYME/LMVis/shader_programs/shaders/text_fs.glsl +42 -0
  713. PYME/LMVis/shader_programs/shaders/text_vs.glsl +84 -0
  714. PYME/LMVis/shader_programs/shaders/widelines_fs.glsl +19 -0
  715. PYME/LMVis/shader_programs/shaders/widelines_gs.glsl +56 -0
  716. PYME/LMVis/shader_programs/shaders/widelines_vs.glsl +51 -0
  717. PYME/LMVis/statusLog.py +57 -0
  718. PYME/LMVis/triBlobs.py +142 -0
  719. PYME/LMVis/view_clipping_pane.py +429 -0
  720. PYME/LMVis/views.py +275 -0
  721. PYME/LMVis/visCore.py +1005 -0
  722. PYME/LMVis/visHelpers.py +880 -0
  723. PYME/LMVis/visHelpersMin.py +230 -0
  724. PYME/LMVis/workspaceTree.py +238 -0
  725. PYME/__init__.py +22 -0
  726. PYME/cluster/HTTPDataServer.py +1063 -0
  727. PYME/cluster/HTTPRulePusher.py +382 -0
  728. PYME/cluster/HTTPTaskPusher.py +377 -0
  729. PYME/cluster/PYMERuleNodeServer.py +192 -0
  730. PYME/cluster/PYMERuleServer.py +109 -0
  731. PYME/cluster/__init__.py +0 -0
  732. PYME/cluster/clusterUI/accounts/__init__.py +0 -0
  733. PYME/cluster/clusterUI/accounts/admin.py +85 -0
  734. PYME/cluster/clusterUI/accounts/forms.py +8 -0
  735. PYME/cluster/clusterUI/accounts/migrations/0001_initial.py +31 -0
  736. PYME/cluster/clusterUI/accounts/migrations/__init__.py +0 -0
  737. PYME/cluster/clusterUI/accounts/models.py +44 -0
  738. PYME/cluster/clusterUI/accounts/templates/accounts/user_form.html +13 -0
  739. PYME/cluster/clusterUI/accounts/templates/registration/initial_complete.html +6 -0
  740. PYME/cluster/clusterUI/accounts/templates/registration/initial_confirm.html +13 -0
  741. PYME/cluster/clusterUI/accounts/templates/registration/initial_done.html +7 -0
  742. PYME/cluster/clusterUI/accounts/templates/registration/verification.html +2 -0
  743. PYME/cluster/clusterUI/accounts/templates/registration/verification_subject.txt +1 -0
  744. PYME/cluster/clusterUI/accounts/urls.py +19 -0
  745. PYME/cluster/clusterUI/accounts/views.py +36 -0
  746. PYME/cluster/clusterUI/clusterUI/__init__.py +0 -0
  747. PYME/cluster/clusterUI/clusterUI/settings.py +147 -0
  748. PYME/cluster/clusterUI/clusterUI/urls.py +31 -0
  749. PYME/cluster/clusterUI/clusterUI/wsgi.py +16 -0
  750. PYME/cluster/clusterUI/clusterbrowser/__init__.py +0 -0
  751. PYME/cluster/clusterUI/clusterbrowser/admin.py +3 -0
  752. PYME/cluster/clusterUI/clusterbrowser/apps.py +7 -0
  753. PYME/cluster/clusterUI/clusterbrowser/migrations/__init__.py +0 -0
  754. PYME/cluster/clusterUI/clusterbrowser/models.py +5 -0
  755. PYME/cluster/clusterUI/clusterbrowser/templates/base.html +129 -0
  756. PYME/cluster/clusterUI/clusterbrowser/templates/clusterbrowser/dirlisting.html +231 -0
  757. PYME/cluster/clusterUI/clusterbrowser/templates/clusterbrowser/form_new_folder.html +18 -0
  758. PYME/cluster/clusterUI/clusterbrowser/templates/clusterbrowser/form_upload_file.html +17 -0
  759. PYME/cluster/clusterUI/clusterbrowser/templates/clusterbrowser/lightlisting.html +25 -0
  760. PYME/cluster/clusterUI/clusterbrowser/templates/registration/login.html +38 -0
  761. PYME/cluster/clusterUI/clusterbrowser/urls.py +10 -0
  762. PYME/cluster/clusterUI/clusterbrowser/views.py +148 -0
  763. PYME/cluster/clusterUI/clusterstatus/__init__.py +0 -0
  764. PYME/cluster/clusterUI/clusterstatus/admin.py +3 -0
  765. PYME/cluster/clusterUI/clusterstatus/apps.py +7 -0
  766. PYME/cluster/clusterUI/clusterstatus/migrations/__init__.py +0 -0
  767. PYME/cluster/clusterUI/clusterstatus/models.py +5 -0
  768. PYME/cluster/clusterUI/clusterstatus/templates/clusterstatus/load_content.html +29 -0
  769. PYME/cluster/clusterUI/clusterstatus/templates/clusterstatus/queue_info.html +28 -0
  770. PYME/cluster/clusterUI/clusterstatus/templates/clusterstatus/queue_info_content.html +50 -0
  771. PYME/cluster/clusterUI/clusterstatus/templates/clusterstatus/status_dash.html +29 -0
  772. PYME/cluster/clusterUI/clusterstatus/templates/clusterstatus/status_dash_content.html +90 -0
  773. PYME/cluster/clusterUI/clusterstatus/urls.py +9 -0
  774. PYME/cluster/clusterUI/clusterstatus/views.py +69 -0
  775. PYME/cluster/clusterUI/clusterui_tornado.py +117 -0
  776. PYME/cluster/clusterUI/deploy.py +62 -0
  777. PYME/cluster/clusterUI/localization/__init__.py +0 -0
  778. PYME/cluster/clusterUI/localization/admin.py +3 -0
  779. PYME/cluster/clusterUI/localization/apps.py +7 -0
  780. PYME/cluster/clusterUI/localization/migrations/__init__.py +0 -0
  781. PYME/cluster/clusterUI/localization/models.py +5 -0
  782. PYME/cluster/clusterUI/localization/templates/localization/settings_form.html +67 -0
  783. PYME/cluster/clusterUI/localization/urls.py +10 -0
  784. PYME/cluster/clusterUI/localization/views.py +170 -0
  785. PYME/cluster/clusterUI/manage.py +17 -0
  786. PYME/cluster/clusterUI/recipes/__init__.py +0 -0
  787. PYME/cluster/clusterUI/recipes/admin.py +3 -0
  788. PYME/cluster/clusterUI/recipes/migrations/__init__.py +0 -0
  789. PYME/cluster/clusterUI/recipes/models.py +3 -0
  790. PYME/cluster/clusterUI/recipes/templates/recipes/extra_inputs.html +35 -0
  791. PYME/cluster/clusterUI/recipes/templates/recipes/form_recipe.html +82 -0
  792. PYME/cluster/clusterUI/recipes/templates/recipes/input_list.html +7 -0
  793. PYME/cluster/clusterUI/recipes/templates/recipes/recipe_editor.html +28 -0
  794. PYME/cluster/clusterUI/recipes/templates/recipes/recipe_editor_common.html +46 -0
  795. PYME/cluster/clusterUI/recipes/templates/recipes/recipe_editrun.html +93 -0
  796. PYME/cluster/clusterUI/recipes/templates/recipes/recipe_standalone.html +100 -0
  797. PYME/cluster/clusterUI/recipes/templates/recipes/recipe_template.html +109 -0
  798. PYME/cluster/clusterUI/recipes/urls.py +17 -0
  799. PYME/cluster/clusterUI/recipes/views.py +109 -0
  800. PYME/cluster/cluster_of_one.py +200 -0
  801. PYME/cluster/dataserver_wsgi.py +615 -0
  802. PYME/cluster/distribution.py +71 -0
  803. PYME/cluster/rulenodeserver.py +487 -0
  804. PYME/cluster/rules.py +979 -0
  805. PYME/cluster/ruleserver.py +1106 -0
  806. PYME/cluster/status.py +101 -0
  807. PYME/cluster/taskWorkerHTTP.py +522 -0
  808. PYME/cluster/webdav.py +1102 -0
  809. PYME/config.py +659 -0
  810. PYME/contrib/TextCtrlAutoComplete.py +582 -0
  811. PYME/contrib/__init__.py +7 -0
  812. PYME/contrib/aniso/__init__.py +2 -0
  813. PYME/contrib/aniso/aniso.py +268 -0
  814. PYME/contrib/auiFloatBook.py +94 -0
  815. PYME/contrib/congrid.py +127 -0
  816. PYME/contrib/dispatch/__init__.py +19 -0
  817. PYME/contrib/dispatch/dispatcher.py +289 -0
  818. PYME/contrib/gohlke/__init__.py +1 -0
  819. PYME/contrib/gohlke/_tifffile.cp39-win_amd64.dll.a +0 -0
  820. PYME/contrib/gohlke/_tifffile.cp39-win_amd64.pyd +0 -0
  821. PYME/contrib/gohlke/czifile.py +1289 -0
  822. PYME/contrib/gohlke/fcsfiles.py +514 -0
  823. PYME/contrib/gohlke/setup.py +54 -0
  824. PYME/contrib/gohlke/tifffile.py +3095 -0
  825. PYME/contrib/listctrlMixins.py +896 -0
  826. PYME/contrib/lzw.py +838 -0
  827. PYME/contrib/pad/__init__.py +13 -0
  828. PYME/contrib/pad/info_pad.py +18 -0
  829. PYME/contrib/pad/pad.py +670 -0
  830. PYME/contrib/setup.py +38 -0
  831. PYME/contrib/wxPlotPanel.py +137 -0
  832. PYME/experimental/SimReconstruction.py +49 -0
  833. PYME/experimental/__init__.pxd +0 -0
  834. PYME/experimental/__init__.py +4 -0
  835. PYME/experimental/_octree.cp39-win_amd64.dll.a +0 -0
  836. PYME/experimental/_octree.cp39-win_amd64.pyd +0 -0
  837. PYME/experimental/_octree.pyx +477 -0
  838. PYME/experimental/_treap.cp39-win_amd64.dll.a +0 -0
  839. PYME/experimental/_treap.cp39-win_amd64.pyd +0 -0
  840. PYME/experimental/_treap.pyx +267 -0
  841. PYME/experimental/_triangle_mesh.cp39-win_amd64.dll.a +0 -0
  842. PYME/experimental/_triangle_mesh.cp39-win_amd64.pyd +0 -0
  843. PYME/experimental/_triangle_mesh.pxd +149 -0
  844. PYME/experimental/_triangle_mesh.pyx +2787 -0
  845. PYME/experimental/aqavg.py +40 -0
  846. PYME/experimental/dcimgFileChucker.py +375 -0
  847. PYME/experimental/dcimgSpoolShim.py +174 -0
  848. PYME/experimental/dcimg_chuck_test.py +78 -0
  849. PYME/experimental/dual_marching_cubes.py +801 -0
  850. PYME/experimental/dual_marching_cubes_v2.py +645 -0
  851. PYME/experimental/expectationMaximisation.py +298 -0
  852. PYME/experimental/func_octree.cp39-win_amd64.dll.a +0 -0
  853. PYME/experimental/func_octree.cp39-win_amd64.pyd +0 -0
  854. PYME/experimental/func_octree.pyx +381 -0
  855. PYME/experimental/fusionRadial.py +520 -0
  856. PYME/experimental/fusionUtils.py +522 -0
  857. PYME/experimental/isosurface.py +147 -0
  858. PYME/experimental/labview_spooling_hacks.py +48 -0
  859. PYME/experimental/marching_cubes.py +811 -0
  860. PYME/experimental/marching_tetrahedra.py +343 -0
  861. PYME/experimental/modified_marching_cubes.py +1245 -0
  862. PYME/experimental/music.py +25 -0
  863. PYME/experimental/octree.py +188 -0
  864. PYME/experimental/pSpaceMap.py +34 -0
  865. PYME/experimental/pointFitConfoc.py +112 -0
  866. PYME/experimental/scanner_control.py +107 -0
  867. PYME/experimental/setup.py +109 -0
  868. PYME/experimental/spark.py +31 -0
  869. PYME/experimental/tcHist.py +71 -0
  870. PYME/experimental/treap.py +262 -0
  871. PYME/experimental/triangle_mesh.py +2287 -0
  872. PYME/experimental/triangle_mesh_utils.cp39-win_amd64.dll.a +0 -0
  873. PYME/experimental/triangle_mesh_utils.cp39-win_amd64.pyd +0 -0
  874. PYME/experimental/triangle_utils.py +87 -0
  875. PYME/experimental/triangular_mesh.py +2255 -0
  876. PYME/experimental/zMotionArtifactUtils.py +55 -0
  877. PYME/localization/FitFactories/AstigGaussFitFR.py +175 -0
  878. PYME/localization/FitFactories/AstigGaussGPUFitFR.py +326 -0
  879. PYME/localization/FitFactories/BeadConvolvedAstigGaussFit.py +206 -0
  880. PYME/localization/FitFactories/ChecksumFR.py +82 -0
  881. PYME/localization/FitFactories/ChecksumGPUFR.py +213 -0
  882. PYME/localization/FitFactories/ChecksumMultifitSR.py +86 -0
  883. PYME/localization/FitFactories/ConfocCOIR.py +93 -0
  884. PYME/localization/FitFactories/Dumbell3DFitR.py +193 -0
  885. PYME/localization/FitFactories/DumbellFitR.py +163 -0
  886. PYME/localization/FitFactories/FFBase.py +403 -0
  887. PYME/localization/FitFactories/Gauss3DFitR.py +213 -0
  888. PYME/localization/FitFactories/GaussFitConstrR.py +177 -0
  889. PYME/localization/FitFactories/GaussMultifitLinesR.py +351 -0
  890. PYME/localization/FitFactories/GaussMultifitR.py +326 -0
  891. PYME/localization/FitFactories/GaussMultifitSR.py +373 -0
  892. PYME/localization/FitFactories/InterpFitR.py +236 -0
  893. PYME/localization/FitFactories/InterpFitWFR.py +111 -0
  894. PYME/localization/FitFactories/Interpolators/CSInterpolator.py +148 -0
  895. PYME/localization/FitFactories/Interpolators/CubeSInterpolatorP.py +121 -0
  896. PYME/localization/FitFactories/Interpolators/CubicSplineInterpolator.py +69 -0
  897. PYME/localization/FitFactories/Interpolators/LinearInterpolator.py +135 -0
  898. PYME/localization/FitFactories/Interpolators/LinearPInterpolatorP.py +95 -0
  899. PYME/localization/FitFactories/Interpolators/__init__.py +33 -0
  900. PYME/localization/FitFactories/Interpolators/baseInterpolator.py +202 -0
  901. PYME/localization/FitFactories/LatFitCOIR.py +82 -0
  902. PYME/localization/FitFactories/LatGaussFitFR.py +220 -0
  903. PYME/localization/FitFactories/LatObjFindFR.py +85 -0
  904. PYME/localization/FitFactories/LatPSFFitR.py +142 -0
  905. PYME/localization/FitFactories/MultiviewFitInterpNR.py +400 -0
  906. PYME/localization/FitFactories/PRInterpFitQR.py +192 -0
  907. PYME/localization/FitFactories/PRInterpFitR.py +223 -0
  908. PYME/localization/FitFactories/ROIExtractNR.py +93 -0
  909. PYME/localization/FitFactories/SpecGaussFitFR.py +174 -0
  910. PYME/localization/FitFactories/SplitterFitCOIR.py +142 -0
  911. PYME/localization/FitFactories/SplitterFitFNR.py +312 -0
  912. PYME/localization/FitFactories/SplitterFitFusionR.py +318 -0
  913. PYME/localization/FitFactories/SplitterFitInterpBNR.py +357 -0
  914. PYME/localization/FitFactories/SplitterFitInterpNR.py +343 -0
  915. PYME/localization/FitFactories/SplitterFitScavNR.py +352 -0
  916. PYME/localization/FitFactories/SplitterMultifitSR.py +410 -0
  917. PYME/localization/FitFactories/SplitterObjFindR.py +194 -0
  918. PYME/localization/FitFactories/SplitterROIExtractNR.py +91 -0
  919. PYME/localization/FitFactories/SplitterShiftEstFR.py +230 -0
  920. PYME/localization/FitFactories/__init__.py +103 -0
  921. PYME/localization/FitFactories/fitCommon.py +195 -0
  922. PYME/localization/FitFactories/zEstimators/__init__.py +29 -0
  923. PYME/localization/FitFactories/zEstimators/astigEstimator.py +173 -0
  924. PYME/localization/FitFactories/zEstimators/biplaneEstimator.py +209 -0
  925. PYME/localization/FitFactories/zEstimators/genericDualEstimator.py +232 -0
  926. PYME/localization/FitFactories/zEstimators/genericEstimator.py +224 -0
  927. PYME/localization/FitFactories/zEstimators/priEstimator.py +255 -0
  928. PYME/localization/MetaDataEdit.py +543 -0
  929. PYME/localization/Test/30_9_series_A_1.sf +0 -0
  930. PYME/localization/Test/PRI.md +45 -0
  931. PYME/localization/Test/PRI_qr.md +52 -0
  932. PYME/localization/Test/PRI_r.md +52 -0
  933. PYME/localization/Test/__init__.py +22 -0
  934. PYME/localization/Test/astig.md +50 -0
  935. PYME/localization/Test/astigCOI.md +50 -0
  936. PYME/localization/Test/astig_gauss.md +52 -0
  937. PYME/localization/Test/astig_pe.md +50 -0
  938. PYME/localization/Test/astig_splitter.md +66 -0
  939. PYME/localization/Test/astig_splitterCOI.md +67 -0
  940. PYME/localization/Test/astig_splitterQ.md +66 -0
  941. PYME/localization/Test/astig_theory.md +50 -0
  942. PYME/localization/Test/astig_theory.psf +0 -0
  943. PYME/localization/Test/astig_theory.tif +0 -0
  944. PYME/localization/Test/beads1.psf +0 -0
  945. PYME/localization/Test/beads1.tif +0 -0
  946. PYME/localization/Test/biplane_splitter.md +75 -0
  947. PYME/localization/Test/biplane_splitter2.md +103 -0
  948. PYME/localization/Test/biplane_splitter3.md +115 -0
  949. PYME/localization/Test/defoc.md +48 -0
  950. PYME/localization/Test/dumbell_3d.md +52 -0
  951. PYME/localization/Test/fitTestJig.py +172 -0
  952. PYME/localization/Test/fitTestJigSCMOS.py +487 -0
  953. PYME/localization/Test/fitTestJigWC.py +225 -0
  954. PYME/localization/Test/gauss.md +50 -0
  955. PYME/localization/Test/gaussCOI.md +50 -0
  956. PYME/localization/Test/pri_pe.md +50 -0
  957. PYME/localization/Test/pri_theory.md +51 -0
  958. PYME/localization/Test/pri_theory.psf +0 -0
  959. PYME/localization/Test/pri_theory.tif +0 -0
  960. PYME/localization/Test/psf_astig.psf +0 -0
  961. PYME/localization/Test/psf_astig.tif +0 -0
  962. PYME/localization/Test/psf_astig_bg_d.psf +0 -0
  963. PYME/localization/Test/psf_astig_bg_d.tif +0 -0
  964. PYME/localization/Test/psf_astig_pe.psf +0 -0
  965. PYME/localization/Test/psf_astig_pe.tif +0 -0
  966. PYME/localization/Test/psf_decon.psf +0 -0
  967. PYME/localization/Test/psf_decon.tif +0 -0
  968. PYME/localization/Test/psf_decon_pe.psf +0 -0
  969. PYME/localization/Test/psf_decon_pe.tif +0 -0
  970. PYME/localization/Test/psf_guess.psf +0 -0
  971. PYME/localization/Test/splitter.md +63 -0
  972. PYME/localization/Test/splitter_scav.md +63 -0
  973. PYME/localization/Test/test.psf +0 -0
  974. PYME/localization/Test/test.tif +0 -0
  975. PYME/localization/Test/test_decon.psf +0 -0
  976. PYME/localization/Test/test_decon.tif +0 -0
  977. PYME/localization/Test/test_pe.psf +0 -0
  978. PYME/localization/Test/test_pe.tif +0 -0
  979. PYME/localization/Test/wf_theory.psf +0 -0
  980. PYME/localization/Test/wf_theory2.psf +0 -0
  981. PYME/localization/__init__.py +17 -0
  982. PYME/localization/autopsf/__init__.py +1 -0
  983. PYME/localization/autopsf/autopsf.py +486 -0
  984. PYME/localization/autopsf/roifit.py +310 -0
  985. PYME/localization/cInterp/__init__.py +24 -0
  986. PYME/localization/cInterp/cInterp.cp39-win_amd64.dll.a +0 -0
  987. PYME/localization/cInterp/cInterp.cp39-win_amd64.pyd +0 -0
  988. PYME/localization/cInterp/setup.py +54 -0
  989. PYME/localization/cModels/__init__.py +24 -0
  990. PYME/localization/cModels/gauss_app.cp39-win_amd64.dll.a +0 -0
  991. PYME/localization/cModels/gauss_app.cp39-win_amd64.pyd +0 -0
  992. PYME/localization/ofind.py +444 -0
  993. PYME/localization/ofind3d.py +306 -0
  994. PYME/localization/ofind_nofilt.py +128 -0
  995. PYME/localization/ofind_pri.py +478 -0
  996. PYME/localization/ofind_split.py +399 -0
  997. PYME/localization/ofind_xcorr.py +351 -0
  998. PYME/localization/ofind_xcorr_fw.py +441 -0
  999. PYME/localization/remFitBuf.py +657 -0
  1000. PYME/localization/setup.py +56 -0
  1001. PYME/localization/splitting.py +240 -0
  1002. PYME/localization/twist.py +60 -0
  1003. PYME/misc/TiSettings.py +36 -0
  1004. PYME/misc/TiffImagePlugin.py +838 -0
  1005. PYME/misc/VolProj.py +144 -0
  1006. PYME/misc/__init__.py +25 -0
  1007. PYME/misc/aligned_array.py +10 -0
  1008. PYME/misc/aniso.py +29 -0
  1009. PYME/misc/big_sur_fix.py +20 -0
  1010. PYME/misc/check_for_updates.py +139 -0
  1011. PYME/misc/colormaps.py +199 -0
  1012. PYME/misc/colorsys.py +201 -0
  1013. PYME/misc/computerName.py +33 -0
  1014. PYME/misc/coverslipGeometries.py +60 -0
  1015. PYME/misc/cython_numpy_monkey.py +40 -0
  1016. PYME/misc/depGraph.py +406 -0
  1017. PYME/misc/dirSize.py +31 -0
  1018. PYME/misc/diskCleanup.py +64 -0
  1019. PYME/misc/djangoRecarray.py +57 -0
  1020. PYME/misc/django_widgets.py +27 -0
  1021. PYME/misc/editgrid.py +101 -0
  1022. PYME/misc/eventSpy.py +122 -0
  1023. PYME/misc/exceptions.py +30 -0
  1024. PYME/misc/facsPlot.py +89 -0
  1025. PYME/misc/fbpIcons.py +90 -0
  1026. PYME/misc/fftw_compat.py +28 -0
  1027. PYME/misc/findCycles.py +91 -0
  1028. PYME/misc/fortran_interrupt_defeat.py +43 -0
  1029. PYME/misc/glassModel.py +64 -0
  1030. PYME/misc/hash32.py +41 -0
  1031. PYME/misc/hillcurve.py +88 -0
  1032. PYME/misc/hybrid_ns.py +84 -0
  1033. PYME/misc/kdFit.py +72 -0
  1034. PYME/misc/locate.py +10 -0
  1035. PYME/misc/memUsage.py +65 -0
  1036. PYME/misc/memmap.py +52 -0
  1037. PYME/misc/mock_ctypes.py +28 -0
  1038. PYME/misc/mock_traits.py +59 -0
  1039. PYME/misc/mock_traitsui.py +29 -0
  1040. PYME/misc/pyme_zeroconf.py +287 -0
  1041. PYME/misc/pyro_tracebacks.py +32 -0
  1042. PYME/misc/read_agf.py +605 -0
  1043. PYME/misc/sffake.py +16 -0
  1044. PYME/misc/snells.py +71 -0
  1045. PYME/misc/sqlite_ns.py +132 -0
  1046. PYME/misc/sqlitendarray.py +56 -0
  1047. PYME/misc/taskDef.py +44 -0
  1048. PYME/misc/tempDB.py +56 -0
  1049. PYME/misc/wxThread.py +33 -0
  1050. PYME/misc/zemax.py +259 -0
  1051. PYME/misc/zernike.py +151 -0
  1052. PYME/pyme_warnings.py +70 -0
  1053. PYME/recipes/Recipes/STEDDeconvolution.yaml +14 -0
  1054. PYME/recipes/Recipes/STEDMinistacks.yaml +15 -0
  1055. PYME/recipes/Recipes/STED_COP3.yaml +25 -0
  1056. PYME/recipes/Recipes/blobMeas.yaml +19 -0
  1057. PYME/recipes/Recipes/blobMeas2.yaml +37 -0
  1058. PYME/recipes/Recipes/blobMeasSR.yaml +66 -0
  1059. PYME/recipes/Recipes/coloc.yaml +43 -0
  1060. PYME/recipes/Recipes/coloc_roi_masked.yaml +46 -0
  1061. PYME/recipes/Recipes/multiblob.yaml +38 -0
  1062. PYME/recipes/Recipes/pre_particleTracking.yaml +30 -0
  1063. PYME/recipes/Recipes/segmentAndMeasureLoci.yaml +41 -0
  1064. PYME/recipes/__init__.py +6 -0
  1065. PYME/recipes/acquisition.py +158 -0
  1066. PYME/recipes/bakeshop.py +32 -0
  1067. PYME/recipes/base.py +1393 -0
  1068. PYME/recipes/batchProcess.py +147 -0
  1069. PYME/recipes/filters.py +394 -0
  1070. PYME/recipes/fudges.py +41 -0
  1071. PYME/recipes/graphing.py +108 -0
  1072. PYME/recipes/html_recipe_editor.py +90 -0
  1073. PYME/recipes/localisations.py +1386 -0
  1074. PYME/recipes/machine_learning.py +286 -0
  1075. PYME/recipes/measurement.py +1062 -0
  1076. PYME/recipes/modules.py +50 -0
  1077. PYME/recipes/multiview.py +435 -0
  1078. PYME/recipes/output.py +667 -0
  1079. PYME/recipes/pointcloud.py +510 -0
  1080. PYME/recipes/processing.py +2954 -0
  1081. PYME/recipes/pyramid.py +206 -0
  1082. PYME/recipes/recipe.py +965 -0
  1083. PYME/recipes/recipeGui.py +1029 -0
  1084. PYME/recipes/recipeLayout.py +499 -0
  1085. PYME/recipes/runRecipe.py +140 -0
  1086. PYME/recipes/setup.py +37 -0
  1087. PYME/recipes/simulation.py +91 -0
  1088. PYME/recipes/skfilters.py +143 -0
  1089. PYME/recipes/surface_fitting.py +776 -0
  1090. PYME/recipes/tablefilters.py +275 -0
  1091. PYME/recipes/tracking.py +343 -0
  1092. PYME/recipes/traits.py +157 -0
  1093. PYME/recipes/vertical_recipe_display.py +373 -0
  1094. PYME/resources/__init__.py +23 -0
  1095. PYME/resources/config_template/distributor.yaml +14 -0
  1096. PYME/resources/config_template/nodeserver.yaml +15 -0
  1097. PYME/resources/icons/LUT.png +0 -0
  1098. PYME/resources/icons/YSM_Shield.png +0 -0
  1099. PYME/resources/icons/YSM_logo.png +0 -0
  1100. PYME/resources/icons/arrow.png +0 -0
  1101. PYME/resources/icons/crosshairs.png +0 -0
  1102. PYME/resources/icons/line_select.png +0 -0
  1103. PYME/resources/icons/logo_fmhs.png +0 -0
  1104. PYME/resources/icons/media-playback-pause.png +0 -0
  1105. PYME/resources/icons/media-playback-start.png +0 -0
  1106. PYME/resources/icons/media-skip-backward.png +0 -0
  1107. PYME/resources/icons/media-skip-forward.png +0 -0
  1108. PYME/resources/icons/mesh.png +0 -0
  1109. PYME/resources/icons/mesh.svg +28 -0
  1110. PYME/resources/icons/pmacquire.icns +0 -0
  1111. PYME/resources/icons/pmacquire.ico +0 -0
  1112. PYME/resources/icons/pmanal.icns +0 -0
  1113. PYME/resources/icons/pmanal.ico +0 -0
  1114. PYME/resources/icons/pmvis.icns +0 -0
  1115. PYME/resources/icons/pmvis.ico +0 -0
  1116. PYME/resources/icons/points.png +0 -0
  1117. PYME/resources/icons/points.svg +16 -0
  1118. PYME/resources/icons/pymeLogo.png +0 -0
  1119. PYME/resources/icons/rect_select.png +0 -0
  1120. PYME/resources/icons/squiggle_select.png +0 -0
  1121. PYME/resources/icons/text3800.icns +0 -0
  1122. PYME/resources/icons/text3800.ico +0 -0
  1123. PYME/resources/icons/text3800.png +0 -0
  1124. PYME/resources/icons/text3800.tif +0 -0
  1125. PYME/resources/icons/text5377.icns +0 -0
  1126. PYME/resources/icons/text5377.ico +0 -0
  1127. PYME/resources/icons/text5377.png +0 -0
  1128. PYME/resources/icons/tracks.png +0 -0
  1129. PYME/resources/icons/tracks.svg +19 -0
  1130. PYME/resources/icons/view-front.png +0 -0
  1131. PYME/resources/icons/view-home.png +0 -0
  1132. PYME/resources/icons/view-right.png +0 -0
  1133. PYME/resources/icons/view-top.png +0 -0
  1134. PYME/resources/test_datasets/README.md +8 -0
  1135. PYME/resources/test_datasets/t_im.tif +0 -0
  1136. PYME/resources/test_datasets/test_csv.csv +4282 -0
  1137. PYME/resources/test_datasets/test_hdf.hdf +0 -0
  1138. PYME/resources/web/rule_inline.html +21 -0
  1139. PYME/resources/web/ruleserver.html +75 -0
  1140. PYME/resources/web/static/bootstrap.min.js +7 -0
  1141. PYME/resources/web/static/cluster.css +137 -0
  1142. PYME/resources/web/static/css/bootstrap-theme.css +587 -0
  1143. PYME/resources/web/static/css/bootstrap-theme.min.css +6 -0
  1144. PYME/resources/web/static/css/bootstrap.css +6757 -0
  1145. PYME/resources/web/static/css/bootstrap.min.css +6 -0
  1146. PYME/resources/web/static/css/flat_design_colors_full.css +306 -0
  1147. PYME/resources/web/static/css/recipe_editor.css +106 -0
  1148. PYME/resources/web/static/dashboard.css +106 -0
  1149. PYME/resources/web/static/fileselector.js +77 -0
  1150. PYME/resources/web/static/fonts/glyphicons-halflings-regular.eot +0 -0
  1151. PYME/resources/web/static/fonts/glyphicons-halflings-regular.svg +288 -0
  1152. PYME/resources/web/static/fonts/glyphicons-halflings-regular.ttf +0 -0
  1153. PYME/resources/web/static/fonts/glyphicons-halflings-regular.woff +0 -0
  1154. PYME/resources/web/static/fonts/glyphicons-halflings-regular.woff2 +0 -0
  1155. PYME/resources/web/static/images/osd_flat/LICENSE.txt +116 -0
  1156. PYME/resources/web/static/images/osd_flat/README.md +9 -0
  1157. PYME/resources/web/static/images/osd_flat/fullpage_grouphover.png +0 -0
  1158. PYME/resources/web/static/images/osd_flat/fullpage_hover.png +0 -0
  1159. PYME/resources/web/static/images/osd_flat/fullpage_pressed.png +0 -0
  1160. PYME/resources/web/static/images/osd_flat/fullpage_rest.png +0 -0
  1161. PYME/resources/web/static/images/osd_flat/home_grouphover.png +0 -0
  1162. PYME/resources/web/static/images/osd_flat/home_hover.png +0 -0
  1163. PYME/resources/web/static/images/osd_flat/home_pressed.png +0 -0
  1164. PYME/resources/web/static/images/osd_flat/home_rest.png +0 -0
  1165. PYME/resources/web/static/images/osd_flat/next_grouphover.png +0 -0
  1166. PYME/resources/web/static/images/osd_flat/next_hover.png +0 -0
  1167. PYME/resources/web/static/images/osd_flat/next_pressed.png +0 -0
  1168. PYME/resources/web/static/images/osd_flat/next_rest.png +0 -0
  1169. PYME/resources/web/static/images/osd_flat/previous_grouphover.png +0 -0
  1170. PYME/resources/web/static/images/osd_flat/previous_hover.png +0 -0
  1171. PYME/resources/web/static/images/osd_flat/previous_pressed.png +0 -0
  1172. PYME/resources/web/static/images/osd_flat/previous_rest.png +0 -0
  1173. PYME/resources/web/static/images/osd_flat/rotateleft_grouphover.png +0 -0
  1174. PYME/resources/web/static/images/osd_flat/rotateleft_hover.png +0 -0
  1175. PYME/resources/web/static/images/osd_flat/rotateleft_pressed.png +0 -0
  1176. PYME/resources/web/static/images/osd_flat/rotateleft_rest.png +0 -0
  1177. PYME/resources/web/static/images/osd_flat/rotateright_grouphover.png +0 -0
  1178. PYME/resources/web/static/images/osd_flat/rotateright_hover.png +0 -0
  1179. PYME/resources/web/static/images/osd_flat/rotateright_pressed.png +0 -0
  1180. PYME/resources/web/static/images/osd_flat/rotateright_rest.png +0 -0
  1181. PYME/resources/web/static/images/osd_flat/zoomin_grouphover.png +0 -0
  1182. PYME/resources/web/static/images/osd_flat/zoomin_hover.png +0 -0
  1183. PYME/resources/web/static/images/osd_flat/zoomin_pressed.png +0 -0
  1184. PYME/resources/web/static/images/osd_flat/zoomin_rest.png +0 -0
  1185. PYME/resources/web/static/images/osd_flat/zoomout_grouphover.png +0 -0
  1186. PYME/resources/web/static/images/osd_flat/zoomout_hover.png +0 -0
  1187. PYME/resources/web/static/images/osd_flat/zoomout_pressed.png +0 -0
  1188. PYME/resources/web/static/images/osd_flat/zoomout_rest.png +0 -0
  1189. PYME/resources/web/static/jigna-vue-bare.js +1395 -0
  1190. PYME/resources/web/static/jquery.min.js +2 -0
  1191. PYME/resources/web/static/jstree.min.js +6 -0
  1192. PYME/resources/web/static/openSeadragonGL.js +102 -0
  1193. PYME/resources/web/static/openseadragon-svg-overlay.js +103 -0
  1194. PYME/resources/web/static/openseadragon-viewerinputhook.min.js +6 -0
  1195. PYME/resources/web/static/openseadragon.min.js +9 -0
  1196. PYME/resources/web/static/recipe_editor.js +119 -0
  1197. PYME/resources/web/static/shaders/fragment/lut.glsl +20 -0
  1198. PYME/resources/web/static/shaders/fragment/none.glsl +17 -0
  1199. PYME/resources/web/static/shaders/fragment/outline.glsl +43 -0
  1200. PYME/resources/web/static/shaders/fragment/rect.glsl +7 -0
  1201. PYME/resources/web/static/shaders/fragment/sobel3.glsl +71 -0
  1202. PYME/resources/web/static/shaders/fragment/white.glsl +4 -0
  1203. PYME/resources/web/static/shaders/vertex/rect.glsl +22 -0
  1204. PYME/resources/web/static/shaders/vertex/square.glsl +9 -0
  1205. PYME/resources/web/static/utils.js +3 -0
  1206. PYME/resources/web/static/viaWebGL.js +217 -0
  1207. PYME/setup.py +109 -0
  1208. PYME/setup_exe.py +57 -0
  1209. PYME/simulation/__init__.py +0 -0
  1210. PYME/simulation/locify.py +319 -0
  1211. PYME/simulation/pointsets.py +279 -0
  1212. PYME/simulation/wormlike.py +99 -0
  1213. PYME/simulation/wormlike2.py +198 -0
  1214. PYME/simulation/wormlike3.py +197 -0
  1215. PYME/simulation/wormlike_curled.py +104 -0
  1216. PYME/tileviewer/__init__.py +0 -0
  1217. PYME/tileviewer/templates/roi_list.html +12 -0
  1218. PYME/tileviewer/templates/tileviewer.html +247 -0
  1219. PYME/tileviewer/tileviewer.py +207 -0
  1220. PYME/ui/AUIFrame.py +279 -0
  1221. PYME/ui/MetadataTree.py +469 -0
  1222. PYME/ui/__init__.py +4 -0
  1223. PYME/ui/about_dlg.py +29 -0
  1224. PYME/ui/autoFoldPanel.py +643 -0
  1225. PYME/ui/cascading_layout.py +45 -0
  1226. PYME/ui/crop_dialog.py +145 -0
  1227. PYME/ui/custom_traits_editors.py +508 -0
  1228. PYME/ui/editFilterDialog.py +80 -0
  1229. PYME/ui/editList.py +40 -0
  1230. PYME/ui/fastGraph.py +240 -0
  1231. PYME/ui/filterPane.py +348 -0
  1232. PYME/ui/histLimits.py +543 -0
  1233. PYME/ui/layerFoldPanel.py +232 -0
  1234. PYME/ui/manualFoldPanel.py +870 -0
  1235. PYME/ui/mytimer.py +116 -0
  1236. PYME/ui/patch_traitsui.py +151 -0
  1237. PYME/ui/progress.py +160 -0
  1238. PYME/ui/recArrayView.py +145 -0
  1239. PYME/ui/selection.py +71 -0
  1240. PYME/ui/shell.py +37 -0
  1241. PYME/ui/traitsui_constants.py +91 -0
  1242. PYME/ui/wx_compat.py +43 -0
  1243. PYME/update_version.py +159 -0
  1244. PYME/util/__init__.py +3 -0
  1245. PYME/util/authenticate.py +164 -0
  1246. PYME/util/execfile.py +21 -0
  1247. PYME/util/fProfile/__init__.py +1 -0
  1248. PYME/util/fProfile/convertProfile.py +117 -0
  1249. PYME/util/fProfile/fProfile.py +162 -0
  1250. PYME/util/fProfile/html/404.html +60 -0
  1251. PYME/util/fProfile/html/css/main.css +258 -0
  1252. PYME/util/fProfile/html/css/normalize.css +427 -0
  1253. PYME/util/fProfile/html/css/normalize.min.css +1 -0
  1254. PYME/util/fProfile/html/index.html +64 -0
  1255. PYME/util/fProfile/html/js/main.js +492 -0
  1256. PYME/util/fProfile/html/js/plugins.js +24 -0
  1257. PYME/util/fProfile/html/js/vendor/crossfilter.min.js +1 -0
  1258. PYME/util/fProfile/html/js/vendor/jquery-1.11.2.min.js +4 -0
  1259. PYME/util/fProfile/html/js/vendor/modernizr-2.8.3.min.js +4 -0
  1260. PYME/util/fProfile/html/js/vendor/tinycolor-min.js +4 -0
  1261. PYME/util/fProfile/viewProfile.py +30 -0
  1262. PYME/util/install_dependencies.py +95 -0
  1263. PYME/util/log_verbosity.py +11 -0
  1264. PYME/util/mProfile/__init__.py +1 -0
  1265. PYME/util/mProfile/colorize_db_t.py +297 -0
  1266. PYME/util/mProfile/mProfile.py +164 -0
  1267. PYME/util/mProfile/p.py +101 -0
  1268. PYME/util/mProfile/tProfile.py +165 -0
  1269. PYME/util/pymelauncher.py +47 -0
  1270. PYME/util/shmarray/__init__.py +0 -0
  1271. PYME/util/shmarray/shmTest.py +74 -0
  1272. PYME/util/shmarray/shmarray.py +165 -0
  1273. PYME/util/webframework.py +365 -0
  1274. PYME/version.py +65 -0
  1275. python_microscopy-25.6.5.dist-info/METADATA +68 -0
  1276. python_microscopy-25.6.5.dist-info/RECORD +1277 -0
  1277. python_microscopy-25.6.5.dist-info/WHEEL +4 -0
@@ -0,0 +1,2787 @@
1
+ # -*- coding: utf8 -*-
2
+ # cython: profile=True
3
+ from libc.stdint cimport int32_t
4
+ cimport numpy as np
5
+ import numpy as np
6
+ cimport cython
7
+
8
+ import warnings
9
+
10
+ #from libc.math cimport sqrtf
11
+
12
+ cdef extern from "<math.h>" nogil:
13
+ float sqrtf(float)
14
+
15
+ from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free
16
+
17
+ import copy
18
+
19
+ from PYME.experimental import triangle_mesh_utils
20
+
21
+ DEF MAX_VERTEX_COUNT = 2**31
22
+
23
+ # Regular, boundary, and max vertex valences. These need tp be
24
+ DEF VALENCE = 6
25
+ DEF BOUNDARY_VALENCE = 4
26
+
27
+ cdef extern from "triangle_mesh_utils.c":
28
+ void _update_face_normals(int32_t *f_idxs, halfedge_t *halfedges, vertex_t *vertices, face_t *faces, signed int n_idxs)
29
+
30
+ void update_face_normal(int f_idx, halfedge_t *halfedges, vertex_d *vertices, face_d *faces)
31
+ void update_single_vertex_neighbours(int v_idx, halfedge_t *halfedges, vertex_d *vertices, face_d *faces)
32
+
33
+ int flood_fill_star_component(int32_t h_idx, int component, halfedge_t *halfedges)
34
+ float norm(const float *pos)
35
+ void cross(const float *a, const float *b, float *n)
36
+ void difference(const float *a, const float *b, float *out)
37
+ void vsum(const float *a, const float *b, float *out)
38
+ float dot(const float *a, const float *b)
39
+ void scalar_mult(float *a, const float b);
40
+
41
+ cdef extern from "remesh_ops.c":
42
+ int remesh_edge_flip(halfedge_t * halfedges, vertex_t *vertices, face_t * faces, int idx, int n_halfedges, int live_update);
43
+ int remesh_relax(halfedge_t *halfedges, vertex_t *vertices, int n_vertices, face_t* faces, int n_faces, float l, int n_iterations);
44
+ void remesh_edge_zipper(halfedge_t * halfedges, int32_t edge1, int32_t edge2);
45
+ int remesh_check_neighour_twins(halfedge_t * halfedges, vertex_t * vertices, int32_t vertex_id);
46
+ int remesh_edge_collapse(halfedge_t * halfedges, int32_t n_halfedges, vertex_t * vertices, face_t* faces, int32_t idx, int live_update);
47
+ int remesh_delete_vertex(vertex_t * vertices, int32_t v_idx);
48
+ int remesh_delete_edge(halfedge_t * halfedges, int32_t e_idx);
49
+ int remesh_delete_face(halfedge_t * halfedges, face_t * faces, int32_t e_idx);
50
+ int remesh_split_edge(halfedge_t * halfedges, int32_t n_halfedges, vertex_t * vertices, face_t* faces, int32_t idx, int32_t * new_edges,
51
+ int32_t *new_vertices, int32_t * new_faces, int n_edge_idx, int n_vertex_idx, int n_face_idx, int live_update, int upsample)
52
+
53
+
54
+ HALFEDGE_DTYPE = np.dtype([('vertex', 'i4'), ('face', 'i4'), ('twin', 'i4'), ('next', 'i4'), ('prev', 'i4'), ('length', 'f4'), ('component', 'i4'), ('locally_manifold', 'i4')], align=True)
55
+ FACE_DTYPE = np.dtype([('halfedge', 'i4'), ('normal', '3f4'), ('area', 'f4'), ('component', 'i4')], align=True)
56
+ FACE_DTYPE2 = np.dtype([('halfedge', 'i4'), ('normal0', 'f4'), ('normal1', 'f4'), ('normal2', 'f4'), ('area', 'f4'), ('component', 'i4')], align=True)
57
+ # Note that VERTEX_DTYPE neighbors size must match NEIGHBORSIZE
58
+ VERTEX_DTYPE = np.dtype([('position', '3f4'), ('normal', '3f4'), ('halfedge', 'i4'), ('valence', 'i4'), ('neighbors', '20i4'), ('component', 'i4'),('locally_manifold', 'i4')], align=True)
59
+ VERTEX_DTYPE2 = np.dtype([('position0', 'f4'),
60
+ ('position1', 'f4'),
61
+ ('position2', 'f4'),
62
+ ('normal0', 'f4'),
63
+ ('normal1', 'f4'),
64
+ ('normal2', 'f4'),
65
+ ('halfedge', 'i4'),
66
+ ('valence', 'i4'),
67
+ ('neighbor0', 'i4'),
68
+ ('neighbor1', 'i4'),
69
+ ('neighbor2', 'i4'),
70
+ ('neighbor3', 'i4'),
71
+ ('neighbor4', 'i4'),
72
+ ('neighbor5', 'i4'),
73
+ ('neighbor6', 'i4'),
74
+ ('neighbor7', 'i4'),
75
+ ('neighbor8', 'i4'),
76
+ ('neighbor9', 'i4'),
77
+ ('neighbor10', 'i4'),
78
+ ('neighbor11', 'i4'),
79
+ ('neighbor12', 'i4'),
80
+ ('neighbor13', 'i4'),
81
+ ('neighbor14', 'i4'),
82
+ ('neighbor15', 'i4'),
83
+ ('neighbor16', 'i4'),
84
+ ('neighbor17', 'i4'),
85
+ ('neighbor18', 'i4'),
86
+ ('neighbor19', 'i4'),
87
+ ('component', 'i4'),
88
+ ('locally_manifold', 'i4')], align=True)
89
+
90
+
91
+ # MESH CONVENTIONS
92
+ ##################
93
+ #
94
+ # halfedge.vertex = vertex the halfedge is pointing TO
95
+ #
96
+ # vertex.halfedge = a halfedge that eminates FROM the vertex.
97
+ #
98
+
99
+ # cdef packed struct halfedge_d:
100
+ # int32_t vertex
101
+ # int32_t face
102
+ # int32_t twin
103
+ # int32_t next
104
+ # int32_t prev
105
+ # np.float32_t length
106
+ # int32_t component
107
+ #
108
+ # cdef packed struct face_d:
109
+ # int32_t halfedge
110
+ # np.float32_t normal0
111
+ # np.float32_t normal1
112
+ # np.float32_t normal2
113
+ # np.float32_t area
114
+ # int32_t component
115
+
116
+ # cdef packed struct face_t: #non flattened type from triangle_mesh_utils.h
117
+ # int32_t halfedge
118
+ # float normal[VECTORSIZE]
119
+ # float area
120
+ # int32_t component
121
+
122
+ # cdef packed struct vertex_d:
123
+ # np.float32_t position0
124
+ # np.float32_t position1
125
+ # np.float32_t position2
126
+ # np.float32_t normal0
127
+ # np.float32_t normal1
128
+ # np.float32_t normal2
129
+ # int32_t halfedge
130
+ # int32_t valence
131
+ # int32_t neighbor0
132
+ # int32_t neighbor1
133
+ # int32_t neighbor2
134
+ # int32_t neighbor3
135
+ # int32_t neighbor4
136
+ # int32_t neighbor5
137
+ # int32_t neighbor6
138
+ # int32_t neighbor7
139
+ # int32_t neighbor8
140
+ # int32_t neighbor9
141
+ # int32_t neighbor10
142
+ # int32_t neighbor11
143
+ # int32_t neighbor12
144
+ # int32_t neighbor13
145
+ # int32_t neighbor14
146
+ # int32_t neighbor15
147
+ # int32_t neighbor16
148
+ # int32_t neighbor17
149
+ # int32_t neighbor18
150
+ # int32_t neighbor19
151
+ # int32_t component
152
+
153
+ # cdef packed struct vertex_t: # non-flattened version from triangle_mesh_utils.c
154
+ # float position[VECTORSIZE];
155
+ # float normal[VECTORSIZE];
156
+ # int32_t halfedge;
157
+ # int32_t valence;
158
+ # int32_t neighbors[NEIGHBORSIZE];
159
+ # int32_t component;
160
+
161
+ #cdef union halfedge_d:
162
+ #ctypedef halfedge_t halfedge_d
163
+ #ctypedef face_t face_d
164
+ #ctypedef vertex_t vertex_d
165
+
166
+ LOOP_ALPHA_FACTOR = (np.log(13)-np.log(3))/12
167
+
168
+
169
+ # string key arguments incur a lot of overhead - use index / enums
170
+ __insertion_keys = ['vertex', 'halfedge']
171
+
172
+ DEF INSERTION_KEY_VERTEX = 0
173
+ DEF INSERTION_KEY_HALFEDGE = 1
174
+
175
+ cdef class TrianglesBase(object):
176
+ """
177
+ Base class to let VisGUI layers detect objects which are a bunch of triangles
178
+
179
+ a derived class should provide suitable attributes/properties for vertices, faces, face_normals,
180
+ and vertex_normals.
181
+
182
+ See Also: Tesselation class in PYME.recipes.pointcloud
183
+
184
+ TODO - move me out of here?
185
+ TODO - provide default implementations for face_normals and vertex_normals?
186
+
187
+ """
188
+ vertices=None
189
+ faces=None
190
+ face_normals=None
191
+ vertex_normals = None
192
+
193
+ def keys(self):
194
+ """
195
+ returns the names of possible vertex attributes
196
+ """
197
+ raise NotImplementedError('Over-ride in derived class')
198
+
199
+ def __getitem__(self, item):
200
+ """
201
+ returns a given vertex attribute
202
+ """
203
+ raise NotImplementedError('Over-ride in derived class')
204
+
205
+ cdef class TriangleMesh(TrianglesBase):
206
+
207
+ def __init__(self, vertices=None, faces=None, mesh=None, **kwargs):
208
+ """
209
+ Base class for triangle meshes stored using halfedge data structure.
210
+ Expects STL-like input.
211
+
212
+ Parameters
213
+ ----------
214
+ vertices : np.array
215
+ N x 3 array of Euclidean points.
216
+ faces : np .array
217
+ M x 3 array of vertex indices indicating triangle connectivity.
218
+ Expects STL redundancy.
219
+ """
220
+ if mesh is not None:
221
+
222
+ self._vertices = np.copy(mesh._vertices)
223
+ # Flatten to address cython view problem
224
+ _flat_vertices = self._vertices.view(VERTEX_DTYPE2)
225
+ # Set up c views to arrays
226
+ self._set_cvertices(_flat_vertices)
227
+ self._vertex_vacancies = copy.copy(mesh._vertex_vacancies)
228
+
229
+ self._faces = np.copy(mesh._faces)
230
+ _flat_faces = self._faces.view(FACE_DTYPE2)
231
+ # Set up c views to arrays
232
+ self._set_cfaces(_flat_faces)
233
+ self._face_vacancies = copy.copy(mesh._face_vacancies)
234
+
235
+ self._halfedges = np.copy(mesh._halfedges)
236
+ self._set_chalfedges(self._halfedges)
237
+ self._halfedge_vacancies = copy.copy(mesh._halfedge_vacancies)
238
+ else:
239
+ self.build_from_verts_faces(vertices, faces)
240
+
241
+ # Representation of faces by triplets of vertices
242
+ self._faces_by_vertex = None
243
+ self._components_valid = 0
244
+ self._H = None
245
+ self._K = None
246
+
247
+ # loop subdivision vars
248
+ self._loop_subdivision_flip_edges = []
249
+ self._loop_subdivision_new_vertices = []
250
+
251
+ # Singular edges
252
+ # self._singular_edges = None
253
+ self._singular_edges_valid = 0
254
+ # self._singular_vertices = None
255
+ self._singular_vertices_valid = 0
256
+
257
+ # Populate the normals
258
+ #print('populating face normals')
259
+ self._face_normals_valid = 0
260
+ self.face_normals
261
+ #print('populating vertex normals')
262
+ self._vertex_normals_valid = 0
263
+ self.vertex_normals
264
+ #print('done populating normals')
265
+
266
+ # Properties we can visualize
267
+ self.vertex_properties = ['x', 'y', 'z', 'component', 'boundary', 'singular', 'curvature_mean', 'curvature_gaussian']
268
+ self.vertex_vector_properties = ['vertex_normals']
269
+
270
+ self.extra_vertex_data = {}
271
+
272
+ self.fix_boundary = 1 # Hold boundary edges in place
273
+
274
+ # Is the mesh manifold?
275
+ self._manifold = 0
276
+
277
+ # Curvatures
278
+ self._H = None
279
+ self._K = None
280
+ self.smooth_curvature = 0
281
+
282
+ self._components_valid = 0
283
+
284
+ self.mdh = None
285
+
286
+ # Set fix_boundary, etc.
287
+ for key, value in kwargs.items():
288
+ setattr(self, key, value)
289
+
290
+ def __getitem__(self, k):
291
+ # this defers evaluation of the properties until we actually access them,
292
+ # as opposed to the mappings which stored the values on class creation.
293
+ try:
294
+ res = getattr(self, k)
295
+ except AttributeError:
296
+ try:
297
+ res = self.extra_vertex_data[k]
298
+ except KeyError:
299
+ raise KeyError('Key %s not defined' % k)
300
+
301
+ return res
302
+
303
+ def __copy__(self):
304
+ return type(self)(mesh=self)
305
+
306
+ @classmethod
307
+ def from_stl(cls, filename, **kwargs):
308
+ """
309
+ Read from an STL file.
310
+ """
311
+ from PYME.IO.FileUtils import stl
312
+
313
+ # Load an STL from file
314
+ triangles_stl = stl.load_stl_binary(filename)
315
+
316
+ # Call from_np_stl on the file stream
317
+ return cls.from_np_stl(triangles_stl, **kwargs)
318
+
319
+ @classmethod
320
+ def from_ply(cls, filename, **kwargs):
321
+ """
322
+ Read from PLY file.
323
+ """
324
+ from PYME.IO.FileUtils import ply
325
+
326
+ # Load a PLY from file
327
+ vertices, faces, _ = ply.load_ply(filename)
328
+
329
+ return cls(vertices, faces, **kwargs)
330
+
331
+ @classmethod
332
+ def from_np_stl(cls, triangles_stl, origin=[0,0,0], **kwargs):
333
+ """
334
+ Read from an already-loaded STL stream.
335
+
336
+
337
+ Parameters
338
+ ----------
339
+
340
+ triangles_stl : np.ndarray
341
+ the triangles as a numpy array
342
+ origin: 3-tuple (or array)
343
+ coordinates of the mesh origin (nm). Used to ensure that meshes generated from image isosurfaces line up with those generated from different ROIs / points.
344
+
345
+ """
346
+ vertices_raw = np.vstack((triangles_stl['vertex0'],
347
+ triangles_stl['vertex1'],
348
+ triangles_stl['vertex2']))
349
+
350
+ vertices_raw = vertices_raw + np.array(origin)[None,:]
351
+ vertices, faces_raw = np.unique(vertices_raw,
352
+ return_inverse=True,
353
+ axis=0)
354
+ faces = faces_raw.reshape(faces_raw.shape[0] // 3, 3, order='F')
355
+
356
+ print('Data munged to vertices, faces')
357
+ return cls(vertices, faces, **kwargs)
358
+
359
+ @property
360
+ def x(self):
361
+ return self.vertices[:,0]
362
+
363
+ @property
364
+ def y(self):
365
+ return self.vertices[:,1]
366
+
367
+ @property
368
+ def z(self):
369
+ return self.vertices[:,2]
370
+
371
+ @property
372
+ def curvature_mean(self):
373
+ if self._H is None:
374
+ self.calculate_curvatures()
375
+ return self._H
376
+
377
+ @property
378
+ def curvature_gaussian(self):
379
+ if self._K is None:
380
+ self.calculate_curvatures()
381
+ return self._K
382
+
383
+ @property
384
+ def component(self):
385
+ # if np.all(self._vertices['component'] == -1):
386
+ if not self._components_valid:
387
+ self.find_connected_components()
388
+ self._components_valid = 1
389
+ return self._vertices['component']
390
+
391
+ @property
392
+ def vertices(self):
393
+ return self._vertices['position']
394
+
395
+ @property
396
+ def vertex_mask(self):
397
+ """
398
+ Returns a mask indicating which vertices are valid
399
+ """
400
+
401
+ return self._vertices['halfedge'] != -1
402
+
403
+ @property
404
+ def faces(self):
405
+ """
406
+ Returns faces by vertex for rendering and STL applications.
407
+ """
408
+ if self._faces_by_vertex is None:
409
+ faces = self._faces['halfedge'][self._faces['halfedge'] != -1]
410
+ v0 = self._halfedges['vertex'][self._halfedges['prev'][faces]]
411
+ v1 = self._halfedges['vertex'][faces]
412
+ v2 = self._halfedges['vertex'][self._halfedges['next'][faces]]
413
+ self._faces_by_vertex = np.vstack([v0, v1, v2]).T
414
+ return self._faces_by_vertex
415
+
416
+ @property
417
+ def boundary(self):
418
+ #FIXME - is this simply a duplicate of the .singular property?
419
+ self._update_vertex_locally_manifold()
420
+ return (self._vertices['locally_manifold'] == 0)
421
+
422
+ @property
423
+ def singular(self):
424
+ self._update_singular_edge_locally_manifold()
425
+ self._update_singular_vertex_locally_manifold()
426
+ return (self._vertices['locally_manifold'] == 0)
427
+
428
+ @property
429
+ def singular_edges(self):
430
+ """
431
+ Get a list of singular edges. A singular edge is any edge shared by
432
+ more than two faces.
433
+ """
434
+ self._update_singular_edge_locally_manifold()
435
+ return np.flatnonzero(self._halfedges['locally_manifold'] == 0)
436
+
437
+ @property
438
+ def singular_vertices(self):
439
+ """
440
+ Get a list of singular vertices. This includes endpoints of singular
441
+ edges, and isolated singular vertices. Isolated singular vertices
442
+ have more than one 1-neighbor ring.
443
+ """
444
+ self._update_singular_edge_locally_manifold()
445
+ self._update_singular_vertex_locally_manifold()
446
+ return np.flatnonzero(self._vertices['locally_manifold'] == 0)
447
+
448
+ def _update_all_face_normals(self, recompute=True):
449
+ """
450
+ Update face normals
451
+
452
+ If called with recompute=False, only recomputes if face normals have been marked as invalid
453
+ """
454
+ if recompute or (not self._face_normals_valid):
455
+ triangle_mesh_utils.c_update_all_face_normals(self._faces.shape[0], self._halfedges, self._vertices, self._faces)
456
+ self._face_normals_valid = 1
457
+
458
+ @property
459
+ def face_normals(self):
460
+ """
461
+ Return the normal of each triangular face.
462
+ """
463
+ self._update_all_face_normals(recompute=False)
464
+ return self._faces['normal'][self._faces['halfedge'] != -1]
465
+
466
+ @property
467
+ def face_areas(self):
468
+ """
469
+ Return the area of each triangular face.
470
+ """
471
+ self._update_all_face_normals(recompute=False)
472
+ return self._faces['area']
473
+
474
+ def _update_all_vertex_neighbours(self, recompute=True):
475
+ """
476
+ Update the vertex neighbour information (neighbours, normals, edge lengths)
477
+
478
+ If called with recompute=False, only recomputes if vertex normals have been marked as invalid
479
+ """
480
+ if recompute or (not self._vertex_normals_valid):
481
+ triangle_mesh_utils.c_update_all_vertex_neighbors(self._vertices.shape[0], self._halfedges, self._vertices, self._faces)
482
+ self._vertex_normals_valid = 1
483
+
484
+ @property
485
+ def vertex_neighbors(self):
486
+ """
487
+ Return the up to NEIGHBORSIZE neighbors of each vertex.
488
+ """
489
+
490
+ self._update_all_vertex_neighbours(recompute=False)
491
+ return self._vertices['neighbors']
492
+
493
+ @property
494
+ def vertex_normals(self):
495
+ """
496
+ Return the normal of each vertex.
497
+ """
498
+ self._update_all_vertex_neighbours(recompute=False)
499
+ return self._vertices['normal']
500
+
501
+ @property
502
+ def valences(self):
503
+ """
504
+ Return the valence of each vertex.
505
+ """
506
+ self._update_all_vertex_neighbours(recompute=False)
507
+ return self._vertices['valence']
508
+
509
+ @property
510
+ def manifold(self):
511
+ """
512
+ Checks if the mesh is manifold: Is every edge is shared by exactly two
513
+ triangles?
514
+ """
515
+
516
+ if not self._manifold:
517
+ vertex_mask = (self._halfedges['vertex'] != -1)
518
+ twin_mask = (self._halfedges['twin'] == -1)
519
+ self._manifold = (np.sum(self.singular) == 0) and (np.sum(vertex_mask & twin_mask) == 0)
520
+
521
+ return self._manifold
522
+
523
+ @property
524
+ def euler_characteristic(self):
525
+ # TODO: Cache on a parameter
526
+ chi = self._calculate_euler_characteristic()
527
+
528
+ return chi
529
+
530
+ @property
531
+ def genus(self):
532
+ return (2-self.euler_characteristic)/2
533
+
534
+ @property
535
+ def bbox(self):
536
+ """
537
+ Mesh bounding box. xl, yl, zl, xu, yu, zu
538
+ """
539
+ cdef float xl, xu, yl, yu, zl, zu
540
+ v = self._vertices['position'][self._vertices['halfedge'] != -1]
541
+ xl, xu = np.min(v[:,0]), np.max(v[:,0])
542
+ yl, yu = np.min(v[:,1]), np.max(v[:,1])
543
+ zl, zu = np.min(v[:,2]), np.max(v[:,2])
544
+
545
+ return xl, yl, zl, xu, yu, zu
546
+
547
+ def keys(self):
548
+ return list(self.vertex_properties) + list(self.extra_vertex_data.keys())
549
+
550
+ def _update_vertex_locally_manifold(self):
551
+ # identify boundary vertices
552
+ self._vertices['locally_manifold'] = 1
553
+ boundary_edges = (self._halfedges['twin'] == -1) & (self._halfedges['vertex'] != -1)
554
+ self._vertices['locally_manifold'][self._halfedges['vertex'][boundary_edges]] = 0
555
+ self._vertices['locally_manifold'][self._halfedges['vertex'][self._halfedges['prev'][boundary_edges]]] = 0
556
+
557
+ self._singular_vertices_valid = 0
558
+
559
+ def _update_singular_vertex_locally_manifold(self, clear=True):
560
+ if not self._singular_vertices_valid:
561
+
562
+ # identify singular vertices
563
+ if clear:
564
+ # optional so this can be chained with _update_vertex_locally_manifold
565
+ self._vertices['locally_manifold'] = 1
566
+
567
+ # vertices connected to singular edges
568
+ non_manifold = self._halfedges['locally_manifold'] == 0
569
+ self._vertices['locally_manifold'][self._halfedges['vertex'][non_manifold]] = 0
570
+ self._vertices['locally_manifold'][self._halfedges['vertex'][self._halfedges['prev'][non_manifold]]] = 0
571
+
572
+ # isolated signular vertices
573
+ valence = np.zeros(self._vertices.shape[0],dtype=int)
574
+ valence[self._vertices['halfedge'] == -1] = -1
575
+ self._compute_raw_vertex_valences(valence)
576
+ # print("valence check")
577
+ # print(valence[valence != self._vertices['valence']])
578
+ # print(self._vertices['valence'][valence != self._vertices['valence']])
579
+ self._vertices['locally_manifold'][valence != self._vertices['valence']] = 0
580
+
581
+ self._singular_vertices_valid = 1
582
+
583
+ # def __update_singular_edge_locally_manifold(self, clear=True):
584
+ # if not self._singular_edges_valid:
585
+ # if clear:
586
+ # # optional so this can be chained with _update_vertex_locally_manifold
587
+ # self._halfedges['locally_manifold'] = 1
588
+
589
+ # # grab all edges
590
+ # edges = np.vstack([self._halfedges['vertex'][self._halfedges['prev']],
591
+ # self._halfedges['vertex']])
592
+
593
+ # # Sort lo->hi
594
+ # sorted_edges = np.sort(edges, axis=0)
595
+
596
+ # # find the number of unique elements
597
+ # _, idxs, counts = np.unique(sorted_edges,axis=1,return_counts=True,return_inverse=True)
598
+ # singular = np.sum(idxs[None,:] == np.flatnonzero(counts > 2)[:,None],axis=0).astype(bool)
599
+
600
+ # self._halfedges['locally_manifold'][singular & (self._halfedges['vertex'] != -1)] = 0
601
+
602
+ # self._singular_edges_valid = 1
603
+
604
+ def _update_singular_edge_locally_manifold(self, clear=True):
605
+ cdef int i
606
+ cdef int v1, v2, _v1, _v2
607
+ cdef int l_v, n_e
608
+
609
+ cdef int n_halfedges = self._halfedges.shape[0]
610
+ cdef int n_vertices = self._vertices.shape[0]
611
+ cdef int n_slots = n_vertices*n_vertices
612
+
613
+ #cdef int * d
614
+
615
+ if not self._singular_edges_valid:
616
+ d = {}
617
+
618
+ if clear:
619
+ # optional so this can be chained with _update_vertex_locally_manifold
620
+ self._halfedges['locally_manifold'] = 1
621
+
622
+ #print('iterating edges')
623
+ # Use a dictionary to keep track of which edges are already assigned twins
624
+
625
+ for i in range(n_halfedges):
626
+ if self._chalfedges[i].prev == -1:
627
+ continue
628
+ _v1 = self._chalfedges[self._chalfedges[i].prev].vertex
629
+ _v2 = self._chalfedges[i].vertex
630
+
631
+ if (_v1 == -1) or (_v2 == -1):
632
+ pass
633
+ else:
634
+ v1 = min(_v1, _v2)
635
+ v2 = max(_v1, _v2)
636
+
637
+ slot_idx = v1*n_vertices + v2
638
+
639
+ n_e = d.get(slot_idx, 0)
640
+
641
+ d[slot_idx] = n_e + 1
642
+
643
+ for i in range(n_halfedges):
644
+ if self._chalfedges[i].prev == -1:
645
+ continue
646
+ _v1 = self._chalfedges[self._chalfedges[i].prev].vertex
647
+ _v2 = self._chalfedges[i].vertex
648
+
649
+ if (_v1 == -1) or (_v2 == -1):
650
+ self._chalfedges[i].locally_manifold = -1 # leave unassigned so it does not get included in _update_singular_vertex_locally_manifold()
651
+ else:
652
+ v1 = min(_v1, _v2)
653
+ v2 = max(_v1, _v2)
654
+
655
+ slot_idx = v1*n_vertices + v2
656
+
657
+ if d[slot_idx] <=2:
658
+ self._chalfedges[i].locally_manifold = 1
659
+ else:
660
+ self._chalfedges[i].locally_manifold = 0
661
+
662
+ #PyMem_Free(d)
663
+
664
+ self._singular_edges_valid = 1
665
+
666
+ cdef _compute_raw_vertex_valences(self, np.ndarray valence):
667
+ cdef int n_halfedges = self._halfedges.shape[0]
668
+ cdef int i
669
+
670
+ # count the valence
671
+ for i in range(n_halfedges):
672
+ if self._chalfedges[i].vertex == -1:
673
+ continue
674
+ valence[self._chalfedges[i].vertex] += 1
675
+
676
+ def _calculate_euler_characteristic(self, component=None):
677
+ if component is not None:
678
+ if not self._components_valid:
679
+ self.find_connected_components()
680
+ self._components_valid = 1
681
+
682
+ faces = self._faces['halfedge'][self._halfedges['component'][self._faces['halfedge']] == component]
683
+ else:
684
+ faces = self._faces['halfedge'][self._faces['halfedge'] != -1]
685
+
686
+ v0 = self._halfedges['vertex'][self._halfedges['prev'][faces]]
687
+ v1 = self._halfedges['vertex'][faces]
688
+ v2 = self._halfedges['vertex'][self._halfedges['next'][faces]]
689
+ faces_by_vertex = np.hstack([v0, v1, v2])
690
+
691
+ F = len(faces)
692
+ V = len(set(faces_by_vertex.ravel()))
693
+
694
+ # grab all edges
695
+ edges = np.vstack([faces_by_vertex, np.hstack([v1,v2,v0])]).T
696
+
697
+ # Sort lo->hi
698
+ sorted_edges = np.sort(edges, axis=1)
699
+
700
+ # find the number of unique elements
701
+ E = len(np.unique(sorted_edges,axis=0))
702
+
703
+ chi = V - E + F
704
+
705
+ return chi
706
+
707
+ cdef _set_chalfedges(self, np.ndarray halfedges):
708
+ self._chalfedges = <halfedge_t *> np.PyArray_DATA(halfedges)
709
+ self._n_edges = np.PyArray_DIM(halfedges, 0)
710
+
711
+ cdef _set_cfaces(self, np.ndarray faces):
712
+ self._cfaces = <face_d *> np.PyArray_DATA(faces)
713
+ self._n_faces = np.PyArray_DIM(faces, 0)
714
+
715
+ cdef _set_cvertices(self, np.ndarray vertices):
716
+ self._cvertices = <vertex_d *> np.PyArray_DATA(vertices)
717
+ self._n_vertices = np.PyArray_DIM(vertices, 0)
718
+
719
+ def _initialize_halfedges(self, vertices, faces):
720
+ """
721
+ Accepts unordered vertices, indices parameterization of a triangular
722
+ mesh from an STL file and converts to topologically-connected halfedge
723
+ representation.
724
+
725
+ Parameters
726
+ ----------
727
+ vertices : np.array
728
+ N x 3 array of Euclidean points.
729
+ faces : np .array
730
+ M x 3 array of vertex indices indicating triangle connectivity.
731
+ Expects STL redundancy.
732
+ """
733
+ if vertices is not None and faces is not None:
734
+ # Unordered halfedges
735
+ edges = np.vstack([faces[:,[0,1]], faces[:,[1,2]], faces[:,[2,0]]])
736
+
737
+ # Now order them...
738
+ n_faces = len(faces)
739
+ n_edges = len(edges)
740
+ j = np.arange(n_faces)
741
+
742
+ self._halfedges = np.zeros(n_edges, dtype=HALFEDGE_DTYPE)# .view(np.recarray)
743
+ self._set_chalfedges(self._halfedges)
744
+
745
+ self._halfedges[:] = -1 # initialize everything to -1 to start with
746
+
747
+ self._halfedges['vertex'] = edges[:, 1]
748
+
749
+ self._faces = np.zeros(n_faces, dtype=FACE_DTYPE)
750
+ # Flatten to address cython view problem
751
+ _flat_faces = self._faces.view(FACE_DTYPE2)
752
+ # Set up c views to arrays
753
+ self._set_cfaces(_flat_faces)
754
+ self._faces[:] = -1 # initialize everything to -1 to start with
755
+
756
+ # Sort the edges lo->hi so we can arrange them uniquely
757
+ # Convert to list of tuples for use with dictionary
758
+ edges_packed = [tuple(e) for e in np.sort(edges, axis=1)]
759
+
760
+
761
+ #print('iterating edges')
762
+ # Use a dictionary to keep track of which edges are already assigned twins
763
+ d = {}
764
+ for i, e in enumerate(edges_packed):
765
+ if e in d:
766
+ idx = d.pop(e)
767
+ if self._halfedges['vertex'][idx] == self._halfedges['vertex'][i]:
768
+ # Don't assign a halfedge to multivalent edges. Note this doesn't
769
+ # account for all multivalent edges, but does prevent double-
770
+ # assignment of twins.
771
+ continue
772
+
773
+ self._halfedges['twin'][idx] = i
774
+ self._halfedges['twin'][i] = idx
775
+ else:
776
+ d[e] = i
777
+
778
+ self._halfedges['face'] = np.hstack([j, j, j])
779
+ self._halfedges['next'] = np.hstack([j+n_faces, j+2*n_faces, j])
780
+ self._halfedges['prev'] = np.hstack([j+2*n_faces, j, j+n_faces])
781
+ self._halfedges['length'] = np.linalg.norm(self._vertices['position'][self._halfedges['vertex']] - self._vertices['position'][self._halfedges['vertex'][self._halfedges['prev']]], axis=1)
782
+
783
+ # Set the vertex halfedges (multiassignment, but should be fine)
784
+ self._vertices['halfedge'][self._halfedges['vertex']] = self._halfedges['next']
785
+
786
+ self._faces['halfedge'] = j # Faces are defined by associated halfedges
787
+ else:
788
+ raise ValueError('Mesh does not contain vertices and faces.')
789
+
790
+ def build_from_verts_faces(self, vertices, faces, clear=False):
791
+ """
792
+ Construct mesh from vertices and faces.
793
+ """
794
+ self._vertices = np.zeros(vertices.shape[0], dtype=VERTEX_DTYPE)
795
+ # Flatten to address cython view problem
796
+ _flat_vertices = self._vertices.view(VERTEX_DTYPE2)
797
+ # Set up c views to arrays
798
+ #print self._flat_vertices.shape
799
+ self._set_cvertices(_flat_vertices)
800
+ self._vertices[:] = -1 # initialize everything to -1 to start with
801
+ self._vertices['position'] = vertices
802
+ self._vertex_vacancies = []
803
+
804
+ self._faces = None # Contains a pointer to one halfedge associated with each face
805
+ self._face_vacancies = []
806
+
807
+ # Halfedges
808
+ self._halfedges = None
809
+ self._halfedge_vacancies = []
810
+
811
+ print('initializing halfedges ...')
812
+ print('vertices.shape = %s, faces.shape = %s' % (vertices.shape, faces.shape))
813
+ if vertices.shape[0] >= MAX_VERTEX_COUNT:
814
+ raise RuntimeError('Maximum vertex count is %d, mesh has %d' % (MAX_VERTEX_COUNT, vertices.shape[0]))
815
+ self._initialize_halfedges(vertices, faces)
816
+ print('done initializing halfedges')
817
+
818
+ if clear:
819
+ self._invalidate_cached_properties()
820
+ self._update_all_face_normals()
821
+ self._update_all_vertex_neighbours()
822
+
823
+ def _invalidate_cached_properties(self):
824
+ """
825
+ Invalidate properties which have been cached and will now need to be recomputed due to changes in mesh
826
+ """
827
+ self._faces_by_vertex = None
828
+ self._manifold = 0
829
+ # self._singular_edges = None
830
+ # self._singular_vertices = None
831
+ self._singular_edges_valid = 0
832
+ self._singular_vertices_valid = 0
833
+ self._components_valid = 0
834
+ self._face_normals_valid = 0
835
+ self._vertex_normals_valid = 0
836
+ self._H = None
837
+ self._K = None
838
+ #self._E = None
839
+ #self._pE = None
840
+
841
+ @cython.boundscheck(False) # Deactivate bounds checking
842
+ @cython.wraparound(False)
843
+ def _update_face_normals(self, f_idxs):
844
+ """
845
+ Recompute len(f_idxs) face normals.
846
+
847
+ Parameters
848
+ ----------
849
+ f_idxs : list or np.array
850
+ List of face indices to recompute.
851
+ """
852
+
853
+ if isinstance(f_idxs, list):
854
+ f_idxs = np.int32(f_idxs)
855
+ #triangle_mesh_utils.c_update_face_normals(f_idxs, self._halfedges, self._vertices, self._faces)
856
+ cdef int32_t [:] idxs = f_idxs
857
+
858
+ _update_face_normals(&(idxs[0]), self._chalfedges, <vertex_t *> self._cvertices, <face_t *> self._cfaces, idxs.shape[0])
859
+
860
+ def _update_vertex_neighbors(self, v_idxs):
861
+ """
862
+ Recalculate len(v_idxs) vertex neighbors/normals.
863
+
864
+ Parameters
865
+ ----------
866
+ v_idxs : list or np.array
867
+ List of vertex indicies indicating which vertices to update.
868
+ """
869
+
870
+ if isinstance(v_idxs, list):
871
+ v_idxs = np.int32(v_idxs)
872
+ triangle_mesh_utils.c_update_vertex_neighbors(v_idxs, self._halfedges, self._vertices, self._faces)
873
+
874
+ def _resize(self, vec, axis=0, return_orig=False, skip_entries=False, key=None, new_items=1):
875
+ """
876
+ Increase the size of an input vector 1.5x, keeping all active data.
877
+
878
+ Parameters
879
+ ---------
880
+ vec : np.array
881
+ Vector to expand, -1 stored in unused entries.
882
+ axis : int
883
+ Axis (0 or 1) along which to resize vector (only works for 2D
884
+ arrays).
885
+ return_orig : bool
886
+ Return indices of non-negative-one entries in the array prior
887
+ to resize.
888
+ key : string
889
+ Optional array key on which to check for -1 value.
890
+ """
891
+
892
+ if (axis > 1) or (len(vec.shape) > 2):
893
+ raise NotImplementedError('This function only works for 2D arrays along axes 0 and 1.')
894
+
895
+ # Increase the size 1.5x along the axis.
896
+ dt = vec.dtype
897
+ new_size = np.array(vec.shape)
898
+ new_size[axis] = int(max(1.5*new_size[axis], new_size[axis]+ new_items + 1) + 0.5)
899
+ new_size = tuple(new_size)
900
+
901
+ # Allocate memory for new array
902
+ new_vec = np.empty(new_size, dtype=dt)
903
+
904
+ # Assign values to the new array
905
+ new_vec[:] = -1
906
+
907
+ if skip_entries:
908
+ # Check for invalid entries
909
+ if key:
910
+ copy_mask = (vec[key] != 1)
911
+ else:
912
+ copy_mask = (vec != -1)
913
+
914
+ if len(copy_mask.shape) > 1:
915
+ copy_mask = np.any(copy_mask, axis=(1-axis))
916
+ copy_size = np.flatnonzero(copy_mask).size
917
+ else:
918
+ copy_size = np.flatnonzero(copy_mask).size
919
+
920
+ if axis:
921
+ new_vec[:,:copy_size] = vec[:, copy_mask]
922
+ else:
923
+ new_vec[:copy_size] = vec[copy_mask]
924
+
925
+ # Return positions of non-negative-1 entries prior to re-ordering
926
+ if return_orig:
927
+ _orig = np.where(copy_mask)[0]
928
+ # Return empty list if original values are all at the same place
929
+ if np.all(_orig == np.arange(len(_orig))):
930
+ _orig = []
931
+ return new_vec, _orig
932
+
933
+ return new_vec
934
+ else:
935
+ # We're not skipping entries
936
+ if axis:
937
+ copy_size = vec.shape[1]
938
+ new_vec[:,:copy_size] = vec
939
+ else:
940
+ copy_size = vec.shape[0]
941
+ new_vec[:copy_size] = vec
942
+
943
+ if return_orig:
944
+ return new_vec, []
945
+
946
+ # Update array
947
+ return new_vec
948
+
949
+ def calculate_curvatures(self):
950
+ # TODO - compare / consolidate with ch-shrinkwrap version
951
+ dN = 0.1
952
+ self._H = np.zeros(self._vertices.shape[0])
953
+ self._K = np.zeros(self._vertices.shape[0])
954
+ I = np.eye(3)
955
+ areas = np.zeros(self._vertices.shape[0])
956
+ for iv in range(self._vertices.shape[0]):
957
+ if self._vertices['halfedge'][iv] == -1:
958
+ continue
959
+
960
+ # Vertex and its normal
961
+ vi = self._vertices['position'][iv,:]
962
+ Nvi = self._vertices['normal'][iv,:]
963
+
964
+ p = I - Nvi[:,None]*Nvi[None,:] # np.outer(Nvi, Nvi)
965
+
966
+ # vertex nearest neighbors
967
+ neighbors = self._vertices['neighbors'][iv]
968
+ neighbor_mask = (neighbors != -1)
969
+ neighbor_vertices = self._halfedges['vertex'][neighbors]
970
+ vjs = self._vertices['position'][neighbor_vertices[neighbor_mask]]
971
+
972
+ # Neighbor vectors & displaced neighbor tangents
973
+ dvs = vjs - vi[None,:]
974
+
975
+ # radial weighting
976
+ r_sum = np.sum(1./np.sqrt((dvs*dvs).sum(1)))
977
+
978
+ # Norms
979
+ dvs_norm = np.sqrt((dvs*dvs).sum(1))
980
+
981
+ # Hats
982
+ dvs_hat = dvs/dvs_norm[:,None]
983
+
984
+ # Tangents
985
+ T_thetas = np.dot(p,-dvs.T).T
986
+ Tijs = T_thetas/np.sqrt((T_thetas*T_thetas).sum(1)[:,None])
987
+ Tijs[np.sum(T_thetas,axis=1) == 0, :] = 0
988
+
989
+ # Edge normals subtracted from vertex normals
990
+ Ni_diffs = np.sqrt(2. - 2.*np.sqrt(1.-((Nvi[None,:]*dvs_hat).sum(1))**2))
991
+
992
+ k = 2.*np.sign((Nvi[None,:]*(-dvs)).sum(1))*Ni_diffs/dvs_norm
993
+ w = (1./dvs_norm)/r_sum
994
+
995
+ Mvi = (w[None,:,None]*k[None,:,None]*Tijs.T[:,:,None]*Tijs[None,:,:]).sum(axis=1)
996
+
997
+ # Solve the eigenproblem in closed form
998
+ m00 = Mvi[0,0]
999
+ m01 = Mvi[0,1]
1000
+ m02 = Mvi[0,2]
1001
+ m11 = Mvi[1,1]
1002
+ m12 = Mvi[1,2]
1003
+ m22 = Mvi[2,2]
1004
+
1005
+ # Here we use the fact that Mvi is symnmetric and we know
1006
+ # one of the eigenvalues must be 0
1007
+ p = -m00*m11 - m00*m22 + m01*m01 + m02*m02 - m11*m22 + m12*m12
1008
+ q = m00 + m11 + m22
1009
+ r = np.sqrt(4*p + q*q)
1010
+
1011
+ # Eigenvalues
1012
+ l1 = 0.5*(q-r)
1013
+ l2 = 0.5*(q+r)
1014
+
1015
+ k_1 = 3.*l1 - l2 #e[0] - e[1]
1016
+ k_2 = 3.*l2 - l1 #e[1] - e[0]
1017
+ self._H[iv] = 0.5*(k_1 + k_2)
1018
+ self._K[iv] = k_1*k_2
1019
+
1020
+ if self.smooth_curvature:
1021
+ self._H = self.smooth_per_vertex_data(self._H)
1022
+ self._K = self.smooth_per_vertex_data(self._K)
1023
+
1024
+ def smooth_per_vertex_data(self, data):
1025
+ # replace a vertex value with the average of that value and it's neighbours. TODO - add some form of weighting
1026
+ out = np.zeros_like(data)
1027
+ for iv in range(self._vertices.shape[0]):
1028
+ # Vertex position
1029
+ #vi = self._vertices['position'][iv,:]
1030
+
1031
+ # vertex nearest neighbors
1032
+ neighbors = self._vertices['neighbors'][iv]
1033
+ neighbor_vertices = self._halfedges['vertex'][neighbors[neighbors != -1]]
1034
+
1035
+ #vjs = self._vertices['position'][neighbor_vertices]
1036
+
1037
+ #TODO add weightings ...
1038
+ out[iv] = (data[iv] + data[neighbor_vertices].sum())/float(1 + len(neighbor_vertices))
1039
+
1040
+ return out
1041
+
1042
+
1043
+ cdef bint _check_neighbour_twins(self, int vertex_id):
1044
+ cdef int i
1045
+ cdef int32_t *neighbours = &(self._cvertices[vertex_id].neighbor0)
1046
+ for i in range(NEIGHBORSIZE):
1047
+ nn = neighbours[i]
1048
+ if (nn != -1) and (self._chalfedges[nn].twin == -1):
1049
+ return 0
1050
+
1051
+ return 1
1052
+
1053
+ cdef int _face_delete(self, int32_t _edge):
1054
+ """
1055
+ Delete a face defined by the halfedge _edge.
1056
+
1057
+ Note that this does not account for adjusting halfedges, twins.
1058
+
1059
+ Parameters
1060
+ ----------
1061
+ edge : int
1062
+ One self._halfedge index of the edge defining a face to delete.
1063
+ """
1064
+ cdef halfedge_t *curr_edge
1065
+
1066
+ if _edge == -1:
1067
+ return 0
1068
+
1069
+ curr_edge = &self._chalfedges[_edge]
1070
+ if curr_edge.vertex == -1:
1071
+ return 0
1072
+
1073
+ self._cfaces[curr_edge.face].halfedge = -1
1074
+ self._cfaces[curr_edge.face].normal0 = -1
1075
+ self._cfaces[curr_edge.face].normal1 = -1
1076
+ self._cfaces[curr_edge.face].normal2 = -1
1077
+ self._cfaces[curr_edge.face].area = -1
1078
+ self._cfaces[curr_edge.face].component = -1
1079
+
1080
+ self._face_vacancies.append(curr_edge.face)
1081
+
1082
+ self._edge_delete(curr_edge.next)
1083
+ self._edge_delete(curr_edge.prev)
1084
+ self._edge_delete(_edge)
1085
+
1086
+ self._invalidate_cached_properties()
1087
+
1088
+ return 1
1089
+
1090
+ cdef int _edge_delete(self, int32_t _edge):
1091
+ """
1092
+ Delete edge _edge in place from self._halfedges.
1093
+
1094
+ Note that this does not account for adjusting halfedges, twins.
1095
+
1096
+ Parameters
1097
+ ----------
1098
+ edge : int
1099
+ One self._halfedge index of the edge to delete.
1100
+ """
1101
+ if (_edge == -1):
1102
+ return 0
1103
+
1104
+ self._chalfedges[_edge].vertex = -1
1105
+ self._chalfedges[_edge].face = -1
1106
+ self._chalfedges[_edge].twin = -1
1107
+ self._chalfedges[_edge].next = -1
1108
+ self._chalfedges[_edge].prev = -1
1109
+ self._chalfedges[_edge].length = -1
1110
+ self._chalfedges[_edge].component = -1
1111
+ self._chalfedges[_edge].locally_manifold = -1
1112
+
1113
+ self._halfedge_vacancies.append(_edge)
1114
+
1115
+ self._invalidate_cached_properties()
1116
+
1117
+ return 1
1118
+
1119
+ cdef int _vertex_delete(self, int32_t v_idx):
1120
+ if v_idx == -1:
1121
+ return 0
1122
+
1123
+ self._cvertices[v_idx].position0 = -1
1124
+ self._cvertices[v_idx].position1 = -1
1125
+ self._cvertices[v_idx].position2 = -1
1126
+ self._cvertices[v_idx].normal0 = -1
1127
+ self._cvertices[v_idx].normal1 = -1
1128
+ self._cvertices[v_idx].normal2 = -1
1129
+ self._cvertices[v_idx].halfedge = -1
1130
+ self._cvertices[v_idx].valence = -1
1131
+ self._cvertices[v_idx].neighbor0 = -1
1132
+ self._cvertices[v_idx].neighbor1 = -1
1133
+ self._cvertices[v_idx].neighbor2 = -1
1134
+ self._cvertices[v_idx].neighbor3 = -1
1135
+ self._cvertices[v_idx].neighbor4 = -1
1136
+ self._cvertices[v_idx].neighbor5 = -1
1137
+ self._cvertices[v_idx].neighbor6 = -1
1138
+ self._cvertices[v_idx].neighbor7 = -1
1139
+ self._cvertices[v_idx].neighbor8 = -1
1140
+ self._cvertices[v_idx].neighbor9 = -1
1141
+ self._cvertices[v_idx].neighbor10 = -1
1142
+ self._cvertices[v_idx].neighbor11 = -1
1143
+ self._cvertices[v_idx].neighbor12 = -1
1144
+ self._cvertices[v_idx].neighbor13 = -1
1145
+ self._cvertices[v_idx].neighbor14 = -1
1146
+ self._cvertices[v_idx].neighbor15 = -1
1147
+ self._cvertices[v_idx].neighbor16 = -1
1148
+ self._cvertices[v_idx].neighbor17 = -1
1149
+ self._cvertices[v_idx].neighbor18 = -1
1150
+ self._cvertices[v_idx].neighbor19 = -1
1151
+ self._cvertices[v_idx].component = -1
1152
+ self._cvertices[v_idx].locally_manifold = -1
1153
+
1154
+ self._vertex_vacancies.append(v_idx)
1155
+
1156
+ self._invalidate_cached_properties()
1157
+
1158
+ return 1
1159
+
1160
+ def _get_insertion_slots(self, int n_slots, el_arr, el_vacancies, key_idx, compact=False):
1161
+ """ get an empty slot to insert into, resizing if needed"""
1162
+
1163
+ if len(el_vacancies) <= n_slots:
1164
+ # not enough vacant slots, resize
1165
+ key = __insertion_keys[key_idx]
1166
+ el_arr = self._resize(el_arr, skip_entries=compact, key=key, new_items=n_slots)
1167
+ #TODO - invalidate neighbours, vertex_halfedges etc ???
1168
+
1169
+ # NOTE: If we search by a different key next time, el_vacancies will
1170
+ # still contain the vacant positions when searching on the previous key.
1171
+ el_vacancies = self._update_vacancies(el_arr, key)
1172
+
1173
+ idx = el_vacancies[-n_slots:]
1174
+ el_vacancies = el_vacancies[:-n_slots]
1175
+
1176
+ if idx == -1:
1177
+ raise ValueError('Index cannot be -1.')
1178
+
1179
+ return idx, el_arr, el_vacancies
1180
+
1181
+ def _update_vacancies(self, el_arr, key):
1182
+ if len(el_arr[key].shape) > 1:
1183
+ # NOTE: If we search on self._vertices['position'] and one position is [-1,-1,-1],
1184
+ # which is not unreasonable, this will replace that vertex. Hence we provide the
1185
+ # option to search and insert on different keys.
1186
+ el_vacancies = [int(x) for x in np.argwhere(np.all(el_arr[key] == -1, axis=1))]
1187
+ else:
1188
+ # el_vacancies = [int(x) for x in np.flatnonzero(el_arr[key] == -1)]
1189
+ el_vacancies = np.flatnonzero(el_arr[key] == -1).tolist()
1190
+
1191
+ return el_vacancies
1192
+
1193
+ def _update_all_vacancies(self):
1194
+ self._halfedge_vacancies = self._update_vacancies(self._halfedges, __insertion_keys[INSERTION_KEY_VERTEX])
1195
+ self._vertex_vacancies = self._update_vacancies(self._vertices, __insertion_keys[INSERTION_KEY_HALFEDGE])
1196
+ self._face_vacancies = self._update_vacancies(self._faces, __insertion_keys[INSERTION_KEY_HALFEDGE])
1197
+
1198
+
1199
+
1200
+ cdef _populate_edge(self, int idx, int vertex, int prev=-1, int next=-1, int face=-1, int twin=-1, int locally_manifold=1):
1201
+ # TODO - make a macro??
1202
+ self._chalfedges[idx].vertex = vertex
1203
+ self._chalfedges[idx].prev = prev
1204
+ self._chalfedges[idx].next = next
1205
+ self._chalfedges[idx].face = face
1206
+ self._chalfedges[idx].twin = twin
1207
+ self._chalfedges[idx].locally_manifold = locally_manifold
1208
+
1209
+ def new_edges(self, int n_edges):
1210
+ idx, self._halfedges, self._halfedge_vacancies = self._get_insertion_slots(n_edges, self._halfedges, self._halfedge_vacancies, key_idx=INSERTION_KEY_VERTEX)
1211
+ self._set_chalfedges(self._halfedges)
1212
+
1213
+ return np.array(idx, np.int32)
1214
+
1215
+ def new_vertices(self, int n_vertices):
1216
+ idx, self._vertices, self._vertex_vacancies = self._get_insertion_slots(n_vertices, self._vertices, self._vertex_vacancies, key_idx=INSERTION_KEY_HALFEDGE)
1217
+ self._set_cvertices(self._vertices)
1218
+
1219
+ self._invalidate_cached_properties()
1220
+
1221
+ return np.array(idx, np.int32)
1222
+
1223
+ def new_faces(self, int n_faces):
1224
+ idx, self._faces, self._face_vacancies = self._get_insertion_slots(n_faces, self._faces, self._face_vacancies, key_idx=INSERTION_KEY_HALFEDGE)
1225
+ self._set_cfaces(self._faces)
1226
+
1227
+ return np.array(idx, np.int32)
1228
+
1229
+ cpdef int edge_flip(self, int32_t _curr, bint live_update=1):
1230
+ cdef int ret
1231
+ ret = remesh_edge_flip(self._chalfedges, <vertex_t *>&self._cvertices[0], <face_t *> &self._cfaces[0], _curr, self._n_edges, live_update)
1232
+ self._invalidate_cached_properties()
1233
+ return ret
1234
+
1235
+ def split_edges(self, float split_threshold):
1236
+ cdef int split_count = 0
1237
+ cdef int i
1238
+ cdef int e
1239
+ cdef int n_halfedges = self._halfedges.shape[0]
1240
+ cdef float *n1
1241
+ cdef float *n2
1242
+ cdef float nd
1243
+ cdef int n_edge_idx, n_face_idx, n_vertex_idx
1244
+ #self.face_normals
1245
+ #self.vertex_normals
1246
+
1247
+ cdef int* edges_to_split = <int*>PyMem_Malloc(n_halfedges*sizeof(int))
1248
+ if not edges_to_split:
1249
+ raise MemoryError()
1250
+ cdef int* twin_split = <int*>PyMem_Malloc(n_halfedges*sizeof(int))
1251
+ if not twin_split:
1252
+ raise MemoryError()
1253
+
1254
+ for i in range(n_halfedges):
1255
+ twin_split[i] = 0
1256
+
1257
+ for i in range(n_halfedges):
1258
+ if (not twin_split[i]) and (self._chalfedges[i].vertex != -1): # and (self._chalfedges[i].length > split_threshold):
1259
+ # n1 = &self._cvertices[self._chalfedges[i].vertex].normal0
1260
+ # n2 = &self._cvertices[self._chalfedges[self._chalfedges[i].prev].vertex].normal0
1261
+ # nd = n1[0]*n2[0] + n1[1]*n2[1] + n1[2]*n2[2]
1262
+ nd = 1.0
1263
+ if (self._chalfedges[i].length > (nd*split_threshold)):
1264
+ if self._chalfedges[i].twin != -1:
1265
+ twin_split[self._chalfedges[i].twin] = 1
1266
+ edges_to_split[split_count] = i
1267
+ split_count += 1
1268
+
1269
+ n_edges = self.new_edges(int(split_count*6))
1270
+ n_edge_idx = 0
1271
+ n_faces = self.new_faces(int(split_count*2))
1272
+ n_face_idx = 0
1273
+ n_vertices = self.new_vertices(int(split_count))
1274
+ n_vertex_idx = 0
1275
+ #print(self._halfedges[n_edges])
1276
+
1277
+ for i in range(split_count):
1278
+ e = edges_to_split[i]
1279
+ #print(i, e, n_edge_idx, n_edges)
1280
+ # self.edge_split_2(e,
1281
+ # <int32_t *> np.PyArray_DATA(n_edges),
1282
+ # <int32_t *> np.PyArray_DATA(n_vertices),
1283
+ # <int32_t *> np.PyArray_DATA(n_faces),
1284
+ # n_edge_idx, n_vertex_idx, n_face_idx)
1285
+
1286
+ remesh_split_edge(self._chalfedges, n_halfedges, <vertex_t *> self._cvertices, <face_t *>self._cfaces, int(e),
1287
+ <int32_t *> np.PyArray_DATA(n_edges),
1288
+ <int32_t *> np.PyArray_DATA(n_vertices),
1289
+ <int32_t *> np.PyArray_DATA(n_faces),
1290
+ n_edge_idx, n_vertex_idx, n_face_idx, 1, 0)
1291
+ #self.edge_split(e)
1292
+ n_edge_idx += 6
1293
+ n_face_idx += 2
1294
+ n_vertex_idx += 1
1295
+
1296
+ PyMem_Free(edges_to_split)
1297
+ PyMem_Free(twin_split)
1298
+
1299
+ #print('Split count: %d' % (split_count))
1300
+ return split_count
1301
+
1302
+ def collapse_edges(self, float collapse_threshold):
1303
+ cdef int collapse_count = 0
1304
+ cdef int collapse_fails = 0
1305
+ cdef int i
1306
+ cdef int n_halfedges = self._halfedges.shape[0]
1307
+ cdef float *n1
1308
+ cdef float *n2
1309
+ cdef float nd
1310
+
1311
+ #find which vertices are locally manifold
1312
+ self._update_vertex_locally_manifold()
1313
+
1314
+ for i in range(n_halfedges):
1315
+ if (self._chalfedges[i].vertex != -1) and (self._chalfedges[i].length < collapse_threshold):
1316
+ n1 = &self._cvertices[self._chalfedges[i].vertex].normal0
1317
+ n2 = &self._cvertices[self._chalfedges[self._chalfedges[i].prev].vertex].normal0
1318
+ # dot product of the normals
1319
+ nd = n1[0]*n2[0] + n1[1]*n2[1] + n1[2]*n2[2]
1320
+ if (self._chalfedges[i].length < nd*nd*collapse_threshold):
1321
+ #collapse_ret = self.edge_collapse(i)
1322
+ collapse_ret = remesh_edge_collapse(self._chalfedges, self._n_edges, <vertex_t *> self._cvertices, <face_t*> self._cfaces, i, 1)
1323
+ collapse_count += collapse_ret
1324
+ collapse_fails += (1-collapse_ret)
1325
+
1326
+ self._update_all_vacancies() #we might have deleted edges, vertices, etc ....
1327
+ self._invalidate_cached_properties()
1328
+ #print('Collapse count: ' + str(collapse_count) + '[' + str(collapse_fails) +' failed]')
1329
+
1330
+ return collapse_count
1331
+
1332
+ cpdef int regularize(self):
1333
+ """
1334
+ Adjust vertices so they tend toward valence VALENCE
1335
+ (or BOUNDARY_VALENCE for boundaries).
1336
+ """
1337
+
1338
+ cdef int i, flip_count, target_valence, r, failed_flip_count
1339
+ cdef halfedge_t *curr_edge
1340
+ cdef halfedge_t *twin_edge
1341
+ cdef int32_t _twin
1342
+ cdef int v1, v2, v3, v4, score_post, score_pre
1343
+
1344
+ flip_count = 0
1345
+ failed_flip_count = 0
1346
+
1347
+ # Do a single pass over all active edges and flip them if the flip minimizes the deviation of vertex valences
1348
+ # for i in np.arange(len(self._halfedges['vertex'])):
1349
+ # if (self._halfedges['vertex'][i] < 0):
1350
+ # continue
1351
+ for i in range(self._halfedges.shape[0]):# np.flatnonzero(self._halfedges['vertex'] != -1).astype(np.int32):
1352
+ curr_edge = &self._chalfedges[i]
1353
+
1354
+ if curr_edge.vertex == -1:
1355
+ continue
1356
+
1357
+ _twin = curr_edge.twin
1358
+
1359
+ v2, v4 = 0, 0
1360
+ target_valence = VALENCE
1361
+ if _twin == -1:
1362
+ # boundary
1363
+ target_valence = BOUNDARY_VALENCE
1364
+ else:
1365
+ twin_edge = &self._chalfedges[_twin]
1366
+ v2 = self._cvertices[twin_edge.vertex].valence - target_valence # pre-flip
1367
+ v4 = self._cvertices[self._chalfedges[twin_edge.next].vertex].valence - target_valence # post-flip
1368
+
1369
+ v1 = self._cvertices[curr_edge.vertex].valence - target_valence # pre-flip
1370
+ v3 = self._cvertices[self._chalfedges[curr_edge.next].vertex].valence - target_valence # post-flip
1371
+
1372
+ # Check valence deviation from VALENCE (or
1373
+ # BOUNDARY_VALENCE for boundaries) pre- and post-flip
1374
+ #score_pre = abs([v1,v2,v3,v4]).sum()
1375
+ #score_post = np.abs([v1-1,v2-1,v3+1,v4+1]).sum()
1376
+
1377
+ score_pre = abs(v1) + abs(v2) + abs(v3) + abs(v4)
1378
+ score_post = abs(v1-1) + abs(v2-1) + abs(v3+1) + abs(v4+1)
1379
+
1380
+ if score_post < score_pre:
1381
+ # Flip minimizes deviation of vertex valences from VALENCE (or
1382
+ # BOUNDARY_VALENCE for boundaries)
1383
+ r = self.edge_flip(i)
1384
+ flip_count += r
1385
+ failed_flip_count += (1-r)
1386
+
1387
+ #print('Flip count: %d [%d failed]' % (flip_count, failed_flip_count))
1388
+ return flip_count
1389
+
1390
+ cpdef int relax(self, float l=1, int n=1):
1391
+ return remesh_relax(self._chalfedges, <vertex_t *> self._cvertices, self._n_vertices, <face_t *> self._cfaces, self._n_faces, l, n)
1392
+
1393
+
1394
+ def remesh(self, int n=5, float target_edge_length=-1, float l=0.5, int n_relax=10):
1395
+ """
1396
+ Produce a higher-quality mesh.
1397
+
1398
+ Follows procedure in Botsch and Kobbelt, A Remeshing Approach to
1399
+ Multiresoluton Modeling, Eurographics Symposium on Geometry Processing,
1400
+ 2004.
1401
+
1402
+ Parameters
1403
+ ----------
1404
+ n : int
1405
+ Number of remeshing iterations to apply.
1406
+ target_edge_length : float
1407
+ Target edge length for all edges in the mesh.
1408
+ l : float
1409
+ Relaxation regularization term, used to avoid oscillations.
1410
+ n_relax : int
1411
+ Number of Lloyd relaxation (relax()) iterations to apply
1412
+ per remeshing iteration.
1413
+ """
1414
+ cdef int k, i, split_count, collapse_count, collapse_ret, collapse_fails, n_singular
1415
+ #cdef float target_edge_length
1416
+
1417
+ cdef int n_halfedges = self._halfedges.shape[0]
1418
+ cdef int n_vertices = self._vertices.shape[0]
1419
+
1420
+ if (target_edge_length < 0):
1421
+ # Guess edge_length
1422
+ target_edge_length = np.mean(self._halfedges['length'][self._halfedges['length'] != -1])
1423
+
1424
+ cdef float split_threshold = 1.33*target_edge_length
1425
+ cdef float collapse_threshold = 0.8*target_edge_length
1426
+
1427
+ # modify cutoffs so we don't constantly undo ourselves ...
1428
+ # TOD - should we regularize between split and collapse?
1429
+ #cdef float split_threshold = 1.41*target_edge_length
1430
+ #cdef float collapse_threshold = 0.7*target_edge_length
1431
+
1432
+ #self.regularize()
1433
+
1434
+ self._singular_edges_valid = 0
1435
+ n_singular = self.singular_edges.shape[0]
1436
+ #f"# singular: {n_singular}")
1437
+
1438
+ for k in range(n):
1439
+ # 1. Split all edges longer than (4/3)*target_edge_length at their midpoint.
1440
+ # split_count = 0
1441
+ # for i in range(n_halfedges):
1442
+ # if (self._chalfedges[i].vertex != -1) and (self._chalfedges[i].length > split_threshold):
1443
+ # self.edge_split(i)
1444
+ # split_count += 1
1445
+ # print('Split count: %d' % (split_count))
1446
+
1447
+ ct = self.split_edges(split_threshold)
1448
+ #while (ct > 0):
1449
+ # # Keep spliting until we can't do any more splits (note - may be more effective to just run everything a couple more times)
1450
+ # ct = self.split_edges(split_threshold)
1451
+
1452
+ self._singular_edges_valid = 0
1453
+ n_singular = self.singular_edges.shape[0]
1454
+ #print(f"# singular: {n_singular}")
1455
+
1456
+ ct = self.regularize()
1457
+ while (ct > 0):
1458
+ # Keep flipping until we can't do any more splits (note - may be more effective to just run everything a couple more times)
1459
+ ct = self.regularize()
1460
+
1461
+ self._singular_edges_valid = 0
1462
+ n_singular = self.singular_edges.shape[0]
1463
+ #print(f"# singular: {n_singular}")
1464
+
1465
+ # 2. Collapse all edges shorter than (4/5)*target_edge_length to their midpoint.
1466
+ # collapse_count = 0
1467
+ # collapse_fails = 0
1468
+ #
1469
+ # #find which vertices are locally manifold
1470
+ # # TODO - move this to a helper function
1471
+ # self._vertices['locally_manifold'] = 1
1472
+ # self._vertices['locally_manifold'][self._halfedges['vertex'][self._halfedges['twin'] == -1]] = 0
1473
+ #
1474
+ # for i in range(n_halfedges):
1475
+ # if (self._chalfedges[i].vertex != -1) and (self._chalfedges[i].length < collapse_threshold):
1476
+ # collapse_ret = self.edge_collapse(i)
1477
+ # collapse_count += collapse_ret
1478
+ # collapse_fails += (1-collapse_ret)
1479
+ # print('Collapse count: ' + str(collapse_count) + '[' + str(collapse_fails) +' failed]')
1480
+
1481
+ ct = self.collapse_edges(collapse_threshold)
1482
+
1483
+ self._singular_edges_valid = 0
1484
+ n_singular = self.singular_edges.shape[0]
1485
+ #print(f"# singular: {n_singular}")
1486
+ # while (ct > 0):
1487
+ # # Keep collapsing until we can't do any more splits (note - may be more effective to just run everything a couple more times)
1488
+ # ct = self.collapse_edges(collapse_threshold)
1489
+
1490
+ # 3. Flip edges in order to minimize deviation from VALENCE.
1491
+
1492
+ # find which vertices are locally manifold
1493
+ self._update_vertex_locally_manifold()
1494
+
1495
+ ct = self.regularize()
1496
+ _n_flip = 0
1497
+ while (ct > 0) and (_n_flip < 20):
1498
+ # Keep flipping until we can't do any more splits (note - may be more effective to just run everything a couple more times)
1499
+ ct = self.regularize()
1500
+ _n_flip += 1
1501
+
1502
+
1503
+ self._singular_edges_valid = 0
1504
+ n_singular = self.singular_edges.shape[0]
1505
+ #print(f"# singular: {n_singular}")
1506
+
1507
+ # 4. Relocate vertices on the surface by tangential smoothing.
1508
+ self.relax(l=l, n=n_relax)
1509
+
1510
+ self._singular_edges_valid = 0
1511
+ n_singular = self.singular_edges.shape[0]
1512
+ #print(f"# singular: {n_singular}")
1513
+
1514
+ # Let's double-check the mesh manifoldness
1515
+ self._manifold = 0
1516
+ self.manifold
1517
+
1518
+ def upsample(self, int n=1):
1519
+ """
1520
+ Upsample the mesh by a factor of 4 (factor of 2 along each axis in the
1521
+ plane of the mesh) and smooth meshes by Loop's 5/8 and 3/8 rule.
1522
+
1523
+ References:
1524
+ 1. C. T. Loop, "Smooth Subdivision surfaces Based on Triangles,"
1525
+ University of Utah, 1987
1526
+ 2. http://462cmu.github.io/asst2_meshedit/, task 4
1527
+
1528
+ Parameters
1529
+ ----------
1530
+ n : int
1531
+ Number of upsampling operations to perform.
1532
+ """
1533
+ cdef int n_edge_idx, n_face_idx, n_vertex_idx, k
1534
+ cdef int n_halfedges = self._halfedges.shape[0]
1535
+
1536
+ for k in range(n):
1537
+ # Reset
1538
+ split_edges = {}
1539
+ self._loop_subdivision_new_vertices = []
1540
+ self._loop_subdivision_flip_edges = []
1541
+ new_vertex_idxs = []
1542
+
1543
+ # Mark the current edges as old
1544
+ edges_to_split = np.where(self._halfedges['vertex'] != -1)[0]
1545
+ split_halfedges = self._halfedges[edges_to_split]
1546
+
1547
+ # Compute new vertex positions for old vertices
1548
+ old_vertex_idxs = split_halfedges['vertex']
1549
+ old_vertices = self._vertices[old_vertex_idxs]
1550
+ old_neighbors = old_vertices['neighbors']
1551
+ neg_mask = (old_neighbors != -1)
1552
+ # alpha is a function of vertex order N that satisifies constraints on equation 3.4 of Loop's thesis
1553
+ alpha = np.tanh(LOOP_ALPHA_FACTOR*old_vertices['valence'])
1554
+ old_vertex_positions = (alpha[:,None])*old_vertices['position'] + ((1-alpha)[:,None])*((1./old_vertices['valence'])[:,None])*np.sum(self._vertices['position'][self._halfedges['vertex'][old_neighbors]]*neg_mask[...,None],axis=1)
1555
+
1556
+ # Compute new vertex positions for new vertices
1557
+ p0 = 0.375*(old_vertices['position'] + self._vertices['position'][self._halfedges['vertex'][split_halfedges['twin']]])
1558
+ p1 = 0.125*(self._vertices['position'][self._halfedges['vertex'][split_halfedges['next']]] + self._vertices['position'][self._halfedges['vertex'][self._halfedges['next'][split_halfedges['twin']]]])
1559
+ new_vertex_positions = p0 + p1
1560
+
1561
+ # 1. Split every edge in the mesh
1562
+ split_count = len(edges_to_split)
1563
+ n_edges = self.new_edges(int(split_count*6))
1564
+ n_edge_idx = 0
1565
+ n_faces = self.new_faces(int(split_count*2))
1566
+ n_face_idx = 0
1567
+ n_vertices = self.new_vertices(int(split_count))
1568
+ n_vertex_idx = 0
1569
+
1570
+ for j, i in enumerate(edges_to_split):
1571
+ if i in split_edges:
1572
+ continue
1573
+
1574
+ split_edges[self._halfedges['twin'][i]] = i
1575
+ new_vertex_idxs.append(j)
1576
+ #self.edge_split(i, upsample=True)
1577
+ # self.edge_split_2(i,
1578
+ # <int32_t *> np.PyArray_DATA(n_edges),
1579
+ # <int32_t *> np.PyArray_DATA(n_vertices),
1580
+ # <int32_t *> np.PyArray_DATA(n_faces),
1581
+ # n_edge_idx, n_vertex_idx, n_face_idx, 1,1)
1582
+ remesh_split_edge(self._chalfedges, n_halfedges, <vertex_t *> self._cvertices, <face_t *>self._cfaces, int(i),
1583
+ <int32_t *> np.PyArray_DATA(n_edges),
1584
+ <int32_t *> np.PyArray_DATA(n_vertices),
1585
+ <int32_t *> np.PyArray_DATA(n_faces),
1586
+ n_edge_idx, n_vertex_idx, n_face_idx, 1, 0)
1587
+
1588
+ n_edge_idx += 6
1589
+ n_face_idx += 2
1590
+ n_vertex_idx += 1
1591
+
1592
+ # 2. Flip any new edge that touches an old vertex and a new vertex
1593
+ for e in self._loop_subdivision_flip_edges:
1594
+ if self._halfedges['vertex'][e] in self._loop_subdivision_new_vertices:
1595
+ continue
1596
+ self.edge_flip(e)
1597
+
1598
+ # Get any boundary vertices
1599
+ boundary_vertex_idxs = self._halfedges['vertex'][(self._halfedges['twin'] == -1)]
1600
+ boundary_vertex_positions = self._vertices['position'][boundary_vertex_idxs]
1601
+
1602
+ # Copy new position values into self._vertices['position']
1603
+ self._vertices['position'][old_vertex_idxs] = old_vertex_positions
1604
+ self._vertices['position'][self._loop_subdivision_new_vertices] = new_vertex_positions[new_vertex_idxs]
1605
+
1606
+ # Restore boundary vertex positions
1607
+ self._vertices['position'][boundary_vertex_idxs] = boundary_vertex_positions
1608
+
1609
+ self._manifold = 0
1610
+
1611
+ def downsample(self, n_triangles=None):
1612
+ """
1613
+ Mesh downsampling via quadratic error metrics.
1614
+
1615
+ Follows procedure in Garland and Heckbert, Surface Simplification Using
1616
+ Quadtratic Error Metrics.
1617
+
1618
+ Parameters
1619
+ ----------
1620
+ n_triangles : int
1621
+ Target number of triangles in the downsampled mesh.
1622
+ """
1623
+
1624
+ import PYME.experimental._treap as treap
1625
+
1626
+ # 1. Compute quadratic matrices for all initial vertices
1627
+ v = self._vertices['position']
1628
+ n = self._vertices['normal']
1629
+ d = (-n*v).sum(1)
1630
+ p = np.vstack([n.T, d]).T # plane
1631
+ vn = self._vertices['neighbors']
1632
+ vn_mask = (vn != -1)
1633
+ pp = p[self._halfedges['vertex'][vn]]*vn_mask[...,None] # neighbor planes
1634
+ Q = (pp[...,None]*pp[:,:,None,:]).sum(1) # per-vertex quadratic
1635
+
1636
+ # 2. Select all valid pairs
1637
+ # For now, operate on edges. In the future we can also operate on any
1638
+ # pair of vertex positions such that || v_1 - v_2 || < t, where v_1,
1639
+ # v_2 \in R^3, and t \in R is a threshold.
1640
+
1641
+ # 3. Compute optimal contraction target and cost of each contraction
1642
+ # NOTE: we do this for all halfedges, empty or not. This saves us from having to search for
1643
+ # unique halfedges later in step 5. Instead, we just ignore -1 values.
1644
+ edges_mask = (self._halfedges['vertex'] != -1)
1645
+ edges = np.vstack([self._halfedges['vertex'], self._halfedges['vertex'][self._halfedges['prev']]]).T
1646
+ # Mask and shift to avoid a copy operation on Q
1647
+ Q_mask = np.ones((4,4))
1648
+ Q_mask[3,:] = 0
1649
+ Q_shift = np.zeros((4,4))
1650
+ Q_shift[3,3] = 1
1651
+ # h is the solution vector for least squares
1652
+ h = np.array([0,0,0,1])
1653
+ # 3a. Compute the optimal contraction target for each valid pair
1654
+ Q_edge = (Q[edges]).sum(1) # Q paired
1655
+ try:
1656
+ vp = (np.linalg.inv(Q_edge*Q_mask[None,...] + Q_shift[None,...])*h).sum(2)
1657
+ except(np.linalg.LinAlgError):
1658
+ vp = 0.5*np.sum(self._vertices['position'][edges],axis=1)
1659
+ vp = np.vstack([vp.T, np.ones(vp.shape[0])]).T
1660
+
1661
+ # 3b. Compute cost of contractions
1662
+ err = ((vp[:,None,:]*Q_edge).sum(2)*vp).sum(1)
1663
+
1664
+ # 4. Place all the pairs in a heap keyed on cost with the minimum cost pair on top
1665
+ cost_heap = treap.Treap()
1666
+ for i in (np.arange(len(edges))[edges_mask]):
1667
+ cost_heap.insert(np.float32(err[i]), np.int32(i))
1668
+
1669
+ # 5. Iteratively remove the pair of least cost from the heap,
1670
+ # contract this pair, and update the costs of all valid
1671
+ # pairs involving the remaining live vertex.
1672
+ n_faces = np.sum(self._faces['halfedge'] != -1)
1673
+ if n_triangles is None:
1674
+ # Assume we want to downsample by a factor of 4
1675
+ n_triangles = int(0.25*n_faces)
1676
+ collapse_tries = int(0.125*(n_faces - n_triangles))
1677
+ while True:
1678
+ if n_faces <= n_triangles:
1679
+ # Sometimes edge_collapse does not collapse the edge
1680
+ # because it would affect the manifoldness of the mesh,
1681
+ # so we need to see how far off we are from the desired
1682
+ # number of triangles n_triangles.
1683
+ tmp = np.sum(self._faces['halfedge'] != -1)
1684
+ if (n_faces == tmp) or (collapse_tries == 0):
1685
+ # We don't want to double check ourselves forever
1686
+ break
1687
+ # Update our estimate and keep refining the mesh
1688
+ n_faces = tmp
1689
+ collapse_tries -= 1
1690
+
1691
+ # Grab the edge with minimum cost
1692
+ _, _edge = cost_heap.pop()
1693
+
1694
+ if _edge == -1:
1695
+ print('Edge was -1???')
1696
+ continue
1697
+
1698
+ # Compute the new quadratic
1699
+ edge = self._halfedges[_edge]
1700
+ # Grab the live and dead vertex
1701
+ _live_vertex = edge['vertex']
1702
+ _dead_vertex = self._halfedges['vertex'][edge['prev']]
1703
+ Q_edge[_edge] = Q[_live_vertex] + Q[_dead_vertex]
1704
+
1705
+ if (_live_vertex == -1) or (_dead_vertex == -1):
1706
+ print('Vertex was -1???')
1707
+ continue
1708
+
1709
+ # Grab all neighboring halfedges
1710
+ nn_live = self._vertices['neighbors'][_live_vertex]
1711
+ nn_live_mask = (nn_live != -1)
1712
+ nn_live_twin = self._halfedges['twin'][nn_live[nn_live_mask]]
1713
+ nn_live_twin_mask = (nn_live_twin != -1)
1714
+ nn_dead = self._vertices['neighbors'][_dead_vertex]
1715
+ nn_dead_mask = (nn_dead != -1)
1716
+ nn_dead_twin = self._halfedges['twin'][nn_dead[nn_dead_mask]]
1717
+ nn_dead_twin_mask = (nn_dead_twin != -1)
1718
+ neighbors = np.hstack([nn_live[nn_live_mask], nn_live_twin[nn_live_twin_mask], nn_dead[nn_dead_mask], nn_dead_twin[nn_dead_twin_mask]])
1719
+
1720
+ # Eliminate edges touching either vertex from the cost heap
1721
+ for neighbor in neighbors:
1722
+ cost_heap.delete(np.int32(neighbor))
1723
+
1724
+ # Collapse the edge
1725
+ #self.edge_collapse(_edge)
1726
+ remesh_edge_collapse(self._chalfedges, self._n_edges, <vertex_t *> self._cvertices, <face_t*> self._cfaces, _edge, 1)
1727
+
1728
+ # Decrement the estimate of the number of triangles in the
1729
+ # mesh accordingly
1730
+ n_faces -= 2
1731
+
1732
+ # Set the quadratic of _live_vertex to the edge quadratic
1733
+ Q[_live_vertex] = Q_edge[_edge]
1734
+ # self._vertices['position'][_live_vertex] = vp[_edge,:3]
1735
+
1736
+ # Grab the new neighbors
1737
+ nn_live = self._vertices['neighbors'][_live_vertex]
1738
+ nn_live_mask = (nn_live != -1)
1739
+ nn_live_twin = self._halfedges['twin'][nn_live[nn_live_mask]]
1740
+ nn_live_twin_mask = (nn_live_twin != -1)
1741
+ neighbors = np.hstack([nn_live[nn_live_mask], nn_live_twin[nn_live_twin_mask]])
1742
+
1743
+ # Update the costs and positions of each of these neighbors and re-insert them into the queue
1744
+ new_edges = np.vstack([self._halfedges['vertex'][neighbors], self._halfedges['vertex'][self._halfedges['prev'][neighbors]]]).T
1745
+ Q_edge[neighbors] = (Q[new_edges]).sum(1)
1746
+
1747
+ try:
1748
+ vp[neighbors] = (np.linalg.inv(Q_edge[neighbors]*Q_mask[None,...] + Q_shift[None,...])*h).sum(2)
1749
+ except(np.linalg.LinAlgError):
1750
+ vp_temp = 0.5*np.sum(self._vertices['position'][new_edges],axis=1)
1751
+ vp[neighbors] = np.vstack([vp_temp.T, np.ones(vp_temp.shape[0])]).T
1752
+
1753
+ err[neighbors] = ((vp[neighbors,None,:]*Q_edge[neighbors]).sum(2)*vp[neighbors]).sum(1)
1754
+
1755
+ for n in neighbors:
1756
+ cost_heap.insert(np.float32(err[n]), np.int32(n))
1757
+
1758
+ self._update_all_vacancies()
1759
+ self._invalidate_cached_properties()
1760
+
1761
+ self._manifold = 0
1762
+
1763
+ def find_connected_components(self):
1764
+ """
1765
+ Label connected components of the mesh.
1766
+ """
1767
+
1768
+ # Set all components to -1 (background)
1769
+ self._halfedges['component'] = -1
1770
+ self._vertices['component'] = -1
1771
+ self._faces['component'] = -1
1772
+
1773
+ # Connect component by vertex
1774
+ component = 0
1775
+ for _vertex in np.arange(self._vertices.shape[0]):
1776
+ curr_vertex = self._vertices[_vertex]
1777
+ if (curr_vertex['halfedge'] == -1) or (curr_vertex['component'] != -1):
1778
+ # We only want to deal with valid, unassigned vertices
1779
+ continue
1780
+
1781
+ # Search vertices contains vertices connected to this vertex.
1782
+ # We want to see if there are other vertices connected to the
1783
+ # search vertices.
1784
+ search_vertices = []
1785
+ self._vertices[_vertex]['component'] = component
1786
+ search_vertices.append(_vertex)
1787
+
1788
+ # Find vertices connected to the search vertices
1789
+ while search_vertices:
1790
+ # Grab the latest search vertex neighbors
1791
+ _v = search_vertices.pop()
1792
+ nn = self._vertices['neighbors'][_v]
1793
+ # Loop over the neighbors
1794
+ for _edge in nn:
1795
+ if _edge == -1:
1796
+ continue
1797
+ # Assign the vertex component label to halfedges and faces
1798
+ # emanating from this vertex
1799
+ curr_edge = self._halfedges[_edge]
1800
+ if self._vertices['component'][curr_edge['vertex']] != component:
1801
+ search_vertices.append(curr_edge['vertex'])
1802
+ self._halfedges['component'][_edge] = component
1803
+ self._halfedges['component'][curr_edge['prev']] = component
1804
+ self._halfedges['component'][curr_edge['next']] = component
1805
+ self._faces['component'][curr_edge['face']] = component
1806
+ self._vertices['component'][curr_edge['vertex']] = component
1807
+
1808
+ # Increment the label as any vertices not included in this
1809
+ # iteration's search will be part of another component.
1810
+ component += 1
1811
+
1812
+ def area(self, component=-1):
1813
+ """
1814
+ Return surface area of the mesh (component) in nm^2.
1815
+ """
1816
+ if component == -1:
1817
+ faces = np.flatnonzero(self._faces['halfedge']!=-1)
1818
+ else:
1819
+ faces = np.flatnonzero(self._faces['component']==component)
1820
+
1821
+ return np.sum(self._faces['area'][faces])
1822
+
1823
+ def volume(self, component=-1):
1824
+ """
1825
+ Return volume of the mesh (component) in nm^3.
1826
+
1827
+ Sum the signed volumes of tetrahedrons formed by the faces and the origin.
1828
+
1829
+ Cha Zhang and Tsuhan Chen. "Efficient Feature Extraction for 2D/3D Objects in Mesh Representation."
1830
+ In Proceedings 2001 International Conference on Image Processing (Cat. No.01CH37205), 2:935–38.
1831
+ Thessaloniki, Greece: IEEE, 2001. https://doi.org/10.1109/ICIP.2001.958278.
1832
+ """
1833
+
1834
+ # TODO?? - more component refactoring - e.g. a component object / class / iterator?
1835
+ if component == -1:
1836
+ faces = np.flatnonzero(self._faces['halfedge']!=-1)
1837
+ else:
1838
+ faces = np.flatnonzero(self._faces['component']==component)
1839
+
1840
+ faces = self._faces['halfedge'][faces]
1841
+ v0 = self._halfedges['vertex'][self._halfedges['prev'][faces]]
1842
+ v1 = self._halfedges['vertex'][faces]
1843
+ v2 = self._halfedges['vertex'][self._halfedges['next'][faces]]
1844
+
1845
+ p0 = self._vertices['position'][v0]
1846
+ p1 = self._vertices['position'][v1]
1847
+ p2 = self._vertices['position'][v2]
1848
+
1849
+ t0 = p2[:,0]*p1[:,1]*p0[:,2]
1850
+ t1 = -p1[:,0]*p2[:,1]*p0[:,2]
1851
+ t2 = -p2[:,0]*p0[:,1]*p1[:,2]
1852
+ t3 = p0[:,0]*p2[:,1]*p1[:,2]
1853
+ t4 = p1[:,0]*p0[:,1]*p2[:,2]
1854
+ t5 = -p0[:,0]*p1[:,1]*p2[:,2]
1855
+
1856
+ volume = (1.0/6.0)*(t0+t1+t2+t3+t4+t5)
1857
+
1858
+ return volume.sum()
1859
+
1860
+ def keep_components_by_volume(self, min_size=0, max_size=1e9):
1861
+ # Find the connected components
1862
+ self.find_connected_components()
1863
+
1864
+ # Get unique component values
1865
+ coms = np.unique(self._faces['component'][self._faces['component']!=-1])
1866
+
1867
+ # Get volumes in nanometers^3
1868
+ sizes = np.array([self.volume(c) for c in coms])
1869
+
1870
+ # keep components within size range
1871
+ kept_coms = coms[(sizes > min_size) & (sizes < max_size)]
1872
+
1873
+ self.keep_components(kept_coms)
1874
+
1875
+
1876
+ def keep_largest_connected_component(self, n=1):
1877
+ # Find the connected components
1878
+ self.find_connected_components()
1879
+
1880
+ # Get n largest connected components
1881
+ com, counts = np.unique(self._vertices['component'][self._vertices['component']!=-1], return_counts=True)
1882
+ # max_count = np.argmax(counts)
1883
+ counts_sorted = np.argsort(counts)
1884
+ # max_com = com[max_count]
1885
+ max_coms = com[counts_sorted[-n:]]
1886
+
1887
+ self.keep_components(max_coms)
1888
+
1889
+ def remove_inner_surfaces(self, max_mean_curvature=0.1):
1890
+ """
1891
+ Isosurfacing of a closed membrane will often generate two surfaces - one on the
1892
+ inside of the structure and one on the outside. The inner and outer surfaces can be
1893
+ distinguished on the basis of mean curvature, with outer surfaces having +ve overall
1894
+ mean curvature and inside surfaces having -ve overall mean curvature. This function
1895
+ culls all surfaces having -ve mean curvature.
1896
+
1897
+ To avoid calculating component curvatures twice, this function also attempts to remove
1898
+ spurious surfaces (such as those generated by a single voxel > threshold) which can be identified
1899
+ by having an excessively large +ve mean curvature.
1900
+
1901
+ Parameters
1902
+ ----------
1903
+
1904
+ max_mean_curvature: float [default=0.1 nm-1]
1905
+ a mean curvature value above which a surface is deemed to be spurious
1906
+
1907
+ Returns
1908
+ -------
1909
+
1910
+ """
1911
+ comps = np.unique(self.component) #find the unique components
1912
+ comps = comps[comps >=0] #discard the -1, or 'invalid' component
1913
+
1914
+ cc = [(i, np.median(self.curvature_mean[self.component==i])) for i in comps]
1915
+ self.keep_components([i for i, c in cc if (c > 0) and (c < max_mean_curvature)])
1916
+
1917
+
1918
+ def keep_components(self, components):
1919
+ """
1920
+ Discard all but the indicated components. Used for pruning the mesh of both
1921
+ small objects, and inner surfaces.
1922
+
1923
+ Parameters
1924
+ ----------
1925
+ components : ndarray / list of int
1926
+ labels of components to keep
1927
+
1928
+ Returns
1929
+ -------
1930
+
1931
+ """
1932
+ components = np.array(components)
1933
+
1934
+ # Remove the smaller components
1935
+ # _vertices = np.where((self._vertices['component'] != max_com))[0]
1936
+ _vertices = np.flatnonzero((self._vertices['component'][:,None] != components[None, :]).prod(1))
1937
+ # _edges = np.where((self._halfedges['component'] != max_com))[0]
1938
+ _edges = np.flatnonzero((self._halfedges['component'][:,None] != components[None, :]).prod(1))
1939
+ _edges_with_twins = _edges[self._halfedges['twin'][_edges] != -1]
1940
+ # _faces = np.where((self._faces['component'] != max_com))[0]
1941
+ _faces = np.flatnonzero((self._faces['component'][:,None] != components[None, :]).prod(1))
1942
+
1943
+ # Maybe don't delete some of these? (If there are singular elements in our mesh.)
1944
+ _kept_edges = np.array(list(set(self._halfedges['twin'][_edges_with_twins])-set(_edges)))
1945
+ if np.any(_kept_edges):
1946
+ _deleted_vertices = np.array(list(set(_vertices)-set(self._halfedges['vertex'][_kept_edges])))
1947
+ self._halfedges['twin'][_kept_edges] = -1
1948
+ else:
1949
+ _deleted_vertices = _vertices
1950
+
1951
+ # Delete vertices
1952
+ self._vertices[_deleted_vertices] = -1
1953
+ self._halfedges[_edges] = -1
1954
+ self._faces[_faces] = -1
1955
+
1956
+ # Mark deleted faces as available
1957
+ self._vertex_vacancies = list(set(self._vertex_vacancies).union(set(_deleted_vertices)))
1958
+ self._halfedge_vacancies = list(set(self._halfedge_vacancies).union(set(_edges)))
1959
+ self._face_vacancies = list(set(self._face_vacancies).union(set(_faces)))
1960
+
1961
+ if np.any(_kept_edges):
1962
+ # Update newly orphaned edges
1963
+ self._update_face_normals(list(set(self._halfedges['face'][_kept_edges])))
1964
+ self._update_vertex_neighbors(list(set(self._halfedges['vertex'][_kept_edges])))
1965
+
1966
+ self._invalidate_cached_properties()
1967
+
1968
+ cdef _find_boundary_polygons(self, np.ndarray boundary_polygons, np.ndarray boundary_edges):
1969
+ """
1970
+ Return array of closed boundary polygons.
1971
+
1972
+ We assume boundary_edges is the exhaustive list of halfedges on a boundary.
1973
+ """
1974
+ cdef int n_edges = boundary_edges.shape[0]
1975
+ cdef int max_iters = 6*n_edges # FIXME: Not clear what this upper bound should be. Seems low.
1976
+ cdef int i, j, k, poly, safety, n_poly, new_poly, n_left
1977
+ cdef int32_t _orig, _curr, _twin
1978
+ cdef halfedge_t *curr_edge
1979
+
1980
+ # keep track of already visited edges in components
1981
+ # later, keep track of vertex valences in components
1982
+ # this is predicated on vertices showing up in only one polygon,
1983
+ # which should be the case if we have removed singularities
1984
+ self._components_valid = 0
1985
+ for i in range(n_edges):
1986
+ curr_edge = &self._chalfedges[boundary_edges[i]]
1987
+ curr_edge.component = 0
1988
+ self._cvertices[curr_edge.vertex].component = 0
1989
+
1990
+ poly = 0 # polygon number
1991
+
1992
+ # loop over all of the boundary edges
1993
+ for i in range(n_edges):
1994
+ if (self._chalfedges[boundary_edges[i]].component == 1):
1995
+ continue
1996
+
1997
+ # if this edge is not included in existing polygons, create a new one
1998
+ k = 0 # element in polygon
1999
+ _curr = boundary_edges[i]
2000
+ _orig = _curr
2001
+
2002
+ # traverse around boundary until we return to _orig
2003
+ safety = 0 # safety counter
2004
+ while True:
2005
+ if safety >= max_iters:
2006
+ # Safety release valve so we don't iterate forever
2007
+ print("ERROR: boundary not found")
2008
+ break
2009
+
2010
+ curr_edge = &self._chalfedges[_curr]
2011
+ _twin = curr_edge.twin
2012
+
2013
+ if _twin == -1:
2014
+ if k >= n_edges:
2015
+ print("ERROR: Not enough space allocated for this polygon.")
2016
+ break
2017
+ # we're on a boundary, mark it as part of the polygon...
2018
+ boundary_polygons[poly,k] = _curr
2019
+ curr_edge.component = 1
2020
+ # ...also increment the number of incident boundary edges on its vertices
2021
+ self._cvertices[curr_edge.vertex].component += 1
2022
+ self._cvertices[self._chalfedges[curr_edge.prev].vertex].component += 1
2023
+
2024
+ k += 1
2025
+
2026
+ # ...then traverse
2027
+ _curr = curr_edge.next
2028
+ else:
2029
+ # traverse
2030
+ _curr = self._chalfedges[_twin].next
2031
+
2032
+ if _curr == _orig:
2033
+ # we closed the loop
2034
+ break
2035
+
2036
+ safety += 1
2037
+
2038
+ if k == 0:
2039
+ print("WARNING: Polygon of length 0????")
2040
+ continue
2041
+ elif k == 1:
2042
+ print("WARNING: Polygons should be of length >= 2. Perhaps there is a singularity in the mesh?")
2043
+ poly += 1
2044
+ else:
2045
+ # Deal with pinched polygons
2046
+ # We keep disconnecting pinched polygons from the original polygon and then checking the
2047
+ # disconnected polygon for further pinches. The function _disconnect_pinched_polygons is
2048
+ # set up s.t. there will be no pinched polygons in the poly entry once run, only in the
2049
+ # disconnected polygon in a later entry
2050
+ #
2051
+ # TODO: Is this necessary? A pinched polygon requires a singular vertex. We should simply
2052
+ # call _remove_singularities() before running this function.
2053
+ n_poly = poly
2054
+ n_left = 1
2055
+ while n_left > 0:
2056
+ n_left -= 1
2057
+ new_poly = self._disconnect_pinched_polygons(boundary_polygons, poly, n_poly, k)
2058
+ n_left += new_poly
2059
+ n_poly += new_poly
2060
+ poly += 1 # net increment poly by the number of new polygons we created un-pinching
2061
+
2062
+
2063
+ cdef int _disconnect_pinched_polygons(self, np.ndarray boundary_polygons, int curr_poly, int n_poly, int n_edges):
2064
+ cdef int32_t _orig, _curr
2065
+ cdef int j, jj, kk, n_new_polygons
2066
+ cdef halfedge_t *curr_edge
2067
+
2068
+ _orig = -1
2069
+ n_new_polygons = 0
2070
+
2071
+ # check to see if any vertices have more than one boundary edge incident on a vertex
2072
+ j = 0
2073
+ while boundary_polygons[curr_poly,j] != -1:
2074
+ curr_edge = &self._chalfedges[boundary_polygons[curr_poly,j]]
2075
+ if self._cvertices[curr_edge.vertex].component > 2:
2076
+ # we've hit the case were the boundary pinches in on itself. the twin->next traverse allows this
2077
+ # (picture a square boundary with a triangular boundary coming off one of its corners).
2078
+
2079
+ # the good news is everything is wound consistently, so we just have to clip from here to the next
2080
+ # instance of this vertex as a new polygon.
2081
+
2082
+ if (_orig != -1) and (curr_edge.vertex == _orig):
2083
+ # we previously marked this vertex, finish splitting this into a new polygon
2084
+ n_new_polygons += 1 # add another polygon
2085
+
2086
+ print(f"New polygon: {_orig} {self._chalfedges[kk].vertex}")
2087
+ print(f"{boundary_polygons[curr_poly,:14]}")
2088
+ for jj in range(kk,j+1):
2089
+ # copy into the new polygon
2090
+ boundary_polygons[(n_poly+n_new_polygons),(jj-kk)] = boundary_polygons[curr_poly,jj]
2091
+ # slide everything in the current polygon backwards (or delete it)
2092
+ for jj in range(j+1,n_edges+1):
2093
+ boundary_polygons[curr_poly,jj-j+kk-1] = boundary_polygons[curr_poly,jj]
2094
+ for jj in range(n_edges-j+kk,n_edges+1):
2095
+ boundary_polygons[curr_poly,jj] = -1
2096
+ #for jj in range(kk,j):
2097
+ # if (j+jj) > n_edges:
2098
+ # boundary_polygons[curr_poly,jj] = -1
2099
+ # else:
2100
+ # boundary_polygons[curr_poly,jj] = boundary_polygons[curr_poly,(j+jj)]
2101
+ # boundary_polygons[curr_poly,(j+jj)] = -1 # restore to -1
2102
+ print(f"{boundary_polygons[curr_poly,:14]}")
2103
+ print(f"{boundary_polygons[(n_poly+n_new_polygons),:14]}")
2104
+ # we moved everything backwards so we need to re-check this slot, which now contains a new value
2105
+ # j -= 1
2106
+
2107
+ # decrement the number of pinches (there might be more than one, picture two triangluar boundaries
2108
+ # coming off the same corner of a square boundary)
2109
+ self._cvertices[curr_edge.vertex].component -= 1
2110
+ _orig = -1
2111
+ else:
2112
+ _orig = curr_edge.vertex # now we use this to track vertex
2113
+ kk = j+1 # use this to track starting index
2114
+ j += 1
2115
+ if j >= n_edges:
2116
+ break
2117
+
2118
+ return n_new_polygons
2119
+
2120
+ cdef _pinch_boundaries(self, np.ndarray boundary_polygons, bint live_update=1):
2121
+ """
2122
+ Take an array of boundary polygons (each row is a polygon) and pinch them to close.
2123
+
2124
+ NOTE: This implementation seems to produce singularities.
2125
+
2126
+ References
2127
+ ----------
2128
+ Gueziec et al. Cutting and Stitching: Converting Sets of Polygons
2129
+ to Manifold Surfaces, IEEE TRANSACTIONS ON VISUALIZATION AND
2130
+ COMPUTER GRAPHICS, 2001.
2131
+ """
2132
+ cdef int j, k, n_edges, n_pinch
2133
+ cdef bint odd
2134
+ cdef int32_t _edge0, _edge1
2135
+ cdef int n_poly = boundary_polygons.shape[0]
2136
+ cdef int max_edges = boundary_polygons.shape[1]
2137
+
2138
+ # Move down the row of polygons
2139
+ j = 0
2140
+ while (j < n_poly) and (boundary_polygons[j,0] != -1):
2141
+ #if j != 1:
2142
+ # j += 1
2143
+ # continue
2144
+ # Count the number of elements in this polygon
2145
+ n_edges = 0
2146
+ while (n_edges < max_edges) and (boundary_polygons[j,n_edges] != -1):
2147
+ n_edges += 1
2148
+
2149
+ if n_edges < 2:
2150
+ print("ERROR: The absolute minimum polygon size is 2. Something's wrong. Skipping.")
2151
+ continue
2152
+
2153
+ #if n_edges > 6:
2154
+ # print(f"Skipping polygon {j} of size {n_edges}")
2155
+ # j += 1
2156
+ # continue
2157
+
2158
+ #print(f"Polygon {j} of size {n_edges}")
2159
+
2160
+ odd = ((n_edges % 2) != 0)
2161
+
2162
+ if odd:
2163
+ # If the length of this polygon is odd, we pinch all but the last three edges...
2164
+ n_pinch = (n_edges-3)//2
2165
+ else:
2166
+ # ...otherwise we pinch every edge
2167
+ n_pinch = (n_edges)//2
2168
+
2169
+ # Now pinch elements together
2170
+ #k = 2
2171
+ for k in range(n_pinch):
2172
+ # Run around the closed boundary loop in opposite directions
2173
+ _edge0 = boundary_polygons[j,k]
2174
+ _edge1 = boundary_polygons[j,n_edges-k-1]
2175
+
2176
+ self._pinch_edges(_edge0, _edge1, live_update=live_update)
2177
+
2178
+ if odd:
2179
+ #print("We went odd!")
2180
+ # Make a triangle from the last three edges to seal the boundary
2181
+ self._fill_triangle(boundary_polygons[j,n_pinch], boundary_polygons[j,n_pinch+1], boundary_polygons[j,n_pinch+2], live_update=live_update)
2182
+
2183
+ j += 1
2184
+
2185
+ self._invalidate_cached_properties()
2186
+
2187
+ cdef _pinch_edges(self, int32_t _edge0, int32_t _edge1, bint live_update=1):
2188
+ """
2189
+ Pinch two (boundary) edges together to stitch part of the mesh.
2190
+ """
2191
+ cdef int i
2192
+ cdef int32_t _vertex0, _vertex1
2193
+ cdef vertex_d *vertex0
2194
+ cdef vertex_d *vertex1
2195
+ cdef float pos0, pos1, pos2
2196
+ cdef halfedge_t *edge0
2197
+ cdef halfedge_t *edge1
2198
+
2199
+ cdef int n_halfedges = self._halfedges.shape[0]
2200
+
2201
+ edge0 = &self._chalfedges[_edge0]
2202
+ edge1 = &self._chalfedges[_edge1]
2203
+
2204
+ if (edge0.twin != -1) or (edge1.twin != -1):
2205
+ print("WARNING: One of these is not a boundary!!! Are you sure you want to stitch them?")
2206
+
2207
+ if (edge1.vertex != self._chalfedges[edge0.prev].vertex):
2208
+ print("WARNING: These are not continuous!")
2209
+
2210
+ # update pinched vertex position
2211
+ _vertex0 = edge0.vertex
2212
+ _vertex1 = self._chalfedges[edge1.prev].vertex
2213
+
2214
+ #print(f"Check vertices: {_vertex1} {edge1.vertex} {self._chalfedges[edge0.prev].vertex} {_vertex0}")
2215
+ #print("Pre-stitch")
2216
+ #print(edge0.vertex, edge0.face, edge0.twin, edge0.next, edge0.prev, edge0.length, edge0.component, edge0.locally_manifold)
2217
+ #print(edge1.vertex, edge1.face, edge1.twin, edge1.next, edge1.prev, edge1.length, edge1.component, edge1.locally_manifold)
2218
+
2219
+ if _vertex0 != _vertex1:
2220
+ vertex0 = &self._cvertices[_vertex0]
2221
+ vertex1 = &self._cvertices[_vertex1]
2222
+ pos0 = 0.5*(vertex0.position0 + vertex1.position0)
2223
+ pos1 = 0.5*(vertex0.position1 + vertex1.position1)
2224
+ pos2 = 0.5*(vertex0.position2 + vertex1.position2)
2225
+ vertex0.position0 = pos0
2226
+ vertex0.position1 = pos1
2227
+ vertex0.position2 = pos2
2228
+
2229
+ # set all vertex0 points to vertex1
2230
+ for i in range(n_halfedges):
2231
+ if self._chalfedges[i].vertex == _vertex1:
2232
+ self._chalfedges[i].vertex = _vertex0
2233
+
2234
+ # Delete this vertex
2235
+ self._vertex_delete(_vertex1)
2236
+
2237
+ # update connectivity
2238
+ edge0.twin = _edge1
2239
+ edge1.twin = _edge0
2240
+
2241
+ if live_update:
2242
+ update_face_normal(int(edge0.face), self._chalfedges, self._cvertices, self._cfaces)
2243
+ update_face_normal(int(edge1.face), self._chalfedges, self._cvertices, self._cfaces)
2244
+
2245
+ update_single_vertex_neighbours(int(edge0.vertex), self._chalfedges, self._cvertices, self._cfaces)
2246
+ update_single_vertex_neighbours(int(edge1.vertex), self._chalfedges, self._cvertices, self._cfaces)
2247
+
2248
+ #print("Post-stitch")
2249
+ #print(edge0.vertex, edge0.face, edge0.twin, edge0.next, edge0.prev, edge0.length, edge0.component, edge0.locally_manifold)
2250
+ #print(edge1.vertex, edge1.face, edge1.twin, edge1.next, edge1.prev, edge1.length, edge1.component, edge1.locally_manifold)
2251
+ self._singular_edges_valid = 0
2252
+ #print(self.singular_edges)
2253
+
2254
+ #for i in range(self.singular_edges.shape[0]):
2255
+ # edge0 = &self._chalfedges[self.singular_edges[i]]
2256
+ # print(edge0.vertex, edge0.face, edge0.twin, edge0.next, edge0.prev, edge0.length, edge0.component, edge0.locally_manifold)
2257
+
2258
+ cdef _color_boundaries(self, np.ndarray boundary_polygons):
2259
+ """
2260
+ Color each boundary a single color.
2261
+ """
2262
+ cdef int j, n_edges
2263
+
2264
+ self._components_valid = 1
2265
+
2266
+ # zero-out components
2267
+ for j in range(self._vertices.shape[0]):
2268
+ self._cvertices[j].component = -1
2269
+
2270
+ # Move down the row of polygons
2271
+ j = 0
2272
+ while boundary_polygons[j,0] != -1:
2273
+ # Count the number of elements in this polygon
2274
+ n_edges = 0
2275
+ #if j != 1:
2276
+ # j+=1
2277
+ # continue
2278
+ while boundary_polygons[j,n_edges] != -1:
2279
+ #self._cvertices[self._chalfedges[self._chalfedges[boundary_polygons[j,n_edges]].prev].vertex].component
2280
+ self._cvertices[self._chalfedges[boundary_polygons[j,n_edges]].vertex].component = j # n_edges
2281
+ #self._cvertices[self._chalfedges[boundary_polygons[j,n_edges]].vertex].component = n_edges
2282
+ n_edges += 1
2283
+ j += 1
2284
+
2285
+ def _fill_triangle(self, int32_t h0, int32_t h1, int32_t h2, bint live_update=1):
2286
+ """
2287
+ Make a triangle from the three open edges to seal a boundary
2288
+ """
2289
+ cdef halfedge_t *edge0
2290
+ cdef halfedge_t *edge1
2291
+ cdef halfedge_t *edge2
2292
+
2293
+ n_edges = self.new_edges(3)
2294
+ n_faces = self.new_faces(1)
2295
+
2296
+ edge0 = &self._chalfedges[h0]
2297
+ edge1 = &self._chalfedges[h1]
2298
+ edge2 = &self._chalfedges[h2]
2299
+
2300
+ self._populate_edge(n_edges[0], edge2.vertex, prev=n_edges[1], next=n_edges[2], face=n_faces[0], twin=h0)
2301
+ self._populate_edge(n_edges[1], edge0.vertex, prev=n_edges[2], next=n_edges[0], face=n_faces[0], twin=h1)
2302
+ self._populate_edge(n_edges[2], edge1.vertex, prev=n_edges[0], next=n_edges[1], face=n_faces[0], twin=h2)
2303
+
2304
+ self._chalfedges[h0].twin = n_edges[0]
2305
+ self._chalfedges[h1].twin = n_edges[1]
2306
+ self._chalfedges[h2].twin = n_edges[2]
2307
+
2308
+ self._cfaces[n_faces[0]].halfedge = n_edges[0]
2309
+
2310
+ if live_update:
2311
+ update_face_normal(int(n_faces[0]), self._chalfedges, self._cvertices, self._cfaces)
2312
+
2313
+ update_single_vertex_neighbours(int(edge2.vertex), self._chalfedges, self._cvertices, self._cfaces)
2314
+ update_single_vertex_neighbours(int(edge1.vertex), self._chalfedges, self._cvertices, self._cfaces)
2315
+ update_single_vertex_neighbours(int(edge0.vertex), self._chalfedges, self._cvertices, self._cfaces)
2316
+
2317
+ cdef _zig_zag_triangulation(self, np.ndarray boundary_polygons, int32_t *n_edges, int32_t *n_faces, int row, int len_poly, bint live_update=1):
2318
+ """
2319
+ Triangulate a (boundary) polygon (an array of halfedges) by zig-zagging
2320
+ between its edges.
2321
+ """
2322
+ cdef int k
2323
+ cdef int _edge0, _edge1
2324
+ cdef halfedge_t *edge0
2325
+ cdef halfedge_t *edge1
2326
+ cdef bint zig
2327
+
2328
+ # How many edges are in this polygon?
2329
+ # len_poly = 0
2330
+ # while poly[len_poly] != -1:
2331
+ # print(poly[len_poly])
2332
+ # len_poly += 1
2333
+
2334
+ if len_poly < 3:
2335
+ print("ERROR: Cannot triangulate polygons with length less than 3. Aborting.")
2336
+
2337
+ # starting edges
2338
+ _edge0 = boundary_polygons[row,0]
2339
+ _edge1 = boundary_polygons[row,1]
2340
+
2341
+ # Zig-zag with the new edges
2342
+ zig = 0
2343
+ for k in range(len_poly-2):
2344
+ #print(k, _edge0, _edge1)
2345
+ # grab the two existing edges on the polygon
2346
+ edge0 = &self._chalfedges[_edge0]
2347
+ edge1 = &self._chalfedges[_edge1]
2348
+
2349
+ #print(edge0.vertex, edge0.face, edge0.twin, edge0.next, edge0.prev, edge0.length, edge0.component, edge0.locally_manifold)
2350
+ #print(edge1.vertex, edge1.face, edge1.twin, edge1.next, edge1.prev, edge1.length, edge1.component, edge1.locally_manifold)
2351
+
2352
+ # Populate the new triangle
2353
+ self._populate_edge(n_edges[3*k], self._chalfedges[edge0.prev].vertex, prev=n_edges[3*k+1], next=n_edges[3*k+2], face=n_faces[k], twin=_edge0)
2354
+ self._populate_edge(n_edges[3*k+1], edge0.vertex, prev=n_edges[3*k+2], next=n_edges[3*k], face=n_faces[k], twin=_edge1)
2355
+ self._populate_edge(n_edges[3*k+2], edge1.vertex, prev=n_edges[3*k], next=n_edges[3*k+1], face=n_faces[k], twin=-1)
2356
+ self._cfaces[n_faces[k]].halfedge = n_edges[3*k]
2357
+
2358
+ # stitch to previously-existing edges
2359
+ self._chalfedges[_edge0].twin = n_edges[3*k]
2360
+ self._chalfedges[_edge1].twin = n_edges[3*k+1]
2361
+
2362
+ if live_update:
2363
+ update_face_normal(int(n_faces[k]), self._chalfedges, self._cvertices, self._cfaces)
2364
+
2365
+ update_single_vertex_neighbours(int(self._chalfedges[edge0.prev].vertex), self._chalfedges, self._cvertices, self._cfaces)
2366
+ update_single_vertex_neighbours(int(edge0.vertex), self._chalfedges, self._cvertices, self._cfaces)
2367
+ update_single_vertex_neighbours(int(edge1.vertex), self._chalfedges, self._cvertices, self._cfaces)
2368
+
2369
+ if k >= (len_poly-3):
2370
+ # don't index beyond the length of new_edge
2371
+ break
2372
+
2373
+ # Choose the next two edges to stitch
2374
+ if zig:
2375
+ _edge0 = n_edges[3*k+2]
2376
+ _edge1 = boundary_polygons[row,(k+1)//2+1]
2377
+ else:
2378
+ _edge0 = boundary_polygons[row,len_poly-k//2-1]
2379
+ _edge1 = n_edges[3*k+2]
2380
+ zig = 1-zig
2381
+
2382
+ # stitch the last edge
2383
+ _edge0 = boundary_polygons[row,len_poly-(len_poly-2)//2-1]
2384
+ _edge1 = n_edges[3*(len_poly-3)+2]
2385
+ self._chalfedges[_edge0].twin = _edge1
2386
+ self._chalfedges[_edge1].twin = _edge0
2387
+
2388
+ if live_update:
2389
+ update_single_vertex_neighbours(int(self._chalfedges[_edge0].vertex), self._chalfedges, self._cvertices, self._cfaces)
2390
+ update_single_vertex_neighbours(int(self._chalfedges[_edge1].vertex), self._chalfedges, self._cvertices, self._cfaces)
2391
+
2392
+ def _fill_holes(self, np.ndarray boundary_polygons, bint live_update=1):
2393
+ """
2394
+ Fill in the mesh holes: closed loops of boundary edges, one per row in boundary_polygons.
2395
+ """
2396
+ cdef int j, n_edges
2397
+ cdef int n_poly = boundary_polygons.shape[0]
2398
+ cdef int max_edges = boundary_polygons.shape[1]
2399
+
2400
+ j = 0
2401
+ while (j < n_poly) and (boundary_polygons[j,0] != -1):
2402
+ # Count the number of elements in this polygon
2403
+ n_edges = 0
2404
+ while (n_edges < max_edges) and (boundary_polygons[j,n_edges] != -1):
2405
+ n_edges += 1
2406
+
2407
+ #print(f"Polygon {j} of length {n_edges}")
2408
+
2409
+ if n_edges == 2:
2410
+ self._pinch_edges(boundary_polygons[j,0], boundary_polygons[j,1], live_update=live_update)
2411
+ else:
2412
+ # Initialize new edges/faces
2413
+ new_faces = self.new_faces(int(n_edges-2))
2414
+ new_edges = self.new_edges(int(3*(n_edges-3)+3))
2415
+
2416
+ # Right now we use zig-zag, but we should use Liepa, Peter. "Filling Holes in Meshes."
2417
+ # In Proceedings of the 2003 Eurographics/ACM SIGGRAPH Symposium on Geometry Processing,
2418
+ # 200-205. SGP '03. Goslar, DEU: Eurographics Association, 2003.
2419
+
2420
+ self._zig_zag_triangulation(boundary_polygons,
2421
+ <int32_t *> np.PyArray_DATA(new_edges),
2422
+ <int32_t *> np.PyArray_DATA(new_faces),
2423
+ j, n_edges, live_update=live_update)
2424
+
2425
+ j += 1
2426
+
2427
+ def _remove_singularities(self):
2428
+ """
2429
+ Excise singularities from the mesh. This essentially unzips multivalent
2430
+ edges from the rest of the mesh, creating new, smaller connected
2431
+ components, each of which is free of singularities. This follows the
2432
+ local method for cutting outlined in Gueziec et al.
2433
+
2434
+ References
2435
+ ----------
2436
+ Gueziec et al. Cutting and Stitching: Converting Sets of Polygons
2437
+ to Manifold Surfaces, IEEE TRANSACTIONS ON VISUALIZATION AND
2438
+ COMPUTER GRAPHICS, 2001.
2439
+ """
2440
+ cdef int i, j, k, component, n_v
2441
+ cdef int32_t twin_idx, next_idx
2442
+
2443
+ # Iterate over vertices that are endpoints of a singular edge or are
2444
+ # isolated singular vertices (number of incident edges =/= number of
2445
+ # neighbors)
2446
+ self._singular_vertices_valid = 0
2447
+ self._singular_edges_valid = 0
2448
+ singular_vertices = self.singular_vertices
2449
+
2450
+ for i in range(singular_vertices.shape[0]):
2451
+ # Find the star of this vertex. That is, all of the vertices
2452
+ # edges incident on this vertex. We grab only the edges pointing at the vertex
2453
+ # because 1) this is sufficient and 2) this allows unambiguous traversal of
2454
+ # the one-ring neighbor via twin->next movement
2455
+ star = np.flatnonzero(self._halfedges['vertex'] == singular_vertices[i]).astype('i4')
2456
+
2457
+ # Partition the faces of the star s.t. two faces are connected if
2458
+ # they share a non-singular edge. Denote the number of connected
2459
+ # components under this relation as component.
2460
+ for j in range(star.shape[0]):
2461
+ self._chalfedges[star[j]].component = -1
2462
+ self._chalfedges[self._chalfedges[star[j]].next].component = -1
2463
+ self._chalfedges[self._chalfedges[star[j]].prev].component = -1
2464
+ component = 0
2465
+ for j in range(star.shape[0]):
2466
+ component = flood_fill_star_component(star[j], component, self._chalfedges)
2467
+
2468
+ # Construct n_v = component-1 additional compies of vertex i...
2469
+ n_v = component-1
2470
+ if n_v < 1:
2471
+ continue
2472
+ n_vertices = self.new_vertices(n_v)
2473
+ for k in range(n_v):
2474
+ # ...with the same coordinates
2475
+ self._cvertices[n_vertices[k]].position0 = copy.copy(self._cvertices[singular_vertices[i]].position0) #+ 10*(k+1)*self._cvertices[singular_vertices[i]].normal0
2476
+ self._cvertices[n_vertices[k]].position1 = copy.copy(self._cvertices[singular_vertices[i]].position1) #+ 10*(k+1)*self._cvertices[singular_vertices[i]].normal1
2477
+ self._cvertices[n_vertices[k]].position2 = copy.copy(self._cvertices[singular_vertices[i]].position2) #+ 10*(k+1)*self._cvertices[singular_vertices[i]].normal2
2478
+ self._cvertices[n_vertices[k]].halfedge = -1
2479
+
2480
+ # Loop over the star again, assigning vertices to each component
2481
+ for j in range(star.shape[0]):
2482
+ k = self._chalfedges[star[j]].component
2483
+ if k < n_v:
2484
+ # Assign halfedges associated with component k \in range(n_v) a unique copy of this vertex
2485
+ self._chalfedges[star[j]].vertex = n_vertices[k]
2486
+
2487
+ # Mark singular edges in this component as non-singular, boundary
2488
+ if self._chalfedges[star[j]].locally_manifold == 0:
2489
+ #print(f"Singular in component {k}")
2490
+ #self._chalfedges[star[j]].locally_manifold = 1
2491
+ twin_idx = self._chalfedges[star[j]].twin
2492
+ if twin_idx != -1:
2493
+ self._chalfedges[twin_idx].twin = -1
2494
+ self._chalfedges[star[j]].twin = -1
2495
+
2496
+ next_idx = self._chalfedges[star[j]].next
2497
+ if self._chalfedges[next_idx].locally_manifold == 0:
2498
+ #print(f"Next singular in component {k}")
2499
+ #self._chalfedges[next_idx].locally_manifold = 1
2500
+ twin_idx = self._chalfedges[next_idx].twin
2501
+ if twin_idx != -1:
2502
+ self._chalfedges[twin_idx].twin = -1
2503
+ self._chalfedges[next_idx].twin = -1
2504
+
2505
+ # find an emanating halfedge in component k
2506
+ self._cvertices[self._chalfedges[star[j]].vertex].halfedge = next_idx
2507
+
2508
+ for j in range(star.shape[0]):
2509
+ update_face_normal(int(self._chalfedges[star[j]].face), self._chalfedges, self._cvertices, self._cfaces)
2510
+ update_single_vertex_neighbours(int(self._chalfedges[star[j]].vertex), self._chalfedges, self._cvertices, self._cfaces)
2511
+ update_single_vertex_neighbours(int(self._chalfedges[self._chalfedges[star[j]].prev].vertex), self._chalfedges, self._cvertices, self._cfaces)
2512
+ update_single_vertex_neighbours(int(self._chalfedges[self._chalfedges[star[j]].next].vertex), self._chalfedges, self._cvertices, self._cfaces)
2513
+
2514
+ # reset
2515
+ self._invalidate_cached_properties()
2516
+
2517
+ def repair(self, bint only_largest_component=0, bint pinch=0):
2518
+ """
2519
+ Repair the mesh so it's topologically manifold.
2520
+
2521
+ References
2522
+ ----------
2523
+ M. Attene, A lightweight approach to repairing digitized polygon
2524
+ meshes, The Visual Computer, 2010.
2525
+ """
2526
+
2527
+ # 1. Remove singularities (singular edges and isolated singular vertices)
2528
+ self._remove_singularities()
2529
+
2530
+ # (Optional) 2. Remove all connected components except the largest
2531
+ if only_largest_component:
2532
+ self.keep_largest_connected_component()
2533
+
2534
+ #self.remesh(n=1)
2535
+
2536
+ # construct an array to store the boundary polygons
2537
+ # the longest boundary polygon possible contains all of the boundary edges in the mesh,
2538
+ # the shortest boundary "polygon" possible is of length 2
2539
+ # TODO: This could be quite large if we have a lot of boundary edges
2540
+ boundary_edges = np.flatnonzero((self._halfedges['vertex'] != -1) & (self._halfedges['twin'] == -1)).astype('i4')
2541
+ max_poly_length = max(boundary_edges.shape[0],4)
2542
+ boundary_polygons = -1*np.ones([max_poly_length//2, max_poly_length], dtype='i4')
2543
+
2544
+ # 3. Patch mesh holes with new triangles
2545
+ self._find_boundary_polygons(boundary_polygons, boundary_edges) # fill the boundary polygon array
2546
+
2547
+ if pinch == 1:
2548
+ self._pinch_boundaries(boundary_polygons) # close the boundary polygons by pinching
2549
+ else:
2550
+ self._fill_holes(boundary_polygons)
2551
+ # self._color_boundaries(boundary_polygons)
2552
+
2553
+ # Reset
2554
+ #self._manifold = None
2555
+ # self._faces['normal'][:] = -1
2556
+ # self._vertices['normal'][:] = -1
2557
+ #self._face_normals_valid = 0
2558
+ #self._vertex_normals_valid = 0
2559
+ self._invalidate_cached_properties()
2560
+ self._update_all_face_normals()
2561
+ self._update_all_vertex_neighbours()
2562
+
2563
+ def deneck(self, e_threshold=30.0):
2564
+ """
2565
+ Remove topological necks by deleting high energy vertices and patching the holes
2566
+ this creates.
2567
+
2568
+ TODO - threshold is very emperical.
2569
+ TODO - is E actually defined here, or only in subclasses?
2570
+ """
2571
+ verts = np.flatnonzero(self.E > e_threshold)
2572
+ self.unsafe_remove_vertices(verts)
2573
+ self.repair()
2574
+
2575
+ @cython.boundscheck(False)
2576
+ @cython.initializedcheck(False)
2577
+ @cython.wraparound(False)
2578
+ def unsafe_remove_vertices(self, ids_to_remove):
2579
+ """
2580
+ Remove a vertex and incident halfedges/faces.
2581
+
2582
+ NOTE: this will leave a non-manifold mesh! We then rely on repair to tidy it up.
2583
+ """
2584
+
2585
+ #ids_to_remove = [id,]
2586
+
2587
+ cdef int id, h, f, i, hvx, cand, ht, j
2588
+ cdef int n_vertices = self._vertices.shape[0]
2589
+
2590
+ cdef int [::1] _h_to_remove
2591
+ cdef int _n_h_to_remove, c_in_h_to_remove
2592
+
2593
+ assert(len(ids_to_remove) > 0)
2594
+
2595
+ h0 = np.concatenate([np.argwhere(self._halfedges['vertex']==id).squeeze() for id in ids_to_remove])
2596
+ h1 = self._halfedges['twin'][h0]
2597
+ h1 = h1[h1 != -1]
2598
+
2599
+ h_to_remove = np.concatenate((h0, h1))
2600
+
2601
+ #print([self._halfedges['face'][h] for h in h_to_remove])
2602
+
2603
+ f_to_remove = np.unique([f for f in [self._halfedges['face'][h] for h in h_to_remove] if f >=0])
2604
+
2605
+ h_to_remove = np.array([h for h in np.unique(np.concatenate([np.argwhere(self._halfedges['face'] == f).squeeze() for f in f_to_remove])) if h >=0], 'i')
2606
+
2607
+ _h_to_remove = h_to_remove
2608
+ _n_h_to_remove = len(h_to_remove)
2609
+
2610
+ # make sure that none of the halfedges that we remove is referenced from a vertex
2611
+ # that should remain
2612
+ for j in range(_n_h_to_remove):
2613
+ h = _h_to_remove[j]
2614
+ #try:
2615
+ # hvx = np.flatnonzero(self._vertices['halfedge'] == h)[0]
2616
+ #except IndexError:
2617
+ # # no vertices point to this halfedge - we don't need to do anything
2618
+ # continue
2619
+
2620
+ hvx = -1
2621
+ for i in range(n_vertices):
2622
+ if self._cvertices[i].halfedge == h:
2623
+ hvx = i
2624
+ break
2625
+
2626
+ if hvx == -1:
2627
+ # we did not find any vertex which points to this half edge
2628
+ continue
2629
+
2630
+
2631
+ if hvx in ids_to_remove:
2632
+ # we are going to remove the vertex, ignore
2633
+ continue
2634
+
2635
+ #iterate around the vertex trying to find a halfedge that will remain
2636
+ cand = self._chalfedges[self._chalfedges[h].twin].next
2637
+
2638
+ # cand in h_to_remove
2639
+ c_in_h_to_remove = 0
2640
+ for i in range(_n_h_to_remove):
2641
+ c_in_h_to_remove |= (cand == _h_to_remove[i])
2642
+
2643
+ while (cand != h) and (cand != -1) and c_in_h_to_remove:
2644
+ cand = self._chalfedges[self._chalfedges[cand].twin].next
2645
+
2646
+ # cand in h_to_remove
2647
+ c_in_h_to_remove = 0
2648
+ for i in range(_n_h_to_remove):
2649
+ c_in_h_to_remove |= (cand == _h_to_remove[i])
2650
+
2651
+ # didn't find a candidate, iterate in other direction
2652
+ if (cand != h) or (cand != -1) or (c_in_h_to_remove):
2653
+ cand = self._chalfedges[self._chalfedges[h].prev].twin
2654
+
2655
+ # cand in h_to_remove
2656
+ c_in_h_to_remove = 0
2657
+ for i in range(_n_h_to_remove):
2658
+ c_in_h_to_remove |= (cand == _h_to_remove[i])
2659
+
2660
+ while (cand != h) and (cand != -1) and (c_in_h_to_remove):
2661
+ cand = self._chalfedges[self._chalfedges[cand].prev].twin
2662
+
2663
+ # cand in h_to_remove
2664
+ c_in_h_to_remove = 0
2665
+ for i in range(_n_h_to_remove):
2666
+ c_in_h_to_remove |= (cand == _h_to_remove[i])
2667
+
2668
+ if (cand == h) or (cand == -1) or (c_in_h_to_remove):
2669
+
2670
+ #print(f'h: {h}, {self._halfedges[h]}')
2671
+ # Check if this is a single vertex on it's own
2672
+ vtx_halfedges = np.argwhere(self._halfedges['vertex']==hvx).squeeze()
2673
+ if np.all([vtx_h in h_to_remove for vtx_h in vtx_halfedges]):
2674
+ print('All incident halfedges are being removed, remove vertex too')
2675
+ ids_to_remove = np.concatenate([ids_to_remove, [hvx, ]])
2676
+ else:
2677
+ print('ERROR: could not find a suitable replacement vertex halfedge, mesh will be singular')
2678
+ else:
2679
+ self._cvertices[hvx].halfedge = cand
2680
+
2681
+
2682
+ # remove twin references
2683
+ for h in h_to_remove:
2684
+ ht = self._chalfedges[h].twin
2685
+ if ht >=0:
2686
+ self._chalfedges[ht].twin = -1
2687
+
2688
+ self._halfedges[h_to_remove] = -1
2689
+ self._halfedge_vacancies.extend(h_to_remove)
2690
+
2691
+ self._vertices[ids_to_remove] = -1
2692
+ self._vertex_vacancies.extend(ids_to_remove)
2693
+
2694
+ self._faces[f_to_remove] = -1
2695
+ self._face_vacancies.extend(f_to_remove)
2696
+
2697
+
2698
+
2699
+ def to_stl(self, filename):
2700
+ """
2701
+ Save list of triangles to binary STL file.
2702
+
2703
+ Parameters
2704
+ ----------
2705
+ filename : string
2706
+ File name to write
2707
+
2708
+ Returns
2709
+ -------
2710
+ None
2711
+ """
2712
+ from PYME.IO.FileUtils import stl
2713
+
2714
+ dt = np.dtype([('normal', '3f4'), ('vertex0', '3f4'), ('vertex1', '3f4'),
2715
+ ('vertex2', '3f4'), ('attrib', 'u2')])
2716
+
2717
+ triangles_stl = np.zeros(self.faces.shape[0], dtype=dt)
2718
+ triangles_stl['vertex0'] = self.vertices[self.faces[:, 0]]
2719
+ triangles_stl['vertex1'] = self.vertices[self.faces[:, 1]]
2720
+ triangles_stl['vertex2'] = self.vertices[self.faces[:, 2]]
2721
+ triangles_stl['normal'] = self.face_normals
2722
+
2723
+ stl.save_stl_binary(filename, triangles_stl)
2724
+
2725
+ def to_ply(self, filename, colors=None):
2726
+ """
2727
+ Save a list of triangles and their vertex colors to a PLY file.
2728
+ We assume colors are a Nx3 array where N is the number of vertices.
2729
+ """
2730
+ from PYME.IO.FileUtils import ply
2731
+
2732
+ # Construct a re-indexing for non-negative vertices
2733
+ live_vertices = np.flatnonzero(self._vertices['halfedge'] != -1)
2734
+ new_vertex_indices = np.arange(live_vertices.shape[0])
2735
+ vertex_lookup = np.zeros(self._vertices.shape[0], dtype=np.int)
2736
+
2737
+ vertex_lookup[live_vertices] = new_vertex_indices
2738
+
2739
+ # Grab the faces and vertices we want
2740
+ faces = vertex_lookup[self.faces]
2741
+ vertices = self._vertices['position'][live_vertices]
2742
+
2743
+ ply.save_ply(filename, vertices, faces, colors)
2744
+
2745
+ def to_threejs(self, color='red'):
2746
+ import pythreejs as p3j
2747
+
2748
+ t = self.vertices[self.faces].reshape(-1, 3)
2749
+ n = self.vertex_normals[self.faces].reshape(-1, 3)
2750
+
2751
+ m = p3j.Mesh(p3j.BufferGeometry(attributes={'position': p3j.BufferAttribute(t.astype('f4')),
2752
+ 'index': p3j.BufferAttribute(np.arange(t.shape[0], dtype='uint32')),
2753
+ 'normal': p3j.BufferAttribute(n.astype('f4'))}),
2754
+ material=p3j.MeshPhongMaterial(color=color))
2755
+
2756
+ return m
2757
+
2758
+ def _repr_mimebundle_(self, **kwargs):
2759
+ try:
2760
+ return self.to_threejs()._repr_mimebundle_(**kwargs)
2761
+ except ImportError:
2762
+ return {'text/plain': repr(self) + '\nInstall pythreejs to display in notebook'}
2763
+
2764
+
2765
+ def plot(self, figure=None, axis=None):
2766
+ """
2767
+ Display surface using matplotlib. Useful for quick use in a notebook (or similar) when a wx GUI is not
2768
+ available. NOTE: will be slower than displaying through PYMEVis
2769
+
2770
+ Parameters
2771
+ ----------
2772
+ figure : [optional] a matplotlib figure instance
2773
+ axis : [optional] a matplotlib Axes3D instance
2774
+
2775
+ Returns
2776
+ -------
2777
+
2778
+ """
2779
+ import matplotlib.pyplot as plt
2780
+
2781
+ if axis is None:
2782
+ if figure is None:
2783
+ figure = plt.figure()
2784
+
2785
+ axis = figure.add_subplot(projection='3d')
2786
+
2787
+ return axis.plot_trisurf(self.vertices[:,0], self.vertices[:,1], self.faces, self.vertices[:,2])