mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 08:28:31 +00:00
fix v1.38.4: derived wall segments trimmed under open boundaries
- the markup layer's .seg lines ran solid through open stretches; cutSegments extracted (outlineWithout now reuses it) and applied to _segments in _renderMarkupLayer - smoke_openwall: planSegCut check (15 total); docs same-commit
This commit is contained in:
@@ -11,7 +11,7 @@ PLANS_DIR = "houseplan/plans" # relative to the HA configuration directory
|
|||||||
FILES_URL = "/houseplan_files/files"
|
FILES_URL = "/houseplan_files/files"
|
||||||
FILES_DIR = "houseplan/files"
|
FILES_DIR = "houseplan/files"
|
||||||
CONF_ADMIN_ONLY = "admin_only"
|
CONF_ADMIN_ONLY = "admin_only"
|
||||||
VERSION = "1.38.3"
|
VERSION = "1.38.4"
|
||||||
|
|
||||||
DEFAULT_CONFIG: dict = {
|
DEFAULT_CONFIG: dict = {
|
||||||
"spaces": [],
|
"spaces": [],
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -16,5 +16,5 @@
|
|||||||
"issue_tracker": "https://github.com/Matysh/houseplan-card/issues",
|
"issue_tracker": "https://github.com/Matysh/houseplan-card/issues",
|
||||||
"requirements": [],
|
"requirements": [],
|
||||||
"single_config_entry": true,
|
"single_config_entry": true,
|
||||||
"version": "1.38.3"
|
"version": "1.38.4"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,14 @@ const res = await page.evaluate(async () => {
|
|||||||
out.planDashes = sr().querySelectorAll('.openwall').length > 0;
|
out.planDashes = sr().querySelectorAll('.openwall').length > 0;
|
||||||
out.planNoedge = sr().querySelectorAll('.room.noedge').length >= 2;
|
out.planNoedge = sr().querySelectorAll('.room.noedge').length >= 2;
|
||||||
out.planBlueOutline = sr().querySelectorAll('.room-outline.outlined').length >= 2;
|
out.planBlueOutline = sr().querySelectorAll('.room-outline.outlined').length >= 2;
|
||||||
|
// производные стены (.seg) не проходят сквозь открытый участок x=550, y≈0.25H
|
||||||
|
const midY = 0.25 * H;
|
||||||
|
out.planSegCut = ![...sr().querySelectorAll('line.seg')].some((l) => {
|
||||||
|
const x1 = +l.getAttribute('x1'), x2 = +l.getAttribute('x2');
|
||||||
|
const y1 = +l.getAttribute('y1'), y2 = +l.getAttribute('y2');
|
||||||
|
return Math.abs(x1 - 550) < 0.5 && Math.abs(x2 - 550) < 0.5
|
||||||
|
&& Math.min(y1, y2) < midY && Math.max(y1, y2) > midY;
|
||||||
|
});
|
||||||
c._tool = 'openwall'; await c.updateComplete;
|
c._tool = 'openwall'; await c.updateComplete;
|
||||||
// повторный клик закрывает
|
// повторный клик закрывает
|
||||||
c._openWallClick([550, 0.25 * H]); await c.updateComplete;
|
c._openWallClick([550, 0.25 * H]); await c.updateComplete;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Vendored
+23
-23
File diff suppressed because one or more lines are too long
@@ -1,5 +1,11 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v1.38.4 — 2026-07-23
|
||||||
|
- Plan editor: the DERIVED wall segments (the markup layer's solid lines)
|
||||||
|
are now trimmed under open boundaries as well — v1.38.3 only trimmed the
|
||||||
|
room outlines, so the virtual wall still looked solid in the editor.
|
||||||
|
(`cutSegments` extracted as the shared workhorse; outlineWithout reuses it.)
|
||||||
|
|
||||||
## v1.38.3 — 2026-07-23
|
## v1.38.3 — 2026-07-23
|
||||||
- The Plan editor now shows open boundaries as a true dash as well: the blue
|
- The Plan editor now shows open boundaries as a true dash as well: the blue
|
||||||
markup outlines are trimmed under the open stretches (rooms picked for
|
markup outlines are trimmed under the open stretches (rooms picked for
|
||||||
|
|||||||
@@ -140,6 +140,9 @@ Run the *core flows* (marked ★ below) in each environment at least once per mi
|
|||||||
(explicit ripple color still wins); off/white lights unchanged [auto]
|
(explicit ripple color still wins); off/white lights unchanged [auto]
|
||||||
- [ ] Alarm pulse (v1.27.0): leak/smoke/gas/CO/siren in 'on' pulse a red ring over any
|
- [ ] Alarm pulse (v1.27.0): leak/smoke/gas/CO/siren in 'on' pulse a red ring over any
|
||||||
display mode; clears on 'off'; unavailable never alarms [auto]; reduced-motion static
|
display mode; clears on 'off'; unavailable never alarms [auto]; reduced-motion static
|
||||||
|
- [ ] Derived walls cut too (v1.38.4): in the Plan editor the derived wall
|
||||||
|
segments (.seg) no longer run solid through an open stretch — only the
|
||||||
|
dash remains there [auto]
|
||||||
- [ ] Dashed boundaries in the Plan editor (v1.38.3): open stretches render as
|
- [ ] Dashed boundaries in the Plan editor (v1.38.3): open stretches render as
|
||||||
a true dash in markup too (blue trimmed outlines); merge/split-picked
|
a true dash in markup too (blue trimmed outlines); merge/split-picked
|
||||||
rooms keep their full amber highlight [auto]
|
rooms keep their full amber highlight [auto]
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "houseplan-card",
|
"name": "houseplan-card",
|
||||||
"version": "1.38.3",
|
"version": "1.38.4",
|
||||||
"description": "Interactive house plan Lovelace card for Home Assistant",
|
"description": "Interactive house plan Lovelace card for Home Assistant",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
lqiColor, snapToGrid, samePoint, pointInPolygon, markerIdForBinding,
|
lqiColor, snapToGrid, samePoint, pointInPolygon, markerIdForBinding,
|
||||||
segmentCm, formatLength, roomEdges, roomPoly, pointStrictlyInside, roomsOverlap,
|
segmentCm, formatLength, roomEdges, roomPoly, pointStrictlyInside, roomsOverlap,
|
||||||
pointOnBoundary, mergeRooms, splitRoomPath, polygonArea, closestPointOnBoundary, pointStrictlyInside as ptInside, islandsOf, sharedBoundary, openZoneOf, distToSegment, outlineWithout,
|
pointOnBoundary, mergeRooms, splitRoomPath, polygonArea, closestPointOnBoundary, pointStrictlyInside as ptInside, islandsOf, sharedBoundary, openZoneOf, distToSegment, outlineWithout, cutSegments,
|
||||||
snapToWall, openingAmount,
|
snapToWall, openingAmount,
|
||||||
averageLqi, fitView, declump, safeUrl, resolveTapAction, floorsOf, type FloorInfo,
|
averageLqi, fitView, declump, safeUrl, resolveTapAction, floorsOf, type FloorInfo,
|
||||||
stateIcon, lightColorOf, isAlarmState, parseRoomRef, diffNewDevices, glowColorOf, doorSector, hasRoomBehind, controlsAction, isControllable,
|
stateIcon, lightColorOf, isAlarmState, parseRoomRef, diffNewDevices, glowColorOf, doorSector, hasRoomBehind, controlsAction, isControllable,
|
||||||
@@ -32,7 +32,7 @@ import './space-card';
|
|||||||
import { cardStyles } from './styles';
|
import { cardStyles } from './styles';
|
||||||
import { langOf, t, type I18nKey } from './i18n';
|
import { langOf, t, type I18nKey } from './i18n';
|
||||||
|
|
||||||
const CARD_VERSION = '1.38.3';
|
const CARD_VERSION = '1.38.4';
|
||||||
const LS_KEY = 'houseplan_card_layout_v1';
|
const LS_KEY = 'houseplan_card_layout_v1';
|
||||||
const LS_CFG = 'houseplan_card_cfg_v1'; // cache of the server config+layout for instant rendering
|
const LS_CFG = 'houseplan_card_cfg_v1'; // cache of the server config+layout for instant rendering
|
||||||
const LS_ZOOM = 'houseplan_card_zoom_v1';
|
const LS_ZOOM = 'houseplan_card_zoom_v1';
|
||||||
@@ -3780,7 +3780,11 @@ class HouseplanCard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _renderMarkupLayer(vb: number[]): TemplateResult {
|
private _renderMarkupLayer(vb: number[]): TemplateResult {
|
||||||
const segs = this._segments;
|
// derived walls minus the open stretches — those are drawn dashed on top
|
||||||
|
const openCuts = this._openPairs().flatMap((p) => p.segs);
|
||||||
|
const segs = openCuts.length
|
||||||
|
? cutSegments(this._segments, openCuts, this._gridPitch * 0.02)
|
||||||
|
: this._segments;
|
||||||
const path = this._path;
|
const path = this._path;
|
||||||
const g = this._gridPitch;
|
const g = this._gridPitch;
|
||||||
return svg`
|
return svg`
|
||||||
|
|||||||
+15
-6
@@ -912,14 +912,13 @@ export function openZoneOf(roomId: string, rooms: { id?: string; open_to?: strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Room outline pieces with the given collinear stretches removed — used to
|
* Segments with the given collinear stretches removed — the workhorse behind
|
||||||
* draw a TRUE dashed open boundary (the solid stroke must not run beneath).
|
* TRUE dashed open boundaries (derived walls and room outlines alike).
|
||||||
* Returns segments [x1,y1,x2,y2].
|
|
||||||
*/
|
*/
|
||||||
export function outlineWithout(poly: number[][], cuts: number[][], eps = 1e-6): number[][] {
|
export function cutSegments(segs: number[][], cuts: number[][], eps = 1e-6): number[][] {
|
||||||
const out: number[][] = [];
|
const out: number[][] = [];
|
||||||
for (let i = 0; i < poly.length; i++) {
|
for (const seg of segs) {
|
||||||
const p1 = poly[i], p2 = poly[(i + 1) % poly.length];
|
const p1 = [seg[0], seg[1]], p2 = [seg[2], seg[3]];
|
||||||
const dx = p2[0] - p1[0], dy = p2[1] - p1[1];
|
const dx = p2[0] - p1[0], dy = p2[1] - p1[1];
|
||||||
const len = Math.hypot(dx, dy);
|
const len = Math.hypot(dx, dy);
|
||||||
if (len < eps) continue;
|
if (len < eps) continue;
|
||||||
@@ -952,6 +951,16 @@ export function outlineWithout(poly: number[][], cuts: number[][], eps = 1e-6):
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Room outline pieces with the given collinear stretches removed. */
|
||||||
|
export function outlineWithout(poly: number[][], cuts: number[][], eps = 1e-6): number[][] {
|
||||||
|
const edges: number[][] = [];
|
||||||
|
for (let i = 0; i < poly.length; i++) {
|
||||||
|
const p1 = poly[i], p2 = poly[(i + 1) % poly.length];
|
||||||
|
edges.push([p1[0], p1[1], p2[0], p2[1]]);
|
||||||
|
}
|
||||||
|
return cutSegments(edges, cuts, eps);
|
||||||
|
}
|
||||||
|
|
||||||
/** Distance from a point to a segment [x1,y1,x2,y2]. */
|
/** Distance from a point to a segment [x1,y1,x2,y2]. */
|
||||||
export function distToSegment(p: number[], s: number[]): number {
|
export function distToSegment(p: number[], s: number[]): number {
|
||||||
const dx = s[2] - s[0], dy = s[3] - s[1];
|
const dx = s[2] - s[0], dy = s[3] - s[1];
|
||||||
|
|||||||
Reference in New Issue
Block a user