@@ -20,6 +20,7 @@ import type { SubgraphEventMap } from './infrastructure/SubgraphEventMap'
2020import type {
2121 DefaultConnectionColors ,
2222 Dictionary ,
23+ HasBoundingRect ,
2324 IContextMenuValue ,
2425 INodeInputSlot ,
2526 INodeOutputSlot ,
@@ -28,7 +29,8 @@ import type {
2829 MethodNames ,
2930 OptionalProps ,
3031 Point ,
31- Positionable
32+ Positionable ,
33+ Size
3234} from './interfaces'
3335import { LiteGraph , SubgraphNode } from './litegraph'
3436import {
@@ -1569,6 +1571,9 @@ export class LGraph
15691571 boundingRect
15701572 )
15711573
1574+ //Correct for title height. It's included in bounding box, but not _posSize
1575+ subgraphNode . pos [ 1 ] += LiteGraph . NODE_TITLE_HEIGHT / 2
1576+
15721577 // Add the subgraph node to the graph
15731578 this . add ( subgraphNode )
15741579
@@ -1668,14 +1673,21 @@ export class LGraph
16681673 if ( ! ( subgraphNode instanceof SubgraphNode ) )
16691674 throw new Error ( 'Can only unpack Subgraph Nodes' )
16701675 this . beforeChange ( )
1671- const center = [ 0 , 0 ]
1672- for ( const node of subgraphNode . subgraph . nodes ) {
1673- center [ 0 ] += node . pos [ 0 ] + node . size [ 0 ] / 2
1674- center [ 1 ] += node . pos [ 1 ] + node . size [ 1 ] / 2
1675- }
1676- center [ 0 ] /= subgraphNode . subgraph . nodes . length
1677- center [ 1 ] /= subgraphNode . subgraph . nodes . length
1676+ //NOTE: Create bounds can not be called on positionables directly as the subgraph is not being displayed and boundingRect is not initialized.
1677+ //NOTE: NODE_TITLE_HEIGHT is explicitly excluded here
1678+ const positionables = [
1679+ ...subgraphNode . subgraph . nodes ,
1680+ ...subgraphNode . subgraph . reroutes . values ( ) ,
1681+ ...subgraphNode . subgraph . groups
1682+ ] . map ( ( p : { pos : Point ; size ?: Size } ) : HasBoundingRect => {
1683+ return {
1684+ boundingRect : [ p . pos [ 0 ] , p . pos [ 1 ] , p . size ?. [ 0 ] ?? 0 , p . size ?. [ 1 ] ?? 0 ]
1685+ }
1686+ } )
1687+ const bounds = createBounds ( positionables ) ?? [ 0 , 0 , 0 , 0 ]
1688+ const center = [ bounds [ 0 ] + bounds [ 2 ] / 2 , bounds [ 1 ] + bounds [ 3 ] / 2 ]
16781689
1690+ const toSelect : Positionable [ ] = [ ]
16791691 const offsetX = subgraphNode . pos [ 0 ] - center [ 0 ] + subgraphNode . size [ 0 ] / 2
16801692 const offsetY = subgraphNode . pos [ 1 ] - center [ 1 ] + subgraphNode . size [ 1 ] / 2
16811693 const movedNodes = multiClone ( subgraphNode . subgraph . nodes )
@@ -1697,6 +1709,21 @@ export class LGraph
16971709 for ( const input of node . inputs ) {
16981710 input . link = null
16991711 }
1712+ for ( const output of node . outputs ) {
1713+ output . links = [ ]
1714+ }
1715+ toSelect . push ( node )
1716+ }
1717+ const groups = structuredClone (
1718+ [ ...subgraphNode . subgraph . groups ] . map ( ( g ) => g . serialize ( ) )
1719+ )
1720+ for ( const g_info of groups ) {
1721+ const group = new LGraphGroup ( g_info . title , g_info . id )
1722+ this . add ( group , true )
1723+ group . configure ( g_info )
1724+ group . pos [ 0 ] += offsetX
1725+ group . pos [ 1 ] += offsetY
1726+ toSelect . push ( group )
17001727 }
17011728 //cleanup reoute.linkIds now, but leave link.parentIds dangling
17021729 for ( const islot of subgraphNode . inputs ) {
@@ -1722,17 +1749,16 @@ export class LGraph
17221749 }
17231750 }
17241751 }
1725-
1726- const newLinks : [
1727- NodeId ,
1728- number ,
1729- NodeId ,
1730- number ,
1731- LinkId ,
1732- RerouteId | undefined ,
1733- RerouteId | undefined ,
1734- boolean
1735- ] [ ] = [ ]
1752+ const newLinks : {
1753+ oid : NodeId
1754+ oslot : number
1755+ tid : NodeId
1756+ tslot : number
1757+ id : LinkId
1758+ iparent ?: RerouteId
1759+ eparent ?: RerouteId
1760+ externalFirst : boolean
1761+ } [ ] = [ ]
17361762 for ( const [ , link ] of subgraphNode . subgraph . _links ) {
17371763 let externalParentId : RerouteId | undefined
17381764 if ( link . origin_id === SUBGRAPH_INPUT_ID ) {
@@ -1757,16 +1783,16 @@ export class LGraph
17571783 for ( const linkId of subgraphNode . outputs [ link . target_slot ] . links ??
17581784 [ ] ) {
17591785 const sublink = this . links [ linkId ]
1760- newLinks . push ( [
1761- link . origin_id ,
1762- link . origin_slot ,
1763- sublink . target_id ,
1764- sublink . target_slot ,
1765- link . id ,
1766- link . parentId ,
1767- sublink . parentId ,
1768- true
1769- ] )
1786+ newLinks . push ( {
1787+ oid : link . origin_id ,
1788+ oslot : link . origin_slot ,
1789+ tid : sublink . target_id ,
1790+ tslot : sublink . target_slot ,
1791+ id : link . id ,
1792+ iparent : link . parentId ,
1793+ eparent : sublink . parentId ,
1794+ externalFirst : true
1795+ } )
17701796 sublink . parentId = undefined
17711797 }
17721798 continue
@@ -1778,60 +1804,60 @@ export class LGraph
17781804 }
17791805 link . target_id = target_id
17801806 }
1781- newLinks . push ( [
1782- link . origin_id ,
1783- link . origin_slot ,
1784- link . target_id ,
1785- link . target_slot ,
1786- link . id ,
1787- link . parentId ,
1788- externalParentId ,
1789- false
1790- ] )
1807+ newLinks . push ( {
1808+ oid : link . origin_id ,
1809+ oslot : link . origin_slot ,
1810+ tid : link . target_id ,
1811+ tslot : link . target_slot ,
1812+ id : link . id ,
1813+ iparent : link . parentId ,
1814+ eparent : externalParentId ,
1815+ externalFirst : false
1816+ } )
17911817 }
17921818 this . remove ( subgraphNode )
17931819 this . subgraphs . delete ( subgraphNode . subgraph . id )
17941820 const linkIdMap = new Map < LinkId , LinkId [ ] > ( )
17951821 for ( const newLink of newLinks ) {
17961822 let created : LLink | null | undefined
1797- if ( newLink [ 0 ] == SUBGRAPH_INPUT_ID ) {
1823+ if ( newLink . oid == SUBGRAPH_INPUT_ID ) {
17981824 if ( ! ( this instanceof Subgraph ) ) {
17991825 console . error ( 'Ignoring link to subgraph outside subgraph' )
18001826 continue
18011827 }
1802- const tnode = this . _nodes_by_id [ newLink [ 2 ] ]
1803- created = this . inputNode . slots [ newLink [ 1 ] ] . connect (
1804- tnode . inputs [ newLink [ 3 ] ] ,
1828+ const tnode = this . _nodes_by_id [ newLink . tid ]
1829+ created = this . inputNode . slots [ newLink . oslot ] . connect (
1830+ tnode . inputs [ newLink . tslot ] ,
18051831 tnode
18061832 )
1807- } else if ( newLink [ 2 ] == SUBGRAPH_OUTPUT_ID ) {
1833+ } else if ( newLink . tid == SUBGRAPH_OUTPUT_ID ) {
18081834 if ( ! ( this instanceof Subgraph ) ) {
18091835 console . error ( 'Ignoring link to subgraph outside subgraph' )
18101836 continue
18111837 }
1812- const tnode = this . _nodes_by_id [ newLink [ 0 ] ]
1813- created = this . outputNode . slots [ newLink [ 3 ] ] . connect (
1814- tnode . outputs [ newLink [ 1 ] ] ,
1838+ const tnode = this . _nodes_by_id [ newLink . oid ]
1839+ created = this . outputNode . slots [ newLink . tslot ] . connect (
1840+ tnode . outputs [ newLink . oslot ] ,
18151841 tnode
18161842 )
18171843 } else {
1818- created = this . _nodes_by_id [ newLink [ 0 ] ] . connect (
1819- newLink [ 1 ] ,
1820- this . _nodes_by_id [ newLink [ 2 ] ] ,
1821- newLink [ 3 ]
1844+ created = this . _nodes_by_id [ newLink . oid ] . connect (
1845+ newLink . oslot ,
1846+ this . _nodes_by_id [ newLink . tid ] ,
1847+ newLink . tslot
18221848 )
18231849 }
18241850 if ( ! created ) {
18251851 console . error ( 'Failed to create link' )
18261852 continue
18271853 }
18281854 //This is a little unwieldy since Map.has isn't a type guard
1829- const linkIds = linkIdMap . get ( newLink [ 4 ] ) ?? [ ]
1855+ const linkIds = linkIdMap . get ( newLink . id ) ?? [ ]
18301856 linkIds . push ( created . id )
1831- if ( ! linkIdMap . has ( newLink [ 4 ] ) ) {
1832- linkIdMap . set ( newLink [ 4 ] , linkIds )
1857+ if ( ! linkIdMap . has ( newLink . id ) ) {
1858+ linkIdMap . set ( newLink . id , linkIds )
18331859 }
1834- newLink [ 4 ] = created . id
1860+ newLink . id = created . id
18351861 }
18361862 const rerouteIdMap = new Map < RerouteId , RerouteId > ( )
18371863 for ( const reroute of subgraphNode . subgraph . reroutes . values ( ) ) {
@@ -1847,17 +1873,18 @@ export class LGraph
18471873 ] )
18481874 rerouteIdMap . set ( reroute . id , migratedReroute . id )
18491875 this . reroutes . set ( migratedReroute . id , migratedReroute )
1876+ toSelect . push ( migratedReroute )
18501877 }
18511878 //iterate over newly created links to update reroute parentIds
18521879 for ( const newLink of newLinks ) {
1853- const linkInstance = this . links . get ( newLink [ 4 ] )
1880+ const linkInstance = this . links . get ( newLink . id )
18541881 if ( ! linkInstance ) {
18551882 continue
18561883 }
18571884 let instance : Reroute | LLink | undefined = linkInstance
1858- let parentId : RerouteId | undefined = newLink [ 6 ]
1859- if ( newLink [ 7 ] ) {
1860- parentId = newLink [ 6 ]
1885+ let parentId : RerouteId | undefined = undefined
1886+ if ( newLink . externalFirst ) {
1887+ parentId = newLink . eparent
18611888 //TODO: recursion check/helper method? Probably exists, but wouldn't mesh with the reference tracking used by this implementation
18621889 while ( parentId ) {
18631890 instance . parentId = parentId
@@ -1869,7 +1896,7 @@ export class LGraph
18691896 parentId = instance . parentId
18701897 }
18711898 }
1872- parentId = newLink [ 5 ]
1899+ parentId = newLink . iparent
18731900 while ( parentId ) {
18741901 const migratedId = rerouteIdMap . get ( parentId )
18751902 if ( ! migratedId ) throw new Error ( 'Broken Id link when unpacking' )
@@ -1883,8 +1910,8 @@ export class LGraph
18831910 if ( ! oldReroute ) throw new Error ( 'Broken Id link when unpacking' )
18841911 parentId = oldReroute . parentId
18851912 }
1886- if ( ! newLink [ 7 ] ) {
1887- parentId = newLink [ 6 ]
1913+ if ( ! newLink . externalFirst ) {
1914+ parentId = newLink . eparent
18881915 while ( parentId ) {
18891916 instance . parentId = parentId
18901917 instance = this . reroutes . get ( parentId )
@@ -1897,18 +1924,13 @@ export class LGraph
18971924 }
18981925 }
18991926
1900- const nodes : LGraphNode [ ] = [ ]
19011927 for ( const nodeId of nodeIdMap . values ( ) ) {
19021928 const node = this . _nodes_by_id [ nodeId ]
1903- nodes . push ( node )
19041929 node . _setConcreteSlots ( )
19051930 node . arrange ( )
19061931 }
1907- const reroutes = [ ...rerouteIdMap . values ( ) ]
1908- . map ( ( i ) => this . reroutes . get ( i ) )
1909- . filter ( ( x ) : x is Reroute => ! ! x )
19101932
1911- this . canvasAction ( ( c ) => c . selectItems ( [ ... nodes , ... reroutes ] ) )
1933+ this . canvasAction ( ( c ) => c . selectItems ( toSelect ) )
19121934 this . afterChange ( )
19131935 }
19141936
0 commit comments