@zukhruf/diagram 0.3.31 → 0.3.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/lib/layouts/graph/areas.js +17 -8
  2. package/dist/lib/layouts/graph/areas.js.map +1 -1
  3. package/dist/lib/layouts/graph/attach.d.ts +25 -0
  4. package/dist/lib/layouts/graph/attach.d.ts.map +1 -0
  5. package/dist/lib/layouts/graph/attach.js +216 -0
  6. package/dist/lib/layouts/graph/attach.js.map +1 -0
  7. package/dist/lib/layouts/graph/connections.d.ts +38 -0
  8. package/dist/lib/layouts/graph/connections.d.ts.map +1 -0
  9. package/dist/lib/layouts/graph/connections.js +112 -0
  10. package/dist/lib/layouts/graph/connections.js.map +1 -0
  11. package/dist/lib/layouts/graph/graph-layout-result.d.ts +1 -1
  12. package/dist/lib/layouts/graph/graph-layout-result.d.ts.map +1 -1
  13. package/dist/lib/layouts/graph/graph-layout-result.js.map +1 -1
  14. package/dist/lib/layouts/graph/gutters.d.ts +1 -2
  15. package/dist/lib/layouts/graph/gutters.d.ts.map +1 -1
  16. package/dist/lib/layouts/graph/gutters.js +20 -19
  17. package/dist/lib/layouts/graph/gutters.js.map +1 -1
  18. package/dist/lib/layouts/graph/labels.d.ts +16 -0
  19. package/dist/lib/layouts/graph/labels.d.ts.map +1 -0
  20. package/dist/lib/layouts/graph/labels.js +76 -0
  21. package/dist/lib/layouts/graph/labels.js.map +1 -0
  22. package/dist/lib/layouts/graph/layout.d.ts.map +1 -1
  23. package/dist/lib/layouts/graph/layout.js +34 -13
  24. package/dist/lib/layouts/graph/layout.js.map +1 -1
  25. package/dist/lib/layouts/graph/measure.d.ts +7 -6
  26. package/dist/lib/layouts/graph/measure.d.ts.map +1 -1
  27. package/dist/lib/layouts/graph/measure.js +35 -29
  28. package/dist/lib/layouts/graph/measure.js.map +1 -1
  29. package/dist/lib/layouts/graph/min-heap.d.ts +12 -0
  30. package/dist/lib/layouts/graph/min-heap.d.ts.map +1 -0
  31. package/dist/lib/layouts/graph/min-heap.js +60 -0
  32. package/dist/lib/layouts/graph/min-heap.js.map +1 -0
  33. package/dist/lib/layouts/graph/node-content.d.ts +1 -2
  34. package/dist/lib/layouts/graph/node-content.d.ts.map +1 -1
  35. package/dist/lib/layouts/graph/node-content.js +43 -42
  36. package/dist/lib/layouts/graph/node-content.js.map +1 -1
  37. package/dist/lib/layouts/graph/path.d.ts +14 -0
  38. package/dist/lib/layouts/graph/path.d.ts.map +1 -0
  39. package/dist/lib/layouts/graph/path.js +55 -0
  40. package/dist/lib/layouts/graph/path.js.map +1 -0
  41. package/dist/lib/layouts/graph/route-search.d.ts +34 -0
  42. package/dist/lib/layouts/graph/route-search.d.ts.map +1 -0
  43. package/dist/lib/layouts/graph/route-search.js +208 -0
  44. package/dist/lib/layouts/graph/route-search.js.map +1 -0
  45. package/dist/lib/layouts/graph/route.d.ts +16 -0
  46. package/dist/lib/layouts/graph/route.d.ts.map +1 -0
  47. package/dist/lib/layouts/graph/route.js +241 -0
  48. package/dist/lib/layouts/graph/route.js.map +1 -0
  49. package/dist/lib/layouts/graph/sides.d.ts +23 -0
  50. package/dist/lib/layouts/graph/sides.d.ts.map +1 -0
  51. package/dist/lib/layouts/graph/sides.js +91 -0
  52. package/dist/lib/layouts/graph/sides.js.map +1 -0
  53. package/dist/lib/layouts/graph/tokens.d.ts +2 -7
  54. package/dist/lib/layouts/graph/tokens.d.ts.map +1 -1
  55. package/dist/lib/layouts/graph/tokens.js +91 -104
  56. package/dist/lib/layouts/graph/tokens.js.map +1 -1
  57. package/dist/lib/model/diagram-ir.d.ts +0 -3
  58. package/dist/lib/model/diagram-ir.d.ts.map +1 -1
  59. package/dist/lib/model/diagram-ir.js.map +1 -1
  60. package/package.json +1 -1
@@ -56,7 +56,15 @@ export function gridAreas(ir) {
56
56
  };
57
57
  }
58
58
  function explicitArea(group) {
59
- if (group.row === undefined && group.col === undefined) return undefined;
59
+ const {
60
+ row,
61
+ col,
62
+ rowSpan,
63
+ colSpan
64
+ } = group;
65
+ if ([row, col, rowSpan, colSpan].every(value => value === undefined)) {
66
+ return undefined;
67
+ }
60
68
  return checkedArea(`group "${group.id}"`, group);
61
69
  }
62
70
  function checkedArea(what, cell) {
@@ -66,18 +74,19 @@ function checkedArea(what, cell) {
66
74
  throw new Error(`Diagram ${what} ${key} ${value} must be a non-negative integer.`);
67
75
  }
68
76
  }
69
- for (const key of ['rowSpan', 'colSpan']) {
70
- const value = cell[key] ?? 1;
71
- if (!Number.isInteger(value) || value < 1) {
72
- throw new Error(`Diagram ${what} ${key} ${value} must be an integer of at least 1.`);
73
- }
74
- }
75
- return {
77
+ // An area without a span covers one track, as a CSS Grid item does.
78
+ const area = {
76
79
  row: cell.row,
77
80
  col: cell.col,
78
81
  rowSpan: cell.rowSpan ?? 1,
79
82
  colSpan: cell.colSpan ?? 1
80
83
  };
84
+ for (const key of ['rowSpan', 'colSpan']) {
85
+ if (!Number.isInteger(area[key]) || area[key] < 1) {
86
+ throw new Error(`Diagram ${what} ${key} ${area[key]} must be an integer of at least 1.`);
87
+ }
88
+ }
89
+ return area;
81
90
  }
