Files
Endoleap/src/lib/canvas-core/structure.ts
T

39 lines
3.6 KiB
TypeScript

import type {DocumentQueries} from './queries.ts';
import type { Change, Document, Id, Target } from './types.ts';
import { commonCanvas, targetElement } from './document.ts';
import { canvasTransform, convertPath } from './coordinates.ts';
import { equal, freeze, fault } from './values.ts';
/** Own the subtree explicitly; geometry/visibility never selects which descendants are deleted. */
export function removalChanges(document:Document,target:Target,queries?:DocumentQueries):readonly Change[] {
if(queries){
if(queries.document!==document)throw fault('stale_query_snapshot');const element=targetElement(document,target),elements=new Set<Id>([element.id]),canvases=new Set<Id>(),child=queries.relations.childCanvasByEntry.get(element.id),stack=child?[child]:[];
while(stack.length){const id=stack.pop()!;if(canvases.has(id))continue;canvases.add(id);stack.push(...queries.childCanvases(id));for(const member of queries.members(id))elements.add(member);}
for(const id of [...elements])for(const edge of queries.incident(id))elements.add(edge);
return freeze([...queries.orderedElements(elements).map(id=>({kind:'element' as const,id,before:document.elements.get(id)!,after:null})),...queries.orderedCanvases(canvases).map(id=>({kind:'canvas' as const,id,before:document.canvases.get(id)!,after:null}))]);
}
const element=targetElement(document,target),elements=new Set<Id>([element.id]),canvases=new Set<Id>(),children=new Map<Id,Id[]>();
for(const canvas of document.canvases.values())if(canvas.parent){const list=children.get(canvas.parent.canvasId)??[];list.push(canvas.id);children.set(canvas.parent.canvasId,list);}
const stack=[...document.canvases.values()].filter(canvas=>canvas.parent?.entryElementId===element.id).map(canvas=>canvas.id);
while(stack.length){const id=stack.pop()!;if(canvases.has(id))continue;canvases.add(id);for(const child of children.get(id)??[])stack.push(child);}
for(const record of document.elements.values())if(canvases.has(record.ownerCanvasId))elements.add(record.id);
for(const record of document.elements.values())if(record.structure==='connection'&&(elements.has(record.source.elementId)||elements.has(record.target.elementId)))elements.add(record.id);
return freeze([
...[...document.elements.values()].filter(record=>elements.has(record.id)).map(record=>({kind:'element' as const,id:record.id,before:record,after:null})),
...[...document.canvases.values()].filter(canvas=>canvases.has(canvas.id)).map(canvas=>({kind:'canvas' as const,id:canvas.id,before:canvas,after:null})),
]);
}
/** Ownership changes and their curve coordinates are one operation, with the same stable edge identity. */
export function repairConnectionOwners(before:Document,document:Document):readonly Change[] {
const changes:Change[]=[];
for(const edge of document.elements.values())if(edge.structure==='connection'){
const source=document.elements.get(edge.source.elementId),target=document.elements.get(edge.target.elementId);if(!source||!target)continue;
const owner=commonCanvas(document,source.ownerCanvasId,target.ownerCanvasId);if(owner===edge.ownerCanvasId)continue;
const path=convertPath(edge.path,canvasTransform(before.canvases.has(edge.ownerCanvasId)?before:document,edge.ownerCanvasId),canvasTransform(document,owner));
changes.push({kind:'field',target:{id:edge.id,incarnation:edge.incarnation},field:'ownerCanvasId',before:edge.ownerCanvasId,after:owner});
if(!equal(edge.path,path))changes.push({kind:'field',target:{id:edge.id,incarnation:edge.incarnation},field:'path',before:edge.path,after:path});
}
return freeze(changes);
}