matter-vis 0.0.0__tar.gz

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 (399) hide show
  1. matter_vis-0.0.0/.github/workflows/release.yml +151 -0
  2. matter_vis-0.0.0/.gitignore +31 -0
  3. matter_vis-0.0.0/AGENTS.md +104 -0
  4. matter_vis-0.0.0/PKG-INFO +28 -0
  5. matter_vis-0.0.0/README.md +366 -0
  6. matter_vis-0.0.0/agents/README.md +138 -0
  7. matter_vis-0.0.0/agents/atom_groups_api.md +257 -0
  8. matter_vis-0.0.0/agents/bond_groups_api.md +174 -0
  9. matter_vis-0.0.0/agents/compass_api.md +88 -0
  10. matter_vis-0.0.0/agents/config_api.md +68 -0
  11. matter_vis-0.0.0/agents/cube_api.md +188 -0
  12. matter_vis-0.0.0/agents/dash_service.md +536 -0
  13. matter_vis-0.0.0/agents/ortep_api.md +63 -0
  14. matter_vis-0.0.0/agents/polyhedron_api.md +315 -0
  15. matter_vis-0.0.0/agents/scene_api.md +166 -0
  16. matter_vis-0.0.0/agents/selection_api.md +39 -0
  17. matter_vis-0.0.0/agents/transforms_api.md +369 -0
  18. matter_vis-0.0.0/agents/tui_api.md +140 -0
  19. matter_vis-0.0.0/crystal_viewer/__init__.py +16 -0
  20. matter_vis-0.0.0/crystal_viewer/__main__.py +7 -0
  21. matter_vis-0.0.0/crystal_viewer/_version.py +24 -0
  22. matter_vis-0.0.0/crystal_viewer/api/__init__.py +43 -0
  23. matter_vis-0.0.0/crystal_viewer/api/shared.py +51 -0
  24. matter_vis-0.0.0/crystal_viewer/api/v1_legacy.py +128 -0
  25. matter_vis-0.0.0/crystal_viewer/api/v2_config.py +47 -0
  26. matter_vis-0.0.0/crystal_viewer/api/v2_export.py +80 -0
  27. matter_vis-0.0.0/crystal_viewer/api/v2_intent.py +18 -0
  28. matter_vis-0.0.0/crystal_viewer/api/v2_overlays.py +420 -0
  29. matter_vis-0.0.0/crystal_viewer/api/v2_perf.py +17 -0
  30. matter_vis-0.0.0/crystal_viewer/api/v2_scenes.py +64 -0
  31. matter_vis-0.0.0/crystal_viewer/api/v2_selection.py +87 -0
  32. matter_vis-0.0.0/crystal_viewer/api/v2_state.py +108 -0
  33. matter_vis-0.0.0/crystal_viewer/api/ws.py +77 -0
  34. matter_vis-0.0.0/crystal_viewer/app/__init__.py +17 -0
  35. matter_vis-0.0.0/crystal_viewer/app/backend.py +27 -0
  36. matter_vis-0.0.0/crystal_viewer/app/backend_analysis.py +381 -0
  37. matter_vis-0.0.0/crystal_viewer/app/backend_camera.py +533 -0
  38. matter_vis-0.0.0/crystal_viewer/app/backend_core.py +1308 -0
  39. matter_vis-0.0.0/crystal_viewer/app/backend_io.py +367 -0
  40. matter_vis-0.0.0/crystal_viewer/app/backend_operations.py +265 -0
  41. matter_vis-0.0.0/crystal_viewer/app/backend_overlays.py +338 -0
  42. matter_vis-0.0.0/crystal_viewer/app/backend_selection.py +188 -0
  43. matter_vis-0.0.0/crystal_viewer/app/backend_topology.py +842 -0
  44. matter_vis-0.0.0/crystal_viewer/app/callbacks_analysis.py +320 -0
  45. matter_vis-0.0.0/crystal_viewer/app/callbacks_disorder.py +328 -0
  46. matter_vis-0.0.0/crystal_viewer/app/callbacks_editors.py +315 -0
  47. matter_vis-0.0.0/crystal_viewer/app/callbacks_operations.py +298 -0
  48. matter_vis-0.0.0/crystal_viewer/app/callbacks_state.py +565 -0
  49. matter_vis-0.0.0/crystal_viewer/app/callbacks_view.py +823 -0
  50. matter_vis-0.0.0/crystal_viewer/app/camera_helpers.py +342 -0
  51. matter_vis-0.0.0/crystal_viewer/app/dash_impl.py +16 -0
  52. matter_vis-0.0.0/crystal_viewer/app/editor_operations.py +91 -0
  53. matter_vis-0.0.0/crystal_viewer/app/editor_tables.py +622 -0
  54. matter_vis-0.0.0/crystal_viewer/app/editor_transforms.py +328 -0
  55. matter_vis-0.0.0/crystal_viewer/app/factory.py +1114 -0
  56. matter_vis-0.0.0/crystal_viewer/app/normalizers.py +711 -0
  57. matter_vis-0.0.0/crystal_viewer/app/render_worker.py +218 -0
  58. matter_vis-0.0.0/crystal_viewer/app/rightclick.py +347 -0
  59. matter_vis-0.0.0/crystal_viewer/app/runtime.py +151 -0
  60. matter_vis-0.0.0/crystal_viewer/app/shared.py +76 -0
  61. matter_vis-0.0.0/crystal_viewer/app/status_helpers.py +63 -0
  62. matter_vis-0.0.0/crystal_viewer/app/style_helpers.py +187 -0
  63. matter_vis-0.0.0/crystal_viewer/atom_groups/__init__.py +10 -0
  64. matter_vis-0.0.0/crystal_viewer/bond_groups/__init__.py +10 -0
  65. matter_vis-0.0.0/crystal_viewer/bonds/__init__.py +10 -0
  66. matter_vis-0.0.0/crystal_viewer/cli.py +840 -0
  67. matter_vis-0.0.0/crystal_viewer/compass/__init__.py +15 -0
  68. matter_vis-0.0.0/crystal_viewer/compass/annotations.py +8 -0
  69. matter_vis-0.0.0/crystal_viewer/compass/basis.py +7 -0
  70. matter_vis-0.0.0/crystal_viewer/compass/core.py +296 -0
  71. matter_vis-0.0.0/crystal_viewer/compass/projection.py +7 -0
  72. matter_vis-0.0.0/crystal_viewer/config/__init__.py +120 -0
  73. matter_vis-0.0.0/crystal_viewer/config/colors.py +4 -0
  74. matter_vis-0.0.0/crystal_viewer/config/elements.py +294 -0
  75. matter_vis-0.0.0/crystal_viewer/config/loader.py +200 -0
  76. matter_vis-0.0.0/crystal_viewer/config/schema.py +202 -0
  77. matter_vis-0.0.0/crystal_viewer/cube/__init__.py +51 -0
  78. matter_vis-0.0.0/crystal_viewer/cube/bridge.py +206 -0
  79. matter_vis-0.0.0/crystal_viewer/cube/core.py +948 -0
  80. matter_vis-0.0.0/crystal_viewer/cube/export.py +9 -0
  81. matter_vis-0.0.0/crystal_viewer/cube/io.py +12 -0
  82. matter_vis-0.0.0/crystal_viewer/cube/meshes.py +9 -0
  83. matter_vis-0.0.0/crystal_viewer/cube/traces.py +13 -0
  84. matter_vis-0.0.0/crystal_viewer/depth_sort/__init__.py +10 -0
  85. matter_vis-0.0.0/crystal_viewer/depth_sort/core.py +160 -0
  86. matter_vis-0.0.0/crystal_viewer/legacy/__init__.py +1 -0
  87. matter_vis-0.0.0/crystal_viewer/legacy/crystal_scene.py +410 -0
  88. matter_vis-0.0.0/crystal_viewer/legacy/plot_crystal.py +223 -0
  89. matter_vis-0.0.0/crystal_viewer/legacy/publication_view.py +12 -0
  90. matter_vis-0.0.0/crystal_viewer/loader/__init__.py +39 -0
  91. matter_vis-0.0.0/crystal_viewer/loader/core.py +1224 -0
  92. matter_vis-0.0.0/crystal_viewer/loader/cube_adapter.py +199 -0
  93. matter_vis-0.0.0/crystal_viewer/loader/uploads.py +79 -0
  94. matter_vis-0.0.0/crystal_viewer/math/__init__.py +18 -0
  95. matter_vis-0.0.0/crystal_viewer/math/camera.py +426 -0
  96. matter_vis-0.0.0/crystal_viewer/math/ellipsoid.py +5 -0
  97. matter_vis-0.0.0/crystal_viewer/math/pbc.py +46 -0
  98. matter_vis-0.0.0/crystal_viewer/math/projection.py +5 -0
  99. matter_vis-0.0.0/crystal_viewer/math/rotation.py +144 -0
  100. matter_vis-0.0.0/crystal_viewer/operations/__init__.py +5 -0
  101. matter_vis-0.0.0/crystal_viewer/operations/disorder.py +174 -0
  102. matter_vis-0.0.0/crystal_viewer/ortep/__init__.py +12 -0
  103. matter_vis-0.0.0/crystal_viewer/ortep/billboard.py +8 -0
  104. matter_vis-0.0.0/crystal_viewer/ortep/core.py +952 -0
  105. matter_vis-0.0.0/crystal_viewer/ortep/flat_render.py +331 -0
  106. matter_vis-0.0.0/crystal_viewer/ortep/math.py +8 -0
  107. matter_vis-0.0.0/crystal_viewer/ortep/mesh.py +7 -0
  108. matter_vis-0.0.0/crystal_viewer/ortep/traces.py +12 -0
  109. matter_vis-0.0.0/crystal_viewer/perf/__init__.py +6 -0
  110. matter_vis-0.0.0/crystal_viewer/perf/bench.py +176 -0
  111. matter_vis-0.0.0/crystal_viewer/perf/bench_pipeline.py +223 -0
  112. matter_vis-0.0.0/crystal_viewer/perf/oracle.py +237 -0
  113. matter_vis-0.0.0/crystal_viewer/perf/profile_app.py +74 -0
  114. matter_vis-0.0.0/crystal_viewer/perf_log/__init__.py +10 -0
  115. matter_vis-0.0.0/crystal_viewer/perf_log/core.py +184 -0
  116. matter_vis-0.0.0/crystal_viewer/presets/__init__.py +10 -0
  117. matter_vis-0.0.0/crystal_viewer/presets/core.py +313 -0
  118. matter_vis-0.0.0/crystal_viewer/render/__init__.py +5 -0
  119. matter_vis-0.0.0/crystal_viewer/render/api.py +84 -0
  120. matter_vis-0.0.0/crystal_viewer/render/assembly.py +4 -0
  121. matter_vis-0.0.0/crystal_viewer/render/boundary_replicas.py +155 -0
  122. matter_vis-0.0.0/crystal_viewer/render/cache.py +341 -0
  123. matter_vis-0.0.0/crystal_viewer/render/common.py +30 -0
  124. matter_vis-0.0.0/crystal_viewer/render/compass.py +265 -0
  125. matter_vis-0.0.0/crystal_viewer/render/cube/__init__.py +3 -0
  126. matter_vis-0.0.0/crystal_viewer/render/cube/core.py +3 -0
  127. matter_vis-0.0.0/crystal_viewer/render/cube/export.py +3 -0
  128. matter_vis-0.0.0/crystal_viewer/render/cube/io.py +3 -0
  129. matter_vis-0.0.0/crystal_viewer/render/cube/meshes.py +3 -0
  130. matter_vis-0.0.0/crystal_viewer/render/cube/traces.py +3 -0
  131. matter_vis-0.0.0/crystal_viewer/render/display_modes.py +179 -0
  132. matter_vis-0.0.0/crystal_viewer/render/figures.py +306 -0
  133. matter_vis-0.0.0/crystal_viewer/render/meshes.py +143 -0
  134. matter_vis-0.0.0/crystal_viewer/render/morphology.py +95 -0
  135. matter_vis-0.0.0/crystal_viewer/render/ortep/__init__.py +3 -0
  136. matter_vis-0.0.0/crystal_viewer/render/ortep/billboard.py +3 -0
  137. matter_vis-0.0.0/crystal_viewer/render/ortep/core.py +3 -0
  138. matter_vis-0.0.0/crystal_viewer/render/ortep/math.py +3 -0
  139. matter_vis-0.0.0/crystal_viewer/render/ortep/mesh.py +3 -0
  140. matter_vis-0.0.0/crystal_viewer/render/ortep/traces.py +3 -0
  141. matter_vis-0.0.0/crystal_viewer/render/overlay/__init__.py +5 -0
  142. matter_vis-0.0.0/crystal_viewer/render/overlay/compass.py +3 -0
  143. matter_vis-0.0.0/crystal_viewer/render/overlay/labels.py +8 -0
  144. matter_vis-0.0.0/crystal_viewer/render/overlay/manual.py +26 -0
  145. matter_vis-0.0.0/crystal_viewer/render/scene_traces.py +25 -0
  146. matter_vis-0.0.0/crystal_viewer/render/selection.py +272 -0
  147. matter_vis-0.0.0/crystal_viewer/render/serialize.py +92 -0
  148. matter_vis-0.0.0/crystal_viewer/render/style/__init__.py +9 -0
  149. matter_vis-0.0.0/crystal_viewer/render/style/atom_groups.py +3 -0
  150. matter_vis-0.0.0/crystal_viewer/render/style/bond_groups.py +3 -0
  151. matter_vis-0.0.0/crystal_viewer/render/style/core.py +360 -0
  152. matter_vis-0.0.0/crystal_viewer/render/style/disorder.py +3 -0
  153. matter_vis-0.0.0/crystal_viewer/render/style/palette.py +3 -0
  154. matter_vis-0.0.0/crystal_viewer/render/style.py +292 -0
  155. matter_vis-0.0.0/crystal_viewer/render/topology.py +625 -0
  156. matter_vis-0.0.0/crystal_viewer/render/traces_atoms.py +642 -0
  157. matter_vis-0.0.0/crystal_viewer/render/traces_isosurface.py +461 -0
  158. matter_vis-0.0.0/crystal_viewer/render/traces_overlays.py +384 -0
  159. matter_vis-0.0.0/crystal_viewer/render/viewport.py +640 -0
  160. matter_vis-0.0.0/crystal_viewer/renderer/__init__.py +18 -0
  161. matter_vis-0.0.0/crystal_viewer/scene/__init__.py +36 -0
  162. matter_vis-0.0.0/crystal_viewer/scene/core.py +764 -0
  163. matter_vis-0.0.0/crystal_viewer/scene/serialize.py +56 -0
  164. matter_vis-0.0.0/crystal_viewer/scene/state.py +39 -0
  165. matter_vis-0.0.0/crystal_viewer/scene/store.py +3 -0
  166. matter_vis-0.0.0/crystal_viewer/scene/style.py +93 -0
  167. matter_vis-0.0.0/crystal_viewer/scene/types.py +203 -0
  168. matter_vis-0.0.0/crystal_viewer/scenes/__init__.py +10 -0
  169. matter_vis-0.0.0/crystal_viewer/scenes/core.py +323 -0
  170. matter_vis-0.0.0/crystal_viewer/structure/__init__.py +2 -0
  171. matter_vis-0.0.0/crystal_viewer/structure/bonds.py +505 -0
  172. matter_vis-0.0.0/crystal_viewer/structure/cif_parse.py +252 -0
  173. matter_vis-0.0.0/crystal_viewer/structure/disorder_index.py +117 -0
  174. matter_vis-0.0.0/crystal_viewer/structure/formula_unit.py +201 -0
  175. matter_vis-0.0.0/crystal_viewer/structure/geometry.py +37 -0
  176. matter_vis-0.0.0/crystal_viewer/structure/molcrys_bridge.py +465 -0
  177. matter_vis-0.0.0/crystal_viewer/structure/snapshot.py +23 -0
  178. matter_vis-0.0.0/crystal_viewer/style/__init__.py +2 -0
  179. matter_vis-0.0.0/crystal_viewer/style/atom_groups.py +258 -0
  180. matter_vis-0.0.0/crystal_viewer/style/bond_groups.py +230 -0
  181. matter_vis-0.0.0/crystal_viewer/style/disorder.py +113 -0
  182. matter_vis-0.0.0/crystal_viewer/style/palette.py +25 -0
  183. matter_vis-0.0.0/crystal_viewer/topology/__init__.py +36 -0
  184. matter_vis-0.0.0/crystal_viewer/topology/analysis.py +593 -0
  185. matter_vis-0.0.0/crystal_viewer/transforms/__init__.py +17 -0
  186. matter_vis-0.0.0/crystal_viewer/transforms/core.py +873 -0
  187. matter_vis-0.0.0/crystal_viewer/transforms/pipeline.py +263 -0
  188. matter_vis-0.0.0/crystal_viewer/tui/__init__.py +121 -0
  189. matter_vis-0.0.0/crystal_viewer/tui/_hull2d.py +57 -0
  190. matter_vis-0.0.0/crystal_viewer/tui/app.py +403 -0
  191. matter_vis-0.0.0/crystal_viewer/tui/braille.py +214 -0
  192. matter_vis-0.0.0/crystal_viewer/tui/compositor.py +965 -0
  193. matter_vis-0.0.0/crystal_viewer/tui/controller.py +693 -0
  194. matter_vis-0.0.0/crystal_viewer/tui/crystal_ir.py +171 -0
  195. matter_vis-0.0.0/crystal_viewer/tui/inspection.py +424 -0
  196. matter_vis-0.0.0/crystal_viewer/tui/loader_adapter.py +694 -0
  197. matter_vis-0.0.0/crystal_viewer/tui/observation.py +67 -0
  198. matter_vis-0.0.0/crystal_viewer/tui/renderer.py +372 -0
  199. matter_vis-0.0.0/crystal_viewer/tui/serializer.py +225 -0
  200. matter_vis-0.0.0/crystal_viewer/tui/state.py +180 -0
  201. matter_vis-0.0.0/crystal_viewer/tui/summary.py +66 -0
  202. matter_vis-0.0.0/crystal_viewer/tui/text.py +32 -0
  203. matter_vis-0.0.0/crystal_viewer/utils/__init__.py +3 -0
  204. matter_vis-0.0.0/crystal_viewer/utils/json_safe.py +5 -0
  205. matter_vis-0.0.0/crystal_viewer/viewpoint/__init__.py +33 -0
  206. matter_vis-0.0.0/crystal_viewer/viewpoint/core.py +605 -0
  207. matter_vis-0.0.0/docs/README.md +12 -0
  208. matter_vis-0.0.0/docs/build_images.py +195 -0
  209. matter_vis-0.0.0/docs/cli.md +289 -0
  210. matter_vis-0.0.0/docs/derivations/README.md +146 -0
  211. matter_vis-0.0.0/docs/derivations/camera.md +510 -0
  212. matter_vis-0.0.0/docs/derivations/display_modes.md +276 -0
  213. matter_vis-0.0.0/docs/derivations/lattice.md +149 -0
  214. matter_vis-0.0.0/docs/derivations/ortep.md +311 -0
  215. matter_vis-0.0.0/docs/derivations/transforms.md +335 -0
  216. matter_vis-0.0.0/docs/dev-notes.md +129 -0
  217. matter_vis-0.0.0/docs/images/banner.png +0 -0
  218. matter_vis-0.0.0/docs/images/feature_coordination.png +0 -0
  219. matter_vis-0.0.0/docs/images/feature_histogram.png +0 -0
  220. matter_vis-0.0.0/docs/images/feature_publication.png +0 -0
  221. matter_vis-0.0.0/docs/images/feature_three_modes.png +0 -0
  222. matter_vis-0.0.0/docs/images/feature_unit_cell.png +0 -0
  223. matter_vis-0.0.0/docs/perf.md +150 -0
  224. matter_vis-0.0.0/docs/postmortems.md +111 -0
  225. matter_vis-0.0.0/docs/redesign/README.md +153 -0
  226. matter_vis-0.0.0/docs/redesign/caches.md +185 -0
  227. matter_vis-0.0.0/docs/redesign/callbacks.md +156 -0
  228. matter_vis-0.0.0/docs/redesign/migration.md +173 -0
  229. matter_vis-0.0.0/docs/redesign/operations.md +193 -0
  230. matter_vis-0.0.0/docs/redesign/rendering.md +151 -0
  231. matter_vis-0.0.0/docs/redesign/state.md +174 -0
  232. matter_vis-0.0.0/docs/scores.md +272 -0
  233. matter_vis-0.0.0/frontend/README.md +8 -0
  234. matter_vis-0.0.0/frontend/assets/diagnostic.js +333 -0
  235. matter_vis-0.0.0/frontend/assets/mattervis.js +1110 -0
  236. matter_vis-0.0.0/frontend/assets/native_upload.js +285 -0
  237. matter_vis-0.0.0/frontend/assets/panel_resize.css +500 -0
  238. matter_vis-0.0.0/frontend/assets/panel_resize.js +105 -0
  239. matter_vis-0.0.0/frontend/assets/right_click_menu.css +150 -0
  240. matter_vis-0.0.0/frontend/assets/view_tools.css +57 -0
  241. matter_vis-0.0.0/matter_vis.egg-info/PKG-INFO +28 -0
  242. matter_vis-0.0.0/matter_vis.egg-info/SOURCES.txt +397 -0
  243. matter_vis-0.0.0/matter_vis.egg-info/dependency_links.txt +1 -0
  244. matter_vis-0.0.0/matter_vis.egg-info/entry_points.txt +2 -0
  245. matter_vis-0.0.0/matter_vis.egg-info/requires.txt +25 -0
  246. matter_vis-0.0.0/matter_vis.egg-info/scm_file_list.json +392 -0
  247. matter_vis-0.0.0/matter_vis.egg-info/scm_version.json +8 -0
  248. matter_vis-0.0.0/matter_vis.egg-info/top_level.txt +1 -0
  249. matter_vis-0.0.0/pyproject.toml +42 -0
  250. matter_vis-0.0.0/pytest.ini +36 -0
  251. matter_vis-0.0.0/requirements.txt +22 -0
  252. matter_vis-0.0.0/scripts/01_quick_render.py +59 -0
  253. matter_vis-0.0.0/scripts/02_coordination_analysis.py +221 -0
  254. matter_vis-0.0.0/scripts/03_display_modes_panel.py +94 -0
  255. matter_vis-0.0.0/scripts/04_static_publication.py +48 -0
  256. matter_vis-0.0.0/scripts/05_app_and_api.py +156 -0
  257. matter_vis-0.0.0/scripts/06_cp2k_cube_orbital.py +91 -0
  258. matter_vis-0.0.0/scripts/07_scene_tabs.py +32 -0
  259. matter_vis-0.0.0/scripts/08_ortep_interactive.py +39 -0
  260. matter_vis-0.0.0/scripts/09_ortep_publication.py +40 -0
  261. matter_vis-0.0.0/scripts/10_periodic_cube_isosurface.py +84 -0
  262. matter_vis-0.0.0/scripts/10_tui_controller_visuals.py +47 -0
  263. matter_vis-0.0.0/scripts/11_bond_scale_benchmark.py +41 -0
  264. matter_vis-0.0.0/scripts/README.md +13 -0
  265. matter_vis-0.0.0/scripts/data/DAP-4.cif +878 -0
  266. matter_vis-0.0.0/scripts/data/README.md +15 -0
  267. matter_vis-0.0.0/server.log +373 -0
  268. matter_vis-0.0.0/setup.cfg +4 -0
  269. matter_vis-0.0.0/skills/visualize-materials/SKILL.md +56 -0
  270. matter_vis-0.0.0/skills/visualize-materials/references/camera.md +45 -0
  271. matter_vis-0.0.0/skills/visualize-materials/references/diagnose-and-select.md +60 -0
  272. matter_vis-0.0.0/skills/visualize-materials/references/install.md +38 -0
  273. matter_vis-0.0.0/skills/visualize-materials/references/matplotlib-flat-ortep.md +37 -0
  274. matter_vis-0.0.0/skills/visualize-materials/references/molecule-highlight.md +32 -0
  275. matter_vis-0.0.0/skills/visualize-materials/references/plotly-render.md +50 -0
  276. matter_vis-0.0.0/skills/visualize-materials/references/publication-layout.md +69 -0
  277. matter_vis-0.0.0/skills/visualize-materials/references/trajectory-animation.md +52 -0
  278. matter_vis-0.0.0/skills/visualize-materials/references/tui.md +40 -0
  279. matter_vis-0.0.0/skills/visualize-materials/references/verification.md +46 -0
  280. matter_vis-0.0.0/tests/_layout_helpers.py +169 -0
  281. matter_vis-0.0.0/tests/app/test_app_state.py +28 -0
  282. matter_vis-0.0.0/tests/app/test_bfdh_analysis.py +64 -0
  283. matter_vis-0.0.0/tests/app/test_callback_dispatch_regression.py +163 -0
  284. matter_vis-0.0.0/tests/app/test_camera_capture_no_poll_echo.py +143 -0
  285. matter_vis-0.0.0/tests/app/test_camera_persistence_per_tab.py +50 -0
  286. matter_vis-0.0.0/tests/app/test_editor_builders.py +318 -0
  287. matter_vis-0.0.0/tests/app/test_frontend_scene_tab_routing.py +94 -0
  288. matter_vis-0.0.0/tests/app/test_inline_edit_emits_state.py +59 -0
  289. matter_vis-0.0.0/tests/app/test_layout_refresh_reflects_state.py +77 -0
  290. matter_vis-0.0.0/tests/app/test_minor_opacity_disable.py +24 -0
  291. matter_vis-0.0.0/tests/app/test_phase3_ui_layout.py +201 -0
  292. matter_vis-0.0.0/tests/app/test_preset_security.py +114 -0
  293. matter_vis-0.0.0/tests/app/test_scene_store_recovery.py +148 -0
  294. matter_vis-0.0.0/tests/app/test_scene_tabs_dispatcher.py +269 -0
  295. matter_vis-0.0.0/tests/app/test_status_banner.py +27 -0
  296. matter_vis-0.0.0/tests/app/test_surface_editor_error.py +250 -0
  297. matter_vis-0.0.0/tests/app/test_upload_security.py +205 -0
  298. matter_vis-0.0.0/tests/app/test_view_camera_callbacks.py +71 -0
  299. matter_vis-0.0.0/tests/app/test_visual_control_regressions.py +272 -0
  300. matter_vis-0.0.0/tests/config/test_config_loader.py +53 -0
  301. matter_vis-0.0.0/tests/config/test_config_mck_overrides_passthrough.py +38 -0
  302. matter_vis-0.0.0/tests/config/test_config_rest.py +51 -0
  303. matter_vis-0.0.0/tests/config/test_element_tables.py +89 -0
  304. matter_vis-0.0.0/tests/conftest.py +92 -0
  305. matter_vis-0.0.0/tests/cube/test_cube_bond_scale.py +185 -0
  306. matter_vis-0.0.0/tests/cube/test_periodic_isosurface.py +224 -0
  307. matter_vis-0.0.0/tests/loader/test_disorder_fast_path.py +59 -0
  308. matter_vis-0.0.0/tests/operations/test_resolve_disorder.py +19 -0
  309. matter_vis-0.0.0/tests/ortep/test_adp_propagation.py +33 -0
  310. matter_vis-0.0.0/tests/ortep/test_ortep_billboard.py +20 -0
  311. matter_vis-0.0.0/tests/ortep/test_ortep_integration.py +37 -0
  312. matter_vis-0.0.0/tests/ortep/test_ortep_math.py +30 -0
  313. matter_vis-0.0.0/tests/ortep/test_ortep_mesh.py +20 -0
  314. matter_vis-0.0.0/tests/ortep/test_ortep_mixed_modes.py +55 -0
  315. matter_vis-0.0.0/tests/ortep/test_ortep_traces.py +111 -0
  316. matter_vis-0.0.0/tests/perf/oracles/pipeline_v1.json +81 -0
  317. matter_vis-0.0.0/tests/perf/test_dap_o4_oracle.py +98 -0
  318. matter_vis-0.0.0/tests/perf/test_near_zero_latency.py +146 -0
  319. matter_vis-0.0.0/tests/perf/test_pipeline_oracle.py +130 -0
  320. matter_vis-0.0.0/tests/perf/test_topology_compute_offthread.py +50 -0
  321. matter_vis-0.0.0/tests/perf/test_update_view_under_50ms.py +72 -0
  322. matter_vis-0.0.0/tests/render/test_atom_groups_extra_selectors.py +122 -0
  323. matter_vis-0.0.0/tests/render/test_atom_groups_renderer.py +396 -0
  324. matter_vis-0.0.0/tests/render/test_bond_effective_opacity.py +78 -0
  325. matter_vis-0.0.0/tests/render/test_bond_groups.py +215 -0
  326. matter_vis-0.0.0/tests/render/test_boundary_replicas.py +824 -0
  327. matter_vis-0.0.0/tests/render/test_camera_aspect_persistence.py +86 -0
  328. matter_vis-0.0.0/tests/render/test_camera_projection.py +702 -0
  329. matter_vis-0.0.0/tests/render/test_canonical_boundary_bonds.py +224 -0
  330. matter_vis-0.0.0/tests/render/test_cif_symmetry_expansion.py +35 -0
  331. matter_vis-0.0.0/tests/render/test_compass_basis.py +18 -0
  332. matter_vis-0.0.0/tests/render/test_depth_sort.py +73 -0
  333. matter_vis-0.0.0/tests/render/test_disorder_modifiers.py +9 -0
  334. matter_vis-0.0.0/tests/render/test_disorder_preview_trace.py +31 -0
  335. matter_vis-0.0.0/tests/render/test_figure_cache_perf.py +233 -0
  336. matter_vis-0.0.0/tests/render/test_flat_projected_scale.py +100 -0
  337. matter_vis-0.0.0/tests/render/test_legacy_aliases.py +17 -0
  338. matter_vis-0.0.0/tests/render/test_ordered_structure_bond_opacity.py +72 -0
  339. matter_vis-0.0.0/tests/render/test_partial_occupancy_render_identity.py +199 -0
  340. matter_vis-0.0.0/tests/render/test_payload_size.py +87 -0
  341. matter_vis-0.0.0/tests/render/test_polyhedron_enabled_toggle_perf.py +485 -0
  342. matter_vis-0.0.0/tests/render/test_render_dispatch.py +312 -0
  343. matter_vis-0.0.0/tests/render/test_selection_outline_trace.py +26 -0
  344. matter_vis-0.0.0/tests/render/test_style_schema.py +39 -0
  345. matter_vis-0.0.0/tests/render/test_topology_with_styles.py +37 -0
  346. matter_vis-0.0.0/tests/render/test_unit_cell_box_aspectratio.py +665 -0
  347. matter_vis-0.0.0/tests/render/test_unwrap_modes.py +65 -0
  348. matter_vis-0.0.0/tests/render/test_view_alignment.py +237 -0
  349. matter_vis-0.0.0/tests/scenes/test_atom_groups_api.py +127 -0
  350. matter_vis-0.0.0/tests/scenes/test_atom_groups_backend.py +168 -0
  351. matter_vis-0.0.0/tests/scenes/test_healthz_api.py +57 -0
  352. matter_vis-0.0.0/tests/scenes/test_legacy_alias_v1.py +16 -0
  353. matter_vis-0.0.0/tests/scenes/test_monochrome_migration.py +97 -0
  354. matter_vis-0.0.0/tests/scenes/test_per_tab_close.py +39 -0
  355. matter_vis-0.0.0/tests/scenes/test_perf_log.py +111 -0
  356. matter_vis-0.0.0/tests/scenes/test_scenes_api.py +66 -0
  357. matter_vis-0.0.0/tests/scenes/test_scenes_resolver.py +16 -0
  358. matter_vis-0.0.0/tests/scenes/test_scenes_store.py +39 -0
  359. matter_vis-0.0.0/tests/scenes/test_v1_state_deprecation.py +59 -0
  360. matter_vis-0.0.0/tests/scenes/test_view_alignment_api.py +100 -0
  361. matter_vis-0.0.0/tests/scenes/test_ws_set_state_scene_id.py +76 -0
  362. matter_vis-0.0.0/tests/selection/test_box_select_projection.py +13 -0
  363. matter_vis-0.0.0/tests/selection/test_selection_dispatcher.py +41 -0
  364. matter_vis-0.0.0/tests/selection/test_selection_rest.py +42 -0
  365. matter_vis-0.0.0/tests/selection/test_selection_state_schema.py +27 -0
  366. matter_vis-0.0.0/tests/state_machine/test_no_lost_events.py +44 -0
  367. matter_vis-0.0.0/tests/state_machine/test_reducer_order.py +31 -0
  368. matter_vis-0.0.0/tests/structure/test_bonds_pbc_reference.py +106 -0
  369. matter_vis-0.0.0/tests/structure/test_disorder_index.py +53 -0
  370. matter_vis-0.0.0/tests/test_cli_camera.py +63 -0
  371. matter_vis-0.0.0/tests/test_cli_identity.py +13 -0
  372. matter_vis-0.0.0/tests/test_disorder_isolated_atoms.py +103 -0
  373. matter_vis-0.0.0/tests/test_module_organization.py +46 -0
  374. matter_vis-0.0.0/tests/test_public_import_contracts.py +53 -0
  375. matter_vis-0.0.0/tests/topology/test_disorder_bonds.py +64 -0
  376. matter_vis-0.0.0/tests/topology/test_loader_mol_indices.py +447 -0
  377. matter_vis-0.0.0/tests/topology/test_phase4_api.py +375 -0
  378. matter_vis-0.0.0/tests/topology/test_polyhedron_instance_overrides.py +207 -0
  379. matter_vis-0.0.0/tests/topology/test_polyhedron_specs_api.py +138 -0
  380. matter_vis-0.0.0/tests/topology/test_polyhedron_specs_backend.py +366 -0
  381. matter_vis-0.0.0/tests/topology/test_polyhedron_specs_mck04.py +278 -0
  382. matter_vis-0.0.0/tests/topology/test_polyhedron_specs_renderer.py +280 -0
  383. matter_vis-0.0.0/tests/topology/test_shape_classification.py +267 -0
  384. matter_vis-0.0.0/tests/topology/test_topology.py +72 -0
  385. matter_vis-0.0.0/tests/topology/test_topology_api_errors.py +80 -0
  386. matter_vis-0.0.0/tests/topology/test_transforms.py +501 -0
  387. matter_vis-0.0.0/tests/tui/fixtures/dirty_geometry.vasp +40 -0
  388. matter_vis-0.0.0/tests/tui/test_tui_camera_controls.py +112 -0
  389. matter_vis-0.0.0/tests/tui/test_tui_command_mode.py +183 -0
  390. matter_vis-0.0.0/tests/tui/test_tui_controller.py +217 -0
  391. matter_vis-0.0.0/tests/tui/test_tui_focus_and_inspection.py +150 -0
  392. matter_vis-0.0.0/tests/tui/test_tui_local_geometry.py +115 -0
  393. matter_vis-0.0.0/tests/tui/test_tui_usability.py +492 -0
  394. matter_vis-0.0.0/verification_screens/tui_controller/01_initial_mono.txt +20 -0
  395. matter_vis-0.0.0/verification_screens/tui_controller/02_orbit_mono.txt +21 -0
  396. matter_vis-0.0.0/verification_screens/tui_controller/03_focus_mono.txt +24 -0
  397. matter_vis-0.0.0/verification_screens/tui_controller/04_molecule_minor_mono.txt +24 -0
  398. matter_vis-0.0.0/verification_screens/tui_controller/05_molecule_minor_color.txt +21 -0
  399. matter_vis-0.0.0/verification_screens/tui_controller/README.md +12 -0
