fix: announce 3d camera orientation

This commit is contained in:
2026-08-30 11:42:54 +09:00
parent 2a2e192196
commit 7172d5654a
2 changed files with 54 additions and 7 deletions
+30 -7
View File
@@ -85,8 +85,7 @@ const beamPatternSurfaceView = createReceiveBeamPatternSurfaceView(
elements["beam-pattern-surface"], elements["beam-pattern-surface"],
{ {
onViewDirectionChange(direction) { onViewDirectionChange(direction) {
elements["beam-pattern-axis-view"].value = direction; syncBeamViewDirection(direction);
document.documentElement.dataset.beamPatternViewAxis = direction;
}, },
}, },
); );
@@ -372,8 +371,29 @@ const VIEW_DIRECTION_LABELS = Object.freeze({
custom: "자유 회전", custom: "자유 회전",
}); });
function resolvedViewDirection(direction) {
return Object.hasOwn(VIEW_DIRECTION_LABELS, direction) ? direction : "iso";
}
function viewDirectionDescription(direction) {
const resolved = resolvedViewDirection(direction);
return resolved === "custom"
? VIEW_DIRECTION_LABELS.custom
: VIEW_DIRECTION_LABELS[resolved] + " 원점 보기";
}
function syncBeamViewDirection(direction) {
const resolved = resolvedViewDirection(direction);
elements["beam-pattern-axis-view"].value = resolved;
document.documentElement.dataset.beamPatternViewAxis = resolved;
elements["beam-pattern-surface"].setAttribute(
"aria-label",
"3D 수신 빔 패턴 표면 · " + viewDirectionDescription(resolved),
);
}
function syncArrayViewDirection(direction) { function syncArrayViewDirection(direction) {
const resolved = Object.hasOwn(VIEW_DIRECTION_LABELS, direction) ? direction : "iso"; const resolved = resolvedViewDirection(direction);
elements["array-axis-view"].value = resolved; elements["array-axis-view"].value = resolved;
elements["array-canvas"].dataset.viewAxis = resolved; elements["array-canvas"].dataset.viewAxis = resolved;
document.documentElement.dataset.arrayViewAxis = resolved; document.documentElement.dataset.arrayViewAxis = resolved;
@@ -381,6 +401,10 @@ function syncArrayViewDirection(direction) {
elements["canvas-front-badge"].textContent = resolved === "custom" elements["canvas-front-badge"].textContent = resolved === "custom"
? "+Z FRONT · 자유 회전" ? "+Z FRONT · 자유 회전"
: "시점 " + VIEW_DIRECTION_LABELS[resolved] + " 원점 보기 · +Z FRONT"; : "시점 " + VIEW_DIRECTION_LABELS[resolved] + " 원점 보기 · +Z FRONT";
elements["array-canvas"].setAttribute(
"aria-label",
"마이크 어레이 3D 검사 canvas · " + viewDirectionDescription(resolved),
);
} }
} }
@@ -434,7 +458,6 @@ function setArrayViewMode(mode, options = {}) {
elements["canvas-front-badge"].textContent = resolved === "3d" elements["canvas-front-badge"].textContent = resolved === "3d"
? "+Z FRONT · 초록 축" ? "+Z FRONT · 초록 축"
: "+Z FRONT · 화면 바깥쪽"; : "+Z FRONT · 화면 바깥쪽";
if (resolved === "3d") syncArrayViewDirection(elements["array-axis-view"].value);
elements["array-canvas"].setAttribute( elements["array-canvas"].setAttribute(
"aria-label", "aria-label",
resolved === "3d" resolved === "3d"
@@ -447,6 +470,7 @@ function setArrayViewMode(mode, options = {}) {
? "ArrowLeft ArrowRight ArrowUp ArrowDown F R Escape" ? "ArrowLeft ArrowRight ArrowUp ArrowDown F R Escape"
: "V A F Escape Delete ArrowLeft ArrowRight ArrowUp ArrowDown", : "V A F Escape Delete ArrowLeft ArrowRight ArrowUp ArrowDown",
); );
if (resolved === "3d") syncArrayViewDirection(elements["array-axis-view"].value);
setTool(state.tool); setTool(state.tool);
canvasView.render(canvasState()); canvasView.render(canvasState());
if (options.focus) canvasView.focus(); if (options.focus) canvasView.focus();
@@ -1409,7 +1433,7 @@ elements["array-view-3d-button"].addEventListener("click", () =>
elements["array-axis-view"].addEventListener("change", () => { elements["array-axis-view"].addEventListener("change", () => {
const direction = elements["array-axis-view"].value; const direction = elements["array-axis-view"].value;
canvasView.setViewDirection(direction); canvasView.setViewDirection(direction);
announceSelection("3D 시점 · " + VIEW_DIRECTION_LABELS[direction] + " 원점 보기"); announceSelection("3D 시점 · " + viewDirectionDescription(direction));
}); });
elements["fit-view-button"].addEventListener("click", () => canvasView.fit()); elements["fit-view-button"].addEventListener("click", () => canvasView.fit());
elements["zoom-in-button"].addEventListener("click", () => canvasView.zoomBy(1.25)); elements["zoom-in-button"].addEventListener("click", () => canvasView.zoomBy(1.25));
@@ -1659,8 +1683,7 @@ window.addEventListener("unload", () => {
elements["snap-enabled"].checked = state.snapEnabled; elements["snap-enabled"].checked = state.snapEnabled;
setNumber(elements["snap-step"], state.snapStepM * 1000); setNumber(elements["snap-step"], state.snapStepM * 1000);
syncArrayViewDirection("iso"); syncArrayViewDirection("iso");
elements["beam-pattern-axis-view"].value = "iso"; syncBeamViewDirection("iso");
document.documentElement.dataset.beamPatternViewAxis = "iso";
setArrayViewMode("2d", { announce: false }); setArrayViewMode("2d", { announce: false });
setBeamPatternViewMode("cuts", { render: false }); setBeamPatternViewMode("cuts", { render: false });
syncBeamPatternFrequency(); syncBeamPatternFrequency();
@@ -282,6 +282,10 @@ test("Array Builder가 preset·직접 편집·검증·표준 JSON round trip을
const beamAxisView = documentObject.querySelector("#beam-pattern-axis-view"); const beamAxisView = documentObject.querySelector("#beam-pattern-axis-view");
assertEqual(beamAxisView.value, "iso", "3D beam surface starts in isometric view"); assertEqual(beamAxisView.value, "iso", "3D beam surface starts in isometric view");
assertEqual(beamSurface.dataset.viewAxis, "iso", "beam canvas exposes isometric provenance"); assertEqual(beamSurface.dataset.viewAxis, "iso", "beam canvas exposes isometric provenance");
assert(
beamSurface.getAttribute("aria-label").includes("등각 원점 보기"),
"beam canvas accessible name exposes its current view",
);
const beamComputeBeforeAxisView = const beamComputeBeforeAxisView =
documentObject.documentElement.dataset.beamPatternComputeMs; documentObject.documentElement.dataset.beamPatternComputeMs;
const beamFrequencyBeforeAxisView = beamSurface.dataset.frequencyHz; const beamFrequencyBeforeAxisView = beamSurface.dataset.frequencyHz;
@@ -357,6 +361,10 @@ test("Array Builder가 preset·직접 편집·검증·표준 JSON round trip을
); );
assertEqual(beamSurface.dataset.viewAxis, "custom", "beam drag enters free-orbit mode"); assertEqual(beamSurface.dataset.viewAxis, "custom", "beam drag enters free-orbit mode");
assertEqual(beamAxisView.value, "custom", "beam view selector reflects free orbit"); assertEqual(beamAxisView.value, "custom", "beam view selector reflects free orbit");
assert(
beamSurface.getAttribute("aria-label").includes("자유 회전"),
"beam canvas accessible name follows free orbit",
);
beamSurface.dispatchEvent(new windowObject.KeyboardEvent("keydown", { beamSurface.dispatchEvent(new windowObject.KeyboardEvent("keydown", {
bubbles: true, bubbles: true,
cancelable: true, cancelable: true,
@@ -365,6 +373,10 @@ test("Array Builder가 preset·직접 편집·검증·표준 JSON round trip을
await nextPaint(windowObject); await nextPaint(windowObject);
assertEqual(beamSurface.dataset.viewAxis, "iso", "beam R shortcut restores isometric view"); assertEqual(beamSurface.dataset.viewAxis, "iso", "beam R shortcut restores isometric view");
assertEqual(beamAxisView.value, "iso", "beam selector follows the R shortcut"); assertEqual(beamAxisView.value, "iso", "beam selector follows the R shortcut");
assert(
beamSurface.getAttribute("aria-label").includes("등각 원점 보기"),
"beam accessible name follows the R shortcut",
);
assertEqual( assertEqual(
documentObject.documentElement.dataset.beamPatternComputeMs, documentObject.documentElement.dataset.beamPatternComputeMs,
beamComputeBeforeAxisView, beamComputeBeforeAxisView,
@@ -446,6 +458,10 @@ test("Array Builder가 preset·직접 편집·검증·표준 JSON round trip을
const arrayAxisView = documentObject.querySelector("#array-axis-view"); const arrayAxisView = documentObject.querySelector("#array-axis-view");
assert(!arrayAxisControl.hidden, "array axis selector appears only for 3D inspection"); assert(!arrayAxisControl.hidden, "array axis selector appears only for 3D inspection");
assertEqual(arrayAxisView.value, "iso", "array 3D starts in isometric view"); assertEqual(arrayAxisView.value, "iso", "array 3D starts in isometric view");
assert(
canvas.getAttribute("aria-label").includes("등각 원점 보기"),
"array canvas accessible name exposes its current view",
);
const arraySaveStateBeforeAxisView = const arraySaveStateBeforeAxisView =
documentObject.querySelector("#save-state").textContent; documentObject.querySelector("#save-state").textContent;
const array3dBeforeAxisView = canvas.toDataURL(); const array3dBeforeAxisView = canvas.toDataURL();
@@ -517,6 +533,10 @@ test("Array Builder가 preset·직접 편집·검증·표준 JSON round trip을
); );
assertEqual(canvas.dataset.viewAxis, "custom", "array drag enters free-orbit mode"); assertEqual(canvas.dataset.viewAxis, "custom", "array drag enters free-orbit mode");
assertEqual(arrayAxisView.value, "custom", "array view selector reflects free orbit"); assertEqual(arrayAxisView.value, "custom", "array view selector reflects free orbit");
assert(
canvas.getAttribute("aria-label").includes("자유 회전"),
"array canvas accessible name follows free orbit",
);
assertEqual( assertEqual(
documentObject.querySelector('#microphone-list [aria-selected="true"]') documentObject.querySelector('#microphone-list [aria-selected="true"]')
?.dataset.microphoneId, ?.dataset.microphoneId,
@@ -531,6 +551,10 @@ test("Array Builder가 preset·직접 편집·검증·표준 JSON round trip을
await nextPaint(windowObject); await nextPaint(windowObject);
assertEqual(canvas.dataset.viewAxis, "iso", "array R shortcut restores isometric view"); assertEqual(canvas.dataset.viewAxis, "iso", "array R shortcut restores isometric view");
assertEqual(arrayAxisView.value, "iso", "array selector follows the R shortcut"); assertEqual(arrayAxisView.value, "iso", "array selector follows the R shortcut");
assert(
canvas.getAttribute("aria-label").includes("등각 원점 보기"),
"array accessible name follows the R shortcut",
);
assertEqual( assertEqual(
documentObject.querySelector("#save-state").textContent, documentObject.querySelector("#save-state").textContent,
arraySaveStateBeforeAxisView, arraySaveStateBeforeAxisView,