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