82
91
  function boundingArea(group, members) {
83
92
  if (members.length === 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"areas.js","names":["gridAreas","ir","nodes","Map","map","node","id","checkedArea","rejectSharedCells","groupsById","groups","group","undefined","has","Error","depth","parent","get","areas","areaOf","known","members","filter","what","area","child","explicitArea","boundingArea","member","covers","formatArea","set","sort","a","b","rejectSiblingOverlap","rejectStrangers","rejectSingleCellDividers","all","values","rows","Math","max","row","rowSpan","cols","col","colSpan","cell","key","value","Number","isInteger","length","min","occupant","cellsOf","other","index","entries","slice","firstSharedCell","ancestors","ids","Set","add","memberOf","groupArea","dividers","outer","inner","shared","range","start","span"],"sources":["../../../../src/lib/layouts/graph/areas.ts"],"sourcesContent":["import type {\n DiagramGraphIR,\n DiagramGroup,\n DiagramNode,\n} from '../../model/diagram-ir.ts';\n\n/** A rectangle of grid cells: rows `[row, row + rowSpan)` and columns `[col, col + colSpan)`. */\nexport type CellArea = {\n row: number;\n col: number;\n rowSpan: number;\n colSpan: number;\n};\n\nexport type GroupArea = {\n group: DiagramGroup;\n /** The nesting level; a top-level group is depth 0. */\n depth: number;\n area: CellArea;\n};\n\nexport type GridAreas = {\n rows: number;\n cols: number;\n nodes: Map<string, CellArea>;\n /** Parents before their children. */\n groups: GroupArea[];\n};\n\n/**\n * Gives every node and group its cell area and rejects a grid that cannot be\n * drawn without overlap. Each error names the elements and the cells.\n */\nexport function gridAreas(ir: DiagramGraphIR): GridAreas {\n const nodes = new Map(\n ir.nodes.map(\n (node) => [node.id, checkedArea(`node \"${node.id}\"`, node)] as const,\n ),\n );\n rejectSharedCells(ir.nodes, nodes);\n\n const groupsById = new Map(ir.groups.map((group) => [group.id, group]));\n for (const node of ir.nodes) {\n if (node.group !== undefined && !groupsById.has(node.group)) {\n throw new Error(\n `Diagram node \"${node.id}\" names group \"${node.group}\", which does not exist.`,\n );\n }\n }\n const depth = (group: DiagramGroup): number => {\n const parent = group.parent && groupsById.get(group.parent);\n return parent ? depth(parent) + 1 : 0;\n };\n\n const areas = new Map<string, CellArea>();\n const areaOf = (group: DiagramGroup): CellArea => {\n const known = areas.get(group.id);\n if (known) return known;\n const members = [\n ...ir.nodes\n .filter((node) => node.group === group.id)\n .map((node) => ({\n what: `node \"${node.id}\"`,\n area: nodes.get(node.id)!,\n })),\n ...ir.groups\n .filter((child) => child.parent === group.id)\n .map((child) => ({ what: `group \"${child.id}\"`, area: areaOf(child) })),\n ];\n const area = explicitArea(group) ?? boundingArea(group, members);\n for (const member of members) {\n if (!covers(area, member.area)) {\n throw new Error(\n `Diagram group \"${group.id}\" area ${formatArea(area)} misses its member ${member.what} at ${formatArea(member.area)}.`,\n );\n }\n }\n areas.set(group.id, area);\n return area;\n };\n\n const groups = ir.groups\n .map((group) => ({ group, depth: depth(group), area: areaOf(group) }))\n .sort((a, b) => a.depth - b.depth);\n rejectSiblingOverlap(groups);\n rejectStrangers(ir.nodes, nodes, groups, groupsById);\n rejectSingleCellDividers(groups);\n\n const all = [...nodes.values(), ...groups.map(({ area }) => area)];\n return {\n rows: Math.max(0, ...all.map((area) => area.row + area.rowSpan)),\n cols: Math.max(0, ...all.map((area) => area.col + area.colSpan)),\n nodes,\n groups,\n };\n}\n\nfunction explicitArea(group: DiagramGroup): CellArea | undefined {\n if (group.row === undefined && group.col === undefined) return undefined;\n return checkedArea(`group \"${group.id}\"`, group);\n}\n\nfunction checkedArea(\n what: string,\n cell: {\n row?: number | undefined;\n col?: number | undefined;\n rowSpan?: number | undefined;\n colSpan?: number | undefined;\n },\n): CellArea {\n for (const key of ['row', 'col'] as const) {\n const value = cell[key];\n if (value === undefined || !Number.isInteger(value) || value < 0) {\n throw new Error(\n `Diagram ${what} ${key} ${value} must be a non-negative integer.`,\n );\n }\n }\n for (const key of ['rowSpan', 'colSpan'] as const) {\n const value = cell[key] ?? 1;\n if (!Number.isInteger(value) || value < 1) {\n throw new Error(\n `Diagram ${what} ${key} ${value} must be an integer of at least 1.`,\n );\n }\n }\n return {\n row: cell.row!,\n col: cell.col!,\n rowSpan: cell.rowSpan ?? 1,\n colSpan: cell.colSpan ?? 1,\n };\n}\n\nfunction boundingArea(\n group: DiagramGroup,\n members: { area: CellArea }[],\n): CellArea {\n if (members.length === 0) {\n throw new Error(\n `Diagram group \"${group.id}\" has no members and no area. Give it members or row, col, rowSpan and colSpan.`,\n );\n }\n const areas = members.map(({ area }) => area);\n const row = Math.min(...areas.map((area) => area.row));\n const col = Math.min(...areas.map((area) => area.col));\n return {\n row,\n col,\n rowSpan: Math.max(...areas.map((area) => area.row + area.rowSpan)) - row,\n colSpan: Math.max(...areas.map((area) => area.col + area.colSpan)) - col,\n };\n}\n\nfunction rejectSharedCells(\n nodes: DiagramNode[],\n areas: Map<string, CellArea>,\n): void {\n const occupant = new Map<string, string>();\n for (const node of nodes) {\n for (const { row, col } of cellsOf(areas.get(node.id)!)) {\n const key = `${row},${col}`;\n const other = occupant.get(key);\n if (other !== undefined) {\n throw new Error(\n `Diagram nodes \"${other}\" and \"${node.id}\" both occupy cell (row ${row}, col ${col}).`,\n );\n }\n occupant.set(key, node.id);\n }\n }\n}\n\nfunction rejectSiblingOverlap(groups: GroupArea[]): void {\n for (const [index, a] of groups.entries()) {\n for (const b of groups.slice(index + 1)) {\n if (a.group.parent !== b.group.parent) continue;\n const cell = firstSharedCell(a.area, b.area);\n if (cell) {\n throw new Error(\n `Diagram groups \"${a.group.id}\" and \"${b.group.id}\" overlap at cell (row ${cell.row}, col ${cell.col}). Sibling groups need disjoint areas.`,\n );\n }\n }\n }\n}\n\n/** A node inside a group's area must belong to the group, or its border would cut through the node. */\nfunction rejectStrangers(\n nodes: DiagramNode[],\n areas: Map<string, CellArea>,\n groups: GroupArea[],\n groupsById: Map<string, DiagramGroup>,\n): void {\n const ancestors = (node: DiagramNode) => {\n const ids = new Set<string>();\n for (\n let id = node.group;\n id !== undefined && !ids.has(id);\n id = groupsById.get(id)?.parent\n ) {\n ids.add(id);\n }\n return ids;\n };\n for (const node of nodes) {\n const memberOf = ancestors(node);\n const area = areas.get(node.id)!;\n for (const { group, area: groupArea } of groups) {\n if (memberOf.has(group.id) || !firstSharedCell(area, groupArea)) continue;\n throw new Error(\n `Diagram node \"${node.id}\" lies inside the area of group \"${group.id}\" ${formatArea(groupArea)} but is not its member. Move it into the group or out of the area.`,\n );\n }\n }\n}\n\nfunction rejectSingleCellDividers(groups: GroupArea[]): void {\n for (const { group, area } of groups) {\n if (group.dividers && area.rowSpan === 1 && area.colSpan === 1) {\n throw new Error(\n `Diagram group \"${group.id}\" has dividers, but its area is the one cell (row ${area.row}, col ${area.col}). Dividers need two rows or two columns.`,\n );\n }\n }\n}\n\nfunction covers(outer: CellArea, inner: CellArea): boolean {\n return (\n inner.row >= outer.row &&\n inner.col >= outer.col &&\n inner.row + inner.rowSpan <= outer.row + outer.rowSpan &&\n inner.col + inner.colSpan <= outer.col + outer.colSpan\n );\n}\n\nfunction firstSharedCell(\n a: CellArea,\n b: CellArea,\n): { row: number; col: number } | undefined {\n const row = Math.max(a.row, b.row);\n const col = Math.max(a.col, b.col);\n const shared =\n row < Math.min(a.row + a.rowSpan, b.row + b.rowSpan) &&\n col < Math.min(a.col + a.colSpan, b.col + b.colSpan);\n return shared ? { row, col } : undefined;\n}\n\nfunction* cellsOf(area: CellArea) {\n for (let row = area.row; row < area.row + area.rowSpan; row++) {\n for (let col = area.col; col < area.col + area.colSpan; col++) {\n yield { row, col };\n }\n }\n}\n\n/** Inclusive ranges, as an author reads them: `(rows 2–6, cols 3–6)`. */\nfunction formatArea(area: CellArea): string {\n const range = (start: number, span: number) =>\n span === 1 ? `${start}` : `${start}–${start + span - 1}`;\n const rows = `${area.rowSpan === 1 ? 'row' : 'rows'} ${range(area.row, area.rowSpan)}`;\n const cols = `${area.colSpan === 1 ? 'col' : 'cols'} ${range(area.col, area.colSpan)}`;\n return `(${rows}, ${cols})`;\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"areas.js","names":["gridAreas","ir","nodes","Map","map","node","id","checkedArea","rejectSharedCells","groupsById","groups","group","undefined","has","Error","depth","parent","get","areas","areaOf","known","members","filter","what","area","child","explicitArea","boundingArea","member","covers","formatArea","set","sort","a","b","rejectSiblingOverlap","rejectStrangers","rejectSingleCellDividers","all","values","rows","Math","max","row","rowSpan","cols","col","colSpan","every","value","cell","key","Number","isInteger","length","min","occupant","cellsOf","other","index","entries","slice","firstSharedCell","ancestors","ids","Set","add","memberOf","groupArea","dividers","outer","inner","shared","range","start","span"],"sources":["../../../../src/lib/layouts/graph/areas.ts"],"sourcesContent":["import type {\n DiagramGraphIR,\n DiagramGroup,\n DiagramNode,\n} from '../../model/diagram-ir.ts';\n\n/** A rectangle of grid cells: rows `[row, row + rowSpan)` and columns `[col, col + colSpan)`. */\nexport type CellArea = {\n row: number;\n col: number;\n rowSpan: number;\n colSpan: number;\n};\n\nexport type GroupArea = {\n group: DiagramGroup;\n /** The nesting level; a top-level group is depth 0. */\n depth: number;\n area: CellArea;\n};\n\nexport type GridAreas = {\n rows: number;\n cols: number;\n nodes: Map<string, CellArea>;\n /** Parents before their children. */\n groups: GroupArea[];\n};\n\n/**\n * Gives every node and group its cell area and rejects a grid that cannot be\n * drawn without overlap. Each error names the elements and the cells.\n */\nexport function gridAreas(ir: DiagramGraphIR): GridAreas {\n const nodes = new Map(\n ir.nodes.map(\n (node) => [node.id, checkedArea(`node \"${node.id}\"`, node)] as const,\n ),\n );\n rejectSharedCells(ir.nodes, nodes);\n\n const groupsById = new Map(ir.groups.map((group) => [group.id, group]));\n for (const node of ir.nodes) {\n if (node.group !== undefined && !groupsById.has(node.group)) {\n throw new Error(\n `Diagram node \"${node.id}\" names group \"${node.group}\", which does not exist.`,\n );\n }\n }\n const depth = (group: DiagramGroup): number => {\n const parent = group.parent && groupsById.get(group.parent);\n return parent ? depth(parent) + 1 : 0;\n };\n\n const areas = new Map<string, CellArea>();\n const areaOf = (group: DiagramGroup): CellArea => {\n const known = areas.get(group.id);\n if (known) return known;\n const members = [\n ...ir.nodes\n .filter((node) => node.group === group.id)\n .map((node) => ({\n what: `node \"${node.id}\"`,\n area: nodes.get(node.id)!,\n })),\n ...ir.groups\n .filter((child) => child.parent === group.id)\n .map((child) => ({ what: `group \"${child.id}\"`, area: areaOf(child) })),\n ];\n const area = explicitArea(group) ?? boundingArea(group, members);\n for (const member of members) {\n if (!covers(area, member.area)) {\n throw new Error(\n `Diagram group \"${group.id}\" area ${formatArea(area)} misses its member ${member.what} at ${formatArea(member.area)}.`,\n );\n }\n }\n areas.set(group.id, area);\n return area;\n };\n\n const groups = ir.groups\n .map((group) => ({ group, depth: depth(group), area: areaOf(group) }))\n .sort((a, b) => a.depth - b.depth);\n rejectSiblingOverlap(groups);\n rejectStrangers(ir.nodes, nodes, groups, groupsById);\n rejectSingleCellDividers(groups);\n\n const all = [...nodes.values(), ...groups.map(({ area }) => area)];\n return {\n rows: Math.max(0, ...all.map((area) => area.row + area.rowSpan)),\n cols: Math.max(0, ...all.map((area) => area.col + area.colSpan)),\n nodes,\n groups,\n };\n}\n\nfunction explicitArea(group: DiagramGroup): CellArea | undefined {\n const { row, col, rowSpan, colSpan } = group;\n if ([row, col, rowSpan, colSpan].every((value) => value === undefined)) {\n return undefined;\n }\n return checkedArea(`group \"${group.id}\"`, group);\n}\n\nfunction checkedArea(\n what: string,\n cell: {\n row?: number | undefined;\n col?: number | undefined;\n rowSpan?: number | undefined;\n colSpan?: number | undefined;\n },\n): CellArea {\n for (const key of ['row', 'col'] as const) {\n const value = cell[key];\n if (value === undefined || !Number.isInteger(value) || value < 0) {\n throw new Error(\n `Diagram ${what} ${key} ${value} must be a non-negative integer.`,\n );\n }\n }\n // An area without a span covers one track, as a CSS Grid item does.\n const area: CellArea = {\n row: cell.row!,\n col: cell.col!,\n rowSpan: cell.rowSpan ?? 1,\n colSpan: cell.colSpan ?? 1,\n };\n for (const key of ['rowSpan', 'colSpan'] as const) {\n if (!Number.isInteger(area[key]) || area[key] < 1) {\n throw new Error(\n `Diagram ${what} ${key} ${area[key]} must be an integer of at least 1.`,\n );\n }\n }\n return area;\n}\n\nfunction boundingArea(\n group: DiagramGroup,\n members: { area: CellArea }[],\n): CellArea {\n if (members.length === 0) {\n throw new Error(\n `Diagram group \"${group.id}\" has no members and no area. Give it members or row, col, rowSpan and colSpan.`,\n );\n }\n const areas = members.map(({ area }) => area);\n const row = Math.min(...areas.map((area) => area.row));\n const col = Math.min(...areas.map((area) => area.col));\n return {\n row,\n col,\n rowSpan: Math.max(...areas.map((area) => area.row + area.rowSpan)) - row,\n colSpan: Math.max(...areas.map((area) => area.col + area.colSpan)) - col,\n };\n}\n\nfunction rejectSharedCells(\n nodes: DiagramNode[],\n areas: Map<string, CellArea>,\n): void {\n const occupant = new Map<string, string>();\n for (const node of nodes) {\n for (const { row, col } of cellsOf(areas.get(node.id)!)) {\n const key = `${row},${col}`;\n const other = occupant.get(key);\n if (other !== undefined) {\n throw new Error(\n `Diagram nodes \"${other}\" and \"${node.id}\" both occupy cell (row ${row}, col ${col}).`,\n );\n }\n occupant.set(key, node.id);\n }\n }\n}\n\nfunction rejectSiblingOverlap(groups: GroupArea[]): void {\n for (const [index, a] of groups.entries()) {\n for (const b of groups.slice(index + 1)) {\n if (a.group.parent !== b.group.parent) continue;\n const cell = firstSharedCell(a.area, b.area);\n if (cell) {\n throw new Error(\n `Diagram groups \"${a.group.id}\" and \"${b.group.id}\" overlap at cell (row ${cell.row}, col ${cell.col}). Sibling groups need disjoint areas.`,\n );\n }\n }\n }\n}\n\n/** A node inside a group's area must belong to the group, or its border would cut through the node. */\nfunction rejectStrangers(\n nodes: DiagramNode[],\n areas: Map<string, CellArea>,\n groups: GroupArea[],\n groupsById: Map<string, DiagramGroup>,\n): void {\n const ancestors = (node: DiagramNode) => {\n const ids = new Set<string>();\n for (\n let id = node.group;\n id !== undefined && !ids.has(id);\n id = groupsById.get(id)?.parent\n ) {\n ids.add(id);\n }\n return ids;\n };\n for (const node of nodes) {\n const memberOf = ancestors(node);\n const area = areas.get(node.id)!;\n for (const { group, area: groupArea } of groups) {\n if (memberOf.has(group.id) || !firstSharedCell(area, groupArea)) continue;\n throw new Error(\n `Diagram node \"${node.id}\" lies inside the area of group \"${group.id}\" ${formatArea(groupArea)} but is not its member. Move it into the group or out of the area.`,\n );\n }\n }\n}\n\nfunction rejectSingleCellDividers(groups: GroupArea[]): void {\n for (const { group, area } of groups) {\n if (group.dividers && area.rowSpan === 1 && area.colSpan === 1) {\n throw new Error(\n `Diagram group \"${group.id}\" has dividers, but its area is the one cell (row ${area.row}, col ${area.col}). Dividers need two rows or two columns.`,\n );\n }\n }\n}\n\nfunction covers(outer: CellArea, inner: CellArea): boolean {\n return (\n inner.row >= outer.row &&\n inner.col >= outer.col &&\n inner.row + inner.rowSpan <= outer.row + outer.rowSpan &&\n inner.col + inner.colSpan <= outer.col + outer.colSpan\n );\n}\n\nfunction firstSharedCell(\n a: CellArea,\n b: CellArea,\n): { row: number; col: number } | undefined {\n const row = Math.max(a.row, b.row);\n const col = Math.max(a.col, b.col);\n const shared =\n row < Math.min(a.row + a.rowSpan, b.row + b.rowSpan) &&\n col < Math.min(a.col + a.colSpan, b.col + b.colSpan);\n return shared ? { row, col } : undefined;\n}\n\nfunction* cellsOf(area: CellArea) {\n for (let row = area.row; row < area.row + area.rowSpan; row++) {\n for (let col = area.col; col < area.col + area.colSpan; col++) {\n yield { row, col };\n }\n }\n}\n\n/** Inclusive ranges, as an author reads them: `(rows 2–6, cols 3–6)`. */\nfunction formatArea(area: CellArea): string {\n const range = (start: number, span: number) =>\n span === 1 ? `${start}` : `${start}–${start + span - 1}`;\n const rows = `${area.rowSpan === 1 ? 'row' : 'rows'} ${range(area.row, area.rowSpan)}`;\n const cols = `${area.colSpan === 1 ? 'col' : 'cols'} ${range(area.col, area.colSpan)}`;\n return `(${rows}, ${cols})`;\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,25 @@
1
+ import type { Box, Point } from '../../model/diagram-ir.ts';
2
+ import type { Connection } from './connections.ts';
3
+ /** Where a connection touches its two shapes, and where its stubs leave them. */
4
+ export type Attachment = {
5
+ start: Point;
6
+ startStub: Point;
7
+ end: Point;
8
+ endStub: Point;
9
+ };
10
+ /**
11
+ * Picks where every connection touches its shapes. A junction end is its
12
+ * centre and a port end is its port. The other ends that share a side of a
13
+ * shape split the free part of it, clear of the corners and of a caption on
14
+ * that side, into even slots, ordered by where their other ends lie, so they
15
+ * do not cross. A connection alone on two facing sides that overlap runs
16
+ * straight across the middle of the widest part of the overlap that no
17
+ * group label text lies on.
18
+ */
19
+ export declare function attachConnections(connections: Connection[],
20
+ /** Group label text, by group id: a straight line between two facing sides passes beside it. */
21
+ groupLabels: {
22
+ group: string;
23
+ box: Box;
24
+ }[]): Attachment[];
25
+ //# sourceMappingURL=attach.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attach.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/graph/attach.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,kBAAkB,CAAC;AAYlE,iFAAiF;AACjF,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,KAAK,CAAC;IACjB,GAAG,EAAE,KAAK,CAAC;IACX,OAAO,EAAE,KAAK,CAAC;CAChB,CAAC;AAQF;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,UAAU,EAAE;AACzB,gGAAgG;AAChG,WAAW,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,GAAG,CAAA;CAAE,EAAE,GACzC,UAAU,EAAE,CA+Ed"}
@@ -0,0 +1,216 @@
1
+ import { GRID } from "./route-search.js";
2
+ import { OPPOSITE, beyond, isVertical, pointOnSide, sideLine, sideSpan } from "./sides.js";
3
+ import { LAYOUT_TOKENS } from "./tokens.js";
4
+
5
+ /** Where a connection touches its two shapes, and where its stubs leave them. */
6
+
7
+ /** The closest two slots on one side may lie: two grid lines, so their routes keep a line between them. */
8
+ const SLOT_PITCH = 2 * GRID;
9
+ /**
10
+ * Picks where every connection touches its shapes. A junction end is its
11
+ * centre and a port end is its port. The other ends that share a side of a
12
+ * shape split the free part of it, clear of the corners and of a caption on
13
+ * that side, into even slots, ordered by where their other ends lie, so they
14
+ * do not cross. A connection alone on two facing sides that overlap runs
15
+ * straight across the middle of the widest part of the overlap that no
16
+ * group label text lies on.
17
+ */
18
+ export function attachConnections(connections, /** Group label text, by group id: a straight line between two facing sides passes beside it. */
19
+ groupLabels) {
20
+ const fixed = end => {
21
+ if (end.port) return end.port.point;
22
+ if (end.shape.kind === 'junction') return centre(end.shape.box);
23
+ return undefined;
24
+ };
25
+ const bySide = new Map();
26
+ for (const [index, connection] of connections.entries()) {
27
+ for (const role of ['from', 'to']) {
28
+ const end = connection[role];
29
+ if (fixed(end)) continue;
30
+ const key = sideKey(end);
31
+ bySide.set(key, [...(bySide.get(key) ?? []), {
32
+ connection: index,
33
+ role,
34
+ end
35
+ }]);
36
+ }
37
+ }
38
+ const crowd = end => bySide.get(sideKey(end)).length;
39
+
40
+ /** Where each slot end touches its side, by `${connection}:${role}`. */
41
+ const touches = new Map();
42
+ const touchKey = (connection, role) => `${connection}:${role}`;
43
+ for (const [index, {
44
+ from,
45
+ to
46
+ }] of connections.entries()) {
47
+ const between = groupLabels.flatMap(({
48
+ group,
49
+ box
50
+ }) => group === from.shape.id || group === to.shape.id ? [] : [box]);
51
+ const aligned = facingAlignment(from, to, fixed(from), fixed(to), crowd, between);
52
+ if (aligned) {
53
+ touches.set(touchKey(index, 'from'), aligned.from);
54
+ touches.set(touchKey(index, 'to'), aligned.to);
55
+ }
56
+ }
57
+ for (const slots of bySide.values()) {
58
+ const open = slots.filter(({
59
+ connection,
60
+ role
61
+ }) => !touches.has(touchKey(connection, role)));
62
+ if (open.length === 0) continue;
63
+ const {
64
+ end
65
+ } = slots[0];
66
+ const vertical = isVertical(end.side);
67
+ const otherAt = ({
68
+ connection,
69
+ role
70
+ }) => {
71
+ const other = connections[connection][role === 'from' ? 'to' : 'from'];
72
+ const at = fixed(other) ?? centre(other.shape.box);
73
+ return vertical ? at.x : at.y;
74
+ };
75
+ const ordered = open.map((slot, order) => ({
76
+ slot,
77
+ order,
78
+ at: otherAt(slot)
79
+ })).sort((a, b) => a.at - b.at || a.order - b.order);
80
+ const alongs = spread(end, ordered.length);
81
+ for (const [index, {
82
+ slot
83
+ }] of ordered.entries()) {
84
+ touches.set(touchKey(slot.connection, slot.role), pointOnSide(end.shape.box, end.side, alongs[index]));
85
+ }
86
+ }
87
+ return connections.map((connection, index) => {
88
+ const start = fixed(connection.from) ?? touches.get(touchKey(index, 'from'));
89
+ const end = fixed(connection.to) ?? touches.get(touchKey(index, 'to'));
90
+ const stub = stubLength(connection, start, end);
91
+ return {
92
+ start,
93
+ startStub: beyond(start, connection.from.side, stub),
94
+ end,
95
+ endStub: beyond(end, connection.to.side, stub)
96
+ };
97
+ });
98
+ }
99
+
100
+ /**
101
+ * The parts of a side a connection may touch: the side less its corner
102
+ * inset, less what lies behind a caption on that side. A short side gives up
103
+ * its corner inset before its slots come closer than SLOT_PITCH.
104
+ */
105
+ function freeIntervals(end, count) {
106
+ const {
107
+ shape,
108
+ side
109
+ } = end;
110
+ const [from, to] = sideSpan(shape.box, side);
111
+ const inset = Math.max(0, Math.min(shape.radius, (to - from - (count + 1) * SLOT_PITCH) / 2));
112
+ const vertical = isVertical(side);
113
+ const line = sideLine(shape.box, side);
114
+ const outward = side === 'bottom' || side === 'right';
115
+ const behind = shape.clear.flatMap(box => {
116
+ const [near, far] = vertical ? [box.y, box.y + box.h] : [box.x, box.x + box.w];
117
+ if (outward ? near < line : far > line) return [];
118
+ return vertical ? [[box.x - SLOT_PITCH, box.x + box.w + SLOT_PITCH]] : [[box.y - SLOT_PITCH, box.y + box.h + SLOT_PITCH]];
119
+ });
120
+ return cut([[from + inset, to - inset]], behind);
121
+ }
122
+
123
+ /** The parts of `intervals` outside every one of `cuts`. */
124
+ function cut(intervals, cuts) {
125
+ let free = intervals;
126
+ for (const [cutStart, cutEnd] of cuts) {
127
+ free = free.flatMap(([a, b]) => [[a, Math.min(b, cutStart)], [Math.max(a, cutEnd), b]]).filter(([low, high]) => high > low);
128
+ }
129
+ return free;
130
+ }
131
+
132
+ /**
133
+ * Positions for `count` slots on the free parts of a side, in order along
134
+ * it: each next slot goes to the part where slots would lie farthest apart,
135
+ * and each part spaces its slots evenly.
136
+ */
137
+ function spread(end, count) {
138
+ const intervals = freeIntervals(end, count);
139
+ const counts = intervals.map(() => 0);
140
+ const pitchWithOneMore = index => (intervals[index][1] - intervals[index][0]) / (counts[index] + 2);
141
+ for (let placed = 0; placed < count && intervals.length > 0; placed++) {
142
+ const roomiest = counts.reduce((best, _, index) => pitchWithOneMore(index) > pitchWithOneMore(best) ? index : best, 0);
143
+ counts[roomiest] = counts[roomiest] + 1;
144
+ }
145
+ const alongs = intervals.flatMap(([a, b], index) => Array.from({
146
+ length: counts[index]
147
+ }, (_, slot) => a + (slot + 1) / (counts[index] + 1) * (b - a)));
148
+ const closest = Math.min(...intervals.flatMap(([a, b], index) => counts[index] > 1 ? [(b - a) / (counts[index] + 1)] : []));
149
+ if (alongs.length < count || closest < SLOT_PITCH) {
150
+ const {
151
+ shape,
152
+ side
153
+ } = end;
154
+ throw new Error(`Diagram ${shape.kind === 'group' ? 'group' : 'node'} "${shape.id}" side ${side} has no room for ${count} connections ${SLOT_PITCH}px apart beside its corners and caption.`);
155
+ }
156
+ return alongs;
157
+ }
158
+
159
+ /**
160
+ * Both ends on the middle of the widest part of the overlap of two facing
161
+ * sides that no label in `labels` between the sides lies on, when the
162
+ * connection is alone on each.
163
+ */
164
+ function facingAlignment(from, to, fromFixed, toFixed, crowd, labels) {
165
+ if (to.side !== OPPOSITE[from.side] || fromFixed && toFixed) return;
166
+ if (!fromFixed && crowd(from) !== 1) return;
167
+ if (!toFixed && crowd(to) !== 1) return;
168
+ const vertical = isVertical(from.side);
169
+ const free = (end, at) => {
170
+ if (!at) return freeIntervals(end, 1);
171
+ const along = vertical ? at.x : at.y;
172
+ return [[along, along]];
173
+ };
174
+ const lines = [sideLine(from.shape.box, from.side), sideLine(to.shape.box, to.side)];
175
+ const [gapStart, gapEnd] = [Math.min(...lines), Math.max(...lines)];
176
+ const inGap = labels.flatMap(({
177
+ x,
178
+ y,
179
+ w,
180
+ h
181
+ }) => {
182
+ const [across, acrossEnd, along, alongEnd] = vertical ? [y, y + h, x, x + w] : [x, x + w, y, y + h];
183
+ return across < gapEnd && acrossEnd > gapStart ? [[along, alongEnd]] : [];
184
+ });
185
+ const widest = free(from, fromFixed).flatMap(([a0, a1]) => free(to, toFixed).flatMap(([b0, b1]) => cut([[Math.max(a0, b0), Math.min(a1, b1)]], inGap))).filter(([low, high]) => low <= high).sort((a, b) => b[1] - b[0] - (a[1] - a[0]))[0];
186
+ if (!widest) return;
187
+ const along = (widest[0] + widest[1]) / 2;
188
+ return {
189
+ from: fromFixed ?? pointOnSide(from.shape.box, from.side, along),
190
+ to: toFixed ?? pointOnSide(to.shape.box, to.side, along)
191
+ };
192
+ }
193
+
194
+ /**
195
+ * A stub is STUB long, except between two ends that face each other closer
196
+ * than two stubs: then each stub reaches half way, so they meet in the gap.
197
+ */
198
+ function stubLength(connection, start, end) {
199
+ const {
200
+ from,
201
+ to
202
+ } = connection;
203
+ if (to.side !== OPPOSITE[from.side]) return LAYOUT_TOKENS.STUB;
204
+ const gap = (isVertical(from.side) ? end.y - start.y : end.x - start.x) * (from.side === 'bottom' || from.side === 'right' ? 1 : -1);
205
+ return gap > 0 && gap < 2 * LAYOUT_TOKENS.STUB ? gap / 2 : LAYOUT_TOKENS.STUB;
206
+ }
207
+ function sideKey(end) {
208
+ return `${end.shape.id}\u0000${end.side}`;
209
+ }
210
+ function centre(box) {
211
+ return {
212
+ x: box.x + box.w / 2,
213
+ y: box.y + box.h / 2
214
+ };
215
+ }
216
+ //# sourceMappingURL=attach.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attach.js","names":["GRID","OPPOSITE","beyond","isVertical","pointOnSide","sideLine","sideSpan","LAYOUT_TOKENS","SLOT_PITCH","attachConnections","connections","groupLabels","fixed","end","port","point","shape","kind","centre","box","undefined","bySide","Map","index","connection","entries","role","key","sideKey","set","get","crowd","length","touches","touchKey","from","to","between","flatMap","group","id","aligned","facingAlignment","slots","values","open","filter","has","vertical","side","otherAt","other","at","x","y","ordered","map","slot","order","sort","a","b","alongs","spread","start","stub","stubLength","startStub","endStub","freeIntervals","count","inset","Math","max","min","radius","line","outward","behind","clear","near","far","h","w","cut","intervals","cuts","free","cutStart","cutEnd","low","high","counts","pitchWithOneMore","placed","roomiest","reduce","best","_","Array","closest","Error","fromFixed","toFixed","labels","along","lines","gapStart","gapEnd","inGap","across","acrossEnd","alongEnd","widest","a0","a1","b0","b1","STUB","gap"],"sources":["../../../../src/lib/layouts/graph/attach.ts"],"sourcesContent":["import type { Box, Point } from '../../model/diagram-ir.ts';\nimport type { Connection, ConnectionEnd } from './connections.ts';\nimport { GRID } from './route-search.ts';\nimport {\n OPPOSITE,\n beyond,\n isVertical,\n pointOnSide,\n sideLine,\n sideSpan,\n} from './sides.ts';\nimport { LAYOUT_TOKENS } from './tokens.ts';\n\n/** Where a connection touches its two shapes, and where its stubs leave them. */\nexport type Attachment = {\n start: Point;\n startStub: Point;\n end: Point;\n endStub: Point;\n};\n\n/** The closest two slots on one side may lie: two grid lines, so their routes keep a line between them. */\nconst SLOT_PITCH = 2 * GRID;\n\ntype Slot = { connection: number; role: 'from' | 'to'; end: ConnectionEnd };\ntype Interval = [number, number];\n\n/**\n * Picks where every connection touches its shapes. A junction end is its\n * centre and a port end is its port. The other ends that share a side of a\n * shape split the free part of it, clear of the corners and of a caption on\n * that side, into even slots, ordered by where their other ends lie, so they\n * do not cross. A connection alone on two facing sides that overlap runs\n * straight across the middle of the widest part of the overlap that no\n * group label text lies on.\n */\nexport function attachConnections(\n connections: Connection[],\n /** Group label text, by group id: a straight line between two facing sides passes beside it. */\n groupLabels: { group: string; box: Box }[],\n): Attachment[] {\n const fixed = (end: ConnectionEnd): Point | undefined => {\n if (end.port) return end.port.point;\n if (end.shape.kind === 'junction') return centre(end.shape.box);\n return undefined;\n };\n\n const bySide = new Map<string, Slot[]>();\n for (const [index, connection] of connections.entries()) {\n for (const role of ['from', 'to'] as const) {\n const end = connection[role];\n if (fixed(end)) continue;\n const key = sideKey(end);\n bySide.set(key, [\n ...(bySide.get(key) ?? []),\n { connection: index, role, end },\n ]);\n }\n }\n const crowd = (end: ConnectionEnd) => bySide.get(sideKey(end))!.length;\n\n /** Where each slot end touches its side, by `${connection}:${role}`. */\n const touches = new Map<string, Point>();\n const touchKey = (connection: number, role: 'from' | 'to') =>\n `${connection}:${role}`;\n for (const [index, { from, to }] of connections.entries()) {\n const between = groupLabels.flatMap(({ group, box }) =>\n group === from.shape.id || group === to.shape.id ? [] : [box],\n );\n const aligned = facingAlignment(\n from,\n to,\n fixed(from),\n fixed(to),\n crowd,\n between,\n );\n if (aligned) {\n touches.set(touchKey(index, 'from'), aligned.from);\n touches.set(touchKey(index, 'to'), aligned.to);\n }\n }\n\n for (const slots of bySide.values()) {\n const open = slots.filter(\n ({ connection, role }) => !touches.has(touchKey(connection, role)),\n );\n if (open.length === 0) continue;\n const { end } = slots[0]!;\n const vertical = isVertical(end.side);\n const otherAt = ({ connection, role }: Slot) => {\n const other = connections[connection]![role === 'from' ? 'to' : 'from'];\n const at = fixed(other) ?? centre(other.shape.box);\n return vertical ? at.x : at.y;\n };\n const ordered = open\n .map((slot, order) => ({ slot, order, at: otherAt(slot) }))\n .sort((a, b) => a.at - b.at || a.order - b.order);\n const alongs = spread(end, ordered.length);\n for (const [index, { slot }] of ordered.entries()) {\n touches.set(\n touchKey(slot.connection, slot.role),\n pointOnSide(end.shape.box, end.side, alongs[index]!),\n );\n }\n }\n\n return connections.map((connection, index) => {\n const start =\n fixed(connection.from) ?? touches.get(touchKey(index, 'from'))!;\n const end = fixed(connection.to) ?? touches.get(touchKey(index, 'to'))!;\n const stub = stubLength(connection, start, end);\n return {\n start,\n startStub: beyond(start, connection.from.side, stub),\n end,\n endStub: beyond(end, connection.to.side, stub),\n };\n });\n}\n\n/**\n * The parts of a side a connection may touch: the side less its corner\n * inset, less what lies behind a caption on that side. A short side gives up\n * its corner inset before its slots come closer than SLOT_PITCH.\n */\nfunction freeIntervals(end: ConnectionEnd, count: number): Interval[] {\n const { shape, side } = end;\n const [from, to] = sideSpan(shape.box, side);\n const inset = Math.max(\n 0,\n Math.min(shape.radius, (to - from - (count + 1) * SLOT_PITCH) / 2),\n );\n const vertical = isVertical(side);\n const line = sideLine(shape.box, side);\n const outward = side === 'bottom' || side === 'right';\n const behind = shape.clear.flatMap((box): Interval[] => {\n const [near, far] = vertical\n ? [box.y, box.y + box.h]\n : [box.x, box.x + box.w];\n if (outward ? near < line : far > line) return [];\n return vertical\n ? [[box.x - SLOT_PITCH, box.x + box.w + SLOT_PITCH]]\n : [[box.y - SLOT_PITCH, box.y + box.h + SLOT_PITCH]];\n });\n return cut([[from + inset, to - inset]], behind);\n}\n\n/** The parts of `intervals` outside every one of `cuts`. */\nfunction cut(intervals: Interval[], cuts: Interval[]): Interval[] {\n let free = intervals;\n for (const [cutStart, cutEnd] of cuts) {\n free = free\n .flatMap(([a, b]): Interval[] => [\n [a, Math.min(b, cutStart)],\n [Math.max(a, cutEnd), b],\n ])\n .filter(([low, high]) => high > low);\n }\n return free;\n}\n\n/**\n * Positions for `count` slots on the free parts of a side, in order along\n * it: each next slot goes to the part where slots would lie farthest apart,\n * and each part spaces its slots evenly.\n */\nfunction spread(end: ConnectionEnd, count: number): number[] {\n const intervals = freeIntervals(end, count);\n const counts = intervals.map(() => 0);\n const pitchWithOneMore = (index: number) =>\n (intervals[index]![1] - intervals[index]![0]) / (counts[index]! + 2);\n for (let placed = 0; placed < count && intervals.length > 0; placed++) {\n const roomiest = counts.reduce(\n (best, _, index) =>\n pitchWithOneMore(index) > pitchWithOneMore(best) ? index : best,\n 0,\n );\n counts[roomiest] = counts[roomiest]! + 1;\n }\n const alongs = intervals.flatMap(([a, b], index) =>\n Array.from(\n { length: counts[index]! },\n (_, slot) => a + ((slot + 1) / (counts[index]! + 1)) * (b - a),\n ),\n );\n const closest = Math.min(\n ...intervals.flatMap(([a, b], index) =>\n counts[index]! > 1 ? [(b - a) / (counts[index]! + 1)] : [],\n ),\n );\n if (alongs.length < count || closest < SLOT_PITCH) {\n const { shape, side } = end;\n throw new Error(\n `Diagram ${shape.kind === 'group' ? 'group' : 'node'} \"${shape.id}\" side ${side} has no room for ${count} connections ${SLOT_PITCH}px apart beside its corners and caption.`,\n );\n }\n return alongs;\n}\n\n/**\n * Both ends on the middle of the widest part of the overlap of two facing\n * sides that no label in `labels` between the sides lies on, when the\n * connection is alone on each.\n */\nfunction facingAlignment(\n from: ConnectionEnd,\n to: ConnectionEnd,\n fromFixed: Point | undefined,\n toFixed: Point | undefined,\n crowd: (end: ConnectionEnd) => number,\n labels: Box[],\n): { from: Point; to: Point } | undefined {\n if (to.side !== OPPOSITE[from.side] || (fromFixed && toFixed)) return;\n if (!fromFixed && crowd(from) !== 1) return;\n if (!toFixed && crowd(to) !== 1) return;\n const vertical = isVertical(from.side);\n const free = (end: ConnectionEnd, at: Point | undefined): Interval[] => {\n if (!at) return freeIntervals(end, 1);\n const along = vertical ? at.x : at.y;\n return [[along, along]];\n };\n const lines = [\n sideLine(from.shape.box, from.side),\n sideLine(to.shape.box, to.side),\n ];\n const [gapStart, gapEnd] = [Math.min(...lines), Math.max(...lines)];\n const inGap = labels.flatMap(({ x, y, w, h }): Interval[] => {\n const [across, acrossEnd, along, alongEnd] = vertical\n ? [y, y + h, x, x + w]\n : [x, x + w, y, y + h];\n return across < gapEnd && acrossEnd > gapStart ? [[along, alongEnd]] : [];\n });\n const widest = free(from, fromFixed)\n .flatMap(([a0, a1]) =>\n free(to, toFixed).flatMap(([b0, b1]) =>\n cut([[Math.max(a0, b0), Math.min(a1, b1)]], inGap),\n ),\n )\n .filter(([low, high]) => low <= high)\n .sort((a, b) => b[1] - b[0] - (a[1] - a[0]))[0];\n if (!widest) return;\n const along = (widest[0] + widest[1]) / 2;\n return {\n from: fromFixed ?? pointOnSide(from.shape.box, from.side, along),\n to: toFixed ?? pointOnSide(to.shape.box, to.side, along),\n };\n}\n\n/**\n * A stub is STUB long, except between two ends that face each other closer\n * than two stubs: then each stub reaches half way, so they meet in the gap.\n */\nfunction stubLength(connection: Connection, start: Point, end: Point): number {\n const { from, to } = connection;\n if (to.side !== OPPOSITE[from.side]) return LAYOUT_TOKENS.STUB;\n const gap =\n (isVertical(from.side) ? end.y - start.y : end.x - start.x) *\n (from.side === 'bottom' || from.side === 'right' ? 1 : -1);\n return gap > 0 && gap < 2 * LAYOUT_TOKENS.STUB ? gap / 2 : LAYOUT_TOKENS.STUB;\n}\n\nfunction sideKey(end: ConnectionEnd): string {\n return `${end.shape.id}\\u0000${end.side}`;\n}\n\nfunction centre(box: Box): Point {\n return { x: box.x + box.w / 2, y: box.y + box.h / 2 };\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,38 @@
1
+ import type { Box, DiagramGraphIR, DiagramSide } from '../../model/diagram-ir.ts';
2
+ import type { CellArea, GridAreas } from './areas.ts';
3
+ import type { GroupLayout, NodeLayout, PortLayout } from './graph-layout-result.ts';
4
+ /** A node or group that a connection starts or ends at. */
5
+ export type Shape = {
6
+ id: string;
7
+ kind: 'node' | 'junction' | 'group';
8
+ box: Box;
9
+ /** The corner radius, which slots keep clear of. */
10
+ radius: number;
11
+ ports: PortLayout[];
12
+ /** What a connection may not touch the shape through: its caption. */
13
+ clear: Box[];
14
+ area: CellArea;
15
+ };
16
+ export type ConnectionEnd = {
17
+ shape: Shape;
18
+ side: DiagramSide;
19
+ port: PortLayout | undefined;
20
+ };
21
+ /** An edge, or the leader from a note to its target: anything the router draws between two shapes. */
22
+ export type Connection = {
23
+ /** How errors name it: `edge a→b`, or `note leader "n"→"t"`. */
24
+ name: string;
25
+ /** The index of the edge in the graph, or undefined for a leader. */
26
+ edge: number | undefined;
27
+ from: ConnectionEnd;
28
+ to: ConnectionEnd;
29
+ /** The Manhattan distance between the two cell areas' centres. */
30
+ cellDistance: number;
31
+ };
32
+ /**
33
+ * Resolves every edge and every note leader to the shapes and sides it
34
+ * joins. A side hint wins over the cells; a port fixes the side; a side that
35
+ * declares ports takes no edge without one.
36
+ */
37
+ export declare function graphConnections(ir: DiagramGraphIR, nodes: NodeLayout[], groups: GroupLayout[], areas: GridAreas): Connection[];
38
+ //# sourceMappingURL=connections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connections.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/graph/connections.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,GAAG,EACH,cAAc,EACd,WAAW,EACZ,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,UAAU,EACX,MAAM,0BAA0B,CAAC;AAIlC,2DAA2D;AAC3D,MAAM,MAAM,KAAK,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC;IACpC,GAAG,EAAE,GAAG,CAAC;IACT,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,sEAAsE;IACtE,KAAK,EAAE,GAAG,EAAE,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC;CAC9B,CAAC;AAEF,sGAAsG;AACtG,MAAM,MAAM,UAAU,GAAG;IACvB,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,IAAI,EAAE,aAAa,CAAC;IACpB,EAAE,EAAE,aAAa,CAAC;IAClB,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,UAAU,EAAE,EACnB,MAAM,EAAE,WAAW,EAAE,EACrB,KAAK,EAAE,SAAS,GACf,UAAU,EAAE,CAiHd"}
@@ -0,0 +1,112 @@
1
+ import { parseEndpointRef } from "../../model/endpoint-ref.js";
2
+ import { facingSides } from "./sides.js";
3
+ import { LAYOUT_TOKENS } from "./tokens.js";
4
+
5
+ /** A node or group that a connection starts or ends at. */
6
+
7
+ /** An edge, or the leader from a note to its target: anything the router draws between two shapes. */
8
+
9
+ /**
10
+ * Resolves every edge and every note leader to the shapes and sides it
11
+ * joins. A side hint wins over the cells; a port fixes the side; a side that
12
+ * declares ports takes no edge without one.
13
+ */
14
+ export function graphConnections(ir, nodes, groups, areas) {
15
+ const shapes = new Map();
16
+ for (const {
17
+ node,
18
+ box,
19
+ radius,
20
+ ports,
21
+ caption
22
+ } of nodes) {
23
+ shapes.set(node.id, {
24
+ id: node.id,
25
+ kind: node.kind === 'junction' ? 'junction' : 'node',
26
+ box,
27
+ radius,
28
+ ports,
29
+ clear: caption ? [caption.box] : [],
30
+ area: areas.nodes.get(node.id)
31
+ });
32
+ }
33
+ for (const [index, {
34
+ group,
35
+ rect,
36
+ ports
37
+ }] of groups.entries()) {
38
+ shapes.set(group.id, {
39
+ id: group.id,
40
+ kind: 'group',
41
+ box: rect,
42
+ radius: LAYOUT_TOKENS.RADIUS,
43
+ ports,
44
+ clear: [],
45
+ area: areas.groups[index].area
46
+ });
47
+ }
48
+ const connect = (name, edge, fromRef, toRef, hints) => {
49
+ const resolve = ref => {
50
+ const {
51
+ nodeId,
52
+ portId
53
+ } = parseEndpointRef(ref);
54
+ const shape = shapes.get(nodeId);
55
+ if (!shape) {
56
+ throw new Error(`Diagram ${name}: "${nodeId}" is not a node or a group.`);
57
+ }
58
+ if (portId === undefined) return {
59
+ shape,
60
+ port: undefined
61
+ };
62
+ const port = shape.ports.find(candidate => candidate.id === portId);
63
+ if (!port) {
64
+ throw new Error(`Diagram ${name}: "${nodeId}" has no port "${portId}".`);
65
+ }
66
+ return {
67
+ shape,
68
+ port
69
+ };
70
+ };
71
+ const from = resolve(fromRef);
72
+ const to = resolve(toRef);
73
+ const [exitByCells, entryByCells] = from.shape === to.shape ?
74
+ // A loop has no cell delta to read; it leaves and re-enters the right side.
75
+ ['right', 'right'] : facingSides(from.shape.area, to.shape.area);
76
+ const end = ({
77
+ shape,
78
+ port
79
+ }, hint, byCells, role, ref) => {
80
+ if (port) {
81
+ if (hint !== undefined && hint !== port.side) {
82
+ throw new Error(`Diagram ${name}: ${role} ${hint} contradicts port "${ref}" on side ${port.side}.`);
83
+ }
84
+ return {
85
+ shape,
86
+ side: port.side,
87
+ port
88
+ };
89
+ }
90
+ const side = hint ?? byCells;
91
+ if (shape.ports.some(candidate => candidate.side === side)) {
92
+ throw new Error(`Diagram ${shape.kind === 'group' ? 'group' : 'node'} "${shape.id}" side ${side} declares ports; ${name} must use "${shape.id}:<port>".`);
93
+ }
94
+ return {
95
+ shape,
96
+ side,
97
+ port: undefined
98
+ };
99
+ };
100
+ const a = from.shape.area;
101
+ const b = to.shape.area;
102
+ return {
103
+ name,
104
+ edge,
105
+ from: end(from, hints.exitSide, exitByCells, 'exitSide', fromRef),
106
+ to: end(to, hints.entrySide, entryByCells, 'entrySide', toRef),
107
+ cellDistance: Math.abs(b.row + b.rowSpan / 2 - (a.row + a.rowSpan / 2)) + Math.abs(b.col + b.colSpan / 2 - (a.col + a.colSpan / 2))
108
+ };
109
+ };
110
+ return [...ir.edges.map((edge, index) => connect(`edge ${edge.from}→${edge.to}`, index, edge.from, edge.to, edge)), ...ir.nodes.flatMap(node => node.kind === 'note' && node.target !== undefined ? [connect(`note leader "${node.id}"→"${node.target}"`, undefined, node.id, node.target, {})] : [])];
111
+ }
112
+ //# sourceMappingURL=connections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connections.js","names":["parseEndpointRef","facingSides","LAYOUT_TOKENS","graphConnections","ir","nodes","groups","areas","shapes","Map","node","box","radius","ports","caption","set","id","kind","clear","area","get","index","group","rect","entries","RADIUS","connect","name","edge","fromRef","toRef","hints","resolve","ref","nodeId","portId","shape","Error","undefined","port","find","candidate","from","to","exitByCells","entryByCells","end","hint","byCells","role","side","some","a","b","exitSide","entrySide","cellDistance","Math","abs","row","rowSpan","col","colSpan","edges","map","flatMap","target"],"sources":["../../../../src/lib/layouts/graph/connections.ts"],"sourcesContent":["import type {\n Box,\n DiagramGraphIR,\n DiagramSide,\n} from '../../model/diagram-ir.ts';\nimport { parseEndpointRef } from '../../model/endpoint-ref.ts';\nimport type { CellArea, GridAreas } from './areas.ts';\nimport type {\n GroupLayout,\n NodeLayout,\n PortLayout,\n} from './graph-layout-result.ts';\nimport { facingSides } from './sides.ts';\nimport { LAYOUT_TOKENS } from './tokens.ts';\n\n/** A node or group that a connection starts or ends at. */\nexport type Shape = {\n id: string;\n kind: 'node' | 'junction' | 'group';\n box: Box;\n /** The corner radius, which slots keep clear of. */\n radius: number;\n ports: PortLayout[];\n /** What a connection may not touch the shape through: its caption. */\n clear: Box[];\n area: CellArea;\n};\n\nexport type ConnectionEnd = {\n shape: Shape;\n side: DiagramSide;\n port: PortLayout | undefined;\n};\n\n/** An edge, or the leader from a note to its target: anything the router draws between two shapes. */\nexport type Connection = {\n /** How errors name it: `edge a→b`, or `note leader \"n\"→\"t\"`. */\n name: string;\n /** The index of the edge in the graph, or undefined for a leader. */\n edge: number | undefined;\n from: ConnectionEnd;\n to: ConnectionEnd;\n /** The Manhattan distance between the two cell areas' centres. */\n cellDistance: number;\n};\n\n/**\n * Resolves every edge and every note leader to the shapes and sides it\n * joins. A side hint wins over the cells; a port fixes the side; a side that\n * declares ports takes no edge without one.\n */\nexport function graphConnections(\n ir: DiagramGraphIR,\n nodes: NodeLayout[],\n groups: GroupLayout[],\n areas: GridAreas,\n): Connection[] {\n const shapes = new Map<string, Shape>();\n for (const { node, box, radius, ports, caption } of nodes) {\n shapes.set(node.id, {\n id: node.id,\n kind: node.kind === 'junction' ? 'junction' : 'node',\n box,\n radius,\n ports,\n clear: caption ? [caption.box] : [],\n area: areas.nodes.get(node.id)!,\n });\n }\n for (const [index, { group, rect, ports }] of groups.entries()) {\n shapes.set(group.id, {\n id: group.id,\n kind: 'group',\n box: rect,\n radius: LAYOUT_TOKENS.RADIUS,\n ports,\n clear: [],\n area: areas.groups[index]!.area,\n });\n }\n\n const connect = (\n name: string,\n edge: number | undefined,\n fromRef: string,\n toRef: string,\n hints: {\n exitSide?: DiagramSide | undefined;\n entrySide?: DiagramSide | undefined;\n },\n ): Connection => {\n const resolve = (ref: string) => {\n const { nodeId, portId } = parseEndpointRef(ref);\n const shape = shapes.get(nodeId);\n if (!shape) {\n throw new Error(\n `Diagram ${name}: \"${nodeId}\" is not a node or a group.`,\n );\n }\n if (portId === undefined) return { shape, port: undefined };\n const port = shape.ports.find((candidate) => candidate.id === portId);\n if (!port) {\n throw new Error(\n `Diagram ${name}: \"${nodeId}\" has no port \"${portId}\".`,\n );\n }\n return { shape, port };\n };\n const from = resolve(fromRef);\n const to = resolve(toRef);\n const [exitByCells, entryByCells] =\n from.shape === to.shape\n ? // A loop has no cell delta to read; it leaves and re-enters the right side.\n (['right', 'right'] as const)\n : facingSides(from.shape.area, to.shape.area);\n const end = (\n { shape, port }: { shape: Shape; port: PortLayout | undefined },\n hint: DiagramSide | undefined,\n byCells: DiagramSide,\n role: 'exitSide' | 'entrySide',\n ref: string,\n ): ConnectionEnd => {\n if (port) {\n if (hint !== undefined && hint !== port.side) {\n throw new Error(\n `Diagram ${name}: ${role} ${hint} contradicts port \"${ref}\" on side ${port.side}.`,\n );\n }\n return { shape, side: port.side, port };\n }\n const side = hint ?? byCells;\n if (shape.ports.some((candidate) => candidate.side === side)) {\n throw new Error(\n `Diagram ${shape.kind === 'group' ? 'group' : 'node'} \"${shape.id}\" side ${side} declares ports; ${name} must use \"${shape.id}:<port>\".`,\n );\n }\n return { shape, side, port: undefined };\n };\n const a = from.shape.area;\n const b = to.shape.area;\n return {\n name,\n edge,\n from: end(from, hints.exitSide, exitByCells, 'exitSide', fromRef),\n to: end(to, hints.entrySide, entryByCells, 'entrySide', toRef),\n cellDistance:\n Math.abs(b.row + b.rowSpan / 2 - (a.row + a.rowSpan / 2)) +\n Math.abs(b.col + b.colSpan / 2 - (a.col + a.colSpan / 2)),\n };\n };\n\n return [\n ...ir.edges.map((edge, index) =>\n connect(`edge ${edge.from}→${edge.to}`, index, edge.from, edge.to, edge),\n ),\n ...ir.nodes.flatMap((node) =>\n node.kind === 'note' && node.target !== undefined\n ? [\n connect(\n `note leader \"${node.id}\"→\"${node.target}\"`,\n undefined,\n node.id,\n node.target,\n {},\n ),\n ]\n : [],\n ),\n ];\n}\n"],"mappings":"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","ignoreList":[]}
@@ -56,7 +56,7 @@ export type GroupLayout = {
56
56
  band?: Box;
57
57
  label: TextRun[];
58
58
  dividers: [Point, Point][];
59
- ports?: PortLayout[];
59
+ ports: PortLayout[];
60
60
  };
61
61
  export type EdgeLabelLayout = {
62
62
  box: Box;
@@ -1 +1 @@
1
- {"version":3,"file":"graph-layout-result.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/graph/graph-layout-result.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"graph-layout-result.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/graph/graph-layout-result.ts"],"names":[],"mappings":"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"}