@nice2dev/ui-3d 1.1.0 → 1.1.1
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/dist/cjs/collaborative/collaborativeScene.js +16 -8
- package/dist/cjs/collaborative/collaborativeScene.js.map +1 -1
- package/dist/cjs/core/i18n.js +3 -3
- package/dist/cjs/dance/DanceBridge.js +65 -30
- package/dist/cjs/dance/DanceBridge.js.map +1 -1
- package/dist/cjs/dance/DanceScoreEngine.js +37 -23
- package/dist/cjs/dance/DanceScoreEngine.js.map +1 -1
- package/dist/cjs/dance/PoseDetector.js +92 -49
- package/dist/cjs/dance/PoseDetector.js.map +1 -1
- package/dist/cjs/material/NiceMaterialEditor.js +134 -91
- package/dist/cjs/material/NiceMaterialEditor.js.map +1 -1
- package/dist/cjs/material/materialEditorUtils.js +86 -55
- package/dist/cjs/material/materialEditorUtils.js.map +1 -1
- package/dist/cjs/model/ModelEditor.js +16 -14
- package/dist/cjs/model/ModelEditor.js.map +1 -1
- package/dist/cjs/model/ModelEditorLeftPanel.js +21 -15
- package/dist/cjs/model/ModelEditorLeftPanel.js.map +1 -1
- package/dist/cjs/model/ModelEditorMenuBar.js +23 -10
- package/dist/cjs/model/ModelEditorMenuBar.js.map +1 -1
- package/dist/cjs/model/ModelEditorRightPanel.js +32 -27
- package/dist/cjs/model/ModelEditorRightPanel.js.map +1 -1
- package/dist/cjs/model/ModelEditorSubComponents.js +20 -20
- package/dist/cjs/model/ModelEditorSubComponents.js.map +1 -1
- package/dist/cjs/model/ModelEditorTimeline.js +12 -11
- package/dist/cjs/model/ModelEditorTimeline.js.map +1 -1
- package/dist/cjs/model/ModelEditorToolbar.js +5 -5
- package/dist/cjs/model/ModelEditorToolbar.js.map +1 -1
- package/dist/cjs/model/ModelEditorViewport.js +12 -8
- package/dist/cjs/model/ModelEditorViewport.js.map +1 -1
- package/dist/cjs/model/ModelViewer.js +16 -15
- package/dist/cjs/model/ModelViewer.js.map +1 -1
- package/dist/cjs/model/NiceArmatureEditor.js +53 -30
- package/dist/cjs/model/NiceArmatureEditor.js.map +1 -1
- package/dist/cjs/model/NiceMorphTargetEditor.js +51 -30
- package/dist/cjs/model/NiceMorphTargetEditor.js.map +1 -1
- package/dist/cjs/model/NiceOctree.js +62 -37
- package/dist/cjs/model/NiceOctree.js.map +1 -1
- package/dist/cjs/model/NicePhysicsSimulation.js +74 -36
- package/dist/cjs/model/NicePhysicsSimulation.js.map +1 -1
- package/dist/cjs/model/NiceProceduralGeometry.js +101 -31
- package/dist/cjs/model/NiceProceduralGeometry.js.map +1 -1
- package/dist/cjs/model/NiceTerrainEditor.js +88 -38
- package/dist/cjs/model/NiceTerrainEditor.js.map +1 -1
- package/dist/cjs/model/NiceWeightPainter.js +46 -24
- package/dist/cjs/model/NiceWeightPainter.js.map +1 -1
- package/dist/cjs/model/NiceXRPreview.js +89 -43
- package/dist/cjs/model/NiceXRPreview.js.map +1 -1
- package/dist/cjs/model/cadModeUtils.js +2 -2
- package/dist/cjs/model/cadModeUtils.js.map +1 -1
- package/dist/cjs/model/editorShortcuts.js +69 -35
- package/dist/cjs/model/editorShortcuts.js.map +1 -1
- package/dist/cjs/model/modelEditorTypes.js +56 -52
- package/dist/cjs/model/modelEditorTypes.js.map +1 -1
- package/dist/cjs/model/modelEditorUtils.js +356 -131
- package/dist/cjs/model/modelEditorUtils.js.map +1 -1
- package/dist/cjs/model/simsModeUtils.js +819 -74
- package/dist/cjs/model/simsModeUtils.js.map +1 -1
- package/dist/cjs/model/useModelEditor.js +501 -394
- package/dist/cjs/model/useModelEditor.js.map +1 -1
- package/dist/cjs/model/useModelViewer.js +117 -86
- package/dist/cjs/model/useModelViewer.js.map +1 -1
- package/dist/cjs/particle/NiceParticleEditor.js +126 -69
- package/dist/cjs/particle/NiceParticleEditor.js.map +1 -1
- package/dist/cjs/particle/particleEditorUtils.js +64 -27
- package/dist/cjs/particle/particleEditorUtils.js.map +1 -1
- package/dist/cjs/rendering/NiceCascadedShadows.js +35 -23
- package/dist/cjs/rendering/NiceCascadedShadows.js.map +1 -1
- package/dist/cjs/rendering/NiceRenderExport.js +26 -12
- package/dist/cjs/rendering/NiceRenderExport.js.map +1 -1
- package/dist/cjs/rendering/NiceSSAO.js +28 -23
- package/dist/cjs/rendering/NiceSSAO.js.map +1 -1
- package/dist/cjs/rendering/NiceSSR.js +22 -18
- package/dist/cjs/rendering/NiceSSR.js.map +1 -1
- package/dist/cjs/rendering/NiceWebGPURenderer.js +60 -37
- package/dist/cjs/rendering/NiceWebGPURenderer.js.map +1 -1
- package/dist/cjs/ui/dist/NiceErrorBoundary-D75yr_WE.js +572 -0
- package/dist/cjs/ui/dist/NiceErrorBoundary-D75yr_WE.js.map +1 -0
- package/dist/cjs/ui/dist/NiceForm-CKoJA6NG.js +4979 -0
- package/dist/cjs/ui/dist/NiceForm-CKoJA6NG.js.map +1 -0
- package/dist/cjs/ui/dist/NiceModal-1QX7NhyH.js +98 -0
- package/dist/cjs/ui/dist/NiceModal-1QX7NhyH.js.map +1 -0
- package/dist/cjs/ui/dist/NiceModuleLifecyclePanel-CMrPbSw9.js +5244 -0
- package/dist/cjs/ui/dist/NiceModuleLifecyclePanel-CMrPbSw9.js.map +1 -0
- package/dist/cjs/ui/dist/NicePagination-DbE-uOkl.js +174 -0
- package/dist/cjs/ui/dist/NicePagination-DbE-uOkl.js.map +1 -0
- package/dist/cjs/ui/dist/NicePinCodeInput-B8cI8zu8.js +10459 -0
- package/dist/cjs/ui/dist/NicePinCodeInput-B8cI8zu8.js.map +1 -0
- package/dist/cjs/ui/dist/NiceSavedQueryPanel-CZ5bYOe_.js +6313 -0
- package/dist/cjs/ui/dist/NiceSavedQueryPanel-CZ5bYOe_.js.map +1 -0
- package/dist/cjs/ui/dist/NiceTabs-DzLHzwuA.js +1639 -0
- package/dist/cjs/ui/dist/NiceTabs-DzLHzwuA.js.map +1 -0
- package/dist/cjs/ui/dist/NiceWindow-DeC83yfE.js +807 -0
- package/dist/cjs/ui/dist/NiceWindow-DeC83yfE.js.map +1 -0
- package/dist/cjs/ui/dist/charts-BAS21jkC.js +3833 -0
- package/dist/cjs/ui/dist/charts-BAS21jkC.js.map +1 -0
- package/dist/cjs/ui/dist/core-Da43q-pO.js +11683 -0
- package/dist/cjs/ui/dist/core-Da43q-pO.js.map +1 -0
- package/dist/cjs/ui/dist/index-pAuGTbuB.js +28876 -0
- package/dist/cjs/ui/dist/index-pAuGTbuB.js.map +1 -0
- package/dist/cjs/uv/NiceUVEditor.js +115 -86
- package/dist/cjs/uv/NiceUVEditor.js.map +1 -1
- package/dist/cjs/uv/uvEditorUtils.js +41 -23
- package/dist/cjs/uv/uvEditorUtils.js.map +1 -1
- package/dist/esm/collaborative/collaborativeScene.js +16 -8
- package/dist/esm/collaborative/collaborativeScene.js.map +1 -1
- package/dist/esm/dance/DanceBridge.js +55 -20
- package/dist/esm/dance/DanceBridge.js.map +1 -1
- package/dist/esm/dance/DanceScoreEngine.js +29 -15
- package/dist/esm/dance/DanceScoreEngine.js.map +1 -1
- package/dist/esm/dance/PoseDetector.js +73 -30
- package/dist/esm/dance/PoseDetector.js.map +1 -1
- package/dist/esm/material/NiceMaterialEditor.js +115 -72
- package/dist/esm/material/NiceMaterialEditor.js.map +1 -1
- package/dist/esm/material/materialEditorUtils.js +86 -55
- package/dist/esm/material/materialEditorUtils.js.map +1 -1
- package/dist/esm/model/ModelEditor.js +16 -14
- package/dist/esm/model/ModelEditor.js.map +1 -1
- package/dist/esm/model/ModelEditorLeftPanel.js +21 -15
- package/dist/esm/model/ModelEditorLeftPanel.js.map +1 -1
- package/dist/esm/model/ModelEditorMenuBar.js +23 -10
- package/dist/esm/model/ModelEditorMenuBar.js.map +1 -1
- package/dist/esm/model/ModelEditorRightPanel.js +32 -27
- package/dist/esm/model/ModelEditorRightPanel.js.map +1 -1
- package/dist/esm/model/ModelEditorSubComponents.js +17 -17
- package/dist/esm/model/ModelEditorSubComponents.js.map +1 -1
- package/dist/esm/model/ModelEditorTimeline.js +12 -11
- package/dist/esm/model/ModelEditorTimeline.js.map +1 -1
- package/dist/esm/model/ModelEditorToolbar.js +5 -5
- package/dist/esm/model/ModelEditorToolbar.js.map +1 -1
- package/dist/esm/model/ModelEditorViewport.js +12 -8
- package/dist/esm/model/ModelEditorViewport.js.map +1 -1
- package/dist/esm/model/ModelViewer.js +8 -7
- package/dist/esm/model/ModelViewer.js.map +1 -1
- package/dist/esm/model/NiceArmatureEditor.js +36 -13
- package/dist/esm/model/NiceArmatureEditor.js.map +1 -1
- package/dist/esm/model/NiceMorphTargetEditor.js +34 -13
- package/dist/esm/model/NiceMorphTargetEditor.js.map +1 -1
- package/dist/esm/model/NiceOctree.js +47 -22
- package/dist/esm/model/NiceOctree.js.map +1 -1
- package/dist/esm/model/NicePhysicsSimulation.js +51 -13
- package/dist/esm/model/NicePhysicsSimulation.js.map +1 -1
- package/dist/esm/model/NiceProceduralGeometry.js +94 -24
- package/dist/esm/model/NiceProceduralGeometry.js.map +1 -1
- package/dist/esm/model/NiceTerrainEditor.js +70 -20
- package/dist/esm/model/NiceTerrainEditor.js.map +1 -1
- package/dist/esm/model/NiceWeightPainter.js +33 -11
- package/dist/esm/model/NiceWeightPainter.js.map +1 -1
- package/dist/esm/model/NiceXRPreview.js +72 -26
- package/dist/esm/model/NiceXRPreview.js.map +1 -1
- package/dist/esm/model/cadModeUtils.js +2 -2
- package/dist/esm/model/cadModeUtils.js.map +1 -1
- package/dist/esm/model/editorShortcuts.js +69 -35
- package/dist/esm/model/editorShortcuts.js.map +1 -1
- package/dist/esm/model/modelEditorTypes.js +56 -52
- package/dist/esm/model/modelEditorTypes.js.map +1 -1
- package/dist/esm/model/modelEditorUtils.js +356 -131
- package/dist/esm/model/modelEditorUtils.js.map +1 -1
- package/dist/esm/model/simsModeUtils.js +819 -74
- package/dist/esm/model/simsModeUtils.js.map +1 -1
- package/dist/esm/model/useModelEditor.js +384 -277
- package/dist/esm/model/useModelEditor.js.map +1 -1
- package/dist/esm/model/useModelViewer.js +56 -25
- package/dist/esm/model/useModelViewer.js.map +1 -1
- package/dist/esm/particle/NiceParticleEditor.js +116 -59
- package/dist/esm/particle/NiceParticleEditor.js.map +1 -1
- package/dist/esm/particle/particleEditorUtils.js +64 -27
- package/dist/esm/particle/particleEditorUtils.js.map +1 -1
- package/dist/esm/rendering/NiceCascadedShadows.js +24 -12
- package/dist/esm/rendering/NiceCascadedShadows.js.map +1 -1
- package/dist/esm/rendering/NiceRenderExport.js +22 -8
- package/dist/esm/rendering/NiceRenderExport.js.map +1 -1
- package/dist/esm/rendering/NiceSSAO.js +11 -6
- package/dist/esm/rendering/NiceSSAO.js.map +1 -1
- package/dist/esm/rendering/NiceSSR.js +9 -5
- package/dist/esm/rendering/NiceSSR.js.map +1 -1
- package/dist/esm/rendering/NiceWebGPURenderer.js +40 -17
- package/dist/esm/rendering/NiceWebGPURenderer.js.map +1 -1
- package/dist/esm/ui/dist/NiceErrorBoundary-D75yr_WE.js +563 -0
- package/dist/esm/ui/dist/NiceErrorBoundary-D75yr_WE.js.map +1 -0
- package/dist/esm/ui/dist/NiceForm-CKoJA6NG.js +4960 -0
- package/dist/esm/ui/dist/NiceForm-CKoJA6NG.js.map +1 -0
- package/dist/esm/ui/dist/NiceModal-1QX7NhyH.js +96 -0
- package/dist/esm/ui/dist/NiceModal-1QX7NhyH.js.map +1 -0
- package/dist/esm/ui/dist/NiceModuleLifecyclePanel-CMrPbSw9.js +5201 -0
- package/dist/esm/ui/dist/NiceModuleLifecyclePanel-CMrPbSw9.js.map +1 -0
- package/dist/esm/ui/dist/NicePagination-DbE-uOkl.js +172 -0
- package/dist/esm/ui/dist/NicePagination-DbE-uOkl.js.map +1 -0
- package/dist/esm/ui/dist/NicePinCodeInput-B8cI8zu8.js +10408 -0
- package/dist/esm/ui/dist/NicePinCodeInput-B8cI8zu8.js.map +1 -0
- package/dist/esm/ui/dist/NiceSavedQueryPanel-CZ5bYOe_.js +6296 -0
- package/dist/esm/ui/dist/NiceSavedQueryPanel-CZ5bYOe_.js.map +1 -0
- package/dist/esm/ui/dist/NiceTabs-DzLHzwuA.js +1630 -0
- package/dist/esm/ui/dist/NiceTabs-DzLHzwuA.js.map +1 -0
- package/dist/esm/ui/dist/NiceWindow-DeC83yfE.js +796 -0
- package/dist/esm/ui/dist/NiceWindow-DeC83yfE.js.map +1 -0
- package/dist/esm/ui/dist/charts-BAS21jkC.js +3809 -0
- package/dist/esm/ui/dist/charts-BAS21jkC.js.map +1 -0
- package/dist/esm/ui/dist/core-Da43q-pO.js +11502 -0
- package/dist/esm/ui/dist/core-Da43q-pO.js.map +1 -0
- package/dist/esm/ui/dist/index-pAuGTbuB.js +28792 -0
- package/dist/esm/ui/dist/index-pAuGTbuB.js.map +1 -0
- package/dist/esm/uv/NiceUVEditor.js +91 -62
- package/dist/esm/uv/NiceUVEditor.js.map +1 -1
- package/dist/esm/uv/uvEditorUtils.js +41 -23
- package/dist/esm/uv/uvEditorUtils.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { useRef, useState, useMemo, useEffect, useCallback, useReducer } from 'react';
|
|
3
|
+
import styles from './MaterialEditor.module.css.js';
|
|
3
4
|
import { MAX_ZOOM, MIN_ZOOM, NODE_CATEGORY_COLORS, NODE_CATEGORY_NAMES, NODE_WIDTH, NODE_HEADER_HEIGHT, PORT_HEIGHT, PORT_TYPE_COLORS, DEFAULT_GRID_SIZE } from './materialEditorTypes.js';
|
|
4
|
-
import { MATERIAL_NODE_MAP, searchNodes, MATERIAL_NODE_DEFINITIONS } from './materialNodeDefinitions.js';
|
|
5
5
|
import { compileShader, importGraphFromJSON, exportGraphToJSON, screenToGraph, updateNodeInGraph, calculateZoomToFit, cloneNode, generateNodeId, removeNodesFromGraph, removeConnectionFromGraph, getNodeDefinition, canConnect, createConnection, addConnectionToGraph, moveNodesInGraph, createNode, addNodeToGraph, createEmptyGraph } from './materialEditorUtils.js';
|
|
6
|
-
import
|
|
6
|
+
import { MATERIAL_NODE_MAP, searchNodes, MATERIAL_NODE_DEFINITIONS } from './materialNodeDefinitions.js';
|
|
7
7
|
|
|
8
8
|
/* ═══════════════════════════════════════════════════════════════════════════
|
|
9
9
|
State Reducer
|
|
@@ -41,8 +41,9 @@ function materialEditorReducer(state, action) {
|
|
|
41
41
|
switch (action.type) {
|
|
42
42
|
case 'ADD_NODE': {
|
|
43
43
|
const node = createNode(action.nodeType, action.position);
|
|
44
|
-
if (!node)
|
|
44
|
+
if (!node) {
|
|
45
45
|
return state;
|
|
46
|
+
}
|
|
46
47
|
const newState = pushToUndoStack(state);
|
|
47
48
|
return {
|
|
48
49
|
...newState,
|
|
@@ -67,9 +68,10 @@ function materialEditorReducer(state, action) {
|
|
|
67
68
|
}
|
|
68
69
|
case 'UPDATE_NODE_PROPERTY': {
|
|
69
70
|
const newState = pushToUndoStack(state);
|
|
70
|
-
const node = newState.graph.nodes.find(n => n.id === action.nodeId);
|
|
71
|
-
if (!node)
|
|
71
|
+
const node = newState.graph.nodes.find((n) => n.id === action.nodeId);
|
|
72
|
+
if (!node) {
|
|
72
73
|
return state;
|
|
74
|
+
}
|
|
73
75
|
return {
|
|
74
76
|
...newState,
|
|
75
77
|
graph: updateNodeInGraph(newState.graph, action.nodeId, {
|
|
@@ -79,9 +81,10 @@ function materialEditorReducer(state, action) {
|
|
|
79
81
|
}
|
|
80
82
|
case 'UPDATE_INPUT_VALUE': {
|
|
81
83
|
const newState = pushToUndoStack(state);
|
|
82
|
-
const node = newState.graph.nodes.find(n => n.id === action.nodeId);
|
|
83
|
-
if (!node)
|
|
84
|
+
const node = newState.graph.nodes.find((n) => n.id === action.nodeId);
|
|
85
|
+
if (!node) {
|
|
84
86
|
return state;
|
|
87
|
+
}
|
|
85
88
|
return {
|
|
86
89
|
...newState,
|
|
87
90
|
graph: updateNodeInGraph(newState.graph, action.nodeId, {
|
|
@@ -90,21 +93,25 @@ function materialEditorReducer(state, action) {
|
|
|
90
93
|
};
|
|
91
94
|
}
|
|
92
95
|
case 'CONNECT': {
|
|
93
|
-
const sourceNode = state.graph.nodes.find(n => n.id === action.connection.sourceNodeId);
|
|
94
|
-
const targetNode = state.graph.nodes.find(n => n.id === action.connection.targetNodeId);
|
|
95
|
-
if (!sourceNode || !targetNode)
|
|
96
|
+
const sourceNode = state.graph.nodes.find((n) => n.id === action.connection.sourceNodeId);
|
|
97
|
+
const targetNode = state.graph.nodes.find((n) => n.id === action.connection.targetNodeId);
|
|
98
|
+
if (!sourceNode || !targetNode) {
|
|
96
99
|
return state;
|
|
100
|
+
}
|
|
97
101
|
const sourceDef = getNodeDefinition(sourceNode);
|
|
98
102
|
const targetDef = getNodeDefinition(targetNode);
|
|
99
|
-
if (!sourceDef || !targetDef)
|
|
103
|
+
if (!sourceDef || !targetDef) {
|
|
100
104
|
return state;
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
105
|
+
}
|
|
106
|
+
const sourcePort = sourceDef.outputs.find((p) => p.id === action.connection.sourcePortId);
|
|
107
|
+
const targetPort = targetDef.inputs.find((p) => p.id === action.connection.targetPortId);
|
|
108
|
+
if (!sourcePort || !targetPort) {
|
|
104
109
|
return state;
|
|
110
|
+
}
|
|
105
111
|
const { valid } = canConnect(sourceNode, sourcePort, targetNode, targetPort, state.graph.connections);
|
|
106
|
-
if (!valid)
|
|
112
|
+
if (!valid) {
|
|
107
113
|
return state;
|
|
114
|
+
}
|
|
108
115
|
const newState = pushToUndoStack(state);
|
|
109
116
|
const connection = createConnection(action.connection.sourceNodeId, action.connection.sourcePortId, action.connection.targetNodeId, action.connection.targetPortId);
|
|
110
117
|
return {
|
|
@@ -117,7 +124,7 @@ function materialEditorReducer(state, action) {
|
|
|
117
124
|
return {
|
|
118
125
|
...newState,
|
|
119
126
|
graph: removeConnectionFromGraph(newState.graph, action.connectionId),
|
|
120
|
-
selectedConnections: state.selectedConnections.filter(id => id !== action.connectionId),
|
|
127
|
+
selectedConnections: state.selectedConnections.filter((id) => id !== action.connectionId),
|
|
121
128
|
};
|
|
122
129
|
}
|
|
123
130
|
case 'SELECT_NODES': {
|
|
@@ -141,8 +148,8 @@ function materialEditorReducer(state, action) {
|
|
|
141
148
|
case 'SELECT_ALL': {
|
|
142
149
|
return {
|
|
143
150
|
...state,
|
|
144
|
-
selectedNodes: state.graph.nodes.map(n => n.id),
|
|
145
|
-
selectedConnections: state.graph.connections.map(c => c.id),
|
|
151
|
+
selectedNodes: state.graph.nodes.map((n) => n.id),
|
|
152
|
+
selectedConnections: state.graph.connections.map((c) => c.id),
|
|
146
153
|
};
|
|
147
154
|
}
|
|
148
155
|
case 'DESELECT_ALL': {
|
|
@@ -153,19 +160,21 @@ function materialEditorReducer(state, action) {
|
|
|
153
160
|
};
|
|
154
161
|
}
|
|
155
162
|
case 'COPY': {
|
|
156
|
-
const nodesToCopy = state.graph.nodes.filter(n => state.selectedNodes.includes(n.id));
|
|
157
|
-
const connectionsToCopy = state.graph.connections.filter(c => state.selectedNodes.includes(c.sourceNodeId) &&
|
|
163
|
+
const nodesToCopy = state.graph.nodes.filter((n) => state.selectedNodes.includes(n.id));
|
|
164
|
+
const connectionsToCopy = state.graph.connections.filter((c) => state.selectedNodes.includes(c.sourceNodeId) &&
|
|
165
|
+
state.selectedNodes.includes(c.targetNodeId));
|
|
158
166
|
return {
|
|
159
167
|
...state,
|
|
160
168
|
clipboard: { nodes: nodesToCopy, connections: connectionsToCopy },
|
|
161
169
|
};
|
|
162
170
|
}
|
|
163
171
|
case 'PASTE': {
|
|
164
|
-
if (!state.clipboard)
|
|
172
|
+
if (!state.clipboard) {
|
|
165
173
|
return state;
|
|
174
|
+
}
|
|
166
175
|
const newState = pushToUndoStack(state);
|
|
167
176
|
const idMap = new Map();
|
|
168
|
-
const newNodes = state.clipboard.nodes.map(node => {
|
|
177
|
+
const newNodes = state.clipboard.nodes.map((node) => {
|
|
169
178
|
const newId = generateNodeId();
|
|
170
179
|
idMap.set(node.id, newId);
|
|
171
180
|
return {
|
|
@@ -178,7 +187,7 @@ function materialEditorReducer(state, action) {
|
|
|
178
187
|
selected: false,
|
|
179
188
|
};
|
|
180
189
|
});
|
|
181
|
-
const newConnections = state.clipboard.connections.map(conn => ({
|
|
190
|
+
const newConnections = state.clipboard.connections.map((conn) => ({
|
|
182
191
|
...conn,
|
|
183
192
|
id: generateNodeId(),
|
|
184
193
|
sourceNodeId: idMap.get(conn.sourceNodeId),
|
|
@@ -191,31 +200,33 @@ function materialEditorReducer(state, action) {
|
|
|
191
200
|
nodes: [...newState.graph.nodes, ...newNodes],
|
|
192
201
|
connections: [...newState.graph.connections, ...newConnections],
|
|
193
202
|
},
|
|
194
|
-
selectedNodes: newNodes.map(n => n.id),
|
|
203
|
+
selectedNodes: newNodes.map((n) => n.id),
|
|
195
204
|
};
|
|
196
205
|
}
|
|
197
206
|
case 'CUT': {
|
|
198
|
-
const nodesToCut = state.graph.nodes.filter(n => state.selectedNodes.includes(n.id) && n.id !== state.graph.outputNodeId);
|
|
199
|
-
const connectionsToCut = state.graph.connections.filter(c => state.selectedNodes.includes(c.sourceNodeId) &&
|
|
207
|
+
const nodesToCut = state.graph.nodes.filter((n) => state.selectedNodes.includes(n.id) && n.id !== state.graph.outputNodeId);
|
|
208
|
+
const connectionsToCut = state.graph.connections.filter((c) => state.selectedNodes.includes(c.sourceNodeId) &&
|
|
209
|
+
state.selectedNodes.includes(c.targetNodeId));
|
|
200
210
|
const newState = pushToUndoStack(state);
|
|
201
211
|
return {
|
|
202
212
|
...newState,
|
|
203
213
|
clipboard: { nodes: nodesToCut, connections: connectionsToCut },
|
|
204
|
-
graph: removeNodesFromGraph(newState.graph, nodesToCut.map(n => n.id)),
|
|
214
|
+
graph: removeNodesFromGraph(newState.graph, nodesToCut.map((n) => n.id)),
|
|
205
215
|
selectedNodes: [],
|
|
206
216
|
};
|
|
207
217
|
}
|
|
208
218
|
case 'DUPLICATE': {
|
|
209
|
-
const nodesToDupe = state.graph.nodes.filter(n => state.selectedNodes.includes(n.id) && n.id !== state.graph.outputNodeId);
|
|
219
|
+
const nodesToDupe = state.graph.nodes.filter((n) => state.selectedNodes.includes(n.id) && n.id !== state.graph.outputNodeId);
|
|
210
220
|
const newState = pushToUndoStack(state);
|
|
211
221
|
const idMap = new Map();
|
|
212
|
-
const newNodes = nodesToDupe.map(node => {
|
|
222
|
+
const newNodes = nodesToDupe.map((node) => {
|
|
213
223
|
const cloned = cloneNode(node);
|
|
214
224
|
idMap.set(node.id, cloned.id);
|
|
215
225
|
return cloned;
|
|
216
226
|
});
|
|
217
|
-
const connectionsToDupe = state.graph.connections.filter(c => state.selectedNodes.includes(c.sourceNodeId) &&
|
|
218
|
-
|
|
227
|
+
const connectionsToDupe = state.graph.connections.filter((c) => state.selectedNodes.includes(c.sourceNodeId) &&
|
|
228
|
+
state.selectedNodes.includes(c.targetNodeId));
|
|
229
|
+
const newConnections = connectionsToDupe.map((conn) => ({
|
|
219
230
|
...conn,
|
|
220
231
|
id: generateNodeId(),
|
|
221
232
|
sourceNodeId: idMap.get(conn.sourceNodeId),
|
|
@@ -228,12 +239,13 @@ function materialEditorReducer(state, action) {
|
|
|
228
239
|
nodes: [...newState.graph.nodes, ...newNodes],
|
|
229
240
|
connections: [...newState.graph.connections, ...newConnections],
|
|
230
241
|
},
|
|
231
|
-
selectedNodes: newNodes.map(n => n.id),
|
|
242
|
+
selectedNodes: newNodes.map((n) => n.id),
|
|
232
243
|
};
|
|
233
244
|
}
|
|
234
245
|
case 'UNDO': {
|
|
235
|
-
if (state.undoStack.length === 0)
|
|
246
|
+
if (state.undoStack.length === 0) {
|
|
236
247
|
return state;
|
|
248
|
+
}
|
|
237
249
|
const previous = state.undoStack[state.undoStack.length - 1];
|
|
238
250
|
return {
|
|
239
251
|
...state,
|
|
@@ -245,8 +257,9 @@ function materialEditorReducer(state, action) {
|
|
|
245
257
|
};
|
|
246
258
|
}
|
|
247
259
|
case 'REDO': {
|
|
248
|
-
if (state.redoStack.length === 0)
|
|
260
|
+
if (state.redoStack.length === 0) {
|
|
249
261
|
return state;
|
|
262
|
+
}
|
|
250
263
|
const next = state.redoStack[state.redoStack.length - 1];
|
|
251
264
|
return {
|
|
252
265
|
...state,
|
|
@@ -279,7 +292,7 @@ function materialEditorReducer(state, action) {
|
|
|
279
292
|
return {
|
|
280
293
|
...state,
|
|
281
294
|
expandedCategories: state.expandedCategories.includes(action.category)
|
|
282
|
-
? state.expandedCategories.filter(c => c !== action.category)
|
|
295
|
+
? state.expandedCategories.filter((c) => c !== action.category)
|
|
283
296
|
: [...state.expandedCategories, action.category],
|
|
284
297
|
};
|
|
285
298
|
case 'COLLAPSE_NODE':
|
|
@@ -351,9 +364,7 @@ function useMaterialEditor(initialGraph) {
|
|
|
351
364
|
return { state, dispatch };
|
|
352
365
|
}
|
|
353
366
|
function NodePalette({ searchQuery, expandedCategories, onSearch, onToggleCategory, onAddNode, onDragStart, }) {
|
|
354
|
-
const filteredNodes = searchQuery
|
|
355
|
-
? searchNodes(searchQuery)
|
|
356
|
-
: MATERIAL_NODE_DEFINITIONS;
|
|
367
|
+
const filteredNodes = searchQuery ? searchNodes(searchQuery) : MATERIAL_NODE_DEFINITIONS;
|
|
357
368
|
const categories = useMemo(() => {
|
|
358
369
|
const cats = new Map();
|
|
359
370
|
for (const node of filteredNodes) {
|
|
@@ -364,15 +375,18 @@ function NodePalette({ searchQuery, expandedCategories, onSearch, onToggleCatego
|
|
|
364
375
|
}
|
|
365
376
|
return cats;
|
|
366
377
|
}, [filteredNodes]);
|
|
367
|
-
return (jsxs("div", { className: styles.palette, children: [jsx("div", { className: styles.paletteSearch, children: jsx("input", { type: "text", placeholder: "Search nodes...", value: searchQuery, onChange: e => onSearch(e.target.value), className: styles.searchInput }) }), jsx("div", { className: styles.paletteCategories, children: Array.from(categories.entries()).map(([category, nodes]) => (jsxs("div", { className: styles.paletteCategory, children: [jsxs("button", { className: styles.categoryHeader, onClick: () => onToggleCategory(category), style: { borderLeftColor: NODE_CATEGORY_COLORS[category] }, children: [jsx("span", { className: styles.categoryIcon, children: expandedCategories.includes(category) ? '▼' : '►' }), jsx("span", { children: NODE_CATEGORY_NAMES[category] }), jsx("span", { className: styles.categoryCount, children: nodes.length })] }), expandedCategories.includes(category) && (jsx("div", { className: styles.categoryNodes, children: nodes.map(nodeDef => (jsxs("div", { className: styles.paletteNode, draggable: true, onDragStart: e => onDragStart(nodeDef.type, e), onDoubleClick: () => onAddNode(nodeDef.type), title: nodeDef.description, children: [jsx("span", { className: styles.nodeColor, style: {
|
|
378
|
+
return (jsxs("div", { className: styles.palette, children: [jsx("div", { className: styles.paletteSearch, children: jsx("input", { type: "text", placeholder: "Search nodes...", value: searchQuery, onChange: (e) => onSearch(e.target.value), className: styles.searchInput }) }), jsx("div", { className: styles.paletteCategories, children: Array.from(categories.entries()).map(([category, nodes]) => (jsxs("div", { className: styles.paletteCategory, children: [jsxs("button", { className: styles.categoryHeader, onClick: () => onToggleCategory(category), style: { borderLeftColor: NODE_CATEGORY_COLORS[category] }, children: [jsx("span", { className: styles.categoryIcon, children: expandedCategories.includes(category) ? '▼' : '►' }), jsx("span", { children: NODE_CATEGORY_NAMES[category] }), jsx("span", { className: styles.categoryCount, children: nodes.length })] }), expandedCategories.includes(category) && (jsx("div", { className: styles.categoryNodes, children: nodes.map((nodeDef) => (jsxs("div", { className: styles.paletteNode, draggable: true, onDragStart: (e) => onDragStart(nodeDef.type, e), onDoubleClick: () => onAddNode(nodeDef.type), title: nodeDef.description, children: [jsx("span", { className: styles.nodeColor, style: {
|
|
379
|
+
backgroundColor: nodeDef.color || NODE_CATEGORY_COLORS[nodeDef.category],
|
|
380
|
+
} }), jsx("span", { className: styles.nodeName, children: nodeDef.name })] }, nodeDef.type))) }))] }, category))) })] }));
|
|
368
381
|
}
|
|
369
382
|
function GraphNode({ node, definition, selected, viewTransform, connections, onSelect, onMove, onStartConnection, }) {
|
|
370
383
|
const nodeRef = useRef(null);
|
|
371
384
|
const [isDragging, setIsDragging] = useState(false);
|
|
372
385
|
const dragStart = useRef(null);
|
|
373
386
|
const handleMouseDown = (e) => {
|
|
374
|
-
if (e.button !== 0)
|
|
387
|
+
if (e.button !== 0) {
|
|
375
388
|
return;
|
|
389
|
+
}
|
|
376
390
|
e.stopPropagation();
|
|
377
391
|
if (!selected) {
|
|
378
392
|
onSelect(node.id, e.shiftKey || e.ctrlKey);
|
|
@@ -383,8 +397,9 @@ function GraphNode({ node, definition, selected, viewTransform, connections, onS
|
|
|
383
397
|
}
|
|
384
398
|
};
|
|
385
399
|
useEffect(() => {
|
|
386
|
-
if (!isDragging)
|
|
400
|
+
if (!isDragging) {
|
|
387
401
|
return;
|
|
402
|
+
}
|
|
388
403
|
const handleMouseMove = (e) => {
|
|
389
404
|
if (dragStart.current) {
|
|
390
405
|
const delta = {
|
|
@@ -428,12 +443,12 @@ function GraphNode({ node, definition, selected, viewTransform, connections, onS
|
|
|
428
443
|
transform: `scale(${viewTransform.zoom})`,
|
|
429
444
|
transformOrigin: 'top left',
|
|
430
445
|
}, onMouseDown: handleMouseDown, children: [jsxs("div", { className: styles.nodeHeader, style: { backgroundColor: nodeColor }, children: [jsx("span", { className: styles.nodeTitle, children: definition.name }), node.locked && jsx("span", { className: styles.lockIcon, children: "\uD83D\uDD12" })] }), !node.collapsed && (jsxs("div", { className: styles.nodePorts, children: [jsx("div", { className: styles.inputPorts, children: definition.inputs.map((port, index) => {
|
|
431
|
-
const isConnected = connections.some(c => c.targetNodeId === node.id && c.targetPortId === port.id);
|
|
432
|
-
return (jsxs("div", { className: styles.port, style: { top: index * PORT_HEIGHT + NODE_HEADER_HEIGHT }, children: [jsx("div", { className: `${styles.portHandle} ${styles.inputHandle} ${isConnected ? styles.connected : ''}`, style: { backgroundColor: PORT_TYPE_COLORS[port.dataType] }, onMouseDown: e => handlePortMouseDown(e, port.id, 'input') }), jsx("span", { className: styles.portLabel, children: port.name })] }, port.id));
|
|
446
|
+
const isConnected = connections.some((c) => c.targetNodeId === node.id && c.targetPortId === port.id);
|
|
447
|
+
return (jsxs("div", { className: styles.port, style: { top: index * PORT_HEIGHT + NODE_HEADER_HEIGHT }, children: [jsx("div", { className: `${styles.portHandle} ${styles.inputHandle} ${isConnected ? styles.connected : ''}`, style: { backgroundColor: PORT_TYPE_COLORS[port.dataType] }, onMouseDown: (e) => handlePortMouseDown(e, port.id, 'input') }), jsx("span", { className: styles.portLabel, children: port.name })] }, port.id));
|
|
433
448
|
}) }), jsx("div", { className: styles.outputPorts, children: definition.outputs.map((port, index) => {
|
|
434
|
-
const isConnected = connections.some(c => c.sourceNodeId === node.id && c.sourcePortId === port.id);
|
|
435
|
-
return (jsxs("div", { className: styles.port, style: { top: index * PORT_HEIGHT + NODE_HEADER_HEIGHT }, children: [jsx("span", { className: styles.portLabel, children: port.name }), jsx("div", { className: `${styles.portHandle} ${styles.outputHandle} ${isConnected ? styles.connected : ''}`, style: { backgroundColor: PORT_TYPE_COLORS[port.dataType] }, onMouseDown: e => handlePortMouseDown(e, port.id, 'output') })] }, port.id));
|
|
436
|
-
}) })] })), node.comment &&
|
|
449
|
+
const isConnected = connections.some((c) => c.sourceNodeId === node.id && c.sourcePortId === port.id);
|
|
450
|
+
return (jsxs("div", { className: styles.port, style: { top: index * PORT_HEIGHT + NODE_HEADER_HEIGHT }, children: [jsx("span", { className: styles.portLabel, children: port.name }), jsx("div", { className: `${styles.portHandle} ${styles.outputHandle} ${isConnected ? styles.connected : ''}`, style: { backgroundColor: PORT_TYPE_COLORS[port.dataType] }, onMouseDown: (e) => handlePortMouseDown(e, port.id, 'output') })] }, port.id));
|
|
451
|
+
}) })] })), node.comment && jsx("div", { className: styles.nodeComment, children: node.comment })] }));
|
|
437
452
|
}
|
|
438
453
|
function Toolbar({ canUndo, canRedo, canPaste, hasSelection, autoCompile, compiling, showGrid, snapToGrid, onUndo, onRedo, onCopy, onCut, onPaste, onDuplicate, onDelete, onSelectAll, onZoomToFit, onToggleGrid, onToggleSnap, onToggleAutoCompile, onCompile, onNew, onExport, onImport, }) {
|
|
439
454
|
return (jsxs("div", { className: styles.toolbar, children: [jsxs("div", { className: styles.toolbarGroup, children: [jsx("button", { onClick: onNew, title: "New Material (Ctrl+N)", children: "\uD83D\uDCC4" }), jsx("button", { onClick: onImport, title: "Import (Ctrl+O)", children: "\uD83D\uDCC2" }), jsx("button", { onClick: onExport, title: "Export (Ctrl+S)", children: "\uD83D\uDCBE" })] }), jsx("div", { className: styles.toolbarSeparator }), jsxs("div", { className: styles.toolbarGroup, children: [jsx("button", { onClick: onUndo, disabled: !canUndo, title: "Undo (Ctrl+Z)", children: "\u21A9\uFE0F" }), jsx("button", { onClick: onRedo, disabled: !canRedo, title: "Redo (Ctrl+Y)", children: "\u21AA\uFE0F" })] }), jsx("div", { className: styles.toolbarSeparator }), jsxs("div", { className: styles.toolbarGroup, children: [jsx("button", { onClick: onCut, disabled: !hasSelection, title: "Cut (Ctrl+X)", children: "\u2702\uFE0F" }), jsx("button", { onClick: onCopy, disabled: !hasSelection, title: "Copy (Ctrl+C)", children: "\uD83D\uDCCB" }), jsx("button", { onClick: onPaste, disabled: !canPaste, title: "Paste (Ctrl+V)", children: "\uD83D\uDCE5" }), jsx("button", { onClick: onDuplicate, disabled: !hasSelection, title: "Duplicate (Ctrl+D)", children: "\uD83D\uDD04" }), jsx("button", { onClick: onDelete, disabled: !hasSelection, title: "Delete (Del)", children: "\uD83D\uDDD1\uFE0F" })] }), jsx("div", { className: styles.toolbarSeparator }), jsxs("div", { className: styles.toolbarGroup, children: [jsx("button", { onClick: onSelectAll, title: "Select All (Ctrl+A)", children: "\u2B1C" }), jsx("button", { onClick: onZoomToFit, title: "Zoom to Fit (F)", children: "\uD83D\uDD0D" }), jsx("button", { onClick: onToggleGrid, className: showGrid ? styles.active : '', title: "Toggle Grid (G)", children: "#" }), jsx("button", { onClick: onToggleSnap, className: snapToGrid ? styles.active : '', title: "Toggle Snap (S)", children: "\uD83E\uDDF2" })] }), jsx("div", { className: styles.toolbarSeparator }), jsxs("div", { className: styles.toolbarGroup, children: [jsx("button", { onClick: onToggleAutoCompile, className: autoCompile ? styles.active : '', title: "Auto Compile", children: "\u26A1" }), jsx("button", { onClick: onCompile, disabled: compiling, title: "Compile (F5)", children: compiling ? '⏳' : '▶️' })] })] }));
|
|
@@ -444,11 +459,13 @@ function PreviewPanel({ shader, mode, compiling, onModeChange }) {
|
|
|
444
459
|
// Simple preview rendering (in real implementation would use Three.js)
|
|
445
460
|
useEffect(() => {
|
|
446
461
|
const canvas = canvasRef.current;
|
|
447
|
-
if (!canvas)
|
|
462
|
+
if (!canvas) {
|
|
448
463
|
return;
|
|
464
|
+
}
|
|
449
465
|
const ctx = canvas.getContext('2d');
|
|
450
|
-
if (!ctx)
|
|
466
|
+
if (!ctx) {
|
|
451
467
|
return;
|
|
468
|
+
}
|
|
452
469
|
// Simple gradient preview
|
|
453
470
|
const gradient = ctx.createRadialGradient(100, 100, 0, 100, 100, 100);
|
|
454
471
|
if ((shader === null || shader === void 0 ? void 0 : shader.errors.length) === 0) {
|
|
@@ -466,7 +483,7 @@ function PreviewPanel({ shader, mode, compiling, onModeChange }) {
|
|
|
466
483
|
ctx.arc(100, 100, 80, 0, Math.PI * 2);
|
|
467
484
|
ctx.fill();
|
|
468
485
|
}, [shader, mode]);
|
|
469
|
-
return (jsxs("div", { className: styles.previewPanel, children: [jsxs("div", { className: styles.previewHeader, children: [jsx("span", { children: "Preview" }), jsx("select", { value: mode, onChange: e => onModeChange(e.target.value), className: styles.previewModeSelect, children: previewModes.map(m => (jsx("option", { value: m, children: m.charAt(0).toUpperCase() + m.slice(1) }, m))) })] }), jsxs("div", { className: styles.previewCanvas, children: [compiling && jsx("div", { className: styles.previewCompiling, children: "Compiling..." }), jsx("canvas", { ref: canvasRef, width: 200, height: 200 })] }), (shader === null || shader === void 0 ? void 0 : shader.errors.length) ? (jsx("div", { className: styles.previewErrors, children: shader.errors.slice(0, 3).map((err, i) => (jsx("div", { className: styles.previewError, children: err.message }, i))) })) : null] }));
|
|
486
|
+
return (jsxs("div", { className: styles.previewPanel, children: [jsxs("div", { className: styles.previewHeader, children: [jsx("span", { children: "Preview" }), jsx("select", { value: mode, onChange: (e) => onModeChange(e.target.value), className: styles.previewModeSelect, children: previewModes.map((m) => (jsx("option", { value: m, children: m.charAt(0).toUpperCase() + m.slice(1) }, m))) })] }), jsxs("div", { className: styles.previewCanvas, children: [compiling && jsx("div", { className: styles.previewCompiling, children: "Compiling..." }), jsx("canvas", { ref: canvasRef, width: 200, height: 200 })] }), (shader === null || shader === void 0 ? void 0 : shader.errors.length) ? (jsx("div", { className: styles.previewErrors, children: shader.errors.slice(0, 3).map((err, i) => (jsx("div", { className: styles.previewError, children: err.message }, i))) })) : null] }));
|
|
470
487
|
}
|
|
471
488
|
/* ═══════════════════════════════════════════════════════════════════════════
|
|
472
489
|
Main Component
|
|
@@ -478,7 +495,7 @@ function NiceMaterialEditor({ graph: initialGraph, onGraphChange, onCompile, cus
|
|
|
478
495
|
// Merge custom node definitions
|
|
479
496
|
const definitions = useMemo(() => {
|
|
480
497
|
const map = new Map(MATERIAL_NODE_MAP);
|
|
481
|
-
customNodes === null || customNodes === void 0 ? void 0 : customNodes.forEach(def => map.set(def.type, def));
|
|
498
|
+
customNodes === null || customNodes === void 0 ? void 0 : customNodes.forEach((def) => map.set(def.type, def));
|
|
482
499
|
return map;
|
|
483
500
|
}, [customNodes]);
|
|
484
501
|
// Notify parent of changes
|
|
@@ -493,8 +510,9 @@ function NiceMaterialEditor({ graph: initialGraph, onGraphChange, onCompile, cus
|
|
|
493
510
|
// Keyboard shortcuts
|
|
494
511
|
useEffect(() => {
|
|
495
512
|
const handleKeyDown = (e) => {
|
|
496
|
-
if (readOnly)
|
|
513
|
+
if (readOnly) {
|
|
497
514
|
return;
|
|
515
|
+
}
|
|
498
516
|
const isCtrl = e.ctrlKey || e.metaKey;
|
|
499
517
|
if (isCtrl && e.key === 'z') {
|
|
500
518
|
e.preventDefault();
|
|
@@ -557,8 +575,9 @@ function NiceMaterialEditor({ graph: initialGraph, onGraphChange, onCompile, cus
|
|
|
557
575
|
const delta = e.deltaY > 0 ? 0.9 : 1.1;
|
|
558
576
|
const newZoom = Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, state.viewTransform.zoom * delta));
|
|
559
577
|
const rect = (_a = canvasRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
560
|
-
if (!rect)
|
|
578
|
+
if (!rect) {
|
|
561
579
|
return;
|
|
580
|
+
}
|
|
562
581
|
const mouseX = e.clientX - rect.left;
|
|
563
582
|
const mouseY = e.clientY - rect.top;
|
|
564
583
|
const newX = mouseX - (mouseX - state.viewTransform.x) * (newZoom / state.viewTransform.zoom);
|
|
@@ -615,11 +634,13 @@ function NiceMaterialEditor({ graph: initialGraph, onGraphChange, onCompile, cus
|
|
|
615
634
|
var _a;
|
|
616
635
|
e.preventDefault();
|
|
617
636
|
const nodeType = e.dataTransfer.getData('text/plain');
|
|
618
|
-
if (!nodeType)
|
|
637
|
+
if (!nodeType) {
|
|
619
638
|
return;
|
|
639
|
+
}
|
|
620
640
|
const rect = (_a = canvasRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
621
|
-
if (!rect)
|
|
641
|
+
if (!rect) {
|
|
622
642
|
return;
|
|
643
|
+
}
|
|
623
644
|
const position = screenToGraph({ x: e.clientX - rect.left, y: e.clientY - rect.top }, state.viewTransform);
|
|
624
645
|
dispatch({ type: 'ADD_NODE', nodeType, position });
|
|
625
646
|
};
|
|
@@ -659,8 +680,9 @@ function NiceMaterialEditor({ graph: initialGraph, onGraphChange, onCompile, cus
|
|
|
659
680
|
input.onchange = async (e) => {
|
|
660
681
|
var _a;
|
|
661
682
|
const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
662
|
-
if (!file)
|
|
683
|
+
if (!file) {
|
|
663
684
|
return;
|
|
685
|
+
}
|
|
664
686
|
const text = await file.text();
|
|
665
687
|
const graph = importGraphFromJSON(text);
|
|
666
688
|
if (graph) {
|
|
@@ -671,24 +693,37 @@ function NiceMaterialEditor({ graph: initialGraph, onGraphChange, onCompile, cus
|
|
|
671
693
|
};
|
|
672
694
|
// Render connections
|
|
673
695
|
const renderConnections = () => {
|
|
674
|
-
return state.graph.connections.map(conn => {
|
|
675
|
-
const sourceNode = state.graph.nodes.find(n => n.id === conn.sourceNodeId);
|
|
676
|
-
const targetNode = state.graph.nodes.find(n => n.id === conn.targetNodeId);
|
|
677
|
-
if (!sourceNode || !targetNode)
|
|
696
|
+
return state.graph.connections.map((conn) => {
|
|
697
|
+
const sourceNode = state.graph.nodes.find((n) => n.id === conn.sourceNodeId);
|
|
698
|
+
const targetNode = state.graph.nodes.find((n) => n.id === conn.targetNodeId);
|
|
699
|
+
if (!sourceNode || !targetNode) {
|
|
678
700
|
return null;
|
|
701
|
+
}
|
|
679
702
|
const sourceDef = definitions.get(sourceNode.type);
|
|
680
703
|
const targetDef = definitions.get(targetNode.type);
|
|
681
|
-
if (!sourceDef || !targetDef)
|
|
704
|
+
if (!sourceDef || !targetDef) {
|
|
682
705
|
return null;
|
|
683
|
-
|
|
684
|
-
const
|
|
685
|
-
|
|
706
|
+
}
|
|
707
|
+
const sourcePortIndex = sourceDef.outputs.findIndex((p) => p.id === conn.sourcePortId);
|
|
708
|
+
const targetPortIndex = targetDef.inputs.findIndex((p) => p.id === conn.targetPortId);
|
|
709
|
+
if (sourcePortIndex === -1 || targetPortIndex === -1) {
|
|
686
710
|
return null;
|
|
711
|
+
}
|
|
687
712
|
const sourcePort = sourceDef.outputs[sourcePortIndex];
|
|
688
713
|
const x1 = (sourceNode.position.x + NODE_WIDTH) * state.viewTransform.zoom + state.viewTransform.x;
|
|
689
|
-
const y1 = (sourceNode.position.y +
|
|
714
|
+
const y1 = (sourceNode.position.y +
|
|
715
|
+
NODE_HEADER_HEIGHT +
|
|
716
|
+
sourcePortIndex * PORT_HEIGHT +
|
|
717
|
+
PORT_HEIGHT / 2) *
|
|
718
|
+
state.viewTransform.zoom +
|
|
719
|
+
state.viewTransform.y;
|
|
690
720
|
const x2 = targetNode.position.x * state.viewTransform.zoom + state.viewTransform.x;
|
|
691
|
-
const y2 = (targetNode.position.y +
|
|
721
|
+
const y2 = (targetNode.position.y +
|
|
722
|
+
NODE_HEADER_HEIGHT +
|
|
723
|
+
targetPortIndex * PORT_HEIGHT +
|
|
724
|
+
PORT_HEIGHT / 2) *
|
|
725
|
+
state.viewTransform.zoom +
|
|
726
|
+
state.viewTransform.y;
|
|
692
727
|
const isSelected = state.selectedConnections.includes(conn.id);
|
|
693
728
|
const controlX = (x1 + x2) / 2;
|
|
694
729
|
return (jsx("path", { d: `M ${x1} ${y1} C ${controlX} ${y1}, ${controlX} ${y2}, ${x2} ${y2}`, fill: "none", stroke: isSelected ? '#fff' : PORT_TYPE_COLORS[sourcePort.dataType], strokeWidth: isSelected ? 3 : 2, className: styles.connection, onClick: () => dispatch({ type: 'SELECT_CONNECTIONS', connectionIds: [conn.id] }) }, conn.id));
|
|
@@ -697,11 +732,13 @@ function NiceMaterialEditor({ graph: initialGraph, onGraphChange, onCompile, cus
|
|
|
697
732
|
// Render pending connection
|
|
698
733
|
const renderPendingConnection = () => {
|
|
699
734
|
var _a;
|
|
700
|
-
if (!pendingConnection)
|
|
735
|
+
if (!pendingConnection) {
|
|
701
736
|
return null;
|
|
737
|
+
}
|
|
702
738
|
const rect = (_a = canvasRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
703
|
-
if (!rect)
|
|
739
|
+
if (!rect) {
|
|
704
740
|
return null;
|
|
741
|
+
}
|
|
705
742
|
const x1 = pendingConnection.startPosition.x - rect.left;
|
|
706
743
|
const y1 = pendingConnection.startPosition.y - rect.top;
|
|
707
744
|
const x2 = pendingConnection.currentPosition.x - rect.left;
|
|
@@ -711,8 +748,9 @@ function NiceMaterialEditor({ graph: initialGraph, onGraphChange, onCompile, cus
|
|
|
711
748
|
};
|
|
712
749
|
// Render grid
|
|
713
750
|
const renderGrid = () => {
|
|
714
|
-
if (!state.showGrid)
|
|
751
|
+
if (!state.showGrid) {
|
|
715
752
|
return null;
|
|
753
|
+
}
|
|
716
754
|
const gridSize = state.gridSize * state.viewTransform.zoom;
|
|
717
755
|
const offsetX = state.viewTransform.x % gridSize;
|
|
718
756
|
const offsetY = state.viewTransform.y % gridSize;
|
|
@@ -722,12 +760,17 @@ function NiceMaterialEditor({ graph: initialGraph, onGraphChange, onCompile, cus
|
|
|
722
760
|
dispatch({ type: 'COMPILE' });
|
|
723
761
|
const shader = compileShader(state.graph, definitions);
|
|
724
762
|
dispatch({ type: 'SET_COMPILED_SHADER', shader });
|
|
725
|
-
}, onNew: () => dispatch({ type: 'NEW_GRAPH' }), onExport: handleExport, onImport: handleImport })), jsxs("div", { className: styles.editorBody, children: [showPalette && (jsx(NodePalette, { searchQuery: state.searchQuery, expandedCategories: state.expandedCategories, onSearch: query => dispatch({ type: 'SET_SEARCH_QUERY', query }), onToggleCategory: cat => dispatch({ type: 'TOGGLE_CATEGORY', category: cat }), onAddNode: handlePaletteAddNode, onDragStart: handlePaletteDragStart })), jsxs("div", { ref: canvasRef, className: styles.graphCanvas, onWheel: handleWheel, onMouseDown: handleCanvasMouseDown, onMouseMove: handleCanvasMouseMove, onMouseUp: handleCanvasMouseUp, onMouseLeave: handleCanvasMouseUp, onDragOver: handleDragOver, onDrop: handleDrop, children: [jsxs("svg", { className: styles.connectionsSvg, children: [jsx("defs", { children: renderGrid() }), state.showGrid && jsx("rect", { width: "100%", height: "100%", fill: "url(#grid)" }), renderConnections(), renderPendingConnection()] }), state.graph.nodes.map(node => {
|
|
763
|
+
}, onNew: () => dispatch({ type: 'NEW_GRAPH' }), onExport: handleExport, onImport: handleImport })), jsxs("div", { className: styles.editorBody, children: [showPalette && (jsx(NodePalette, { searchQuery: state.searchQuery, expandedCategories: state.expandedCategories, onSearch: (query) => dispatch({ type: 'SET_SEARCH_QUERY', query }), onToggleCategory: (cat) => dispatch({ type: 'TOGGLE_CATEGORY', category: cat }), onAddNode: handlePaletteAddNode, onDragStart: handlePaletteDragStart })), jsxs("div", { ref: canvasRef, className: styles.graphCanvas, onWheel: handleWheel, onMouseDown: handleCanvasMouseDown, onMouseMove: handleCanvasMouseMove, onMouseUp: handleCanvasMouseUp, onMouseLeave: handleCanvasMouseUp, onDragOver: handleDragOver, onDrop: handleDrop, children: [jsxs("svg", { className: styles.connectionsSvg, children: [jsx("defs", { children: renderGrid() }), state.showGrid && jsx("rect", { width: "100%", height: "100%", fill: "url(#grid)" }), renderConnections(), renderPendingConnection()] }), state.graph.nodes.map((node) => {
|
|
726
764
|
const def = definitions.get(node.type);
|
|
727
|
-
if (!def)
|
|
765
|
+
if (!def) {
|
|
728
766
|
return null;
|
|
729
|
-
|
|
730
|
-
|
|
767
|
+
}
|
|
768
|
+
return (jsx(GraphNode, { node: node, definition: def, selected: state.selectedNodes.includes(node.id), viewTransform: state.viewTransform, connections: state.graph.connections, onSelect: (nodeId, additive) => dispatch({ type: 'SELECT_NODES', nodeIds: [nodeId], additive }), onMove: (nodeId, delta) => dispatch({
|
|
769
|
+
type: 'MOVE_NODES',
|
|
770
|
+
nodeIds: state.selectedNodes.includes(nodeId) ? state.selectedNodes : [nodeId],
|
|
771
|
+
delta,
|
|
772
|
+
}), onStartConnection: handleStartConnection }, node.id));
|
|
773
|
+
})] }), showPreview && (jsx(PreviewPanel, { shader: state.compiledShader, mode: state.previewMode, compiling: state.compiling, onModeChange: (mode) => dispatch({ type: 'SET_PREVIEW_MODE', mode }) }))] })] }));
|
|
731
774
|
}
|
|
732
775
|
|
|
733
776
|
export { NiceMaterialEditor, useMaterialEditor };
|