@@ -11,12 +11,13 @@ import { nodeTypes, nodeGroups } from './nodeDefinitions';
1111import examples from './examples' ;
1212import { saveAs } from 'file-saver' ;
1313import GraphInspector from './components/GraphInspector' ;
14+ import Node from './engine/Node' ;
1415
1516const VisualScripting = ( ) => {
1617 // #region State Declarations
1718 const canvasRef = useRef ( null ) ;
1819 const [ contextMenu , setContextMenu ] = useState ( { visible : false , x : 0 , y : 0 } ) ;
19- const [ nodes , setNodes ] = useState ( [ ] ) ;
20+ const [ nodes , setNodes ] = useState ( ( ) => [ ] ) ;
2021 const [ edges , setEdges ] = useState ( [ ] ) ;
2122 const [ draggingNode , setDraggingNode ] = useState ( null ) ;
2223 const [ connecting , setConnecting ] = useState ( null ) ;
@@ -119,24 +120,9 @@ const VisualScripting = () => {
119120 const clickedPort = findClickedPort ( x , y ) ;
120121 if ( clickedPort ) {
121122 const node = nodes . find ( n => n . id === clickedPort . nodeId ) ;
122- const { width } = renderer . getNodeDimensions ( node , canvasRef . current . getContext ( '2d' ) ) ;
123-
124- const triangleWidth = 6 ;
125- const triangleHeight = 10 ;
126- const portOffset = 5 ;
127-
128- const portY = node . y + 35 + ( clickedPort . index * 14 ) ;
129- const portYMiddle = portY + ( triangleHeight / 2 ) ;
130-
131- const portX = clickedPort . isInput
132- ? node . x - portOffset - ( triangleWidth / 2 )
133- : node . x + width + portOffset + ( triangleWidth / 2 ) ;
134-
135- setConnecting ( {
136- ...clickedPort ,
137- portY : portYMiddle ,
138- portX
139- } ) ;
123+ const dimensions = renderer . getNodeDimensions ( node , canvasRef . current . getContext ( '2d' ) ) ;
124+ const portPosition = node . getPortPosition ( clickedPort . index , clickedPort . isInput , dimensions ) ;
125+ setConnecting ( portPosition ) ;
140126 } else {
141127 const clickedNode = findClickedNode ( x , y ) ;
142128 if ( clickedNode ) {
@@ -160,15 +146,13 @@ const VisualScripting = () => {
160146 camera . move ( dx , dy ) ;
161147 setLastMousePosition ( { x : e . clientX , y : e . clientY } ) ;
162148 } else if ( draggingNode ) {
163- setNodes ( nodes . map ( node =>
164- node . id === draggingNode . id
165- ? {
166- ...node ,
167- x : x - draggingNode . offsetX ,
168- y : y - draggingNode . offsetY
169- }
170- : node
171- ) ) ;
149+ setNodes ( nodes . map ( node => {
150+ if ( node . id === draggingNode . id ) {
151+ return Node . createInstance ( node , nodeTypes )
152+ . moveTo ( x - draggingNode . offsetX , y - draggingNode . offsetY ) ;
153+ }
154+ return node ;
155+ } ) ) ;
172156 }
173157
174158 setMousePosition ( { x, y } ) ;
@@ -236,68 +220,23 @@ const VisualScripting = () => {
236220
237221 // #region Node Operations
238222 const findClickedNode = ( x , y ) => {
239- return nodes . find ( node => {
240- const { width, height } = renderer . getNodeDimensions ( node , canvasRef . current . getContext ( '2d' ) ) ;
241-
242- return x >= node . x && x <= node . x + width && y >= node . y && y <= node . y + height ;
243- } ) ;
223+ return nodes . find ( node =>
224+ node . isPointInside ( x , y , renderer . getNodeDimensions ( node , canvasRef . current . getContext ( '2d' ) ) )
225+ ) ;
244226 } ;
245227
246228 const findClickedPort = ( x , y ) => {
247- const PORT_WIDTH = 6 ;
248- const PORT_HEIGHT = 10 ;
249- const PORT_OFFSET = 5 ;
250- const SCALE_MULTIPLIER = 1.5 ;
251- const VERTICAL_OFFSET = 2 ;
252-
253229 for ( const node of nodes ) {
254230 const nodeType = nodeTypes [ node . type ] ;
255- const { width } = renderer . getNodeDimensions ( node , canvasRef . current . getContext ( '2d' ) ) ;
256-
257- let currentHeight = 35 ; // Start after title (25 + 10 padding)
258-
259- // Check input ports
260- for ( let i = 0 ; i < nodeType . inputs . length ; i ++ ) {
261- const portX = node . x - PORT_OFFSET - PORT_WIDTH ;
262- const portY = node . y + currentHeight + ( i * 14 ) ;
263-
264- if ( x >= portX && x <= portX + PORT_WIDTH * SCALE_MULTIPLIER &&
265- y >= portY - VERTICAL_OFFSET && y <= portY + PORT_HEIGHT + VERTICAL_OFFSET ) {
266- return { nodeId : node . id , isInput : true , index : i } ;
267- }
268- }
269-
270- // Check output ports
271- for ( let i = 0 ; i < nodeType . outputs . length ; i ++ ) {
272- const portX = node . x + width + PORT_OFFSET ;
273- const portY = node . y + currentHeight + ( i * 14 ) ;
274-
275- if ( x >= portX && x <= portX + PORT_WIDTH * SCALE_MULTIPLIER &&
276- y >= portY - VERTICAL_OFFSET && y <= portY + PORT_HEIGHT + VERTICAL_OFFSET ) {
277- return { nodeId : node . id , isInput : false , index : i } ;
278- }
279- }
231+ const dimensions = renderer . getNodeDimensions ( node , canvasRef . current . getContext ( '2d' ) ) ;
232+ const clickedPort = node . findClickedPort ( x , y , dimensions , nodeType ) ;
233+ if ( clickedPort ) return clickedPort ;
280234 }
281235 return null ;
282236 } ;
283237
284238 const addNode = ( type ) => {
285- const nodeType = nodeTypes [ type ] ;
286- const newNode = {
287- id : Date . now ( ) ,
288- type,
289- x : contextMenu . x ,
290- y : contextMenu . y ,
291- properties : { }
292- } ;
293-
294- // Initialize properties with default values
295- if ( nodeType . properties ) {
296- nodeType . properties . forEach ( prop => {
297- newNode . properties [ prop . name ] = prop . default ;
298- } ) ;
299- }
300-
239+ const newNode = Node . create ( type , contextMenu . x , contextMenu . y , nodeTypes ) ;
301240 const newNodes = [ ...nodes , newNode ] ;
302241 setUndoStack ( [ ...undoStack , { nodes, edges } ] ) ;
303242 setRedoStack ( [ ] ) ;
@@ -307,21 +246,26 @@ const VisualScripting = () => {
307246 } ;
308247
309248 const updateNodeProperty = ( property , value ) => {
310- const updatedNodes = nodes . map ( node =>
311- node . id === selectedNodes [ 0 ] . id
312- ? { ...node , properties : { ...node . properties , [ property ] : value } }
313- : node
314- ) ;
249+ const updatedNodes = nodes . map ( node => {
250+ if ( node . id === selectedNodes [ 0 ] . id ) {
251+ return Node . createInstance ( node , nodeTypes )
252+ . updateProperty ( property , value ) ;
253+ }
254+ return node ;
255+ } ) ;
256+
315257 setUndoStack ( [ ...undoStack , { nodes, edges } ] ) ;
316258 setRedoStack ( [ ] ) ;
317259 setNodes ( updatedNodes ) ;
318260
319- // Update the selectedNodes state as well
320- setSelectedNodes ( prevSelected => prevSelected . map ( node =>
321- node . id === selectedNodes [ 0 ] . id
322- ? { ...node , properties : { ...node . properties , [ property ] : value } }
323- : node
324- ) ) ;
261+ setSelectedNodes ( prevSelected => prevSelected . map ( node => {
262+ if ( node . id === selectedNodes [ 0 ] . id ) {
263+ return Node . createInstance ( node , nodeTypes )
264+ . updateProperty ( property , value ) ;
265+ }
266+ return node ;
267+ } ) ) ;
268+
325269 setNeedsRedraw ( true ) ;
326270 } ;
327271
@@ -377,38 +321,16 @@ const VisualScripting = () => {
377321 break ;
378322 case 'loadExample' :
379323 if ( examples [ param ] ) {
380- // Find any Switch nodes and update nodeTypes with dynamic outputs
381- examples [ param ] . nodes . forEach ( node => {
382- if ( node . type === 'Switch' && node . properties . cases ) {
383- const switchNode = nodeTypes [ 'Switch' ] ;
384- // Create a copy of the original outputs
385- const baseOutputs = [ ...switchNode . outputs ] ;
386-
387- // Add case outputs dynamically
388- node . properties . cases . forEach ( ( caseItem , index ) => {
389- baseOutputs . splice ( index , 0 , {
390- type : 'control' ,
391- name : caseItem . output ,
392- description : `Triggered when value matches ${ caseItem . value } `
393- } ) ;
394- } ) ;
395-
396- // Create a temporary node type with the dynamic outputs
397- nodeTypes [ 'Switch' ] = {
398- ...switchNode ,
399- outputs : baseOutputs
400- } ;
401- }
402- } ) ;
403-
404- setNodes ( examples [ param ] . nodes ) ;
324+ const exampleNodes = examples [ param ] . nodes . map ( node =>
325+ Node . createFromExample ( node , nodeTypes )
326+ ) ;
327+ setNodes ( exampleNodes ) ;
405328 setEdges ( examples [ param ] . edges ) ;
406329 setUndoStack ( [ ] ) ;
407330 setRedoStack ( [ ] ) ;
408331 }
409332 break ;
410333 case 'save' :
411- // Implement file saving logic here
412334 const projectData = JSON . stringify ( { nodes, edges } ) ;
413335 const blob = new Blob ( [ projectData ] , { type : 'application/json' } ) ;
414336 const url = URL . createObjectURL ( blob ) ;
0 commit comments