설계툴 검토

This commit is contained in:
2026-07-19 14:27:50 +09:00
parent 039c4d0292
commit 68d6fe91e6
9 changed files with 148 additions and 21 deletions
@@ -11,6 +11,7 @@
import { AcApDocManager } from "@mlightcad/cad-simple-viewer";
import {
acdbHostApplicationServices,
AcCmColor,
AcDbLayerTableRecord,
AcDbLine,
@@ -113,7 +114,14 @@ export function mountPocCadViewer(host: HTMLElement, drawing: CadDrawingJson): n
if (!drawingLoaded) {
appended = loadDrawingIntoDatabase(manager, drawing);
drawingLoaded = true;
// 파일 오픈 흐름이 아니므로 layoutSwitched 이벤트를 직접 발화해야
// 뷰가 레이아웃 뷰 생성·가시화·초기 줌을 수행한다 (미발화 시 빈 화면).
const db = manager.curDocument.database;
const modelSpaceBtrId = db.tables.blockTable.modelSpace.objectId;
manager.setActiveLayout();
acdbHostApplicationServices().layoutManager.setCurrentLayoutBtrId(modelSpaceBtrId, db);
manager.curView.zoomToFitDrawing();
}
manager.curView.zoomToFitDrawing();
return appended;
}
@@ -1,10 +1,12 @@
/* =============================================================================
* B07_wf4_DesignDetail_UI_Page.ts
* 로그인 후 07: 4차 워크플로우 (상세 설계) — WebCAD PoC
* 로그인 후 07: 4차 워크플로우 (상세 설계) — WebCAD 전체 앱 임베드
*
* 레이아웃 (PLAN.md WebCAD 사용자 지시):
* - 사이드 패널: 빈 상태 유지 (B06→B07 전달 데이터 스펙 미확정)
* - 상세 페이지 영역: CAD 뷰어 (서버 기하 JSON 렌더링, DXF 파싱 없음)
* 방향 전환 (PLAN.md 2026-07-19): 코어 조립이 아니라 완성된 오픈소스 CAD 앱
* (external_program/cad-viewer-example, GPL 워커 제외 빌드)을 /cadviewer 정적
* 경로로 서빙하고 iframe으로 임베드한다. 데이터 연동은 후속 단계.
*
* 레이아웃 (사용자 지시): 사이드 패널 빈 상태 유지 + 상세 영역 CAD 화면.
* 제약 준수 (frontend.md §2 3단 레이아웃): createWorkflowLayout 재사용.
* ========================================================================== */
@@ -19,8 +21,9 @@ import {
WORKFLOW_STEP_ROUTES,
type WorkflowState,
} from "../A00_Common/b_workflow_nav";
import { fetchPocSampleDrawing } from "./B07_wf4_DesignDetail_Api_Fetch";
import { mountPocCadViewer } from "./B07_wf4_DesignDetail_UI_CadViewer";
/** WebCAD 전체 앱 정적 경로 (main.py /cadviewer 마운트, dev는 vite proxy 위임) */
const CAD_VIEWER_APP_URL = "/cadviewer/index.html";
/** locale 헬퍼 */
function L(key: keyof typeof ui_locales): string {
@@ -57,10 +60,11 @@ export async function renderB07DesignDetail(root: HTMLElement): Promise<void> {
const cadHost = document.createElement("div");
cadHost.className = "b07-cad-host";
const status = document.createElement("div");
status.className = "b07-cad-status";
status.textContent = L("B07_Cad_Loading");
cadHost.append(status);
const frame = document.createElement("iframe");
frame.className = "b07-cad-frame";
frame.src = CAD_VIEWER_APP_URL;
frame.title = L("B07_Design_Title");
cadHost.append(frame);
const layout = createWorkflowLayout({
title: L("B07_Design_Title"),
@@ -77,14 +81,4 @@ export async function renderB07DesignDetail(root: HTMLElement): Promise<void> {
});
layout.root.classList.add("b07-design-layout");
root.replaceChildren(layout.root);
try {
const drawing = await fetchPocSampleDrawing();
mountPocCadViewer(cadHost, drawing);
status.remove();
} catch (error) {
status.textContent = `${L("B07_Cad_Load_Failed")} ${
error instanceof Error ? error.message : ""
}`.trim();
}
}
@@ -61,6 +61,15 @@
display: block;
}
/* WebCAD 전체 앱 임베드 (iframe) */
.b07-cad-frame {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
border: 0;
}
/* 로딩·오류 상태 메시지 */
.b07-cad-status {
position: absolute;
Submodule external_program/cad-viewer added at 7b9a8084d7
Submodule external_program/cad-viewer-example added at 482a061c44
+9
View File
@@ -223,6 +223,15 @@ app.mount(
)
logger.info(f"✓ 정적 파일 서빙 경로 등록: {STATIC_URL}{STATIC_DIR}")
# WebCAD 전체 앱 (external_program/cad-viewer-example 빌드본, GPL 워커 제외) — B07 iframe용
CADVIEWER_DIST_DIR = str(Path(__file__).parent / "external_program" / "cad-viewer-example" / "dist")
app.mount(
"/cadviewer",
StaticFiles(directory=CADVIEWER_DIST_DIR, html=True, check_dir=False),
name="cadviewer",
)
logger.info(f"✓ WebCAD 정적 서빙 경로 등록: /cadviewer → {CADVIEWER_DIST_DIR}")
# ─────────────────────────────────────────────────────────────────────────
# 기본 엔드포인트
# ─────────────────────────────────────────────────────────────────────────
+56
View File
@@ -0,0 +1,56 @@
import sys
import traceback
from pathlib import Path
import numpy as np
# Add project root to sys.path
PROJECT_ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(PROJECT_ROOT))
from B04_wf1_Surface.B04_wf1_Surface_Engine_VWorld import download_vworld_satellite_map
from B04_wf1_Surface.B04_wf1_Surface_Engine_GisVector import download_all_gis_vectors
PRJ_PATH = PROJECT_ROOT / "storage/1/3/acb9170b-9ac8-49b3-82a0-51cfa32bb42d/B03_FileInput/input/prj/result.prj"
NPZ_PATH = PROJECT_ROOT / "storage/1/3/acb9170b-9ac8-49b3-82a0-51cfa32bb42d/B04_wf1_Surface/processed/ground_points_csf.npz"
OUTPUT_DIR = PROJECT_ROOT / "storage/1/3/acb9170b-9ac8-49b3-82a0-51cfa32bb42d/B04_wf1_Surface/processed"
def main():
print("Loading NPZ to get bounds...")
with np.load(NPZ_PATH) as data:
bounds = data["bounds"]
print("Raw bounds:", bounds)
bounds_dict = {
"x": [float(bounds[0, 0]), float(bounds[0, 1])],
"y": [float(bounds[1, 0]), float(bounds[1, 1])],
"z": [float(bounds[2, 0]), float(bounds[2, 1])],
}
print("\n--- Test 1: VWorld Satellite Map Download ---")
try:
res = download_vworld_satellite_map(
prj_path=PRJ_PATH,
bounds=bounds_dict,
output_dir=OUTPUT_DIR,
layer_name="Satellite",
ext="jpeg"
)
print("Download Result:", res)
except Exception as e:
print("VWorld Download Failed:")
traceback.print_exc()
print("\n--- Test 2: GIS Vector Download ---")
try:
download_all_gis_vectors(
prj_path=PRJ_PATH,
bounds_meter=bounds_dict,
output_dir=OUTPUT_DIR
)
print("GIS Vector Download completed.")
except Exception as e:
print("GIS Download Failed:")
traceback.print_exc()
if __name__ == "__main__":
main()
+44
View File
@@ -170,6 +170,12 @@
--card-padding: 24px;
--element-gap: 8px;
/* Scrollbar */
--color-scrollbar-track: color-mix(in srgb, var(--color-text) 9%, transparent);
--color-scrollbar-thumb: color-mix(in srgb, var(--color-text) 38%, transparent);
--color-scrollbar-thumb-hover: color-mix(in srgb, var(--color-text) 56%, transparent);
--color-scrollbar-thumb-active: color-mix(in srgb, var(--color-text) 72%, transparent);
/* 워크플로우 3단 레이아웃 (frontend.md §2) */
--wf-header-height: 56px; /* 상단: 타이틀 + 진행 단계 */
--wf-left-panel-width: 320px; /* 좌측: 입력 폼 (너비 고정) */
@@ -271,6 +277,44 @@
box-sizing: border-box;
}
/* Keep the platform scrollbar footprint while giving every scroll container a
* consistent translucent treatment. Width/height are intentionally untouched. */
* {
scrollbar-color: var(--color-scrollbar-thumb) var(--color-scrollbar-track);
scrollbar-width: auto;
}
@supports selector(::-webkit-scrollbar) {
* {
scrollbar-color: auto;
}
*::-webkit-scrollbar-track {
background-color: var(--color-scrollbar-track);
}
*::-webkit-scrollbar-thumb {
border: 3px solid transparent;
border-radius: var(--radius-pills);
background-color: var(--color-scrollbar-thumb);
background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover {
background-color: var(--color-scrollbar-thumb-hover);
}
*::-webkit-scrollbar-thumb:active {
background-color: var(--color-scrollbar-thumb-active);
}
}
@media (forced-colors: active) {
* {
scrollbar-color: auto;
}
}
html,
body {
margin: 0;
+5
View File
@@ -41,6 +41,11 @@ export default {
target: "http://localhost:8000",
changeOrigin: true,
},
// WebCAD 전체 앱 (B07 iframe) — FastAPI 정적 마운트로 위임
"/cadviewer": {
target: "http://localhost:8000",
changeOrigin: true,
},
},
},
build: {