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
42 changes: 34 additions & 8 deletions src/components/Canvas/LineObject.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export function LineObject({
// time without any one-frame delay on release.
const [liveThicknessDots, setLiveThicknessDots] = useState<number | null>(null);
const effectiveThicknessDots = liveThicknessDots ?? p.thickness;
const lineStrokeWidth = Math.max(dotsToPx(effectiveThicknessDots, scale, dpmm), 1);
const rawStrokePx = Math.max(dotsToPx(effectiveThicknessDots, scale, dpmm), 1);

// Option-A geometry (mirrors src/lib/shapeRender.ts):
// - Axis-aligned lines map to ^GB and extrude thickness downward
Expand All @@ -131,8 +131,6 @@ export function LineObject({
// Otherwise dragging a near-horizontal endpoint shows the body locked
// to the horizontal band until release, then snaps to the parallelo-
// gram — a visible jump the user noticed.
const halfStrokePx = lineStrokeWidth / 2;


// Live positions while handles are being dragged (snapped preview)
const [livePt1, setLivePt1] = useState<{ x: number; y: number } | null>(null);
Expand All @@ -152,6 +150,16 @@ export function LineObject({
const dispX2 = livePt2?.x ?? x2 + dx;
const dispY2 = livePt2?.y ?? y2 + dy;

// Mid-drag, an endpoint can be pulled inside the current thickness; the
// onDragEnd commit then snaps thickness down to the new length, which
// would look like a sudden band shrink on release. Cap the visual stroke
// at the live endpoint distance so the band always tracks the t ≤ length
// invariant we commit to. In steady state the data model already
// satisfies this, so the cap is a no-op.
const visualLenPx = Math.hypot(dispX2 - dispX1, dispY2 - dispY1);
const lineStrokeWidth = Math.min(rawStrokePx, visualLenPx);
const halfStrokePx = lineStrokeWidth / 2;

// Half-pixel epsilon: constrainLine's auto-snap commits 45°-step
// positions where ddx/ddy land exactly on axis-aligned values, but
// float math can leave a tiny residue. <0.5 px collapses to "the
Expand Down Expand Up @@ -459,10 +467,18 @@ export function LineObject({
e.target.getParent(),
);
clearSnap();
// Shrinking the line below the current thickness would
// push the ZPL into the `^GB` promotion regime (t > length
// → printed `t × t`); cap thickness to the new length so
// the model preserves the t ≤ length invariant.
onChange({
x: r.movingDotX,
y: r.movingDotY,
props: { length: r.length, angle: r.angle },
props: {
length: r.length,
angle: r.angle,
thickness: Math.min(p.thickness, r.length),
Comment thread
u8array marked this conversation as resolved.
},
});
}}
/>
Expand Down Expand Up @@ -520,7 +536,13 @@ export function LineObject({
e.target.getParent(),
);
clearSnap();
onChange({ props: { length: r.length, angle: r.angle } });
onChange({
props: {
length: r.length,
angle: r.angle,
thickness: Math.min(p.thickness, r.length),
},
});
}}
/>
<Rect
Expand Down Expand Up @@ -549,9 +571,13 @@ export function LineObject({
const extPx = isHorizontal
? cursorY - lineCenterY
: cursorX - lineCenterX;
const newT = Math.max(
1,
Math.round(pxToDots(extPx, scale, dpmm)),
// Cap at p.length so the line never enters the ZPL ^GB
// promotion regime where thickness exceeds length and
// Labelary would print `t × t` rather than the band the
// user is dragging.
const newT = Math.min(
p.length,
Math.max(1, Math.round(pxToDots(extPx, scale, dpmm))),
);
setLiveThicknessDots(newT);
// Pin the Rect to the (possibly-clamped) anchor so
Expand Down
16 changes: 15 additions & 1 deletion src/registry/line.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,17 @@ export const line: ObjectTypeDefinition<LineProps> = {
label={t.registry.line.length}
value={p.length}
min={1}
onChange={(length) => onChange({ length })}
// Shrinking length below the current thickness would land
// the model in the ^GB promotion regime where t > length
// prints `t × t`; auto-clamp thickness down to match the
// new length, mirroring the endpoint-handle drag.
onChange={(length) =>
onChange(
length < p.thickness
? { length, thickness: length }
: { length },
)
}
/>
<NumberInput
label={t.registry.line.angle}
Expand All @@ -140,6 +150,10 @@ export const line: ObjectTypeDefinition<LineProps> = {
label={t.registry.line.thickness}
value={p.thickness}
min={1}
// Capped at length so the ZPL output stays out of the ^GB
// promotion regime (max(w, t) × max(h, t)), where the printer
// would extend the line beyond its declared length.
max={p.length}
onChange={(thickness) => onChange({ thickness })}
/>

Expand Down