first
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
beginGraphNodeDrag,
|
||||
resolveGraphNodeDragTarget,
|
||||
screenPointToWorldPoint,
|
||||
worldPointToScreenPoint
|
||||
} from "../src/render";
|
||||
import type { GraphScreenPoint, GraphWorldPoint, RendererViewport, RendererViewportSize } from "../src/render";
|
||||
|
||||
const VIEWPORT_SIZE: RendererViewportSize = { width: 1000, height: 680 };
|
||||
|
||||
function assertNear(actual: number, expected: number, message?: string): void {
|
||||
assert.ok(Math.abs(actual - expected) < 0.01, `${message ?? "number"}: expected ${expected}, got ${actual}`);
|
||||
}
|
||||
|
||||
function assertPointNear(actual: { x: number; y: number }, expected: { x: number; y: number }): void {
|
||||
assertNear(actual.x, expected.x, "x");
|
||||
assertNear(actual.y, expected.y, "y");
|
||||
}
|
||||
|
||||
describe("graph node drag simulation bridge", () => {
|
||||
it("keeps the grabbed point under the pointer when the viewport is panned and zoomed", () => {
|
||||
const viewport: RendererViewport = { x: -180, y: 72, scale: 2.4 };
|
||||
const nodeWorldPoint: GraphWorldPoint = { x: 360, y: 240 };
|
||||
const nodeScreenPoint = worldPointToScreenPoint(nodeWorldPoint, viewport, VIEWPORT_SIZE);
|
||||
const pointerScreenPoint: GraphScreenPoint = {
|
||||
x: nodeScreenPoint.x + 30,
|
||||
y: nodeScreenPoint.y - 18
|
||||
};
|
||||
|
||||
const drag = beginGraphNodeDrag({
|
||||
nodeWorldPoint,
|
||||
pointerScreenPoint,
|
||||
viewport,
|
||||
viewportSize: VIEWPORT_SIZE
|
||||
});
|
||||
const movedPointer: GraphScreenPoint = {
|
||||
x: pointerScreenPoint.x + 144,
|
||||
y: pointerScreenPoint.y + 96
|
||||
};
|
||||
const target = resolveGraphNodeDragTarget({
|
||||
pointerScreenPoint: movedPointer,
|
||||
viewport,
|
||||
viewportSize: VIEWPORT_SIZE,
|
||||
grabOffset: drag.grabOffset
|
||||
});
|
||||
|
||||
const grabbedWorldPoint = {
|
||||
x: target.x + drag.grabOffset.x,
|
||||
y: target.y + drag.grabOffset.y
|
||||
};
|
||||
assertPointNear(worldPointToScreenPoint(grabbedWorldPoint, viewport, VIEWPORT_SIZE), movedPointer);
|
||||
});
|
||||
|
||||
it("starts a drag at the current node point instead of jumping to the pointer center", () => {
|
||||
const viewport: RendererViewport = { x: -320, y: -140, scale: 1.75 };
|
||||
const nodeWorldPoint: GraphWorldPoint = { x: 700, y: 410 };
|
||||
const pointerScreenPoint: GraphScreenPoint = {
|
||||
x: worldPointToScreenPoint(nodeWorldPoint, viewport, VIEWPORT_SIZE).x - 48,
|
||||
y: worldPointToScreenPoint(nodeWorldPoint, viewport, VIEWPORT_SIZE).y + 22
|
||||
};
|
||||
|
||||
const drag = beginGraphNodeDrag({
|
||||
nodeWorldPoint,
|
||||
pointerScreenPoint,
|
||||
viewport,
|
||||
viewportSize: VIEWPORT_SIZE
|
||||
});
|
||||
|
||||
assertPointNear(drag.targetWorldPoint, nodeWorldPoint);
|
||||
assertPointNear(drag.pointerWorldPoint, screenPointToWorldPoint(pointerScreenPoint, viewport, VIEWPORT_SIZE));
|
||||
});
|
||||
|
||||
it("does not clamp off-world drag targets inside projection or bridge helpers", () => {
|
||||
const viewport: RendererViewport = { x: -120, y: -80, scale: 1.2 };
|
||||
const nodeWorldPoint: GraphWorldPoint = { x: 980, y: 650 };
|
||||
const drag = beginGraphNodeDrag({
|
||||
nodeWorldPoint,
|
||||
pointerScreenPoint: worldPointToScreenPoint(nodeWorldPoint, viewport, VIEWPORT_SIZE),
|
||||
viewport,
|
||||
viewportSize: VIEWPORT_SIZE
|
||||
});
|
||||
|
||||
const target = resolveGraphNodeDragTarget({
|
||||
pointerScreenPoint: { x: 1380, y: 930 },
|
||||
viewport,
|
||||
viewportSize: VIEWPORT_SIZE,
|
||||
grabOffset: drag.grabOffset
|
||||
});
|
||||
|
||||
assert.ok(target.x > 1000, `x should remain outside layout bounds before simulation constraints, got ${target.x}`);
|
||||
assert.ok(target.y > 680, `y should remain outside layout bounds before simulation constraints, got ${target.y}`);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user