Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/screen-name/view/CalibrationToolNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,20 @@ export class CalibrationToolNode extends Node {
};
videoLoadedProperty.link(onVideoLoaded);

// ── Lock out interaction while the user is manually digitizing ─────────
// Dimming + pickable:false signals that the tool is temporarily inactive
// so the user cannot accidentally move calibration points mid-session.
const onActiveTrackChange = (activeId: string | null) => {
const isDigitizing = activeId !== null;
this.pickable = !isDigitizing;
this.opacity = isDigitizing ? 0.35 : 1;
};
model.activeTrackIdProperty.link(onActiveTrackChange);

this.disposeCalibrationToolNode = () => {
calibMultilink.dispose();
videoLoadedProperty.unlink(onVideoLoaded);
model.activeTrackIdProperty.unlink(onActiveTrackChange);
rangePatternProperty.dispose();
buttonLabelProperty.dispose();
};
Expand Down
12 changes: 12 additions & 0 deletions src/screen-name/view/CoordinateSystemNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,22 @@ export class CoordinateSystemNode extends Node {
};
videoLoadedProperty.link(onVideoLoaded);

// ── Lock out interaction while the user is manually digitizing ─────────
// Dimming + pickable:false signals that the coordinate system is temporarily
// inactive so the user cannot accidentally move or rotate the axes while
// placing track points.
const onActiveTrackChange = (activeId: string | null) => {
const isDigitizing = activeId !== null;
this.pickable = !isDigitizing;
this.opacity = isDigitizing ? 0.35 : 1;
};
model.activeTrackIdProperty.link(onActiveTrackChange);

this.disposeCoordinateSystemNode = () => {
model.coordOriginProperty.unlink(onOriginChange);
model.coordAngleProperty.unlink(onAngleChange);
videoLoadedProperty.unlink(onVideoLoaded);
model.activeTrackIdProperty.unlink(onActiveTrackChange);
};
}

Expand Down
40 changes: 21 additions & 19 deletions src/screen-name/view/DigitizingOverlayNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { type Dimension2, Vector2 } from "scenerystack/dot";
import { Shape } from "scenerystack/kite";
import { DOM, FireListener, Line, Node, Path, Rectangle } from "scenerystack/scenery";
import { DOM, FireListener, Node, Path, Rectangle } from "scenerystack/scenery";
import { Tandem } from "scenerystack/tandem";
import TrackLabColors from "../../TrackLabColors.js";
import { VIDEO_HEIGHT, VIDEO_WIDTH } from "../../TrackLabConstants.js";
Expand All @@ -16,6 +16,7 @@ import type { SimModel } from "../model/SimModel.js";
const OUTER_R = 12;
const INNER_R = 2;
const CUR_LW = 1.5;
const CUR_SHADOW_LW = 4; // wider shadow stroke for contrast on all backgrounds

const MAG_SIZE = 100;
const MAG_ZOOM = 4;
Expand All @@ -41,32 +42,33 @@ export class DigitizingOverlayNode extends Node {
let magCrosshairColor = TrackLabColors.digitizingMagnifierCrosshairProperty.value.toCSS();
let magShadowColor = TrackLabColors.digitizingMagnifierShadowProperty.value.toCSS();

// Custom cursor: large circle + 4 segments that stop at the empty centre
const cursorCircle = new Path(Shape.circle(0, 0, OUTER_R), {
stroke: TrackLabColors.digitizingCursorStrokeProperty,
lineWidth: CUR_LW,
});
const cursorLineLeft = new Line(-OUTER_R, 0, -INNER_R, 0, {
stroke: TrackLabColors.digitizingCursorStrokeProperty,
lineWidth: CUR_LW,
});
const cursorLineRight = new Line(INNER_R, 0, OUTER_R, 0, {
stroke: TrackLabColors.digitizingCursorStrokeProperty,
lineWidth: CUR_LW,
});
const cursorLineTop = new Line(0, -OUTER_R, 0, -INNER_R, {
stroke: TrackLabColors.digitizingCursorStrokeProperty,
lineWidth: CUR_LW,
// Custom cursor: circle + 4 crosshair segments that stop at an empty centre.
// Built as a single composite shape shared by both layers so positions are
// always in sync. The shadow layer (wider, dark stroke) renders first and
// gives the same dual-layer contrast used by CalibrationToolNode and
// CoordinateSystemNode — visible on both dark and light video backgrounds.
const cursorShape = new Shape();
cursorShape.circle(0, 0, OUTER_R);
cursorShape.moveTo(-OUTER_R, 0).lineTo(-INNER_R, 0);
cursorShape.moveTo(INNER_R, 0).lineTo(OUTER_R, 0);
cursorShape.moveTo(0, -OUTER_R).lineTo(0, -INNER_R);
cursorShape.moveTo(0, INNER_R).lineTo(0, OUTER_R);

// Shadow layer (rendered first, underneath) for contrast on all backgrounds
const cursorShadow = new Path(cursorShape, {
stroke: TrackLabColors.coordShadowStrokeProperty,
lineWidth: CUR_SHADOW_LW,
});
const cursorLineBottom = new Line(0, INNER_R, 0, OUTER_R, {
// Main cursor stroke (rendered on top of shadow)
const cursorMain = new Path(cursorShape, {
stroke: TrackLabColors.digitizingCursorStrokeProperty,
lineWidth: CUR_LW,
});

const cursorNode = new Node({
visible: false,
pickable: false,
children: [cursorCircle, cursorLineLeft, cursorLineRight, cursorLineTop, cursorLineBottom],
children: [cursorShadow, cursorMain],
});

// ── Magnifier (zoomed view near the cursor) ─────────────────────────────
Expand Down