From 196d0a11b83973fc2c3a9c5e4b320011eceef202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cchrisshank=E2=80=9D?= Date: Sun, 15 Dec 2024 10:23:13 -0800 Subject: [PATCH] cut and mend tools --- demo/cutting-event-propagators.html | 150 +++++++++++++++++++++++ src/common/animation-frame-controller.ts | 10 +- src/folk-event-propagator.ts | 18 ++- src/folk-rope.ts | 46 +++++-- 4 files changed, 211 insertions(+), 13 deletions(-) create mode 100644 demo/cutting-event-propagators.html diff --git a/demo/cutting-event-propagators.html b/demo/cutting-event-propagators.html new file mode 100644 index 0000000..eba39db --- /dev/null +++ b/demo/cutting-event-propagators.html @@ -0,0 +1,150 @@ + + + + + + Event Propagator w/ tools + + + + + + + Hello World + + + + + + + + + diff --git a/src/common/animation-frame-controller.ts b/src/common/animation-frame-controller.ts index 80fabaf..aa431e4 100644 --- a/src/common/animation-frame-controller.ts +++ b/src/common/animation-frame-controller.ts @@ -34,9 +34,7 @@ export class AnimationFrameController implements ReactiveController { } hostUpdated() { - window.clearTimeout(this.#timeoutId); - this.#timeoutId = window.setTimeout(this.stop, this.#timeoutMs); - this.start(); + this.reset(); } hostDisconnected() { @@ -68,6 +66,12 @@ export class AnimationFrameController implements ReactiveController { this.#tick(); } + reset() { + window.clearTimeout(this.#timeoutId); + this.#timeoutId = window.setTimeout(this.stop, this.#timeoutMs); + this.start(); + } + stop = () => { cancelAnimationFrame(this.#tick); window.clearTimeout(this.#timeoutId); diff --git a/src/folk-event-propagator.ts b/src/folk-event-propagator.ts index 6748644..095c07d 100644 --- a/src/folk-event-propagator.ts +++ b/src/folk-event-propagator.ts @@ -68,7 +68,7 @@ export class FolkEventPropagator extends FolkRope { this.#expressionTextarea.addEventListener('focusout', () => { if (this.#hasError) { - this.cut(); + super.cut(); } }); @@ -113,8 +113,10 @@ export class FolkEventPropagator extends FolkRope { this.#hasError = true; }, onParseSuccess: () => { + if (this.#hasError) { + super.mend(); + } this.#hasError = false; - this.mend(); }, }); } @@ -128,4 +130,16 @@ export class FolkEventPropagator extends FolkRope { this.#container.style.top = `${point.pos.y}px`; } } + + override cut(atPercentage?: number): void { + super.cut(atPercentage); + + this.#propagator?.dispose(); + } + + override mend(): void { + super.mend(); + + this.#initializePropagator(); + } } diff --git a/src/folk-rope.ts b/src/folk-rope.ts index 30cd6c6..615a12d 100644 --- a/src/folk-rope.ts +++ b/src/folk-rope.ts @@ -259,18 +259,29 @@ export class FolkRope extends FolkBaseConnection implements AnimationFrameContro if (point.prev) applyConstraint(point, point.prev); } - cut(atPercentage = 0.5) { - const index = this.#getPointIndexAt(atPercentage); + #cutIndex = -1; - this.#points[index].next = null; - this.#points[index + 1].prev = null; + get isCut() { + return this.#cutIndex !== -1; } - mend(atPercentage = 0.5) { - const index = this.#getPointIndexAt(atPercentage); + cut(atPercentage = 0.5) { + if (this.isCut) return; - this.#points[index].next = this.#points[index + 1]; - this.#points[index + 1].prev = this.#points[index]; + this.#cutIndex = this.#getPointIndexAt(atPercentage); + + this.#points[this.#cutIndex].next = null; + this.#points[this.#cutIndex + 1].prev = null; + this.#rAF.reset(); + } + + mend() { + if (!this.isCut) return; + + this.#points[this.#cutIndex].next = this.#points[this.#cutIndex + 1]; + this.#points[this.#cutIndex + 1].prev = this.#points[this.#cutIndex]; + this.#cutIndex = -1; + this.#rAF.reset(); } getPointAt(percentage: number) { @@ -281,6 +292,25 @@ export class FolkRope extends FolkBaseConnection implements AnimationFrameContro const clamped = Math.min(Math.max(percentage, 0), 1); return Math.floor(this.#points.length * clamped); } + + getPercentageFromPoint(hitPoint: Point): number | null { + for (let i = 0; i < this.#points.length - 1; i++) { + const point = this.#points[i]; + const nextPoint = point.next; + + if (nextPoint === null) return null; + + if ( + Vector.distance(point.pos, hitPoint) + + Vector.distance(hitPoint, nextPoint.pos) - + Vector.distance(point.pos, nextPoint.pos) < + 1 + ) { + return i / this.#points.length; + } + } + return null; + } } function applyConstraint(p1: RopePoint, p2: RopePoint) {