mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-03 05:08:53 +00:00
File diff suppressed because one or more lines are too long
@@ -87,6 +87,35 @@ const out = await page.evaluate(async () => {
|
||||
card._markupClick(clickAt(300, 300, true));
|
||||
result.shiftBypassesExistingFaceOffer = card._path.length === 1 && !card._roomDialog;
|
||||
card._cancelPath();
|
||||
|
||||
const largeGap = card._cmToUnits(3) / 1000;
|
||||
const alternativeX = 0.1 + card._cmToUnits(1) / 1000;
|
||||
await reset({ partitions: [
|
||||
...ring(largeGap),
|
||||
{ id: 'alternative', a: [alternativeX, 0.1], b: [alternativeX, 0.08], cm: 20 },
|
||||
] });
|
||||
card._markupClick(clickAt(300, 300));
|
||||
result.ambiguousLargeGapUsesWallsFlow = card._path.length === 1
|
||||
&& !card._roomDialog && !card._wallRepairDiagnostic && !card._toast;
|
||||
|
||||
await reset({
|
||||
partitions: ring(card._cmToUnits(1.2) / 1000),
|
||||
openings: [{
|
||||
id: 'hosted-gap', type: 'door', x: 0.1, y: 0.3, angle: 90, length: 0.08,
|
||||
host: { kind: 'partition', id: 'left', t: 0.5 },
|
||||
}],
|
||||
});
|
||||
const hostedBefore = JSON.stringify(card._curSpaceCfg);
|
||||
const hostedOffered = card._offerExistingWallFace([300, 300]);
|
||||
card._nameSel = 'Must not repair';
|
||||
card._saveRoom();
|
||||
await update();
|
||||
result.hostedOpeningBlocksRepair = hostedOffered
|
||||
&& JSON.stringify(card._curSpaceCfg) === hostedBefore
|
||||
&& !card._roomDialog && !card._wallFaceBatch
|
||||
&& card._toast === card._t('toast.wall_repair_changed');
|
||||
|
||||
await reset({ partitions: ring() });
|
||||
card._offerExistingWallFace([300, 300]);
|
||||
card._nameSel = 'Existing face';
|
||||
card._saveRoom();
|
||||
|
||||
File diff suppressed because one or more lines are too long
Vendored
+3
-3
File diff suppressed because one or more lines are too long
+4
-2
@@ -7,8 +7,10 @@
|
||||
and Shift accepts only an exact 45° ray endpoint or wall intersection. A
|
||||
click inside an already closed empty wall contour can create its room; one
|
||||
unambiguous gap up to 2 cm is repaired only together with that confirmed
|
||||
room. Deleting a room now explicitly offers to keep its exclusive physical
|
||||
walls or delete them, while shared walls and their openings survive
|
||||
room; ambiguous wider gaps remain ordinary drawing, and a wall carrying a
|
||||
hosted opening is never moved by repair. Deleting a room now explicitly
|
||||
offers to keep its exclusive physical walls or delete them, while shared
|
||||
walls and their openings survive
|
||||
([#228](https://github.com/Matysh/houseplan-card/issues/228)).
|
||||
|
||||
- Resizing a room now reports the distance between wall faces, the number a tape
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
неразличимые узлы просят увеличить масштаб, а `Shift` принимает только точную
|
||||
конечную точку или пересечение на луче с шагом 45°. Клик внутри уже готового
|
||||
пустого контура стен позволяет создать комнату; единственный однозначный
|
||||
разрыв до 2 см исправляется только вместе с подтверждённой комнатой. При
|
||||
удалении комнаты теперь можно явно оставить её физические стены либо удалить
|
||||
их; общие стены и их проёмы сохраняются
|
||||
разрыв до 2 см исправляется только вместе с подтверждённой комнатой. Более
|
||||
широкий неоднозначный разрыв остаётся обычным рисованием, а стена с явно
|
||||
привязанным проёмом repair-механикой не сдвигается. При удалении комнаты
|
||||
теперь можно явно оставить её физические стены либо удалить их; общие стены
|
||||
и их проёмы сохраняются
|
||||
([#228](https://github.com/Matysh/houseplan-card/issues/228)).
|
||||
|
||||
- При изменении размера комнаты показываются внутренние размеры — расстояние от
|
||||
|
||||
@@ -163,7 +163,9 @@ import {
|
||||
type WallFaceGraph, type WallGraphFace, type WallGraphSourceSegment,
|
||||
} from './wall-face-graph';
|
||||
import { parameterOnPartition, planRoomDeletion } from './room-deletion';
|
||||
import { planWallFaceRepair, type WallFaceRepairProposal } from './wall-face-repair';
|
||||
import {
|
||||
planWallFaceRepair, repairMovesHostedPartition, type WallFaceRepairProposal,
|
||||
} from './wall-face-repair';
|
||||
import {
|
||||
LightSegment, polygonSegments, splitAtIntersections, visibilityPolygon,
|
||||
} from './light-visibility';
|
||||
@@ -7680,9 +7682,8 @@ class HouseplanCard extends LitElement {
|
||||
gridStep: this._gridPitch,
|
||||
epsilon,
|
||||
});
|
||||
if (diagnostic.kind !== 'none') {
|
||||
this._wallRepairDiagnostic = diagnostic.kind === 'repair'
|
||||
? diagnostic.proposal : diagnostic.proposals[0] || null;
|
||||
if (diagnostic.kind === 'repair') {
|
||||
this._wallRepairDiagnostic = diagnostic.proposal;
|
||||
this._showToast(this._t('toast.wall_repair_too_large'));
|
||||
return true;
|
||||
}
|
||||
@@ -12713,11 +12714,9 @@ class HouseplanCard extends LitElement {
|
||||
if (distToSegment(proposal.to, [target.a[0], target.a[1], target.b[0], target.b[1]]) > epsilon) {
|
||||
return false;
|
||||
}
|
||||
if (proposal.sourceKey.startsWith('static:partition|')) {
|
||||
const id = proposal.sourceKey.slice('static:partition|'.length).split('|')[0];
|
||||
if ((this._curSpaceCfg as any)?.openings?.some((opening: OpeningCfg) =>
|
||||
opening.host?.kind === 'partition' && opening.host.id === id)) return false;
|
||||
}
|
||||
if (repairMovesHostedPartition(
|
||||
proposal, (this._curSpaceCfg as any)?.openings || [],
|
||||
)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,21 @@ export interface WallFaceRepairOptions {
|
||||
requiredSourceKey?: string;
|
||||
}
|
||||
|
||||
export interface WallRepairHostedOpening {
|
||||
host?: { kind?: string; id?: string };
|
||||
}
|
||||
|
||||
/** A hosted opening makes its persisted partition endpoint immovable. */
|
||||
export function repairMovesHostedPartition(
|
||||
proposal: Pick<WallFaceRepairProposal, 'sourceKey'>,
|
||||
openings: readonly WallRepairHostedOpening[],
|
||||
): boolean {
|
||||
if (!proposal.sourceKey.startsWith('static:partition|')) return false;
|
||||
const id = proposal.sourceKey.slice('static:partition|'.length).split('|')[0];
|
||||
return !!id && openings.some((opening) =>
|
||||
opening.host?.kind === 'partition' && opening.host.id === id);
|
||||
}
|
||||
|
||||
const movable = (source: WallGraphSourceSegment): boolean => !source.key.startsWith('static:room|');
|
||||
|
||||
function projection(
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { planWallFaceRepair } from '../test-build/wall-face-repair.js';
|
||||
import {
|
||||
planWallFaceRepair, repairMovesHostedPartition,
|
||||
} from '../test-build/wall-face-repair.js';
|
||||
|
||||
const ringWithGap = (gap) => [
|
||||
{ a: [0, 0], b: [100, 0], key: 'static:partition|top|0' },
|
||||
@@ -48,3 +50,21 @@ test('multiple valid closures fail closed instead of choosing record order', ()
|
||||
});
|
||||
assert.notEqual(result.kind, 'repair');
|
||||
});
|
||||
|
||||
test('a hosted opening makes its partition an invalid repair mover', () => {
|
||||
const proposal = {
|
||||
sourceKey: 'static:partition|left|0', endpoint: 'b',
|
||||
from: [0, 1.2], to: [0, 0], targetSourceKey: 'static:partition|top|0',
|
||||
targetKind: 'endpoint', distance: 1.2,
|
||||
};
|
||||
assert.equal(repairMovesHostedPartition(proposal, [
|
||||
{ host: { kind: 'partition', id: 'left' } },
|
||||
]), true);
|
||||
assert.equal(repairMovesHostedPartition(proposal, [
|
||||
{ host: { kind: 'partition', id: 'right' } },
|
||||
{ host: { kind: 'room', id: 'left' } },
|
||||
]), false);
|
||||
assert.equal(repairMovesHostedPartition({ ...proposal, sourceKey: 'active:session:0' }, [
|
||||
{ host: { kind: 'partition', id: 'left' } },
|
||||
]), false);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user