@woosh/meep-engine 3.15.0 → 3.16.0
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.
- package/editor/Editor.js +7 -7
- package/editor/actions/concrete/TransformModifyAction.d.ts +4 -4
- package/editor/actions/concrete/TransformModifyAction.d.ts.map +1 -1
- package/editor/actions/concrete/TransformModifyAction.js +4 -4
- package/editor/camera/camera_framing.js +7 -7
- package/editor/ecs/component/editors/ecs/Transform64Editor.d.ts +20 -0
- package/editor/ecs/component/editors/ecs/Transform64Editor.d.ts.map +1 -0
- package/editor/ecs/component/editors/ecs/Transform64Editor.js +70 -0
- package/editor/ecs/component/prototypeObjectEditor.js +2 -2
- package/editor/ecs/component/registerEngineComponentEditors.js +3 -3
- package/editor/particles/effect/ParticleEffectDocument.d.ts +396 -0
- package/editor/particles/effect/ParticleEffectDocument.d.ts.map +1 -0
- package/editor/particles/effect/ParticleEffectDocument.js +989 -0
- package/editor/particles/effect/ParticleReferenceSimulation.d.ts +113 -0
- package/editor/particles/effect/ParticleReferenceSimulation.d.ts.map +1 -0
- package/editor/particles/effect/ParticleReferenceSimulation.js +377 -0
- package/editor/particles/effect/actions/ParticleGroupDefineAction.d.ts +35 -0
- package/editor/particles/effect/actions/ParticleGroupDefineAction.d.ts.map +1 -0
- package/editor/particles/effect/actions/ParticleGroupDefineAction.js +45 -0
- package/editor/particles/effect/make_starter_particle_effect.d.ts +23 -0
- package/editor/particles/effect/make_starter_particle_effect.d.ts.map +1 -0
- package/editor/particles/effect/make_starter_particle_effect.js +177 -0
- package/editor/persistence/buildNewSceneDataset.d.ts.map +1 -1
- package/editor/persistence/buildNewSceneDataset.js +10 -8
- package/editor/process/symbolic/AudioEmitterSymbolicDisplay.js +67 -67
- package/editor/process/symbolic/CameraSymbolicDisplay.js +146 -146
- package/editor/process/symbolic/GridPositionSymbolicDisplay.js +6 -6
- package/editor/process/symbolic/LightSymbolicDisplay.js +172 -172
- package/editor/process/symbolic/ParticleEmitterSymbolicDisplay.d.ts.map +1 -1
- package/editor/process/symbolic/ParticleEmitterSymbolicDisplay.js +6 -3
- package/editor/process/symbolic/PositionedMarkerSymbolicDisplay.js +6 -6
- package/editor/selection/editor_pick.js +130 -130
- package/editor/templates/entity_templates.js +7 -7
- package/editor/tools/TransformToolV2.d.ts +3 -3
- package/editor/tools/TransformToolV2.d.ts.map +1 -1
- package/editor/tools/TransformToolV2.js +58 -25
- package/editor/tools/paint/TerrainHeightPaintTool.d.ts.map +1 -1
- package/editor/tools/paint/TerrainHeightPaintTool.js +11 -9
- package/editor/tools/v2/GizmoNode.js +2 -2
- package/editor/tools/v2/TransformControls.d.ts +3 -3
- package/editor/tools/v2/TransformControls.d.ts.map +1 -1
- package/editor/tools/v2/TransformControls.js +1003 -971
- package/editor/tools/v2/handle/gizmo_drag_plane.d.ts.map +1 -1
- package/editor/tools/v2/handle/gizmo_drag_plane.js +6 -1
- package/editor/view/ecs/components/common/AutoCanvasView.js +17 -0
- package/editor/view/node-graph/NodeGraphEditorView.d.ts +143 -2
- package/editor/view/node-graph/NodeGraphEditorView.d.ts.map +1 -1
- package/editor/view/node-graph/NodeGraphEditorView.js +358 -6
- package/editor/view/node-graph/NodeGraphMinimapView.d.ts +120 -0
- package/editor/view/node-graph/NodeGraphMinimapView.d.ts.map +1 -0
- package/editor/view/node-graph/NodeGraphMinimapView.js +859 -0
- package/editor/view/node-graph/NodeGraphPaletteView.d.ts +112 -0
- package/editor/view/node-graph/NodeGraphPaletteView.d.ts.map +1 -0
- package/editor/view/node-graph/NodeGraphPaletteView.js +352 -0
- package/editor/view/node-graph/NodeGraphSearchMenuView.d.ts +76 -0
- package/editor/view/node-graph/NodeGraphSearchMenuView.d.ts.map +1 -0
- package/editor/view/node-graph/NodeGraphSearchMenuView.js +247 -0
- package/editor/view/node-graph/NodeGraphView.d.ts +147 -5
- package/editor/view/node-graph/NodeGraphView.d.ts.map +1 -1
- package/editor/view/node-graph/NodeGraphView.js +809 -76
- package/editor/view/node-graph/NodeIconView.d.ts +33 -0
- package/editor/view/node-graph/NodeIconView.d.ts.map +1 -0
- package/editor/view/node-graph/NodeIconView.js +54 -0
- package/editor/view/node-graph/NodeView.d.ts +71 -1
- package/editor/view/node-graph/NodeView.d.ts.map +1 -1
- package/editor/view/node-graph/NodeView.js +318 -17
- package/editor/view/node-graph/PortView.d.ts +41 -1
- package/editor/view/node-graph/PortView.d.ts.map +1 -1
- package/editor/view/node-graph/PortView.js +121 -69
- package/editor/view/node-graph/actions/ConnectionDeleteAction.d.ts +16 -3
- package/editor/view/node-graph/actions/ConnectionDeleteAction.d.ts.map +1 -1
- package/editor/view/node-graph/actions/ConnectionDeleteAction.js +29 -3
- package/editor/view/node-graph/actions/NodeCreateAction.d.ts +65 -0
- package/editor/view/node-graph/actions/NodeCreateAction.d.ts.map +1 -0
- package/editor/view/node-graph/actions/NodeCreateAction.js +108 -0
- package/editor/view/node-graph/actions/NodeDeleteAction.d.ts +6 -0
- package/editor/view/node-graph/actions/NodeDeleteAction.d.ts.map +1 -1
- package/editor/view/node-graph/actions/NodeDeleteAction.js +24 -2
- package/editor/view/node-graph/connection_wire_geometry.d.ts +34 -0
- package/editor/view/node-graph/connection_wire_geometry.d.ts.map +1 -1
- package/editor/view/node-graph/connection_wire_geometry.js +79 -3
- package/editor/view/node-graph/node-graph.css +678 -0
- package/editor/view/particles/effect/ParticleCodeEditorView.d.ts +37 -0
- package/editor/view/particles/effect/ParticleCodeEditorView.d.ts.map +1 -0
- package/editor/view/particles/effect/ParticleCodeEditorView.js +479 -0
- package/editor/view/particles/effect/ParticleEffectEditorView.d.ts +61 -0
- package/editor/view/particles/effect/ParticleEffectEditorView.d.ts.map +1 -0
- package/editor/view/particles/effect/ParticleEffectEditorView.js +223 -0
- package/editor/view/particles/effect/ParticleEmitterInspectorView.d.ts +25 -0
- package/editor/view/particles/effect/ParticleEmitterInspectorView.d.ts.map +1 -0
- package/editor/view/particles/effect/ParticleEmitterInspectorView.js +606 -0
- package/editor/view/particles/effect/ParticleGraphEditorView.d.ts +156 -0
- package/editor/view/particles/effect/ParticleGraphEditorView.d.ts.map +1 -0
- package/editor/view/particles/effect/ParticleGraphEditorView.js +1636 -0
- package/editor/view/particles/effect/ParticleNodeParametersView.d.ts +22 -0
- package/editor/view/particles/effect/ParticleNodeParametersView.d.ts.map +1 -0
- package/editor/view/particles/effect/ParticleNodeParametersView.js +520 -0
- package/editor/view/particles/effect/ParticlePreviewView.d.ts +52 -0
- package/editor/view/particles/effect/ParticlePreviewView.d.ts.map +1 -0
- package/editor/view/particles/effect/ParticlePreviewView.js +533 -0
- package/editor/view/particles/effect/particle-editor.css +556 -0
- package/editor/view/prepareMeshLibrary.d.ts.map +1 -1
- package/editor/view/prepareMeshLibrary.js +8 -4
- package/package.json +1 -1
- package/samples/engine/first-scene/main.js +152 -152
- package/samples/generation/generators/mir_generator_place_bases.js +3 -3
- package/src/core/assert.d.ts +28 -24
- package/src/core/assert.d.ts.map +1 -1
- package/src/core/assert.js +34 -26
- package/src/core/geom/3d/shape/PosedShape3D.d.ts +17 -0
- package/src/core/geom/3d/shape/PosedShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/PosedShape3D.js +34 -2
- package/src/core/graph/format/graphviz/graphviz_escape_html_label.d.ts +17 -0
- package/src/core/graph/format/graphviz/graphviz_escape_html_label.d.ts.map +1 -0
- package/src/core/graph/format/graphviz/graphviz_escape_html_label.js +25 -0
- package/src/core/graph/format/graphviz/graphviz_escape_quoted.d.ts +16 -0
- package/src/core/graph/format/graphviz/graphviz_escape_quoted.d.ts.map +1 -0
- package/src/core/graph/format/graphviz/graphviz_escape_quoted.js +24 -0
- package/src/core/model/node-graph/NodeGraph.d.ts +12 -0
- package/src/core/model/node-graph/NodeGraph.d.ts.map +1 -1
- package/src/core/model/node-graph/NodeGraph.js +21 -6
- package/src/core/model/node-graph/node/NodeDescription.d.ts +36 -0
- package/src/core/model/node-graph/node/NodeDescription.d.ts.map +1 -1
- package/src/core/model/node-graph/node/NodeDescription.js +41 -0
- package/src/core/model/node-graph/visual/ConnectionVisualData.d.ts +119 -0
- package/src/core/model/node-graph/visual/ConnectionVisualData.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/ConnectionVisualData.js +317 -0
- package/src/core/model/node-graph/visual/NodeDescriptionVisualRegistry.d.ts +4 -0
- package/src/core/model/node-graph/visual/NodeDescriptionVisualRegistry.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/NodeDescriptionVisualRegistry.js +125 -22
- package/src/core/model/node-graph/visual/NodeGraphVisualData.d.ts +91 -2
- package/src/core/model/node-graph/visual/NodeGraphVisualData.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/NodeGraphVisualData.js +164 -113
- package/src/core/model/node-graph/visual/layout/LayoutBoxGraph.d.ts +162 -0
- package/src/core/model/node-graph/visual/layout/LayoutBoxGraph.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/LayoutBoxGraph.js +288 -0
- package/src/core/model/node-graph/visual/layout/LayoutProblem.d.ts +109 -0
- package/src/core/model/node-graph/visual/layout/LayoutProblem.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/LayoutProblem.js +141 -0
- package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts +149 -0
- package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.js +175 -0
- package/src/core/model/node-graph/visual/layout/RouteGrid.d.ts +137 -0
- package/src/core/model/node-graph/visual/layout/RouteGrid.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/RouteGrid.js +242 -0
- package/src/core/model/node-graph/visual/layout/RouteSearch.d.ts +114 -0
- package/src/core/model/node-graph/visual/layout/RouteSearch.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/RouteSearch.js +300 -0
- package/src/core/model/node-graph/visual/layout/RouteSet.d.ts +86 -0
- package/src/core/model/node-graph/visual/layout/RouteSet.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/RouteSet.js +168 -0
- package/src/core/model/node-graph/visual/layout/layout_assign_coordinates.d.ts +48 -0
- package/src/core/model/node-graph/visual/layout/layout_assign_coordinates.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/layout_assign_coordinates.js +555 -0
- package/src/core/model/node-graph/visual/layout/layout_assign_layers.d.ts +34 -0
- package/src/core/model/node-graph/visual/layout/layout_assign_layers.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/layout_assign_layers.js +202 -0
- package/src/core/model/node-graph/visual/layout/layout_break_cycles.d.ts +34 -0
- package/src/core/model/node-graph/visual/layout/layout_break_cycles.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/layout_break_cycles.js +254 -0
- package/src/core/model/node-graph/visual/layout/layout_build_problem.d.ts +29 -0
- package/src/core/model/node-graph/visual/layout/layout_build_problem.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/layout_build_problem.js +184 -0
- package/src/core/model/node-graph/visual/layout/layout_insert_lanes.d.ts +29 -0
- package/src/core/model/node-graph/visual/layout/layout_insert_lanes.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/layout_insert_lanes.js +95 -0
- package/src/core/model/node-graph/visual/layout/layout_node_graph.d.ts +56 -0
- package/src/core/model/node-graph/visual/layout/layout_node_graph.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/layout_node_graph.js +131 -0
- package/src/core/model/node-graph/visual/layout/layout_order_layers.d.ts +36 -0
- package/src/core/model/node-graph/visual/layout/layout_order_layers.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/layout_order_layers.js +415 -0
- package/src/core/model/node-graph/visual/layout/layout_orient_links.d.ts +21 -0
- package/src/core/model/node-graph/visual/layout/layout_orient_links.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/layout_orient_links.js +49 -0
- package/src/core/model/node-graph/visual/layout/route_connections.d.ts +41 -0
- package/src/core/model/node-graph/visual/layout/route_connections.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/route_connections.js +309 -0
- package/src/core/model/node-graph/visual/layout/route_grid_build.d.ts +33 -0
- package/src/core/model/node-graph/visual/layout/route_grid_build.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/route_grid_build.js +237 -0
- package/src/core/model/node-graph/visual/layout/route_nudge.d.ts +25 -0
- package/src/core/model/node-graph/visual/layout/route_nudge.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/route_nudge.js +220 -0
- package/src/core/model/node-graph/visual/layout/route_to_spline.d.ts +29 -0
- package/src/core/model/node-graph/visual/layout/route_to_spline.d.ts.map +1 -0
- package/src/core/model/node-graph/visual/layout/route_to_spline.js +120 -0
- package/src/engine/EngineHarness.d.ts.map +1 -1
- package/src/engine/EngineHarness.js +593 -586
- package/src/engine/EntityCreator.js +116 -116
- package/src/engine/achievements/AchievementManager.js +437 -437
- package/src/engine/animation/AnimatedActions.js +111 -111
- package/src/engine/animation/AnimationUtils.d.ts.map +1 -1
- package/src/engine/animation/AnimationUtils.js +246 -234
- package/src/engine/animation/Animations.d.ts +1 -0
- package/src/engine/animation/Animations.d.ts.map +1 -1
- package/src/engine/animation/Animations.js +13 -5
- package/src/engine/animation/clip/bind_property_writer.d.ts.map +1 -1
- package/src/engine/animation/clip/bind_property_writer.js +12 -5
- package/src/engine/animation/curve/binding/BoundTransform64Writer.d.ts +29 -0
- package/src/engine/animation/curve/binding/BoundTransform64Writer.d.ts.map +1 -0
- package/src/engine/animation/curve/binding/BoundTransform64Writer.js +80 -0
- package/src/engine/animation/removeEntityWithMeshParticlesEffect.js +4 -4
- package/src/engine/asset/loaders/gltf_test_fixtures.d.ts.map +1 -1
- package/src/engine/asset/loaders/gltf_test_fixtures.js +5 -4
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts +12 -12
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +77 -44
- package/src/engine/control/first-person/abilities/LedgeGrab.js +1 -1
- package/src/engine/control/first-person/abilities/Mantle.js +1 -1
- package/src/engine/control/first-person/abilities/Slide.js +1 -1
- package/src/engine/control/first-person/collision/KinematicMover.d.ts +5 -9
- package/src/engine/control/first-person/collision/KinematicMover.d.ts.map +1 -1
- package/src/engine/control/first-person/collision/KinematicMover.js +59 -29
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts +2 -2
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts.map +1 -1
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.js +7 -7
- package/src/engine/ecs/EventType.d.ts +1 -0
- package/src/engine/ecs/EventType.js +35 -10
- package/src/engine/ecs/dynamic_actions/actions/definition/SendRequestActionDescription.d.ts.map +1 -1
- package/src/engine/ecs/dynamic_actions/actions/definition/SendRequestActionDescription.js +12 -4
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts +4 -4
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts.map +1 -1
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.js +10 -11
- package/src/engine/ecs/grid/pick.js +1 -1
- package/src/engine/ecs/gui/hud/HeadsUpDisplaySystem.d.ts +1 -1
- package/src/engine/ecs/gui/hud/HeadsUpDisplaySystem.js +13 -13
- package/src/engine/ecs/gui/hud/testHudEdgeStick.js +3 -3
- package/src/engine/ecs/ik/InverseKinematicsSystem.d.ts +10 -10
- package/src/engine/ecs/ik/InverseKinematicsSystem.d.ts.map +1 -1
- package/src/engine/ecs/ik/InverseKinematicsSystem.js +8 -8
- package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.d.ts.map +1 -1
- package/src/engine/ecs/ik/TwoBoneInverseKinematicsSolver.js +120 -97
- package/src/engine/ecs/ik/ik_bone_chain.d.ts +16 -5
- package/src/engine/ecs/ik/ik_bone_chain.d.ts.map +1 -1
- package/src/engine/ecs/ik/ik_bone_chain.js +16 -4
- package/src/engine/ecs/ik/ik_probe_terrain_contact.d.ts +7 -3
- package/src/engine/ecs/ik/ik_probe_terrain_contact.d.ts.map +1 -1
- package/src/engine/ecs/ik/ik_probe_terrain_contact.js +20 -16
- package/src/engine/ecs/ik/ik_write_local_rotation.d.ts +18 -0
- package/src/engine/ecs/ik/ik_write_local_rotation.d.ts.map +1 -0
- package/src/engine/ecs/ik/ik_write_local_rotation.js +30 -0
- package/src/engine/ecs/parent/EntityNode.d.ts +24 -6
- package/src/engine/ecs/parent/EntityNode.d.ts.map +1 -1
- package/src/engine/ecs/parent/EntityNode.js +54 -42
- package/src/engine/ecs/parent/EntityNodeFlags.d.ts +0 -1
- package/src/engine/ecs/parent/EntityNodeFlags.js +7 -8
- package/src/engine/ecs/speaker/VoiceSystem.d.ts +2 -2
- package/src/engine/ecs/speaker/VoiceSystem.d.ts.map +1 -1
- package/src/engine/ecs/speaker/VoiceSystem.js +4 -4
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +2 -0
- package/src/engine/ecs/terrain/ecs/Terrain.d.ts +5 -2
- package/src/engine/ecs/terrain/ecs/Terrain.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/Terrain.js +7 -4
- package/src/engine/ecs/terrain/ecs/TerrainSystem.js +3 -3
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts +7 -7
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +84 -36
- package/src/engine/ecs/transform/TRANSFORM64_MIGRATION_PLAN_2026_09_04.md +513 -0
- package/src/engine/ecs/transform/Transform64.d.ts +48 -11
- package/src/engine/ecs/transform/Transform64.d.ts.map +1 -1
- package/src/engine/ecs/transform/Transform64.js +54 -15
- package/src/engine/ecs/transform/Transform64SerializationAdapter.d.ts +37 -0
- package/src/engine/ecs/transform/Transform64SerializationAdapter.d.ts.map +1 -0
- package/src/engine/ecs/transform/Transform64SerializationAdapter.js +75 -0
- package/src/engine/ecs/transform/__probe.d.ts +4 -0
- package/src/engine/ecs/transform/__probe.d.ts.map +1 -0
- package/src/engine/ecs/transform/t64_allocation_probe.d.ts +31 -0
- package/src/engine/ecs/transform/t64_allocation_probe.d.ts.map +1 -0
- package/src/engine/ecs/transform/t64_allocation_probe.js +78 -0
- package/src/engine/ecs/transform/t64_announce_change.d.ts +33 -0
- package/src/engine/ecs/transform/t64_announce_change.d.ts.map +1 -0
- package/src/engine/ecs/transform/t64_announce_change.js +39 -0
- package/src/engine/ecs/transform/t64_equals_components.d.ts +30 -0
- package/src/engine/ecs/transform/t64_equals_components.d.ts.map +1 -0
- package/src/engine/ecs/transform/t64_equals_components.js +59 -0
- package/src/engine/ecs/transform/t64_read_rotation.d.ts +13 -0
- package/src/engine/ecs/transform/t64_read_rotation.d.ts.map +1 -0
- package/src/engine/ecs/transform/t64_read_rotation.js +16 -0
- package/src/engine/ecs/transform/t64_read_translation.d.ts +14 -0
- package/src/engine/ecs/transform/t64_read_translation.d.ts.map +1 -0
- package/src/engine/ecs/transform/t64_read_translation.js +17 -0
- package/src/engine/ecs/transform-attachment/TransformAttachment.d.ts +5 -5
- package/src/engine/ecs/transform-attachment/TransformAttachment.d.ts.map +1 -1
- package/src/engine/ecs/transform-attachment/TransformAttachment.js +4 -4
- package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.d.ts +10 -10
- package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.d.ts.map +1 -1
- package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.js +71 -20
- package/src/engine/graphics/debug/createDebugLabel.js +3 -3
- package/src/engine/graphics/ecs/camera/pp/PerfectPanner.d.ts.map +1 -1
- package/src/engine/graphics/ecs/camera/pp/PerfectPanner.js +5 -4
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts.map +1 -1
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js +8 -3
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts +6 -6
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +25 -13
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraLanderSystem.js +1 -1
- package/src/engine/graphics/ecs/mesh/setMesh.js +2 -2
- package/src/engine/graphics/ecs/path/entity/EntityPath.d.ts.map +1 -1
- package/src/engine/graphics/ecs/path/entity/EntityPath.js +21 -7
- package/src/engine/graphics/ecs/path/entity/EntityPathMarkerDefinition.d.ts +4 -4
- package/src/engine/graphics/ecs/path/entity/EntityPathMarkerDefinition.d.ts.map +1 -1
- package/src/engine/graphics/ecs/path/entity/EntityPathMarkerDefinition.js +4 -4
- package/src/engine/graphics/ecs/sprite/SpriteSystemPE.d.ts +2 -2
- package/src/engine/graphics/ecs/sprite/SpriteSystemPE.d.ts.map +1 -1
- package/src/engine/graphics/ecs/sprite/SpriteSystemPE.js +145 -135
- package/src/engine/graphics/geometry/MikkT/Build4RuleGroups.js +2 -2
- package/src/engine/graphics/geometry/MikkT/DegenEpilogue.js +1 -1
- package/src/engine/graphics/geometry/MikkT/DegenPrologue.js +1 -1
- package/src/engine/graphics/geometry/MikkT/EvalTspace.js +1 -1
- package/src/engine/graphics/geometry/MikkT/GenerateTSpaces.js +3 -3
- package/src/engine/graphics/render/frame_graph/CORRECTNESS_FIXES_2026_09_04.md +659 -0
- package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +21 -5
- package/src/engine/graphics/render/frame_graph/FrameGraph.d.ts +45 -0
- package/src/engine/graphics/render/frame_graph/FrameGraph.d.ts.map +1 -1
- package/src/engine/graphics/render/frame_graph/FrameGraph.js +282 -60
- package/src/engine/graphics/render/frame_graph/FramePassBuilder.d.ts.map +1 -1
- package/src/engine/graphics/render/frame_graph/FramePassBuilder.js +20 -9
- package/src/engine/graphics/render/frame_graph/FramePassNode.d.ts +12 -0
- package/src/engine/graphics/render/frame_graph/FramePassNode.d.ts.map +1 -1
- package/src/engine/graphics/render/frame_graph/FramePassNode.js +13 -0
- package/src/engine/graphics/render/frame_graph/FramePassResources.d.ts.map +1 -1
- package/src/engine/graphics/render/frame_graph/FramePassResources.js +5 -0
- package/src/engine/graphics/render/frame_graph/ResourceEntry.d.ts +19 -1
- package/src/engine/graphics/render/frame_graph/ResourceEntry.d.ts.map +1 -1
- package/src/engine/graphics/render/frame_graph/ResourceEntry.js +23 -1
- package/src/engine/graphics/render/frame_graph/backend/void/VoidRenderContext.d.ts +15 -1
- package/src/engine/graphics/render/frame_graph/backend/void/VoidRenderContext.d.ts.map +1 -1
- package/src/engine/graphics/render/frame_graph/backend/void/VoidRenderContext.js +33 -16
- package/src/engine/graphics/render/frame_graph/resource/RenderResourceManager.d.ts.map +1 -1
- package/src/engine/graphics/render/frame_graph/resource/RenderResourceManager.js +19 -1
- package/src/engine/graphics/sh3/path_tracer/populate_path_traced_scene_from_ecd.js +6 -6
- package/src/engine/graphics3/CameraSystem.d.ts +6 -6
- package/src/engine/graphics3/CameraSystem.d.ts.map +1 -1
- package/src/engine/graphics3/CameraSystem.js +170 -170
- package/src/engine/graphics3/DecalSystem.d.ts +6 -6
- package/src/engine/graphics3/DecalSystem.d.ts.map +1 -1
- package/src/engine/graphics3/DecalSystem.js +449 -449
- package/src/engine/graphics3/LightSystem.d.ts +6 -6
- package/src/engine/graphics3/LightSystem.d.ts.map +1 -1
- package/src/engine/graphics3/LightSystem.js +234 -221
- package/src/engine/graphics3/MeshSystem.d.ts +6 -6
- package/src/engine/graphics3/MeshSystem.d.ts.map +1 -1
- package/src/engine/graphics3/MeshSystem.js +53 -22
- package/src/engine/graphics3/ParticipatingMediaSystem.d.ts +20 -15
- package/src/engine/graphics3/ParticipatingMediaSystem.d.ts.map +1 -1
- package/src/engine/graphics3/ParticipatingMediaSystem.js +198 -184
- package/src/engine/graphics3/ParticleEmitterSystem.d.ts +6 -6
- package/src/engine/graphics3/ParticleEmitterSystem.d.ts.map +1 -1
- package/src/engine/graphics3/ParticleEmitterSystem.js +310 -289
- package/src/engine/graphics3/PickingSystem.d.ts +6 -6
- package/src/engine/graphics3/PickingSystem.d.ts.map +1 -1
- package/src/engine/graphics3/PickingSystem.js +273 -273
- package/src/engine/graphics3/ShadedGeometrySystem.d.ts +6 -6
- package/src/engine/graphics3/ShadedGeometrySystem.d.ts.map +1 -1
- package/src/engine/graphics3/ShadedGeometrySystem.js +180 -169
- package/src/engine/graphics3/TerrainSystem.d.ts.map +1 -1
- package/src/engine/graphics3/TerrainSystem.js +565 -566
- package/src/engine/graphics3/Trail3DSystem.d.ts +6 -6
- package/src/engine/graphics3/Trail3DSystem.d.ts.map +1 -1
- package/src/engine/graphics3/Trail3DSystem.js +221 -221
- package/src/engine/graphics3/camera_sync_from_transform.d.ts +2 -2
- package/src/engine/graphics3/camera_sync_from_transform.d.ts.map +1 -1
- package/src/engine/graphics3/camera_sync_from_transform.js +2 -1
- package/src/engine/graphics3/debug/gizmo_view_from_camera.js +4 -4
- package/src/engine/graphics3/particles/GPUParticularRenderer.d.ts +6 -0
- package/src/engine/graphics3/particles/GPUParticularRenderer.d.ts.map +1 -1
- package/src/engine/graphics3/particles/GPUParticularRenderer.js +82 -53
- package/src/engine/graphics3/particles/particle_gpu_records.d.ts +12 -8
- package/src/engine/graphics3/particles/particle_gpu_records.d.ts.map +1 -1
- package/src/engine/graphics3/particles/particle_gpu_records.js +53 -45
- package/src/engine/graphics3/preview/make_model_preview_scene.js +1 -1
- package/src/engine/graphics3/shade_camera_projection_ray.js +4 -4
- package/src/engine/graphics3/shade_node_to_entity_composition.d.ts.map +1 -1
- package/src/engine/graphics3/shade_node_to_entity_composition.js +88 -84
- package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts +7 -7
- package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts.map +1 -1
- package/src/engine/grid/grid2transform/GridPosition2TransformSystem.js +8 -8
- package/src/engine/grid/transform2grid/Transform2GridPositionSystem.d.ts +9 -8
- package/src/engine/grid/transform2grid/Transform2GridPositionSystem.d.ts.map +1 -1
- package/src/engine/grid/transform2grid/Transform2GridPositionSystem.js +29 -8
- package/src/engine/input/ecs/controllers/KeyboardCameraController.js +2 -2
- package/src/engine/intelligence/behavior/ecs/OrbitingBehavior.d.ts.map +1 -1
- package/src/engine/intelligence/behavior/ecs/OrbitingBehavior.js +35 -21
- package/src/engine/intelligence/behavior/util/RotationBehavior.js +5 -5
- package/src/engine/intelligence/mcts/StateNode.js +1 -1
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts +7 -7
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts.map +1 -1
- package/src/engine/interpolation/TransformPoseSerializationAdapter.js +14 -15
- package/src/engine/interpolation/pose_interpoland.d.ts +2 -2
- package/src/engine/interpolation/pose_interpoland.js +4 -4
- package/src/engine/navigation/ecs/path_following/PathFollowingSystem.d.ts +4 -4
- package/src/engine/navigation/ecs/path_following/PathFollowingSystem.d.ts.map +1 -1
- package/src/engine/navigation/ecs/path_following/PathFollowingSystem.js +39 -14
- package/src/engine/network/adapters/TransformReplicationAdapter.d.ts +7 -7
- package/src/engine/network/adapters/TransformReplicationAdapter.d.ts.map +1 -1
- package/src/engine/network/adapters/TransformReplicationAdapter.js +19 -20
- package/src/engine/network/replication/Replicator.d.ts +5 -3
- package/src/engine/network/replication/Replicator.d.ts.map +1 -1
- package/src/engine/network/replication/Replicator.js +65 -16
- package/src/engine/physics/broadphase/compute_fat_world_aabb.js +3 -3
- package/src/engine/physics/ccd/linear_sweep.d.ts.map +1 -1
- package/src/engine/physics/ccd/linear_sweep.js +9 -3
- package/src/engine/physics/constraint/solve_constraints.js +10 -10
- package/src/engine/physics/ecs/ColliderObserverSystem.d.ts +8 -8
- package/src/engine/physics/ecs/ColliderObserverSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/ColliderObserverSystem.js +6 -6
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +22 -32
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +2337 -2310
- package/src/engine/physics/ecs/find_non_finite_physics_state.js +6 -6
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +7 -7
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts.map +1 -1
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.js +16 -8
- package/src/engine/physics/fluid/ecs/FluidSystem.d.ts +4 -4
- package/src/engine/physics/fluid/ecs/FluidSystem.d.ts.map +1 -1
- package/src/engine/physics/fluid/ecs/FluidSystem.js +6 -6
- package/src/engine/physics/fluid/ecs/WorkerFluidSystem.d.ts +2 -2
- package/src/engine/physics/fluid/ecs/WorkerFluidSystem.d.ts.map +1 -1
- package/src/engine/physics/fluid/ecs/WorkerFluidSystem.js +4 -4
- package/src/engine/physics/fluid/ecs/fluid_reanchor_field.js +7 -7
- package/src/engine/physics/fluid/effector/ImpulseFluidEffector.js +4 -4
- package/src/engine/physics/fluid/effector/WakeFluidEffector.js +7 -7
- package/src/engine/physics/inertia/world_inverse_inertia.js +1 -1
- package/src/engine/physics/integration/integrate_position.d.ts.map +1 -1
- package/src/engine/physics/integration/integrate_position.js +13 -12
- package/src/engine/physics/inverse_kinematics/fabrik/fabrik_solve.d.ts.map +1 -1
- package/src/engine/physics/inverse_kinematics/fabrik/fabrik_solve.js +23 -5
- package/src/engine/physics/narrowphase/compute_penetration.js +18 -14
- package/src/engine/physics/narrowphase/convex_convex_manifold.js +2 -2
- package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.js +9 -9
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/narrowphase_step.js +95 -68
- package/src/engine/physics/narrowphase/refine_ray_concave.js +1 -1
- package/src/engine/physics/narrowphase/refine_ray_hit.js +3 -3
- package/src/engine/physics/queries/overlap_shape.d.ts.map +1 -1
- package/src/engine/physics/queries/overlap_shape.js +19 -7
- package/src/engine/physics/queries/raycast.js +3 -3
- package/src/engine/physics/queries/shape_cast.d.ts.map +1 -1
- package/src/engine/physics/queries/shape_cast.js +628 -620
- package/src/engine/physics/solver/solve_contacts.js +10 -10
- package/src/engine/physics/vehicle/RaycastVehicle.d.ts +6 -5
- package/src/engine/physics/vehicle/RaycastVehicle.d.ts.map +1 -1
- package/src/engine/physics/vehicle/RaycastVehicle.js +10 -5
- package/src/engine/sound/ecs/SoundListenerSystem.d.ts +16 -4
- package/src/engine/sound/ecs/SoundListenerSystem.d.ts.map +1 -1
- package/src/engine/sound/ecs/SoundListenerSystem.js +70 -11
- package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts +7 -7
- package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts.map +1 -1
- package/src/engine/sound/ecs/audio/AudioEmitterSystem.js +343 -338
- package/src/engine/sound/ecs/audio/LiveEmitterSet.d.ts +2 -2
- package/src/engine/sound/ecs/audio/LiveEmitterSet.d.ts.map +1 -1
- package/src/engine/sound/ecs/audio/LiveEmitterSet.js +356 -345
- package/src/engine/sound/ecs/audio/SpatialAudioIndex.d.ts +23 -2
- package/src/engine/sound/ecs/audio/SpatialAudioIndex.d.ts.map +1 -1
- package/src/engine/sound/ecs/audio/SpatialAudioIndex.js +174 -140
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.d.ts +6 -6
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.d.ts.map +1 -1
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.js +185 -184
- package/src/engine/sound/material/detector/terrain/TerrainSoundMaterialSurfaceDetector.js +1 -1
- package/src/engine/sound/simulation/core/AcousticRegionIndex.d.ts +2 -1
- package/src/engine/sound/simulation/core/AcousticRegionIndex.d.ts.map +1 -1
- package/src/engine/sound/simulation/core/AcousticRegionIndex.js +4 -3
- package/src/engine/sound/simulation/core/AcousticVolumeRegion.d.ts +3 -2
- package/src/engine/sound/simulation/core/AcousticVolumeRegion.d.ts.map +1 -1
- package/src/engine/sound/simulation/core/AcousticVolumeRegion.js +6 -4
- package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.d.ts +10 -6
- package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.d.ts.map +1 -1
- package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.js +32 -7
- package/src/engine/sound/simulation/ecs/AcousticVolumeSystem.d.ts +7 -7
- package/src/engine/sound/simulation/ecs/AcousticVolumeSystem.d.ts.map +1 -1
- package/src/engine/sound/simulation/ecs/AcousticVolumeSystem.js +6 -6
- package/src/engine/sound/sopra/runtime/EventInstance.d.ts.map +1 -1
- package/src/engine/sound/sopra/runtime/EventInstance.js +8 -2
- package/src/format/scene/gltf/gltf_component_type.d.ts.map +1 -1
- package/src/format/scene/gltf/gltf_component_type.js +3 -1
- package/src/format/texture/ktx2/ktx2_read.d.ts.map +1 -1
- package/src/format/texture/ktx2/ktx2_read.js +2 -1
- package/src/generation/grid/generation/GridTaskDensityMarkerDistribution.d.ts.map +1 -1
- package/src/generation/grid/generation/GridTaskDensityMarkerDistribution.js +8 -1
- package/src/generation/markers/GridCellActionPlaceMarker.d.ts +4 -4
- package/src/generation/markers/GridCellActionPlaceMarker.d.ts.map +1 -1
- package/src/generation/markers/GridCellActionPlaceMarker.js +16 -6
- package/src/generation/markers/MarkerNode.d.ts +3 -3
- package/src/generation/markers/MarkerNode.d.ts.map +1 -1
- package/src/generation/markers/MarkerNode.js +3 -3
- package/src/generation/markers/actions/MarkerNodeActionEntityPlacement.d.ts +4 -4
- package/src/generation/markers/actions/MarkerNodeActionEntityPlacement.d.ts.map +1 -1
- package/src/generation/markers/actions/MarkerNodeActionEntityPlacement.js +7 -7
- package/src/generation/markers/actions/MarkerNodeActionImaginary.d.ts.map +1 -1
- package/src/generation/markers/actions/MarkerNodeActionImaginary.js +20 -2
- package/src/generation/markers/actions/placement/MarkerNodeEntityProcessorClingToTerrain.js +1 -1
- package/src/generation/markers/actions/placement/MarkerNodeEntityProcessorRandomRotation.d.ts.map +1 -1
- package/src/generation/markers/actions/placement/MarkerNodeEntityProcessorRandomRotation.js +13 -3
- package/src/generation/markers/transform/MarkerNodeTransformRotateRandom.d.ts.map +1 -1
- package/src/generation/markers/transform/MarkerNodeTransformRotateRandom.js +11 -1
- package/src/generation/markers/transform/MarkerNodeTransformerAddPositionYFromFilter.d.ts.map +1 -1
- package/src/generation/markers/transform/MarkerNodeTransformerAddPositionYFromFilter.js +8 -4
- package/src/generation/markers/transform/MarkerNodeTransformerOffsetPosition.d.ts.map +1 -1
- package/src/generation/markers/transform/MarkerNodeTransformerOffsetPosition.js +13 -5
- package/src/generation/markers/transform/MarkerNodeTransformerYRotateByFilter.d.ts.map +1 -1
- package/src/generation/markers/transform/MarkerNodeTransformerYRotateByFilter.js +8 -2
- package/src/generation/markers/transform/MarkerNodeTransformerYRotateByFilterGradient.d.ts.map +1 -1
- package/src/generation/markers/transform/MarkerNodeTransformerYRotateByFilterGradient.js +16 -2
- package/src/shade/device/graph/BufferResourceDescriptor.d.ts.map +1 -1
- package/src/shade/device/graph/BufferResourceDescriptor.js +193 -184
- package/src/shade/device/graph/TextureResourceDescriptor.d.ts.map +1 -1
- package/src/shade/device/graph/TextureResourceDescriptor.js +12 -0
- package/src/shade/device/mock/SoftwareGPUDevice.d.ts.map +1 -1
- package/src/shade/device/mock/SoftwareGPUDevice.js +8 -0
- package/src/shade/playground/avboit/main.js +3 -3
- package/src/shade/playground/basis_textures/main.js +1 -1
- package/src/shade/playground/ground_seam/main.js +1 -1
- package/src/shade/playground/particle_editor/README.md +175 -0
- package/src/shade/playground/particle_editor/index.html +76 -0
- package/src/shade/playground/particle_editor/main.d.ts +2 -0
- package/src/shade/playground/particle_editor/main.d.ts.map +1 -0
- package/src/shade/playground/particle_editor/main.js +139 -0
- package/src/shade/playground/particle_system/README.md +92 -62
- package/src/shade/playground/particle_system/index.html +67 -21
- package/src/shade/playground/particle_system/main.js +247 -102
- package/src/shade/{renderer/particles → playground/particle_system}/particle_prototype.d.ts +21 -18
- package/src/shade/playground/particle_system/particle_prototype.d.ts.map +1 -0
- package/src/shade/{renderer/particles → playground/particle_system}/particle_prototype.js +197 -181
- package/src/shade/playground/particle_system/particle_scene.d.ts +136 -20
- package/src/shade/playground/particle_system/particle_scene.d.ts.map +1 -1
- package/src/shade/playground/particle_system/particle_scene.js +487 -670
- package/src/shade/playground/skinned_blas_refit/sweep_views.js +2 -2
- package/src/shade/playground/skinned_blas_refit/verify_instance_bounds.d.ts.map +1 -1
- package/src/shade/playground/skinned_blas_refit/verify_instance_bounds.js +2 -1
- package/src/shade/playground/skinned_blas_refit/verify_skinned_blas_refit.d.ts.map +1 -1
- package/src/shade/playground/skinned_blas_refit/verify_skinned_blas_refit.js +2 -1
- package/src/shade/playground/sponza_path_trace/main.js +1 -1
- package/src/shade/playground/volumetrics_froxel/main.js +917 -917
- package/src/shade/playground/volumetrics_froxel/measure.js +559 -559
- package/src/shade/renderer/Renderer.d.ts +28 -0
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +140 -8
- package/src/shade/renderer/atmosphere/GPUSky.d.ts.map +1 -1
- package/src/shade/renderer/atmosphere/GPUSky.js +4 -2
- package/src/shade/renderer/buffer/graph_read_buffer.d.ts.map +1 -1
- package/src/shade/renderer/buffer/graph_read_buffer.js +8 -1
- package/src/shade/renderer/buffer/table/GPUTypedTable.js +1 -1
- package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.d.ts +30 -0
- package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.js +84 -0
- package/src/shade/renderer/buffer/table/gpu_database_emulator_image.d.ts +27 -0
- package/src/shade/renderer/buffer/table/gpu_database_emulator_image.d.ts.map +1 -0
- package/src/shade/renderer/buffer/table/gpu_database_words.d.ts +24 -0
- package/src/shade/renderer/buffer/table/gpu_database_words.d.ts.map +1 -0
- package/src/shade/renderer/buffer/table/gpu_database_words.js +92 -0
- package/src/shade/renderer/camera/Camera.d.ts +2 -2
- package/src/shade/renderer/camera/Camera.d.ts.map +1 -1
- package/src/shade/renderer/camera/Camera.js +11 -5
- package/src/shade/renderer/camera/GPUCameraContext.d.ts.map +1 -1
- package/src/shade/renderer/camera/GPUCameraContext.js +2 -1
- package/src/shade/renderer/camera/OrbitalCameraController.d.ts +3 -3
- package/src/shade/renderer/camera/OrbitalCameraController.d.ts.map +1 -1
- package/src/shade/renderer/camera/OrbitalCameraController.js +31 -16
- package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts +2 -2
- package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts.map +1 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraController.js +4 -3
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts +10 -5
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts.map +1 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.js +8 -4
- package/src/shade/renderer/extension/FrameContext.d.ts.map +1 -1
- package/src/shade/renderer/extension/FrameContext.js +5 -0
- package/src/shade/renderer/extension/FrameRecord.d.ts +16 -0
- package/src/shade/renderer/extension/FrameRecord.d.ts.map +1 -1
- package/src/shade/renderer/extension/FrameRecord.js +31 -0
- package/src/shade/renderer/extension/GBufferTextures.d.ts +11 -0
- package/src/shade/renderer/extension/GBufferTextures.d.ts.map +1 -1
- package/src/shade/renderer/extension/GBufferTextures.js +14 -0
- package/src/shade/renderer/geometry/attribute/gpu_vertex_buffer_layout_from_spec.js +1 -1
- package/src/shade/renderer/geometry/util/geometry_stripify.js +6 -6
- package/src/shade/renderer/geometry/virtual/format/prototypeVGEOFormatViewer.js +8 -8
- package/src/shade/renderer/global_illumination/lpv/lpv_visualise_probes.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/lpv/lpv_visualise_probes.js +14 -5
- package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.js +17 -9
- package/src/shade/renderer/global_illumination/probe/octahedral/border/graph_lpv_paint_border_depth.d.ts +2 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/border/graph_lpv_paint_border_depth.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/border/graph_lpv_paint_border_depth.js +4 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lvp_atlas_to_sh.d.ts +2 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lvp_atlas_to_sh.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lvp_atlas_to_sh.js +11 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/graph_lpv_shade.d.ts +1 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/graph_lpv_shade.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/graph_lpv_shade.js +4 -2
- package/src/shade/renderer/global_illumination/sharc/SpatialHashRadianceCache.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/sharc/SpatialHashRadianceCache.js +5 -1
- package/src/shade/renderer/light/cluster/cull/graph_cull_lights.d.ts.map +1 -1
- package/src/shade/renderer/light/cluster/cull/graph_cull_lights.js +35 -11
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/particles/DESIGN.md +271 -112
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts +251 -86
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
- package/src/shade/renderer/particles/GPUParticleSystem.js +822 -146
- package/src/shade/renderer/particles/ParticleConstants.d.ts +96 -3
- package/src/shade/renderer/particles/ParticleConstants.d.ts.map +1 -1
- package/src/shade/renderer/particles/ParticleConstants.js +103 -4
- package/src/shade/renderer/particles/coherence/graph_particle_bucket.d.ts +5 -4
- package/src/shade/renderer/particles/coherence/graph_particle_bucket.d.ts.map +1 -1
- package/src/shade/renderer/particles/coherence/graph_particle_bucket.js +11 -15
- package/src/shade/renderer/particles/coherence/shader_particle_bucket.d.ts +2 -2
- package/src/shade/renderer/particles/coherence/shader_particle_bucket.d.ts.map +1 -1
- package/src/shade/renderer/particles/coherence/shader_particle_bucket.js +20 -15
- package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.d.ts +7 -8
- package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.d.ts.map +1 -1
- package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.js +7 -17
- package/src/shade/renderer/particles/data/PARTICLE_DATABASE_SPEC.d.ts +56 -0
- package/src/shade/renderer/particles/data/PARTICLE_DATABASE_SPEC.d.ts.map +1 -0
- package/src/shade/renderer/particles/data/PARTICLE_DATABASE_SPEC.js +68 -0
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STATE.d.ts +39 -0
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STATE.d.ts.map +1 -0
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STATE.js +52 -0
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts +83 -0
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.js +166 -0
- package/src/shade/renderer/particles/data/chunk_particle_emitter.d.ts +21 -5
- package/src/shade/renderer/particles/data/chunk_particle_emitter.d.ts.map +1 -1
- package/src/shade/renderer/particles/data/chunk_particle_emitter.js +44 -85
- package/src/shade/renderer/particles/data/particle_header.d.ts +62 -0
- package/src/shade/renderer/particles/data/particle_header.d.ts.map +1 -0
- package/src/shade/renderer/particles/data/particle_header.js +103 -0
- package/src/shade/renderer/particles/graph/ParticleGroupBuilder.d.ts +179 -0
- package/src/shade/renderer/particles/graph/ParticleGroupBuilder.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/ParticleGroupBuilder.js +303 -0
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts +120 -1
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.js +90 -1
- package/src/shade/renderer/particles/graph/ParticleNodeGroup.d.ts +283 -0
- package/src/shade/renderer/particles/graph/ParticleNodeGroup.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/ParticleNodeGroup.js +419 -0
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts +44 -2
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.js +476 -40
- package/src/shade/renderer/particles/graph/ParticleValueType.d.ts +188 -0
- package/src/shade/renderer/particles/graph/ParticleValueType.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/ParticleValueType.js +287 -0
- package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts +16 -9
- package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/compile_particle_graph.js +464 -205
- package/src/shade/renderer/particles/graph/flatten_particle_graph.d.ts +11 -0
- package/src/shade/renderer/particles/graph/flatten_particle_graph.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/flatten_particle_graph.js +213 -0
- package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.d.ts +27 -0
- package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.js +75 -0
- package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.d.ts +83 -0
- package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.js +319 -0
- package/src/shade/renderer/particles/graph/groups/particle_groups_noise.d.ts +22 -0
- package/src/shade/renderer/particles/graph/groups/particle_groups_noise.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/groups/particle_groups_noise.js +517 -0
- package/src/shade/renderer/particles/graph/groups/particle_groups_utility.d.ts +70 -0
- package/src/shade/renderer/particles/graph/groups/particle_groups_utility.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/groups/particle_groups_utility.js +161 -0
- package/src/shade/renderer/particles/graph/groups/simplex_reference.d.ts +8 -0
- package/src/shade/renderer/particles/graph/groups/simplex_reference.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/groups/simplex_reference.js +159 -0
- package/src/shade/renderer/particles/graph/infer_particle_graph_types.d.ts +88 -0
- package/src/shade/renderer/particles/graph/infer_particle_graph_types.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/infer_particle_graph_types.js +234 -0
- package/src/shade/renderer/particles/graph/make_particle_group.d.ts +98 -0
- package/src/shade/renderer/particles/graph/make_particle_group.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/make_particle_group.js +427 -0
- package/src/shade/renderer/particles/graph/particle_graph_authoring.d.ts +11 -4
- package/src/shade/renderer/particles/graph/particle_graph_authoring.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/particle_graph_authoring.js +121 -108
- package/src/shade/renderer/particles/graph/particle_graph_diagnostic.d.ts +38 -0
- package/src/shade/renderer/particles/graph/particle_graph_diagnostic.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/particle_graph_diagnostic.js +25 -0
- package/src/shade/renderer/particles/graph/particle_graph_distributions.d.ts +29 -0
- package/src/shade/renderer/particles/graph/particle_graph_distributions.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/particle_graph_distributions.js +170 -0
- package/src/shade/renderer/particles/graph/particle_graph_json.d.ts +87 -0
- package/src/shade/renderer/particles/graph/particle_graph_json.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/particle_graph_json.js +196 -0
- package/src/shade/renderer/particles/graph/particle_group_json.d.ts +86 -0
- package/src/shade/renderer/particles/graph/particle_group_json.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/particle_group_json.js +182 -0
- package/src/shade/renderer/particles/graph/particle_group_library.d.ts +33 -0
- package/src/shade/renderer/particles/graph/particle_group_library.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/particle_group_library.js +75 -0
- package/src/shade/renderer/particles/graph/particle_node_library.d.ts +37 -0
- package/src/shade/renderer/particles/graph/particle_node_library.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/particle_node_library.js +64 -0
- package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts +146 -0
- package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/particle_node_parameters.js +234 -0
- package/src/shade/renderer/particles/graph/particle_node_presentation.d.ts +30 -0
- package/src/shade/renderer/particles/graph/particle_node_presentation.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/particle_node_presentation.js +286 -0
- package/src/shade/renderer/particles/graph/validate_particle_graph.d.ts +66 -0
- package/src/shade/renderer/particles/graph/validate_particle_graph.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/validate_particle_graph.js +439 -0
- package/src/shade/renderer/particles/graph_particles.d.ts +143 -68
- package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles.js +424 -71
- package/src/shade/renderer/particles/graph_particles_avboit.d.ts +107 -0
- package/src/shade/renderer/particles/graph_particles_avboit.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph_particles_avboit.js +312 -0
- package/src/shade/renderer/particles/isa/InstructionStream.d.ts +41 -8
- package/src/shade/renderer/particles/isa/InstructionStream.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/InstructionStream.js +111 -33
- package/src/shade/renderer/particles/isa/ParticleAssembler.d.ts +13 -10
- package/src/shade/renderer/particles/isa/ParticleAssembler.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/ParticleAssembler.js +30 -26
- package/src/shade/renderer/particles/isa/ParticleProgram.d.ts +27 -15
- package/src/shade/renderer/particles/isa/ParticleProgram.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/ParticleProgram.js +139 -34
- package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts +117 -9
- package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/ParticleVMISA.js +296 -37
- package/src/shade/renderer/particles/isa/particle_assembly.d.ts +149 -0
- package/src/shade/renderer/particles/isa/particle_assembly.d.ts.map +1 -0
- package/src/shade/renderer/particles/isa/particle_assembly.js +999 -0
- package/src/shade/renderer/particles/layout/ParticleLayout.d.ts +25 -3
- package/src/shade/renderer/particles/layout/ParticleLayout.d.ts.map +1 -1
- package/src/shade/renderer/particles/layout/ParticleLayout.js +33 -5
- package/src/shade/renderer/particles/optimizer/optimize_particle_program.d.ts +160 -0
- package/src/shade/renderer/particles/optimizer/optimize_particle_program.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/optimize_particle_program.js +478 -0
- package/src/shade/renderer/particles/optimizer/particle_program_analysis.d.ts +291 -0
- package/src/shade/renderer/particles/optimizer/particle_program_analysis.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/particle_program_analysis.js +647 -0
- package/src/shade/renderer/particles/optimizer/particle_program_equivalence.d.ts +95 -0
- package/src/shade/renderer/particles/optimizer/particle_program_equivalence.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/particle_program_equivalence.js +213 -0
- package/src/shade/renderer/particles/optimizer/particle_program_fuzz.d.ts +16 -0
- package/src/shade/renderer/particles/optimizer/particle_program_fuzz.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/particle_program_fuzz.js +223 -0
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts +121 -0
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.js +206 -0
- package/src/shade/renderer/particles/optimizer/pass_dedup_constants.d.ts +11 -0
- package/src/shade/renderer/particles/optimizer/pass_dedup_constants.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/pass_dedup_constants.js +197 -0
- package/src/shade/renderer/particles/optimizer/pass_eliminate_dead_code.d.ts +4 -0
- package/src/shade/renderer/particles/optimizer/pass_eliminate_dead_code.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/pass_eliminate_dead_code.js +51 -0
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts +4 -0
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.js +561 -0
- package/src/shade/renderer/particles/optimizer/pass_narrow_widths.d.ts +4 -0
- package/src/shade/renderer/particles/optimizer/pass_narrow_widths.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/pass_narrow_widths.js +72 -0
- package/src/shade/renderer/particles/optimizer/pass_pack_registers.d.ts +4 -0
- package/src/shade/renderer/particles/optimizer/pass_pack_registers.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/pass_pack_registers.js +86 -0
- package/src/shade/renderer/particles/optimizer/pass_peephole.d.ts +4 -0
- package/src/shade/renderer/particles/optimizer/pass_peephole.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/pass_peephole.js +271 -0
- package/src/shade/renderer/particles/optimizer/pass_propagate_copies.d.ts +4 -0
- package/src/shade/renderer/particles/optimizer/pass_propagate_copies.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/pass_propagate_copies.js +268 -0
- package/src/shade/renderer/particles/optimizer/pass_share_subexpressions.d.ts +4 -0
- package/src/shade/renderer/particles/optimizer/pass_share_subexpressions.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/pass_share_subexpressions.js +151 -0
- package/src/shade/renderer/particles/particle_test_fixtures.d.ts +131 -0
- package/src/shade/renderer/particles/particle_test_fixtures.d.ts.map +1 -0
- package/src/shade/renderer/particles/particle_test_fixtures.js +203 -0
- package/src/shade/renderer/particles/runtime/Emitter.d.ts +219 -0
- package/src/shade/renderer/particles/runtime/Emitter.d.ts.map +1 -0
- package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts +128 -60
- package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/EmitterRegistry.js +286 -117
- package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts +264 -0
- package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts.map +1 -0
- package/src/shade/renderer/particles/runtime/ParticleEmitter.js +404 -0
- package/src/shade/renderer/particles/runtime/ParticleSpawnScheduler.d.ts +49 -15
- package/src/shade/renderer/particles/runtime/ParticleSpawnScheduler.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/ProgramArena.d.ts +51 -18
- package/src/shade/renderer/particles/runtime/ProgramArena.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/ProgramArena.js +72 -32
- package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts +4 -4
- package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/create_particle_effect.js +4 -4
- package/src/shade/renderer/particles/runtime/emitter_database_image.d.ts +15 -0
- package/src/shade/renderer/particles/runtime/emitter_database_image.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_avboit_contribution.d.ts +17 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_avboit_contribution.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_avboit_contribution.js +75 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_billboard_vertex.d.ts +20 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_billboard_vertex.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_billboard_vertex.js +94 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_context.d.ts +15 -5
- package/src/shade/renderer/particles/shaders/chunk_particle_context.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/chunk_particle_curve_animation.d.ts +1 -2
- package/src/shade/renderer/particles/shaders/chunk_particle_curve_animation.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/chunk_particle_curve_animation.js +1 -2
- package/src/shade/renderer/particles/shaders/chunk_particle_curve_disabled.d.ts +11 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_curve_disabled.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_curve_disabled.js +13 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_emitter_age.d.ts +17 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_emitter_age.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_emitter_age.js +22 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_emitter_world_sphere.d.ts +14 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_emitter_world_sphere.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_emitter_world_sphere.js +23 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_render_math.js +1 -1
- package/src/shade/renderer/particles/shaders/chunk_particle_scene_view_depth.d.ts +18 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_scene_view_depth.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_scene_view_depth.js +24 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_setup_context.d.ts +26 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_setup_context.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_setup_context.js +59 -0
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.d.ts +14 -0
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.js +153 -0
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_occupancy.d.ts +3 -0
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_occupancy.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_occupancy.js +124 -0
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.d.ts +19 -0
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.js +147 -0
- package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts +18 -8
- package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_emit.js +72 -37
- package/src/shade/renderer/particles/shaders/shader_particle_emitter_tick.d.ts +5 -0
- package/src/shade/renderer/particles/shaders/shader_particle_emitter_tick.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/shader_particle_emitter_tick.js +216 -0
- package/src/shade/renderer/particles/shaders/shader_particle_finalize.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_finalize.js +7 -3
- package/src/shade/renderer/particles/shaders/shader_particle_render.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_render.js +132 -197
- package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +78 -24
- package/src/shade/renderer/particles/shaders/shader_particle_spawn_commands.d.ts +26 -0
- package/src/shade/renderer/particles/shaders/shader_particle_spawn_commands.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/shader_particle_spawn_commands.js +66 -0
- package/src/shade/renderer/particles/sort/shader_particle_sort.d.ts +1 -1
- package/src/shade/renderer/particles/sort/shader_particle_sort.d.ts.map +1 -1
- package/src/shade/renderer/particles/sort/shader_particle_sort.js +21 -36
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts +2 -2
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts.map +1 -1
- package/src/shade/renderer/particles/vm/ParticleVMReference.js +100 -90
- package/src/shade/renderer/particles/vm/chunk_particle_vm.d.ts.map +1 -1
- package/src/shade/renderer/particles/vm/chunk_particle_vm.js +270 -138
- package/src/shade/renderer/postprocess/denoise/debug/graph_pass_debug_reprojection.d.ts +1 -2
- package/src/shade/renderer/postprocess/denoise/debug/graph_pass_debug_reprojection.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/denoise/debug/graph_pass_debug_reprojection.js +0 -3
- package/src/shade/renderer/postprocess/dof/gather/DepthOfFieldU.js +1 -1
- package/src/shade/renderer/postprocess/dof/raymarch/DepthOfField.js +1 -1
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
- package/src/shade/renderer/postprocess/nss/NSS.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/NSS.js +3 -2
- package/src/shade/renderer/postprocess/nss/generate_NSS_WEIGHTS_ACTUAL.js +3 -2
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts +87 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.js +61 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +179 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +27 -2
- package/src/shade/renderer/rasterize/native/avboit/avboit_draw_fragment_body.d.ts +9 -2
- package/src/shade/renderer/rasterize/native/avboit/avboit_draw_fragment_body.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/avboit_draw_fragment_body.js +12 -3
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_splat_slice.d.ts +18 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_splat_slice.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_splat_slice.js +58 -0
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts +25 -1
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +69 -10
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_resolve.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_resolve.js +10 -4
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_splat.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_splat.js +4 -39
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts +7 -2
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +16 -2
- package/src/shade/renderer/scene/optimization/geometry/geometry_deduplicate.d.ts +3 -3
- package/src/shade/renderer/scene/optimization/geometry/geometry_deduplicate.d.ts.map +1 -1
- package/src/shade/renderer/scene/optimization/geometry/geometry_deduplicate.js +29 -23
- package/src/shade/renderer/scene/optimization/geometry/internal/anchor_grow_topological.d.ts +2 -2
- package/src/shade/renderer/scene/optimization/geometry/internal/anchor_grow_topological.d.ts.map +1 -1
- package/src/shade/renderer/scene/optimization/geometry/internal/anchor_grow_topological.js +3 -3
- package/src/shade/renderer/scene/optimization/geometry/internal/cover_greedy.d.ts +4 -4
- package/src/shade/renderer/scene/optimization/geometry/internal/cover_greedy.d.ts.map +1 -1
- package/src/shade/renderer/scene/optimization/geometry/internal/cover_greedy.js +2 -2
- package/src/shade/renderer/scene/optimization/geometry/internal/match_refit_validate.d.ts +2 -2
- package/src/shade/renderer/scene/optimization/geometry/internal/match_refit_validate.d.ts.map +1 -1
- package/src/shade/renderer/scene/optimization/geometry/internal/match_refit_validate.js +2 -2
- package/src/shade/renderer/scene/optimization/geometry/internal/match_solve_transform.d.ts +7 -7
- package/src/shade/renderer/scene/optimization/geometry/internal/match_solve_transform.d.ts.map +1 -1
- package/src/shade/renderer/scene/optimization/geometry/internal/match_solve_transform.js +79 -71
- package/src/shade/renderer/shader/chunk/math/chunk_lower_bound_branchless.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/math/chunk_lower_bound_branchless.js +3 -1
- package/src/shade/renderer/shader/chunk/random/chunk_hash_to_float01.d.ts +13 -3
- package/src/shade/renderer/shader/chunk/random/chunk_hash_to_float01.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/random/chunk_hash_to_float01.js +15 -5
- package/src/shade/renderer/shader/chunk/random/chunk_random_uint.d.ts +8 -0
- package/src/shade/renderer/shader/chunk/random/chunk_random_uint.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/random/chunk_random_uint.js +15 -5
- package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg.d.ts +7 -0
- package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg.js +17 -7
- package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg_advance.d.ts +17 -0
- package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg_advance.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg_advance.js +26 -0
- package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg_output.d.ts +15 -0
- package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg_output.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/random/pcg/chunk_pcg_output.js +26 -0
- package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.js +8 -2
- package/src/shade/renderer/shadow/map/PointLightShadowmap.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/PointLightShadowmap.js +7 -2
- package/src/shade/renderer/shadow/map/SpotLightShadowmap.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/SpotLightShadowmap.js +3 -15
- package/src/shade/renderer/shadow/map/TetrahedronPointLightShadowmap.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/TetrahedronPointLightShadowmap.js +10 -4
- package/src/shade/renderer/shadow/vsm/GPUViewShadowMaps.d.ts.map +1 -1
- package/src/shade/renderer/shadow/vsm/GPUViewShadowMaps.js +7 -2
- package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +2 -2
- package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts +3 -3
- package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.js +3 -3
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +66 -5
- package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLLanguageCore.js +35 -4
- package/src/view/minimap/Minimap.d.ts.map +1 -1
- package/src/view/minimap/Minimap.js +227 -225
- package/src/view/minimap/MinimapCanvasView.js +3 -3
- package/src/view/minimap/MinimapMarker.d.ts +45 -0
- package/src/view/minimap/MinimapMarker.d.ts.map +1 -0
- package/src/view/minimap/MinimapMarker.js +80 -0
- package/src/view/minimap/MinimapMarkerCollection.d.ts +6 -5
- package/src/view/minimap/MinimapMarkerCollection.d.ts.map +1 -1
- package/src/view/minimap/MinimapMarkerCollection.js +6 -6
- package/src/view/minimap/MinimapMarkerSerializationAdapter.d.ts +19 -0
- package/src/view/minimap/MinimapMarkerSerializationAdapter.d.ts.map +1 -0
- package/src/view/minimap/MinimapMarkerSerializationAdapter.js +42 -0
- package/src/view/minimap/dom/MinimapCameraView.d.ts +1 -1
- package/src/view/minimap/dom/MinimapCameraView.d.ts.map +1 -1
- package/src/view/minimap/dom/MinimapCameraView.js +23 -4
- package/src/view/minimap/dom/MinimapMarkerView.js +3 -3
- package/src/view/minimap/gl/MarkerGL.js +3 -3
- package/src/view/util/DomLayoutFrame.d.ts +53 -0
- package/src/view/util/DomLayoutFrame.d.ts.map +1 -0
- package/src/view/util/DomLayoutFrame.js +122 -0
- package/src/core/model/node-graph/visual/layout/BoxLayoutSpec.js +0 -19
- package/src/core/model/node-graph/visual/layout/ConnectedBoxLayouter.js +0 -241
- package/src/core/model/node-graph/visual/layout/ConnectionEndpointLayoutSpec.js +0 -40
- package/src/core/model/node-graph/visual/layout/ConnectionLayoutSpec.js +0 -28
- package/src/engine/ecs/util/hideEntityGracefully.js +0 -196
- package/src/shade/renderer/particles/cull/chunk_particle_cull.js +0 -38
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_LAYOUT.js +0 -214
- package/src/shade/renderer/particles/particle_prototype.d.ts.map +0 -1
- package/src/shade/renderer/particles/runtime/ParticleSpawnScheduler.js +0 -86
- package/src/shade/renderer/particles/runtime/spawn_request_encoding.js +0 -52
- package/src/shade/renderer/particles/shaders/chunk_particle_context.js +0 -58
|
@@ -0,0 +1,1636 @@
|
|
|
1
|
+
import { Color } from "../../../../src/core/color/Color.js";
|
|
2
|
+
import { PortDirection } from "../../../../src/core/model/node-graph/node/PortDirection.js";
|
|
3
|
+
import { ActionProcessor } from "../../../../src/core/process/undo/ActionProcessor.js";
|
|
4
|
+
import { infer_particle_graph_types } from "../../../../src/shade/renderer/particles/graph/infer_particle_graph_types.js";
|
|
5
|
+
import { particle_group_from_nodes } from "../../../../src/shade/renderer/particles/graph/make_particle_group.js";
|
|
6
|
+
import {
|
|
7
|
+
particle_group_descriptions,
|
|
8
|
+
} from "../../../../src/shade/renderer/particles/graph/particle_group_json.js";
|
|
9
|
+
import {
|
|
10
|
+
is_particle_group_description,
|
|
11
|
+
} from "../../../../src/shade/renderer/particles/graph/ParticleNodeGroup.js";
|
|
12
|
+
import {
|
|
13
|
+
particle_node_default_parameters,
|
|
14
|
+
particle_node_parameters,
|
|
15
|
+
} from "../../../../src/shade/renderer/particles/graph/particle_node_parameters.js";
|
|
16
|
+
import {
|
|
17
|
+
PARTICLE_NODE_REGISTRY,
|
|
18
|
+
PARTICLE_VALUE_TYPE,
|
|
19
|
+
} from "../../../../src/shade/renderer/particles/graph/ParticleNodeRegistry.js";
|
|
20
|
+
import {
|
|
21
|
+
particle_library_descriptions,
|
|
22
|
+
} from "../../../../src/shade/renderer/particles/graph/particle_node_library.js";
|
|
23
|
+
import {
|
|
24
|
+
PARTICLE_TYPE_COLOR,
|
|
25
|
+
PARTICLE_TYPE_F32,
|
|
26
|
+
PARTICLE_TYPE_VEC2,
|
|
27
|
+
PARTICLE_TYPE_VEC3,
|
|
28
|
+
PARTICLE_TYPE_VEC4,
|
|
29
|
+
} from "../../../../src/shade/renderer/particles/graph/ParticleValueType.js";
|
|
30
|
+
import KeyboardDevice from "../../../../src/engine/input/devices/KeyboardDevice.js";
|
|
31
|
+
import { KeyCodes } from "../../../../src/engine/input/devices/KeyCodes.js";
|
|
32
|
+
import LabelView from "../../../../src/view/common/LabelView.js";
|
|
33
|
+
import EmptyView from "../../../../src/view/elements/EmptyView.js";
|
|
34
|
+
import View from "../../../../src/view/View.js";
|
|
35
|
+
import { NodeGraphCamera } from "../../node-graph/NodeGraphCamera.js";
|
|
36
|
+
import { NodeGraphEditorView } from "../../node-graph/NodeGraphEditorView.js";
|
|
37
|
+
import { NodeCreateAction } from "../../node-graph/actions/NodeCreateAction.js";
|
|
38
|
+
import { NodeDeleteAction } from "../../node-graph/actions/NodeDeleteAction.js";
|
|
39
|
+
import { ConnectionCreateAction } from "../../node-graph/actions/ConnectionCreateAction.js";
|
|
40
|
+
import { NodesMoveAction } from "../../node-graph/actions/NodesMoveAction.js";
|
|
41
|
+
import { SelectionSetAction } from "../../node-graph/actions/SelectionSetAction.js";
|
|
42
|
+
import { ParticleGroupDefineAction } from "../../../particles/effect/actions/ParticleGroupDefineAction.js";
|
|
43
|
+
import { ParticleNodeParametersView } from "./ParticleNodeParametersView.js";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The graph layer of the particle editor: the two phases of an effect's simulation, authored as
|
|
47
|
+
* node graphs.
|
|
48
|
+
*
|
|
49
|
+
* Built *with* the node-graph editor rather than in it. Everything particle-specific arrives
|
|
50
|
+
* through the seams the base offers — the node library the canvas's add-node menu offers, a
|
|
51
|
+
* parameter-view factory that draws vector, enum, attribute and swizzle controls, and a problems
|
|
52
|
+
* list fed by the document's own compile. The base is left knowing nothing about particles.
|
|
53
|
+
*
|
|
54
|
+
* ## Adding a node
|
|
55
|
+
*
|
|
56
|
+
* On the canvas, where the node is going. Right-click and the library opens under the cursor with
|
|
57
|
+
* the caret in a search field; type a few letters and press Enter and the node lands at the point
|
|
58
|
+
* that was clicked. Drop a wire on open canvas and the same menu opens, narrowed to the types that
|
|
59
|
+
* have a port to take it, and picking one wires it up as it arrives.
|
|
60
|
+
*
|
|
61
|
+
* There is no palette down the side any more. It was a fine list and a bad way to add a node: every
|
|
62
|
+
* node cost a trip to the edge of the window and back, and the node arrived in the middle of the
|
|
63
|
+
* view rather than where it was wanted.
|
|
64
|
+
*
|
|
65
|
+
* ## Groups
|
|
66
|
+
*
|
|
67
|
+
* A {@link ../../../../src/shade/renderer/particles/graph/ParticleNodeGroup.js group} is a node made
|
|
68
|
+
* of nodes, and double-clicking one opens it: the canvas shows the group's own graph and the toolbar
|
|
69
|
+
* grows a breadcrumb back out. Groups nest, so the breadcrumb does. The groups shipped with the
|
|
70
|
+
* editor are locked — one body shared by every instance in every effect — and open read-only.
|
|
71
|
+
*
|
|
72
|
+
* ## Two phases, two graphs
|
|
73
|
+
*
|
|
74
|
+
* INIT runs once when a particle is spawned, UPDATE once per frame per live particle. They are
|
|
75
|
+
* separate graphs with separate cameras and separate selections, and switching between them is a
|
|
76
|
+
* matter of which one is visible — both stay built, so a phase comes back exactly where it was
|
|
77
|
+
* rather than reset to the origin.
|
|
78
|
+
*
|
|
79
|
+
* ## What is undoable
|
|
80
|
+
*
|
|
81
|
+
* Everything that touches a graph, through the {@link ActionProcessor} the host supplies. Adding a
|
|
82
|
+
* node from the menu, wiring, moving, deleting, and every parameter edit inside a node all land
|
|
83
|
+
* on one timeline, so a host editor's global undo covers particle authoring without knowing it
|
|
84
|
+
* exists.
|
|
85
|
+
*/
|
|
86
|
+
|
|
87
|
+
/** Height of the phase / action toolbar, px. @type {number} */
|
|
88
|
+
const TOOLBAR_HEIGHT = 32;
|
|
89
|
+
|
|
90
|
+
/** Height of the problems strip along the bottom, px. @type {number} */
|
|
91
|
+
const PROBLEMS_HEIGHT = 96;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* One colour per type a value can have, and one for a port whose value is not known — nothing
|
|
95
|
+
* wired into it yet, or a parameter that does not describe a width.
|
|
96
|
+
*
|
|
97
|
+
* Every port in the library is declared with the one value type; which value it carries is
|
|
98
|
+
* resolved per node by `infer_particle_graph_types`, and that is what a port is coloured by and a
|
|
99
|
+
* wire drawn in. A wire meeting a port of another colour is a conversion, which is the point: a
|
|
100
|
+
* `vec4` literal feeding a `vec3` add shows as a pink wire into an amber port.
|
|
101
|
+
*
|
|
102
|
+
* Unresolved is registered rather than left to the default because the default exists for a type
|
|
103
|
+
* nobody has thought about, and this one has been.
|
|
104
|
+
*
|
|
105
|
+
* A colour is a `vec4` and gets a colour of its own anyway. Nothing is converted where the two
|
|
106
|
+
* meet — they are the same four lanes — so the change of hue along a wire is not a warning about
|
|
107
|
+
* anything; it is where a raw vector starts being used as a colour, or a colour stops being one,
|
|
108
|
+
* which is the thing a semantic type exists to make visible.
|
|
109
|
+
*
|
|
110
|
+
* @type {Map<number, Color>} data type id -> colour
|
|
111
|
+
*/
|
|
112
|
+
const PARTICLE_TYPE_COLORS = new Map([
|
|
113
|
+
[PARTICLE_VALUE_TYPE.id, new Color(0.58, 0.62, 0.68)],
|
|
114
|
+
[PARTICLE_TYPE_F32.id, new Color(0.55, 0.80, 1.0)],
|
|
115
|
+
[PARTICLE_TYPE_VEC2.id, new Color(0.55, 0.88, 0.50)],
|
|
116
|
+
[PARTICLE_TYPE_VEC3.id, new Color(1.0, 0.78, 0.35)],
|
|
117
|
+
[PARTICLE_TYPE_VEC4.id, new Color(0.95, 0.55, 0.80)],
|
|
118
|
+
[PARTICLE_TYPE_COLOR.id, new Color(0.72, 0.60, 0.98)],
|
|
119
|
+
]);
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The categories the add-node menu groups the library into, in the order they are offered. A node
|
|
123
|
+
* type not named here falls into "other", so the library gaining a type never hides it.
|
|
124
|
+
*
|
|
125
|
+
* @type {Array<{name: string, types: string[]}>}
|
|
126
|
+
*/
|
|
127
|
+
const PALETTE_CATEGORIES = [
|
|
128
|
+
{ name: "sources", types: ["const", "color", "builtin", "attribute", "random", "curve"] },
|
|
129
|
+
{ name: "output", types: ["setAttribute", "kill"] },
|
|
130
|
+
{ name: "arithmetic", types: ["add", "sub", "mul", "div", "mad", "scale", "min", "max", "pow", "step", "mix", "clamp", "smoothstep", "remap", "oneMinus"] },
|
|
131
|
+
{ name: "math", types: ["neg", "abs", "floor", "fract", "round", "ceil", "sqrt", "sin", "cos", "exp", "log"] },
|
|
132
|
+
{ name: "vector", types: ["vec2", "vec3", "vec4", "decompose2", "decompose3", "decompose4", "swizzle", "dot", "cross", "length", "normalize", "distance", "angleBetween2", "angleBetween3"] },
|
|
133
|
+
{ name: "logic", types: ["compare", "select"] },
|
|
134
|
+
{ name: "rotation", types: ["quatFromAxisAngle", "quatLookRotation", "quatRotate"] },
|
|
135
|
+
{ name: "sampling", types: ["sampleSphereSurface", "sampleSphereVolume"] },
|
|
136
|
+
{ name: "noise", types: ["simplex3", "simplex4", "curl3", "curl4"] },
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Whether a port could take a value of a given type, for the menu that finishes a dropped wire.
|
|
141
|
+
*
|
|
142
|
+
* Every port in the library declares the one {@link PARTICLE_VALUE_TYPE} — including a group's, so
|
|
143
|
+
* that a wire may be dragged between any two ports and the graph model never refuses one on a type
|
|
144
|
+
* it does not decide. What a particle node reads is decided by what is wired into it, so for a stock
|
|
145
|
+
* type the answer is always yes, and what the menu is really filtering by is which side the wire came
|
|
146
|
+
* out of. That is the useful filter anyway: a wire out of an output cannot be finished by a `const`.
|
|
147
|
+
*
|
|
148
|
+
* A group *declares* what each of its ports is, separately from the port, and there the answer means
|
|
149
|
+
* something. A value goes in if the shapes match, if it is a scalar (which broadcasts), or if the
|
|
150
|
+
* port is wider (the missing lanes read zero). A port narrower than the value is left out: offering
|
|
151
|
+
* `cross` for a `vec4` would be offering to drop a lane. A group port declared with no type at all
|
|
152
|
+
* takes the shape of what arrives, and so takes anything.
|
|
153
|
+
*
|
|
154
|
+
* @param {Port} port
|
|
155
|
+
* @param {import("../../../../src/shade/renderer/particles/graph/ParticleValueType.js").ParticleValueType|null} dataType
|
|
156
|
+
* @param {import("../../../../src/shade/renderer/particles/graph/ParticleNodeDescription.js").ParticleNodeDescription} description
|
|
157
|
+
* @returns {boolean}
|
|
158
|
+
*/
|
|
159
|
+
function particle_port_accepts(port, dataType, description) {
|
|
160
|
+
if (dataType === null || dataType.isParticleValueType !== true) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const declared = description.group?.portType(port.name, port.direction) ?? null;
|
|
165
|
+
|
|
166
|
+
if (declared === null) {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return dataType.is_scalar || declared.components >= dataType.components;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export class ParticleGraphEditorView extends View {
|
|
174
|
+
/**
|
|
175
|
+
* @type {Array<{id: string, label: string, editor: NodeGraphEditorView, toggle: View}>}
|
|
176
|
+
*/
|
|
177
|
+
#phases = [];
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @type {string}
|
|
181
|
+
*/
|
|
182
|
+
#active = "init";
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* @type {EmptyView}
|
|
186
|
+
*/
|
|
187
|
+
#problems;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @type {EmptyView}
|
|
191
|
+
*/
|
|
192
|
+
#body;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @type {EmptyView}
|
|
196
|
+
*/
|
|
197
|
+
#toolbar;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* The breadcrumb back out of whatever groups are open, and empty while none is.
|
|
201
|
+
* @type {EmptyView}
|
|
202
|
+
*/
|
|
203
|
+
#trail;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* The two commands whose availability depends on what is selected. See {@link #refreshCommands}.
|
|
207
|
+
* @type {View}
|
|
208
|
+
*/
|
|
209
|
+
#group_button;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* @type {View}
|
|
213
|
+
*/
|
|
214
|
+
#ungroup_button;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* The groups currently open, outermost first, each with the canvas showing it. Empty means the
|
|
218
|
+
* phase graphs are what is on screen.
|
|
219
|
+
*
|
|
220
|
+
* @type {Array<{group: import("../../../../src/shade/renderer/particles/graph/ParticleNodeGroup.js").ParticleNodeGroup, editor: NodeGraphEditorView}>}
|
|
221
|
+
*/
|
|
222
|
+
#open_groups = [];
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Phases whose camera has not yet been pointed at their contents. A graph that has just been
|
|
226
|
+
* arranged for the first time is somewhere in particular, and it is not the origin.
|
|
227
|
+
* Discharged by {@link #frameIfNeeded}.
|
|
228
|
+
* @type {Set<string>}
|
|
229
|
+
*/
|
|
230
|
+
#needs_framing = new Set();
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* The last compile's diagnostics, kept so a node view that appears after them can still be
|
|
234
|
+
* marked. Nodes arrive from an undo as readily as from the menu.
|
|
235
|
+
* @type {Array<{scope: string, node: number, severity: string}>}
|
|
236
|
+
*/
|
|
237
|
+
#diagnostics = [];
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* The resolved types of each phase's graph, by phase id, as of the last change to the effect.
|
|
241
|
+
* Filled on demand — see {@link #typesOf} — and emptied whenever the effect changes, since any
|
|
242
|
+
* edit may retype ports far from itself.
|
|
243
|
+
* @type {Map<string, import("../../../../src/shade/renderer/particles/graph/infer_particle_graph_types.js").ParticleGraphTypes>}
|
|
244
|
+
*/
|
|
245
|
+
#types = new Map();
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* The resolved types of each open group's body, by group. See {@link #groupTypes}.
|
|
249
|
+
* @type {Map<import("../../../../src/shade/renderer/particles/graph/ParticleNodeGroup.js").ParticleNodeGroup, import("../../../../src/shade/renderer/particles/graph/infer_particle_graph_types.js").ParticleGraphTypes>}
|
|
250
|
+
*/
|
|
251
|
+
#group_types = new Map();
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* @param {object} options
|
|
255
|
+
* @param {import("../../../particles/effect/ParticleEffectDocument.js").ParticleEffectDocument} options.effect
|
|
256
|
+
* @param {ActionProcessor} [options.actionProcessor] shared with a host editor for a single undo
|
|
257
|
+
* timeline across the whole application
|
|
258
|
+
*/
|
|
259
|
+
constructor({ effect, actionProcessor = new ActionProcessor({}) }) {
|
|
260
|
+
super();
|
|
261
|
+
|
|
262
|
+
this.el = document.createElement('div');
|
|
263
|
+
|
|
264
|
+
this.addClass('ui-particle-graph-editor');
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* @type {import("../../../particles/effect/ParticleEffectDocument.js").ParticleEffectDocument}
|
|
268
|
+
*/
|
|
269
|
+
this.effect = effect;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* @type {ActionProcessor}
|
|
273
|
+
*/
|
|
274
|
+
this.actionProcessor = actionProcessor;
|
|
275
|
+
|
|
276
|
+
this.#toolbar = new EmptyView({ classList: ['toolbar'] });
|
|
277
|
+
this.#body = new EmptyView({ classList: ['body'] });
|
|
278
|
+
this.#problems = new EmptyView({ classList: ['problems'] });
|
|
279
|
+
|
|
280
|
+
for (const phase of [{ id: "init", label: "INIT" }, { id: "update", label: "UPDATE" }]) {
|
|
281
|
+
this.#phases.push(this.#buildPhase(phase.id, phase.label));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
this.addChild(this.#toolbar);
|
|
285
|
+
this.addChild(this.#body);
|
|
286
|
+
this.addChild(this.#problems);
|
|
287
|
+
|
|
288
|
+
this.#buildToolbar();
|
|
289
|
+
|
|
290
|
+
this.setPhase(this.#active);
|
|
291
|
+
|
|
292
|
+
this.#refreshCommands();
|
|
293
|
+
|
|
294
|
+
// Before anything else downstream of a change: a node cannot be drawn until every canvas
|
|
295
|
+
// knows how big its type is, and a group the author just made is a type nothing has seen.
|
|
296
|
+
this.bindSignal(effect.on.changed, this.#ensureGroupPrototypes, this);
|
|
297
|
+
|
|
298
|
+
this.#ensureGroupPrototypes();
|
|
299
|
+
|
|
300
|
+
this.bindSignal(effect.on.changed, this.refreshProblems, this);
|
|
301
|
+
this.on.linked.add(this.refreshProblems, this);
|
|
302
|
+
|
|
303
|
+
// a port's type follows the graph, so every change is a chance for every port to differ
|
|
304
|
+
this.bindSignal(effect.on.changed, this.#refreshTypes, this);
|
|
305
|
+
|
|
306
|
+
this.bindSignal(this.size.onChanged, this.layout, this);
|
|
307
|
+
this.on.linked.add(this.layout, this);
|
|
308
|
+
|
|
309
|
+
this.#bindShortcuts();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* `Ctrl+G` groups the selection and `Ctrl+Shift+G` opens one back up.
|
|
314
|
+
*
|
|
315
|
+
* On this view rather than on a canvas, because it is this view that owns both commands and
|
|
316
|
+
* because a keystroke has to reach them whichever canvas happens to be on screen. The canvases
|
|
317
|
+
* do not stop key events, so one typed over a graph arrives here.
|
|
318
|
+
*/
|
|
319
|
+
#bindShortcuts() {
|
|
320
|
+
this.el.setAttribute('tabindex', '-1');
|
|
321
|
+
|
|
322
|
+
const keyboard = new KeyboardDevice(this.el);
|
|
323
|
+
|
|
324
|
+
keyboard.on.down.add(event => {
|
|
325
|
+
if (!event.ctrlKey || event.keyCode !== KeyCodes.g) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const target = event.target;
|
|
330
|
+
|
|
331
|
+
if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) {
|
|
332
|
+
// typing into a field — a node's parameter, the search box, a group's name
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
event.preventDefault();
|
|
337
|
+
event.stopPropagation();
|
|
338
|
+
|
|
339
|
+
const done = event.shiftKey ? this.ungroupSelection() : this.groupSelection();
|
|
340
|
+
|
|
341
|
+
done.catch(e => console.error(event.shiftKey ? "could not ungroup" : "could not group", e));
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
this.on.linked.add(keyboard.start, keyboard);
|
|
345
|
+
this.on.unlinked.add(keyboard.stop, keyboard);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* @param {string} id
|
|
350
|
+
* @param {string} label
|
|
351
|
+
* @returns {{id: string, label: string, editor: NodeGraphEditorView, toggle: View}}
|
|
352
|
+
*/
|
|
353
|
+
#buildPhase(id, label) {
|
|
354
|
+
const effect = this.effect;
|
|
355
|
+
|
|
356
|
+
const visual = effect[`${id}_visual`];
|
|
357
|
+
|
|
358
|
+
for (const [type_id, color] of PARTICLE_TYPE_COLORS) {
|
|
359
|
+
visual.addDataColor(type_id, color);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// A graph nobody has placed yet — a new effect, or one loaded from a file with no positions
|
|
363
|
+
// — would otherwise open as a stack of nodes on top of each other at the origin. Checked
|
|
364
|
+
// before the editor is built, because building it gives every node visual data at 0,0 and
|
|
365
|
+
// there is then no way to tell "not placed" from "placed at the origin".
|
|
366
|
+
const unplaced = visual.nodes.size === 0 && effect[id].getNodes().length > 0;
|
|
367
|
+
|
|
368
|
+
const editor = this.#buildCanvas({
|
|
369
|
+
graph: effect[id],
|
|
370
|
+
visual,
|
|
371
|
+
types: () => this.#typesOf(id),
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
editor.addClass(`phase-${id}`);
|
|
375
|
+
|
|
376
|
+
if (unplaced) {
|
|
377
|
+
editor.layout();
|
|
378
|
+
|
|
379
|
+
// the camera cannot be pointed at the result yet, see #frameIfNeeded
|
|
380
|
+
this.#needs_framing.add(id);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
this.bindSignal(editor.on.arranged, () => this.#frameIfNeeded());
|
|
384
|
+
|
|
385
|
+
this.#body.addChild(editor);
|
|
386
|
+
|
|
387
|
+
// A node's marker cannot be applied off the document's change signal: that fires before the
|
|
388
|
+
// graph view has built the node's view, because this view's bindings are linked before its
|
|
389
|
+
// children's. The graph view says when the view exists, which is the only moment that works
|
|
390
|
+
// — for a node the canvas menu just added and for one an undo brings back alike.
|
|
391
|
+
this.bindSignal(editor.graphView.on.nodeViewAdded, node => this.#markNode(id, node.id));
|
|
392
|
+
|
|
393
|
+
// Two phases, two maps, one opinion about whether a map is wanted. Folding one away and
|
|
394
|
+
// finding the other still open on the next phase switch would read as the fold not having
|
|
395
|
+
// worked. Setting a value it already holds announces nothing, so this settles rather than
|
|
396
|
+
// loops.
|
|
397
|
+
this.bindSignal(editor.minimap.collapsed.onChanged, collapsed => {
|
|
398
|
+
for (const phase of this.#phases) {
|
|
399
|
+
phase.editor.minimap.collapsed.set(collapsed);
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
const toggle = new EmptyView({ classList: ['phase-toggle'] });
|
|
404
|
+
|
|
405
|
+
toggle.addChild(new LabelView(label, { classList: ['label'] }));
|
|
406
|
+
toggle.el.addEventListener('click', () => this.setPhase(id));
|
|
407
|
+
|
|
408
|
+
this.#toolbar.addChild(toggle);
|
|
409
|
+
|
|
410
|
+
return { id, label, editor, toggle };
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* One canvas over one graph, with everything particle-specific wired into the base's seams.
|
|
415
|
+
*
|
|
416
|
+
* Shared between the two phase graphs and the body of every group that gets opened, because they
|
|
417
|
+
* differ in exactly two things — which graph, and whether it can be edited — and everything else
|
|
418
|
+
* about how a particle graph is drawn and added to should not be written twice.
|
|
419
|
+
*
|
|
420
|
+
* @param {object} options
|
|
421
|
+
* @param {NodeGraph} options.graph
|
|
422
|
+
* @param {NodeGraphVisualData} options.visual
|
|
423
|
+
* @param {function():import("../../../../src/shade/renderer/particles/graph/infer_particle_graph_types.js").ParticleGraphTypes} options.types
|
|
424
|
+
* @param {boolean} [options.readOnly]
|
|
425
|
+
* @returns {NodeGraphEditorView}
|
|
426
|
+
*/
|
|
427
|
+
#buildCanvas({ graph, visual, types, readOnly = false }) {
|
|
428
|
+
const effect = this.effect;
|
|
429
|
+
|
|
430
|
+
for (const [type_id, color] of PARTICLE_TYPE_COLORS) {
|
|
431
|
+
visual.addDataColor(type_id, color);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const editor = new NodeGraphEditorView({
|
|
435
|
+
graph,
|
|
436
|
+
nodeRegistry: PARTICLE_NODE_REGISTRY,
|
|
437
|
+
visual,
|
|
438
|
+
camera: new NodeGraphCamera(),
|
|
439
|
+
actionProcessor: this.actionProcessor,
|
|
440
|
+
readOnly,
|
|
441
|
+
library: {
|
|
442
|
+
// The menu heads each *run* of one category, so the order it is handed decides the
|
|
443
|
+
// order it shows; sorting here keeps that decision on the particle side of the seam.
|
|
444
|
+
descriptions: () => [...particle_library_descriptions(), ...effect.groups.map(g => g.description)]
|
|
445
|
+
.sort((a, b) => category_rank(a.name) - category_rank(b.name)),
|
|
446
|
+
categoryOf: description => category_of(description.name),
|
|
447
|
+
describe: description => description.help,
|
|
448
|
+
accepts: particle_port_accepts,
|
|
449
|
+
parametersOf: description => this.#defaultParameters(description),
|
|
450
|
+
},
|
|
451
|
+
parameterViewFactory: ({ node, commit }) => {
|
|
452
|
+
// A group's configuration is its wiring, and its boundary blocks are its edge:
|
|
453
|
+
// neither declares a parameter, and neither is in the schema to be asked about. A
|
|
454
|
+
// node with no parameters gets no container at all, rather than an empty one.
|
|
455
|
+
if (is_particle_group_description(node.description)) {
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (particle_node_parameters(node.description.name).length === 0) {
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return new ParticleNodeParametersView({ effect, node, commit });
|
|
464
|
+
},
|
|
465
|
+
// What a port carries on this node: its output's type, or for an input the type the
|
|
466
|
+
// node reads it as — which is what an empty port is waiting for. Unknown falls back to
|
|
467
|
+
// the declared type, and its colour.
|
|
468
|
+
portDataType: endpoint => {
|
|
469
|
+
const resolved = types();
|
|
470
|
+
|
|
471
|
+
const type = endpoint.port.direction === PortDirection.Out
|
|
472
|
+
? resolved.output(endpoint.instance.id, endpoint.port.name)
|
|
473
|
+
: resolved.input(endpoint.instance.id, endpoint.port.name);
|
|
474
|
+
|
|
475
|
+
return type ?? endpoint.port.dataType;
|
|
476
|
+
},
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
// Before anything is drawn on it: a canvas cannot build a node until its registry knows how
|
|
480
|
+
// big the type is, and the types this document made are in no shared registry.
|
|
481
|
+
this.#generateGroupPrototypes(editor);
|
|
482
|
+
|
|
483
|
+
// what the commands apply to is what is selected, and that is per canvas
|
|
484
|
+
for (const signal of [editor.selection.nodes.on.added, editor.selection.nodes.on.removed]) {
|
|
485
|
+
editor.bindSignal(signal, () => this.#refreshCommands());
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Double-click a group to open it. Bound on the canvas rather than on this view, so that
|
|
489
|
+
// closing a group takes its bindings with it — and bindings are linked before children, so
|
|
490
|
+
// this is in place before the graph view builds the first node.
|
|
491
|
+
editor.bindSignal(editor.graphView.on.nodeViewAdded, (node, nodeView) => {
|
|
492
|
+
if (node.description.isParticleGroupBoundaryDescription === true) {
|
|
493
|
+
nodeView.addClass('is-group-boundary');
|
|
494
|
+
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (node.description.isParticleGroupNodeDescription !== true) {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
nodeView.addClass('is-group');
|
|
503
|
+
|
|
504
|
+
nodeView.el.addEventListener('dblclick', event => {
|
|
505
|
+
event.preventDefault();
|
|
506
|
+
event.stopPropagation();
|
|
507
|
+
|
|
508
|
+
this.openGroup(node.description.group);
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
return editor;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* The parameters a node of this type should arrive with.
|
|
517
|
+
*
|
|
518
|
+
* @param {import("../../../../src/shade/renderer/particles/graph/ParticleNodeDescription.js").ParticleNodeDescription} description
|
|
519
|
+
* @returns {object}
|
|
520
|
+
*/
|
|
521
|
+
#defaultParameters(description) {
|
|
522
|
+
if (is_particle_group_description(description)) {
|
|
523
|
+
return {};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
return particle_node_default_parameters(description.name, { layout: this.#layoutOrNothing() });
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Give every canvas a box for every node type this document carries of its own.
|
|
531
|
+
*
|
|
532
|
+
* A canvas generates the box it draws a node in from the registry it was built with, and throws
|
|
533
|
+
* when it cannot find one — which is the right behaviour, since the alternative is a node drawn
|
|
534
|
+
* at nothing by nothing. A group an author has just made is a type that registry has never seen,
|
|
535
|
+
* so it is told here, on every change, for every canvas.
|
|
536
|
+
*
|
|
537
|
+
* Cheap and idempotent: a registry generates a box from port and parameter counts and keys it by
|
|
538
|
+
* the description's id, so telling it twice about the same type costs an arithmetic pass.
|
|
539
|
+
*/
|
|
540
|
+
#ensureGroupPrototypes() {
|
|
541
|
+
for (const canvas of [...this.#phases.map(phase => phase.editor), ...this.#open_groups.map(open => open.editor)]) {
|
|
542
|
+
this.#generateGroupPrototypes(canvas);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* The same, for one canvas — which is what a canvas built after the fact needs, and there is no
|
|
548
|
+
* change to hang it off: a group's body is opened, not announced.
|
|
549
|
+
*
|
|
550
|
+
* @param {NodeGraphEditorView} canvas
|
|
551
|
+
*/
|
|
552
|
+
#generateGroupPrototypes(canvas) {
|
|
553
|
+
for (const group of this.effect.groups) {
|
|
554
|
+
for (const description of particle_group_descriptions(group)) {
|
|
555
|
+
canvas.visualRegistry.generate(description);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
#buildToolbar() {
|
|
561
|
+
this.#trail = new EmptyView({ classList: ['group-trail'] });
|
|
562
|
+
|
|
563
|
+
this.#toolbar.addChild(this.#trail);
|
|
564
|
+
|
|
565
|
+
this.#toolbar.addChild(new EmptyView({ classList: ['spacer'] }));
|
|
566
|
+
|
|
567
|
+
this.#group_button = this.#buildButton("Group", () => {
|
|
568
|
+
this.groupSelection().catch(e => console.error("could not group the selection", e));
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
this.#ungroup_button = this.#buildButton("Ungroup", () => {
|
|
572
|
+
this.ungroupSelection().catch(e => console.error("could not ungroup", e));
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
this.#toolbar.addChild(this.#group_button);
|
|
576
|
+
this.#toolbar.addChild(this.#ungroup_button);
|
|
577
|
+
|
|
578
|
+
// The auto-layouter moves every node, which is a big enough change to an author's
|
|
579
|
+
// arrangement that it belongs behind a button rather than happening on its own — and behind
|
|
580
|
+
// an undo, which is what {@link arrange} adds over calling the layouter directly.
|
|
581
|
+
this.#toolbar.addChild(this.#buildButton("Arrange", () => this.arrange()));
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Say whether each command applies to what is selected right now, and why not when it does not.
|
|
586
|
+
*
|
|
587
|
+
* Grouping can be refused for a reason that is not obvious from looking at the canvas — a
|
|
588
|
+
* selection something outside it runs in the middle of cannot become one node — and a button that
|
|
589
|
+
* looked available and then did nothing would be the worst of both. So the reason is worked out
|
|
590
|
+
* up front and shown on the button, which is where an author will look for it.
|
|
591
|
+
*
|
|
592
|
+
* Cheap: the check is a walk downstream from the selection, over a graph with tens of nodes in
|
|
593
|
+
* it, run when the selection changes.
|
|
594
|
+
*/
|
|
595
|
+
#refreshCommands() {
|
|
596
|
+
const editor = this.activeEditor();
|
|
597
|
+
|
|
598
|
+
const selected = editor.selection.nodes.asArray();
|
|
599
|
+
|
|
600
|
+
const grouping = this.#whyNotGroup(editor, selected);
|
|
601
|
+
|
|
602
|
+
this.#group_button.setClass('disabled', grouping !== null);
|
|
603
|
+
this.#group_button.el.setAttribute('title', grouping ?? "make one node out of the selection (Ctrl+G)");
|
|
604
|
+
|
|
605
|
+
const groups = selected
|
|
606
|
+
.map(id => editor.graph.getNode(id))
|
|
607
|
+
.filter(node => node?.description.isParticleGroupNodeDescription === true);
|
|
608
|
+
|
|
609
|
+
const ungrouping = editor.readOnly
|
|
610
|
+
? "this group is locked"
|
|
611
|
+
: (groups.length === 0 ? "select a group node" : null);
|
|
612
|
+
|
|
613
|
+
this.#ungroup_button.setClass('disabled', ungrouping !== null);
|
|
614
|
+
this.#ungroup_button.el.setAttribute('title', ungrouping ?? "put back what the group is made of (Ctrl+Shift+G)");
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Why the selection cannot be grouped, or `null` when it can.
|
|
619
|
+
*
|
|
620
|
+
* @param {NodeGraphEditorView} editor
|
|
621
|
+
* @param {number[]} selected
|
|
622
|
+
* @returns {string|null}
|
|
623
|
+
*/
|
|
624
|
+
#whyNotGroup(editor, selected) {
|
|
625
|
+
if (editor.readOnly) {
|
|
626
|
+
return "this group is locked";
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (selected.length === 0) {
|
|
630
|
+
return "select something to group";
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
try {
|
|
634
|
+
// built and thrown away: the question "could this be grouped" and the answer "here is the
|
|
635
|
+
// group" are the same work, and doing it twice is cheaper than two ways of being wrong
|
|
636
|
+
particle_group_from_nodes({
|
|
637
|
+
graph: editor.graph,
|
|
638
|
+
nodes: selected,
|
|
639
|
+
name: "__probe",
|
|
640
|
+
});
|
|
641
|
+
} catch (e) {
|
|
642
|
+
return e.message;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Turn what is selected into a group, and put the group where the selection was.
|
|
650
|
+
*
|
|
651
|
+
* The one gesture that makes a graph smaller. Which wires become ports, what they are called and
|
|
652
|
+
* what type they are is worked out from the selection — see
|
|
653
|
+
* {@link ../../../../src/shade/renderer/particles/graph/make_particle_group.js} — so a group
|
|
654
|
+
* arrives finished rather than as a form to fill in, and its face is the author's to change
|
|
655
|
+
* afterwards.
|
|
656
|
+
*
|
|
657
|
+
* Everything it does is one step on the timeline: the nodes leaving, the group type appearing,
|
|
658
|
+
* the node that replaces them, and every wire re-made around it. An undo that left a type behind
|
|
659
|
+
* in the add-node menu would be an undo that did not undo.
|
|
660
|
+
*
|
|
661
|
+
* @returns {Promise<NodeInstance|null>} the node that replaced the selection, or null when there
|
|
662
|
+
* was nothing selected to group
|
|
663
|
+
*/
|
|
664
|
+
async groupSelection() {
|
|
665
|
+
const editor = this.activeEditor();
|
|
666
|
+
|
|
667
|
+
if (editor.readOnly) {
|
|
668
|
+
throw new Error("This group is locked, so what is inside it cannot be re-grouped.");
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const selected = editor.selection.nodes.asArray().slice();
|
|
672
|
+
|
|
673
|
+
if (selected.length === 0) {
|
|
674
|
+
return null;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
const graph = editor.graph;
|
|
678
|
+
const visual = editor.visual;
|
|
679
|
+
|
|
680
|
+
const { group, inputs, outputs } = particle_group_from_nodes({
|
|
681
|
+
graph,
|
|
682
|
+
visual,
|
|
683
|
+
visualRegistry: editor.visualRegistry,
|
|
684
|
+
layout: this.#layoutOrNothing(),
|
|
685
|
+
nodes: selected,
|
|
686
|
+
name: this.effect.unusedGroupName(),
|
|
687
|
+
label: `Group ${this.effect.groups.length + 1}`,
|
|
688
|
+
help: "a group made from a selection",
|
|
689
|
+
icon: PARTICLE_GROUP_ICON,
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
const centre = centroid(visual, selected);
|
|
693
|
+
|
|
694
|
+
const create = new NodeCreateAction(
|
|
695
|
+
graph, visual, editor.visualRegistry, group.description, centre.x, centre.y
|
|
696
|
+
);
|
|
697
|
+
|
|
698
|
+
this.actionProcessor.mark('group');
|
|
699
|
+
|
|
700
|
+
// The group is defined before the node that uses it is created: creating one needs a box to
|
|
701
|
+
// draw it in, and that is generated when the document announces the type.
|
|
702
|
+
await this.actionProcessor.doMany([
|
|
703
|
+
new SelectionSetAction(editor.selection, [], []),
|
|
704
|
+
...selected.map(id => new NodeDeleteAction(graph, visual, id)),
|
|
705
|
+
new ParticleGroupDefineAction(this.effect, group),
|
|
706
|
+
create,
|
|
707
|
+
]);
|
|
708
|
+
|
|
709
|
+
const node = create.node;
|
|
710
|
+
|
|
711
|
+
const wires = [];
|
|
712
|
+
|
|
713
|
+
for (const entry of inputs) {
|
|
714
|
+
const port = group.description.findPortByNameAndDirection(entry.port, PortDirection.In);
|
|
715
|
+
|
|
716
|
+
wires.push(new ConnectionCreateAction(graph, entry.from.node, entry.from.port, node.id, port.id));
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
for (const entry of outputs) {
|
|
720
|
+
const port = group.description.findPortByNameAndDirection(entry.port, PortDirection.Out);
|
|
721
|
+
|
|
722
|
+
for (const target of entry.to) {
|
|
723
|
+
wires.push(new ConnectionCreateAction(graph, node.id, port.id, target.node, target.port));
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// and what is selected afterwards is the thing the author just made
|
|
728
|
+
wires.push(new SelectionSetAction(editor.selection, [node.id], []));
|
|
729
|
+
|
|
730
|
+
await this.actionProcessor.doMany(wires);
|
|
731
|
+
|
|
732
|
+
return node;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Replace every selected group node with the nodes it is made of.
|
|
737
|
+
*
|
|
738
|
+
* The way out of a grouping made by mistake, and the way in to a library group an author wants to
|
|
739
|
+
* change one use of: the definition is not touched, only this instance. What lands in the graph
|
|
740
|
+
* is what compilation would have inlined anyway.
|
|
741
|
+
*
|
|
742
|
+
* @returns {Promise<NodeInstance[]>} the nodes that replaced them; empty when nothing applied
|
|
743
|
+
*/
|
|
744
|
+
async ungroupSelection() {
|
|
745
|
+
const editor = this.activeEditor();
|
|
746
|
+
|
|
747
|
+
if (editor.readOnly) {
|
|
748
|
+
throw new Error("This group is locked, so what is inside it cannot be changed.");
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
const selected = editor.selection.nodes.asArray()
|
|
752
|
+
.map(id => editor.graph.getNode(id))
|
|
753
|
+
.filter(node => node !== undefined && node.description.isParticleGroupNodeDescription === true);
|
|
754
|
+
|
|
755
|
+
if (selected.length === 0) {
|
|
756
|
+
return [];
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
this.actionProcessor.mark('ungroup');
|
|
760
|
+
|
|
761
|
+
const opened = [];
|
|
762
|
+
|
|
763
|
+
for (const node of selected) {
|
|
764
|
+
opened.push(...await this.#ungroup(editor, node));
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
await this.actionProcessor.do(new SelectionSetAction(
|
|
768
|
+
editor.selection,
|
|
769
|
+
opened.map(node => node.id),
|
|
770
|
+
[]
|
|
771
|
+
));
|
|
772
|
+
|
|
773
|
+
return opened;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* One group node, replaced by a copy of its body.
|
|
778
|
+
*
|
|
779
|
+
* Built out of the same actions every other edit is, rather than as a bespoke reversible splice:
|
|
780
|
+
* the nodes arrive through {@link NodeCreateAction}, the wires through
|
|
781
|
+
* {@link ConnectionCreateAction}, and the group node leaves through {@link NodeDeleteAction}, so
|
|
782
|
+
* undo already knows what to do with all of it.
|
|
783
|
+
*
|
|
784
|
+
* @param {NodeGraphEditorView} editor
|
|
785
|
+
* @param {NodeInstance} node the group instance
|
|
786
|
+
* @returns {Promise<NodeInstance[]>}
|
|
787
|
+
*/
|
|
788
|
+
async #ungroup(editor, node) {
|
|
789
|
+
const graph = editor.graph;
|
|
790
|
+
const visual = editor.visual;
|
|
791
|
+
|
|
792
|
+
const group = node.description.group;
|
|
793
|
+
const body = group.graph;
|
|
794
|
+
|
|
795
|
+
const placed = visual.getNode(node.id)?.dimensions.position;
|
|
796
|
+
|
|
797
|
+
// Where the body sits inside the group, so what comes out of it lands around where the node
|
|
798
|
+
// was rather than at the origin.
|
|
799
|
+
const origin = body_origin(group);
|
|
800
|
+
|
|
801
|
+
/** @type {Map<number, NodeCreateAction>} body node id -> the action that recreates it */
|
|
802
|
+
const created = new Map();
|
|
803
|
+
|
|
804
|
+
for (const inner of body.getNodes()) {
|
|
805
|
+
if (inner === group.inputNode || inner === group.outputNode) {
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
const at = group.visual.getNode(inner.id)?.dimensions.position;
|
|
810
|
+
|
|
811
|
+
created.set(inner.id, new NodeCreateAction(
|
|
812
|
+
graph,
|
|
813
|
+
visual,
|
|
814
|
+
editor.visualRegistry,
|
|
815
|
+
inner.description,
|
|
816
|
+
(placed?.x ?? 0) + (at === undefined ? 0 : at.x - origin.x),
|
|
817
|
+
(placed?.y ?? 0) + (at === undefined ? 0 : at.y - origin.y),
|
|
818
|
+
clone_parameters(inner.parameters)
|
|
819
|
+
));
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
await this.actionProcessor.doMany(Array.from(created.values()));
|
|
823
|
+
|
|
824
|
+
// What the group node was wired to, read before it goes.
|
|
825
|
+
const sources = new Map();
|
|
826
|
+
const targets = new Map();
|
|
827
|
+
|
|
828
|
+
for (const connection of node.inConnections) {
|
|
829
|
+
sources.set(connection.target.port.name, {
|
|
830
|
+
node: connection.source.instance.id,
|
|
831
|
+
port: connection.source.port.id,
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
for (const connection of node.outConnections) {
|
|
836
|
+
const listed = targets.get(connection.source.port.name) ?? [];
|
|
837
|
+
|
|
838
|
+
listed.push({ node: connection.target.instance.id, port: connection.target.port.id });
|
|
839
|
+
|
|
840
|
+
targets.set(connection.source.port.name, listed);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
const wires = [];
|
|
844
|
+
|
|
845
|
+
for (const connection of body.getConnections()) {
|
|
846
|
+
const from = connection.source;
|
|
847
|
+
const to = connection.target;
|
|
848
|
+
|
|
849
|
+
const from_boundary = from.instance === group.inputNode;
|
|
850
|
+
const to_boundary = to.instance === group.outputNode;
|
|
851
|
+
|
|
852
|
+
if (from_boundary && to_boundary) {
|
|
853
|
+
const source = sources.get(from.port.name);
|
|
854
|
+
|
|
855
|
+
if (source !== undefined) {
|
|
856
|
+
for (const target of targets.get(to.port.name) ?? []) {
|
|
857
|
+
wires.push(new ConnectionCreateAction(
|
|
858
|
+
graph, source.node, source.port, target.node, target.port
|
|
859
|
+
));
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
continue;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
if (from_boundary) {
|
|
867
|
+
const source = sources.get(from.port.name);
|
|
868
|
+
|
|
869
|
+
if (source !== undefined) {
|
|
870
|
+
wires.push(new ConnectionCreateAction(
|
|
871
|
+
graph, source.node, source.port, created.get(to.instance.id).node.id, to.port.id
|
|
872
|
+
));
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
if (to_boundary) {
|
|
879
|
+
for (const target of targets.get(to.port.name) ?? []) {
|
|
880
|
+
wires.push(new ConnectionCreateAction(
|
|
881
|
+
graph, created.get(from.instance.id).node.id, from.port.id, target.node, target.port
|
|
882
|
+
));
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
wires.push(new ConnectionCreateAction(
|
|
889
|
+
graph,
|
|
890
|
+
created.get(from.instance.id).node.id, from.port.id,
|
|
891
|
+
created.get(to.instance.id).node.id, to.port.id
|
|
892
|
+
));
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// last: deleting the group node takes its own wires with it, and the ones above replace them
|
|
896
|
+
await this.actionProcessor.doMany([...wires, new NodeDeleteAction(graph, visual, node.id)]);
|
|
897
|
+
|
|
898
|
+
return Array.from(created.values()).map(action => action.node);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* The effect's attribute schema, or nothing when the declarations do not make one.
|
|
903
|
+
*
|
|
904
|
+
* @returns {import("../../../../src/shade/renderer/particles/layout/ParticleLayout.js").ParticleLayout|undefined}
|
|
905
|
+
*/
|
|
906
|
+
#layoutOrNothing() {
|
|
907
|
+
try {
|
|
908
|
+
return this.effect.buildLayout();
|
|
909
|
+
} catch (e) {
|
|
910
|
+
// a broken attribute schema types the attribute reads as unknown, and nothing else
|
|
911
|
+
return undefined;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Show the body of a group instead of the graph that holds it.
|
|
917
|
+
*
|
|
918
|
+
* Every instance of a group shares one body, so this is the group's own graph and not a copy of
|
|
919
|
+
* it — which is the point, and is why the ones shipped with the editor are locked: an edit inside
|
|
920
|
+
* an unlocked group is an edit to every instance of it in every effect. A locked one opens
|
|
921
|
+
* read-only, and is worth opening anyway to read.
|
|
922
|
+
*
|
|
923
|
+
* Groups nest, so this stacks: opening one from inside another pushes onto the trail rather than
|
|
924
|
+
* replacing it.
|
|
925
|
+
*
|
|
926
|
+
* @param {import("../../../../src/shade/renderer/particles/graph/ParticleNodeGroup.js").ParticleNodeGroup} group
|
|
927
|
+
* @returns {void}
|
|
928
|
+
*/
|
|
929
|
+
openGroup(group) {
|
|
930
|
+
const unplaced = group.visual.nodes.size === 0;
|
|
931
|
+
|
|
932
|
+
const editor = this.#buildCanvas({
|
|
933
|
+
graph: group.graph,
|
|
934
|
+
visual: group.visual,
|
|
935
|
+
types: () => this.#groupTypes(group),
|
|
936
|
+
readOnly: group.locked,
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
editor.addClass('group-body');
|
|
940
|
+
|
|
941
|
+
this.#open_groups.push({ group, editor });
|
|
942
|
+
|
|
943
|
+
// An editable group's body is not part of the document, so nothing in the effect's change
|
|
944
|
+
// signal says when its types have moved. Its own graph does.
|
|
945
|
+
for (const signal of [
|
|
946
|
+
group.graph.on.nodeAdded,
|
|
947
|
+
group.graph.on.nodeRemoved,
|
|
948
|
+
group.graph.on.connectionAdded,
|
|
949
|
+
group.graph.on.connectionRemoved,
|
|
950
|
+
]) {
|
|
951
|
+
editor.bindSignal(signal, () => {
|
|
952
|
+
this.#group_types.delete(group);
|
|
953
|
+
|
|
954
|
+
editor.graphView.refreshPortTypes();
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
this.#body.addChild(editor);
|
|
959
|
+
|
|
960
|
+
if (unplaced) {
|
|
961
|
+
// A group's body is built in code and has never been arranged, so the first time anybody
|
|
962
|
+
// looks at one is the moment to do it. The result is kept on the definition, so the
|
|
963
|
+
// second look is where the first one left it.
|
|
964
|
+
editor.layout();
|
|
965
|
+
|
|
966
|
+
// bound on the canvas rather than on this view, so closing the group takes the binding
|
|
967
|
+
// with it rather than leaving one pointed at a canvas nobody will draw again
|
|
968
|
+
editor.bindSignal(editor.on.arranged, final => {
|
|
969
|
+
if (final) {
|
|
970
|
+
editor.cameraContainAll();
|
|
971
|
+
}
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
this.#showActive();
|
|
976
|
+
this.layout();
|
|
977
|
+
|
|
978
|
+
if (!editor.isArrangementProvisional()) {
|
|
979
|
+
editor.cameraContainAll();
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* Leave the innermost open group. Harmless when none is open.
|
|
985
|
+
* @returns {void}
|
|
986
|
+
*/
|
|
987
|
+
closeGroup() {
|
|
988
|
+
this.openGroupDepth(this.#open_groups.length - 1);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
/**
|
|
992
|
+
* Go back to a particular depth in the trail: 0 is the phase graph, 1 the outermost group.
|
|
993
|
+
*
|
|
994
|
+
* @param {number} depth
|
|
995
|
+
* @returns {void}
|
|
996
|
+
*/
|
|
997
|
+
openGroupDepth(depth) {
|
|
998
|
+
const target = Math.max(0, Math.min(depth, this.#open_groups.length));
|
|
999
|
+
|
|
1000
|
+
for (const closing of this.#open_groups.splice(target)) {
|
|
1001
|
+
this.#body.removeChild(closing.editor);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
this.#showActive();
|
|
1005
|
+
this.layout();
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* The groups currently open, outermost first.
|
|
1010
|
+
*
|
|
1011
|
+
* @returns {import("../../../../src/shade/renderer/particles/graph/ParticleNodeGroup.js").ParticleNodeGroup[]}
|
|
1012
|
+
*/
|
|
1013
|
+
openGroups() {
|
|
1014
|
+
return this.#open_groups.map(entry => entry.group);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/**
|
|
1018
|
+
* Re-run the automatic layout over the graph on screen, as one undoable step.
|
|
1019
|
+
*
|
|
1020
|
+
* It moves every node in the graph, which makes it the most destructive thing on the toolbar:
|
|
1021
|
+
* an author who does not like the result has lost an arrangement they may have spent a while on.
|
|
1022
|
+
* So the positions before and after are recorded and pushed as a {@link NodesMoveAction}, the
|
|
1023
|
+
* same action a drag lands on the timeline.
|
|
1024
|
+
*
|
|
1025
|
+
* Inside a locked group it does nothing: an arrangement is a change to the group's definition
|
|
1026
|
+
* like any other, and a locked group's definition is not the author's to change.
|
|
1027
|
+
*/
|
|
1028
|
+
arrange() {
|
|
1029
|
+
const editor = this.activeEditor();
|
|
1030
|
+
|
|
1031
|
+
if (editor.readOnly) {
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
const open = this.#open_groups[this.#open_groups.length - 1];
|
|
1036
|
+
|
|
1037
|
+
const graph = open === undefined ? this.effect[this.#active] : open.group.graph;
|
|
1038
|
+
const visual = open === undefined ? this.effect[`${this.#active}_visual`] : open.group.visual;
|
|
1039
|
+
|
|
1040
|
+
/** @type {{visual: NodeVisualData, from: Vector2}[]} */
|
|
1041
|
+
const before = [];
|
|
1042
|
+
|
|
1043
|
+
for (const node of graph.getNodes()) {
|
|
1044
|
+
const node_visual = visual.getNode(node.id);
|
|
1045
|
+
|
|
1046
|
+
if (node_visual !== undefined) {
|
|
1047
|
+
before.push({ visual: node_visual, from: node_visual.dimensions.position.clone() });
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
editor.layout();
|
|
1052
|
+
|
|
1053
|
+
const moved = before.filter(entry => !entry.visual.dimensions.position.equals(entry.from));
|
|
1054
|
+
|
|
1055
|
+
if (moved.length === 0) {
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// the nodes are already where the layouter put them; the action records the net change so
|
|
1060
|
+
// the whole arrange lands on the timeline as one entry
|
|
1061
|
+
this.actionProcessor.mark('arrange');
|
|
1062
|
+
this.actionProcessor.do(new NodesMoveAction(moved.map(entry => ({
|
|
1063
|
+
visual: entry.visual,
|
|
1064
|
+
from: entry.from,
|
|
1065
|
+
to: entry.visual.dimensions.position.clone()
|
|
1066
|
+
}))));
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
/**
|
|
1070
|
+
* @param {string} label
|
|
1071
|
+
* @param {function()} action
|
|
1072
|
+
* @returns {View}
|
|
1073
|
+
*/
|
|
1074
|
+
#buildButton(label, action) {
|
|
1075
|
+
const button = new EmptyView({ classList: ['toolbar-button'] });
|
|
1076
|
+
|
|
1077
|
+
button.addChild(new LabelView(label));
|
|
1078
|
+
|
|
1079
|
+
button.el.addEventListener('click', () => {
|
|
1080
|
+
if (button.el.classList.contains('disabled')) {
|
|
1081
|
+
// the title says why, and doing nothing is what "disabled" means
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
action();
|
|
1086
|
+
});
|
|
1087
|
+
|
|
1088
|
+
return button;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
/**
|
|
1092
|
+
* Add a node of the given type to the phase being edited, seeded with the defaults its type
|
|
1093
|
+
* declares — so what lands in the graph is a node that compiles rather than one to repair.
|
|
1094
|
+
*
|
|
1095
|
+
* @param {ParticleNodeDescription} description
|
|
1096
|
+
* @param {object} [options] position, see {@link NodeGraphEditorView#addNode}
|
|
1097
|
+
* @returns {Promise<NodeInstance>} the new node, once the action timeline has applied the add
|
|
1098
|
+
*/
|
|
1099
|
+
async addNode(description, options = {}) {
|
|
1100
|
+
const editor = this.activeEditor();
|
|
1101
|
+
|
|
1102
|
+
if (editor.readOnly) {
|
|
1103
|
+
// the canvas on screen is a locked group's body, which is not the author's to add to
|
|
1104
|
+
throw new Error(
|
|
1105
|
+
`Cannot add a '${description.name}': `
|
|
1106
|
+
+ `the group '${this.#open_groups[this.#open_groups.length - 1].group.name}' is locked.`
|
|
1107
|
+
);
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
return editor.addNode(description, {
|
|
1111
|
+
...options,
|
|
1112
|
+
parameters: this.#defaultParameters(description),
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
/**
|
|
1117
|
+
* The resolved types of one group's body, computed on first use and kept.
|
|
1118
|
+
*
|
|
1119
|
+
* A group's body does not change while an effect is being edited — the shipped ones are locked,
|
|
1120
|
+
* and an unlocked one changing is a change to the group rather than to the effect — so unlike a
|
|
1121
|
+
* phase's types these are not thrown away when the document changes. They are keyed on the group
|
|
1122
|
+
* itself, so two effects open on the same group share one answer.
|
|
1123
|
+
*
|
|
1124
|
+
* @param {import("../../../../src/shade/renderer/particles/graph/ParticleNodeGroup.js").ParticleNodeGroup} group
|
|
1125
|
+
* @returns {import("../../../../src/shade/renderer/particles/graph/infer_particle_graph_types.js").ParticleGraphTypes}
|
|
1126
|
+
*/
|
|
1127
|
+
#groupTypes(group) {
|
|
1128
|
+
let types = this.#group_types.get(group);
|
|
1129
|
+
|
|
1130
|
+
if (types === undefined) {
|
|
1131
|
+
// No layout: a group has no effect and so no attribute schema of its own. A group that
|
|
1132
|
+
// reads an attribute types that read as unknown, which is honest — what it reads depends
|
|
1133
|
+
// on the effect it is placed in.
|
|
1134
|
+
types = infer_particle_graph_types(group.graph);
|
|
1135
|
+
|
|
1136
|
+
this.#group_types.set(group, types);
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
return types;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
/**
|
|
1143
|
+
* The resolved types of one phase's graph, computed on first use since the effect last changed.
|
|
1144
|
+
*
|
|
1145
|
+
* @param {string} id `"init"` or `"update"`
|
|
1146
|
+
* @returns {import("../../../../src/shade/renderer/particles/graph/infer_particle_graph_types.js").ParticleGraphTypes}
|
|
1147
|
+
*/
|
|
1148
|
+
#typesOf(id) {
|
|
1149
|
+
let types = this.#types.get(id);
|
|
1150
|
+
|
|
1151
|
+
if (types === undefined) {
|
|
1152
|
+
types = infer_particle_graph_types(this.effect[id], { layout: this.#layoutOrNothing() });
|
|
1153
|
+
|
|
1154
|
+
this.#types.set(id, types);
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
return types;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
/**
|
|
1161
|
+
* Forget the resolved types and have every port in both phases ask again.
|
|
1162
|
+
*
|
|
1163
|
+
* The open groups are asked too. Their own types have not changed — a group's body is not part
|
|
1164
|
+
* of the document — but a group editor's ports are drawn by the same path, and one left showing
|
|
1165
|
+
* the colours it had before a rebuild would be the only stale thing on screen.
|
|
1166
|
+
*/
|
|
1167
|
+
#refreshTypes() {
|
|
1168
|
+
this.#types.clear();
|
|
1169
|
+
|
|
1170
|
+
for (const phase of this.#phases) {
|
|
1171
|
+
phase.editor.graphView.refreshPortTypes();
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
for (const open of this.#open_groups) {
|
|
1175
|
+
open.editor.graphView.refreshPortTypes();
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
/**
|
|
1180
|
+
* The canvas an edit would land on: the innermost open group, or the phase being edited.
|
|
1181
|
+
*
|
|
1182
|
+
* @returns {NodeGraphEditorView}
|
|
1183
|
+
*/
|
|
1184
|
+
activeEditor() {
|
|
1185
|
+
const open = this.#open_groups[this.#open_groups.length - 1];
|
|
1186
|
+
|
|
1187
|
+
return open === undefined
|
|
1188
|
+
? this.#phases.find(phase => phase.id === this.#active).editor
|
|
1189
|
+
: open.editor;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
/**
|
|
1193
|
+
* @returns {string} `"init"` or `"update"`
|
|
1194
|
+
*/
|
|
1195
|
+
activePhase() {
|
|
1196
|
+
return this.#active;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
/**
|
|
1200
|
+
* Show one phase. Both stay built, so the one being left keeps its camera and its selection.
|
|
1201
|
+
* @param {string} id `"init"` or `"update"`
|
|
1202
|
+
*/
|
|
1203
|
+
setPhase(id) {
|
|
1204
|
+
this.#active = id;
|
|
1205
|
+
|
|
1206
|
+
// A group is opened from a graph, so leaving that graph leaves the group. Coming back to
|
|
1207
|
+
// find somebody else's group still open would be the phase switch not having happened.
|
|
1208
|
+
for (const closing of this.#open_groups.splice(0)) {
|
|
1209
|
+
this.#body.removeChild(closing.editor);
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
for (const phase of this.#phases) {
|
|
1213
|
+
phase.toggle.setClass('active', phase.id === id);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
this.#showActive();
|
|
1217
|
+
|
|
1218
|
+
this.layout();
|
|
1219
|
+
this.refreshProblems();
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
/**
|
|
1223
|
+
* Show exactly one canvas — the innermost open group, or the active phase — and redraw the trail
|
|
1224
|
+
* back out to it.
|
|
1225
|
+
*/
|
|
1226
|
+
#showActive() {
|
|
1227
|
+
const active = this.activeEditor();
|
|
1228
|
+
|
|
1229
|
+
for (const phase of this.#phases) {
|
|
1230
|
+
phase.editor.visible = phase.editor === active;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
for (const open of this.#open_groups) {
|
|
1234
|
+
open.editor.visible = open.editor === active;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
this.#refreshCommands();
|
|
1238
|
+
|
|
1239
|
+
this.#trail.removeAllChildren();
|
|
1240
|
+
|
|
1241
|
+
if (this.#open_groups.length === 0) {
|
|
1242
|
+
// nothing to find the way back from
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
const crumb = (label, depth, group) => {
|
|
1247
|
+
const view = new EmptyView({ classList: ['crumb'] });
|
|
1248
|
+
|
|
1249
|
+
const current = depth === this.#open_groups.length;
|
|
1250
|
+
|
|
1251
|
+
if (current && group !== undefined && !group.locked) {
|
|
1252
|
+
// The group an author is inside, and made: its name is the one thing about it that
|
|
1253
|
+
// wants changing the moment it exists, so it is a field rather than a word.
|
|
1254
|
+
view.addChild(this.#buildGroupNameField(group));
|
|
1255
|
+
} else {
|
|
1256
|
+
// `name` rather than `label`: LabelView puts `label` on every one of its elements,
|
|
1257
|
+
// so the crumb's name and its "locked" tag would be indistinguishable by class
|
|
1258
|
+
view.addChild(new LabelView(label, { classList: ['name'] }));
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
if (group?.locked === true) {
|
|
1262
|
+
view.addChild(new LabelView("locked", { classList: ['locked'] }));
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
view.setClass('current', current);
|
|
1266
|
+
|
|
1267
|
+
view.el.addEventListener('click', event => {
|
|
1268
|
+
if (event.target instanceof HTMLInputElement) {
|
|
1269
|
+
// renaming the group is not asking to leave it
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
this.openGroupDepth(depth);
|
|
1274
|
+
});
|
|
1275
|
+
|
|
1276
|
+
this.#trail.addChild(view);
|
|
1277
|
+
};
|
|
1278
|
+
|
|
1279
|
+
crumb(this.#active.toUpperCase(), 0, undefined);
|
|
1280
|
+
|
|
1281
|
+
this.#open_groups.forEach((open, index) => crumb(open.group.label, index + 1, open.group));
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
/**
|
|
1285
|
+
* The field that renames the group being edited.
|
|
1286
|
+
*
|
|
1287
|
+
* What it changes is the *label* — what a person reads. The group's name is an address: it is
|
|
1288
|
+
* what a saved graph holds and what a node is resolved by, and renaming that would mean
|
|
1289
|
+
* rewriting every node that used it. Groups an author makes are numbered rather than named, and
|
|
1290
|
+
* this is what makes that all right.
|
|
1291
|
+
*
|
|
1292
|
+
* Committed when the field is left or Enter is pressed, not per keystroke: a rename that fires
|
|
1293
|
+
* on every letter would put a dozen entries on the undo timeline for one edit.
|
|
1294
|
+
*
|
|
1295
|
+
* @param {import("../../../../src/shade/renderer/particles/graph/ParticleNodeGroup.js").ParticleNodeGroup} group
|
|
1296
|
+
* @returns {View}
|
|
1297
|
+
*/
|
|
1298
|
+
#buildGroupNameField(group) {
|
|
1299
|
+
const field = document.createElement('input');
|
|
1300
|
+
|
|
1301
|
+
field.setAttribute('type', 'text');
|
|
1302
|
+
field.setAttribute('spellcheck', 'false');
|
|
1303
|
+
field.classList.add('name', 'rename');
|
|
1304
|
+
field.value = group.label;
|
|
1305
|
+
|
|
1306
|
+
field.addEventListener('change', () => this.renameGroup(group, field.value));
|
|
1307
|
+
|
|
1308
|
+
field.addEventListener('keydown', event => {
|
|
1309
|
+
if (event.key === "Enter") {
|
|
1310
|
+
field.blur();
|
|
1311
|
+
}
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
// typing a name is not a graph shortcut, and Delete in here must not delete the selection
|
|
1315
|
+
field.addEventListener('keydown', event => event.stopPropagation());
|
|
1316
|
+
|
|
1317
|
+
return new EmptyView({ el: field });
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
/**
|
|
1321
|
+
* Call a group something else.
|
|
1322
|
+
*
|
|
1323
|
+
* Reaches every node already on screen that is an instance of it: a canvas is not rebuilt for a
|
|
1324
|
+
* word, and one left showing the old name would be the only stale thing in the window.
|
|
1325
|
+
*
|
|
1326
|
+
* @param {import("../../../../src/shade/renderer/particles/graph/ParticleNodeGroup.js").ParticleNodeGroup} group
|
|
1327
|
+
* @param {string} label
|
|
1328
|
+
* @returns {void}
|
|
1329
|
+
*/
|
|
1330
|
+
renameGroup(group, label) {
|
|
1331
|
+
const wanted = label.trim();
|
|
1332
|
+
|
|
1333
|
+
if (wanted === "" || wanted === group.label) {
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
group.label = wanted;
|
|
1338
|
+
group.description.display_label = wanted;
|
|
1339
|
+
|
|
1340
|
+
for (const canvas of [...this.#phases.map(phase => phase.editor), ...this.#open_groups.map(open => open.editor)]) {
|
|
1341
|
+
for (const nodeView of canvas.graphView.nodeViews.values()) {
|
|
1342
|
+
if (nodeView.node.description === group.description) {
|
|
1343
|
+
nodeView.refreshLabel();
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
this.effect.touch();
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
/**
|
|
1352
|
+
* Rebuild the problems strip from a fresh compile of the effect.
|
|
1353
|
+
*
|
|
1354
|
+
* Everything is listed, whichever phase it belongs to, because a problem in the phase an author
|
|
1355
|
+
* is not looking at is exactly the one they would otherwise hunt for. Nodes carrying a problem
|
|
1356
|
+
* are marked in the graph as well.
|
|
1357
|
+
*/
|
|
1358
|
+
refreshProblems() {
|
|
1359
|
+
// The graph's own problems, not the running program's: with the code layer authoritative the
|
|
1360
|
+
// graphs are not compiled at all, and a panel that then said "no problems" about a graph
|
|
1361
|
+
// with a dangling input in plain sight would be lying about the thing it is showing.
|
|
1362
|
+
const diagnostics = this.effect.graphDiagnostics();
|
|
1363
|
+
|
|
1364
|
+
this.#diagnostics = diagnostics;
|
|
1365
|
+
|
|
1366
|
+
this.#problems.removeAllChildren();
|
|
1367
|
+
|
|
1368
|
+
if (diagnostics.length === 0) {
|
|
1369
|
+
this.#problems.addChild(new LabelView("No problems.", { classList: ['none'] }));
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
for (const diagnostic of diagnostics) {
|
|
1373
|
+
const entry = new EmptyView({ classList: ['problem', diagnostic.severity] });
|
|
1374
|
+
|
|
1375
|
+
const where = diagnostic.node >= 0
|
|
1376
|
+
? `${diagnostic.scope}: node ${diagnostic.node}`
|
|
1377
|
+
: diagnostic.scope;
|
|
1378
|
+
|
|
1379
|
+
entry.addChild(new LabelView(where, { classList: ['where'] }));
|
|
1380
|
+
entry.addChild(new LabelView(diagnostic.message, { classList: ['message'] }));
|
|
1381
|
+
|
|
1382
|
+
if (diagnostic.node >= 0) {
|
|
1383
|
+
entry.el.addEventListener('click', () => this.revealNode(diagnostic.scope, diagnostic.node));
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
this.#problems.addChild(entry);
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
this.#markProblemNodes(diagnostics);
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
/**
|
|
1393
|
+
* Put a class on the view of every node a diagnostic names, so the graph shows where the
|
|
1394
|
+
* problems are and not only the list below it.
|
|
1395
|
+
*
|
|
1396
|
+
* @param {Array<{scope: string, node: number, severity: string}>} diagnostics
|
|
1397
|
+
*/
|
|
1398
|
+
#markProblemNodes(diagnostics) {
|
|
1399
|
+
for (const phase of this.#phases) {
|
|
1400
|
+
for (const node_id of phase.editor.graphView.nodeViews.keys()) {
|
|
1401
|
+
this.#markNode(phase.id, node_id, diagnostics);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
/**
|
|
1407
|
+
* Mark one node from the diagnostics in hand.
|
|
1408
|
+
*
|
|
1409
|
+
* @param {string} phase_id
|
|
1410
|
+
* @param {number} node_id
|
|
1411
|
+
* @param {Array<{scope: string, node: number, severity: string}>} [diagnostics]
|
|
1412
|
+
*/
|
|
1413
|
+
#markNode(phase_id, node_id, diagnostics = this.#diagnostics) {
|
|
1414
|
+
const phase = this.#phases.find(candidate => candidate.id === phase_id);
|
|
1415
|
+
const nodeView = phase?.editor.graphView.nodeViews.get(node_id);
|
|
1416
|
+
|
|
1417
|
+
if (nodeView === undefined) {
|
|
1418
|
+
return;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
let severity;
|
|
1422
|
+
|
|
1423
|
+
for (const diagnostic of diagnostics) {
|
|
1424
|
+
if (diagnostic.scope !== phase_id || diagnostic.node !== node_id) {
|
|
1425
|
+
continue;
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
// an error outranks a warning on the same node
|
|
1429
|
+
if (diagnostic.severity === "error" || severity === undefined) {
|
|
1430
|
+
severity = diagnostic.severity;
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
nodeView.setClass('has-error', severity === "error");
|
|
1435
|
+
nodeView.setClass('has-warning', severity === "warning");
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
/**
|
|
1439
|
+
* Switch to the phase a node is in and centre the view on it.
|
|
1440
|
+
*
|
|
1441
|
+
* @param {string} phase_id `"init"` or `"update"`
|
|
1442
|
+
* @param {number} node_id
|
|
1443
|
+
*/
|
|
1444
|
+
revealNode(phase_id, node_id) {
|
|
1445
|
+
const phase = this.#phases.find(candidate => candidate.id === phase_id);
|
|
1446
|
+
|
|
1447
|
+
if (phase === undefined) {
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
this.setPhase(phase_id);
|
|
1452
|
+
|
|
1453
|
+
const visual = this.effect[`${phase_id}_visual`].getNode(node_id);
|
|
1454
|
+
|
|
1455
|
+
if (visual === undefined) {
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
const camera = phase.editor.camera;
|
|
1460
|
+
const scale = camera.scale.getValue();
|
|
1461
|
+
|
|
1462
|
+
camera.position.set(
|
|
1463
|
+
visual.dimensions.position.x + visual.dimensions.size.x / 2 - (phase.editor.size.x / 2) / scale,
|
|
1464
|
+
visual.dimensions.position.y + visual.dimensions.size.y / 2 - (phase.editor.size.y / 2) / scale
|
|
1465
|
+
);
|
|
1466
|
+
|
|
1467
|
+
phase.editor.selection.nodes.reset();
|
|
1468
|
+
phase.editor.selection.nodes.add(node_id);
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
/**
|
|
1472
|
+
* The add-node menu of the canvas currently on screen — a test's way in, and a host's way to
|
|
1473
|
+
* open it from a menu of its own.
|
|
1474
|
+
*
|
|
1475
|
+
* @returns {import("../../node-graph/NodeGraphSearchMenuView.js").NodeGraphSearchMenuView|null}
|
|
1476
|
+
*/
|
|
1477
|
+
searchMenu() {
|
|
1478
|
+
return this.activeEditor().searchMenu;
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
layout() {
|
|
1482
|
+
const width = this.size.x;
|
|
1483
|
+
const height = this.size.y;
|
|
1484
|
+
|
|
1485
|
+
const body_height = Math.max(0, height - TOOLBAR_HEIGHT - PROBLEMS_HEIGHT);
|
|
1486
|
+
|
|
1487
|
+
this.#toolbar.position.set(0, 0);
|
|
1488
|
+
this.#toolbar.size.set(width, TOOLBAR_HEIGHT);
|
|
1489
|
+
|
|
1490
|
+
this.#body.position.set(0, TOOLBAR_HEIGHT);
|
|
1491
|
+
this.#body.size.set(width, body_height);
|
|
1492
|
+
|
|
1493
|
+
// Every canvas takes the whole body — the phases, and any group opened over them — because
|
|
1494
|
+
// exactly one of them is visible at a time.
|
|
1495
|
+
for (const canvas of [...this.#phases.map(phase => phase.editor), ...this.#open_groups.map(open => open.editor)]) {
|
|
1496
|
+
canvas.position.set(0, 0);
|
|
1497
|
+
canvas.size.set(width, body_height);
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
this.#problems.position.set(0, TOOLBAR_HEIGHT + body_height);
|
|
1501
|
+
this.#problems.size.set(width, PROBLEMS_HEIGHT);
|
|
1502
|
+
|
|
1503
|
+
this.#frameIfNeeded();
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
/**
|
|
1507
|
+
* Point the camera of any phase arranged for the first time at what the arranging produced.
|
|
1508
|
+
*
|
|
1509
|
+
* Two things have to have happened, and they arrive in either order — hence trying from both
|
|
1510
|
+
* ends. The editor needs a size, or there is no viewport to fit a graph into. And its
|
|
1511
|
+
* arrangement has to be the final one: a graph arranged before its nodes were drawn is arranged
|
|
1512
|
+
* again from their real sizes, and a camera aimed at the first attempt is aimed at where the
|
|
1513
|
+
* nodes were about to stop being.
|
|
1514
|
+
*/
|
|
1515
|
+
#frameIfNeeded() {
|
|
1516
|
+
for (const phase of this.#phases) {
|
|
1517
|
+
if (!this.#needs_framing.has(phase.id)) {
|
|
1518
|
+
continue;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
const editor = phase.editor;
|
|
1522
|
+
|
|
1523
|
+
if (editor.size.x <= 0 || editor.size.y <= 0) {
|
|
1524
|
+
continue;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
if (editor.isArrangementProvisional()) {
|
|
1528
|
+
continue;
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
this.#needs_framing.delete(phase.id);
|
|
1532
|
+
|
|
1533
|
+
editor.cameraContainAll();
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
/**
|
|
1539
|
+
* The glyph a group made out of a selection carries: a box with a smaller box inside it, which is
|
|
1540
|
+
* what one is.
|
|
1541
|
+
*
|
|
1542
|
+
* @type {string}
|
|
1543
|
+
*/
|
|
1544
|
+
const PARTICLE_GROUP_ICON = "M4 5h16v14H4z M9 10h6v4H9z";
|
|
1545
|
+
|
|
1546
|
+
/**
|
|
1547
|
+
* The middle of a set of nodes, in graph space, so a group lands where the thing it replaced was.
|
|
1548
|
+
*
|
|
1549
|
+
* @param {NodeGraphVisualData} visual
|
|
1550
|
+
* @param {number[]} nodes
|
|
1551
|
+
* @returns {{x: number, y: number}}
|
|
1552
|
+
*/
|
|
1553
|
+
function centroid(visual, nodes) {
|
|
1554
|
+
let x = 0;
|
|
1555
|
+
let y = 0;
|
|
1556
|
+
let counted = 0;
|
|
1557
|
+
|
|
1558
|
+
for (const id of nodes) {
|
|
1559
|
+
const placed = visual.getNode(id);
|
|
1560
|
+
|
|
1561
|
+
if (placed === undefined) {
|
|
1562
|
+
continue;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
x += placed.dimensions.position.x;
|
|
1566
|
+
y += placed.dimensions.position.y;
|
|
1567
|
+
|
|
1568
|
+
counted++;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
return counted === 0 ? { x: 0, y: 0 } : { x: x / counted, y: y / counted };
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
/**
|
|
1575
|
+
* The top-left corner of a group's body, so what comes out of an ungrouping keeps its arrangement
|
|
1576
|
+
* relative to where the group node was rather than jumping to wherever the body happens to sit.
|
|
1577
|
+
*
|
|
1578
|
+
* @param {import("../../../../src/shade/renderer/particles/graph/ParticleNodeGroup.js").ParticleNodeGroup} group
|
|
1579
|
+
* @returns {{x: number, y: number}}
|
|
1580
|
+
*/
|
|
1581
|
+
function body_origin(group) {
|
|
1582
|
+
let x = Infinity;
|
|
1583
|
+
let y = Infinity;
|
|
1584
|
+
|
|
1585
|
+
for (const node of group.graph.getNodes()) {
|
|
1586
|
+
const placed = group.visual.getNode(node.id);
|
|
1587
|
+
|
|
1588
|
+
if (placed === undefined) {
|
|
1589
|
+
continue;
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
x = Math.min(x, placed.dimensions.position.x);
|
|
1593
|
+
y = Math.min(y, placed.dimensions.position.y);
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
return Number.isFinite(x) ? { x, y } : { x: 0, y: 0 };
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
/**
|
|
1600
|
+
* A node's parameters, deep enough that the copy and the original cannot write through to each
|
|
1601
|
+
* other — an ungrouped node must not share an array with the group's definition.
|
|
1602
|
+
*
|
|
1603
|
+
* @param {object} parameters
|
|
1604
|
+
* @returns {object}
|
|
1605
|
+
*/
|
|
1606
|
+
function clone_parameters(parameters) {
|
|
1607
|
+
const result = {};
|
|
1608
|
+
|
|
1609
|
+
for (const key of Object.keys(parameters)) {
|
|
1610
|
+
const value = parameters[key];
|
|
1611
|
+
|
|
1612
|
+
result[key] = Array.isArray(value) ? value.slice() : value;
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
return result;
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
/**
|
|
1619
|
+
* @param {string} type node type name
|
|
1620
|
+
* @returns {string}
|
|
1621
|
+
*/
|
|
1622
|
+
function category_of(type) {
|
|
1623
|
+
const index = category_rank(type);
|
|
1624
|
+
|
|
1625
|
+
return index === PALETTE_CATEGORIES.length ? "other" : PALETTE_CATEGORIES[index].name;
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
/**
|
|
1629
|
+
* @param {string} type node type name
|
|
1630
|
+
* @returns {number} index into {@link PALETTE_CATEGORIES}, or its length for an uncategorised type
|
|
1631
|
+
*/
|
|
1632
|
+
function category_rank(type) {
|
|
1633
|
+
const index = PALETTE_CATEGORIES.findIndex(candidate => candidate.types.includes(type));
|
|
1634
|
+
|
|
1635
|
+
return index === -1 ? PALETTE_CATEGORIES.length : index;
|
|
1636
|
+
}
|