diff --git a/CHANGELOG.md b/CHANGELOG.md index f748519fd..e15815cdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ - Match regional browser languages to supported locales without selecting a secondary language. (#417) - Save auto-layout frames that stretch their children to `.fig` without failing. (#427) -- Reduce large `.fig` page-switch work to the active page and coalesce Layers tree rebuilds. (#420) +- Reduce large `.fig` page-switch work to the active page, reuse fixed-point propagation indexes, and coalesce Layers tree rebuilds. (#420) - Center text glyphs within explicit line-height leading in CanvasKit paragraph rendering. ### Added diff --git a/packages/fig/src/instance-overrides/index.ts b/packages/fig/src/instance-overrides/index.ts index 9126378f8..7f241d371 100644 --- a/packages/fig/src/instance-overrides/index.ts +++ b/packages/fig/src/instance-overrides/index.ts @@ -85,10 +85,35 @@ function buildKiwiGeometryNodes( return result } -function propagateResolvedFills(graph: SceneGraph, protectedNodes: Set): void { +function componentLinkedNodes(graph: SceneGraph): SceneNode[] { + const nodes: SceneNode[] = [] + for (const node of graph.getAllNodes()) if (node.componentId) nodes.push(node) + return nodes +} + +function instancePlacementPairs( + graph: SceneGraph +): Array<{ sourceChildId: string; childId: string }> { + const pairs: Array<{ sourceChildId: string; childId: string }> = [] + for (const node of graph.getAllNodes()) { + if (node.type !== 'INSTANCE' || !node.componentId) continue + const source = graph.getNode(node.componentId) + if (!source || source.childIds.length !== node.childIds.length) continue + for (let index = 0; index < node.childIds.length; index++) { + pairs.push({ sourceChildId: source.childIds[index], childId: node.childIds[index] }) + } + } + return pairs +} + +function propagateResolvedFills( + graph: SceneGraph, + protectedNodes: Set, + candidates = componentLinkedNodes(graph) +): void { for (let pass = 0; pass < 10; pass++) { let changed = false - for (const node of graph.getAllNodes()) { + for (const node of candidates) { if (!node.componentId) continue const source = graph.getNode(node.componentId) if (!source || isEqual(source.fills, node.fills)) continue @@ -100,32 +125,30 @@ function propagateResolvedFills(graph: SceneGraph, protectedNodes: Set): } } -function propagateResolvedChildPlacementClones(graph: SceneGraph): void { +function propagateResolvedChildPlacementClones( + graph: SceneGraph, + pairs = instancePlacementPairs(graph) +): void { for (let pass = 0; pass < 10; pass++) { let changed = false - for (const node of graph.getAllNodes()) { - if (node.type !== 'INSTANCE' || !node.componentId) continue - const source = graph.getNode(node.componentId) - if (!source || source.childIds.length !== node.childIds.length) continue - for (let i = 0; i < node.childIds.length; i++) { - const sourceChild = graph.getNode(source.childIds[i]) - const child = graph.getNode(node.childIds[i]) - if (!sourceChild || !child) continue - if ( - sourceChild.overrideKey && - child.overrideKey && - sourceChild.overrideKey !== child.overrideKey - ) { - continue - } - const updates: Partial = {} - if (!sourceChild.visible && child.visible) updates.visible = false - if (sourceChild.x !== child.x) updates.x = sourceChild.x - if (sourceChild.y !== child.y) updates.y = sourceChild.y - if (Object.keys(updates).length === 0) continue - graph.updateNode(child.id, updates) - changed = true + for (const pair of pairs) { + const sourceChild = graph.getNode(pair.sourceChildId) + const child = graph.getNode(pair.childId) + if (!sourceChild || !child) continue + if ( + sourceChild.overrideKey && + child.overrideKey && + sourceChild.overrideKey !== child.overrideKey + ) { + continue } + const updates: Partial = {} + if (!sourceChild.visible && child.visible) updates.visible = false + if (sourceChild.x !== child.x) updates.x = sourceChild.x + if (sourceChild.y !== child.y) updates.y = sourceChild.y + if (Object.keys(updates).length === 0) continue + graph.updateNode(child.id, updates) + changed = true } if (!changed) return }