Implemented the narrower same-location wall merge.

This commit is contained in:
Nick Beckley 2026-06-21 19:17:30 +01:00
parent 0ea8b2a57e
commit 7bcc6d0598
3 changed files with 117 additions and 67 deletions

View File

@ -8,37 +8,6 @@
var selectedBlockId = int.TryParse(Context.Request.Query["selectedBlockId"], out var parsedSelectedBlockId) ? parsedSelectedBlockId : 0;
string BlockClass(string blockType) => $"floor-plan-block floor-plan-block--{blockType.ToLowerInvariant()}";
string CssNumber(decimal value) => value.ToString("0.###", System.Globalization.CultureInfo.InvariantCulture);
bool RangesOverlap(int aStart, int aLength, int bStart, int bLength) => aStart < bStart + bLength && bStart < aStart + aLength;
string SharedWallClasses(FloorPlanBlockEditViewModel block, IReadOnlyList<FloorPlanBlockEditViewModel> floorBlocks)
{
var classes = new List<string>();
foreach (var other in floorBlocks)
{
if (other.FloorPlanBlockID == block.FloorPlanBlockID || other.LocationID != block.LocationID)
{
continue;
}
if (block.X + block.WidthCells == other.X && RangesOverlap(block.Y, block.HeightCells, other.Y, other.HeightCells))
{
classes.Add("merge-east");
}
if (other.X + other.WidthCells == block.X && RangesOverlap(block.Y, block.HeightCells, other.Y, other.HeightCells))
{
classes.Add("merge-west");
}
if (block.Y + block.HeightCells == other.Y && RangesOverlap(block.X, block.WidthCells, other.X, other.WidthCells))
{
classes.Add("merge-south");
}
if (other.Y + other.HeightCells == block.Y && RangesOverlap(block.X, block.WidthCells, other.X, other.WidthCells))
{
classes.Add("merge-north");
}
}
return string.Join(" ", classes.Distinct());
}
string TransitionDetails(FloorPlanTransitionEditViewModel transition)
{
var details = $"{transition.FromLocationName} -> {transition.ToLocationName}\n{transition.TransitionType}";
@ -186,7 +155,7 @@
{
var blockIndex = Model.Blocks.FindIndex(x => x.FloorPlanBlockID == block.FloorPlanBlockID);
<button type="button"
class="@BlockClass(block.BlockType) @SharedWallClasses(block, floorBlocks)"
class="@BlockClass(block.BlockType)"
style="--x:@block.X;--y:@block.Y;--w:@block.WidthCells;--h:@block.HeightCells;"
data-block="@block.FloorPlanBlockID"
data-block-index="@blockIndex"
@ -1464,18 +1433,83 @@
return aStart < bStart + bLength && bStart < aStart + aLength;
}
function subtractRange(segments, start, end) {
return segments.flatMap(segment => {
const clippedStart = Math.max(segment.start, start);
const clippedEnd = Math.min(segment.end, end);
if (clippedStart >= clippedEnd) return [segment];
return [
{ start: segment.start, end: clippedStart },
{ start: clippedEnd, end: segment.end }
].filter(next => next.end > next.start);
});
}
function renderWallSegment(block, side, start, end, total) {
if (end <= start || total <= 0) return;
const segment = document.createElement("span");
segment.dataset.wallSegment = side;
segment.className = `floor-plan-wall-segment floor-plan-wall-segment--${side}`;
segment.style.setProperty("--wall-start", `${(start / total) * 100}%`);
segment.style.setProperty("--wall-length", `${((end - start) / total) * 100}%`);
block.append(segment);
}
function visibleEdgeSegments(item, visibleBlocks, side) {
const axisStart = side === "east" || side === "west" ? item.y : item.x;
const axisLength = side === "east" || side === "west" ? item.h : item.w;
let segments = [{ start: 0, end: axisLength }];
visibleBlocks.forEach(other => {
if (item === other || !item.block.dataset.locationId || item.block.dataset.locationId !== other.block.dataset.locationId) return;
let touches = false;
let overlapStart = 0;
let overlapEnd = 0;
if (side === "east" && item.x + item.w === other.x) {
touches = true;
overlapStart = Math.max(item.y, other.y) - axisStart;
overlapEnd = Math.min(item.y + item.h, other.y + other.h) - axisStart;
} else if (side === "west" && other.x + other.w === item.x) {
touches = true;
overlapStart = Math.max(item.y, other.y) - axisStart;
overlapEnd = Math.min(item.y + item.h, other.y + other.h) - axisStart;
} else if (side === "south" && item.y + item.h === other.y) {
touches = true;
overlapStart = Math.max(item.x, other.x) - axisStart;
overlapEnd = Math.min(item.x + item.w, other.x + other.w) - axisStart;
} else if (side === "north" && other.y + other.h === item.y) {
touches = true;
overlapStart = Math.max(item.x, other.x) - axisStart;
overlapEnd = Math.min(item.x + item.w, other.x + other.w) - axisStart;
}
if (touches && overlapStart < overlapEnd) {
segments = subtractRange(segments, overlapStart, overlapEnd);
}
});
return segments;
}
function refreshWallSegments(visibleBlocks) {
visibleBlocks.forEach(item => {
item.block.querySelectorAll("[data-wall-segment]").forEach(segment => segment.remove());
["north", "east", "south", "west"].forEach(side => {
const total = side === "east" || side === "west" ? item.h : item.w;
visibleEdgeSegments(item, visibleBlocks, side).forEach(segment => {
renderWallSegment(item.block, side, segment.start, segment.end, total);
});
});
});
}
function refreshSharedWalls(activePanel) {
const visibleBlocks = [...activePanel.querySelectorAll("[data-block]")].map(block => ({ block, ...readBlock(block) }));
visibleBlocks.forEach(item => item.block.classList.remove("merge-east", "merge-west", "merge-north", "merge-south"));
visibleBlocks.forEach(a => {
visibleBlocks.forEach(b => {
if (a === b || !a.block.dataset.locationId || a.block.dataset.locationId !== b.block.dataset.locationId) return;
if (a.x + a.w === b.x && rangesOverlap(a.y, a.h, b.y, b.h)) a.block.classList.add("merge-east");
if (b.x + b.w === a.x && rangesOverlap(a.y, a.h, b.y, b.h)) a.block.classList.add("merge-west");
if (a.y + a.h === b.y && rangesOverlap(a.x, a.w, b.x, b.w)) a.block.classList.add("merge-south");
if (b.y + b.h === a.y && rangesOverlap(a.x, a.w, b.x, b.w)) a.block.classList.add("merge-north");
});
});
refreshWallSegments(visibleBlocks);
return visibleBlocks;
}

View File

@ -4615,7 +4615,8 @@ body.dragging-location [data-drag-type="location"] {
.floor-plan-block {
align-items: center;
border: 2px solid rgba(44, 84, 75, .55);
--floor-plan-wall-color: rgba(44, 84, 75, .55);
border: 2px solid transparent;
border-radius: 2px;
color: #12332c;
cursor: grab;
@ -4636,6 +4637,43 @@ body.dragging-location [data-drag-type="location"] {
z-index: 2;
}
.floor-plan-wall-segment {
background: var(--floor-plan-wall-color);
pointer-events: none;
position: absolute;
z-index: 2;
}
.floor-plan-wall-segment--north,
.floor-plan-wall-segment--south {
height: 2px;
left: var(--wall-start);
width: var(--wall-length);
}
.floor-plan-wall-segment--north {
top: 0;
}
.floor-plan-wall-segment--south {
bottom: 0;
}
.floor-plan-wall-segment--east,
.floor-plan-wall-segment--west {
height: var(--wall-length);
top: var(--wall-start);
width: 2px;
}
.floor-plan-wall-segment--east {
right: 0;
}
.floor-plan-wall-segment--west {
left: 0;
}
.floor-plan-block-label {
display: -webkit-box;
max-width: 100%;
@ -4662,29 +4700,6 @@ body.dragging-location [data-drag-type="location"] {
z-index: 3;
}
.floor-plan-block.merge-east {
border-right-color: transparent;
}
.floor-plan-block.merge-west {
border-left-color: transparent;
}
.floor-plan-block.merge-north {
border-top-color: transparent;
}
.floor-plan-block.merge-south {
border-bottom-color: transparent;
}
.floor-plan-block.selected.merge-east,
.floor-plan-block.selected.merge-west,
.floor-plan-block.selected.merge-north,
.floor-plan-block.selected.merge-south {
outline-offset: 0;
}
.floor-plan-block.overlap {
border-color: #b45544;
box-shadow: inset 0 0 0 2px rgba(180, 85, 68, .18);
@ -4971,6 +4986,7 @@ body.dragging-location [data-drag-type="location"] {
[data-bs-theme="dark"] .floor-plan-block,
.dark .floor-plan-block {
--floor-plan-wall-color: rgba(212, 154, 98, .6);
color: #f1f6f3;
}

File diff suppressed because one or more lines are too long