vertices for arrows
This commit is contained in:
parent
c0dd007495
commit
65f1e16f5e
|
|
@ -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>
|
||||
|
|
@ -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}']"],
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue