From ceea967172c21171c7f809e6405ac85de047c5c8 Mon Sep 17 00:00:00 2001 From: zavx0z Date: Sat, 23 Dec 2023 00:47:46 +0300 Subject: [PATCH] draw svg --- index.html | 7 ++- src/actions/layout.js | 4 ++ src/actions/{compound.js => svgPath.js} | 2 +- src/core/worker.js | 59 ++++++++++++++++++++++++- src/index.js | 9 ++-- src/styles.css | 17 ++++++- src/templates/Line.js | 16 +++++++ 7 files changed, 106 insertions(+), 8 deletions(-) rename src/actions/{compound.js => svgPath.js} (99%) create mode 100644 src/templates/Line.js diff --git a/index.html b/index.html index 7f02265..7b50cc9 100644 --- a/index.html +++ b/index.html @@ -16,11 +16,16 @@ color: #edf8fc; /* --color-tertiary-900: 255 141 141; */ } + body { + margin: 0; + padding: 0; + height: 100svh; + width: 100svw; + } diff --git a/src/actions/layout.js b/src/actions/layout.js index 06525e1..40d9388 100644 --- a/src/actions/layout.js +++ b/src/actions/layout.js @@ -1,3 +1,7 @@ /** * @typedef { import("elkjs").ElkNode & { absolutePosition: { x: number, y: number } } } ELKNode */ + +async function algorithm(GraphBounding, MachineRelation, GraphRelation) { + +} diff --git a/src/actions/compound.js b/src/actions/svgPath.js similarity index 99% rename from src/actions/compound.js rename to src/actions/svgPath.js index 88bb8ca..3302bfa 100644 --- a/src/actions/compound.js +++ b/src/actions/svgPath.js @@ -6,7 +6,7 @@ * @typedef {[Point, Point]} LineSegment * @typedef {Point[]} Path * @typedef {"top"| "right" | "bottom" | "left"} Sides - * @typedef {MPathParam | LPathParam | ZPathParam | CPathParam | QPathParam} PathParam + * @typedef { MPathParam | LPathParam | ZPathParam | CPathParam | QPathParam } PathParam * @typedef {[MPathParam, ...PathParam[]]} SvgPath * @typedef {["M", Point]} MPathParam * @typedef {["L", Point]} LPathParam diff --git a/src/core/worker.js b/src/core/worker.js index 6e0a67f..10d972a 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -3,6 +3,7 @@ import { representation } from "../actions/repr.js" import { createSimulator } from "./simulator.js" import { machineToGraphRelation } from "../actions/relation.js" import "https://cdn.jsdelivr.net/npm/elkjs@0.8.2/lib/elk-api.min.js" +import { getPath, pathToD } from "../actions/svgPath.js" /** * @typedef {Object} Size @@ -65,9 +66,11 @@ onmessage = async ({ data: { type, params } }) => { // simulator.send({ type: "PREVIEW.CLEAR" }) break case "DOM.BOUNDED": + // ======================= SET DOM BOUNDING SIZE ====================== const /** @type {import("types").GraphSize}}*/ { edges, nodes } = params for (let [id, size] of nodes) GraphBounding.nodes.set(id, { size }) for (let [id, size] of edges) GraphBounding.edges.set(id, { size }) + // ======================= ELK ALGORITHM ====================== /** * @param {string} nodeID * @returns {import("elkjs").ElkNode} @@ -114,6 +117,7 @@ onmessage = async ({ data: { type, params } }) => { ], } } + // ==================== LAYOUT EDGES/NODES ====================== const rootEdges = GraphRelation.nodes.has(undefined) ? GraphRelation.nodes.get(undefined) : [] const layoutElkNode = await elk.layout({ id: "root", @@ -130,7 +134,6 @@ onmessage = async ({ data: { type, params } }) => { /** @param {import("elkjs").ElkExtendedEdge} elkEdge */ const setEdgeLayout = (elkEdge) => { const lca = GraphRelation.edges.get(elkEdge.id) - // if (!lca) return const elkLca = stateNodeToElkNodeMap.get(lca) const edge = GraphBounding.edges.get(elkEdge.id) if (elkEdge.sections) { @@ -186,8 +189,60 @@ onmessage = async ({ data: { type, params } }) => { } layoutElkNode.edges.forEach(setEdgeLayout) setLayout(layoutElkNode.children[0], undefined) - console.log("[worker]", GraphBounding) postMessage({ type: "DOM.LAYOUT", params: GraphBounding }) + // ======================= LINES ================================= + const lines = new Map() + + for (const [id, edge] of MachineRelation.edges) { + const sourceBound = GraphBounding.nodes.get(edge.source) + const edgeBound = GraphBounding.edges.get(id) + const targetBound = GraphBounding.nodes.get(edge.target) + const sections = edgeBound.sections || [] + const sourceRect = { + ...sourceBound.position, + ...sourceBound.size, + top: sourceBound.position.y, + bottom: sourceBound.position.y + sourceBound.size.height, + left: sourceBound.position.x, + right: sourceBound.position.x + sourceBound.size.width, + toJSON: () => {}, + } + const edgeRect = { + ...edgeBound.position, + ...edgeBound.size, + top: edgeBound.position.y, + bottom: edgeBound.position.y + edgeBound.size.height, + left: edgeBound.position.x, + right: edgeBound.position.x + edgeBound.size.width, + toJSON: () => {}, + } + const targetRect = { + ...targetBound.position, + ...targetBound.size, + top: targetBound.position.y, + bottom: targetBound.position.y + targetBound.size.height, + left: targetBound.position.x, + right: targetBound.position.x + targetBound.size.width, + toJSON: () => {}, + } + if (sourceRect && edgeRect && targetRect) { + /** @type {import("src/actions/svgPath.js").SvgPath | undefined} */ + let path + if (sections.length) { + const section = sections[0] + path = [["M", section.startPoint], ...(section.bendPoints?.map((point) => ["L", point]) || [])] + const preLastPoint = path[path.length - 1][1] + const xSign = Math.sign(section.endPoint.x - preLastPoint.x) + const ySign = Math.sign(section.endPoint.y - preLastPoint.y) + const endPoint = { x: section.endPoint.x - 5 * xSign, y: section.endPoint.y - 5 * ySign } + path.push(["L", endPoint]) + } else path = getPath(sourceRect, edgeRect, targetRect) + if (path) { + lines.set(id, pathToD(path)) + } + } + } + postMessage({ type: "LINES.INIT", params: lines }) break default: console.log("[worker]", type, params) diff --git a/src/index.js b/src/index.js index 5d30b37..d35bbe0 100644 --- a/src/index.js +++ b/src/index.js @@ -6,6 +6,7 @@ import Node from "./templates/Node.js" import Edge from "./templates/Edge.js" import Line from "./templates/Line.js" +// import Line from "./templates/Line.js" const template = document.createElement("template") template.innerHTML = /*html*/ ` @@ -60,9 +61,11 @@ class StateMachine extends HTMLElement { element.style.top = `${edge.position.y}px` element.style.visibility = "visible" } - for (const [id, edge] of GraphBounding.edges) { - // Line({id, sourceRect: GraphBounding.nodes.get())}) - } + break + case "LINES.INIT": + const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg") + for (const [id, path] of params) svg.innerHTML += Line(id, path) + this.#shadowRoot.append(svg) break default: console.log("[shadow]", type, params) diff --git a/src/styles.css b/src/styles.css index 2e46f4a..6bd054c 100644 --- a/src/styles.css +++ b/src/styles.css @@ -83,7 +83,7 @@ .edge { visibility: hidden; position: fixed; - z-index: 40; + z-index: 9999; display: flex; cursor: pointer; align-items: center; @@ -149,3 +149,18 @@ .edge-cond::after { content: "]"; } + +svg { + pointer-events: none; + position: fixed; + left: 0px; + top: 0px; + height: 100%; + width: 100%; + overflow: visible; + z-index: 9999; +} +.line { + fill: rgb(var(--tertiary-900) / 1); + stroke: rgb(var(--tertiary-900) / 1); +} diff --git a/src/templates/Line.js b/src/templates/Line.js new file mode 100644 index 0000000..0134833 --- /dev/null +++ b/src/templates/Line.js @@ -0,0 +1,16 @@ +const svg = String.raw +/** + * @param {string} id + * @param {string} path + * @returns {string} + */ +export default (id, path) => svg` + + + + + + + + +`