export class Vertex { constructor(id, data) { this.id = id; this.data = data; this.edges = { from: [], to: [], }; } getEdges(label, away) { return this.edges[away ? 'from' : 'to'].filter( (edge) => edge.label === label, ); } } export class Edge { constructor(label, from, to, weight) { this.from = from; this.to = to; this.label = label; this.weight = weight; } } export class WDAG { constructor(scene) { this.scene = scene; this.vertices = new Map(); this.edgeLabels = new Map(); this.nextVertexId = 0; this.flowchart = scene?.flowchart ?? null; } withFlowchart() { this.scene.withAdditionalFlowchart(); this.flowchart = this.scene.lastFlowchart(); return this; } addVertex(id, data, label) { // Support simple case of auto-incremented numeric ids if (typeof id === 'object') { data = id; id = this.nextVertexId++; } if (this.vertices.has(id)) { throw new Error(`Vertex already exists with id: ${id}`); } const vertex = new Vertex(id, data); this.vertices.set(id, vertex); this.flowchart?.log(`${id}[${label ?? id}]`); return this; } setVertexLabel(id, label) { this.flowchart?.log(`${id}[${label}]`); } getVertex(id) { return this.vertices.get(id); } getVertexData(id) { return this.getVertex(id)?.data; } getVerticesData() { return Array.from(this.vertices.values()).map(({ data }) => data); } static getEdgeKey({ from, to }) { return btoa([from.id, to.id]).replaceAll(/[^A-Z]+/g, ''); } getEdge(label, from, to) { from = from instanceof Vertex ? from : this.getVertex(from); to = to instanceof Vertex ? to : this.getVertex(to); if (!from || !to) { return undefined; } const edges = this.edgeLabels.get(label); const edgeKey = WDAG.getEdgeKey({ from, to }); return edges?.get(edgeKey); } getEdgeWeight(label, from, to) { return this.getEdge(label, from, to)?.weight; } getEdgeHtml({ from, to }) { let html = ''; for (const { label, weight } of this.getEdges(null, from, to)) { html += ``; } html += '
${label}${weight}
'; return html; } getEdgeFlowchartNode(edge) { const edgeKey = WDAG.getEdgeKey(edge); return `${edgeKey}(${this.getEdgeHtml(edge)})`; } setEdgeWeight(label, from, to, weight) { from = from instanceof Vertex ? from : this.getVertex(from); to = to instanceof Vertex ? to : this.getVertex(to); const edge = new Edge(label, from, to, weight); let edges = this.edgeLabels.get(label); if (!edges) { edges = new Map(); this.edgeLabels.set(label, edges); } const edgeKey = WDAG.getEdgeKey(edge); edges.set(edgeKey, edge); this.flowchart?.log(this.getEdgeFlowchartNode(edge)); return edge; } addEdge(label, from, to, weight) { from = from instanceof Vertex ? from : this.getVertex(from); to = to instanceof Vertex ? to : this.getVertex(to); if (this.getEdge(label, from, to)) { throw new Error(`Edge ${label} from ${from} to ${to} already exists`); } const edge = this.setEdgeWeight(label, from, to, weight); from.edges.from.push(edge); to.edges.to.push(edge); this.flowchart?.log(`${from.id} --- ${this.getEdgeFlowchartNode(edge)} --> ${to.id}`); this.flowchart?.log(`class ${WDAG.getEdgeKey(edge)} edge`); return this; } getEdges(label, from, to) { from = from instanceof Vertex ? from : this.getVertex(from); to = to instanceof Vertex ? to : this.getVertex(to); const edgeLabels = label ? [label] : Array.from(this.edgeLabels.keys()); return edgeLabels.flatMap((edgeLabel) => { const edges = this.edgeLabels.get(edgeLabel); return Array.from(edges?.values() || []).filter((edge) => { const matchFrom = from === null || from === undefined || from === edge.from; const matchTo = to === null || to === undefined || to === edge.to; return matchFrom && matchTo; }); }); } countVertices() { return this.vertices.size; } }