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) {