feat(계산묶음): 346-1 격자 준비 — designTerrainGridNow(받는 중엔 null · 서버 길 · 받은 뒤 브라우저 길) · groundStageDeps(격자 · elevations 서버 길 4,000점씩 · AbortSignal)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Se7ytqbohmBQnfYpwYFnYf
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
*
|
||||
* GET /projects/{id}/route/terrain-sources → 설계 지형 · 격자 받을 자리(`grid`)
|
||||
* GET /projects/{id}/surface/models/{mid}/grid → DTM 격자 원시 이진(ETag · IndexedDB)
|
||||
* POST /projects/{id}/route/elevations → 격자를 못 쓸 때 계산 묶음 ground 단계의 서버 길
|
||||
*
|
||||
* 주소에 manifest 서명을 붙임 — 보관함은 옛 저장본을 먼저 내므로(`fetchCachedBytes`) 지형이
|
||||
* 바뀐 뒤 옛 격자로 한 번 그리는 일을 막음. DTM 아닌 표현 · 못 받음 = null → 서버 표본 길.
|
||||
@@ -12,6 +13,7 @@
|
||||
import { API_BASE_URL } from "@config/config_frontend";
|
||||
import { fetchCachedBytes } from "../A00_Common/b_asset_cache";
|
||||
import { type DtmGrid, parseDtmGrid } from "../common_util/common_util_surface_sampler";
|
||||
import type { GroundStageDeps } from "./logic/B05_Profile_Pipeline_RouteGround";
|
||||
|
||||
export interface DesignTerrainGrid {
|
||||
/** 이 격자의 지형 — 편집 창 분석 지형이 이것과 같을 때만 씀. */
|
||||
@@ -25,6 +27,8 @@ interface TerrainSourcesPayload {
|
||||
}
|
||||
|
||||
const loaded = new Map<string, Promise<DesignTerrainGrid | null>>();
|
||||
/** 받기가 끝난 격자 — 끝나기 전엔 칸이 없음(그동안 서버 길). */
|
||||
const ready = new Map<string, DesignTerrainGrid | null>();
|
||||
|
||||
async function download(projectId: string): Promise<DesignTerrainGrid | null> {
|
||||
const response = await fetch(`${API_BASE_URL}/projects/${projectId}/route/terrain-sources`, {
|
||||
@@ -45,11 +49,53 @@ async function download(projectId: string): Promise<DesignTerrainGrid | null> {
|
||||
export function loadDesignTerrainGrid(projectId: string): Promise<DesignTerrainGrid | null> {
|
||||
let pending = loaded.get(projectId);
|
||||
if (!pending) {
|
||||
pending = download(projectId).catch(() => {
|
||||
loaded.delete(projectId);
|
||||
return null;
|
||||
});
|
||||
pending = download(projectId).then(
|
||||
(value) => {
|
||||
ready.set(projectId, value);
|
||||
return value;
|
||||
},
|
||||
() => {
|
||||
loaded.delete(projectId);
|
||||
return null;
|
||||
},
|
||||
);
|
||||
loaded.set(projectId, pending);
|
||||
}
|
||||
return pending;
|
||||
}
|
||||
|
||||
/** 지금 쓸 수 있는 격자 — 받는 중이면 null 을 돌려주고 받기를 띄움(기다리지 않음). */
|
||||
export function designTerrainGridNow(projectId: string): DesignTerrainGrid | null {
|
||||
if (!ready.has(projectId)) void loadDesignTerrainGrid(projectId);
|
||||
return ready.get(projectId) ?? null;
|
||||
}
|
||||
|
||||
/** 한 번에 물을 수 있는 점 수 — 서버 `MAX_POINTS` 와 같은 값. */
|
||||
const ELEVATION_CHUNK = 4000;
|
||||
|
||||
/** 계산 묶음 ground 단계에 넣을 격자 · 서버 길(PLAN 346-1). 서버 길 값은 mm 자리(응답 그대로). */
|
||||
export function groundStageDeps(projectId: string): Pick<GroundStageDeps, "grid" | "remote"> {
|
||||
return {
|
||||
grid: () => designTerrainGridNow(projectId),
|
||||
async remote(points, terrain, signal) {
|
||||
const chunks: Array<Array<[number, number]>> = [];
|
||||
for (let i = 0; i < points.length; i += ELEVATION_CHUNK) {
|
||||
chunks.push(points.slice(i, i + ELEVATION_CHUNK));
|
||||
}
|
||||
const answers = await Promise.all(
|
||||
chunks.map(async (chunk) => {
|
||||
const response = await fetch(`${API_BASE_URL}/projects/${projectId}/route/elevations`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ points: chunk, terrain }),
|
||||
signal,
|
||||
});
|
||||
if (!response.ok) throw new Error(`지반고 요청 실패: HTTP ${response.status}`);
|
||||
return ((await response.json()) as { z: Array<number | null> }).z;
|
||||
}),
|
||||
);
|
||||
return answers.flat();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user