feat: add axis-aligned 3d camera views
This commit is contained in:
@@ -42,6 +42,7 @@ const requiredIds = [
|
||||
"preset-hint",
|
||||
"generate-preset-button", "array-canvas", "select-tool-button",
|
||||
"add-tool-button", "array-view-2d-button", "array-view-3d-button",
|
||||
"array-axis-view-control", "array-axis-view",
|
||||
"canvas-front-badge", "fit-view-button", "zoom-in-button", "zoom-out-button",
|
||||
"snap-enabled", "snap-step", "microphone-list", "microphone-id",
|
||||
"microphone-x", "microphone-y", "microphone-z", "add-microphone-button",
|
||||
@@ -52,7 +53,7 @@ const requiredIds = [
|
||||
"beam-pattern-samples", "beam-pattern-surface-resolution",
|
||||
"beam-pattern-floor", "beam-pattern-view-cuts-button",
|
||||
"beam-pattern-view-3d-button", "beam-pattern-cuts-view",
|
||||
"beam-pattern-surface-view", "beam-pattern-surface",
|
||||
"beam-pattern-surface-view", "beam-pattern-surface", "beam-pattern-axis-view",
|
||||
"beam-pattern-plot-xz", "beam-pattern-plot-yz", "beam-pattern-plot-xy",
|
||||
"beam-pattern-caption-xz", "beam-pattern-caption-yz",
|
||||
"beam-pattern-caption-xy", "beam-pattern-surface-caption",
|
||||
@@ -82,6 +83,12 @@ const beamPatternViews = Object.fromEntries(BEAM_PATTERN_CUTS.map((cut) => [
|
||||
]));
|
||||
const beamPatternSurfaceView = createReceiveBeamPatternSurfaceView(
|
||||
elements["beam-pattern-surface"],
|
||||
{
|
||||
onViewDirectionChange(direction) {
|
||||
elements["beam-pattern-axis-view"].value = direction;
|
||||
document.documentElement.dataset.beamPatternViewAxis = direction;
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const splitPane = createSplitPane({
|
||||
@@ -354,6 +361,29 @@ function announceSelection(message) {
|
||||
if (elements["selection-status"]) elements["selection-status"].textContent = message;
|
||||
}
|
||||
|
||||
const VIEW_DIRECTION_LABELS = Object.freeze({
|
||||
iso: "등각",
|
||||
"+x": "+X에서",
|
||||
"-x": "−X에서",
|
||||
"+y": "+Y에서",
|
||||
"-y": "−Y에서",
|
||||
"+z": "+Z FRONT에서",
|
||||
"-z": "−Z BACK에서",
|
||||
custom: "자유 회전",
|
||||
});
|
||||
|
||||
function syncArrayViewDirection(direction) {
|
||||
const resolved = Object.hasOwn(VIEW_DIRECTION_LABELS, direction) ? direction : "iso";
|
||||
elements["array-axis-view"].value = resolved;
|
||||
elements["array-canvas"].dataset.viewAxis = resolved;
|
||||
document.documentElement.dataset.arrayViewAxis = resolved;
|
||||
if (state.arrayViewMode === "3d") {
|
||||
elements["canvas-front-badge"].textContent = resolved === "custom"
|
||||
? "+Z FRONT · 자유 회전"
|
||||
: "시점 " + VIEW_DIRECTION_LABELS[resolved] + " 원점 보기 · +Z FRONT";
|
||||
}
|
||||
}
|
||||
|
||||
function dismissCanvasInstructions() {
|
||||
elements["canvas-instructions"]?.classList.add("is-hidden");
|
||||
}
|
||||
@@ -382,6 +412,7 @@ function setArrayViewMode(mode, options = {}) {
|
||||
elements["array-view-3d-button"].classList.toggle("is-active", resolved === "3d");
|
||||
elements["array-view-2d-button"].setAttribute("aria-pressed", String(resolved === "2d"));
|
||||
elements["array-view-3d-button"].setAttribute("aria-pressed", String(resolved === "3d"));
|
||||
elements["array-axis-view-control"].hidden = resolved !== "3d";
|
||||
elements["add-tool-button"].disabled = resolved === "3d";
|
||||
elements["select-tool-button"].disabled = resolved === "3d";
|
||||
const instruction = elements["canvas-instructions"];
|
||||
@@ -403,6 +434,7 @@ function setArrayViewMode(mode, options = {}) {
|
||||
elements["canvas-front-badge"].textContent = resolved === "3d"
|
||||
? "+Z FRONT · 초록 축"
|
||||
: "+Z FRONT · 화면 바깥쪽";
|
||||
if (resolved === "3d") syncArrayViewDirection(elements["array-axis-view"].value);
|
||||
elements["array-canvas"].setAttribute(
|
||||
"aria-label",
|
||||
resolved === "3d"
|
||||
@@ -567,6 +599,9 @@ const canvasView = createArrayCanvas(elements["array-canvas"], {
|
||||
onToolChange(tool) {
|
||||
setTool(tool, { userInitiated: true });
|
||||
},
|
||||
onViewDirectionChange(direction) {
|
||||
syncArrayViewDirection(direction);
|
||||
},
|
||||
onFit() {
|
||||
canvasView.fit();
|
||||
},
|
||||
@@ -1371,6 +1406,11 @@ elements["array-view-2d-button"].addEventListener("click", () =>
|
||||
setArrayViewMode("2d", { focus: true }));
|
||||
elements["array-view-3d-button"].addEventListener("click", () =>
|
||||
setArrayViewMode("3d", { focus: true }));
|
||||
elements["array-axis-view"].addEventListener("change", () => {
|
||||
const direction = elements["array-axis-view"].value;
|
||||
canvasView.setViewDirection(direction);
|
||||
announceSelection("3D 시점 · " + VIEW_DIRECTION_LABELS[direction] + " 원점 보기");
|
||||
});
|
||||
elements["fit-view-button"].addEventListener("click", () => canvasView.fit());
|
||||
elements["zoom-in-button"].addEventListener("click", () => canvasView.zoomBy(1.25));
|
||||
elements["zoom-out-button"].addEventListener("click", () => canvasView.zoomBy(0.8));
|
||||
@@ -1399,6 +1439,9 @@ elements["beam-pattern-view-cuts-button"].addEventListener("click", () =>
|
||||
setBeamPatternViewMode("cuts"));
|
||||
elements["beam-pattern-view-3d-button"].addEventListener("click", () =>
|
||||
setBeamPatternViewMode("surface"));
|
||||
elements["beam-pattern-axis-view"].addEventListener("change", () => {
|
||||
beamPatternSurfaceView.setViewDirection(elements["beam-pattern-axis-view"].value);
|
||||
});
|
||||
elements["beam-pattern-panel"].addEventListener("toggle", () => {
|
||||
if (elements["beam-pattern-panel"].open && state.beamPatternDirty) {
|
||||
scheduleBeamPatternRender();
|
||||
@@ -1615,6 +1658,9 @@ window.addEventListener("unload", () => {
|
||||
|
||||
elements["snap-enabled"].checked = state.snapEnabled;
|
||||
setNumber(elements["snap-step"], state.snapStepM * 1000);
|
||||
syncArrayViewDirection("iso");
|
||||
elements["beam-pattern-axis-view"].value = "iso";
|
||||
document.documentElement.dataset.beamPatternViewAxis = "iso";
|
||||
setArrayViewMode("2d", { announce: false });
|
||||
setBeamPatternViewMode("cuts", { render: false });
|
||||
syncBeamPatternFrequency();
|
||||
|
||||
Reference in New Issue
Block a user