fix: make circular and spiral presets truthful

This commit is contained in:
2026-08-30 11:18:51 +09:00
parent 9d937160eb
commit 51c7bbd859
8 changed files with 233 additions and 29 deletions
+110
View File
@@ -67,6 +67,116 @@ test("격자·선형·링·Fermat preset이 개수와 생성 경계를 지킨다
}
});
test("원형과 Fermat preset은 비정사각 경계에서도 실제 내접원 좌표를 만든다", () => {
const aperture = { width: 0.5, height: 0.3 };
const edgeInsetM = 0.02;
const expectedRadius = 0.13;
const count = 12;
for (const type of ["ring", "spiral"]) {
const rectangle = generatePresetMicrophones({
type,
aperture,
shape: "rectangle",
edgeInsetM,
count,
});
const ellipse = generatePresetMicrophones({
type,
aperture,
shape: "ellipse",
edgeInsetM,
count,
});
assertEqual(
JSON.stringify(rectangle),
JSON.stringify(ellipse),
type + " coordinates describe the preset instead of tracing the boundary shape",
);
assert(
rectangle.every((microphone) =>
positionInsideAperture(microphone.position, aperture, "rectangle") &&
positionInsideAperture(microphone.position, aperture, "ellipse")),
type + " inscribed-circle coordinates fit either generation boundary",
);
}
const ring = generatePresetMicrophones({
type: "ring",
aperture,
shape: "rectangle",
edgeInsetM,
count,
});
for (const microphone of ring) {
assertNear(
Math.hypot(microphone.position[0], microphone.position[1]),
expectedRadius,
1e-8,
"ring radius",
);
}
const spiral = generatePresetMicrophones({
type: "spiral",
aperture,
shape: "rectangle",
edgeInsetM,
count,
});
spiral.forEach((microphone, index) => {
assertNear(
Math.hypot(microphone.position[0], microphone.position[1]),
expectedRadius * Math.sqrt((index + 0.5) / count),
1e-8,
"equal-area Fermat radius " + index,
);
});
assertEqual(
generatePresetMicrophones({ type: "spiral", aperture, count: 1 })[0].position.join(","),
"0,0,0",
"single-microphone spiral is centered",
);
});
test("Archimedean preset은 중심부터 내접원까지 선형 반경의 실제 나선을 만든다", () => {
const aperture = { width: 0.5, height: 0.3 };
const edgeInsetM = 0.02;
const maximumRadius = 0.13;
const turns = 2.5;
const count = 11;
const microphones = generatePresetMicrophones({
type: "archimedean",
aperture,
shape: "rectangle",
edgeInsetM,
turns,
count,
});
assertEqual(microphones.length, count, "Archimedean count");
microphones.forEach((microphone, index) => {
assertNear(
Math.hypot(microphone.position[0], microphone.position[1]),
maximumRadius * index / (count - 1),
1e-8,
"Archimedean linear radius " + index,
);
});
assertEqual(microphones[0].position.join(","), "0,0,0", "spiral starts at center");
assertNear(microphones.at(-1).position[0], -maximumRadius, 1e-8, "2.5 turns end at -x");
assertNear(microphones.at(-1).position[1], 0, 1e-8, "2.5 turns end on x axis");
assertThrows(
() => generatePresetMicrophones({
type: "archimedean",
aperture,
turns: 0,
count,
}),
"0.25",
"Archimedean turn lower bound",
);
});
test("타원 격자는 요청한 rows x columns 수를 유지하며 경계 안에 배치된다", () => {
const aperture = { width: 0.45, height: 0.3 };
const microphones = generatePresetMicrophones({