feat(ui): F8-12 — owner feedback round 2 (#55)
* Bug fix: theme flip no longer leaves planets oversized. The camera-preserving remount now calls a new `RendererHandle.refreshCameraDerivedDraws` explicitly after the manual moveCenter/setZoom pair so the post-mount geometry tracks `viewport.scaled` even if pixi-viewport's `'zoomed'` listener races the next Ticker tick. * Doc #3: clicks on a planet label route through the same hit-test path as a click on the disc. The label `Container` now has a pointer hit area sized to the text + frame padding; pointertap simulates a click at the planet centre, so selection and pick-mode resolution behave identically. * Doc #4: battle X-crosses + cargo arrowhead wings grow sub-linearly with zoom (PLANET_SIZE_ZOOM_ALPHA). New `Style.softLengthAnchor` ('center' / 'start') makes the renderer treat the recorded endpoints as the geometry "at the reference scale" and rescale around the midpoint (X-cross) or the start endpoint (arrow wings). Arrowhead base length is halved from 6 to 3 world units to match the owner's "in half" request. * Doc #5: picker overlay loses the anchor ring at the source, the cursor line drops to a cargo-route-thin 0.6 px stroke, and the hover ring around the destination is replaced by a planet-style outline (visible disc + 1 px padding) in the `pickHighlight` accent — so candidate destinations read like selection in warm yellow. * Doc #6: regression test pins the in-disc hit zone. * Perf #1: camera-driven redraws are throttled onto the next Ticker tick. A rapid wheel / pinch burst now coalesces into at most one `clear() + redraw` pass per painted frame, which keeps the 500-planet map responsive on zoom and toggle flips. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -47,6 +47,15 @@ export interface Style {
|
||||
// this for the IncomingGroup trajectory line; ignored on point
|
||||
// and circle primitives.
|
||||
strokeDashPx?: number;
|
||||
// softLengthAnchor — when set on a `LinePrim`, the renderer treats
|
||||
// the world-coord endpoints as the line length "at the reference
|
||||
// scale" and grows / shrinks them with `PLANET_SIZE_ZOOM_ALPHA`
|
||||
// the same way planet discs do. `'center'` scales both endpoints
|
||||
// around the segment midpoint (used by battle X-crosses anchored
|
||||
// on the planet centre); `'start'` keeps `(x1, y1)` fixed and
|
||||
// only scales `(x2, y2)` along the original direction (used by
|
||||
// cargo-route arrowhead wings anchored at the destination).
|
||||
softLengthAnchor?: "center" | "start";
|
||||
}
|
||||
|
||||
// PrimitiveBase carries the fields shared by every primitive kind.
|
||||
@@ -278,6 +287,62 @@ export function displayStrokeWidthWorld(
|
||||
return px / cameraScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* softLengthFactor returns the multiplier that scales a line's
|
||||
* length when `style.softLengthAnchor` is set. The factor matches
|
||||
* the planet-radius softening rule: at `scale = scaleRef` it equals
|
||||
* `1` (the recorded geometry is the reference length); zooming in
|
||||
* shrinks the world-space length so the on-screen length grows by
|
||||
* `(scale / scaleRef)^α`. `displayLineEndpoints` is the convenience
|
||||
* wrapper that applies it to a line's `(x1, y1)–(x2, y2)` pair
|
||||
* given the configured anchor.
|
||||
*/
|
||||
export function softLengthFactor(
|
||||
cameraScale: number,
|
||||
scaleRef: number,
|
||||
): number {
|
||||
if (cameraScale <= 0 || scaleRef <= 0) return 1;
|
||||
return Math.pow(cameraScale / scaleRef, PLANET_SIZE_ZOOM_ALPHA - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* displayLineEndpoints returns the world-space endpoints the
|
||||
* renderer should draw a `LinePrim` between, honouring
|
||||
* `style.softLengthAnchor` if set. Used by both the renderer and
|
||||
* the hit-test so the click zone always matches the visible stroke.
|
||||
*/
|
||||
export function displayLineEndpoints(
|
||||
style: Style,
|
||||
x1: number,
|
||||
y1: number,
|
||||
x2: number,
|
||||
y2: number,
|
||||
cameraScale: number,
|
||||
scaleRef: number,
|
||||
): { x1: number; y1: number; x2: number; y2: number } {
|
||||
if (style.softLengthAnchor === undefined) {
|
||||
return { x1, y1, x2, y2 };
|
||||
}
|
||||
const factor = softLengthFactor(cameraScale, scaleRef);
|
||||
if (factor === 1) return { x1, y1, x2, y2 };
|
||||
if (style.softLengthAnchor === "start") {
|
||||
return {
|
||||
x1,
|
||||
y1,
|
||||
x2: x1 + (x2 - x1) * factor,
|
||||
y2: y1 + (y2 - y1) * factor,
|
||||
};
|
||||
}
|
||||
const cx = (x1 + x2) / 2;
|
||||
const cy = (y1 + y2) / 2;
|
||||
return {
|
||||
x1: cx + (x1 - cx) * factor,
|
||||
y1: cy + (y1 - cy) * factor,
|
||||
x2: cx + (x2 - cx) * factor,
|
||||
y2: cy + (y2 - cy) * factor,
|
||||
};
|
||||
}
|
||||
|
||||
export const DARK_THEME: Theme = {
|
||||
background: 0x0a0e1a,
|
||||
fog: 0x12162a,
|
||||
|
||||
Reference in New Issue
Block a user