Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1

This commit is contained in:
2026-09-08 22:17:43 +09:00
4 changed files with 36 additions and 2 deletions
+4 -1
View File
@@ -52,7 +52,10 @@ async function withDraftWalls(
projectId: string,
): Promise<SectionDetailResponse> {
const drafts = readPendingStructures(projectId);
if (!drafts) return withStructureAreas(detail);
// ⚠ **빈 목록도 「초안 없음」이다**(2026-09-09). 예전에는 `[]` 가 「초안이 있는데 벽이
// 하나도 없다」로 읽혀 **아래에서 서버 저장분을 통째로 지웠다** — 구조물을 놓아도
// 횡단도에 아무것도 안 보이던 결함의 원인이다(창 둘에서 같은 증상, 실측 확인).
if (!drafts || !drafts.length) return withStructureAreas(detail);
const types = await fetchStructureTypes().catch(() => []);
const names = new Map(
types
@@ -256,6 +256,27 @@ export function wallStandsAt(owner: CrossSection, section: CrossSection, key: st
return spanCovers(revetSpanOfSpec(side), deltaM);
}
/**
* 링크된 관의 벽이 **이 카드에 실제로 서는가**. 하나도 안 서면 그 링크는 이 측점의
* 그림을 막을 이유가 없다 — 독립 벽(D경로)이 그려져야 한다.
*
* 왜 있나(2026-09-09) — 「관이 없는 측점인데도 구조물을 놓으면 아무것도 안 보인다」가
* 사용자에게 보이던 결함이었다. 겹침 방지 가드가 **링크가 있기만 하면** 막고 있었는데,
* 관이 아홉·열하나인 노선에서는 링크가 거의 모든 측점을 덮어 D경로가 통째로 죽었다.
* 가드를 없애지 않고 **좁힌다** — 벽이 실제로 서는 카드에서만 막는다.
*/
export function culvertWallsStandAt(owner: CrossSection, section: CrossSection): boolean {
const keys = ["inlet", "outlet"];
const counts = owner.design?.extra_wall_counts ?? {};
for (const [side, count] of Object.entries(counts)) {
const total = Math.max(Math.trunc(Number(count) || 0), 0);
for (let index = 0; index < total; index += 1) {
keys.push(side === "basin" ? `bextra${index}` : `extra${index}`);
}
}
return keys.some((key) => wallStandsAt(owner, section, key));
}
export function culvertLinkFor(
section: CrossSection,
sections: readonly CrossSection[],
+7 -1
View File
@@ -18,6 +18,7 @@ import {
appendFordPavementOverlay,
appendFordSurfaceDropPlan,
} from "./B06_Section_UI_Cross_Ford_Pavement";
import { culvertWallsStandAt } from "./B06_Section_UI_Cross_Culvert_Wire";
import { appendRevetmentOverlay, computeRevetmentLayout } from "./B06_Section_UI_Cross_Revetment";
import {
appendCrossDesignOverlay,
@@ -422,7 +423,12 @@ export function createCrossSectionCard(
// 이 측점에 배관/숨김 기슭막이 세트가 직접 붙었거나(section.culvert) **연동으로
// 옆에서 이어져 온**(culvertLink) 경우엔 배관 경로가 그린다 — 옛 D경로는 건너뛴다
// (둘 다 그리면 이웃 카드에 벽이 겹친다 — 2026-08-28 이관 이중그리기 방지).
if (!section.culvert && !culvertLink) {
// ⚠ 가드를 **좁혔다**(2026-09-09) — 예전에는 링크가 **있기만 하면** 막았는데,
// 관이 아홉·열하나인 노선에서는 링크가 거의 모든 측점을 덮어 **구조물을 놓아도
// 횡단도에 아무것도 안 보였다**(사용자에게 보이던 결함). 겹침 방지라는 까닭은
// 그대로 두고, **그 링크의 벽이 이 카드에 실제로 설 때만** 막는다.
const linkedWallsHere = !!culvertLink && culvertWallsStandAt(culvertLink.source, section);
if (!section.culvert && !linkedWallsHere) {
const ownAdjust = revetOffset?.adjustFor(section, "own");
const ownLayout = computeRevetmentLayout(section, ownAdjust);
ownDesignTrim = ownLayout?.designTrim;
@@ -32,6 +32,8 @@ export interface WallSpec {
form: string | null;
height_m: number | null;
side: string | null;
/** 기초 축 — "기초유" | "기초버림". 저장 칸과 같은 글자(터파기 그림이 이 값으로 갈린다). */
foundation: string | null;
tiers: number | null;
lift_m: number | null;
shift_m: number | null;
@@ -78,6 +80,8 @@ export function wallSpecsFrom(
form: (options.form as string) || FORM_BY_TYPE[structure.type_id] || null,
height_m: num(options.height_m),
side: (options.side as string) ?? null,
// 기초 축 — 저장 칸과 **같은 글자**. 초안 경로에서 빠지면 터파기가 안 그려진다.
foundation: (options.foundation as string) ?? null,
tiers: num(options.tiers),
lift_m: num(options.lift_m),
shift_m: num(options.shift_m),