feat(E04): 3D 보기 = 활동체 걷어낸 지표 + 잘린 활동면(테두리 틈 없음) · 노선 관 · 측점 이름 솎기 · 토글 꼴 · 시험 더함 (533-3)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 20:35:16 +09:00
co-authored by Claude Opus 5.5
parent cbc4cf1a59
commit d37efddc46
7 changed files with 155 additions and 62 deletions
+34 -6
View File
@@ -59,6 +59,8 @@ writeFileSync(process.argv[3], JSON.stringify({
terrain: mesh(M.terrainMesh(c.grid, c.z)),
sphere: mesh(M.slipMesh(c.grid, c.z, c.sphere)),
ellipsoid: mesh(M.slipMesh(c.grid, c.z, c.ellipsoid)),
cut: Array.from(M.cutCells(c.grid, c.z, c.sphere).keys()),
holed: mesh(M.terrainWithout(c.grid, c.z, M.cutCells(c.grid, c.z, c.sphere))),
hCenter: M.slipHeight(c.sphere, 110, 207),
hEdge: M.slipHeight(c.sphere, 116.5, 207),
hEll: [M.slipHeight(c.ellipsoid, 121, 207), M.slipHeight(c.ellipsoid, 110, 212)],
@@ -146,17 +148,43 @@ def test_terrain_mesh(ts):
assert 4 * nx + 3 not in ts["terrain"]["cells"]
def _py_rim(cut: set[int]) -> set[int]:
nx, ny = GRID["nx"], GRID["ny"]
rim = set()
for index in cut:
ix, iy = index % nx, index // nx
for dy in (-1, 0, 1):
for dx in (-1, 0, 1):
jx, jy = ix + dx, iy + dy
nxt = jy * nx + jx
if 0 <= jx < nx and 0 <= jy < ny and nxt not in cut and Z[nxt] is not None:
rim.add(nxt)
return rim
@pytest.mark.parametrize("name", ["sphere", "ellipsoid"])
def test_slip_mesh_vertices(ts, name):
"""활동면 꼭짓점 = 잘린 셀(활동면 높이 · 지표 아래) + 테두리 셀(지표 높이)."""
best = SPHERE if name == "sphere" else ELLIPSOID
want = _py_cut(best)
cut = dict(_py_cut(best))
rim = _py_rim(set(cut))
want = sorted(set(cut) | rim)
got = ts[name]
assert got["n"] == len(want) > 20
assert got["cells"] == [i for i, _ in want]
assert got["zs"] == pytest.approx([z for _, z in want], abs=1e-3) # Float32
assert len(cut) > 20 and rim
assert got["n"] == len(want)
assert got["cells"] == want
assert got["zs"] == pytest.approx([cut.get(i, Z[i]) for i in want], abs=1e-3) # Float32
assert got["tri"] > 0
# 활동면 꼭짓점은 모두 지표 아래
assert all(z < Z[i] for i, z in want)
assert all(z < Z[i] for i, z in cut.items())
def test_terrain_hole_matches_cut(ts):
"""활동체를 걷어낸 지표 = 지표 꼭짓점 − 잘린 셀 · 구멍 테두리는 활동면 메쉬와 같은 꼭짓점."""
cut = [i for i, _ in _py_cut(SPHERE)]
assert ts["cut"] == cut
assert ts["holed"]["n"] == ts["terrain"]["n"] - len(cut)
assert not set(ts["holed"]["cells"]) & set(cut)
assert _py_rim(set(cut)) <= set(ts["holed"]["cells"])
def test_slip_height_and_terrain_height(ts):