- helper_ts_stub_loader: 옛 헬퍼가 저마다 다르게 쓰던 것(풀이 함수 직접 · esModuleInterop · 캐시 끔 · 파일 통째 대역)을 옵션으로 · 기본 풀이는 그대로 - 헬퍼 동작 그대로(풀이 함수 본문은 옮겨 담음) · 제품 코드 안 고침 - 확인: 이 묶음 헬퍼를 쓰는 시험 241 개 전 241 통과 → 후 241 통과 (비교 = 시험별 junit) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
94 lines
3.7 KiB
JavaScript
94 lines
3.7 KiB
JavaScript
/* PLAN 135-2 — 흑백 지형 단추: 분할본 색 = 지형 원래 색 · far 색 버퍼 따로 · 원래 색 기억 = 색 버퍼 열쇠.
|
|
* TS 를 트랜스파일해 돌리고 결과(JSON)를 낸다(helper_115_8 과 같은 방식). */
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ROOT = path.resolve(__dirname, "..", "..");
|
|
const NM = path.join(ROOT, "config", "node_modules");
|
|
const { makeLoader } = require("./helper_ts_stub_loader.cjs");
|
|
const THREE = require(path.join(NM, "three"));
|
|
|
|
const { loadTs } = makeLoader({
|
|
root: ROOT,
|
|
cache: false,
|
|
requireFor: (full) => {
|
|
return (r) => (r === "three" ? THREE : require(r));
|
|
},
|
|
});
|
|
const B05 = (name) => path.join(ROOT, "B05_Profile", `${name}.ts`);
|
|
const { TerrainBandSplit, ORIGINAL_COLORS } = loadTs(B05("B05_Profile_UI_Corridor_Split"));
|
|
|
|
// 뷰어 applyGrayscaleTo 와 같은 규칙(색 버퍼 열쇠로 원본 한 번만 기억).
|
|
function gray(object, on) {
|
|
object.traverse((child) => {
|
|
const color = child.geometry?.getAttribute("color");
|
|
if (!color) return;
|
|
const target = color.array;
|
|
let original = ORIGINAL_COLORS.get(color);
|
|
if (!original) {
|
|
original = target.slice();
|
|
ORIGINAL_COLORS.set(color, original);
|
|
}
|
|
if (on) {
|
|
for (let i = 0; i + 2 < original.length; i += 3) {
|
|
const g = 0.299 * original[i] + 0.587 * original[i + 1] + 0.114 * original[i + 2];
|
|
target[i] = target[i + 1] = target[i + 2] = g;
|
|
}
|
|
} else target.set(original);
|
|
});
|
|
}
|
|
|
|
// 지형 — 40 × 40 m 격자 · 색 = 빨강 → 초록(회색과 다름).
|
|
const N = 21;
|
|
const pos = [];
|
|
const col = [];
|
|
const idx = [];
|
|
for (let j = 0; j < N; j += 1)
|
|
for (let i = 0; i < N; i += 1) {
|
|
pos.push(i * 2 - 20, 0, j * 2 - 20);
|
|
col.push(1, i / (N - 1), 0);
|
|
}
|
|
for (let j = 0; j + 1 < N; j += 1)
|
|
for (let i = 0; i + 1 < N; i += 1) {
|
|
const a = j * N + i;
|
|
idx.push(a, a + N, a + 1, a + 1, a + N, a + N + 1);
|
|
}
|
|
const geometry = new THREE.BufferGeometry();
|
|
geometry.setAttribute("position", new THREE.Float32BufferAttribute(pos, 3));
|
|
geometry.setAttribute("color", new THREE.Float32BufferAttribute(col, 3));
|
|
geometry.setIndex(idx);
|
|
const terrain = new THREE.Group();
|
|
terrain.add(new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ vertexColors: true })));
|
|
const original = Float32Array.from(col);
|
|
|
|
gray(terrain, true); // 기본 흑백 — 지형 적재 직후 회색 먼저.
|
|
const split = new TerrainBandSplit(terrain, { minX: -5, maxX: 5, minZ: -5, maxZ: 5 });
|
|
const near = split.near[0].geometry.getAttribute("color");
|
|
const far = split.far[0].geometry.getAttribute("color");
|
|
const same = (a, b) => a.length === b.length && a.every((v, k) => Math.abs(v - b[k]) < 1e-6);
|
|
const r = {};
|
|
r.nearOriginal = same(near.array, original);
|
|
r.farOriginal = same(far.array, original);
|
|
r.farOwnBuffer = near !== far && near.array !== far.array;
|
|
|
|
// far 를 감싼 메시 두 번(③ 다시 짬) — 흑백 → 끔이 원래 색으로.
|
|
const wrap = () => {
|
|
const root = new THREE.Group();
|
|
root.add(new THREE.Mesh(split.far[0].geometry, split.far[0].material));
|
|
return root;
|
|
};
|
|
const first = wrap();
|
|
gray(first, true);
|
|
r.nearUntouchedByFarGray = same(near.array, original);
|
|
const second = wrap();
|
|
gray(second, true); // 새 메시 — 회색을 원본으로 잡으면 안 됨.
|
|
gray(second, false);
|
|
r.farBackToOriginal = same(far.array, original);
|
|
gray(terrain, false);
|
|
r.terrainBackToOriginal = same(geometry.getAttribute("color").array, original);
|
|
|
|
// 뷰어가 같은 열쇠를 쓰는가.
|
|
const viewer = fs.readFileSync(B05("B05_Profile_UI_Viewer"), "utf8");
|
|
r.viewerUsesKey =
|
|
viewer.includes("ORIGINAL_COLORS.get(color)") && !viewer.includes("userData.originalColors");
|
|
process.stdout.write(JSON.stringify(r));
|