mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-04 05:41:34 +00:00
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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];
|
||||
|
||||
Reference in New Issue
Block a user