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,2287 @@
1
+ # -*- coding: utf8 -*-
2
+
3
+ import numpy as np
4
+
5
+ # Regular, boundary, and max vertex valences. These need tp be
6
+ VALENCE = 6
7
+ BOUNDARY_VALENCE = 4
8
+ NEIGHBORSIZE = 20 # Note this must match NEIGHBORSIZE in triangle_mesh_utils.h
9
+
10
+ USE_C = True
11
+
12
+ if USE_C:
13
+ from PYME.experimental import triangle_mesh_utils
14
+
15
+ # def pack_edges(arr, axis=1):
16
+ # """
17
+ # Stores edges as a single unique integer.
18
+
19
+ # NOTE: that this implictly caps the mesh at 2**32
20
+ # vertices.
21
+ # """
22
+ # arr = np.sort(arr, axis=axis)
23
+ # res = ((arr[:,0].astype(np.uint64)) << 32)
24
+ # res += arr[:,1].astype(np.uint64)
25
+
26
+ # return res
27
+
28
+ MAX_VERTEX_COUNT = 2**64
29
+
30
+ def fast_3x3_cross(a,b):
31
+ # Index only once
32
+ a0 = a[0]
33
+ a1 = a[1]
34
+ a2 = a[2]
35
+ b0 = b[0]
36
+ b1 = b[1]
37
+ b2 = b[2]
38
+
39
+ x = a1*b2 - a2*b1
40
+ y = a2*b0 - a0*b2
41
+ z = a0*b1 - a1*b0
42
+
43
+ vec = np.array([x,y,z])
44
+ return vec
45
+
46
+ HALFEDGE_DTYPE = np.dtype([('vertex', 'i4'), ('face', 'i4'), ('twin', 'i4'), ('next', 'i4'), ('prev', 'i4'), ('length', 'f4'), ('component', 'i4')])
47
+ FACE_DTYPE = np.dtype([('halfedge', 'i4'), ('normal', '3f4'), ('area', 'f4'), ('component', 'i4')])
48
+ # Note that VERTEX_DTYPE neighbors size must match NEIGHBORSIZE
49
+ VERTEX_DTYPE = np.dtype([('position', '3f4'), ('normal', '3f4'), ('halfedge', 'i4'), ('valence', 'i4'), ('neighbors', '20i4'), ('component', 'i4')])
50
+
51
+ LOOP_ALPHA_FACTOR = (np.log(13)-np.log(3))/12
52
+
53
+ class TrianglesBase(object):
54
+ """
55
+ Base class to let VisGUI layers detect objects which are a bunch of triangles
56
+
57
+ a derived class should provide suitable attributes/properties for vertices, faces, face_normals,
58
+ and vertex_normals.
59
+
60
+ See Also: Tesselation class in PYME.recipes.pointcloud
61
+
62
+ TODO - move me out of here?
63
+ TODO - provide default implementations for face_normals and vertex_normals?
64
+
65
+ """
66
+ vertices=None
67
+ faces=None
68
+ face_normals=None
69
+ vertex_normals = None
70
+
71
+ def keys(self):
72
+ """
73
+ returns the names of possible vertex attributes
74
+ """
75
+ raise NotImplementedError('Over-ride in derived class')
76
+
77
+ def __getitem__(self, item):
78
+ """
79
+ returns a given vertex attribute
80
+ """
81
+ raise NotImplementedError('Over-ride in derived class')
82
+
83
+ class TriangleMesh(TrianglesBase):
84
+ def __init__(self, vertices=None, faces=None, mesh=None, **kwargs):
85
+ """
86
+ Base class for triangle meshes stored using halfedge data structure.
87
+ Expects STL-like input.
88
+
89
+ Parameters
90
+ ----------
91
+ vertices : np.array
92
+ N x 3 array of Euclidean points.
93
+ faces : np .array
94
+ M x 3 array of vertex indices indicating triangle connectivity.
95
+ Expects STL redundancy.
96
+ debug : bool
97
+ Print debug statements (assumes manifold mesh).
98
+ """
99
+ if mesh is not None:
100
+ import copy
101
+
102
+ self._vertices = np.copy(mesh._vertices)
103
+ self._vertex_vacancies = copy.copy(mesh._vertex_vacancies)
104
+
105
+ self._faces = np.copy(mesh._faces)
106
+ self._face_vacancies = copy.copy(mesh._face_vacancies)
107
+
108
+ self._halfedges = np.copy(mesh._halfedges)
109
+ self._halfedge_vacancies = copy.copy(mesh._halfedge_vacancies)
110
+ else:
111
+ self._vertices = np.zeros(vertices.shape[0], dtype=VERTEX_DTYPE)
112
+ self._vertices[:] = -1 # initialize everything to -1 to start with
113
+ self._vertices['position'] = vertices
114
+ self._vertex_vacancies = []
115
+
116
+ self._faces = None # Contains a pointer to one halfedge associated with each face
117
+ self._face_vacancies = []
118
+
119
+ # Halfedges
120
+ self._halfedges = None
121
+ self._halfedge_vacancies = []
122
+
123
+ print('initializing halfedges ...')
124
+ print('vertices.shape = %s, faces.shape = %s' % (vertices.shape, faces.shape))
125
+ if vertices.shape[0] >= MAX_VERTEX_COUNT:
126
+ raise RuntimeError('Maximum vertex count is %d, mesh has %d' % (MAX_VERTEX_COUNT, vertices.shape[0]))
127
+ self._initialize_halfedges(vertices, faces)
128
+ print('done initializing halfedges')
129
+
130
+ # Representation of faces by triplets of vertices
131
+ self._faces_by_vertex = None
132
+
133
+ # loop subdivision vars
134
+ self._loop_subdivision_flip_edges = []
135
+ self._loop_subdivision_new_vertices = []
136
+
137
+ # Singular edges
138
+ self._singular_edges = None
139
+ self._singular_vertices = None
140
+
141
+ # Populate the normals
142
+ self.face_normals
143
+ self.vertex_normals
144
+
145
+ # Properties we can visualize
146
+ self.vertex_properties = ['x', 'y', 'z', 'component', 'boundary', 'singular']
147
+
148
+ self.fix_boundary = True # Hold boundary edges in place
149
+ self.debug = False # Print debug statements
150
+
151
+ # Is the mesh manifold?
152
+ self._manifold = None
153
+
154
+ # Set debug, fix_boundary, etc.
155
+ for key, value in kwargs.items():
156
+ setattr(self, key, value)
157
+
158
+ def __getitem__(self, k):
159
+ # this defers evaluation of the properties until we actually access them,
160
+ # as opposed to the mappings which stored the values on class creation.
161
+ try:
162
+ res = getattr(self, k)
163
+ except AttributeError:
164
+ raise KeyError('Key %s not defined' % k)
165
+
166
+ return res
167
+
168
+ def __copy__(self):
169
+ return type(self)(mesh=self)
170
+
171
+ @classmethod
172
+ def from_stl(cls, filename, **kwargs):
173
+ """
174
+ Read from an STL file.
175
+ """
176
+ from PYME.IO.FileUtils import stl
177
+
178
+ # Load an STL from file
179
+ triangles_stl = stl.load_stl_binary(filename)
180
+
181
+ # Call from_np_stl on the file stream
182
+ return cls.from_np_stl(triangles_stl, **kwargs)
183
+
184
+ @classmethod
185
+ def from_ply(cls, filename, **kwargs):
186
+ """
187
+ Read from PLY file.
188
+ """
189
+ from PYME.IO.FileUtils import ply
190
+
191
+ # Load a PLY from file
192
+ vertices, faces, _ = ply.load_ply(filename)
193
+
194
+ return cls(vertices, faces, **kwargs)
195
+
196
+ @classmethod
197
+ def from_np_stl(cls, triangles_stl, **kwargs):
198
+ """
199
+ Read from an already-loaded STL stream.
200
+ """
201
+ vertices_raw = np.vstack((triangles_stl['vertex0'],
202
+ triangles_stl['vertex1'],
203
+ triangles_stl['vertex2']))
204
+ vertices, faces_raw = np.unique(vertices_raw,
205
+ return_inverse=True,
206
+ axis=0)
207
+ faces = faces_raw.reshape(faces_raw.shape[0] // 3, 3, order='F')
208
+
209
+ print('Data munged to vertices, faces')
210
+ return cls(vertices, faces, **kwargs)
211
+
212
+ @property
213
+ def x(self):
214
+ return self.vertices[:,0]
215
+
216
+ @property
217
+ def y(self):
218
+ return self.vertices[:,1]
219
+
220
+ @property
221
+ def z(self):
222
+ return self.vertices[:,2]
223
+
224
+ @property
225
+ def component(self):
226
+ # if np.all(self._vertices['component'] == -1):
227
+ self.find_connected_components()
228
+ return self._vertices['component']
229
+
230
+ @property
231
+ def vertices(self):
232
+ return self._vertices['position']
233
+
234
+ @property
235
+ def faces(self):
236
+ """
237
+ Returns faces by vertex for rendering and STL applications.
238
+ """
239
+ if self._faces_by_vertex is None:
240
+ faces = self._faces['halfedge'][self._faces['halfedge'] != -1]
241
+ v0 = self._halfedges['vertex'][self._halfedges['prev'][faces]]
242
+ v1 = self._halfedges['vertex'][faces]
243
+ v2 = self._halfedges['vertex'][self._halfedges['next'][faces]]
244
+ self._faces_by_vertex = np.vstack([v0, v1, v2]).T
245
+ return self._faces_by_vertex
246
+
247
+ @property
248
+ def boundary(self):
249
+ nn = self._vertices['neighbors']
250
+ nn_mask = (nn!=-1)
251
+ return np.any((self._halfedges['twin'][nn] == -1)*nn_mask, axis=1)
252
+
253
+ @property
254
+ def singular(self):
255
+ self._singular_edges = None
256
+ self._singular_vertices = None
257
+ singular_vertices = np.zeros_like(self._vertices['halfedge'])
258
+ if len(self.singular_vertices) > 0:
259
+ singular_vertices[np.array(self.singular_vertices)] = 1
260
+ return singular_vertices
261
+
262
+ @property
263
+ def singular_edges(self):
264
+ """
265
+ Get a list of singular edges. A singular edge is any edge shared by
266
+ more than two faces.
267
+ """
268
+ if self._singular_edges is None:
269
+ edges = np.vstack([self._halfedges['vertex'], self._halfedges[self._halfedges['prev']]['vertex']]).T
270
+ edges = edges[edges[:,0] != -1] # Drop non-edges
271
+ # packed_edges = pack_edges(edges)
272
+ packed_edges = np.sort(edges, axis=1)
273
+ e, c = np.unique(packed_edges, return_counts=True, axis=0)
274
+ singular_packed_edges = e[c>2]
275
+ singular_edges = []
276
+ for singular_packed_edge in singular_packed_edges:
277
+ singular_edges.extend(list(np.where(packed_edges[:, None] == singular_packed_edge)[0]))
278
+
279
+ singular_edges.extend(self._halfedges['twin'][singular_edges])
280
+ self._singular_edges = list(set(singular_edges) - set([-1]))
281
+
282
+ return self._singular_edges
283
+
284
+ @property
285
+ def singular_vertices(self):
286
+ """
287
+ Get a list of singular vertices. This includes endpoints of singular
288
+ edges, and isolated singular vertices. Isolated singular vertices
289
+ have more than one 1-neighbor ring.
290
+ """
291
+ if self._singular_vertices is None:
292
+ # Mark vertices that are endpoints of these edges
293
+ singular_vertices = list(set(list(np.hstack([self._halfedges['vertex'][self.singular_edges], self._halfedges['vertex'][self._halfedges['twin'][self.singular_edges]]]))))
294
+
295
+ # Mark isolated singular vertices
296
+ for _vertex in np.arange(self._vertices.shape[0]):
297
+ if self._vertices['halfedge'][_vertex] == -1:
298
+ continue
299
+
300
+ # If the number of elements in the 1-neighbor ring does not match
301
+ # the number of halfedges pointing to a vertex, we have an isolated
302
+ # singular vertex (note this requires the C code version of
303
+ # update_vertex_neighbors, which reverses direction upon hitting
304
+ # an edge to ensure a more accurate neighborhood)
305
+ n_incident = np.flatnonzero(self._halfedges['vertex'] == _vertex).size
306
+ n_neighbors = np.flatnonzero(self._vertices['neighbors'][_vertex] != -1).size
307
+ if n_neighbors != n_incident:
308
+ singular_vertices.append(_vertex)
309
+
310
+ # Remove duplicates
311
+ self._singular_vertices = list(set(singular_vertices) - set([-1]))
312
+
313
+ return self._singular_vertices
314
+
315
+ @property
316
+ def face_normals(self):
317
+ """
318
+ Return the normal of each triangular face.
319
+ """
320
+ if np.all(self._faces['normal'] == -1):
321
+ if USE_C:
322
+ triangle_mesh_utils.c_update_face_normals(list(np.arange(len(self._faces)).astype(np.int32)), self._halfedges, self._vertices, self._faces)
323
+ else:
324
+ v2 = self._vertices['position'][self._halfedges['vertex'][self._halfedges['prev'][self._faces['halfedge']]]]
325
+ v1 = self._vertices['position'][self._halfedges['vertex'][self._faces['halfedge']]]
326
+ v0 = self._vertices['position'][self._halfedges['vertex'][self._halfedges['next'][self._faces['halfedge']]]]
327
+ u = v2 - v1
328
+ v = v0 - v1
329
+ n = np.cross(u, v, axis=1)
330
+ nn = np.linalg.norm(n, axis=1)
331
+ self._faces['area'] = 0.5*nn
332
+ self._faces['normal'] = n/nn[:, None]
333
+ self._faces['normal'][np.isnan(self._faces['normal'])] = 0
334
+ self._faces['normal'][self._faces['halfedge'] == -1] = -1
335
+ return self._faces['normal'][self._faces['halfedge'] != -1]
336
+
337
+ @property
338
+ def face_areas(self):
339
+ """
340
+ Return the area of each triangular face.
341
+ """
342
+ if np.all(self._faces['area'] == -1):
343
+ self._faces['normal'][:] = -1
344
+ self.face_normals
345
+ return self._faces['area']
346
+
347
+ @property
348
+ def vertex_neighbors(self):
349
+ """
350
+ Return the up to NEIGHBORSIZE neighbors of each vertex.
351
+ """
352
+ if np.all(self._vertices['neighbors'] == -1):
353
+ if USE_C:
354
+ triangle_mesh_utils.c_update_vertex_neighbors(list(np.arange(len(self._vertices)).astype(np.int32)), self._halfedges, self._vertices, self._faces)
355
+ else:
356
+ for v_idx in np.arange(len(self._vertices)):
357
+ _orig = self._vertices['halfedge'][v_idx]
358
+ _curr = _orig
359
+
360
+ curr_edge = self._halfedges[_curr]
361
+ _twin = curr_edge['twin']
362
+ twin_edge = self._halfedges[_twin]
363
+
364
+ self._vertices['normal'][v_idx] = 0
365
+
366
+ i = 0
367
+ self._vertices['valence'][v_idx] = 0
368
+ while True:
369
+ if _curr == -1:
370
+ break
371
+ _vertex = curr_edge['vertex']
372
+ _face = curr_edge['face']
373
+ if (i < NEIGHBORSIZE):
374
+ self._vertices['neighbors'][v_idx, i] = _curr
375
+ n = self._faces['normal'][_face]
376
+ a = self._faces['area'][_face]
377
+ self._vertices['normal'][v_idx] += n*a
378
+
379
+ l = self._vertices['position'][_vertex] - self._vertices['position'][self._halfedges['vertex'][curr_edge['prev']]]
380
+ curr_edge['length'] = np.sqrt((l*l).sum())
381
+
382
+ _curr = twin_edge['next']
383
+ curr_edge = self._halfedges[_curr]
384
+ if _twin == -1:
385
+ break
386
+ _twin = curr_edge['twin']
387
+ twin_edge = self._halfedges[_twin]
388
+
389
+ i += 1
390
+ if (_curr == _orig):
391
+ break
392
+ self._vertices['valence'][v_idx] = i
393
+ nn = np.linalg.norm(self._vertices['normal'], axis=1)
394
+ self._vertices['normal'] /= nn[:, None]
395
+ self._vertices['normal'][np.isnan(self._vertices['normal'])] = 0
396
+ self._vertices['normal'][self._vertices['halfedge'] == -1] = -1
397
+
398
+ return self._vertices['neighbors']
399
+
400
+ @property
401
+ def vertex_normals(self):
402
+ """
403
+ Return the normal of each vertex.
404
+ """
405
+ if np.all(self._vertices['normal'] == -1):
406
+ self._vertices['neighbors'][:] = -1
407
+ self.vertex_neighbors
408
+ return self._vertices['normal']
409
+
410
+ @property
411
+ def valences(self):
412
+ """
413
+ Return the valence of each vertex.
414
+ """
415
+ if np.all(self._vertices['valence'] == -1):
416
+ self._vertices['neighbors'][:] = -1
417
+ self.vertex_neighbors
418
+ return self._vertices['valence']
419
+
420
+ @property
421
+ def manifold(self):
422
+ """
423
+ Checks if the mesh is manifold: Is every edge is shared by exactly two
424
+ triangles?
425
+
426
+ TODO: Check that we don't have any isolated singular vertices, which
427
+ also make the mesh non-manifold.
428
+ """
429
+ # edges = np.vstack([self._halfedges['vertex'], self._halfedges[self._halfedges['prev']]['vertex']]).T
430
+ # edges[edges[:, 0] != -1]
431
+ # edges = np.vstack([self.faces[:,[0,1]], self.faces[:,[1,2]], self.faces[:,[2,0]]])
432
+ # packed_edges = pack_edges(edges)
433
+ # _, c = np.unique(packed_edges, return_counts=True)
434
+
435
+ # return np.all(c==2)
436
+
437
+ if self._manifold is None:
438
+ self._singular_edges = None
439
+ self._singular_vertices = None
440
+ vertex_mask = (self._halfedges['vertex'] != -1)
441
+ twin_mask = (self._halfedges['twin'] == -1)
442
+ boundary_check = ((np.flatnonzero(vertex_mask & twin_mask).size) == 0)
443
+ self._manifold = (len(self.singular_vertices) == 0) & boundary_check
444
+
445
+ return self._manifold
446
+
447
+ def keys(self):
448
+ return list(self.vertex_properties)
449
+
450
+ def _initialize_halfedges(self, vertices, faces):
451
+ """
452
+ Accepts unordered vertices, indices parameterization of a triangular
453
+ mesh from an STL file and converts to topologically-connected halfedge
454
+ representation.
455
+
456
+ Parameters
457
+ ----------
458
+ vertices : np.array
459
+ N x 3 array of Euclidean points.
460
+ faces : np .array
461
+ M x 3 array of vertex indices indicating triangle connectivity.
462
+ Expects STL redundancy.
463
+ """
464
+ if vertices is not None and faces is not None:
465
+ # Unordered halfedges
466
+ edges = np.vstack([faces[:,[0,1]], faces[:,[1,2]], faces[:,[2,0]]])
467
+
468
+ # Now order them...
469
+ n_faces = len(faces)
470
+ n_edges = len(edges)
471
+ j = np.arange(n_faces)
472
+
473
+ self._halfedges = np.zeros(n_edges, dtype=HALFEDGE_DTYPE)
474
+ self._halfedges[:] = -1 # initialize everything to -1 to start with
475
+
476
+ self._halfedges['vertex'] = edges[:, 1]
477
+
478
+ self._faces = np.zeros(n_faces, dtype=FACE_DTYPE)
479
+ self._faces[:] = -1 # initialize everything to -1 to start with
480
+
481
+ # Create a unique handle for each edge
482
+ # edges_packed = pack_edges(edges)
483
+
484
+ # Sort the edges lo->hi so we can arrange them uniquely
485
+ # Convert to list of tuples for use with dictionary
486
+ edges_packed = [tuple(e) for e in np.sort(edges, axis=1)]
487
+
488
+ # Account for multivalent edges
489
+ # edges_unique, edges_counts = np.unique(edges, return_counts=True, axis=0)
490
+
491
+ # print('iterating edges')
492
+ # # Use a dictionary to keep track of which edges are already assigned twins
493
+ # d = {}
494
+ # multivalent_edge = False
495
+ # multivalent_dict = {}
496
+ # for i, e in enumerate(edges_packed):
497
+ # if e in d:
498
+ # idx = d.pop(e)
499
+ # if self._halfedges['vertex'][idx] == self._halfedges['vertex'][i]:
500
+ # # Push it back in the queue and let's see if the next time we
501
+ # # pull it out we get the right matching edge.
502
+ # d[e] = idx
503
+ # # Trip the flag
504
+ # multivalent_edge = True
505
+ # multivalent_dict[e] = i
506
+ # continue
507
+
508
+ # self._halfedges['twin'][idx] = i
509
+ # self._halfedges['twin'][i] = idx
510
+ # else:
511
+ # d[e] = i
512
+
513
+ print('iterating edges')
514
+ # Use a dictionary to keep track of which edges are already assigned twins
515
+ d = {}
516
+ for i, e in enumerate(edges_packed):
517
+ if e in d:
518
+ idx = d.pop(e)
519
+ if self._halfedges['vertex'][idx] == self._halfedges['vertex'][i]:
520
+ # Don't assign a halfedge to multivalent edges
521
+ continue
522
+
523
+ self._halfedges['twin'][idx] = i
524
+ self._halfedges['twin'][i] = idx
525
+ else:
526
+ d[e] = i
527
+
528
+ # print('multivalent')
529
+ # # Go back and grab the remaining multivalent edges
530
+ # if multivalent_edge:
531
+ # n_multivalent = len(list(multivalent_dict.keys()))
532
+ # print('%d multivalent edges' % n_multivalent)
533
+ # for j_, k in enumerate(list(multivalent_dict.keys())):
534
+ # if (not (j_ % 10)):
535
+ # print('mv edge %d of %d' % (j_, n_multivalent))
536
+ #
537
+ # idx = multivalent_dict.pop(k)
538
+ # for i, e in enumerate(edges_packed):
539
+ # if e != idx:
540
+ # continue
541
+ #
542
+ # if self._halfedges['vertex'][idx] == self._halfedges['vertex'][i]:
543
+ # continue
544
+ #
545
+ # self._halfedges['twin'][idx] = i
546
+ # self._halfedges['twin'][i] = idx
547
+ # break
548
+ #
549
+ # print('multivalent done')
550
+
551
+ self._halfedges['face'] = np.hstack([j, j, j])
552
+ self._halfedges['next'] = np.hstack([j+n_faces, j+2*n_faces, j])
553
+ self._halfedges['prev'] = np.hstack([j+2*n_faces, j, j+n_faces])
554
+ self._halfedges['length'] = np.linalg.norm(self._vertices['position'][self._halfedges['vertex']] - self._vertices['position'][self._halfedges['vertex'][self._halfedges['prev']]], axis=1)
555
+
556
+ # Set the vertex halfedges (multiassignment, but should be fine)
557
+ self._vertices['halfedge'][self._halfedges['vertex']] = self._halfedges['next']
558
+
559
+ self._faces['halfedge'] = j # Faces are defined by associated halfedges
560
+ else:
561
+ raise ValueError('Mesh does not contain vertices and faces.')
562
+
563
+ def _update_face_normals(self, f_idxs):
564
+ """
565
+ Recompute len(f_idxs) face normals.
566
+
567
+ Parameters
568
+ ----------
569
+ f_idxs : list or np.array
570
+ List of face indices to recompute.
571
+ """
572
+
573
+ if USE_C:
574
+ if isinstance(f_idxs, list):
575
+ f_idxs = np.int32(f_idxs)
576
+ triangle_mesh_utils.c_update_face_normals(f_idxs, self._halfedges, self._vertices, self._faces)
577
+ else:
578
+ for f_idx in f_idxs:
579
+ v2 = self._vertices['position'][self._halfedges['vertex'][self._halfedges['prev'][self._faces['halfedge'][f_idx]]]]
580
+ v1 = self._vertices['position'][self._halfedges['vertex'][self._faces['halfedge'][f_idx]]]
581
+ v0 = self._vertices['position'][self._halfedges['vertex'][self._halfedges['next'][self._faces['halfedge'][f_idx]]]]
582
+ u = v2 - v1
583
+ v = v0 - v1
584
+ n = fast_3x3_cross(u.squeeze(), v.squeeze())
585
+ nn = np.sqrt((n*n).sum())
586
+ self._faces['area'][f_idx] = 0.5*nn
587
+ if (self._faces['halfedge'][f_idx] == -1):
588
+ self._faces['normal'][f_idx] = -1
589
+ if (nn > 0):
590
+ self._faces['normal'][f_idx] = n/nn
591
+ else:
592
+ self._faces['normal'][f_idx] = 0
593
+
594
+ def _update_vertex_neighbors(self, v_idxs):
595
+ """
596
+ Recalculate len(v_idxs) vertex neighbors/normals.
597
+
598
+ Parameters
599
+ ----------
600
+ v_idxs : list or np.array
601
+ List of vertex indicies indicating which vertices to update.
602
+ """
603
+
604
+ if USE_C:
605
+ if isinstance(v_idxs, list):
606
+ v_idxs = np.int32(v_idxs)
607
+ triangle_mesh_utils.c_update_vertex_neighbors(v_idxs, self._halfedges, self._vertices, self._faces)
608
+ else:
609
+ for v_idx in v_idxs:
610
+ _orig = self._vertices['halfedge'][v_idx]
611
+ _curr = _orig
612
+
613
+ curr_edge = self._halfedges[_curr]
614
+ _twin = curr_edge['twin']
615
+ twin_edge = self._halfedges[_twin]
616
+
617
+ if self.debug and (twin_edge['vertex'] != v_idx):
618
+ print(_curr, curr_edge, _twin,twin_edge)
619
+ raise RuntimeError('Twin (%d) should point back to starting vertex (%d) but points to %d' % (_twin, v_idx, twin_edge['vertex']))
620
+
621
+ i = 0
622
+ self._vertices['valence'][v_idx] = 0
623
+ self._vertices['neighbors'][v_idx] = -1
624
+ self._vertices['normal'][v_idx] = 0
625
+
626
+ _normal = 0*self._vertices['normal'][v_idx] # avoid a few lookups by using a local variable
627
+
628
+ while True:
629
+ if (_curr == -1):
630
+ break
631
+
632
+ if (i < NEIGHBORSIZE):
633
+ self._vertices['neighbors'][v_idx, i] = _curr
634
+
635
+ #TODO - should these be in if clause?
636
+ _face = curr_edge['face']
637
+ n = self._faces['normal'][_face]
638
+ a = self._faces['area'][_face]
639
+ _normal += n*a
640
+ else:
641
+ pass
642
+ if self.debug and (i > 2*NEIGHBORSIZE):
643
+ print('Abnormal vertex valance detected on vertex %d' % v_idx)
644
+ # raise RuntimeError('Abnormal vertex valance detected on vertex %d' % v_idx)
645
+
646
+ vertex = self._vertices['position'][v_idx]
647
+ l = vertex - self._vertices['position'][curr_edge['vertex']]
648
+ l = np.sqrt((l*l).sum())
649
+ self._halfedges['length'][_curr] = l
650
+ if (_twin == -1):
651
+ break
652
+ self._halfedges['length'][_twin] = l
653
+ # curr_edge['length'] = l
654
+ # twin_edge['length'] = l
655
+
656
+ _curr = twin_edge['next']
657
+ curr_edge = self._halfedges[_curr]
658
+ _twin = curr_edge['twin']
659
+ twin_edge = self._halfedges[_twin]
660
+
661
+ i += 1
662
+
663
+ if (_curr == _orig):
664
+ break
665
+
666
+ self._vertices['valence'][v_idx] = i
667
+
668
+ if self.debug and (self._vertices['valence'][v_idx] < 3):
669
+ raise RuntimeError('Detected valence <3 on vertex %d' % v_idx)
670
+
671
+ nn = np.sqrt((_normal*_normal).sum())
672
+ if nn > 0:
673
+ self._vertices['normal'][v_idx] = _normal/nn
674
+ else:
675
+ self._vertices['normal'][v_idx] = 0
676
+
677
+ def _resize(self, vec, axis=0, skip_entries=False, return_orig=False, key=None):
678
+ """
679
+ Increase the size of an input vector 1.5x, keeping all active data.
680
+
681
+ Parameters
682
+ ---------
683
+ vec : np.array
684
+ Vector to expand, -1 stored in unused entries.
685
+ axis : int
686
+ Axis (0 or 1) along which to resize vector (only works for 2D
687
+ arrays).
688
+ skip_entries : bool
689
+ Don't copy -1 entries to the new array.
690
+ return_orig : bool
691
+ Return indices of non-negative-one entries in the array prior
692
+ to resize.
693
+ key : string
694
+ Optional array key on which to check for -1 value.
695
+ """
696
+
697
+ if (axis > 1) or (len(vec.shape) > 2):
698
+ raise NotImplementedError('This function only works for 2D arrays along axes 0 and 1.')
699
+
700
+ # Increase the size 1.5x along the axis.
701
+ dt = vec.dtype
702
+ new_size = np.array(vec.shape)
703
+ new_size[axis] = int(1.5*new_size[axis] + 0.5)
704
+ new_size = tuple(new_size)
705
+
706
+ # Allocate memory for new array
707
+ new_vec = np.empty(new_size, dtype=dt)
708
+ new_vec[:] = -1
709
+
710
+ if skip_entries:
711
+ # Check for invalid entries
712
+ if key:
713
+ copy_mask = (vec[key] != 1)
714
+ else:
715
+ copy_mask = (vec != -1)
716
+
717
+ if len(copy_mask.shape) > 1:
718
+ copy_mask = np.any(copy_mask, axis=(1-axis))
719
+ copy_size = np.flatnonzero(copy_mask).size
720
+ else:
721
+ copy_size = np.flatnonzero(copy_mask).size
722
+
723
+ if axis:
724
+ new_vec[:,:copy_size] = vec[:, copy_mask]
725
+ else:
726
+ new_vec[:copy_size] = vec[copy_mask]
727
+
728
+ # Return positions of non-negative-1 entries prior to re-ordering
729
+ if return_orig:
730
+ _orig = np.where(copy_mask)[0]
731
+ # Return empty list if original values are all at the same place
732
+ if np.all(_orig == np.arange(len(_orig))):
733
+ _orig = []
734
+ return new_vec, _orig
735
+
736
+ return new_vec
737
+ else:
738
+ # We're not skipping entries
739
+ if axis:
740
+ copy_size = vec.shape[1]
741
+ new_vec[:,:copy_size] = vec
742
+ else:
743
+ copy_size = vec.shape[0]
744
+ new_vec[:copy_size] = vec
745
+
746
+ if return_orig:
747
+ return new_vec, []
748
+
749
+ # Update array
750
+ return new_vec
751
+
752
+ def edge_collapse(self, _curr, live_update=True):
753
+ """
754
+ A.k.a. delete two triangles. Remove an edge, defined by halfedge _curr,
755
+ and its associated triangles, while keeping mesh connectivity.
756
+
757
+ Parameters
758
+ ----------
759
+ _curr: int
760
+ Pointer to halfedge defining edge to collapse.
761
+ live_update : bool
762
+ Update associated faces and vertices after collapse. Set to
763
+ False to handle this externally (useful if operating on
764
+ multiple, disjoint edges).
765
+ """
766
+
767
+ if _curr == -1:
768
+ return
769
+
770
+ # Create the pointers we need
771
+ curr_halfedge = self._halfedges[_curr]
772
+ _prev = curr_halfedge['prev']
773
+ _next = curr_halfedge['next']
774
+ _twin = curr_halfedge['twin']
775
+
776
+ if (self._halfedges['twin'][_next] == -1) or (self._halfedges['twin'][_prev] == -1):
777
+ # Collapsing this edge will create another free edge
778
+ return
779
+
780
+ interior = (_twin != -1) # Are we on a boundary?
781
+
782
+ if interior:
783
+ nn = self._vertices['neighbors'][curr_halfedge['vertex']]
784
+ nn_mask = (nn != -1)
785
+ if (-1 in self._halfedges['twin'][nn]*nn_mask):
786
+ return
787
+
788
+ nn = self._vertices['neighbors'][self._halfedges['vertex'][_twin]]
789
+ nn_mask = (nn != -1)
790
+ if (-1 in self._halfedges['twin'][nn]*nn_mask):
791
+ return
792
+
793
+ twin_halfedge = self._halfedges[_twin]
794
+ _twin_prev = twin_halfedge['prev']
795
+ _twin_next = twin_halfedge['next']
796
+
797
+ if self.debug:
798
+ assert(_twin_prev != -1)
799
+ assert(_twin_next != -1)
800
+
801
+ if (self._halfedges['twin'][_twin_prev] == -1) or (self._halfedges['twin'][_twin_next] == -1):
802
+ # Collapsing this edge will create another free edge
803
+ return
804
+
805
+ _dead_vertex = self._halfedges['vertex'][_prev]
806
+ _live_vertex = curr_halfedge['vertex']
807
+
808
+ # Grab the valences of the 4 points near the edge
809
+ if interior:
810
+ vn, vtn = self._vertices['valence'][self._halfedges['vertex'][_next]], self._vertices['valence'][self._halfedges['vertex'][_twin_next]]
811
+
812
+ # Make sure we create no vertices of valence <3 (manifoldness)
813
+ # ((vl + vd - 3) < 4) or
814
+ if (vn < 4) or (vtn < 4):
815
+ return
816
+
817
+ vl, vd = self._vertices['valence'][_live_vertex], self._vertices['valence'][_dead_vertex]
818
+
819
+ if ((vl + vd - 3) < 4):
820
+ return
821
+
822
+ # Check for creation of multivalent edges and prevent this (manifoldness)
823
+ fast_collapse_bool = (self.manifold and (vl < NEIGHBORSIZE) and (vd < NEIGHBORSIZE))
824
+ if fast_collapse_bool:
825
+ # Do it the fast way if we can
826
+ live_nn = self._vertices['neighbors'][_live_vertex]
827
+ dead_nn = self._vertices['neighbors'][_dead_vertex]
828
+ live_mask = (live_nn != -1)
829
+ dead_mask = (dead_nn != -1)
830
+ live_list = self._halfedges['vertex'][live_nn[live_mask]]
831
+ dead_list = self._halfedges['vertex'][dead_nn[dead_mask]]
832
+ else:
833
+ twin_mask = (self._halfedges['twin'] != -1)
834
+ dead_mask = (self._halfedges['vertex'] == _dead_vertex)
835
+ dead_list = self._halfedges['vertex'][self._halfedges['twin'][dead_mask & twin_mask]]
836
+ live_list = self._halfedges['vertex'][self._halfedges['twin'][(self._halfedges['vertex'] == _live_vertex) & twin_mask]]
837
+
838
+ twin_list = list((set(dead_list) & set(live_list)) - set([-1]))
839
+ if len(twin_list) != 2:
840
+ return
841
+
842
+ if self.debug and (_live_vertex == _dead_vertex):
843
+ print(_curr, curr_halfedge, _twin, twin_halfedge)
844
+ raise RuntimeError('Live vertex equals dead vertex (both halves of edge point to same place)')
845
+
846
+ # Collapse to the midpoint of the original edge vertices
847
+ if fast_collapse_bool:
848
+ self._halfedges['vertex'][self._halfedges['twin'][dead_nn[dead_mask]]] = _live_vertex
849
+ else:
850
+ self._halfedges['vertex'][dead_mask] = _live_vertex
851
+
852
+ _live_pos = self._vertices['position'][_live_vertex]
853
+ _dead_pos = self._vertices['position'][_dead_vertex]
854
+ self._vertices['position'][_live_vertex] = 0.5*(_live_pos + _dead_pos)
855
+
856
+ # update valence of vertex we keep
857
+ self._vertices['valence'][_live_vertex] = vl + vd - 3
858
+
859
+ if self.debug:
860
+ print(self._vertices['valence'][_live_vertex], self._vertices['valence'][_dead_vertex])
861
+ assert(self._vertices['valence'][_live_vertex] >=3)
862
+
863
+ # delete dead vertex
864
+ self._vertices[_dead_vertex] = -1
865
+
866
+ # Zipper the remaining triangles
867
+ def _zipper(edge1, edge2):
868
+ # DB optimization notes:
869
+ # 1) there is a (very?) small performance penalty for nested functions - move this up to class level?
870
+ # 2) This would be a good candidate for a c/cython implementation (but possibly applies to whole edge collapse function)
871
+ # 3) function can be simplified (see _zipper1 implementation below). Probably minimal performance impact in
872
+ # python version, but logic flow likely necessary in c/cython (avoids indexing with -1)
873
+ t1 = self._halfedges['twin'][edge1]
874
+ t2 = self._halfedges['twin'][edge2]
875
+
876
+ if edge1 == -1:
877
+ t1 = -1
878
+ if edge2 == -1:
879
+ t2 = -1
880
+
881
+ if (t1 != -1) and (t2 != -1):
882
+ self._halfedges['twin'][t2] = t1
883
+ self._halfedges['twin'][t1] = t2
884
+ else:
885
+ if (t1 != -1):
886
+ self._halfedges['twin'][t1] = -1
887
+ if (t2 != -1):
888
+ self._halfedges['twin'][t2] = -1
889
+
890
+ def _zipper1(edge1, edge2):
891
+ t1 = -1 if edge1 == -1 else self._halfedges['twin'][edge1]
892
+ t2 = -1 if edge2 == -1 else self._halfedges['twin'][edge2]
893
+
894
+ if (t1 != -1):
895
+ self._halfedges['twin'][t1] = t2
896
+
897
+ if (t2 != -1):
898
+ self._halfedges['twin'][t2] = t1
899
+
900
+
901
+ _zipper1(_next, _prev)
902
+ if interior:
903
+ _zipper1(_twin_next, _twin_prev)
904
+
905
+ # We need some more pointers
906
+ # TODO: make these safer
907
+ _prev_twin = self._halfedges['twin'][_prev]
908
+ _prev_twin_vertex = self._halfedges['vertex'][_prev_twin]
909
+ _next_prev_twin = self._halfedges['next'][_prev_twin]
910
+ _next_prev_twin_vertex = self._halfedges['vertex'][_next_prev_twin]
911
+ if interior:
912
+ _twin_next_vertex = self._halfedges['vertex'][_twin_next]
913
+ _next_twin_twin_next = self._halfedges['next'][self._halfedges['twin'][_twin_next]]
914
+ _next_twin_twin_next_vertex = self._halfedges['vertex'][_next_twin_twin_next]
915
+
916
+ # Make sure we have good _vertex_halfedges references
917
+ self._vertices['halfedge'][_live_vertex] = _prev_twin
918
+ self._vertices['halfedge'][_prev_twin_vertex] = _next_prev_twin
919
+ if interior:
920
+ self._vertices['halfedge'][_twin_next_vertex] = self._halfedges['twin'][_twin_next]
921
+ self._vertices['halfedge'][_next_twin_twin_next_vertex] = self._halfedges['next'][_next_twin_twin_next]
922
+
923
+ # Grab faces to update
924
+ face0 = self._halfedges['face'][self._halfedges['twin'][_next]]
925
+ face1 = self._halfedges['face'][self._halfedges['twin'][_prev]]
926
+ if interior:
927
+ face2 = self._halfedges['face'][self._halfedges['twin'][_twin_next]]
928
+ face3 = self._halfedges['face'][self._halfedges['twin'][_twin_prev]]
929
+
930
+ # Delete the inner triangles
931
+ self._face_delete(_curr)
932
+ if interior:
933
+ self._face_delete(_twin)
934
+
935
+ if self.debug and interior:
936
+ print(curr_halfedge, twin_halfedge)
937
+
938
+ try:
939
+ if live_update:
940
+ if interior:
941
+ # Update faces
942
+ self._update_face_normals([face0, face1, face2, face3])
943
+ self._update_vertex_neighbors([_live_vertex, _prev_twin_vertex, _next_prev_twin_vertex, _twin_next_vertex])
944
+ else:
945
+ self._update_face_normals([face0, face1])
946
+ self._update_vertex_neighbors([_live_vertex, _prev_twin_vertex, _next_prev_twin_vertex])
947
+ self._faces_by_vertex = None
948
+
949
+ except RuntimeError as e:
950
+ print(_curr, _twin, _next, _prev, _twin_next, _twin_prev, _next_prev_twin, _next_twin_twin_next, _prev_twin)
951
+ print([_live_vertex, _prev_twin_vertex, _next_prev_twin_vertex, _twin_next_vertex])
952
+ raise e
953
+
954
+ def _face_delete(self, _edge):
955
+ """
956
+ Delete a face defined by the halfedge _edge.
957
+
958
+ Note that this does not account for adjusting halfedges, twins.
959
+
960
+ Parameters
961
+ ----------
962
+ edge : int
963
+ One self._halfedge index of the edge defining a face to delete.
964
+ """
965
+ if _edge == -1:
966
+ return
967
+
968
+ curr_edge = self._halfedges[_edge]
969
+ if curr_edge['vertex'] == -1:
970
+ return
971
+ self._faces[curr_edge['face']] = -1
972
+
973
+ self._face_vacancies.append(curr_edge['face'])
974
+
975
+ self._edge_delete(curr_edge['next'])
976
+ self._edge_delete(curr_edge['prev'])
977
+ self._edge_delete(_edge)
978
+
979
+ def _edge_delete(self, _edge):
980
+ """
981
+ Delete edge _edge in place from self._halfedges.
982
+
983
+ Note that this does not account for adjusting halfedges, twins.
984
+
985
+ Parameters
986
+ ----------
987
+ edge : int
988
+ One self._halfedge index of the edge to delete.
989
+ """
990
+ if (_edge == -1):
991
+ return
992
+
993
+ self._halfedges[_edge] = -1
994
+ self._halfedge_vacancies.append(_edge)
995
+
996
+ self._faces_by_vertex = None
997
+
998
+ def _insert(self, el, el_arr, el_vacancies, key, compact=False, insert_key=None, **kwargs):
999
+ """
1000
+ Insert an element into an array at the position of the smallest empty
1001
+ entry when searching the array by key.
1002
+
1003
+ Parameters
1004
+ ----------
1005
+ el
1006
+ Element to add to the array. Must be one of the dtypes in the
1007
+ structured dtype of el_arr.
1008
+ el_arr : np.array
1009
+ Array where we add the element
1010
+ el_vacancies : list
1011
+ External list where we keep empty positions of el_arr.
1012
+ key : string
1013
+ Key on which to search the array
1014
+ insert_key : string
1015
+ Key on which to insert element el. This is by default the
1016
+ search key.
1017
+ compact : bool
1018
+ Do we copy -1 values in the resize of el_arr?
1019
+ kwargs
1020
+ List of dtype parameters to additionally define el.
1021
+
1022
+ Returns
1023
+ -------
1024
+ ed
1025
+ Element added, includes el stored in full structured dtype of
1026
+ el_arr.
1027
+ idx : int
1028
+ Index of el_arr where ed is stored.
1029
+ el_arr : np.array
1030
+ Modified array containing ed.
1031
+ el_vacancies : list
1032
+ Modified list of el_arr vacancies.
1033
+ """
1034
+ try:
1035
+ idx = el_vacancies.pop(0)
1036
+ except IndexError:
1037
+ # no vacant slot, resize
1038
+ el_arr = self._resize(el_arr, skip_entries=compact, key=key)
1039
+ #TODO - invalidate neighbours, vertex_halfedges etc ???
1040
+
1041
+ # NOTE: If we search by a different key next time, el_vacancies will
1042
+ # still contain the vacant positions when searching on the previous key.
1043
+ if len(el_arr[key].shape) > 1:
1044
+ # NOTE: If we search on self._vertices['position'] and one position is [-1,-1,-1],
1045
+ # which is not unreasonable, this will replace that vertex. Hence we provide the
1046
+ # option to search and insert on different keys.
1047
+ el_vacancies = [int(x) for x in np.argwhere(np.all(el_arr[key] == -1, axis=1))]
1048
+ else:
1049
+ # el_vacancies = [int(x) for x in np.flatnonzero(el_arr[key] == -1)]
1050
+ el_vacancies = np.flatnonzero(el_arr[key] == -1).tolist()
1051
+
1052
+ idx = el_vacancies.pop(0)
1053
+
1054
+ if idx == -1:
1055
+ raise ValueError('Index cannot be -1.')
1056
+
1057
+ if insert_key is None:
1058
+ # Default to searching and inserting on the same key
1059
+ insert_key = key
1060
+
1061
+ # Put el in el_arr
1062
+ ed = el_arr[idx]
1063
+ ed[insert_key] = el
1064
+ # Define additional structured dtype values for el
1065
+ for k, v in kwargs.items():
1066
+ ed[k] = v
1067
+
1068
+ return ed, idx, el_arr, el_vacancies
1069
+
1070
+ def _new_edge(self, vertex, compact=False, **kwargs):
1071
+ """
1072
+ Create a new edge.
1073
+
1074
+ Parameters
1075
+ ----------
1076
+ vertex : idx
1077
+ self._vertices index which this halfedge points to
1078
+ compact : bool
1079
+ Do we copy -1 values in the resize of _halfedges?
1080
+ kwargs
1081
+ List of _halfedge dtype parameters (e.g. prev=10, next=2) to
1082
+ define halfedge via more than just its vertex.
1083
+
1084
+ Returns
1085
+ -------
1086
+ ed : HALFEDGE_DTYPE
1087
+ Halfedge created
1088
+ idx : int
1089
+ Index of the halfedge in self._halfedges.
1090
+ """
1091
+
1092
+ ed, idx, self._halfedges, self._halfedge_vacancies = self._insert(vertex, self._halfedges, self._halfedge_vacancies, 'vertex', compact, None, **kwargs)
1093
+
1094
+ return ed, idx
1095
+
1096
+ def _new_face(self, _edge, compact=False, **kwargs):
1097
+ """
1098
+ Create a new face based on edge _edge.
1099
+
1100
+ Parameters
1101
+ ----------
1102
+ _edge : idx
1103
+ self._halfedges index of a single halfedge on this face
1104
+ compact : bool
1105
+ Do we copy -1 values in the resize of _faces?
1106
+ kwargs
1107
+ List of _faces dtype parameters (e.g. area=13.556) to define
1108
+ face via more than just one of its halfedges.
1109
+
1110
+ Returns
1111
+ -------
1112
+ fa : FACE_DTYPE
1113
+ Face created
1114
+ idx : int
1115
+ Index of the face in self._faces.
1116
+ """
1117
+
1118
+ fa, idx, self._faces, self._face_vacancies = self._insert(_edge, self._faces, self._face_vacancies, 'halfedge', compact, None, **kwargs)
1119
+
1120
+ return fa, idx
1121
+
1122
+ def _new_vertex(self, _vertex, compact=False, **kwargs):
1123
+ """
1124
+ Insert a new vertex into the mesh.
1125
+
1126
+ Parameters
1127
+ ----------
1128
+ _vertex : np.array
1129
+ 1D array of x, y, z coordinates of the new vertex.
1130
+ compact : bool
1131
+ Do we copy -1 values in the resize of _vertices?
1132
+ kwargs
1133
+ List of _vertices dtype parameters (e.g. valence=2) to define
1134
+ vertex via more than just its position.
1135
+
1136
+ Returns
1137
+ -------
1138
+ vx : VERTEX_DTYPE
1139
+ Vertex created
1140
+ idx : int
1141
+ Index of the vertex in self._vertices.
1142
+ """
1143
+ vx, idx, self._vertices, self._vertex_vacancies = self._insert(_vertex, self._vertices, self._vertex_vacancies, 'halfedge', compact, 'position', **kwargs)
1144
+
1145
+ self._faces_by_vertex = None # Reset
1146
+
1147
+ return vx, idx
1148
+
1149
+ def edge_split(self, _curr, live_update=True, upsample=False):
1150
+ """
1151
+ Split triangles evenly along an edge specified by halfedege index _curr.
1152
+
1153
+ Parameters
1154
+ ----------
1155
+ _curr : int
1156
+ Pointer to halfedge defining edge to split.
1157
+ live_update : bool
1158
+ Update associated faces and vertices after split. Set to False
1159
+ to handle this externally (useful if operating on multiple,
1160
+ disjoint edges).
1161
+ upsample: bool
1162
+ Are we doing loop subdivision? If so, keep track of all edges
1163
+ incident on both a new vertex and an old verex that do not
1164
+ split an existing edge.
1165
+ """
1166
+ if _curr == -1:
1167
+ return
1168
+
1169
+ curr_edge = self._halfedges[_curr]
1170
+ _prev = curr_edge['prev']
1171
+ _next = curr_edge['next']
1172
+
1173
+ # Grab the new vertex position
1174
+ _vertex = 0.5*(self._vertices['position'][curr_edge['vertex'], :] + self._vertices['position'][self._halfedges['vertex'][_prev], :])
1175
+ _, _vertex_idx = self._new_vertex(_vertex)
1176
+
1177
+ _twin = curr_edge['twin']
1178
+ interior = (_twin != -1) # Are we on a boundary?
1179
+
1180
+ if interior:
1181
+ twin_edge = self._halfedges[_twin]
1182
+ _twin_prev = twin_edge['prev']
1183
+ _twin_next = twin_edge['next']
1184
+
1185
+ # Ensure the original faces have the correct pointers and add two new faces
1186
+ self._faces['halfedge'][curr_edge['face']] = _curr
1187
+ if interior:
1188
+ self._faces['halfedge'][twin_edge['face']] = _twin
1189
+ _, _face_1_idx = self._new_face(_twin_prev)
1190
+ self._halfedges['face'][_twin_prev] = _face_1_idx
1191
+ _, _face_2_idx = self._new_face(_next)
1192
+ self._halfedges['face'][_next] = _face_2_idx
1193
+
1194
+ # Insert the new faces
1195
+ _, _he_0_idx = self._new_edge(self._halfedges['vertex'][_next], prev=_curr, next=_prev, face=self._halfedges['face'][_curr])
1196
+ if interior:
1197
+ _, _he_1_idx = self._new_edge(_vertex_idx, prev=_twin_next, next=_twin, face=self._halfedges['face'][_twin])
1198
+
1199
+ _, _he_2_idx = self._new_edge(self._halfedges['vertex'][_twin_next], next=_twin_prev, face=_face_1_idx)
1200
+ _, _he_3_idx = self._new_edge(_vertex_idx, prev=_twin_prev, next=_he_2_idx, face=_face_1_idx)
1201
+ self._halfedges['prev'][_he_2_idx] = _he_3_idx
1202
+
1203
+ _, _he_4_idx = self._new_edge(self._halfedges['vertex'][_curr], next=_next, face=_face_2_idx, twin=-1)
1204
+ _, _he_5_idx = self._new_edge(_vertex_idx, prev=_next, next=_he_4_idx, face=_face_2_idx)
1205
+ self._halfedges['prev'][_he_4_idx] = _he_5_idx
1206
+
1207
+ self._halfedges['twin'][_he_0_idx] = _he_5_idx
1208
+ self._halfedges['twin'][_he_5_idx] = _he_0_idx
1209
+
1210
+ if interior:
1211
+ self._halfedges['twin'][_he_1_idx] = _he_2_idx
1212
+ self._halfedges['twin'][_he_2_idx] = _he_1_idx
1213
+
1214
+ self._halfedges['twin'][_he_3_idx] = _he_4_idx
1215
+ self._halfedges['twin'][_he_4_idx] = _he_3_idx
1216
+
1217
+ # Update _prev, next
1218
+ self._halfedges['prev'][_prev] = _he_0_idx
1219
+ self._halfedges['prev'][_next] = _he_4_idx
1220
+ self._halfedges['next'][_next] = _he_5_idx
1221
+
1222
+ if interior:
1223
+ # Update _twin_next, _twin_prev
1224
+ self._halfedges['next'][_twin_next] = _he_1_idx
1225
+ self._halfedges['prev'][_twin_prev] = _he_2_idx
1226
+ self._halfedges['next'][_twin_prev] = _he_3_idx
1227
+
1228
+ self._halfedges['prev'][_twin] = _he_1_idx
1229
+ # Update _curr and _twin
1230
+ self._halfedges['vertex'][_curr] = _vertex_idx
1231
+ self._halfedges['next'][_curr] = _he_0_idx
1232
+
1233
+ # Update halfedges
1234
+ if interior:
1235
+ self._vertices['halfedge'][self._halfedges['vertex'][_he_2_idx]] = _he_1_idx
1236
+ self._vertices['halfedge'][self._halfedges['vertex'][_prev]] = _curr
1237
+ self._vertices['halfedge'][self._halfedges['vertex'][_he_4_idx]] = _next
1238
+ self._vertices['halfedge'][_vertex_idx] = _he_4_idx
1239
+ self._vertices['halfedge'][self._halfedges['vertex'][_he_0_idx]] = _he_5_idx
1240
+
1241
+ if upsample:
1242
+ # Make sure these edges emanate from the new vertex stored at _vertex_idx
1243
+ if interior:
1244
+ self._loop_subdivision_flip_edges.extend([_he_2_idx])
1245
+
1246
+ self._loop_subdivision_flip_edges.extend([_he_0_idx])
1247
+ self._loop_subdivision_new_vertices.extend([_vertex_idx])
1248
+
1249
+ if live_update:
1250
+ if interior:
1251
+ self._update_face_normals([self._halfedges['face'][_he_0_idx], self._halfedges['face'][_he_1_idx], self._halfedges['face'][_he_2_idx], self._halfedges['face'][_he_4_idx]])
1252
+ self._update_vertex_neighbors([self._halfedges['vertex'][_curr], self._halfedges['vertex'][_twin], self._halfedges['vertex'][_he_0_idx], self._halfedges['vertex'][_he_2_idx], self._halfedges['vertex'][_he_4_idx]])
1253
+ else:
1254
+ self._update_face_normals([self._halfedges['face'][_he_0_idx], self._halfedges['face'][_he_4_idx]])
1255
+ self._update_vertex_neighbors([self._halfedges['vertex'][_curr], self._halfedges['vertex'][_prev], self._halfedges['vertex'][_he_0_idx], self._halfedges['vertex'][_he_4_idx]])
1256
+ self._faces_by_vertex = None
1257
+
1258
+ def edge_flip(self, _curr, live_update=True):
1259
+ """
1260
+ Flip an edge specified by halfedge index _curr.
1261
+
1262
+ Parameters
1263
+ ----------
1264
+ _curr : int
1265
+ Pointer to halfedge defining edge to flip.
1266
+ live_update : bool
1267
+ Update associated faces and vertices after flip. Set to False
1268
+ to handle this externally (useful if operating on multiple,
1269
+ disjoint edges).
1270
+ """
1271
+
1272
+ if (_curr == -1):
1273
+ return
1274
+
1275
+ curr_edge = self._halfedges[_curr]
1276
+ _twin = curr_edge['twin']
1277
+ if (_twin == -1):
1278
+ # This is a boundary edge
1279
+ return
1280
+ _prev = curr_edge['prev']
1281
+ _next = curr_edge['next']
1282
+
1283
+ twin_edge = self._halfedges[_twin]
1284
+ _twin_prev = twin_edge['prev']
1285
+ _twin_next = twin_edge['next']
1286
+
1287
+ # Make sure both vertices have valence > 3 (preserve manifoldness)
1288
+ vc, vt = self._vertices['valence'][curr_edge['vertex']], self._vertices['valence'][twin_edge['vertex']]
1289
+ if (vc < 4) or (vt < 4):
1290
+ return
1291
+
1292
+ # Calculate adjustments to the halfedges we're flipping
1293
+ new_v0 = self._halfedges['vertex'][_next]
1294
+ new_v1 = self._halfedges['vertex'][_twin_next]
1295
+
1296
+ # If there's already an edge between these two vertices, don't flip (preserve manifoldness)
1297
+ # NOTE: This is potentially a problem if we start with a high-valence mesh. In that case, swap this
1298
+ # check with the more expensive commented one below.
1299
+
1300
+ # Check for creation of multivalent edges and prevent this (manifoldness)
1301
+ fast_collapse_bool = (self.manifold and (vc < NEIGHBORSIZE) and (vt < NEIGHBORSIZE))
1302
+ if fast_collapse_bool:
1303
+ if new_v1 in self._vertices['neighbors'][new_v0]:
1304
+ return
1305
+ else:
1306
+ if new_v1 in self._halfedges['vertex'][self._halfedges['twin'][self._halfedges['vertex'] == new_v0]]:
1307
+ return
1308
+
1309
+ # Convexity check: Let's see if the midpoint of the flipped edge will be above or below the plane of the
1310
+ # current edge
1311
+ flip_midpoint = 0.5*(self._vertices['position'][new_v0] + self._vertices['position'][new_v1])
1312
+ plane_midpoint = (1./3)*(self._vertices['position'][curr_edge['vertex']] + self._vertices['position'][self._halfedges['vertex'][curr_edge['next']]] + self._vertices['position'][self._halfedges['vertex'][curr_edge['prev']]])
1313
+ flipped_dot = ((self._faces['normal'][curr_edge['face']])*(flip_midpoint - plane_midpoint)).sum()
1314
+
1315
+ if flipped_dot < 0:
1316
+ # If flipping moves the midpoint of the edge below the original triangle's plane, this introduces
1317
+ # concavity, so don't flip.
1318
+ return
1319
+
1320
+ # _next's next and prev must be adjusted
1321
+ self._halfedges['prev'][_next] = _twin_prev
1322
+ self._halfedges['next'][_next] = _twin
1323
+
1324
+ # _twin_next's next and prev must be adjusted
1325
+ self._halfedges['prev'][_twin_next] = _prev
1326
+ self._halfedges['next'][_twin_next] = _curr
1327
+
1328
+ # _prev's next and prev must be updated
1329
+ self._halfedges['prev'][_prev] = _curr
1330
+ self._halfedges['next'][_prev] = _twin_next
1331
+
1332
+ # _twin_prev's next and prev must be updated
1333
+ self._halfedges['prev'][_twin_prev] = _twin
1334
+ self._halfedges['next'][_twin_prev] = _next
1335
+
1336
+ # Don't even bother with checking, just make sure to update the
1337
+ # vertex_halfedges references to halfedges we know work
1338
+ self._vertices['halfedge'][curr_edge['vertex']] = _next
1339
+ self._vertices['halfedge'][twin_edge['vertex']] = _twin_next
1340
+
1341
+ # Apply adjustments to the the halfedges we're flipping
1342
+ curr_edge['vertex'] = new_v0
1343
+ twin_edge['vertex'] = new_v1
1344
+ curr_edge['next'] = _prev
1345
+ twin_edge['next'] = _twin_prev
1346
+ curr_edge['prev'] = _twin_next
1347
+ twin_edge['prev'] = _next
1348
+
1349
+ # Update pointers
1350
+ _prev = curr_edge['prev']
1351
+ _next = curr_edge['next']
1352
+ _twin_prev = twin_edge['prev']
1353
+ _twin_next = twin_edge['next']
1354
+
1355
+ # Set faces
1356
+ self._halfedges['face'][_next] = curr_edge['face']
1357
+ self._halfedges['face'][_prev] = curr_edge['face']
1358
+ self._halfedges['face'][_twin_next] = twin_edge['face']
1359
+ self._halfedges['face'][_twin_prev] = twin_edge['face']
1360
+
1361
+ # Finish updating vertex_halfedges references, update face references
1362
+ self._vertices['halfedge'][curr_edge['vertex']] = _twin
1363
+ self._vertices['halfedge'][twin_edge['vertex']] = _curr
1364
+ self._faces['halfedge'][curr_edge['face']] = _curr
1365
+ self._faces['halfedge'][twin_edge['face']] = _twin
1366
+
1367
+ if live_update:
1368
+ # Update face and vertex normals
1369
+ self._update_face_normals([curr_edge['face'], twin_edge['face']])
1370
+ self._update_vertex_neighbors([curr_edge['vertex'], twin_edge['vertex'], self._halfedges['vertex'][_next], self._halfedges['vertex'][_twin_next]])
1371
+
1372
+ self._faces_by_vertex = None
1373
+
1374
+ def regularize(self):
1375
+ """
1376
+ Adjust vertices so they tend toward valence VALENCE
1377
+ (or BOUNDARY_VALENCE for boundaries).
1378
+ """
1379
+ flip_count = 0
1380
+
1381
+ # Do a single pass over all active edges and flip them if the flip minimizes the deviation of vertex valences
1382
+ # for i in np.arange(len(self._halfedges['vertex'])):
1383
+ # if (self._halfedges['vertex'][i] < 0):
1384
+ # continue
1385
+ for i in np.flatnonzero(self._halfedges['vertex'] != -1):
1386
+
1387
+ curr_edge = self._halfedges[i]
1388
+ _twin = curr_edge['twin']
1389
+ twin_edge = self._halfedges[_twin]
1390
+
1391
+ target_valence = VALENCE
1392
+ if _twin == -1:
1393
+ # boundary
1394
+ target_valence = BOUNDARY_VALENCE
1395
+
1396
+ # Pre-flip vertices
1397
+ v1 = self._vertices['valence'][curr_edge['vertex']] - target_valence
1398
+ v2 = self._vertices['valence'][twin_edge['vertex']] - target_valence
1399
+
1400
+ # Post-flip vertices
1401
+ v3 = self._vertices['valence'][self._halfedges['vertex'][curr_edge['next']]] - target_valence
1402
+ v4 = self._vertices['valence'][self._halfedges['vertex'][twin_edge['next']]] - target_valence
1403
+
1404
+ # Check valence deviation from VALENCE (or
1405
+ # BOUNDARY_VALENCE for boundaries) pre- and post-flip
1406
+ score_pre = np.abs([v1,v2,v3,v4]).sum()
1407
+ score_post = np.abs([v1-1,v2-1,v3+1,v4+1]).sum()
1408
+
1409
+ if score_post < score_pre:
1410
+ # Flip minimizes deviation of vertex valences from VALENCE (or
1411
+ # BOUNDARY_VALENCE for boundaries)
1412
+ self.edge_flip(i)
1413
+ flip_count += 1
1414
+
1415
+ print('Flip count: %d' % (flip_count))
1416
+
1417
+ def relax(self, l=1, n=1):
1418
+ """
1419
+ Perform n iterations of Lloyd relaxation on the mesh.
1420
+
1421
+ Parameters
1422
+ ----------
1423
+ l : float
1424
+ Regularization (damping) term, used to avoid oscillations.
1425
+ n : int
1426
+ Number of iterations to apply.
1427
+ """
1428
+
1429
+ for k in range(n):
1430
+ # Get vertex neighbors
1431
+ nn = self._vertices['neighbors']
1432
+ nn_mask = (self._vertices['neighbors'] != -1)
1433
+ vn = self._vertices['position'][self._halfedges['vertex'][nn]]
1434
+
1435
+ # Weight by distance to neighors
1436
+ an = (1./self._halfedges['length'][nn])*nn_mask
1437
+ an[self._halfedges['length'][nn] == 0] = 0
1438
+
1439
+ # Calculate gravity-weighted centroids
1440
+ A = np.sum(an, axis=1)
1441
+ c = 1./(A[...,None])*np.sum(an[...,None]*vn, axis=1)
1442
+ # Don't get messed up by slivers. NOTE: This is a hack in case
1443
+ # edge split/collapse don't do their jobs.
1444
+ A_mask = (A == 0)
1445
+ c[A_mask] = self._vertices['position'][A_mask]
1446
+ if self.fix_boundary:
1447
+ # Don't move vertices on a boundary
1448
+ boundary = self._halfedges[(self._halfedges['twin'] == -1)]
1449
+ tn = np.hstack([boundary['vertex'], self._halfedges['vertex'][boundary['prev']]])
1450
+ c[tn] = self._vertices['position'][tn]
1451
+
1452
+ # Construct projection vector into tangent plane
1453
+ pn = self._vertices['normal'][...,None]*self._vertices['normal'][:,None,:]
1454
+ p = np.eye(3)[None,:] - pn
1455
+
1456
+ # Update vertex positions
1457
+ self._vertices['position'] = self._vertices['position'] + l*np.sum(p*((c-self._vertices['position'])[...,None]),axis=1)
1458
+
1459
+ # Now we gotta recalculate the normals
1460
+ self._faces['normal'][:] = -1
1461
+ self._vertices['normal'][:] = -1
1462
+ self.face_normals
1463
+ self.vertex_normals
1464
+
1465
+ def remesh(self, n=5, target_edge_length=-1, l=0.5, n_relax=10):
1466
+ """
1467
+ Produce a higher-quality mesh.
1468
+
1469
+ Follows procedure in Botsch and Kobbelt, A Remeshing Approach to
1470
+ Multiresoluton Modeling, Eurographics Symposium on Geometry Processing,
1471
+ 2004.
1472
+
1473
+ Parameters
1474
+ ----------
1475
+ n : int
1476
+ Number of remeshing iterations to apply.
1477
+ target_edge_length : float
1478
+ Target edge length for all edges in the mesh.
1479
+ l : float
1480
+ Relaxation regularization term, used to avoid oscillations.
1481
+ n_relax : int
1482
+ Number of Lloyd relaxation (relax()) iterations to apply
1483
+ per remeshing iteration.
1484
+ """
1485
+
1486
+ if (target_edge_length == -1):
1487
+ # Guess edge_length
1488
+ target_edge_length = np.mean(self._halfedges['length'][self._halfedges['length'] != -1])
1489
+
1490
+ for k in range(n):
1491
+ # 1. Split all edges longer than (4/3)*target_edge_length at their midpoint.
1492
+ split_count = 0
1493
+ for i in np.arange(len(self._halfedges['length'])):
1494
+ if (self._halfedges['vertex'][i] != -1) and (self._halfedges['length'][i] > 1.33*target_edge_length):
1495
+ self.edge_split(i)
1496
+ split_count += 1
1497
+ print('Split count: %d' % (split_count))
1498
+
1499
+ if self.debug:
1500
+ # check for dud half edges
1501
+ _valid = self._halfedges['vertex'] != -1
1502
+ assert(not np.any(self._halfedges['twin'][_valid] == -1))
1503
+ assert (not np.any(self._halfedges['next'][_valid] == -1))
1504
+ assert (not np.any(self._halfedges['vertex'][self._halfedges['next'][_valid]] == -1))
1505
+
1506
+ assert (not np.any(self._halfedges['next'][self._halfedges['twin'][_valid]] == self._halfedges['twin'][self._halfedges['prev'][_valid]]))
1507
+
1508
+ assert(self._vertices['valence'][self._vertices['valence']>0].min() >=3)
1509
+
1510
+ # 2. Collapse all edges shorter than (4/5)*target_edge_length to their midpoint.
1511
+ collapse_count = 0
1512
+ for i in np.arange(len(self._halfedges['length'])):
1513
+ if (self._halfedges['vertex'][i] != -1) and (self._halfedges['length'][i] < 0.8*target_edge_length):
1514
+ self.edge_collapse(i)
1515
+ collapse_count += 1
1516
+ print('Collapse count: ' + str(collapse_count))
1517
+
1518
+ # 3. Flip edges in order to minimize deviation from VALENCE.
1519
+ self.regularize()
1520
+
1521
+ # 4. Relocate vertices on the surface by tangential smoothing.
1522
+ self.relax(l=l, n=n_relax)
1523
+
1524
+ # Let's double-check the mesh manifoldness
1525
+ self._manifold = None
1526
+ self.manifold
1527
+
1528
+ def upsample(self, n=1):
1529
+ """
1530
+ Upsample the mesh by a factor of 4 (factor of 2 along each axis in the
1531
+ plane of the mesh) and smooth meshes by Loop's 5/8 and 3/8 rule.
1532
+
1533
+ References:
1534
+ 1. C. T. Loop, "Smooth Subdivision surfaces Based on Triangles,"
1535
+ University of Utah, 1987
1536
+ 2. http://462cmu.github.io/asst2_meshedit/, task 4
1537
+
1538
+ Parameters
1539
+ ----------
1540
+ n : int
1541
+ Number of upsampling operations to perform.
1542
+ """
1543
+
1544
+ for k in range(n):
1545
+ # Reset
1546
+ split_edges = {}
1547
+ self._loop_subdivision_new_vertices = []
1548
+ self._loop_subdivision_flip_edges = []
1549
+ new_vertex_idxs = []
1550
+
1551
+ # Mark the current edges as old
1552
+ edges_to_split = np.where(self._halfedges['length'] != -1)[0]
1553
+ split_halfedges = self._halfedges[edges_to_split]
1554
+
1555
+ # Compute new vertex positions for old vertices
1556
+ old_vertex_idxs = split_halfedges['vertex']
1557
+ old_vertices = self._vertices[old_vertex_idxs]
1558
+ old_neighbors = old_vertices['neighbors']
1559
+ neg_mask = (old_neighbors != -1)
1560
+ # alpha is a function of vertex order N that satisifies constraints on equation 3.4 of Loop's thesis
1561
+ alpha = np.tanh(LOOP_ALPHA_FACTOR*old_vertices['valence'])
1562
+ 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)
1563
+
1564
+ # Compute new vertex positions for new vertices
1565
+ p0 = 0.375*(old_vertices['position'] + self._vertices['position'][self._halfedges['vertex'][split_halfedges['twin']]])
1566
+ p1 = 0.125*(self._vertices['position'][self._halfedges['vertex'][split_halfedges['next']]] + self._vertices['position'][self._halfedges['vertex'][self._halfedges['next'][split_halfedges['twin']]]])
1567
+ new_vertex_positions = p0 + p1
1568
+
1569
+ # 1. Split every edge in the mesh
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
+
1578
+ # 2. Flip any new edge that touches an old vertex and a new vertex
1579
+ edges_to_flip = list(set(self._halfedges['vertex'][self._loop_subdivision_flip_edges]) - set(self._loop_subdivision_new_vertices))
1580
+ for e in edges_to_flip:
1581
+ self.edge_flip(e)
1582
+
1583
+ # Get any boundary vertices
1584
+ boundary_vertex_idxs = self._halfedges['vertex'][(self._halfedges['twin'] == -1)]
1585
+ boundary_vertex_positions = self._vertices['position'][boundary_vertex_idxs]
1586
+
1587
+ # Copy new position values into self._vertices['position']
1588
+ self._vertices['position'][old_vertex_idxs] = old_vertex_positions
1589
+ self._vertices['position'][self._loop_subdivision_new_vertices] = new_vertex_positions[new_vertex_idxs]
1590
+
1591
+ # Restore boundary vertex positions
1592
+ self._vertices['position'][boundary_vertex_idxs] = boundary_vertex_positions
1593
+
1594
+ self._manifold = None
1595
+
1596
+ def downsample(self, n_triangles=None):
1597
+ """
1598
+ Mesh downsampling via quadratic error metrics.
1599
+
1600
+ Follows procedure in Garland and Heckbert, Surface Simplification Using
1601
+ Quadtratic Error Metrics.
1602
+
1603
+ Parameters
1604
+ ----------
1605
+ n_triangles : int
1606
+ Target number of triangles in the downsampled mesh.
1607
+ """
1608
+
1609
+ import PYME.experimental._treap as treap
1610
+
1611
+ # 1. Compute quadratic matrices for all initial vertices
1612
+ v = self._vertices['position']
1613
+ n = self._vertices['normal']
1614
+ d = (-n*v).sum(1)
1615
+ p = np.vstack([n.T, d]).T # plane
1616
+ vn = self._vertices['neighbors']
1617
+ vn_mask = (vn != -1)
1618
+ pp = p[self._halfedges['vertex'][vn]]*vn_mask[...,None] # neighbor planes
1619
+ Q = (pp[...,None]*pp[:,:,None,:]).sum(1) # per-vertex quadratic
1620
+
1621
+ # 2. Select all valid pairs
1622
+ # For now, operate on edges. In the future we can also operate on any
1623
+ # pair of vertex positions such that || v_1 - v_2 || < t, where v_1,
1624
+ # v_2 \in R^3, and t \in R is a threshold.
1625
+
1626
+ # 3. Compute optimal contraction target and cost of each contraction
1627
+ # NOTE: we do this for all halfedges, empty or not. This saves us from having to search for
1628
+ # unique halfedges later in step 5. Instead, we just ignore -1 values.
1629
+ edges_mask = (self._halfedges['vertex'] != -1)
1630
+ edges = np.vstack([self._halfedges['vertex'], self._halfedges['vertex'][self._halfedges['prev']]]).T
1631
+ # Mask and shift to avoid a copy operation on Q
1632
+ Q_mask = np.ones((4,4))
1633
+ Q_mask[3,:] = 0
1634
+ Q_shift = np.zeros((4,4))
1635
+ Q_shift[3,3] = 1
1636
+ # h is the solution vector for least squares
1637
+ h = np.array([0,0,0,1])
1638
+ # 3a. Compute the optimal contraction target for each valid pair
1639
+ Q_edge = (Q[edges]).sum(1) # Q paired
1640
+ try:
1641
+ vp = (np.linalg.inv(Q_edge*Q_mask[None,...] + Q_shift[None,...])*h).sum(2)
1642
+ except(np.linalg.LinAlgError):
1643
+ vp = 0.5*np.sum(self._vertices['position'][edges],axis=1)
1644
+ vp = np.vstack([vp.T, np.ones(vp.shape[0])]).T
1645
+
1646
+ # 3b. Compute cost of contractions
1647
+ err = ((vp[:,None,:]*Q_edge).sum(2)*vp).sum(1)
1648
+
1649
+ # 4. Place all the pairs in a heap keyed on cost with the minimum cost pair on top
1650
+ cost_heap = treap.Treap()
1651
+ for i in (np.arange(len(edges))[edges_mask]):
1652
+ cost_heap.insert(np.float32(err[i]), np.int32(i))
1653
+
1654
+ # 5. Iteratively remove the pair of least cost from the heap,
1655
+ # contract this pair, and update the costs of all valid
1656
+ # pairs involving the remaining live vertex.
1657
+ n_faces = np.sum(self._faces['halfedge'] != -1)
1658
+ if n_triangles is None:
1659
+ # Assume we want to downsample by a factor of 4
1660
+ n_triangles = int(0.25*n_faces)
1661
+ collapse_tries = int(0.125*(n_faces - n_triangles))
1662
+ while True:
1663
+ if n_faces <= n_triangles:
1664
+ # Sometimes edge_collapse does not collapse the edge
1665
+ # because it would affect the manifoldness of the mesh,
1666
+ # so we need to see how far off we are from the desired
1667
+ # number of triangles n_triangles.
1668
+ tmp = np.sum(self._faces['halfedge'] != -1)
1669
+ if (n_faces == tmp) or (collapse_tries == 0):
1670
+ # We don't want to double check ourselves forever
1671
+ break
1672
+ # Update our estimate and keep refining the mesh
1673
+ n_faces = tmp
1674
+ collapse_tries -= 1
1675
+
1676
+ # Grab the edge with minimum cost
1677
+ _, _edge = cost_heap.pop()
1678
+
1679
+ if _edge == -1:
1680
+ print('Edge was -1???')
1681
+ continue
1682
+
1683
+ # Compute the new quadratic
1684
+ edge = self._halfedges[_edge]
1685
+ # Grab the live and dead vertex
1686
+ _live_vertex = edge['vertex']
1687
+ _dead_vertex = self._halfedges['vertex'][edge['prev']]
1688
+ Q_edge[_edge] = Q[_live_vertex] + Q[_dead_vertex]
1689
+
1690
+ if (_live_vertex == -1) or (_dead_vertex == -1):
1691
+ print('Vertex was -1???')
1692
+ continue
1693
+
1694
+ # Grab all neighboring halfedges
1695
+ nn_live = self._vertices['neighbors'][_live_vertex]
1696
+ nn_live_mask = (nn_live != -1)
1697
+ nn_live_twin = self._halfedges['twin'][nn_live[nn_live_mask]]
1698
+ nn_live_twin_mask = (nn_live_twin != -1)
1699
+ nn_dead = self._vertices['neighbors'][_dead_vertex]
1700
+ nn_dead_mask = (nn_dead != -1)
1701
+ nn_dead_twin = self._halfedges['twin'][nn_dead[nn_dead_mask]]
1702
+ nn_dead_twin_mask = (nn_dead_twin != -1)
1703
+ 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]])
1704
+
1705
+ # Eliminate edges touching either vertex from the cost heap
1706
+ for neighbor in neighbors:
1707
+ cost_heap.delete(np.int32(neighbor))
1708
+
1709
+ # Collapse the edge
1710
+ self.edge_collapse(_edge)
1711
+
1712
+ # Decrement the estimate of the number of triangles in the
1713
+ # mesh accordingly
1714
+ n_faces -= 2
1715
+
1716
+ # Set the quadratic of _live_vertex to the edge quadratic
1717
+ Q[_live_vertex] = Q_edge[_edge]
1718
+ # self._vertices['position'][_live_vertex] = vp[_edge,:3]
1719
+
1720
+ # Grab the new neighbors
1721
+ nn_live = self._vertices['neighbors'][_live_vertex]
1722
+ nn_live_mask = (nn_live != -1)
1723
+ nn_live_twin = self._halfedges['twin'][nn_live[nn_live_mask]]
1724
+ nn_live_twin_mask = (nn_live_twin != -1)
1725
+ neighbors = np.hstack([nn_live[nn_live_mask], nn_live_twin[nn_live_twin_mask]])
1726
+
1727
+ # Update the costs and positions of each of these neighbors and re-insert them into the queue
1728
+ new_edges = np.vstack([self._halfedges['vertex'][neighbors], self._halfedges['vertex'][self._halfedges['prev'][neighbors]]]).T
1729
+ Q_edge[neighbors] = (Q[new_edges]).sum(1)
1730
+
1731
+ try:
1732
+ vp[neighbors] = (np.linalg.inv(Q_edge[neighbors]*Q_mask[None,...] + Q_shift[None,...])*h).sum(2)
1733
+ except(np.linalg.LinAlgError):
1734
+ vp_temp = 0.5*np.sum(self._vertices['position'][new_edges],axis=1)
1735
+ vp[neighbors] = np.vstack([vp_temp.T, np.ones(vp_temp.shape[0])]).T
1736
+
1737
+ err[neighbors] = ((vp[neighbors,None,:]*Q_edge[neighbors]).sum(2)*vp[neighbors]).sum(1)
1738
+
1739
+ for n in neighbors:
1740
+ cost_heap.insert(np.float32(err[n]), np.int32(n))
1741
+
1742
+ self._manifold = None
1743
+
1744
+ def find_connected_components(self):
1745
+ """
1746
+ Label connected components of the mesh.
1747
+ """
1748
+
1749
+ # Set all components to -1 (background)
1750
+ self._halfedges['component'] = -1
1751
+ self._vertices['component'] = -1
1752
+ self._faces['component'] = -1
1753
+
1754
+ # Connect component by vertex
1755
+ component = 0
1756
+ for _vertex in np.arange(self._vertices.shape[0]):
1757
+ curr_vertex = self._vertices[_vertex]
1758
+ if (curr_vertex['halfedge'] == -1) or (curr_vertex['component'] != -1):
1759
+ # We only want to deal with valid, unassigned vertices
1760
+ continue
1761
+
1762
+ # Search vertices contains vertices connected to this vertex.
1763
+ # We want to see if there are other vertices connected to the
1764
+ # search vertices.
1765
+ search_vertices = []
1766
+ self._vertices[_vertex]['component'] = component
1767
+ search_vertices.append(_vertex)
1768
+
1769
+ # Find vertices connected to the search vertices
1770
+ while search_vertices:
1771
+ # Grab the latest search vertex neighbors
1772
+ _v = search_vertices.pop()
1773
+ nn = self._vertices['neighbors'][_v]
1774
+ # Loop over the neighbors
1775
+ for _edge in nn:
1776
+ if _edge == -1:
1777
+ continue
1778
+ # Assign the vertex component label to halfedges and faces
1779
+ # emanating from this vertex
1780
+ curr_edge = self._halfedges[_edge]
1781
+ self._halfedges['component'][_edge] = component
1782
+ self._halfedges['component'][curr_edge['prev']] = component
1783
+ self._halfedges['component'][curr_edge['next']] = component
1784
+ self._faces['component'][curr_edge['face']] = component
1785
+
1786
+ # If the vertex is assigned, we've already visited it and
1787
+ # don't need to do so again.
1788
+ if self._vertices['component'][curr_edge['vertex']] != -1:
1789
+ continue
1790
+ self._vertices['component'][curr_edge['vertex']] = component
1791
+ search_vertices.append(curr_edge['vertex'])
1792
+
1793
+ # Increment the label as any vertices not included in this
1794
+ # iteration's search will be part of another component.
1795
+ component += 1
1796
+
1797
+ def keep_largest_connected_component(self):
1798
+ # Find the connected components
1799
+ self.find_connected_components()
1800
+
1801
+ # Which connected component is largest?
1802
+ com, counts = np.unique(self._vertices['component'][self._vertices['component']!=-1], return_counts=True)
1803
+ max_count = np.argmax(counts)
1804
+ max_com = com[max_count]
1805
+
1806
+ # Remove the smaller components
1807
+ _vertices = np.where((self._vertices['component'] != max_com))[0]
1808
+ _edges = np.where((self._halfedges['component'] != max_com))[0]
1809
+ _edges_with_twins = _edges[self._halfedges['twin'][_edges] != -1]
1810
+ _faces = np.where((self._faces['component'] != max_com))[0]
1811
+ self._vertices[_vertices] = -1
1812
+ _kept_edges = self._halfedges['twin'][_edges_with_twins]
1813
+ self._halfedges['twin'][_kept_edges] = -1
1814
+ self._halfedges[_edges] = -1
1815
+ self._faces[_faces] = -1
1816
+
1817
+ # Mark deleted faces as available
1818
+ self._vertex_vacancies = list(set(self._vertex_vacancies).union(set(_vertices)))
1819
+ self._halfedge_vacancies = list(set(self._halfedge_vacancies).union(set(_edges)))
1820
+ self._face_vacancies = list(set(self._face_vacancies).union(set(_faces)))
1821
+
1822
+ # Update newly orphaned edges
1823
+ self._update_face_normals(list(set(self._halfedges['face'][_kept_edges])))
1824
+ self._update_vertex_neighbors(list(set(self._halfedges['vertex'][_kept_edges])))
1825
+
1826
+ self._faces_by_vertex = None
1827
+
1828
+ def _find_boundary_polygons(self):
1829
+ """
1830
+ Return a list of closed polygons, each defined by a list of halfedges,
1831
+ where each polygon defines a boundary in the mesh.
1832
+ """
1833
+ boundary_polygons = []
1834
+
1835
+ # 1. Construct an initial list of boundary edges
1836
+ boundary_edges = list(np.where((self._halfedges['twin'] == -1) & (self._halfedges['vertex'] != -1))[0])
1837
+
1838
+ # 2. Get the boundary polygons
1839
+ while len(boundary_edges) > 0:
1840
+ _root = boundary_edges[-1] # initial halfedge defining the polygon
1841
+ curr_polygon = [_root]
1842
+ # Step next-twin-next to move around the boundary
1843
+ _next = self._halfedges['next'][_root]
1844
+ _twin = self._halfedges['twin'][_next]
1845
+ _curr = self._halfedges['next'][_twin]
1846
+ if self._halfedges['twin'][_next] == -1:
1847
+ _curr = _next
1848
+ if self._halfedges['twin'][_twin] == -1:
1849
+ _curr = _twin
1850
+
1851
+ # We check open_chain instead of _curr != root since open_chain accounts for closed polygons
1852
+ # ending at an isolated singular vertex (two boundaries emit from a single vertex)
1853
+ open_chain = (self._halfedges['vertex'][self._halfedges['prev'][_root]] != self._halfedges['vertex'][curr_polygon[-1]])
1854
+
1855
+ # Loop around the boundary until we return the initial halfedge
1856
+ max_polygon_size = 100 # Put an upper limit on the polygon sizes so we don't look forever
1857
+ while open_chain and (max_polygon_size > 0):
1858
+ # We're not always guaranteed to hit the boundary again right
1859
+ # away doing next-twin-next, so wait until we do
1860
+ twin_next_attemps = 2*BOUNDARY_VALENCE
1861
+ while (twin_next_attemps > 0) and (self._halfedges['twin'][_curr] != -1):
1862
+
1863
+ # Need to do twin next in between boundary edges
1864
+ _twin = self._halfedges['twin'][_curr]
1865
+ _curr = self._halfedges['next'][_twin]
1866
+ if _curr == -1:
1867
+ # We hit a isolated singular vertex
1868
+ break
1869
+ twin_next_attemps -= 1
1870
+
1871
+ if _curr == -1:
1872
+ break
1873
+
1874
+ curr_polygon.append(_curr)
1875
+
1876
+ _next = self._halfedges['next'][_curr]
1877
+ _twin = self._halfedges['twin'][_next]
1878
+ _curr = self._halfedges['next'][_twin]
1879
+
1880
+ if self._halfedges['twin'][_next] == -1:
1881
+ _curr = _next
1882
+ if self._halfedges['twin'][_twin] == -1:
1883
+ _curr = _twin
1884
+
1885
+ open_chain = (self._halfedges['vertex'][self._halfedges['prev'][_root]] != self._halfedges['vertex'][curr_polygon[-1]])
1886
+ max_polygon_size -= 1
1887
+
1888
+ if max_polygon_size == 0:
1889
+ print('Polygon search took too long. Canceled.')
1890
+
1891
+ if ((_curr == -1) and (open_chain)) or (max_polygon_size == 0):
1892
+ # We failed to find a boundary polygon
1893
+ boundary_edges.pop()
1894
+ continue
1895
+ else:
1896
+ # We found a boundary polygon!
1897
+ boundary_polygons.append(curr_polygon)
1898
+ # Delete the polygon from the potential polygon list
1899
+ for edge in curr_polygon:
1900
+ if edge in boundary_edges:
1901
+ boundary_edges.remove(edge)
1902
+
1903
+ return boundary_polygons
1904
+
1905
+ def _fill_triangle(self, h0, h1, h2):
1906
+ """
1907
+ Create a triangle inside three halfedges.
1908
+
1909
+ Note that this does not update the vertices or faces.
1910
+ """
1911
+
1912
+ _h0_twin_vertex = self._halfedges['vertex'][h2]
1913
+ _, _h0_twin = self._new_edge(_h0_twin_vertex, twin=h0)
1914
+ _, _face = self._new_face(_h0_twin)
1915
+ _h1_twin_vertex = self._halfedges['vertex'][h0]
1916
+ _, _h1_twin = self._new_edge(_h1_twin_vertex, twin=h1, face=_face, next=_h0_twin)
1917
+ self._halfedges['face'][_h0_twin] = _face
1918
+ self._halfedges['prev'][_h0_twin] = _h1_twin
1919
+ _h2_twin_vertex = self._halfedges['vertex'][h1]
1920
+ _, _h2_twin = self._new_edge(_h2_twin_vertex, twin=h2, face=_face, prev=_h0_twin, next=_h1_twin)
1921
+ self._halfedges['next'][_h0_twin] = _h2_twin
1922
+ self._halfedges['prev'][_h1_twin] = _h2_twin
1923
+
1924
+ if h0 != -1:
1925
+ self._halfedges['twin'][h0] = _h0_twin
1926
+ if h1 != -1:
1927
+ self._halfedges['twin'][h1] = _h1_twin
1928
+ if h2 != -1:
1929
+ self._halfedges['twin'][h2] = _h2_twin
1930
+
1931
+ def _fan_triangulation(self, polygon):
1932
+ """
1933
+ Triangulate a polygon, defined by halfedges in the mesh,
1934
+ by creating a fan emanating from a single vertex out to
1935
+ the boundary halfedges.
1936
+
1937
+ Parameters
1938
+ ----------
1939
+ polygon : list
1940
+ List of halfedges defining a closed polygon in the mesh.
1941
+ """
1942
+
1943
+ if len(polygon) < 3:
1944
+ raise ValueError('polygon must be of length >= 3')
1945
+
1946
+ # Reverse order
1947
+ polygon = polygon[::-1]
1948
+
1949
+ while len(polygon) > 0:
1950
+ h0 = polygon.pop()
1951
+ h1 = polygon.pop()
1952
+ if (len(polygon) == 1):
1953
+ # Base case, make a triangle
1954
+ # polygon = polygon[::-1]
1955
+ h2 = polygon.pop()
1956
+ self._fill_triangle(h0,h1,h2)
1957
+ _h0_twin = self._halfedges['twin'][h0]
1958
+ else:
1959
+ # Create a new triangle with a boundary edge
1960
+ self._fill_triangle(h0, h1, -1)
1961
+
1962
+ # Due to the way fill_triangle works, we need to set h0's vertex after the fact
1963
+ _h0_twin = self._halfedges['twin'][h0]
1964
+ self._halfedges['vertex'][_h0_twin] = self._halfedges['vertex'][self._halfedges['prev'][h0]]
1965
+
1966
+ # Adjust the boundary
1967
+ polygon.append(self._halfedges['next'][_h0_twin])
1968
+
1969
+ # Update faces and vertices
1970
+ self._update_face_normals([self._halfedges['face'][_h0_twin], self._halfedges['face'][h0], self._halfedges['face'][h1]])
1971
+ self._update_vertex_neighbors([self._halfedges['vertex'][_h0_twin], self._halfedges['vertex'][h0], self._halfedges['vertex'][h1]])
1972
+
1973
+ def _zig_zag_triangulation(self, polygon):
1974
+ """
1975
+ Triangulate a polygon, defined by halfedges in the mesh,
1976
+ by creating zig-zagging between edges of the boundary.
1977
+
1978
+ Parameters
1979
+ ----------
1980
+ polygon : list
1981
+ List of halfedges defining a closed polygon in the mesh.
1982
+ """
1983
+
1984
+ if len(polygon) < 3:
1985
+ raise ValueError('polygon must be of length >= 3')
1986
+
1987
+ # Reverse order
1988
+ polygon = polygon[::-1]
1989
+ odd = True
1990
+
1991
+ while len(polygon) > 0:
1992
+ h0 = polygon.pop()
1993
+ h1 = polygon.pop()
1994
+ if (len(polygon) == 1):
1995
+ # Base case, make a triangle
1996
+ # polygon = polygon[::-1]
1997
+ h2 = polygon.pop()
1998
+ self._fill_triangle(h0,h1,h2)
1999
+ _h0_twin = self._halfedges['twin'][h0]
2000
+ else:
2001
+ # Create a new triangle with a boundary edge
2002
+ self._fill_triangle(h0, h1, -1)
2003
+
2004
+ # Due to the way fill_triangle works, we need to set h0's vertex after the fact
2005
+ _h0_twin = self._halfedges['twin'][h0]
2006
+ self._halfedges['vertex'][_h0_twin] = self._halfedges['vertex'][self._halfedges['prev'][h0]]
2007
+
2008
+ # Adjust the boundary
2009
+ polygon = polygon[::-1] # zig-zag
2010
+ if odd:
2011
+ polygon.insert(-1,self._halfedges['next'][_h0_twin])
2012
+ else:
2013
+ polygon.append(self._halfedges['next'][_h0_twin])
2014
+ odd = (not odd)
2015
+
2016
+ # Update faces and vertices
2017
+ self._update_face_normals([self._halfedges['face'][_h0_twin], self._halfedges['face'][h0], self._halfedges['face'][h1]])
2018
+ self._update_vertex_neighbors([self._halfedges['vertex'][_h0_twin], self._halfedges['vertex'][h0], self._halfedges['vertex'][h1]])
2019
+
2020
+ def _fill_holes(self, method='zig-zag'):
2021
+ """
2022
+ Fill holes in the mesh.
2023
+
2024
+ Parameters
2025
+ ----------
2026
+ method : string
2027
+ The method to use to patch boundary polygons. A string listed
2028
+ in options.
2029
+ """
2030
+
2031
+ options = ['fan', 'zig-zag']
2032
+
2033
+ if method not in options:
2034
+ print('Unknown triangulation method. Using default.')
2035
+ method = 'zig-zag'
2036
+
2037
+ boundary_polygons = self._find_boundary_polygons()
2038
+
2039
+ # Find length 2 polygons and zipper them ("pinch")
2040
+ to_remove = []
2041
+ for polygon in boundary_polygons:
2042
+ if len(polygon) == 2:
2043
+ self._halfedges['twin'][polygon[0]] = polygon[1]
2044
+ self._halfedges['twin'][polygon[1]] = polygon[0]
2045
+ to_remove.append(polygon)
2046
+
2047
+ # Eliminate the length 2 polygons before triangulation
2048
+ for polygon in to_remove:
2049
+ boundary_polygons.remove(polygon)
2050
+
2051
+ # Triangulate
2052
+ if method == 'fan':
2053
+ for polygon in boundary_polygons:
2054
+ self._fan_triangulation(polygon)
2055
+
2056
+ if method == 'zig-zag':
2057
+ for polygon in boundary_polygons:
2058
+ self._zig_zag_triangulation(polygon)
2059
+
2060
+ # Reset display
2061
+ self._faces_by_vertex = None
2062
+
2063
+ def _connect_neighbors(self, _nn, starting_component=0):
2064
+ """
2065
+ Connect the neighbors in the list _nn starting from
2066
+ starting_component.
2067
+
2068
+ Parameters
2069
+ ----------
2070
+ _nn : list
2071
+ List of neighbors to connect.
2072
+ starting_component : int
2073
+ Component numbering to start at.
2074
+
2075
+ Returns
2076
+ -------
2077
+ component : int
2078
+ Maxmimum component number used to group neighbors.
2079
+ """
2080
+ # Assign components via the 1-neighbors
2081
+ component = starting_component
2082
+ for _neighbor in _nn:
2083
+ singular = (_neighbor in self.singular_edges)
2084
+ if singular:
2085
+ component += 1
2086
+ if self._faces['component'][self._halfedges['face'][_neighbor]] != -1:
2087
+ continue
2088
+ self._faces['component'][self._halfedges['face'][_neighbor]] = component
2089
+
2090
+ # Extra check in case we're examining the edges out of order
2091
+ if singular:
2092
+ component += 1
2093
+
2094
+ # Loop through the neighbors again and group connected components
2095
+ for _neighbor in _nn:
2096
+ if (_neighbor in self.singular_edges):
2097
+ continue
2098
+
2099
+ _twin_neighbor = self._halfedges['twin'][_neighbor]
2100
+ if _twin_neighbor != -1:
2101
+ _face = self._halfedges['face'][_neighbor]
2102
+ _twin_face = self._halfedges['face'][_twin_neighbor]
2103
+ min_component = np.min([self._faces['component'][_face], self._faces['component'][_twin_face]])
2104
+ self._faces['component'][_face] = min_component
2105
+ self._faces['component'][_twin_face] = min_component
2106
+
2107
+ return component
2108
+
2109
+ def _group_neighbors(self, _vertex):
2110
+ """
2111
+ Group faces around the 1-neighbor ring of a vertex by reachability.
2112
+ Faces are not reachable if they are separated by a singular edge.
2113
+
2114
+ Parameters
2115
+ ----------
2116
+ _vertex : int
2117
+ Index of vertex at the center of a 1-neighbor ring.
2118
+ """
2119
+
2120
+ # Zero out connectivity
2121
+ self._vertices['component'][:] = -1
2122
+ self._faces['component'][:] = -1
2123
+ self._halfedges['component'][:] = -1
2124
+
2125
+ # Grab the neighbors
2126
+ _nn = self._vertices[_vertex]['neighbors']
2127
+ _nn = _nn[_nn!=-1]
2128
+
2129
+ # Connect the neighbors
2130
+ component = self._connect_neighbors(_nn, 0)
2131
+
2132
+ # Don't trust self._vertices['neighbors'] because we're dealing with
2133
+ # singular vertices
2134
+ _twin_nn = list(np.where(self._halfedges['vertex'] == _vertex)[0])
2135
+
2136
+ # If there were any incident vertices not included in this list, group them, too
2137
+ _remaining = list(set(_twin_nn) - set(self._halfedges['twin'][_nn]) - set([-1]))
2138
+
2139
+ if _remaining:
2140
+ component += 1 # We're off the original 1-neighbor ring, so disconnect
2141
+ self._connect_neighbors(_remaining, component)
2142
+
2143
+ def _remove_singularities(self):
2144
+ """
2145
+ Excise singularities from the mesh. This essentially unzips multivalent
2146
+ edges from the rest of the mesh, creating new, smaller connected
2147
+ components, each of which is free of singularities. This follows the
2148
+ local method for cutting outlined in Gueziec et al.
2149
+
2150
+ References
2151
+ ----------
2152
+ Gueziec et al. Cutting and Stitching: Converting Sets of Polygons
2153
+ to Manifold Surfaces, IEEE TRANSACTIONS ON VISUALIZATION AND
2154
+ COMPUTER GRAPHICS, 2001.
2155
+ """
2156
+
2157
+ # Make sure we're working with the latest singular edges/vertices
2158
+ self._singular_edges = None
2159
+ self._singular_vertices = None
2160
+
2161
+ # For each marked vertex, partition the faces of the 1-neighbor ring
2162
+ # into nc "is reachable" equivalence classes. Faces are reachable if
2163
+ # they share a non-singular edge incident on the marked vertex.
2164
+ for _vertex in self.singular_vertices[::-1]:
2165
+
2166
+ _twin_nn = list(np.where(self._halfedges['vertex'] == _vertex)[0])
2167
+ _update_vertices = np.hstack([_vertex, self._halfedges['vertex'][self._halfedges['twin'][_twin_nn]]])
2168
+ # _update_faces = self._faces[self._halfedges['face'][_twin_nn]]
2169
+
2170
+ self._group_neighbors(_vertex)
2171
+
2172
+ # Create nc-1 copies of the vertex and assign each equivalence class
2173
+ # one of these vertices. (All but one component gets a new vertex).
2174
+ components = np.unique(self._faces['component'][self._faces['component'] != -1])
2175
+ for c in components[1:]:
2176
+ _faces = self._faces[self._faces['component'] == c]
2177
+ _edges = np.hstack([self._halfedges['prev'][_faces['halfedge']], _faces['halfedge'], self._halfedges['next'][_faces['halfedge']]])
2178
+ _vertices = self._halfedges['vertex'][_edges]
2179
+
2180
+ _modified_edges = _edges[_vertices == _vertex]
2181
+
2182
+ _edge = self._halfedges['next'][_modified_edges[0]]
2183
+
2184
+ _, _new_vertex = self._new_vertex(self._vertices['position'][_vertex], halfedge=_edge)
2185
+ # Assign edges in this component connected to _vertex to _new_vertex
2186
+ self._halfedges['vertex'][_modified_edges] = _new_vertex
2187
+
2188
+ # Disconnect the boundaries of the components
2189
+ # Any edge pointing to the new vertex with a twin in a
2190
+ # different component must be reassigned
2191
+ _twin_face_component = self._faces['component'][self._halfedges['face'][self._halfedges['twin'][_modified_edges]]]
2192
+ _boundary = (_twin_face_component != c)
2193
+ _twin_edges = self._halfedges['twin'][_modified_edges[_boundary]]
2194
+ _twin_edges_mask = (_twin_edges != -1)
2195
+ _valid_edges = _modified_edges[_boundary][_twin_edges_mask]
2196
+ self._halfedges['twin'][_twin_edges[_twin_edges_mask]] = -1
2197
+ self._halfedges['twin'][_valid_edges] = -1
2198
+ # Any edge emanating from the new vertex with a twin in a
2199
+ # different component must be reassigned
2200
+ _twin_next_face_component = self._faces['component'][self._halfedges['face'][self._halfedges['twin'][self._halfedges['next'][_modified_edges]]]]
2201
+ _boundary = (_twin_next_face_component != c)
2202
+ _twin_next_edges = self._halfedges['twin'][self._halfedges['next'][_modified_edges[_boundary]]]
2203
+ _twin_next_edges_mask = (_twin_next_edges != -1)
2204
+ _valid_edges = self._halfedges['next'][_modified_edges[_boundary][_twin_next_edges_mask]]
2205
+ self._halfedges['twin'][_twin_next_edges[_twin_next_edges_mask]] = -1
2206
+ self._halfedges['twin'][_valid_edges] = -1
2207
+
2208
+ # Double check that the original vertex did not have a halfedge
2209
+ # in this component
2210
+ _safe_faces_bool = (self._faces['component'][self._halfedges['face']] != c)
2211
+ _safe_vertices = np.where((self._halfedges['vertex'] == _vertex) & (_safe_faces_bool))[0]
2212
+ _safe_halfedge = self._halfedges['next'][_safe_vertices[0]]
2213
+ self._vertices['halfedge'][_vertex] = _safe_halfedge
2214
+
2215
+ # self._update_face_normals(_update_faces)
2216
+ self._update_vertex_neighbors(np.hstack([_update_vertices, _new_vertex]))
2217
+
2218
+ # Reset
2219
+ self._singular_edges = None
2220
+ self._singular_vertices = None
2221
+
2222
+ def repair(self):
2223
+ """
2224
+ Repair the mesh so it's topologically manifold.
2225
+
2226
+ References
2227
+ ----------
2228
+ M. Attene, A lightweight approach to repairing digitized polygon
2229
+ meshes, The Visual Computer, 2010.
2230
+ """
2231
+
2232
+ # 1. Remove singularities (singular edges and isolated singular vertices)
2233
+ self._remove_singularities()
2234
+
2235
+ # 2. Remove all connected components except the largest
2236
+ self.keep_largest_connected_component()
2237
+
2238
+ # 3. Patch mesh holes with new triangles
2239
+ self._fill_holes()
2240
+
2241
+ self._manifold = None
2242
+
2243
+ def to_stl(self, filename):
2244
+ """
2245
+ Save list of triangles to binary STL file.
2246
+
2247
+ Parameters
2248
+ ----------
2249
+ filename : string
2250
+ File name to write
2251
+
2252
+ Returns
2253
+ -------
2254
+ None
2255
+ """
2256
+ from PYME.IO.FileUtils import stl
2257
+
2258
+ dt = np.dtype([('normal', '3f4'), ('vertex0', '3f4'), ('vertex1', '3f4'),
2259
+ ('vertex2', '3f4'), ('attrib', 'u2')])
2260
+
2261
+ triangles_stl = np.zeros(self.face_normals.shape[0], dtype=dt)
2262
+ triangles_stl['vertex0'] = self.vertices[self.faces[:, 0]]
2263
+ triangles_stl['vertex1'] = self.vertices[self.faces[:, 1]]
2264
+ triangles_stl['vertex2'] = self.vertices[self.faces[:, 2]]
2265
+ triangles_stl['normal'] = self.face_normals
2266
+
2267
+ stl.save_stl_binary(filename, triangles_stl)
2268
+
2269
+ def to_ply(self, filename, colors=None):
2270
+ """
2271
+ Save a list of triangles and their vertex colors to a PLY file.
2272
+ We assume colors are a Nx3 array where N is the number of vertices.
2273
+ """
2274
+ from PYME.IO.FileUtils import ply
2275
+
2276
+ # Construct a re-indexing for non-negative vertices
2277
+ live_vertices = np.flatnonzero(self._vertices['halfedge'] != -1)
2278
+ new_vertex_indices = np.arange(live_vertices.shape[0])
2279
+ vertex_lookup = np.zeros(self._vertices.shape[0], dtype=int)
2280
+
2281
+ vertex_lookup[live_vertices] = new_vertex_indices
2282
+
2283
+ # Grab the faces and vertices we want
2284
+ faces = vertex_lookup[self.faces]
2285
+ vertices = self._vertices['position'][live_vertices]
2286
+
2287
+ ply.save_ply(filename, vertices, faces, colors)