fix(B06): 0값 칸도 선택 가능, 성토 경계는 첫 교차까지, 접기 손잡이 치수 교정
- 면적표의 모든 칸을 버튼으로 만든다. 예전에는 그 측점의 암종과 맞지 않는 칸(값 0)을 막아 칸마다 눌리는 자리가 달라졌다. 강조 키를 cut_rock 하나에서 cut_rr/cut_br 둘로 갈라, 표는 늘 따로 서되 그림의 암반 밴드는 암종이 맞는 쪽을 눌렀을 때만 켜진다. - 성토 밴드는 중심선에서 바깥으로 나가다 지면과 처음 만나는 곳까지만 유효하다. 그 너머에서 지면이 다시 설계선 아래로 내려가 생기던 조각을 버린다(좌·우 각각 안쪽 조각만 유지). - 접기 손잡이 치수를 좌측 사이드 손잡이(24×64, 오른쪽 모서리 둥금)의 시계방향 90° 회전값인 64×24 · 아래쪽 모서리 둥금으로 교정한다. 리사이저 위치와 패널 여유 높이도 함께 맞췄다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,8 +18,15 @@
|
||||
import type { CrossDesign, SectionSample } from "./B06_wf3_ProfileCross_Api_Fetch";
|
||||
import { L, svgElement } from "./B06_wf3_ProfileCross_UI_Section_Common";
|
||||
|
||||
/** 강조 대상. `cut_total`은 토사·암반 두 밴드를 함께 켠다. */
|
||||
export type CrossAreaKey = "cut_soil" | "cut_rock" | "cut_total" | "fill";
|
||||
/**
|
||||
* 강조 대상 = 면적표의 칸. `cut_total`은 토사·암반 두 밴드를 함께 켠다.
|
||||
* `cut_rr`/`cut_br`은 표에서는 늘 따로 서지만 그림에서는 암반 밴드 **하나**를 가리키므로,
|
||||
* 그 측점의 암종과 맞는 쪽을 눌렀을 때만 밴드가 켜진다(값이 0인 쪽은 눌려도 그림 변화 없음).
|
||||
*/
|
||||
export type CrossAreaKey = "cut_soil" | "cut_rr" | "cut_br" | "cut_total" | "fill";
|
||||
|
||||
/** 그림에 실제로 깔리는 밴드. 표의 칸(`CrossAreaKey`)보다 적다. */
|
||||
type BandKey = "cut_soil" | "cut_rock" | "fill";
|
||||
|
||||
export type AreaHighlightSetter = (key: CrossAreaKey | null) => void;
|
||||
|
||||
@@ -43,6 +50,37 @@ function interpolator(points: Array<{ offset: number; value: number }>) {
|
||||
};
|
||||
}
|
||||
|
||||
/** 밴드 조각 하나. `from`/`to`는 그 조각이 덮는 편거리(offset) 범위다. */
|
||||
interface BandPiece {
|
||||
points: string;
|
||||
from: number;
|
||||
to: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 성토 밴드는 **중심선에서 바깥으로 나가다 지면과 처음 만나는 곳까지**만 유효하다.
|
||||
* 그 너머에서 지면이 다시 설계선 아래로 내려가 생기는 조각은 성토부가 아니므로 버린다
|
||||
* (2026-08-02 사용자 지시). 좌·우 각각 중심선에 가장 가까운 조각만 남긴다.
|
||||
*/
|
||||
function keepInnermost(pieces: BandPiece[]): BandPiece[] {
|
||||
let left: BandPiece | null = null;
|
||||
let right: BandPiece | null = null;
|
||||
const kept: BandPiece[] = [];
|
||||
for (const piece of pieces) {
|
||||
if (piece.from <= 0 && piece.to >= 0) {
|
||||
// 중심선을 물고 있는 조각은 좌우 어느 쪽으로도 첫 조각이라 그대로 둔다.
|
||||
kept.push(piece);
|
||||
continue;
|
||||
}
|
||||
if (piece.to < 0) {
|
||||
if (!left || piece.to > left.to) left = piece;
|
||||
} else if (!right || piece.from < right.from) right = piece;
|
||||
}
|
||||
if (left) kept.push(left);
|
||||
if (right) kept.push(right);
|
||||
return kept;
|
||||
}
|
||||
|
||||
/**
|
||||
* `top > bottom`인 구간만 잘라 폴리곤 점 문자열을 만든다.
|
||||
* 두께가 0이 되는 지점을 끼워 넣어 밴드 끝이 지면선·설계선 교점에서 정확히 닫히게 한다.
|
||||
@@ -53,16 +91,21 @@ function bandPolygons(
|
||||
bottom: number[],
|
||||
px: (offset: number) => number,
|
||||
py: (elevation: number) => number,
|
||||
): string[] {
|
||||
const polygons: string[] = [];
|
||||
): BandPiece[] {
|
||||
const polygons: BandPiece[] = [];
|
||||
let upper: string[] = [];
|
||||
let lower: string[] = [];
|
||||
let from = 0;
|
||||
let to = 0;
|
||||
const flush = (): void => {
|
||||
if (upper.length >= 2) polygons.push([...upper, ...lower.reverse()].join(" "));
|
||||
if (upper.length >= 2)
|
||||
polygons.push({ points: [...upper, ...lower.reverse()].join(" "), from, to });
|
||||
upper = [];
|
||||
lower = [];
|
||||
};
|
||||
const add = (offset: number, high: number, low: number): void => {
|
||||
if (!upper.length) from = offset;
|
||||
to = offset;
|
||||
upper.push(`${px(offset)},${py(high)}`);
|
||||
lower.push(`${px(offset)},${py(low)}`);
|
||||
};
|
||||
@@ -142,7 +185,7 @@ export function appendCrossAreaBands(
|
||||
const depth = soilDepth(design);
|
||||
const rockLevels = depth === null ? null : groundLevels.map((level) => level - depth);
|
||||
|
||||
const bands: Array<{ key: CrossAreaKey; top: number[]; bottom: number[] }> = [
|
||||
const bands: Array<{ key: BandKey; top: number[]; bottom: number[] }> = [
|
||||
{ key: "fill", top: designLevels, bottom: groundLevels },
|
||||
];
|
||||
if (rockLevels) {
|
||||
@@ -156,16 +199,19 @@ export function appendCrossAreaBands(
|
||||
bands.push({ key: "cut_soil", top: groundLevels, bottom: designLevels });
|
||||
}
|
||||
|
||||
const groups = new Map<CrossAreaKey, SVGGElement>();
|
||||
// 표에서 암반 칸을 눌렀을 때 이 측점의 암종과 맞는 쪽만 밴드를 켠다.
|
||||
const rockCell: CrossAreaKey = design.cut_rock_kind === "blasting_rock" ? "cut_br" : "cut_rr";
|
||||
const groups = new Map<BandKey, SVGGElement>();
|
||||
for (const band of bands) {
|
||||
const polygons = bandPolygons(xs, band.top, band.bottom, x, toDisplayY);
|
||||
if (!polygons.length) continue;
|
||||
let pieces = bandPolygons(xs, band.top, band.bottom, x, toDisplayY);
|
||||
if (band.key === "fill") pieces = keepInnermost(pieces);
|
||||
if (!pieces.length) continue;
|
||||
const group = svgElement("g", { class: `b06-chart__area b06-chart__area--${band.key}` });
|
||||
for (const points of polygons) group.append(svgElement("polygon", { points }));
|
||||
for (const piece of pieces) group.append(svgElement("polygon", { points: piece.points }));
|
||||
group.addEventListener("click", (event) => {
|
||||
// 카드까지 올라가면 측점 선택이 다시 걸려 카드가 통째로 다시 그려진다 — 방금 켠 강조가 사라진다.
|
||||
event.stopPropagation();
|
||||
onSelect(band.key);
|
||||
onSelect(band.key === "cut_rock" ? rockCell : band.key);
|
||||
});
|
||||
groups.set(band.key, group);
|
||||
svg.append(group);
|
||||
@@ -174,8 +220,11 @@ export function appendCrossAreaBands(
|
||||
return (key) => {
|
||||
for (const [bandKey, group] of groups) {
|
||||
const active =
|
||||
key === bandKey ||
|
||||
(key === "cut_total" && (bandKey === "cut_soil" || bandKey === "cut_rock"));
|
||||
key === "cut_total"
|
||||
? bandKey === "cut_soil" || bandKey === "cut_rock"
|
||||
: bandKey === "cut_rock"
|
||||
? key === rockCell
|
||||
: key === bandKey;
|
||||
group.classList.toggle("is-active", active);
|
||||
}
|
||||
};
|
||||
@@ -282,18 +331,17 @@ export function buildAreaReadout(
|
||||
|
||||
const cells = new Map<CrossAreaKey, HTMLElement[]>();
|
||||
const cut = cutBreakdown(design);
|
||||
const rockKind =
|
||||
design.cut_rock_kind ?? (design.ground_type === "soil" ? null : design.ground_type);
|
||||
const tbody = document.createElement("tbody");
|
||||
const rows: Array<{ label: string; variant: string; cells: AreaCell[] }> = [
|
||||
{
|
||||
label: L("B06_Design_Cut_Area"),
|
||||
variant: "cut_total",
|
||||
// 값이 0이어도 누를 수 있다(2026-08-02 사용자 지시) — 그림에 없는 면적이면 강조만 켜지고
|
||||
// 밴드는 그대로다. 칸마다 눌리는 자리가 달라지면 어느 칸이 눌리는지 매번 확인해야 한다.
|
||||
cells: [
|
||||
{ key: "cut_soil", value: cut.ea, variant: "cut_soil" },
|
||||
// 암반 밴드는 하나뿐이다 — 그 측점의 암종에 해당하는 칸만 강조 버튼이 된다.
|
||||
{ key: rockKind === "ripping_rock" ? "cut_rock" : null, value: cut.rr, variant: "cut_rr" },
|
||||
{ key: rockKind === "blasting_rock" ? "cut_rock" : null, value: cut.br, variant: "cut_br" },
|
||||
{ key: "cut_rr", value: cut.rr, variant: "cut_rr" },
|
||||
{ key: "cut_br", value: cut.br, variant: "cut_br" },
|
||||
{ key: "cut_total", value: cut.total, variant: "cut_total" },
|
||||
],
|
||||
},
|
||||
|
||||
@@ -65,7 +65,7 @@ export type { CrossDesignChange, DesignChangeHandler, RockBoundaryControl };
|
||||
* 그래프 몫이 된다. (`.b06-section__panel-body`가 `overflow: hidden`이라 모자라면 아래가
|
||||
* 잘린다. 범례는 그래프 위에 겹치는 오버레이라 흐름 높이를 먹지 않는다.)
|
||||
*/
|
||||
const PANEL_CHROME_PX = 116;
|
||||
const PANEL_CHROME_PX = 126;
|
||||
/** 손대지 않았을 때의 패널 높이 — 이 값이 축소 비례의 기준(H₀)이 된다. */
|
||||
const BASE_PANEL_HEIGHT = LONG_HEIGHT + MASS_HAUL_HEIGHT + PANEL_CHROME_PX;
|
||||
const MIN_LONG_HEIGHT = 110;
|
||||
|
||||
@@ -60,8 +60,8 @@
|
||||
잘려 나가 잡히지 않는다 — 안쪽에 붙여 전체가 클릭 영역으로 남게 한다. */
|
||||
.b06-section__panel > .ui-resizer--vertical {
|
||||
top: auto;
|
||||
/* 하단 가운데 접기 손잡이(14px) 위에 얹는다 — 겹치면 어느 쪽이 잡히는지 헷갈린다. */
|
||||
bottom: 14px;
|
||||
/* 하단 가운데 접기 손잡이(24px) 위에 얹는다 — 겹치면 어느 쪽이 잡히는지 헷갈린다. */
|
||||
bottom: var(--spacing-24);
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
@@ -107,10 +107,12 @@
|
||||
/* `ui-collapsible__title`이 flex + space-between을 걸어 두므로 아이콘을 가운데로 되돌린다. */
|
||||
justify-content: center;
|
||||
align-self: center;
|
||||
/* 좌측 사이드 손잡이(24×64, 오른쪽 모서리 둥금)를 시계방향 90° 돌린 값 —
|
||||
가로 64 × 세로 24, 아래쪽 모서리가 둥글다. */
|
||||
width: var(--spacing-64);
|
||||
height: 14px;
|
||||
height: var(--spacing-24);
|
||||
margin-top: auto;
|
||||
border-radius: var(--radius-buttons) var(--radius-buttons) 0 0;
|
||||
border-radius: 0 0 var(--radius-buttons) var(--radius-buttons);
|
||||
}
|
||||
|
||||
.b06-section__panel-toggle::after {
|
||||
|
||||
Reference in New Issue
Block a user