feat(datums): make ellipses user-flaggable as the primary datum
Previously only rectangles (`isAxisReference`) and lines (`axisRole`) could be flagged as the gauge primary; ellipses fell through to the auto-pick by type rank. Add `EllipseDatum.isPrimary` so the user can pick a circle/ellipse as the primary datum and override the type-rank heuristic. Mutual exclusion with the existing rect/line flags is enforced through `setAxisRole`. Adds a "Primary reference" toggle in the ellipse datum row and a "primary" badge next to the datum name so the active primary is visible at a glance.
This commit is contained in:
parent
590ba16596
commit
c2f7bf0df2
@ -96,6 +96,7 @@ function axisBadge(datum: Datum): string | null {
|
||||
if (datum.type === "rectangle" && datum.isAxisReference) return "axis"
|
||||
if (datum.type === "line" && datum.axisRole === "x") return "+x"
|
||||
if (datum.type === "line" && datum.axisRole === "y") return "+y"
|
||||
if (datum.type === "ellipse" && datum.isPrimary) return "primary"
|
||||
return null
|
||||
}
|
||||
</script>
|
||||
@ -407,6 +408,35 @@ function axisBadge(datum: Datum): string | null {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="datum.type === 'ellipse'"
|
||||
class="flex items-center justify-between"
|
||||
>
|
||||
<Label class="text-xs">Primary reference</Label>
|
||||
<label
|
||||
class="flex cursor-pointer items-center gap-1.5"
|
||||
@click.stop
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="accent-primary"
|
||||
:checked="datum.isPrimary ?? false"
|
||||
@change="
|
||||
(e) =>
|
||||
store.setAxisRole(
|
||||
datum.id,
|
||||
(e.target as HTMLInputElement)
|
||||
.checked
|
||||
? 'ellipse'
|
||||
: null,
|
||||
)
|
||||
"
|
||||
/>
|
||||
<span class="text-xs text-muted-foreground"
|
||||
>Use</span
|
||||
>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Confidence -->
|
||||
<div>
|
||||
|
||||
@ -268,7 +268,10 @@ type Primary =
|
||||
function pickPrimary(datums: Datum[]): Primary {
|
||||
if (datums.length === 0) throw new Error("No datums provided.")
|
||||
|
||||
// User-flagged world-axis reference wins regardless of type priority.
|
||||
// User-flagged primary wins regardless of type priority. Rect's
|
||||
// `isAxisReference` and line's `axisRole` carry axis semantics on top
|
||||
// of "primary"; ellipse's `isPrimary` is a pure primary flag (ellipses
|
||||
// don't define axis directions on their own).
|
||||
for (const d of datums) {
|
||||
if (d.type === "rectangle" && d.isAxisReference) {
|
||||
return { kind: "rect", datum: d }
|
||||
@ -276,6 +279,9 @@ function pickPrimary(datums: Datum[]): Primary {
|
||||
if (d.type === "line" && d.axisRole) {
|
||||
return { kind: "line", datum: d }
|
||||
}
|
||||
if (d.type === "ellipse" && d.isPrimary) {
|
||||
return { kind: "ellipse", datum: d }
|
||||
}
|
||||
}
|
||||
|
||||
const typeRank = (d: Datum): number =>
|
||||
|
||||
@ -103,14 +103,15 @@ export const useAppStore = defineStore("app", () => {
|
||||
}
|
||||
}
|
||||
|
||||
/** Set (or clear) the world-axis role on a datum, enforcing that at
|
||||
/** Set (or clear) the gauge-primary role on a datum, enforcing that at
|
||||
* most one datum holds the role at a time.
|
||||
* `role`: "rect" → rectangle.isAxisReference = true
|
||||
* "x"/"y" → line.axisRole = "x"|"y"
|
||||
* null → clear the role on `id` (no-op if it wasn't set). */
|
||||
* `role`: "rect" → rectangle.isAxisReference = true
|
||||
* "x"/"y" → line.axisRole = "x"|"y"
|
||||
* "ellipse" → ellipse.isPrimary = true
|
||||
* null → clear the role on `id` (no-op if it wasn't set). */
|
||||
function setAxisRole(
|
||||
id: string,
|
||||
role: "rect" | "x" | "y" | null,
|
||||
role: "rect" | "x" | "y" | "ellipse" | null,
|
||||
) {
|
||||
// Clear any existing flag on other datums.
|
||||
for (let i = 0; i < datums.value.length; i++) {
|
||||
@ -120,6 +121,8 @@ export const useAppStore = defineStore("app", () => {
|
||||
datums.value[i] = { ...d, isAxisReference: false }
|
||||
} else if (d.type === "line" && d.axisRole) {
|
||||
datums.value[i] = { ...d, axisRole: null }
|
||||
} else if (d.type === "ellipse" && d.isPrimary) {
|
||||
datums.value[i] = { ...d, isPrimary: false }
|
||||
}
|
||||
}
|
||||
const idx = datums.value.findIndex((d) => d.id === id)
|
||||
@ -131,6 +134,8 @@ export const useAppStore = defineStore("app", () => {
|
||||
datums.value[idx] = { ...target, isAxisReference: false }
|
||||
} else if (target.type === "line") {
|
||||
datums.value[idx] = { ...target, axisRole: null }
|
||||
} else {
|
||||
datums.value[idx] = { ...target, isPrimary: false }
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -138,6 +143,8 @@ export const useAppStore = defineStore("app", () => {
|
||||
datums.value[idx] = { ...target, isAxisReference: true }
|
||||
} else if ((role === "x" || role === "y") && target.type === "line") {
|
||||
datums.value[idx] = { ...target, axisRole: role }
|
||||
} else if (role === "ellipse" && target.type === "ellipse") {
|
||||
datums.value[idx] = { ...target, isPrimary: true }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -49,6 +49,10 @@ export interface EllipseDatum {
|
||||
diameterMm: number
|
||||
confidence: 1 | 2 | 3 | 4 | 5
|
||||
label: string
|
||||
/** When true, this ellipse is the gauge primary — overrides the
|
||||
* type-rank auto-pick. Mutually exclusive with `RectDatum.isAxisReference`
|
||||
* and `LineDatum.axisRole`; setting any of those clears the others. */
|
||||
isPrimary?: boolean
|
||||
}
|
||||
|
||||
export type Datum = RectDatum | LineDatum | EllipseDatum
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user