propagator tool thing
This commit is contained in:
parent
abf1d73aa6
commit
753e9f490c
|
|
@ -32,23 +32,27 @@
|
|||
<body>
|
||||
<folk-toolset>
|
||||
<folk-distance-field>
|
||||
<folk-shape x="100" y="100" width="50" height="50"></folk-shape>
|
||||
<folk-shape x="100" y="200" width="50" height="50"></folk-shape>
|
||||
<folk-shape x="100" y="300" width="50" height="50"></folk-shape>
|
||||
<folk-shape x="300" y="150" width="80" height="40"></folk-shape>
|
||||
<folk-shape x="400" y="250" width="60" height="90"></folk-shape>
|
||||
<folk-shape x="200" y="400" width="100" height="100"></folk-shape>
|
||||
<folk-shape x="500" y="100" width="30" height="70"></folk-shape>
|
||||
<folk-shape x="500" y="200">
|
||||
<folk-shape id="s1" x="100" y="100" width="50" height="50"></folk-shape>
|
||||
<folk-shape id="s2" x="100" y="200" width="50" height="50"></folk-shape>
|
||||
<folk-shape id="s3" x="100" y="300" width="50" height="50"></folk-shape>
|
||||
<folk-shape id="s4" x="300" y="150" width="80" height="40"></folk-shape>
|
||||
<folk-shape id="s5" x="400" y="250" width="60" height="90"></folk-shape>
|
||||
<folk-shape id="s6" x="200" y="400" width="100" height="100"></folk-shape>
|
||||
<folk-shape id="s7" x="500" y="100" width="30" height="70"></folk-shape>
|
||||
<folk-shape id="s8" x="500" y="200">
|
||||
<folk-shape-tool></folk-shape-tool>
|
||||
</folk-shape>
|
||||
<folk-shape x="700" y="200">
|
||||
<folk-delete-tool></folk-delete-tool>
|
||||
</folk-shape>
|
||||
<folk-shape x="500" y="300">
|
||||
<folk-propagator-tool></folk-propagator-tool>
|
||||
</folk-shape>
|
||||
</folk-distance-field>
|
||||
</folk-toolset>
|
||||
|
||||
<script type="module">
|
||||
import '../src/standalone/folk-event-propagator.ts';
|
||||
import '../src/standalone/folk-shape.ts';
|
||||
import '../src/standalone/folk-distance-field.ts';
|
||||
import '../src/standalone/folk-toolset.ts';
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ export class FolkBaseConnection extends FolkElement {
|
|||
}
|
||||
`;
|
||||
|
||||
@property({ type: String, reflect: true }) source = '';
|
||||
@property({ type: String, reflect: true }) source?: string;
|
||||
|
||||
@state() sourceElement: Element | null = null;
|
||||
|
||||
@state() sourceRect: DOMRectReadOnly | null = null;
|
||||
|
||||
@property({ type: String, reflect: true }) target = '';
|
||||
@property({ type: String, reflect: true }) target?: string;
|
||||
|
||||
@state() targetRect: DOMRectReadOnly | null = null;
|
||||
|
||||
|
|
@ -41,6 +41,8 @@ export class FolkBaseConnection extends FolkElement {
|
|||
if (changedProperties.has('source')) {
|
||||
this.#unobserveSource();
|
||||
|
||||
if (!this.source) return;
|
||||
|
||||
const vertex = parseVertex(this.source);
|
||||
|
||||
if (vertex) {
|
||||
|
|
@ -59,6 +61,8 @@ export class FolkBaseConnection extends FolkElement {
|
|||
if (changedProperties.has('target')) {
|
||||
this.#unobserveTarget();
|
||||
|
||||
if (!this.target) return;
|
||||
|
||||
const vertex = parseVertex(this.target);
|
||||
|
||||
if (vertex) {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ declare global {
|
|||
export class FolkRope extends FolkBaseConnection implements AnimationFrameControllerHost {
|
||||
static override tagName = 'folk-rope';
|
||||
|
||||
static #resolution = 5;
|
||||
|
||||
static styles = [
|
||||
FolkBaseConnection.styles,
|
||||
css`
|
||||
|
|
@ -41,7 +43,7 @@ export class FolkRope extends FolkBaseConnection implements AnimationFrameContro
|
|||
|
||||
path {
|
||||
fill: none;
|
||||
pointer-events: auto;
|
||||
pointer-events: none;
|
||||
stroke: var(--folk-rope-color, black);
|
||||
stroke-width: var(--folk-rope-width, 3);
|
||||
stroke-linecap: var(--folk-rope-linecap, round);
|
||||
|
|
@ -134,6 +136,38 @@ export class FolkRope extends FolkBaseConnection implements AnimationFrameContro
|
|||
endingPoint.pos = target;
|
||||
}
|
||||
|
||||
/** add/remove points based on distance between source and target rects */
|
||||
stretch() {
|
||||
if (this.sourceRect === null || this.targetRect === null || this.#points.length < 2) return;
|
||||
|
||||
// Calculate desired length based on source and target positions
|
||||
const distance = Vector.distance(this.sourceRect, this.targetRect);
|
||||
const desiredPoints = Math.floor(distance / FolkRope.#resolution);
|
||||
|
||||
while (this.#points.length < desiredPoints) {
|
||||
const lastPoint = this.#points.at(-1)!;
|
||||
lastPoint.isFixed = false;
|
||||
const newPoint = {
|
||||
pos: { ...lastPoint.pos },
|
||||
oldPos: { ...lastPoint.pos },
|
||||
distanceToNextPoint: FolkRope.#resolution,
|
||||
mass: 1,
|
||||
damping: 0.99,
|
||||
velocity: Vector.zero(),
|
||||
isFixed: true,
|
||||
prev: lastPoint,
|
||||
next: null,
|
||||
};
|
||||
lastPoint.next = newPoint;
|
||||
this.#points.push(newPoint);
|
||||
}
|
||||
|
||||
while (this.#points.length > desiredPoints) {
|
||||
this.#points.pop();
|
||||
this.#points.at(-1)!.isFixed = true;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.#points.length < 2) return;
|
||||
|
||||
|
|
@ -167,9 +201,8 @@ export class FolkRope extends FolkBaseConnection implements AnimationFrameContro
|
|||
#generatePoints(start: Point, end: Point) {
|
||||
const delta = Vector.sub(end, start);
|
||||
const len = Vector.mag(delta);
|
||||
const resolution = 5;
|
||||
const points: RopePoint[] = [];
|
||||
const pointsLen = Math.floor(len / resolution);
|
||||
const pointsLen = Math.floor(len / FolkRope.#resolution);
|
||||
|
||||
for (let i = 0; i < pointsLen; i++) {
|
||||
const percentage = i / (pointsLen - 1);
|
||||
|
|
@ -178,7 +211,7 @@ export class FolkRope extends FolkBaseConnection implements AnimationFrameContro
|
|||
points.push({
|
||||
pos,
|
||||
oldPos: { ...pos },
|
||||
distanceToNextPoint: resolution,
|
||||
distanceToNextPoint: FolkRope.#resolution,
|
||||
mass: 1,
|
||||
damping: 0.99,
|
||||
velocity: Vector.zero(),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { FolkEventPropagator } from './folk-event-propagator';
|
||||
import { FolkShape } from './folk-shape';
|
||||
|
||||
export abstract class FolkInteractionHandler extends HTMLElement {
|
||||
|
|
@ -32,11 +33,86 @@ export abstract class FolkInteractionHandler extends HTMLElement {
|
|||
}
|
||||
|
||||
activate() {
|
||||
console.log('activate', this);
|
||||
FolkToolset.setActiveTool(this);
|
||||
}
|
||||
}
|
||||
|
||||
export class FolkPropagatorTool extends FolkInteractionHandler {
|
||||
static tagName = 'folk-propagator-tool';
|
||||
readonly events = ['pointerdown', 'pointermove', 'pointerup'];
|
||||
|
||||
private currentPropagator: FolkEventPropagator | null = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.button.textContent = 'Create Propagator';
|
||||
}
|
||||
|
||||
handleEvent(event: Event): void {
|
||||
if (!(event instanceof PointerEvent)) return;
|
||||
const target = event.target as HTMLElement;
|
||||
|
||||
switch (event.type) {
|
||||
case 'pointerdown':
|
||||
if (!target || target instanceof FolkEventPropagator || target instanceof FolkInteractionHandler) return;
|
||||
event.stopImmediatePropagation();
|
||||
event.preventDefault();
|
||||
|
||||
if (!target.id) {
|
||||
target.id = `folk-source-${Date.now()}`;
|
||||
}
|
||||
|
||||
this.currentPropagator = new FolkEventPropagator();
|
||||
this.currentPropagator.source = `#${target.id}`;
|
||||
|
||||
document.body.appendChild(this.currentPropagator);
|
||||
break;
|
||||
|
||||
case 'pointermove':
|
||||
if (!this.currentPropagator) return;
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
// Update the target position to follow the mouse
|
||||
const rect = document.body.getBoundingClientRect();
|
||||
const targetPoint = `${event.clientX - rect.left}, ${event.clientY - rect.top}`;
|
||||
this.currentPropagator.target = targetPoint;
|
||||
this.currentPropagator.stretch();
|
||||
break;
|
||||
|
||||
case 'pointerup':
|
||||
if (!this.currentPropagator) return;
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
const finalTarget = document.elementFromPoint(event.clientX, event.clientY) as HTMLElement;
|
||||
|
||||
if (
|
||||
!finalTarget ||
|
||||
finalTarget instanceof FolkEventPropagator ||
|
||||
finalTarget instanceof FolkInteractionHandler
|
||||
) {
|
||||
this.currentPropagator.remove();
|
||||
} else {
|
||||
if (!finalTarget.id) {
|
||||
finalTarget.id = `folk-target-${Date.now()}`;
|
||||
}
|
||||
|
||||
this.currentPropagator.target = `#${finalTarget.id}`;
|
||||
}
|
||||
|
||||
this.currentPropagator = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static define() {
|
||||
if (!customElements.get(this.tagName)) {
|
||||
customElements.define(this.tagName, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add this line at the bottom of the file with the other define() calls
|
||||
|
||||
export class FolkShapeTool extends FolkInteractionHandler {
|
||||
static tagName = 'folk-shape-tool';
|
||||
readonly events = ['pointerdown', 'pointermove', 'pointerup'];
|
||||
|
|
@ -213,4 +289,5 @@ export class FolkToolset extends HTMLElement {
|
|||
|
||||
FolkShapeTool.define();
|
||||
FolkDeleteTool.define();
|
||||
FolkPropagatorTool.define();
|
||||
FolkToolset.define();
|
||||
|
|
|
|||
Loading…
Reference in New Issue