feat: visualize array receive beam patterns
This commit is contained in:
@@ -98,6 +98,95 @@ test("Array Builder가 preset·직접 편집·검증·표준 JSON round trip을
|
||||
() => documentObject.querySelector("#array-canvas").width > 0,
|
||||
"Array canvas 렌더",
|
||||
);
|
||||
await waitFor(
|
||||
() => documentObject.documentElement.dataset.beamPatternReady === "true",
|
||||
"기본 수신 빔 패턴 렌더",
|
||||
);
|
||||
const beamPlot = documentObject.querySelector("#beam-pattern-plot");
|
||||
const initialBeamPath = beamPlot.querySelector(".beam-pattern-response")
|
||||
?.getAttribute("d");
|
||||
assert(initialBeamPath?.startsWith("M"), "polar response path is rendered");
|
||||
assertEqual(
|
||||
beamPlot.querySelectorAll(".beam-pattern-ring").length,
|
||||
5,
|
||||
"polar plot has four inner dB rings and the 0 dB ring",
|
||||
);
|
||||
assertEqual(beamPlot.dataset.cut, "xz", "default great-circle cut");
|
||||
assertEqual(beamPlot.dataset.frequencyHz, "1000", "default analog frequency");
|
||||
assert(
|
||||
Number(beamPlot.dataset.sampleCount) >= 720,
|
||||
"auto angular sampling uses at least 720 directions",
|
||||
);
|
||||
assert(
|
||||
documentObject.querySelector("#beam-pattern-model-note").textContent.includes(
|
||||
"Nyquist 초과",
|
||||
),
|
||||
"UI explains that the geometry-only pattern does not fold analog frequency",
|
||||
);
|
||||
|
||||
const initialSaveState = documentObject.querySelector("#save-state").textContent;
|
||||
dispatchValue(
|
||||
windowObject,
|
||||
documentObject.querySelector("#beam-pattern-frequency"),
|
||||
49_125,
|
||||
"input",
|
||||
);
|
||||
await waitFor(
|
||||
() => beamPlot.dataset.frequencyHz === "49125" &&
|
||||
Number(beamPlot.dataset.sampleCount) > 720,
|
||||
"above-Nyquist-style analog pattern with auto angular sampling",
|
||||
);
|
||||
assert(
|
||||
documentObject.querySelector("#beam-pattern-wavelength").textContent.includes("mm"),
|
||||
"high analog frequency uses its short physical wavelength",
|
||||
);
|
||||
dispatchValue(
|
||||
windowObject,
|
||||
documentObject.querySelector("#beam-pattern-samples"),
|
||||
360,
|
||||
);
|
||||
await waitFor(
|
||||
() => beamPlot.dataset.sampleCount === "360" &&
|
||||
documentObject.querySelector("#beam-pattern-hpbw").textContent.includes(
|
||||
"해상도 부족",
|
||||
),
|
||||
"coarse angular grid exposes unresolved metrics",
|
||||
);
|
||||
dispatchValue(
|
||||
windowObject,
|
||||
documentObject.querySelector("#beam-pattern-frequency"),
|
||||
"",
|
||||
"input",
|
||||
);
|
||||
await waitFor(
|
||||
() => documentObject.documentElement.dataset.beamPatternReady === "error",
|
||||
"invalid beam preview setting",
|
||||
);
|
||||
assert(
|
||||
!documentObject.querySelector("#export-button").disabled,
|
||||
"invalid preview never blocks valid Array export",
|
||||
);
|
||||
dispatchValue(
|
||||
windowObject,
|
||||
documentObject.querySelector("#beam-pattern-samples"),
|
||||
"auto",
|
||||
);
|
||||
dispatchValue(
|
||||
windowObject,
|
||||
documentObject.querySelector("#beam-pattern-frequency"),
|
||||
1_000,
|
||||
"input",
|
||||
);
|
||||
await waitFor(
|
||||
() => documentObject.documentElement.dataset.beamPatternReady === "true" &&
|
||||
beamPlot.dataset.frequencyHz === "1000",
|
||||
"beam preview recovery",
|
||||
);
|
||||
assertEqual(
|
||||
documentObject.querySelector("#save-state").textContent,
|
||||
initialSaveState,
|
||||
"beam preview settings are outside Array dirty state",
|
||||
);
|
||||
assertEqual(microphoneCount(documentObject), 12, "default 4x3 channels");
|
||||
assertEqual(
|
||||
documentObject.querySelector("#microphone-list [data-microphone-id]").dataset.microphoneId,
|
||||
@@ -143,7 +232,12 @@ test("Array Builder가 preset·직접 편집·검증·표준 JSON round trip을
|
||||
dispatchDragPointer("pointerdown", 0, 0, 1);
|
||||
dispatchDragPointer("pointermove", 36, -24, 1);
|
||||
dispatchDragPointer("pointerup", 36, -24, 0);
|
||||
await nextPaint(windowObject);
|
||||
await waitFor(
|
||||
() => documentObject.documentElement.dataset.beamPatternPreview === "false" &&
|
||||
beamPlot.querySelector(".beam-pattern-response")?.getAttribute("d") !==
|
||||
initialBeamPath,
|
||||
"beam pattern follows edited microphone geometry",
|
||||
);
|
||||
const draggedPosition = selectedCoordinates(documentObject);
|
||||
assert(
|
||||
draggedPosition[0] !== initialFirstPosition[0] &&
|
||||
@@ -444,6 +538,7 @@ test("Array Builder가 preset·직접 편집·검증·표준 JSON round trip을
|
||||
assert(!Object.hasOwn(exported, "shape"), "editor shape is not exported");
|
||||
assert(!Object.hasOwn(exported, "preset"), "editor preset is not exported");
|
||||
assert(!Object.hasOwn(exported, "seed"), "editor seed is not exported");
|
||||
assert(!Object.hasOwn(exported, "beamPattern"), "beam preview is not exported");
|
||||
const calibrated = exported.microphones.find(
|
||||
(microphone) => microphone.id === "reference-channel",
|
||||
);
|
||||
@@ -694,6 +789,28 @@ test("Array Builder가 preset·직접 편집·검증·표준 JSON round trip을
|
||||
documentObject.documentElement.clientWidth + 1,
|
||||
"Array Builder has no horizontal overflow at 390px",
|
||||
);
|
||||
const compactBeamPlotRect = documentObject.querySelector("#beam-pattern-plot")
|
||||
.getBoundingClientRect();
|
||||
const compactBeamCardRect = documentObject.querySelector("#beam-pattern-panel")
|
||||
.getBoundingClientRect();
|
||||
assert(
|
||||
compactBeamPlotRect.left >= compactBeamCardRect.left - 1 &&
|
||||
compactBeamPlotRect.right <= compactBeamCardRect.right + 1,
|
||||
"compact polar plot stays inside its analysis card",
|
||||
);
|
||||
assert(
|
||||
Math.abs(compactBeamPlotRect.width - compactBeamPlotRect.height) <= 2,
|
||||
"compact polar plot remains square",
|
||||
);
|
||||
const compactBeamFields = [
|
||||
...documentObject.querySelectorAll(".beam-pattern-fields > .field"),
|
||||
].map((field) => field.getBoundingClientRect());
|
||||
assert(
|
||||
compactBeamFields.every(
|
||||
(rect, index) => index === 0 || rect.top >= compactBeamFields[index - 1].bottom - 1,
|
||||
),
|
||||
"compact beam controls stack without clipping",
|
||||
);
|
||||
assert(
|
||||
documentObject.querySelector("#array-canvas").getBoundingClientRect().height >= 350,
|
||||
"Array canvas remains usable at 390px",
|
||||
|
||||
Reference in New Issue
Block a user