refactor(B04): 전처리 화면 자체 변종을 부품 상자 부품으로 — 켜기 칩 · 분할 고르기 · 고르기 · 슬라이더 · 값 목록 · 카드 · 도구줄 (PLAN 553-1)

- 지도 층 단추 열다섯 안팎 · 표시 토글 셋 · 라이다 겹침 = 켜기 칩(층 색 점) · 도엽 보간 방식 = 분할 고르기 · 도구줄 부품
- 고르기 38 → 고르기 부품 · 점 크기 · 밀도 = 슬라이더 · 입력 파일 정보 = 값 목록 · 초기화 = 위험 고스트 · 묶음 상자 = 카드 + 패널 머리
- 죽은 CSS 열여섯 걷음 · 날 색 → 공통 값 · 가이드 촬영 선택자 · 지킴 시험 test_553_1_pages 늘림

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 22:09:36 +09:00
co-authored by Claude Sonnet 5.5
parent a826804277
commit 58dafb1572
14 changed files with 441 additions and 858 deletions
+28 -59
View File
@@ -1,9 +1,13 @@
import { RENDER_OPTIONS } from "@config/config_frontend";
import { createBadge } from "@ui/ui_template_badge";
import { createButton } from "@ui/ui_template_elements";
import { createSlider } from "@ui/ui_template_input";
import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale";
import { createProgressCircle } from "@ui/ui_template_progress";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import type { SurfaceBounds, SurfacePointCloudSampleResponse } from "./B04_PreProcess_Api_Fetch";
import { buildSurfaceGroup } from "./B04_PreProcess_UI_Group";
import {
bindCursorPivotControls,
bindSurfaceViewerTheme,
@@ -41,19 +45,16 @@ export interface SurfacePointCloudViewer {
type CameraView = "iso" | "top" | "front" | "side";
function makeButton(label: string): HTMLButtonElement {
const button = document.createElement("button");
button.type = "button";
button.textContent = label;
return button;
return createButton({ label, variant: "ghost", size: 28 });
}
export function createSurfacePointCloudViewer(): SurfacePointCloudViewer {
const root = document.createElement("div");
root.className = "point-viewer";
root.className = "ui-card point-viewer";
const statusSpan = document.createElement("span");
statusSpan.className = "b04-surface__status-info";
statusSpan.textContent = "포인트 데이터 로딩 중...";
// 뷰어 위 상태 글 — 배지 부품 · 자리(좌상단)만 B04 쪽 클래스가 정함
const statusSpan = createBadge("포인트 데이터 로딩 중...", "neutral");
statusSpan.classList.add("b04-surface__status-info");
const controls = document.createElement("div");
controls.className = "viewer-controls";
@@ -65,60 +66,31 @@ export function createSurfacePointCloudViewer(): SurfacePointCloudViewer {
];
controls.append(...viewButtons.map(([, button]) => button));
const controlsGroup = document.createElement("section");
controlsGroup.className = "b04-surface__group";
const controlsTitle = document.createElement("h3");
controlsTitle.className = "b04-surface__panel-title";
controlsTitle.textContent = L("B04_Surface_Group_ViewControls");
controlsGroup.append(controlsTitle, controls);
/** 슬라이더 한 줄 — `이름 [현재값] [게이지]`. 값이 게이지 앞에 온다(2026-08-01 사용자 지시). */
function buildSlider(
label: string,
attributes: { min: string; max: string; step: string; value: string },
): { root: HTMLLabelElement; input: HTMLInputElement; value: HTMLSpanElement } {
const root = document.createElement("label");
root.className = "viewer-option-row";
const name = document.createElement("span");
name.className = "viewer-option-name";
name.textContent = label;
const value = document.createElement("span");
value.className = "viewer-option-val";
const input = document.createElement("input");
input.type = "range";
input.min = attributes.min;
input.max = attributes.max;
input.step = attributes.step;
input.value = attributes.value;
root.append(name, value, input);
return { root, input, value };
}
const controlsGroup = buildSurfaceGroup(L("B04_Surface_Group_ViewControls"), [controls]);
/** 점 크기 · 밀도 — 슬라이더 부품(이름 · 막대 · 현재값). */
const options = document.createElement("div");
options.className = "viewer-options";
const size = buildSlider(L("B04_Surface_Opt_PointSize"), {
min: "0.1",
max: "2.5",
step: "0.01",
value: "0.42",
options.className = "b04-surface__options";
const size = createSlider({
label: L("B04_Surface_Opt_PointSize"),
min: 0.1,
max: 2.5,
step: 0.01,
value: 0.42,
format: (value) => value.toFixed(2),
});
const density = buildSlider(L("B04_Surface_Opt_Density"), {
min: "1",
max: "10",
step: "1",
value: "10",
const density = createSlider({
label: L("B04_Surface_Opt_Density"),
min: 1,
max: 10,
step: 1,
value: 10,
format: (value) => `${value * 10}%`,
});
const sizeInput = size.input;
const densityInput = density.input;
options.append(size.root, density.root);
/** 게이지 앞 숫자를 현재값으로 맞춘다. */
function syncOptionValues(): void {
size.value.textContent = Number(sizeInput.value).toFixed(2);
density.value.textContent = `${Number(densityInput.value) * 10}%`;
}
syncOptionValues();
const viewerArea = document.createElement("div");
viewerArea.className = "three-viewer";
const canvas = document.createElement("canvas");
@@ -329,12 +301,10 @@ export function createSurfacePointCloudViewer(): SurfacePointCloudViewer {
button.addEventListener("click", () => setCameraView(view));
});
sizeInput.addEventListener("input", () => {
syncOptionValues();
if (pointsObject)
(pointsObject.material as THREE.PointsMaterial).size = Number(sizeInput.value);
});
densityInput.addEventListener("input", () => {
syncOptionValues();
if (currentData) renderPointCloud(currentData);
});
orbit.addEventListener("change", emitCameraState);
@@ -366,9 +336,8 @@ export function createSurfacePointCloudViewer(): SurfacePointCloudViewer {
},
resetOptions() {
axes.visible = false;
sizeInput.value = "0.42";
densityInput.value = "10";
syncOptionValues();
size.set(0.42);
density.set(10);
if (currentData) renderPointCloud(currentData);
setCameraView("iso");
},