@@ -0,0 +1,151 @@
1
+ name: release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ release-checks:
13
+ name: Release metadata checks
14
+ runs-on: ubuntu-latest
15
+ outputs:
16
+ version: ${{ steps.release.outputs.version }}
17
+ tag: ${{ steps.release.outputs.tag }}
18
+ steps:
19
+ - name: Check out repository
20
+ uses: actions/checkout@v4
21
+ with:
22
+ fetch-depth: 0
23
+
24
+ - name: Validate release tag
25
+ id: release
26
+ shell: bash
27
+ run: |
28
+ set -euo pipefail
29
+ TAG="${GITHUB_REF_NAME}"
30
+ if [[ ! "${TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
31
+ echo "Release tag must match vX.Y.Z: ${TAG}" >&2
32
+ exit 1
33
+ fi
34
+
35
+ echo "tag=${TAG}" >> "${GITHUB_OUTPUT}"
36
+ echo "version=${TAG#v}" >> "${GITHUB_OUTPUT}"
37
+
38
+ tests:
39
+ name: Unit tests (Python ${{ matrix.python-version }})
40
+ runs-on: ubuntu-latest
41
+ needs: release-checks
42
+ strategy:
43
+ fail-fast: false
44
+ matrix:
45
+ python-version: ["3.10", "3.11", "3.12"]
46
+ steps:
47
+ - name: Check out repository
48
+ uses: actions/checkout@v4
49
+ with:
50
+ fetch-depth: 0
51
+
52
+ - name: Set up Python ${{ matrix.python-version }}
53
+ uses: actions/setup-python@v5
54
+ with:
55
+ python-version: ${{ matrix.python-version }}
56
+ cache: pip
57
+
58
+ - name: Install test dependencies
59
+ run: |
60
+ python -m pip install --upgrade pip
61
+ python -m pip install -e ".[test]"
62
+
63
+ - name: Run tests
64
+ run: pytest tests/
65
+
66
+ build-package:
67
+ name: Build package distributions
68
+ runs-on: ubuntu-latest
69
+ needs: [release-checks, tests]
70
+ steps:
71
+ - name: Check out repository
72
+ uses: actions/checkout@v4
73
+ with:
74
+ fetch-depth: 0
75
+
76
+ - name: Set up Python
77
+ uses: actions/setup-python@v5
78
+ with:
79
+ python-version: "3.10"
80
+ cache: pip
81
+
82
+ - name: Build source and wheel distributions
83
+ run: |
84
+ python -m pip install --upgrade pip build
85
+ python -m build
86
+
87
+ - name: Verify built version
88
+ env:
89
+ EXPECTED_VERSION: ${{ needs.release-checks.outputs.version }}
90
+ run: |
91
+ python -m pip install --no-deps dist/*.whl
92
+ python - <<'PY'
93
+ import importlib.metadata
94
+ import os
95
+
96
+ actual = importlib.metadata.version("matter-vis")
97
+ expected = os.environ["EXPECTED_VERSION"]
98
+ if actual != expected:
99
+ raise SystemExit(f"Built version {actual!r} does not match tag version {expected!r}")
100
+ print(f"matter-vis {actual}")
101
+ PY
102
+
103
+ - name: Upload distributions
104
+ uses: actions/upload-artifact@v4
105
+ with:
106
+ name: python-package-distributions
107
+ path: dist/
108
+ if-no-files-found: error
109
+
110
+ publish-pypi:
111
+ name: Publish to PyPI
112
+ runs-on: ubuntu-latest
113
+ needs: [release-checks, build-package]
114
+ environment:
115
+ name: pypi
116
+ url: https://pypi.org/project/matter-vis/
117
+ permissions:
118
+ contents: read
119
+ id-token: write
120
+ steps:
121
+ - name: Download distributions
122
+ uses: actions/download-artifact@v4
123
+ with:
124
+ name: python-package-distributions
125
+ path: dist/
126
+
127
+ - name: Publish to PyPI
128
+ uses: pypa/gh-action-pypi-publish@release/v1
129
+ with:
130
+ packages-dir: dist/
131
+
132
+ create-github-release:
133
+ name: Create GitHub Release
134
+ runs-on: ubuntu-latest
135
+ needs: [release-checks, publish-pypi]
136
+ permissions:
137
+ contents: write
138
+ steps:
139
+ - name: Download distributions
140
+ uses: actions/download-artifact@v4
141
+ with:
142
+ name: python-package-distributions
143
+ path: dist/
144
+
145
+ - name: Create GitHub Release
146
+ env:
147
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
148
+ run: |
149
+ gh release create "${GITHUB_REF_NAME}" dist/* \
150
+ --generate-notes \
151
+ --title "${GITHUB_REF_NAME}"
@@ -0,0 +1,31 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.pyo
4
+ *.pyd
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .ruff_cache/
8
+ .venv/
9
+ venv/
10
+ env/
11
+
12
+ .local/
13
+ .exports/
14
+ catalog.local.json
15
+
16
+ uploads/
17
+ exports/
18
+ scripts/_outputs/
19
+ scripts/data/HPEP.cif
20
+ scripts/data/MPEP.cif
21
+ scripts/data/PEP.cif
22
+ scripts/data/SY.cif
23
+ scripts/_data/
24
+
25
+ .DS_Store
26
+ Thumbs.db
27
+
28
+ scripts/private/
29
+ verification_screens/
30
+ _tmp/
31
+ server.log
@@ -0,0 +1,104 @@
1
+ # MatterVis — Developer Contract
2
+
3
+ This file is for agents and humans **modifying** MatterVis. It is the
4
+ top-level contract only; module/API details belong in linked docs.
5
+ Keep this file short, architectural, and free of function-specific rules.
6
+
7
+ ## Read the right document first
8
+
9
+ - Caller-facing API contracts: [`agents/README.md`](agents/README.md).
10
+ - Detailed feature contracts: `agents/*_api.md` and
11
+ [`agents/dash_service.md`](agents/dash_service.md).
12
+ - Implementation notes and historical traps:
13
+ [`docs/dev-notes.md`](docs/dev-notes.md),
14
+ [`docs/postmortems.md`](docs/postmortems.md).
15
+
16
+ If a change touches a public feature, read the matching `agents/` file
17
+ before editing. If a change touches chemistry delegation, disorder,
18
+ fragmentation, topology, slabs, or loader/render boundaries, also read
19
+ `docs/dev-notes.md`.
20
+
21
+ ## Repository ownership
22
+
23
+ | Path | Owns |
24
+ | --- | --- |
25
+ | `crystal_viewer/` | Python library: structure, operations, analysis, render, scene, math, utilities |
26
+ | `api/` | REST/WebSocket service surface for scripts and agents |
27
+ | `app/` | Dash Python BFF, callbacks, layouts, `ViewerBackend` |
28
+ | `frontend/` | Browser-only JavaScript/CSS assets loaded by Dash |
29
+ | `agents/` | Public caller contracts and automation-facing API docs |
30
+ | `docs/` | Human docs, developer notes, postmortems, score tables |
31
+ | `scripts/` | Runnable examples and regression figure generators |
32
+ | `tests/` | Unit and integration tests |
33
+
34
+ New features should normally fit one of these owners. Avoid new
35
+ top-level modules unless the existing ownership model cannot stretch.
36
+
37
+ ## Non-negotiable design principles
38
+
39
+ 1. **Separate caller style from library capability.** No journal,
40
+ project, or paper-specific naming under `crystal_viewer/`. Defaults
41
+ are conveniences; styling must be caller-overridable.
42
+ 2. **Keep APIs layered.** Prefer pure primitives at the bottom,
43
+ composable builders above them, and convenience wrappers at the top.
44
+ Callers must be able to drop down a layer without reimplementing math.
45
+ 3. **Do not mutate shared module state.** Exposed palettes/configs are
46
+ readable defaults. Overrides must flow through explicit call/state data.
47
+ 4. **Reuse before reinventing.** Before adding geometry, chemistry,
48
+ topology, PBC, shape, disorder, or slab logic, check upstream
49
+ `molcrys_kit` and existing MatterVis modules for the current primitive.
50
+ 5. **Respect ownership boundaries.** Browser code stays in `frontend/`,
51
+ service code in `api/` or `app/`, reusable library code in
52
+ `crystal_viewer/`, and chemistry semantics in `molcrys_kit`.
53
+ 6. **Keep operation paths distinct.** Source operations work on real
54
+ crystal objects and return through the loader. Display operations work
55
+ on manifested scene data and must not pretend to create source objects.
56
+ 7. **Keep math domain-neutral.** Projection, rotation, ellipsoid, and PBC
57
+ primitives may live in math; coordination, labels, covalent radii,
58
+ fragments, and species semantics do not.
59
+ 8. **Separate state, assembly, and snapshots.** Editable scene state,
60
+ render assembly, manual overlays, and scene-to-source snapshots have
61
+ different owners; do not blur them for convenience.
62
+ 9. **Keep interactive work asynchronous.** Dash/Flask request callbacks
63
+ must not run expensive topology or figure assembly inline. Use the
64
+ established worker/intent/state flow.
65
+ 10. **Visually verify rendering changes.** Static export can fail
66
+ silently; regenerate the relevant script output and inspect it.
67
+
68
+ ## MolCrysKit boundary
69
+
70
+ `molcrys_kit` owns chemistry: molecule grouping, PBC unwrapping,
71
+ packing shells, shape classification, disorder resolution, slabs, and
72
+ related provenance. MatterVis owns loading adapters, scene state,
73
+ rendering, UI, and service surfaces.
74
+
75
+ When upstream already computes a chemistry quantity, consume it as the
76
+ single source of truth. Do not re-derive it locally “for safety”. When
77
+ upstream deprecates a primitive, read the replacement docs and update
78
+ the public contract in `agents/` if the payload changes.
79
+
80
+ ## Public contracts
81
+
82
+ The caller-visible guarantees for cube rendering, compass overlays,
83
+ scene figures, ORTEP, atom/bond groups, transforms, polyhedra,
84
+ selection, config, REST, and WebSocket behavior live under `agents/`.
85
+ Breaking one of those contracts requires updating the matching doc;
86
+ back-incompatible changes require an API/version bump.
87
+
88
+ ## Tests, lint, and examples
89
+
90
+ - Unit tests: `pytest tests/`.
91
+ - Lint: `ruff check crystal_viewer/`.
92
+ - Example regressions: run the relevant `python scripts/<n>_*.py` and
93
+ inspect the generated artifact under `scripts/_outputs/`.
94
+
95
+ ## Adding or changing code
96
+
97
+ - Name modules and symbols for the data/operation, not a journal,
98
+ project, or one-off output.
99
+ - Add new capability at the lowest layer that fits, then wrap upward.
100
+ - Expose style choices as kwargs/state fields instead of constants.
101
+ - Update the matching `agents/` file for public API changes.
102
+ - Update examples and tests when behavior changes.
103
+ - Put deep implementation warnings in `docs/dev-notes.md` or local
104
+ code comments, not in this top-level contract.
@@ -0,0 +1,28 @@
1
+ Metadata-Version: 2.4
2
+ Name: matter-vis
3
+ Version: 0.0.0
4
+ Summary: Publication-quality crystal structure visualization + terminal viewer
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: dash
7
+ Requires-Dist: plotly
8
+ Requires-Dist: numpy
9
+ Requires-Dist: scipy
10
+ Requires-Dist: scikit-image
11
+ Requires-Dist: gemmi
12
+ Requires-Dist: pymatgen
13
+ Requires-Dist: kaleido
14
+ Requires-Dist: flask-sock
15
+ Requires-Dist: flask-compress
16
+ Requires-Dist: matplotlib
17
+ Requires-Dist: pillow
18
+ Requires-Dist: ase
19
+ Requires-Dist: networkx
20
+ Requires-Dist: textual
21
+ Requires-Dist: molcrys-kit>=0.6.1
22
+ Provides-Extra: test
23
+ Requires-Dist: pytest>=8.0; extra == "test"
24
+ Requires-Dist: pytest-cov; extra == "test"
25
+ Provides-Extra: dev
26
+ Requires-Dist: matter-vis[test]; extra == "dev"
27
+ Requires-Dist: build; extra == "dev"
28
+ Requires-Dist: ruff; extra == "dev"
@@ -0,0 +1,366 @@
1
+ # MatterVis · crystal_viewer
2
+
3
+ Standalone Dash / Plotly frontend and automation toolkit for molecular
4
+ perchlorate crystals (**A**₂**B**(ClO₄)₄ and friends). CIF in — interactive 3D
5
+ viewer, coordination-topology scores, publication-quality Matplotlib export
6
+ and a REST + WebSocket API for other agents, all out.
7
+
8
+ ![banner](docs/images/banner.png)
9
+
10
+ Everything in this README is reproduced from the bundled
11
+ `scripts/data/DAP-4.cif` (a triclinic diammonium diperchlorate `P1` cell,
12
+ see [`scripts/data/README.md`](scripts/data/README.md)); swap it for your
13
+ own CIF with a single flag.
14
+
15
+ ---
16
+
17
+ ## Highlights
18
+
19
+ - **Browser viewer** — Dash front-end, drag-and-drop CIF upload, formula /
20
+ unit-cell / asymmetric-unit / isolated-cluster display, `Mesh3d` atoms and
21
+ bonds with a fast `Scatter3d` fallback for large cells.
22
+ - **Coordination topology** — automatic CN detection via the nearest-neighbour
23
+ gap, angular RMSD vs 12 ideal polyhedra (CN 8-12), planarity RMS of any
24
+ 5-atom subset and a prism / antiprism twist check. See
25
+ [`docs/scores.md`](docs/scores.md).
26
+ - **Publication export** — vendored ORTEP-style Matplotlib renderer with
27
+ correct depth ordering, two-colour bonds, smart label placement and
28
+ configurable presets. Plotly path shares radius-aware viewport bounds so
29
+ large halides never clip at the panel edge.
30
+ - **Multi-panel figures** — `uniform_viewport(scenes)` stamps a shared
31
+ world-cube on any list of scenes so every `build_figure` call emits at the
32
+ same physical length per pixel. Drop-in for N-up comparison figures.
33
+ - **Automation** — REST + WebSocket API on the same Flask server. Drive the
34
+ viewer from notebooks, agents or subprocesses (`GET /api/v1/state`,
35
+ `POST /api/v1/topology`, `GET /api/v1/screenshot`, ...).
36
+ - **Zero catalog required** — the package ships with a single public CIF so
37
+ `mat-vis serve --cif scripts/data/DAP-4.cif` just works.
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ git clone https://github.com/SchrodingersCattt/MatterVis.git
43
+ cd MatterVis
44
+ python -m pip install -r requirements.txt
45
+ python -m pip install -e .
46
+ ```
47
+
48
+ `molcrys_kit` is optional. When available the per-fragment **A / B / X**
49
+ heuristic falls back to its classifier; otherwise `crystal_viewer` uses its
50
+ built-in element / size heuristics. The browser UI doesn't expose A/B/X
51
+ labels directly -- it lists the stoichiometric formulas it detects (e.g.
52
+ `C6N2 ×2`, `ClO4 ×4`, `N ×1`) so the same controls work for non-perovskite
53
+ crystals.
54
+
55
+ ## Quick render (CLI)
56
+
57
+ Generate publication-quality figures from CIF files without launching the
58
+ browser:
59
+
60
+ ```bash
61
+ # PNG with default ball-and-stick style
62
+ mat-vis render structure.cif -o figure.png
63
+
64
+ # PDF, full unit cell, ORTEP hatch shading in greyscale
65
+ mat-vis render structure.cif -o figure.pdf \
66
+ --view unit_cell --style ortep --ortep-mode ortep_hatch --monochrome
67
+
68
+ # Interactive HTML for supplementary information
69
+ mat-vis render structure.cif -o si_figure.html \
70
+ --show-hydrogen --show-labels
71
+ ```
72
+
73
+ See [`docs/cli.md`](docs/cli.md) for the full flag reference, common
74
+ recipes, and advanced `--config` JSON usage.
75
+
76
+ ## Launch the browser viewer
77
+
78
+ ```bash
79
+ mat-vis serve --cif scripts/data/DAP-4.cif
80
+ # Serving crystal viewer at http://0.0.0.0:50001
81
+ ```
82
+
83
+ Additional flags:
84
+
85
+ ```bash
86
+ mat-vis serve --port 8051 # pick a port
87
+ mat-vis serve --host 127.0.0.1 # bind to localhost only
88
+ mat-vis serve --structure DAP-4 # limit catalog to one name
89
+ mat-vis serve --cif a.cif --cif b.cif --cif c.cif
90
+ ```
91
+
92
+ `--cif` is `action="append"` -- repeat the flag once per CIF file you want to
93
+ preload. The viewer listens on `127.0.0.1` by default; pass `--host 0.0.0.0`
94
+ when the dev box is behind a port-forwarding proxy (see below).
95
+
96
+ ### Remote dev boxes (Bohrium and friends)
97
+
98
+ Container platforms typically only expose a small range of ports. On a
99
+ **Bohrium tech development machine** the public proxy maps the URL
100
+ `http://<your-id>.bohrium.tech:5000X` straight through to container port
101
+ `5000X` for `X` in `1..5` -- nothing else is reachable from outside. Two
102
+ common gotchas:
103
+
104
+ 1. **Bind to `0.0.0.0`.** Loopback-only listeners (the `--host 127.0.0.1`
105
+ default) never see traffic from the platform proxy.
106
+ 2. **Use one of `50001`-`50005`.** Other ports on the container may be in
107
+ use by Jupyter / MCP servers; check with `ss -ltn | grep 5000` before
108
+ you pick one.
109
+
110
+ Putting it together for a Bohrium dev box:
111
+
112
+ ```bash
113
+ mat-vis serve --host 0.0.0.0 --port 50001 \
114
+ --cif scripts/data/DAP-4.cif --cif scripts/data/SY.cif
115
+ # Reachable at http://<your-id>.bohrium.tech:50001/
116
+ ```
117
+
118
+ If the URL still ERR_CONNECTION_REFUSED, port `50001` is already taken on
119
+ that container -- pick another from the `50001-50005` window.
120
+
121
+ ### Open the page in a real browser, not an embedded webview
122
+
123
+ Cursor's *Simple Browser*, VS Code's *preview panel* and similar
124
+ Electron-hosted webviews dispatch click events at the DOM level but
125
+ do not always propagate them through React 16's synthetic event
126
+ system that Dash binds onto. The visible failure mode is brutal:
127
+ **every checkbox / dropdown / slider appears to "click" but nothing
128
+ happens** -- the figure never updates, the network tab is silent
129
+ (no `/_dash-update-component` POST is ever sent), and the user
130
+ concludes the app is broken.
131
+
132
+ The viewer auto-detects this case by sniffing the
133
+ `User-Agent` and renders a sticky red banner at the top of the page
134
+ with a one-click *Copy URL* button. Paste the URL into Chrome, Edge,
135
+ Firefox or Safari and every control will start firing as expected.
136
+
137
+ A built-in wire-tap strip is also available behind the
138
+ `?diag=1` query parameter -- it shows live counts of clicks, Dash
139
+ POSTs and JS errors so you can tell at a glance whether a "no
140
+ response" report is a DOM problem (no clicks), an event-delegation
141
+ problem (clicks but no POST), or a server problem (POST but no OK).
142
+
143
+ For server-side debugging the same information is available via
144
+ `MATTERVIS_AUDIT=1 mat-vis serve ...`, which prints a
145
+ one-line summary of every non-poll callback (changedPropIds,
146
+ duration, payload size, originating IP / User-Agent).
147
+
148
+ See [`AGENTS.md`](AGENTS.md) for every REST / WebSocket endpoint and the full
149
+ set of stable UI element IDs.
150
+
151
+ ## Headless scripts
152
+
153
+ Every script in [`scripts/`](scripts) can be run end-to-end without a
154
+ browser:
155
+
156
+ ```bash
157
+ python scripts/01_quick_render.py # CIF -> PNG + interactive HTML
158
+ python scripts/02_coordination_analysis.py # coordination shell + all scores
159
+ python scripts/03_display_modes_panel.py # formula / unit cell / shell
160
+ python scripts/04_static_publication.py # ORTEP-style PNG + PDF
161
+ python scripts/05_app_and_api.py # launch app + drive it via REST
162
+ python scripts/06_cp2k_cube_orbital.py --cube orbital.cube # CP2K/Gaussian cube isosurfaces
163
+ ```
164
+
165
+ Outputs land under `scripts/_outputs/` (gitignored). Regenerate the README
166
+ showcase images with `python docs/build_images.py`.
167
+
168
+ ### CP2K / Gaussian cube orbitals
169
+
170
+ MatterVis can render CP2K or Gaussian `.cube` orbital files as paired positive
171
+ and negative Plotly isosurfaces, with atom positions overlaid from the cube
172
+ header:
173
+
174
+ ```bash
175
+ python scripts/06_cp2k_cube_orbital.py \
176
+ --cube /path/to/cp2k-WFN_00292_1-1_0.cube \
177
+ --output-prefix HOCO \
178
+ --stride 2 \
179
+ --percentile 98.5
180
+ ```
181
+
182
+ The reader converts cube coordinates from Bohr to Å and automatically chooses a
183
+ robust isovalue from the absolute-value distribution unless `--isovalue` is
184
+ provided. HTML output is always generated; PNG export is attempted when Kaleido
185
+ is available.
186
+
187
+ ### 1. `01_quick_render.py` — CIF to unit cell in ten lines
188
+
189
+ ```python
190
+ from crystal_viewer.loader import build_bundle_scene, build_loaded_crystal
191
+ from crystal_viewer.renderer import build_figure
192
+ from crystal_viewer.scene import scene_style
193
+
194
+ bundle = build_loaded_crystal(name="DAP-4", cif_path="scripts/data/DAP-4.cif")
195
+ scene = build_bundle_scene(bundle, display_mode="unit_cell")
196
+ style = scene_style(scene, {"show_unit_cell": True})
197
+
198
+ fig = build_figure(scene, style)
199
+ fig.write_image("dap4.png", width=900, height=720, scale=2)
200
+ fig.write_html("dap4.html", include_plotlyjs="cdn")
201
+ ```
202
+
203
+ ![unit cell](docs/images/feature_unit_cell.png)
204
+
205
+ ### 2. `02_coordination_analysis.py` — topology scores with one function call
206
+
207
+ ```python
208
+ from crystal_viewer.loader import build_loaded_crystal
209
+ from crystal_viewer.topology import analyze_topology
210
+
211
+ bundle = build_loaded_crystal(name="DAP-4", cif_path="scripts/data/DAP-4.cif")
212
+ # Pick the first DABCO ring -- in DAP-4 those have stoichiometry C6N2.
213
+ target = next(f for f in bundle.topology_fragment_table if f.get("formula") == "C6N2")
214
+ result = analyze_topology(bundle, center_index=target["index"], cutoff=8.0)
215
+
216
+ print(result["coordination_number"]) # 9
217
+ print(result["shape"]["primary_label"]) # tricapped_trigonal_prism
218
+ print(result["shape"]["label_modifier"]) # distorted
219
+ print(result["gap_info"]["gap_value"]) # 0.124 Å
220
+ ```
221
+
222
+ The example also dumps a tidy `02_coordination_summary.json` with every score
223
+ broken out — see [`docs/scores.md`](docs/scores.md) for a full reference.
224
+
225
+ <p>
226
+ <img src="docs/images/feature_coordination.png" alt="coordination hull" width="49%"/>
227
+ <img src="docs/images/feature_histogram.png" alt="distance histogram" width="49%"/>
228
+ </p>
229
+
230
+ ### 3. `03_display_modes_panel.py` — side-by-side display modes
231
+
232
+ A single Plotly figure stitches formula unit, unit cell, and coordination
233
+ shell together so reviewers can switch between them without reloading:
234
+
235
+ ![three modes](docs/images/feature_three_modes.png)
236
+
237
+ ### 4. `04_static_publication.py` — ORTEP-style Matplotlib export
238
+
239
+ Runs the vendored `crystal_viewer.static_publication.plot_crystal` renderer (same code
240
+ used by `POST /api/v1/export`) to produce high-DPI PNG + PDF suitable for
241
+ Nature-style figures:
242
+
243
+ ![publication](docs/images/feature_publication.png)
244
+
245
+ ### 5. `05_app_and_api.py` — drive the live viewer over HTTP
246
+
247
+ Starts `create_app()` in the background, preloads `DAP-4`, then hits
248
+ `POST /api/v1/topology` and `GET /api/v1/screenshot`. The call sequence is a
249
+ one-page recipe for wiring the viewer into a larger agent pipeline.
250
+
251
+ ## Topology scores
252
+
253
+ `analyze_topology()` (and the REST `POST /api/v1/topology`) return five
254
+ named scores that together describe a coordination shell. One line summary:
255
+
256
+ | Score | Module field | What it measures |
257
+ | --- | --- | --- |
258
+ | Coordination number | `coordination_number` + `gap_info.gap_value` | Neighbours in the first shell, picked by the largest distance jump. |
259
+ | Shape classification | `shape.primary_label` / `label_modifier` / `cshm_value` | CShM-based polyhedron name + distortion tier (`clean` / `distorted` / `ambiguous`) for CN 4-12. |
260
+ | Planarity RMS | `planarity.best_rms` / `best_indices` | Best-fit plane through any 5 shell atoms (Å). |
261
+ | Prism / antiprism twist | `prism_analysis.twist_deg` / `classification` | Average inter-ring rotation; threshold 18°, only for CN ≥ 10. |
262
+ | Convex hull | `hull.vertices` / `simplices` / `edges` | Geometry the viewer draws as the purple polyhedron. |
263
+
264
+ See [`docs/scores.md`](docs/scores.md) for how each score is computed,
265
+ reasonable thresholds, and worked numbers for DAP-4.
266
+
267
+ ## Rendering isolated clusters
268
+
269
+ Perchlorate-cluster CIFs (dummy 100 Å cell, atoms listed in P1, no periodic
270
+ imaging expected) go through a dedicated display mode that skips formula-unit
271
+ trimming and MIC bond search:
272
+
273
+ ```python
274
+ from crystal_viewer.scene import build_scene_from_cif
275
+ from crystal_viewer.renderer import build_figure, uniform_viewport
276
+ from crystal_viewer.presets import DEFAULT_STYLE, deep_merge
277
+
278
+ # Palette override: add elements not in the vendored table (I, Na, K, Rb, …)
279
+ # or restyle existing ones for a specific figure.
280
+ style = deep_merge(DEFAULT_STYLE, {
281
+ "show_title": False,
282
+ "show_labels": False,
283
+ "show_axes": False,
284
+ "show_hydrogen": True,
285
+ "atom_scale": 0.9,
286
+ "bond_radius": 0.14,
287
+ "element_colors": {"I": "#940094", "Na": "#E6D11E", "K": "#AB82FF"},
288
+ })
289
+ preset = {"version": 1, "style": style, "structures": {}}
290
+
291
+ scenes = [
292
+ build_scene_from_cif(
293
+ name=name,
294
+ cif_path=f"clusters/{name}.cif",
295
+ title=name,
296
+ preset=preset,
297
+ show_hydrogen=True,
298
+ display_mode="cluster",
299
+ )
300
+ for name in ("DAI-1", "DAI-4", "DAP-2", "DAP-M4")
301
+ ]
302
+
303
+ # Pin every panel to the same world cube so panel-to-panel scale is identical
304
+ # and no Cl / I atoms clip at the edge. The cube side = biggest scene span +
305
+ # padding (in Å).
306
+ uniform_viewport(scenes, padding=0.5)
307
+
308
+ for scene in scenes:
309
+ fig = build_figure(scene, style)
310
+ fig.write_image(f"{scene['name']}.png", width=600, height=600, scale=2)
311
+ ```
312
+
313
+ Every scene now renders at the same Å-per-pixel, with radius-aware bounds so
314
+ even Cl / I / Br spheres are fully visible. `display_mode="cluster"` honours
315
+ the stored Cartesian coordinates exactly — useful when the CIF encodes a
316
+ hand-curated fragment rather than a crystallographic asymmetric unit.
317
+
318
+ ## Package layout
319
+
320
+ ```
321
+ crystal_viewer/
322
+ ├── __init__.py # re-exports create_app
323
+ ├── __main__.py # compatibility entry point; use `mat-vis`
324
+ ├── app/ # Dash layout, callbacks, ViewerBackend internals
325
+ ├── api/ # REST + WebSocket blueprints
326
+ ├── render/ # Plotly viewport / traces / cache internals
327
+ ├── structure/ # CIF parsing, bonds, formula units, MolCrysKit bridge
328
+ ├── loader/ # CIF/fragment bundle loading facade + upload helpers
329
+ ├── scene/ # cell/cluster scene builder facade
330
+ ├── transforms/ # repeat/grow/slab transform primitives and pipeline
331
+ ├── topology/ # coordination-shell extraction & shape analysis
332
+ ├── style/ # atom/bond rules, palette, disorder render helpers
333
+ ├── cube/ # cube/orbital I/O, meshes, traces, export facade
334
+ ├── ortep/ # thermal ellipsoid math, billboards, traces
335
+ ├── compass/ # camera-projected lattice direction annotations
336
+ ├── renderer/ # public Plotly facade for atoms, bonds, hulls, axes
337
+ ├── presets/ # preset / style / catalog IO
338
+ ├── scenes/ # tab/session scene state
339
+ ├── perf_log/ # lightweight server event log facade
340
+ ├── depth_sort/ # matplotlib depth-order helpers
341
+ ├── viewer_backend/ # public ViewerBackend compatibility facade
342
+ ├── assets/ # Dash CSS + JS for the panel layout
343
+ └── static_publication/ # matplotlib publication exporter
344
+ scripts/ # runnable demo scripts (see section above)
345
+ docs/ # README showcase images + scores.md
346
+ ```
347
+
348
+ ## Notes
349
+
350
+ - Plotly screenshot export uses `kaleido`; the first call is slow because
351
+ it spins up a headless Chromium.
352
+ - Static publication export writes to `.exports/` (gitignored).
353
+ - Local presets default to `.local/crystal_view_preset.json`.
354
+ - Local catalog overrides can be supplied via `catalog.local.json` or
355
+ `.local/catalog.local.json`.
356
+ - The CIF parser handles Materials-Studio style loops that omit
357
+ `_atom_site_disorder_*` columns — no manual cleanup required.
358
+ - For automation details (every endpoint, stable UI IDs, WebSocket schema),
359
+ see [`AGENTS.md`](AGENTS.md).
360
+
361
+ ## Citing
362
+
363
+ If you use the bundled example structure, please also credit the originating
364
+ publication that released it — see [`scripts/data/README.md`](scripts/data/README.md).
365
+ The `crystal_viewer` code itself is released under the repository's root
366
+ license file.