vertices for arrows

This commit is contained in:
“chrisshank” 2024-10-25 14:03:17 -07:00
parent c0dd007495
commit 65f1e16f5e
5 changed files with 119 additions and 46 deletions

45
demo/arrows.html Normal file
View File

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Arrows</title>
<style>
html {
height: 100%;
}
body {
min-height: 100%;
position: relative;
margin: 0;
}
spatial-geometry {
border: 1px solid black;
border-radius: 3px;
}
spatial-connection {
display: block;
position: absolute;
inset: 0 0 0 0;
}
</style>
</head>
<body>
<spatial-geometry id="box1" x="100" y="100" width="50" height="50"></spatial-geometry>
<spatial-geometry id="box2" x="200" y="300" width="50" height="50">Hello World</spatial-geometry>
<spatial-connection source="#box1" target="#box2"></spatial-connection>
<spatial-connection source="#box1" target="400,100"></spatial-connection>
<script type="module">
import { SpatialGeometry } from '../src/canvas/spatial-geometry.ts';
import { SpatialConnection } from '../src/arrows/spatial-connection.ts';
SpatialGeometry.register();
SpatialConnection.register();
</script>
</body>
</html>

View File

@ -33,6 +33,7 @@ class SpatialThought extends HTMLElement {
handleEvent(event: PointerEvent): void {
if (event.type === 'click' && event.target === this.#deleteButton) {
this.#geometry.remove();
document
.querySelectorAll(
`spatial-connection[source="spatial-geometry[id='${this.#geometry.id}']"],

View File

@ -24,7 +24,7 @@
<li><a href="/maps">Maps</a></li>
<li><a href="/music">Music</a></li>
<li><a href="/ink">Ink</a></li>
<li><a href="/arrow">Arrow</a></li>
<li><a href="/arrows">Arrows</a></li>
<li><a href="/canvasify">Canvasify</a></li>
<li><a href="/spreadsheet">Spreadsheet</a></li>
<li><a href="/chains-of-thought/index.html">Chains of thought</a></li>

View File

@ -2,6 +2,24 @@ import { VisualObserverEntry, VisualObserverManager } from './visual-observer';
const visualObserver = new VisualObserverManager();
interface Vertex {
x: number;
y: number;
}
const vertexRegex = /(?<x>-?([0-9]*[.])?[0-9]+),\s*(?<y>-?([0-9]*[.])?[0-9]+)/;
function parseVertex(str: string): Vertex | null {
const results = vertexRegex.exec(str);
if (results === null) return null;
return {
x: Number(results.groups?.x),
y: Number(results.groups?.y),
};
}
export class AbstractArrow extends HTMLElement {
static tagName = 'abstract-arrow';
@ -9,8 +27,6 @@ export class AbstractArrow extends HTMLElement {
customElements.define(this.tagName, this);
}
static observedAttributes = ['source', 'target'];
#source = '';
/** A CSS selector for the source of the arrow. */
get source() {
@ -18,10 +34,15 @@ export class AbstractArrow extends HTMLElement {
}
set source(source) {
this.setAttribute('source', source);
this.#source = source;
this.observeSource();
}
#sourceRect!: DOMRectReadOnly;
get sourceRect() {
return this.#sourceRect;
}
#sourceElement: Element | null = null;
get sourceElement() {
@ -38,14 +59,17 @@ export class AbstractArrow extends HTMLElement {
get target() {
return this.#target;
}
set target(target) {
this.setAttribute('target', target);
this.#target = target;
this.observeTarget();
}
#targetRect!: DOMRectReadOnly;
#targetElement: Element | null = null;
get targetRect() {
return this.#targetRect;
}
#targetElement: Element | null = null;
get targetElement() {
return this.#targetElement;
}
@ -55,14 +79,9 @@ export class AbstractArrow extends HTMLElement {
this.update();
};
attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
if (name === 'source') {
this.#source = newValue;
this.observeSource();
} else if (name === 'target') {
this.#target = newValue;
this.observeTarget();
}
connectedCallback() {
this.source = this.getAttribute('source') || '';
this.target = this.getAttribute('target') || '';
}
disconnectedCallback() {
@ -70,16 +89,29 @@ export class AbstractArrow extends HTMLElement {
this.unobserveTarget();
}
// TODO: why reparse the vertex?
setSourceVertex(vertex: Vertex) {
this.target = `${vertex.x},${vertex.y}`;
}
observeSource() {
this.unobserveSource();
const el = document.querySelector(this.source);
if (el === null) {
throw new Error('source is not a valid element');
const vertex = parseVertex(this.#source);
if (vertex) {
this.#sourceRect = DOMRectReadOnly.fromRect(vertex);
this.update();
} else {
const el = document.querySelector(this.source);
if (el === null) {
throw new Error('source is not a valid element');
}
this.#sourceElement = el;
visualObserver.observe(this.#sourceElement, this.#sourceCallback);
}
this.#sourceElement = el;
visualObserver.observe(this.#sourceElement, this.#sourceCallback);
}
unobserveSource() {
@ -90,13 +122,21 @@ export class AbstractArrow extends HTMLElement {
observeTarget() {
this.unobserveTarget();
this.#targetElement = document.querySelector(this.target);
if (!this.#targetElement) {
throw new Error('target is not a valid element');
const vertex = parseVertex(this.#target);
if (vertex) {
this.#targetRect = DOMRectReadOnly.fromRect(vertex);
this.update();
} else {
this.#targetElement = document.querySelector(this.#target);
if (!this.#targetElement) {
throw new Error('target is not a valid element');
}
visualObserver.observe(this.#targetElement, this.#targetCallback);
}
visualObserver.observe(this.#targetElement, this.#targetCallback);
}
unobserveTarget() {
@ -106,25 +146,10 @@ export class AbstractArrow extends HTMLElement {
}
update() {
if (
this.#sourceRect === undefined ||
this.#targetRect === undefined ||
this.#sourceElement === null ||
this.#targetElement === null
)
return;
if (this.#sourceRect === undefined || this.#targetRect === undefined) return;
this.render(this.#sourceRect, this.#targetRect, this.#sourceElement, this.#targetElement);
this.render();
}
render(
// @ts-ignore
sourceRect: DOMRectReadOnly,
// @ts-ignore
targetRect: DOMRectReadOnly,
// @ts-ignore
sourceElement: Element,
// @ts-ignore
targetElement: Element
) {}
render() {}
}

View File

@ -53,8 +53,10 @@ export class SpatialConnection extends AbstractArrow {
},
};
render(sourceRect: DOMRectReadOnly, targetRect: DOMRectReadOnly) {
const [sx, sy, cx, cy, ex, ey, ae] = getBoxToBoxArrow(
render() {
const { sourceRect, targetRect } = this;
const [sx, sy, cx, cy, ex, ey] = getBoxToBoxArrow(
sourceRect.x,
sourceRect.y,
sourceRect.width,