meltygui 0.1.0__py3-none-any.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.
- meltygui/__init__.py +107 -0
- meltygui/accounts/__init__.py +0 -0
- meltygui/accounts/internet_accounts.py +1355 -0
- meltygui/chat/__init__.py +91 -0
- meltygui/chat/activity.py +75 -0
- meltygui/chat/backends.py +36 -0
- meltygui/chat/chat_interface.py +732 -0
- meltygui/chat/chat_proxy.py +352 -0
- meltygui/chat/codex_proxy.py +592 -0
- meltygui/chat/codex_settings.py +100 -0
- meltygui/chat/codex_transport.py +60 -0
- meltygui/chat/command_parser.py +204 -0
- meltygui/chat/images.py +227 -0
- meltygui/chat/messages.py +417 -0
- meltygui/chat/metadata.py +139 -0
- meltygui/chat/writer_locks.py +64 -0
- meltygui/code/__init__.py +0 -0
- meltygui/code/basic_converters.py +533 -0
- meltygui/code/chain_converters.py +2111 -0
- meltygui/code/code_checks.py +2209 -0
- meltygui/code/core_syntax.py +1430 -0
- meltygui/code/file_converters.py +1933 -0
- meltygui/code/fileref.py +702 -0
- meltygui/code/hotswap_guard.py +144 -0
- meltygui/code/libcst_conversion.py +9724 -0
- meltygui/code/live_instrument.py +392 -0
- meltygui/code/live_view.py +2490 -0
- meltygui/code/melty_scan.py +2684 -0
- meltygui/code/new_codecs.py +1255 -0
- meltygui/code/new_converters.py +3017 -0
- meltygui/code/project_code.py +278 -0
- meltygui/code/source_context.py +63 -0
- meltygui/code/symbol_roster.py +1588 -0
- meltygui/code/syntax_check.py +34 -0
- meltygui/code/syntax_check_worker.py +114 -0
- meltygui/completion/__init__.py +0 -0
- meltygui/completion/fim.py +1232 -0
- meltygui/completion/fim_context.py +481 -0
- meltygui/completion/providers/__init__.py +0 -0
- meltygui/completion/providers/anthropic_oauth.py +446 -0
- meltygui/completion/providers/anthropic_requests.py +69 -0
- meltygui/completion/providers/claude.py +203 -0
- meltygui/completion/providers/claude_usage.py +499 -0
- meltygui/completion/providers/codex_accounts.py +180 -0
- meltygui/completion/providers/copilot.py +617 -0
- meltygui/completion/providers/oauth_popup.py +220 -0
- meltygui/completion/providers/ollama.py +320 -0
- meltygui/completion/providers/profiles.py +23 -0
- meltygui/core/README.md +88 -0
- meltygui/core/__init__.py +1 -0
- meltygui/core/automation/__init__.py +1 -0
- meltygui/core/automation/action_core.py +153 -0
- meltygui/core/automation/collection_action.py +45 -0
- meltygui/core/automation/mcp_eval.py +167 -0
- meltygui/core/automation/mcp_hotswap.py +107 -0
- meltygui/core/automation/mcp_query.py +552 -0
- meltygui/core/automation/mcp_server.py +657 -0
- meltygui/core/automation/orchestration_core.py +2636 -0
- meltygui/core/automation/query_core.py +124 -0
- meltygui/core/automation/search_core.py +26 -0
- meltygui/core/automation/selector_core.py +340 -0
- meltygui/core/automation/value_core.py +1752 -0
- meltygui/core/cache/__init__.py +1 -0
- meltygui/core/cache/cache_diagnostics.py +0 -0
- meltygui/core/cache/invalidation_decoration.py +153 -0
- meltygui/core/cache/invalidation_tracker.py +43 -0
- meltygui/core/cache/tile_cache.py +5968 -0
- meltygui/core/conversion/__init__.py +1 -0
- meltygui/core/conversion/bubbling.py +599 -0
- meltygui/core/conversion/cache_tree.py +188 -0
- meltygui/core/conversion/chain.py +113 -0
- meltygui/core/conversion/converter_register.py +145 -0
- meltygui/core/conversion/data_decoration.py +67 -0
- meltygui/core/conversion/dict_conversion.py +1815 -0
- meltygui/core/conversion/dict_conversion_util.py +177 -0
- meltygui/core/conversion/dynamic_obj.py +89 -0
- meltygui/core/conversion/graph_compare.py +183 -0
- meltygui/core/conversion/load_save_v2.py +1032 -0
- meltygui/core/conversion/missing_saved_class.py +39 -0
- meltygui/core/conversion/path_finder.py +606 -0
- meltygui/core/conversion/render_host.py +1083 -0
- meltygui/core/core_render.py +6537 -0
- meltygui/core/definition_hotswap.py +231 -0
- meltygui/core/diagnostics/__init__.py +1 -0
- meltygui/core/diagnostics/attribute_churn.py +38 -0
- meltygui/core/diagnostics/fps_counter.py +39 -0
- meltygui/core/diagnostics/gpu_frame_timer.py +103 -0
- meltygui/core/diagnostics/inspection_core.py +169 -0
- meltygui/core/diagnostics/monitor_core.py +105 -0
- meltygui/core/diagnostics/notifications.py +706 -0
- meltygui/core/diagnostics/perf_trace.py +281 -0
- meltygui/core/diagnostics/profile_decoration.py +88 -0
- meltygui/core/diagnostics/resize_trace.py +62 -0
- meltygui/core/diagnostics/screenshot_core.py +247 -0
- meltygui/core/diagnostics/session_status.py +98 -0
- meltygui/core/diagnostics/trace_core.py +445 -0
- meltygui/core/files/__init__.py +1 -0
- meltygui/core/files/file_core.py +208 -0
- meltygui/core/files/file_explorer_core.py +104 -0
- meltygui/core/files/file_tree_core.py +198 -0
- meltygui/core/files/file_watch_core.py +43 -0
- meltygui/core/files/import_graph_core.py +43 -0
- meltygui/core/files/metadata_core.py +51 -0
- meltygui/core/graphics/__init__.py +1 -0
- meltygui/core/graphics/cuda_context_core.py +166 -0
- meltygui/core/graphics/cuda_interop_core.py +136 -0
- meltygui/core/graphics/cuda_kernel_core.py +91 -0
- meltygui/core/graphics/framebuffer_recorder.py +337 -0
- meltygui/core/graphics/gl_state.py +658 -0
- meltygui/core/graphics/lut_core.py +52 -0
- meltygui/core/graphics/overlay_renderer.py +984 -0
- meltygui/core/graphics/scene_target.py +180 -0
- meltygui/core/graphics/screenshot.py +439 -0
- meltygui/core/graphics/shader_func.py +478 -0
- meltygui/core/graphics/tensor_core.py +45 -0
- meltygui/core/graphics/text_texture.py +329 -0
- meltygui/core/graphics/wayland_color.py +635 -0
- meltygui/core/input/__init__.py +1 -0
- meltygui/core/input/collision.py +165 -0
- meltygui/core/input/drag_drop_core.py +1525 -0
- meltygui/core/input/hypr_left_drag.py +323 -0
- meltygui/core/input/input_core.py +245 -0
- meltygui/core/input/input_handler.py +1101 -0
- meltygui/core/input/mouse_cursor.py +355 -0
- meltygui/core/input/pynput_backend.py +1054 -0
- meltygui/core/input/space_mouse.py +338 -0
- meltygui/core/input/touchpad_backend.py +393 -0
- meltygui/core/input/view_selection.py +177 -0
- meltygui/core/layout/__init__.py +1 -0
- meltygui/core/layout/column_core.py +2153 -0
- meltygui/core/layout/cursor_core.py +161 -0
- meltygui/core/layout/dropdown_core.py +278 -0
- meltygui/core/layout/edge_constraints.py +155 -0
- meltygui/core/layout/grid_core.py +117 -0
- meltygui/core/layout/header_core.py +23 -0
- meltygui/core/layout/header_runtime.py +67 -0
- meltygui/core/layout/layout_core.py +87 -0
- meltygui/core/layout/tile_manager_core.py +591 -0
- meltygui/core/melty.py +6726 -0
- meltygui/core/module_map.json +896 -0
- meltygui/core/module_names.py +19 -0
- meltygui/core/rendering/__init__.py +1 -0
- meltygui/core/rendering/core_decoration.py +431 -0
- meltygui/core/rendering/core_render_helpers.py +328 -0
- meltygui/core/rendering/func_metadata.py +398 -0
- meltygui/core/rendering/mode.py +818 -0
- meltygui/core/rendering/mode_defaults.py +41 -0
- meltygui/core/rendering/modes.py +136 -0
- meltygui/core/rendering/parameter_core.py +1665 -0
- meltygui/core/rendering/render_dispatch.py +1891 -0
- meltygui/core/rendering/render_funcs.py +273 -0
- meltygui/core/rendering/shaped.py +312 -0
- meltygui/core/rendering/window_decoration.py +25 -0
- meltygui/core/runtime/__init__.py +1 -0
- meltygui/core/runtime/app.py +735 -0
- meltygui/core/runtime/app_session.py +140 -0
- meltygui/core/runtime/background.py +564 -0
- meltygui/core/runtime/extensions.py +53 -0
- meltygui/core/runtime/gc_manager.py +1163 -0
- meltygui/core/runtime/lifecycle.py +21 -0
- meltygui/core/runtime/paths.py +27 -0
- meltygui/core/runtime/settings.py +14 -0
- meltygui/core/runtime/singleton.py +16 -0
- meltygui/core/runtime/thread_safe_bool.py +24 -0
- meltygui/core/runtime/thread_signal.py +30 -0
- meltygui/core/runtime/toggles.py +3115 -0
- meltygui/core/services/__init__.py +1 -0
- meltygui/core/services/account_core.py +10 -0
- meltygui/core/services/chat_core.py +19 -0
- meltygui/core/services/claude_terminal_core.py +346 -0
- meltygui/core/services/terminal_core.py +458 -0
- meltygui/core/services/terminal_runtime.py +78 -0
- meltygui/core/styling/__init__.py +1 -0
- meltygui/core/styling/color_core.py +46 -0
- meltygui/core/styling/fonts.py +639 -0
- meltygui/core/styling/global_style.py +338 -0
- meltygui/core/styling/style.py +198 -0
- meltygui/core/styling/style_core.py +522 -0
- meltygui/core/styling/warm_start.py +149 -0
- meltygui/core/windowing/__init__.py +1 -0
- meltygui/core/windowing/backends/PYIMGUI_LICENSE +28 -0
- meltygui/core/windowing/backends/__init__.py +1 -0
- meltygui/core/windowing/backends/imgui_renderer.py +138 -0
- meltygui/core/windowing/backends/native_wayland.py +850 -0
- meltygui/core/windowing/backends/protocols/xdg-decoration-unstable-v1.xml +156 -0
- meltygui/core/windowing/backends/protocols/xdg-shell.xml +1420 -0
- meltygui/core/windowing/backends/wayland_protocol.py +101 -0
- meltygui/core/windowing/dock_core.py +182 -0
- meltygui/core/windowing/frame_geometry.py +42 -0
- meltygui/core/windowing/geometry_feed.py +864 -0
- meltygui/core/windowing/glfw_utils.py +1343 -0
- meltygui/core/windowing/os_frame.py +1552 -0
- meltygui/core/windowing/surface.py +643 -0
- meltygui/core/windowing/titlebar.py +1560 -0
- meltygui/core/windowing/titlebar_buttons.py +281 -0
- meltygui/core/windowing/wayland_move.py +932 -0
- meltygui/core/windowing/window_api.py +62 -0
- meltygui/core/windowing/window_constants.py +339 -0
- meltygui/core/windowing/window_visibility.py +162 -0
- meltygui/debug/__init__.py +0 -0
- meltygui/debug/app_view_utils.py +9 -0
- meltygui/editor/__init__.py +0 -0
- meltygui/editor/bash_syntax.py +30 -0
- meltygui/editor/code_line_fast.py +152 -0
- meltygui/editor/diff.py +139 -0
- meltygui/editor/external_changes.py +159 -0
- meltygui/editor/file_header.py +41 -0
- meltygui/editor/live_usage.py +169 -0
- meltygui/editor/live_view_views.py +1803 -0
- meltygui/editor/pending_save.py +1351 -0
- meltygui/editor/roster_tints.py +547 -0
- meltygui/editor/source_preview.py +16 -0
- meltygui/editor/source_tools.py +10 -0
- meltygui/editor/source_ui.py +70 -0
- meltygui/editor/spell_check.py +77 -0
- meltygui/editor/text_editor.py +9076 -0
- meltygui/editor/usage_picker.py +538 -0
- meltygui/events/__init__.py +0 -0
- meltygui/events/example.py +118 -0
- meltygui/examples/__init__.py +0 -0
- meltygui/examples/columns_demo.py +33 -0
- meltygui/examples/columns_window_demo.py +60 -0
- meltygui/examples/context_menu_demo.py +24 -0
- meltygui/examples/context_menu_window_demo.py +49 -0
- meltygui/examples/gui_playground.py +127 -0
- meltygui/examples/live_view_playground.py +256 -0
- meltygui/examples/lora.py +43 -0
- meltygui/examples/lora_data.py +59 -0
- meltygui/examples/lora_policies.py +67 -0
- meltygui/examples/mode_demo.py +49 -0
- meltygui/examples/modifies_demo.py +119 -0
- meltygui/examples/scalar_policies.py +60 -0
- meltygui/examples/style_layouts.py +149 -0
- meltygui/examples/tile_manager_demo.py +74 -0
- meltygui/examples/tint_demo.py +160 -0
- meltygui/examples/tint_functions.py +56 -0
- meltygui/examples/trace_demo.py +88 -0
- meltygui/examples/two_windows.py +36 -0
- meltygui/files/__init__.py +0 -0
- meltygui/files/fast_file_explorer.py +439 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/extension.js +237 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/lsd-window-geometry@latent-descent.iml +9 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/metadata.json +7 -0
- meltygui/graphics/__init__.py +6 -0
- meltygui/graphics/base.py +85 -0
- meltygui/graphics/examples.py +507 -0
- meltygui/graphics/executor.py +520 -0
- meltygui/graphics/filter.py +667 -0
- meltygui/graphics/filter.pyi +856 -0
- meltygui/graphics/generate_stubs.py +22 -0
- meltygui/graphics/registry.py +203 -0
- meltygui/graphics/shader_compiler.py +155 -0
- meltygui/graphics/shaders.py +1302 -0
- meltygui/graphics/stub_generator.py +250 -0
- meltygui/graphics/texture_manager.py +170 -0
- meltygui/graphics/texture_min_max.py +295 -0
- meltygui/hdr_color.py +757 -0
- meltygui/image_load.py +308 -0
- meltygui/model/__init__.py +1 -0
- meltygui/model/account_model.py +142 -0
- meltygui/model/camera_model.py +159 -0
- meltygui/model/chat_model.py +86 -0
- meltygui/model/code_model.py +62 -0
- meltygui/model/code_proxy_model.py +876 -0
- meltygui/model/collection_model.py +41 -0
- meltygui/model/color_model.py +127 -0
- meltygui/model/cuda_tensor_model.py +36 -0
- meltygui/model/cuda_texture_model.py +149 -0
- meltygui/model/dropdown_model.py +137 -0
- meltygui/model/file_metadata_model.py +73 -0
- meltygui/model/file_model.py +289 -0
- meltygui/model/format_model.py +390 -0
- meltygui/model/graph_model.py +98 -0
- meltygui/model/icon_model.py +1024 -0
- meltygui/model/import_graph_model.py +468 -0
- meltygui/model/layout_model.py +15 -0
- meltygui/model/lut_model.py +266 -0
- meltygui/model/search_model.py +173 -0
- meltygui/model/tensor_model.py +402 -0
- meltygui/model/terminal_model.py +329 -0
- meltygui/model/texture_model.py +146 -0
- meltygui/model/tile_model.py +24 -0
- meltygui/model/trace_model.py +198 -0
- meltygui/model/trace_report_model.py +146 -0
- meltygui/models/__init__.py +0 -0
- meltygui/models/file_meta.py +613 -0
- meltygui/models/function_console.py +184 -0
- meltygui/models/orchestration.py +48 -0
- meltygui/pbr.py +1576 -0
- meltygui/png_unfilter.c +49 -0
- meltygui/resources/JetBrainsMono-Regular.ttf +0 -0
- meltygui/resources/THIRD_PARTY_NOTICES.md +21 -0
- meltygui/resources/dejavu/DejaVuSans-Bold.ttf +0 -0
- meltygui/resources/dejavu/DejaVuSans-ExtraLight.ttf +0 -0
- meltygui/resources/dejavu/DejaVuSans.ttf +0 -0
- meltygui/resources/dejavu/LICENSE.txt +78 -0
- meltygui/resources/fontawesome-LICENSE.txt +121 -0
- meltygui/resources/fontawesome-webfont.ttf +0 -0
- meltygui/resources/hdri/studio_small_09_1k.hdr +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Bold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraBold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraLight.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Light.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Medium.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-SemiBold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Thin.ttf +0 -0
- meltygui/resources/jetbrains-weights/OFL.txt +93 -0
- meltygui/resources/jetbrains-weights/README.md +2 -0
- meltygui/state/__init__.py +0 -0
- meltygui/state/account_state.py +22 -0
- meltygui/state/animation_state.py +97 -0
- meltygui/state/annotation_state.py +22 -0
- meltygui/state/chat_state.py +36 -0
- meltygui/state/code_state.py +10 -0
- meltygui/state/core_enums.py +59 -0
- meltygui/state/core_markers.py +115 -0
- meltygui/state/core_undo.py +1075 -0
- meltygui/state/file_state.py +75 -0
- meltygui/state/graph_state.py +26 -0
- meltygui/state/inspection_state.py +47 -0
- meltygui/state/menu_state.py +10 -0
- meltygui/state/model_enums.py +11 -0
- meltygui/state/new_core_model.py +2394 -0
- meltygui/state/orchestration_state.py +14 -0
- meltygui/state/query_state.py +24 -0
- meltygui/state/tensor_state.py +10 -0
- meltygui/state/terminal_state.py +23 -0
- meltygui/state/trace_state.py +32 -0
- meltygui/state/voxel_state.py +12 -0
- meltygui/text_index.py +816 -0
- meltygui/utils/__init__.py +0 -0
- meltygui/utils/jump_to_code.py +344 -0
- meltygui/utils/pkl_inspect.py +90 -0
- meltygui/utils/render_utils.py +1419 -0
- meltygui/view/__init__.py +1 -0
- meltygui/view/account_view.py +524 -0
- meltygui/view/action_view.py +73 -0
- meltygui/view/chat_decoration_view.py +112 -0
- meltygui/view/chat_view.py +1703 -0
- meltygui/view/code_view.py +2677 -0
- meltygui/view/collection_view.py +1185 -0
- meltygui/view/color_view.py +824 -0
- meltygui/view/control_view.py +559 -0
- meltygui/view/decoration_view.py +439 -0
- meltygui/view/diagnostic_view.py +177 -0
- meltygui/view/dropdown_view.py +1082 -0
- meltygui/view/file_view.py +1599 -0
- meltygui/view/graph_cuda_view.py +105 -0
- meltygui/view/graph_view.py +882 -0
- meltygui/view/header_view.py +848 -0
- meltygui/view/input_view.py +238 -0
- meltygui/view/inspection_view.py +1783 -0
- meltygui/view/layout_view.py +596 -0
- meltygui/view/lut_view.py +45 -0
- meltygui/view/menu_view.py +212 -0
- meltygui/view/orchestration_view.py +658 -0
- meltygui/view/query_view.py +118 -0
- meltygui/view/search_view.py +342 -0
- meltygui/view/tab_view.py +258 -0
- meltygui/view/tensor_view.py +430 -0
- meltygui/view/terminal_view.py +355 -0
- meltygui/view/text_view.py +8031 -0
- meltygui/view/texture_view.py +480 -0
- meltygui/view/tile_view.py +38 -0
- meltygui/view/trace_view.py +923 -0
- meltygui/view/voxel_cuda_view.py +824 -0
- meltygui/view/voxel_view.py +1860 -0
- meltygui/view/window_view.py +111 -0
- meltygui-0.1.0.dist-info/METADATA +143 -0
- meltygui-0.1.0.dist-info/RECORD +372 -0
- meltygui-0.1.0.dist-info/WHEEL +4 -0
- meltygui-0.1.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
"""Tensor types and data transformations, independent of rendering and GL."""
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
# Display-axis position in a sliced (z, y, x) volume.
|
|
8
|
+
_AXIS_POS = {"z": 0, "y": 1, "x": 2}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TensorDim(int):
|
|
13
|
+
"""A tensor dim index that is still an int everywhere it matters
|
|
14
|
+
(indexing, comparisons, arithmetic, `int()`, pickling) but carries its own
|
|
15
|
+
TYPE, so meltygui routes it to its own renderer instead of the plain int one
|
|
16
|
+
— a dim picker rather than a number field.
|
|
17
|
+
|
|
18
|
+
Values only stay TensorDim if whatever writes them keeps the type: a
|
|
19
|
+
renderer registered `@render_func(is_default_for=TensorDim)` should return
|
|
20
|
+
TensorDim(...), otherwise the first edit stores a plain int and the row
|
|
21
|
+
falls back to the int renderer."""
|
|
22
|
+
|
|
23
|
+
__slots__ = ()
|
|
24
|
+
|
|
25
|
+
def __repr__(self):
|
|
26
|
+
return f"TensorDim({int(self)})"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TensorDims(tuple):
|
|
30
|
+
"""A SET of tensor dim indices (`mean_dims`) — tuple everywhere it
|
|
31
|
+
matters, but typed so it routes to the same dim picker as TensorDim
|
|
32
|
+
(multi-select tabs). A tuple needs SOME type to route by; this is the
|
|
33
|
+
minimal one, and the renderer is shared."""
|
|
34
|
+
|
|
35
|
+
__slots__ = ()
|
|
36
|
+
|
|
37
|
+
def __repr__(self):
|
|
38
|
+
return f"TensorDims({tuple(int(v) for v in self)})"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
from meltygui.model.lut_model import Lut
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _clean_dim_name(x, i):
|
|
45
|
+
"""A dim name is a short single-line LABEL, whatever lands in the list —
|
|
46
|
+
DnD/paste can drop arbitrary objects whose str() is a multi-KB code repr,
|
|
47
|
+
and one of those blows up every radio row and billboard bake."""
|
|
48
|
+
first = (str(x).splitlines() or [""])[0].strip()
|
|
49
|
+
return first[:48] if first else f"dim{i}"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _collection_dim_labels(col):
|
|
53
|
+
"""Dim-name labels from the collection's `dim_names` entry; [] when no
|
|
54
|
+
names are in reach."""
|
|
55
|
+
raw_names = col.get("dim_names", ()) if col is not None else ()
|
|
56
|
+
return [_clean_dim_name(x, i) for i, x in enumerate(raw_names or ())]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _resolve_dim(dim_names, v, n):
|
|
60
|
+
"""A dim given by INDEX or by NAME (resolved through dim_names); None
|
|
61
|
+
stays None, out-of-range collapses to None."""
|
|
62
|
+
if v is None:
|
|
63
|
+
return None
|
|
64
|
+
if isinstance(v, str):
|
|
65
|
+
names = list(dim_names or ())
|
|
66
|
+
if v not in names:
|
|
67
|
+
return None
|
|
68
|
+
v = names.index(v)
|
|
69
|
+
try:
|
|
70
|
+
v = int(v)
|
|
71
|
+
except (TypeError, ValueError):
|
|
72
|
+
# params are user-editable from the panel and from source, so a dim
|
|
73
|
+
# can arrive as anything at all. Unusable = unset.
|
|
74
|
+
return None
|
|
75
|
+
return v if 0 <= v < n else None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _resolve_axes(shape, dim_names, x_dim, y_dim, z_dim):
|
|
79
|
+
"""(z, y, x) display dims for a shape: dims by index or NAME, None
|
|
80
|
+
derives the default (last three dims → z/y/x, like the old viewer).
|
|
81
|
+
|
|
82
|
+
ALWAYS returns three DISTINCT in-range dims (for n >= 3) — the params are
|
|
83
|
+
editable from the panel and from source, so two axes can name the same dim
|
|
84
|
+
or a garbage one. A dim already claimed by an earlier axis is treated as
|
|
85
|
+
unset and re-derived, which keeps the slicing downstream well-formed
|
|
86
|
+
(duplicate picks collapse the sliced volume to 2 dims and the permute
|
|
87
|
+
blows up). z wins over y wins over x, so the LAST axis you retarget onto a
|
|
88
|
+
taken dim is the one that moves."""
|
|
89
|
+
n = len(shape)
|
|
90
|
+
resolved = []
|
|
91
|
+
taken = set()
|
|
92
|
+
for cur in (z_dim, y_dim, x_dim):
|
|
93
|
+
d = _resolve_dim(dim_names, cur, n)
|
|
94
|
+
if d is None or d in taken:
|
|
95
|
+
resolved.append(None) # unset, or a duplicate: re-derive
|
|
96
|
+
else:
|
|
97
|
+
taken.add(d)
|
|
98
|
+
resolved.append(d)
|
|
99
|
+
|
|
100
|
+
def fill(default):
|
|
101
|
+
# The default dim, else the nearest free one scanning down then up.
|
|
102
|
+
# (The old walk stopped at 0 and could hand back a taken 0.)
|
|
103
|
+
if default not in taken:
|
|
104
|
+
return default
|
|
105
|
+
for d in range(default - 1, -1, -1):
|
|
106
|
+
if d not in taken:
|
|
107
|
+
return d
|
|
108
|
+
for d in range(default + 1, n):
|
|
109
|
+
if d not in taken:
|
|
110
|
+
return d
|
|
111
|
+
return default # n < 3: nothing free left
|
|
112
|
+
|
|
113
|
+
for i, default in enumerate((max(0, n - 3), max(0, n - 2), max(0, n - 1))):
|
|
114
|
+
if resolved[i] is None:
|
|
115
|
+
resolved[i] = fill(default)
|
|
116
|
+
taken.add(resolved[i])
|
|
117
|
+
return tuple(resolved)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def to_display_dtype(t):
|
|
121
|
+
"""Coerce ANY torch tensor into something the raymarcher can sample:
|
|
122
|
+
a dense, real, float16/float32 tensor. float16/32 pass through untouched
|
|
123
|
+
(they upload as R16F/R32F with no copy); every other dtype maps to
|
|
124
|
+
float32 by meaning, not by bit pattern — complex → magnitude, bool →
|
|
125
|
+
0/1, ints/uints → their values, float64/bfloat16 → narrowed (bfloat16
|
|
126
|
+
must NOT go to float16: its exponent range overflows). Quantized tensors
|
|
127
|
+
dequantize, sparse layouts densify. Raises ValueError with a readable
|
|
128
|
+
reason for anything that can't become a real float volume."""
|
|
129
|
+
import torch
|
|
130
|
+
if t.is_quantized:
|
|
131
|
+
t = t.dequantize()
|
|
132
|
+
if t.layout != torch.strided:
|
|
133
|
+
try:
|
|
134
|
+
t = t.to_dense()
|
|
135
|
+
except Exception as e:
|
|
136
|
+
raise ValueError(f"cannot densify {t.layout} tensor: {e}") from e
|
|
137
|
+
if t.dtype in (torch.float16, torch.float32):
|
|
138
|
+
return t
|
|
139
|
+
if t.is_complex():
|
|
140
|
+
return t.abs().float()
|
|
141
|
+
if t.dtype == torch.bool or not t.is_floating_point():
|
|
142
|
+
return t.to(torch.float32) # bool, int8..int64, uint8..
|
|
143
|
+
try:
|
|
144
|
+
return t.float() # float64, bfloat16, float8_*...
|
|
145
|
+
except Exception as e:
|
|
146
|
+
raise ValueError(f"unsupported tensor dtype {t.dtype}: {e}") from e
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _display_view_dtype(t):
|
|
150
|
+
"""Keep supported CUDA dtypes/layouts intact; reject implicit conversions.
|
|
151
|
+
CPU reference paths can still materialize sparse/complex/quantized inputs.
|
|
152
|
+
bf16/ints/bool/f64 decode in the CUDA kernel without an f32 copy."""
|
|
153
|
+
import torch
|
|
154
|
+
if t.is_cuda and (t.is_quantized or t.layout != torch.strided or t.is_complex()):
|
|
155
|
+
raise ValueError("Direct CUDA rendering requires a dense real-valued tensor; "
|
|
156
|
+
"convert explicitly in user code to visualize this value.")
|
|
157
|
+
if t.is_quantized:
|
|
158
|
+
t = t.dequantize()
|
|
159
|
+
if t.layout != torch.strided:
|
|
160
|
+
try:
|
|
161
|
+
t = t.to_dense()
|
|
162
|
+
except Exception as e:
|
|
163
|
+
raise ValueError(f"cannot densify {t.layout} tensor: {e}") from e
|
|
164
|
+
if t.is_complex():
|
|
165
|
+
t = t.abs().float()
|
|
166
|
+
return t
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _slice_core(t, dim_names, x_dim, y_dim, z_dim, slices, mean_dims, sort_dim,
|
|
170
|
+
nf_on, nf_chop, nf_along, materialize):
|
|
171
|
+
"""Shared slice logic: tensor → (z, y, x) volume as a VIEW (no
|
|
172
|
+
contiguous() — `materialize` decides the dtype pre-pass), plus the
|
|
173
|
+
mapping, source shape and the resolved neural-flow axes (positions in
|
|
174
|
+
the (z, y, x) volume, None = off)."""
|
|
175
|
+
import torch
|
|
176
|
+
t = (to_display_dtype if materialize else _display_view_dtype)(t.detach())
|
|
177
|
+
if t.numel() == 0:
|
|
178
|
+
raise ValueError(f"empty tensor (shape {tuple(t.shape)}) — nothing to display")
|
|
179
|
+
while t.dim() < 3:
|
|
180
|
+
t = t.unsqueeze(0)
|
|
181
|
+
n = t.dim()
|
|
182
|
+
shape = tuple(int(s) for s in t.shape)
|
|
183
|
+
zd, yd, xd = _resolve_axes(shape, dim_names, x_dim, y_dim, z_dim)
|
|
184
|
+
if 0 <= int(sort_dim) < n:
|
|
185
|
+
t = torch.sort(t, dim=int(sort_dim), descending=True).values
|
|
186
|
+
picked = (zd, yd, xd)
|
|
187
|
+
mean_set = {int(d) for d in (mean_dims or ()) if 0 <= int(d) < n}
|
|
188
|
+
for d in mean_set:
|
|
189
|
+
# f32 accumulate + result regardless of the input dtype (int inputs
|
|
190
|
+
# need it; bf16 inputs would otherwise round the mean - the view
|
|
191
|
+
# path must match the materialized one bit for bit).
|
|
192
|
+
m = t.mean(dim=d, keepdim=True, dtype=torch.float32)
|
|
193
|
+
# A DISPLAYED dim keeps its extent with the mean BROADCAST along it
|
|
194
|
+
# (the same value repeats across the plot - visual convenience);
|
|
195
|
+
# an unmapped dim stays collapsed and pins at 0 below.
|
|
196
|
+
t = m.expand(t.shape) if d in picked else m
|
|
197
|
+
def _pin(d):
|
|
198
|
+
# A pinned index from `slices` can be anything the panel/source lets;
|
|
199
|
+
# clamp into range instead of letting torch raise (or silently wrap on
|
|
200
|
+
# a negative).
|
|
201
|
+
try:
|
|
202
|
+
v = int(slices[d]) if d < len(slices) else 0
|
|
203
|
+
except (TypeError, ValueError):
|
|
204
|
+
v = 0
|
|
205
|
+
return max(0, min(v, shape[d] - 1))
|
|
206
|
+
|
|
207
|
+
index = tuple(
|
|
208
|
+
slice(None) if d in picked
|
|
209
|
+
else (0 if d in mean_set else _pin(d))
|
|
210
|
+
for d in range(n))
|
|
211
|
+
sub = t[index] # picked 3 dims keep original order
|
|
212
|
+
remaining = sorted(picked)
|
|
213
|
+
vol = sub.permute(remaining.index(zd), remaining.index(yd),
|
|
214
|
+
remaining.index(xd))
|
|
215
|
+
chop = along = None
|
|
216
|
+
if nf_on:
|
|
217
|
+
# Flow is pinned to TENSOR DIMS (remapping x/y/z never changes WHICH
|
|
218
|
+
# data gets chopped); unset dims default to chop=x, along=z. A chop
|
|
219
|
+
# or along dim that isn't mapped makes it a no-op.
|
|
220
|
+
chop_d = _resolve_dim(dim_names, nf_chop, n)
|
|
221
|
+
along_d = _resolve_dim(dim_names, nf_along, n)
|
|
222
|
+
dim_to_axis = {xd: "x", yd: "y", zd: "z"}
|
|
223
|
+
chop = dim_to_axis.get(xd if chop_d is None else chop_d)
|
|
224
|
+
along = dim_to_axis.get(zd if along_d is None else along_d)
|
|
225
|
+
if not (chop and along and chop != along):
|
|
226
|
+
chop = along = None
|
|
227
|
+
return vol, (zd, yd, xd), shape, chop, along
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def slice_volume(t, dim_names=(), x_dim=None, y_dim=None, z_dim=None,
|
|
231
|
+
slices=(), mean_dims=(), sort_dim=-1, normalize=False,
|
|
232
|
+
nf_on=False, nf_chop=None, nf_along=None, nf_chunk=128,
|
|
233
|
+
nf_pad=False):
|
|
234
|
+
"""tensor → (depth, height, width) display volume, PURE: every choice
|
|
235
|
+
arrives as an argument (the draw_voxels params), nothing is stored.
|
|
236
|
+
Unmapped dims pin to their `slices` index (missing entries → 0) or
|
|
237
|
+
average when listed in mean_dims (keepdim, then pinned at 0); a
|
|
238
|
+
DISPLAYED dim in mean_dims keeps its extent with the mean broadcast
|
|
239
|
+
along it (the value repeats across the plot); sort
|
|
240
|
+
orders fibers along a dim; normalize min-max stretches the DISPLAYED
|
|
241
|
+
volume (signed data scales by max-magnitude so zero stays anchored).
|
|
242
|
+
Stays on t's device. Returns (vol3, (z_dim, y_dim, x_dim), shape)."""
|
|
243
|
+
import torch
|
|
244
|
+
vol, mapping, shape, chop, along = _slice_core(
|
|
245
|
+
t, dim_names, x_dim, y_dim, z_dim, slices, mean_dims, sort_dim,
|
|
246
|
+
nf_on, nf_chop, nf_along, materialize=True)
|
|
247
|
+
vol = vol.contiguous()
|
|
248
|
+
if chop is not None:
|
|
249
|
+
vol = neural_flow_volume(vol, chop, along, int(nf_chunk), pad=nf_pad)
|
|
250
|
+
if normalize:
|
|
251
|
+
lo, hi = vol.min(), vol.max()
|
|
252
|
+
if lo < 0:
|
|
253
|
+
vol = vol / (torch.maximum(hi.abs(), lo.abs()) + 1e-12)
|
|
254
|
+
else:
|
|
255
|
+
vol = (vol - lo) / (hi - lo + 1e-12)
|
|
256
|
+
return vol, mapping, shape
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
class CudaVolumeView:
|
|
260
|
+
"""The cuda_march stand-in for the volume GLTexture: NO GL object — the
|
|
261
|
+
kernel samples `view` (a strided (z, y, x) torch view of the source, on
|
|
262
|
+
whatever GPU it lives) in place. Carries the same metadata draw_voxels
|
|
263
|
+
reads off a volume texture (`shape` = DISPLAYED extents after neural
|
|
264
|
+
flow, source_shape, mapping, clamp_note) plus the kernel's sampling
|
|
265
|
+
facts: `nf` = (chop_axis, along_axis, chunk) with axes 0=z 1=y 2=x
|
|
266
|
+
(chop -1 = off) and `norm` = (lo, hi, mode)."""
|
|
267
|
+
|
|
268
|
+
__slots__ = ("view", "shape", "nf", "norm", "source_shape", "source_ndim",
|
|
269
|
+
"mapping", "clamp_note", "_vol_key", "dim_names")
|
|
270
|
+
|
|
271
|
+
def __init__(self, view, shape, nf, norm, mapping, source_shape):
|
|
272
|
+
self.view, self.shape, self.nf, self.norm = view, tuple(shape), nf, norm
|
|
273
|
+
self.mapping, self.source_shape = mapping, tuple(source_shape)
|
|
274
|
+
self.source_ndim = len(source_shape)
|
|
275
|
+
self.clamp_note = None
|
|
276
|
+
self._vol_key = None
|
|
277
|
+
self.dim_names = ()
|
|
278
|
+
|
|
279
|
+
def __repr__(self):
|
|
280
|
+
return f"CudaVolumeView({self.shape} of {tuple(self.view.shape)} on {self.view.device})"
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def slice_volume_view(t, dim_names=(), x_dim=None, y_dim=None, z_dim=None,
|
|
284
|
+
slices=(), mean_dims=(), sort_dim=-1, normalize=False,
|
|
285
|
+
nf_on=False, nf_chop=None, nf_along=None, nf_chunk=128,
|
|
286
|
+
nf_pad=False):
|
|
287
|
+
"""slice_volume for the cuda_march path: the same choices, but the result
|
|
288
|
+
is a CudaVolumeView over a strided VIEW of the source — no contiguous(),
|
|
289
|
+
no dtype copy, neural flow as in-kernel index math, normalize as a
|
|
290
|
+
(lo, hi) pair the kernel applies per sample. Only sort/mean (genuine
|
|
291
|
+
transforms) and densify/complex materialize anything; those run once
|
|
292
|
+
per vol_key like everything else behind draw_voxels' cache gate."""
|
|
293
|
+
vol, mapping, shape, chop, along = _slice_core(
|
|
294
|
+
t, dim_names, x_dim, y_dim, z_dim, slices, mean_dims, sort_dim,
|
|
295
|
+
nf_on, nf_chop, nf_along, materialize=False)
|
|
296
|
+
display_shape, nf = tuple(int(s) for s in vol.shape), (-1, -1, 0)
|
|
297
|
+
if chop is not None:
|
|
298
|
+
display_shape, nf = nf_display_shape(
|
|
299
|
+
vol.shape, _AXIS_POS[chop], _AXIS_POS[along], int(nf_chunk), pad=nf_pad)
|
|
300
|
+
norm = (0.0, 1.0, 0)
|
|
301
|
+
if normalize:
|
|
302
|
+
lo, hi = float(vol.min()), float(vol.max())
|
|
303
|
+
norm = (lo, max(abs(hi), abs(lo)), 2) if lo < 0 else (lo, hi, 1)
|
|
304
|
+
return CudaVolumeView(vol, display_shape, nf, norm, mapping, shape)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def _volume_scale(shape):
|
|
308
|
+
"""Box extents per axis for a (depth, height, width) = (z, y, x) volume,
|
|
309
|
+
proportional to voxel counts (longest axis = 1), so every voxel renders
|
|
310
|
+
as a CUBE and a (4, 32, 48) tensor reads as a flat slab. Returned as the
|
|
311
|
+
shader's (x, y, z) order. No visibility floor: an earlier max(0.02, …)
|
|
312
|
+
per axis inflated the short side of anything past 50:1 (a (2048, 16)
|
|
313
|
+
time tensor drew its 16-voxel side 2.5× too wide). Thin slabs don't need
|
|
314
|
+
it — opacity accumulates in volume-NORMALIZED segment lengths, so a
|
|
315
|
+
1-voxel dim still reads at full density. The epsilon only guards the
|
|
316
|
+
`/ volume_scale` divisions (labels/silhouette use the same scale)."""
|
|
317
|
+
t_depth, t_height, t_width = (max(1, int(s)) for s in shape)
|
|
318
|
+
longest = float(max(t_depth, t_height, t_width))
|
|
319
|
+
return (max(1e-5, t_width / longest),
|
|
320
|
+
max(1e-5, t_height / longest),
|
|
321
|
+
max(1e-5, t_depth / longest))
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def neural_flow_volume(vol, chop_axis, along_axis, chunk, pad=False):
|
|
325
|
+
"""The old viewer's neural flow on the DISPLAY volume: chop one axis into
|
|
326
|
+
`chunk`-wide blocks and concatenate them group-major along another —
|
|
327
|
+
identical layout to the original get_neural_flow's j*orig+i ordering,
|
|
328
|
+
which is exactly cat(split). No-op when the axes coincide, or when the
|
|
329
|
+
chop doesn't divide evenly — unless `pad`, which zero-fills the chop
|
|
330
|
+
axis up to the next multiple first (the auto-wrap path: any chunk must
|
|
331
|
+
work, a ragged last block is fine)."""
|
|
332
|
+
chop, along = _AXIS_POS[chop_axis], _AXIS_POS[along_axis]
|
|
333
|
+
size = int(vol.shape[chop])
|
|
334
|
+
if chop == along or chunk <= 0 or size <= chunk:
|
|
335
|
+
return vol
|
|
336
|
+
import torch
|
|
337
|
+
if size % chunk != 0:
|
|
338
|
+
if not pad:
|
|
339
|
+
return vol
|
|
340
|
+
extra = chunk - size % chunk
|
|
341
|
+
# F.pad's (before, after) pairs run from the last dim backwards.
|
|
342
|
+
spec = [0, 0] * (vol.dim() - 1 - chop) + [0, extra]
|
|
343
|
+
vol = torch.nn.functional.pad(vol, spec)
|
|
344
|
+
return torch.cat(vol.split(chunk, dim=chop), dim=along).contiguous()
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def auto_neural_flow(shape, dim_names, x_dim, y_dim, z_dim, max_extent):
|
|
348
|
+
"""The auto-wrap decision for a DISPLAYED axis longer than `max_extent`
|
|
349
|
+
(the GL limit, or the user's readability cap): returns
|
|
350
|
+
(chop_dim, along_dim, chunk) tensor-dim indices for neural flow, or None
|
|
351
|
+
when every displayed extent fits. Chops the LONGEST over-limit axis into
|
|
352
|
+
~sqrt-sized chunks — the smallest divisor >= sqrt(size) when one exists
|
|
353
|
+
below the limit, else ceil(sqrt) with padding — and lays the blocks along
|
|
354
|
+
the SHORTEST other displayed axis (a (1, 32000) row becomes a ~180x180
|
|
355
|
+
slab). One pass only; anything still over-limit afterwards clamps."""
|
|
356
|
+
shape = tuple(int(s) for s in shape)
|
|
357
|
+
shape = (1,) * (3 - len(shape)) + shape if len(shape) < 3 else shape
|
|
358
|
+
if max_extent <= 0:
|
|
359
|
+
return None
|
|
360
|
+
zd, yd, xd = _resolve_axes(shape, dim_names, x_dim, y_dim, z_dim)
|
|
361
|
+
shown = (zd, yd, xd)
|
|
362
|
+
over = [d for d in shown if shape[d] > max_extent]
|
|
363
|
+
if not over:
|
|
364
|
+
return None
|
|
365
|
+
chop = max(over, key=lambda d: shape[d])
|
|
366
|
+
along = min((d for d in shown if d != chop), key=lambda d: shape[d])
|
|
367
|
+
size = shape[chop]
|
|
368
|
+
root = int(math.ceil(math.sqrt(size)))
|
|
369
|
+
chunk = next((c for c in range(root, min(size, max_extent) + 1) if size % c == 0),
|
|
370
|
+
root)
|
|
371
|
+
return chop, along, chunk
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def _is_tensorish(v):
|
|
375
|
+
"""A torch tensor / ndarray, or a container whose top level holds one."""
|
|
376
|
+
if isinstance(v, np.ndarray):
|
|
377
|
+
return True
|
|
378
|
+
if type(v).__module__.startswith("torch") and hasattr(v, "data_ptr"):
|
|
379
|
+
return True
|
|
380
|
+
if isinstance(v, (list, tuple)):
|
|
381
|
+
return any(_is_tensorish(x) for x in v)
|
|
382
|
+
if isinstance(v, dict):
|
|
383
|
+
return any(_is_tensorish(x) for x in v.values())
|
|
384
|
+
return False
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def nf_display_shape(src_shape, chop_axis, along_axis, chunk, pad=True):
|
|
388
|
+
"""Displayed (z, y, x) extents after neural flow of a (z, y, x) source
|
|
389
|
+
view — the cat(split) layout: chop axis → chunk, along axis →
|
|
390
|
+
along * ceil(size/chunk) (the ragged last block zero-padded in the
|
|
391
|
+
sampler). Axes are 0=z 1=y 2=x; chop -1 = off. Mirrors
|
|
392
|
+
neural_flow_volume: a chunk that doesn't divide the axis is a no-op
|
|
393
|
+
unless `pad`."""
|
|
394
|
+
shape = list(int(s) for s in src_shape)
|
|
395
|
+
if chop_axis < 0 or chop_axis == along_axis or chunk <= 0 or shape[chop_axis] <= chunk:
|
|
396
|
+
return tuple(shape), (-1, -1, 0)
|
|
397
|
+
if shape[chop_axis] % chunk != 0 and not pad:
|
|
398
|
+
return tuple(shape), (-1, -1, 0)
|
|
399
|
+
blocks = -(-shape[chop_axis] // chunk)
|
|
400
|
+
shape[along_axis] *= blocks
|
|
401
|
+
shape[chop_axis] = chunk
|
|
402
|
+
return tuple(shape), (chop_axis, along_axis, chunk)
|