fix: satisfy render review gates

Issue: #451
User-Visible: no
This commit is contained in:
Sergey Matyunin
2026-09-04 23:54:09 +03:00
parent cb68492c99
commit fe03eae213
30 changed files with 283 additions and 260 deletions
+4 -4
View File
@@ -92,9 +92,8 @@ import {
type LinearWallSegment, type WallEntry, type WallInterval,
innerEdgeSpan, ownEdgeOffsets, thicknessCmAt,
} from './wall-thickness';
import {
checkNodeDistances, checkNodes, checkRoomClearance, checkSegmentLengths,
increasedViolations, type JunctionLimitViolation, type LimitSegment,
import type {
JunctionLimitViolation, JunctionSharedGeometry, LimitSegment,
} from './junction-limits';
import {
pointOnOpenCut, sanitizeOpenSpans,
@@ -7789,7 +7788,8 @@ export class HouseplanCard extends LitElement {
}
/** #329 П1-П5 over one space. Pure input, no side effects. */
private _junctionLimitViolations(config: any, spaceId: string, sharedGeometry?: any,
private _junctionLimitViolations(config: unknown, spaceId: string,
sharedGeometry?: JunctionSharedGeometry | null,
roomIds?: ReadonlySet<string>): JunctionLimitViolation[] {
return this._editorRuntimeOrThrow()._junctionLimitViolations(config, spaceId, sharedGeometry, roomIds);
}
+8 -8
View File
@@ -105,7 +105,7 @@ import {
} from './wall-thickness';
import {
junctionLimitViolations, increasedViolations,
type JunctionLimitViolation, type LimitSegment,
type JunctionLimitViolation, type JunctionSharedGeometry, type LimitSegment,
} from './junction-limits';
import {
pointOnOpenCut, sanitizeOpenSpans,
@@ -973,9 +973,8 @@ export interface HouseplanEditorHostPort {
_importTotal: number;
_infoCard: DevItem | null;
_innerRoomContour: (space: SpaceModel, roomId: string, openCuts?: number[][], roomWalls?: any, multiWallNodes?: MultiWallNodeMap | null | undefined) => number[][] | null;
_junctionLimitViolations: (
config: unknown, spaceId: string, sharedGeometry?: unknown, roomIds?: ReadonlySet<string>,
) => JunctionLimitViolation[];
_junctionLimitViolations: (config: unknown, spaceId: string,
sharedGeometry?: JunctionSharedGeometry | null, roomIds?: ReadonlySet<string>) => JunctionLimitViolation[];
_isVacDev: (d: DevItem) => boolean;
_kiosk: boolean;
_kioskDialog: boolean;
@@ -2047,13 +2046,14 @@ public _limitSegmentsOf(space: any): LimitSegment[] {
}
public _junctionLimitViolations(
config: any, spaceId: string,
config: unknown, spaceId: string,
/** #330 §4.7: an already computed masonry pass of THIS config (e.g. the
* resize preflight's artifact) — the union is never paid twice. */
sharedGeometry?: any,
sharedGeometry?: JunctionSharedGeometry | null,
roomIds?: ReadonlySet<string>,
): JunctionLimitViolation[] {
const space = (config?.spaces || []).find((item) => item?.id === spaceId);
const space = ((config as { spaces?: Array<{ id?: unknown }> } | null)?.spaces || [])
.find((item) => item?.id === spaceId);
return space ? junctionLimitViolations(
config, spaceId, this._limitSegmentsOf(space), sharedGeometry, roomIds,
) : [];
@@ -2068,7 +2068,7 @@ public _junctionLimitLabel(violation: JunctionLimitViolation): string {
public _junctionLimitsIntroduced(
candidate: any, previousConfig: any, spaceId: string,
candidateGeometry?: unknown,
candidateGeometry?: JunctionSharedGeometry | null,
affectedRoomIds?: readonly string[],
): JunctionLimitViolation[] {
// The baseline must be the previous document AS THE CANDIDATE SEES IT: a
+22 -5
View File
@@ -15,7 +15,21 @@
import { GRID_STEP_N } from './space-geometry';
import {
innerContourForRoom, multiWallNodesForGeometry, wallBodiesGeometry,
type MultiWallNodeMap, type WallBodiesGeometryResult, type WallEntry,
} from './wall-thickness';
import type { RoomCfg } from './types';
interface JunctionLimitSpace {
id?: string;
cell_cm?: unknown;
rooms?: RoomCfg[];
walls?: WallEntry[];
}
type JunctionLimitConfig = { spaces?: JunctionLimitSpace[] };
export type JunctionSharedGeometry =
| Pick<WallBodiesGeometryResult, 'status' | 'roomGeom' | 'multiWallNodes'>
| { status: 'lightweight'; multiWallNodes?: MultiWallNodeMap | null };
export const MIN_JUNCTION_ANGLE_DEG = 15;
export const MAX_JUNCTION_VALENCE = 6;
@@ -48,13 +62,14 @@ export interface LimitSegment {
/** Shared full/affected-room validation used by editor writes and lightweight resize previews. */
export function junctionLimitViolations(
config: any,
config: unknown,
spaceId: string,
segments: readonly LimitSegment[],
sharedGeometry?: any,
sharedGeometry?: JunctionSharedGeometry | null,
roomIds?: ReadonlySet<string>,
): JunctionLimitViolation[] {
const space = (config?.spaces || []).find((item: any) => item?.id === spaceId);
const spaces = (config as JunctionLimitConfig | null)?.spaces || [];
const space = spaces.find((item) => item?.id === spaceId);
if (!space) return [];
const cellCm = Number(space.cell_cm) > 0 ? Number(space.cell_cm) : 5;
const violations = [
@@ -70,8 +85,10 @@ export function junctionLimitViolations(
);
} catch { nodes = null; }
}
let roomGeometry: any = sharedGeometry?.status === 'ok'
|| sharedGeometry?.status === 'degraded-extra' ? sharedGeometry.roomGeom : null;
const completeGeometry = sharedGeometry && sharedGeometry.status !== 'lightweight'
? sharedGeometry : null;
let roomGeometry: unknown = completeGeometry?.status === 'ok'
|| completeGeometry?.status === 'degraded-extra' ? completeGeometry.roomGeom : null;
const lightweight = sharedGeometry === null || sharedGeometry?.status === 'lightweight';
if (!roomGeometry && !lightweight && nodes?.nodes.length) {
try {
+15 -9
View File
@@ -1,6 +1,9 @@
import { nothing, render, svg, type TemplateResult } from 'lit';
import { cancelHouseplanPointerMove } from './pointer-move-queue';
import { gridVisualUnits } from './grid-scale';
import type { RenderOpening } from './interaction-types';
import type { SpaceDisplay } from './logic';
import type { SpaceModel, WallEntry } from './types';
import {
wallBodyNeedsSolid, wallCmToUnits, wallEdgePathD,
wallHatchNeedsSolid, wallHatchStepUnits,
@@ -38,7 +41,7 @@ interface LiveEditorHost {
_liveEditorPaintCount: number;
_baseVb: () => number[];
_viewOr: (viewBox: number[]) => { x: number; y: number; w: number; h: number };
_spaceDisplayForRender: () => unknown;
_spaceDisplayForRender: () => SpaceDisplay;
_renderWallBodies: (display: unknown) => unknown;
_renderResizeMeasurements: () => unknown;
_renderOpenings: (display: unknown, onlyIds?: readonly string[]) => unknown;
@@ -58,11 +61,11 @@ interface LiveEditorHost {
_livePos: (device: { id: string }) => { x: number; y: number };
_scenePoint: (point: number[]) => number[];
_renderProjection: string;
_spaceModel: () => any;
_spaceWalls: any[];
_spaceModel: () => SpaceModel | null;
_spaceWalls: WallEntry[];
_cellCm: number;
_gridPitch: number;
_openingsR: any[];
_openingsR: RenderOpening[];
_stageEl: HTMLElement | null;
_fillColors: { wall_fill: { c: string; a: number } };
}
@@ -154,16 +157,19 @@ const restore = (state: LiveEditorState): void => {
/** Resize moves paint individual wall strips; pointerup still validates the canonical union. */
const resizePreviewWalls = (
host: LiveEditorHost, display: any, roomIds: readonly string[] = [],
host: LiveEditorHost, display: SpaceDisplay, roomIds: readonly string[] = [],
): TemplateResult => {
if (host._renderProjection === 'iso') return svg``;
const space = host._spaceModel();
if (!space) return svg``;
const affected = new Set(roomIds);
const boundary = (space.rooms || []).filter((room: any) => affected.has(room.id))
.flatMap((room: any) => (room.poly || []).map((point: number[], index: number) => [
point, room.poly[(index + 1) % room.poly.length],
]));
const boundary = (space.rooms || []).filter((room) => !!room.id && affected.has(room.id))
.flatMap((room) => {
const poly = room.poly || [];
return poly.map((point: number[], index: number) => [
point, poly[(index + 1) % poly.length],
]);
});
const onSegment = (point: number[], segment: number[][]): boolean => {
const [a, b] = segment;
const dx = b[0] - a[0], dy = b[1] - a[1];