@@ -18,6 +18,7 @@ import type { SubgraphEventMap } from './infrastructure/SubgraphEventMap'
1818import type {
1919 DefaultConnectionColors ,
2020 Dictionary ,
21+ HasBoundingRect ,
2122 IContextMenuValue ,
2223 INodeInputSlot ,
2324 INodeOutputSlot ,
@@ -26,7 +27,8 @@ import type {
2627 MethodNames ,
2728 OptionalProps ,
2829 Point ,
29- Positionable
30+ Positionable ,
31+ Size
3032} from './interfaces'
3133import { LiteGraph , SubgraphNode } from './litegraph'
3234import {
@@ -1568,6 +1570,9 @@ export class LGraph
15681570 boundingRect
15691571 )
15701572
1573+ //Correct for title height. It's included in bounding box, but not _posSize
1574+ subgraphNode . pos [ 1 ] += LiteGraph . NODE_TITLE_HEIGHT / 2
1575+
15711576 // Add the subgraph node to the graph
15721577 this . add ( subgraphNode )
15731578
@@ -1667,14 +1672,21 @@ export class LGraph
16671672 if ( ! ( subgraphNode instanceof SubgraphNode ) )
16681673 throw new Error ( 'Can only unpack Subgraph Nodes' )
16691674 this . beforeChange ( )
1670- const center = [ 0 , 0 ]
1671- for ( const node of subgraphNode . subgraph . nodes ) {
1672- center [ 0 ] += node . pos [ 0 ] + node . size [ 0 ] / 2
1673- center [ 1 ] += node . pos [ 1 ] + node . size [ 1 ] / 2
1674- }
1675- center [ 0 ] /= subgraphNode . subgraph . nodes . length
1676- center [ 1 ] /= subgraphNode . subgraph . nodes . length
1675+ //NOTE: Create bounds can not be called on positionables directly as the subgraph is not being displayed and boundingRect is not initialized.
1676+ //NOTE: NODE_TITLE_HEIGHT is explicitly excluded here
1677+ const positionables = [
1678+ ...subgraphNode . subgraph . nodes ,
1679+ ...subgraphNode . subgraph . reroutes . values ( ) ,
1680+ ...subgraphNode . subgraph . groups
1681+ ] . map ( ( p : { pos : Point ; size ?: Size } ) : HasBoundingRect => {
1682+ return {
1683+ boundingRect : [ p . pos [ 0 ] , p . pos [ 1 ] , p . size ?. [ 0 ] ?? 0 , p . size ?. [ 1 ] ?? 0 ]
1684+ }
1685+ } )
1686+ const bounds = createBounds ( positionables ) ?? [ 0 , 0 , 0 , 0 ]
1687+ const center = [ bounds [ 0 ] + bounds [ 2 ] / 2 , bounds [ 1 ] + bounds [ 3 ] / 2 ]
16771688
1689+ const toSelect : Positionable [ ] = [ ]
16781690 const offsetX = subgraphNode . pos [ 0 ] - center [ 0 ] + subgraphNode . size [ 0 ] / 2
16791691 const offsetY = subgraphNode . pos [ 1 ] - center [ 1 ] + subgraphNode . size [ 1 ] / 2
16801692 const movedNodes = multiClone ( subgraphNode . subgraph . nodes )
@@ -1696,6 +1708,21 @@ export class LGraph
16961708 for ( const input of node . inputs ) {
16971709 input . link = null
16981710 }
1711+ for ( const output of node . outputs ) {
1712+ output . links = [ ]
1713+ }
1714+ toSelect . push ( node )
1715+ }
1716+ const groups = structuredClone (
1717+ [ ...subgraphNode . subgraph . groups ] . map ( ( g ) => g . serialize ( ) )
1718+ )
1719+ for ( const g_info of groups ) {
1720+ const group = new LGraphGroup ( g_info . title , g_info . id )
1721+ this . add ( group , true )
1722+ group . configure ( g_info )
1723+ group . pos [ 0 ] += offsetX
1724+ group . pos [ 1 ] += offsetY
1725+ toSelect . push ( group )
16991726 }
17001727 //cleanup reoute.linkIds now, but leave link.parentIds dangling
17011728 for ( const islot of subgraphNode . inputs ) {
@@ -1721,17 +1748,16 @@ export class LGraph
17211748 }
17221749 }
17231750 }
1724-
1725- const newLinks : [
1726- NodeId ,
1727- number ,
1728- NodeId ,
1729- number ,
1730- LinkId ,
1731- RerouteId | undefined ,
1732- RerouteId | undefined ,
1733- boolean
1734- ] [ ] = [ ]
1751+ const newLinks : {
1752+ oid : NodeId
1753+ oslot : number
1754+ tid : NodeId
1755+ tslot : number
1756+ id : LinkId
1757+ iparent ?: RerouteId
1758+ eparent ?: RerouteId
1759+ externalFirst : boolean
1760+ } [ ] = [ ]
17351761 for ( const [ , link ] of subgraphNode . subgraph . _links ) {
17361762 let externalParentId : RerouteId | undefined
17371763 if ( link . origin_id === SUBGRAPH_INPUT_ID ) {
@@ -1756,16 +1782,16 @@ export class LGraph
17561782 for ( const linkId of subgraphNode . outputs [ link . target_slot ] . links ??
17571783 [ ] ) {
17581784 const sublink = this . links [ linkId ]
1759- newLinks . push ( [
1760- link . origin_id ,
1761- link . origin_slot ,
1762- sublink . target_id ,
1763- sublink . target_slot ,
1764- link . id ,
1765- link . parentId ,
1766- sublink . parentId ,
1767- true
1768- ] )
1785+ newLinks . push ( {
1786+ oid : link . origin_id ,
1787+ oslot : link . origin_slot ,
1788+ tid : sublink . target_id ,
1789+ tslot : sublink . target_slot ,
1790+ id : link . id ,
1791+ iparent : link . parentId ,
1792+ eparent : sublink . parentId ,
1793+ externalFirst : true
1794+ } )
17691795 sublink . parentId = undefined
17701796 }
17711797 continue
@@ -1777,60 +1803,60 @@ export class LGraph
17771803 }
17781804 link . target_id = target_id
17791805 }
1780- newLinks . push ( [
1781- link . origin_id ,
1782- link . origin_slot ,
1783- link . target_id ,
1784- link . target_slot ,
1785- link . id ,
1786- link . parentId ,
1787- externalParentId ,
1788- false
1789- ] )
1806+ newLinks . push ( {
1807+ oid : link . origin_id ,
1808+ oslot : link . origin_slot ,
1809+ tid : link . target_id ,
1810+ tslot : link . target_slot ,
1811+ id : link . id ,
1812+ iparent : link . parentId ,
1813+ eparent : externalParentId ,
1814+ externalFirst : false
1815+ } )
17901816 }
17911817 this . remove ( subgraphNode )
17921818 this . subgraphs . delete ( subgraphNode . subgraph . id )
17931819 const linkIdMap = new Map < LinkId , LinkId [ ] > ( )
17941820 for ( const newLink of newLinks ) {
17951821 let created : LLink | null | undefined
1796- if ( newLink [ 0 ] == SUBGRAPH_INPUT_ID ) {
1822+ if ( newLink . oid == SUBGRAPH_INPUT_ID ) {
17971823 if ( ! ( this instanceof Subgraph ) ) {
17981824 console . error ( 'Ignoring link to subgraph outside subgraph' )
17991825 continue
18001826 }
1801- const tnode = this . _nodes_by_id [ newLink [ 2 ] ]
1802- created = this . inputNode . slots [ newLink [ 1 ] ] . connect (
1803- tnode . inputs [ newLink [ 3 ] ] ,
1827+ const tnode = this . _nodes_by_id [ newLink . tid ]
1828+ created = this . inputNode . slots [ newLink . oslot ] . connect (
1829+ tnode . inputs [ newLink . tslot ] ,
18041830 tnode
18051831 )
1806- } else if ( newLink [ 2 ] == SUBGRAPH_OUTPUT_ID ) {
1832+ } else if ( newLink . tid == SUBGRAPH_OUTPUT_ID ) {
18071833 if ( ! ( this instanceof Subgraph ) ) {
18081834 console . error ( 'Ignoring link to subgraph outside subgraph' )
18091835 continue
18101836 }
1811- const tnode = this . _nodes_by_id [ newLink [ 0 ] ]
1812- created = this . outputNode . slots [ newLink [ 3 ] ] . connect (
1813- tnode . outputs [ newLink [ 1 ] ] ,
1837+ const tnode = this . _nodes_by_id [ newLink . oid ]
1838+ created = this . outputNode . slots [ newLink . tslot ] . connect (
1839+ tnode . outputs [ newLink . oslot ] ,
18141840 tnode
18151841 )
18161842 } else {
1817- created = this . _nodes_by_id [ newLink [ 0 ] ] . connect (
1818- newLink [ 1 ] ,
1819- this . _nodes_by_id [ newLink [ 2 ] ] ,
1820- newLink [ 3 ]
1843+ created = this . _nodes_by_id [ newLink . oid ] . connect (
1844+ newLink . oslot ,
1845+ this . _nodes_by_id [ newLink . tid ] ,
1846+ newLink . tslot
18211847 )
18221848 }
18231849 if ( ! created ) {
18241850 console . error ( 'Failed to create link' )
18251851 continue
18261852 }
18271853 //This is a little unwieldy since Map.has isn't a type guard
1828- const linkIds = linkIdMap . get ( newLink [ 4 ] ) ?? [ ]
1854+ const linkIds = linkIdMap . get ( newLink . id ) ?? [ ]
18291855 linkIds . push ( created . id )
1830- if ( ! linkIdMap . has ( newLink [ 4 ] ) ) {
1831- linkIdMap . set ( newLink [ 4 ] , linkIds )
1856+ if ( ! linkIdMap . has ( newLink . id ) ) {
1857+ linkIdMap . set ( newLink . id , linkIds )
18321858 }
1833- newLink [ 4 ] = created . id
1859+ newLink . id = created . id
18341860 }
18351861 const rerouteIdMap = new Map < RerouteId , RerouteId > ( )
18361862 for ( const reroute of subgraphNode . subgraph . reroutes . values ( ) ) {
@@ -1846,17 +1872,18 @@ export class LGraph
18461872 ] )
18471873 rerouteIdMap . set ( reroute . id , migratedReroute . id )
18481874 this . reroutes . set ( migratedReroute . id , migratedReroute )
1875+ toSelect . push ( migratedReroute )
18491876 }
18501877 //iterate over newly created links to update reroute parentIds
18511878 for ( const newLink of newLinks ) {
1852- const linkInstance = this . links . get ( newLink [ 4 ] )
1879+ const linkInstance = this . links . get ( newLink . id )
18531880 if ( ! linkInstance ) {
18541881 continue
18551882 }
18561883 let instance : Reroute | LLink | undefined = linkInstance
1857- let parentId : RerouteId | undefined = newLink [ 6 ]
1858- if ( newLink [ 7 ] ) {
1859- parentId = newLink [ 6 ]
1884+ let parentId : RerouteId | undefined = undefined
1885+ if ( newLink . externalFirst ) {
1886+ parentId = newLink . eparent
18601887 //TODO: recursion check/helper method? Probably exists, but wouldn't mesh with the reference tracking used by this implementation
18611888 while ( parentId ) {
18621889 instance . parentId = parentId
@@ -1868,7 +1895,7 @@ export class LGraph
18681895 parentId = instance . parentId
18691896 }
18701897 }
1871- parentId = newLink [ 5 ]
1898+ parentId = newLink . iparent
18721899 while ( parentId ) {
18731900 const migratedId = rerouteIdMap . get ( parentId )
18741901 if ( ! migratedId ) throw new Error ( 'Broken Id link when unpacking' )
@@ -1882,8 +1909,8 @@ export class LGraph
18821909 if ( ! oldReroute ) throw new Error ( 'Broken Id link when unpacking' )
18831910 parentId = oldReroute . parentId
18841911 }
1885- if ( ! newLink [ 7 ] ) {
1886- parentId = newLink [ 6 ]
1912+ if ( ! newLink . externalFirst ) {
1913+ parentId = newLink . eparent
18871914 while ( parentId ) {
18881915 instance . parentId = parentId
18891916 instance = this . reroutes . get ( parentId )
@@ -1896,18 +1923,13 @@ export class LGraph
18961923 }
18971924 }
18981925
1899- const nodes : LGraphNode [ ] = [ ]
19001926 for ( const nodeId of nodeIdMap . values ( ) ) {
19011927 const node = this . _nodes_by_id [ nodeId ]
1902- nodes . push ( node )
19031928 node . _setConcreteSlots ( )
19041929 node . arrange ( )
19051930 }
1906- const reroutes = [ ...rerouteIdMap . values ( ) ]
1907- . map ( ( i ) => this . reroutes . get ( i ) )
1908- . filter ( ( x ) : x is Reroute => ! ! x )
19091931
1910- this . canvasAction ( ( c ) => c . selectItems ( [ ... nodes , ... reroutes ] ) )
1932+ this . canvasAction ( ( c ) => c . selectItems ( toSelect ) )
19111933 this . afterChange ( )
19121934 }
19131935
0 commit comments