fix(geometry): canonicalize decor image coordinates

Issue: #431
User-Visible: yes
This commit is contained in:
Sergey Matyunin
2026-09-03 11:26:31 +03:00
parent d97bf63feb
commit 58efb6a1b2
20 changed files with 301 additions and 139 deletions
+14 -2
View File
@@ -5,6 +5,13 @@
* Keep the precision, lattice formula and field allow-list in lockstep.
*/
import {
DECOR_BOX_KINDS,
type DecorBoxKind,
} from './editors/decor/types';
export { DECOR_BOX_KINDS };
export const COORDINATE_DECIMALS = 9;
export const COORDINATE_FACTOR = 10 ** COORDINATE_DECIMALS;
export const LATTICE_GRID_N = 240;
@@ -84,6 +91,11 @@ function records(value: unknown): JsonRecord[] {
: [];
}
function isDecorBoxKind(value: unknown): value is DecorBoxKind {
return typeof value === 'string'
&& (DECOR_BOX_KINDS as readonly string[]).includes(value);
}
function scalarFields(item: JsonRecord, names: readonly string[]): void {
for (const name of names) {
if (Object.prototype.hasOwnProperty.call(item, name)) {
@@ -155,7 +167,7 @@ function visitLatticeCoordinates(
for (const opening of records(space.openings)) fieldValues(opening, ['x', 'y']);
for (const decor of records(space.decor)) {
if (decor.kind === 'line') fieldValues(decor, ['x1', 'y1', 'x2', 'y2']);
else if (decor.kind === 'rect' || decor.kind === 'ellipse' || decor.kind === 'furniture') {
else if (isDecorBoxKind(decor.kind)) {
fieldValues(decor, ['x', 'y', 'w', 'h']);
} else if (decor.kind === 'text') fieldValues(decor, ['x', 'y']);
}
@@ -335,7 +347,7 @@ export function canonicalizeConfigGeometryInPlace<T>(config: T): T {
for (const decor of records(space.decor)) {
if (decor.kind === 'line') latticeFields(decor, ['x1', 'y1', 'x2', 'y2']);
else if (decor.kind === 'rect' || decor.kind === 'ellipse' || decor.kind === 'furniture') {
else if (isDecorBoxKind(decor.kind)) {
latticeFields(decor, ['x', 'y', 'w', 'h']);
scalarFields(decor, ['angle']);
} else if (decor.kind === 'text') {
+3 -1
View File
@@ -5,7 +5,9 @@
* presentation (`width_cm`) is deliberately separate from geometry: resizing
* an object must never make its outline thicker.
*/
export type DecorKind = 'line' | 'rect' | 'ellipse' | 'text' | 'furniture' | 'image';
export const DECOR_BOX_KINDS = ['rect', 'ellipse', 'furniture', 'image'] as const;
export type DecorBoxKind = (typeof DECOR_BOX_KINDS)[number];
export type DecorKind = 'line' | 'text' | DecorBoxKind;
export interface DecorBase {
id: string;