Velocity-weighted region opacity replaces the (sub-perceptual) frame fade
The linear prev→cur bitmap blend between rebuilds was verified working but made no visible difference — the 3-frame bitmap cadence was already below perceptual threshold for large blurred blobs; the churn during fast drags is the SHAPE, not the cadence. Rolled back entirely (no extra per-frame blits). In its place, per the owner: while nodes move fast, their blob fades out and fades back in on settling. Speed per node = screen px per frame from position stamps between rebuilds, EMA-smoothed. The blob is a CLUSTER attribute (still neighbors' skeleton strokes pass right through a moving node's area — per-node weighting measurably did nothing), so each cluster's weight = f(max member speed): linear 1→0 between 1.5 and 8 px/frame. Strokes and discs paint at that alpha — before the blur + final edgeAlpha multiply, so coverage semantics are unchanged at rest (all weights 1 = the 2026-09-14 no-stacking fix). Stamps reset on data-object change, NOT geoVersion (the live sim bumps geoVersion every frame — resetting on it zeroed all speeds; caught live). regionMotionPending keeps rebuilding after the sim stops until weights converge back to 1, so a blob never freezes at partial opacity.
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
||||
fitTransform,
|
||||
labelAlpha,
|
||||
pickNearest,
|
||||
regionVelocityWeight,
|
||||
zoomRatioOf,
|
||||
LABELS_FADE_SPAN,
|
||||
} from "../components/graph-canvas.ts";
|
||||
@@ -226,3 +227,29 @@ describe("sanitizePhysicsTuning (physics controls)", () => {
|
||||
assert.equal(t.velocityDecay, 0.9);
|
||||
});
|
||||
});
|
||||
|
||||
describe("regionVelocityWeight", () => {
|
||||
test("still nodes: at or below the still threshold → full opacity", () => {
|
||||
assert.equal(regionVelocityWeight(0), 1);
|
||||
assert.equal(regionVelocityWeight(1.5), 1);
|
||||
});
|
||||
|
||||
test("fast nodes: at or above the moving threshold → blob off", () => {
|
||||
assert.equal(regionVelocityWeight(8), 0);
|
||||
assert.equal(regionVelocityWeight(100), 0);
|
||||
});
|
||||
|
||||
test("linear ramp between the thresholds, monotone decreasing", () => {
|
||||
const mid = regionVelocityWeight((1.5 + 8) / 2);
|
||||
assert.ok(Math.abs(mid - 0.5) < 1e-12);
|
||||
// strictly decreasing across the ramp
|
||||
for (let s = 1.5; s <= 8; s += 0.25) {
|
||||
assert.ok(regionVelocityWeight(s) >= regionVelocityWeight(s + 0.25));
|
||||
}
|
||||
// never leaves [0, 1]
|
||||
for (let s = -10; s <= 50; s += 1.7) {
|
||||
const w = regionVelocityWeight(s);
|
||||
assert.ok(w >= 0 && w <= 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user