docs: connect the array builder workflow

This commit is contained in:
2026-08-28 22:21:32 +09:00
parent 0f9b4f8942
commit 3714146df9
9 changed files with 214 additions and 27 deletions
+29 -9
View File
@@ -2,9 +2,9 @@
192채널 MEMS 마이크 배열의 배치와 자유음장·저비용 실내 근사에서 DAS·DAS-PHAT·GCC-PHAT 192채널 MEMS 마이크 배열의 배치와 자유음장·저비용 실내 근사에서 DAS·DAS-PHAT·GCC-PHAT
위치 추정을 비교하는 로컬 브라우저 위치 추정을 비교하는 로컬 브라우저
시뮬레이터입니다. Dataset Builder에서 녹음 파일·합성 신호와 이동 경로를 포함한 재현 가능한 시뮬레이터입니다. Array Builder에서 마이크 배치를 설계하고, Dataset Builder에서 녹음 파일·합성
음향 장면을 만들고 Acoustic Camera에서 CPU 또는 WebGPU로 배열 응답, acoustic map과 위치 검출 신호와 이동 경로를 포함한 재현 가능한 음향 장면을 만든 뒤, Acoustic Camera에서 CPU 또는
지표를 계산하거나 이동 장면을 시간축으로 재생합니다. WebGPU로 배열 응답, acoustic map과 위치 검출 지표를 계산하거나 이동 장면을 시간축으로 재생합니다.
별도 프로그램이나 패키지를 설치하지 않습니다. 모든 앱과 계산은 이 저장소와 브라우저 안에서 별도 프로그램이나 패키지를 설치하지 않습니다. 모든 앱과 계산은 이 저장소와 브라우저 안에서
실행됩니다. 실행됩니다.
@@ -58,8 +58,8 @@ powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File .\scripts\serve.
### 화면 폭 조절 ### 화면 폭 조절
데스크톱에서는 Dataset Builder의 오른쪽 설정 패널과 Acoustic Camera의 왼쪽 설정 패널 경계를 데스크톱에서는 Array Builder와 Acoustic Camera의 왼쪽 설정 패널, Dataset Builder의 오른쪽 설정
드래그해 넓힐 수 있습니다. 경계선에 키보드 초점을 두면 `←`/`→`로 16px씩, 패널 경계를 드래그해 넓힐 수 있습니다. 경계선에 키보드 초점을 두면 `←`/`→`로 16px씩,
`Shift`와 함께 누르면 64px씩 조절됩니다. `Home`/`End`는 최소·최대 `Shift`와 함께 누르면 64px씩 조절됩니다. `Home`/`End`는 최소·최대
폭, `Enter` 또는 더블클릭은 기본 폭으로 되돌립니다. 폭, `Enter` 또는 더블클릭은 기본 폭으로 되돌립니다.
@@ -77,7 +77,25 @@ powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File .\scripts\serve.
## 내 장면으로 전체 흐름 실행하기 ## 내 장면으로 전체 흐름 실행하기
### 1. Dataset Builder에서 Scene 만들기 ### 1. Array Builder에서 마이크 배열 만들기 (선택)
1. 시작 화면에서 **Array Builder**를 엽니다.
2. aperture 너비·높이와 프리셋을 배치할 **생성 경계**(사각형 또는 타원)를 정합니다.
3. **격자**, **선형**, **원형**, **Fermat 나선**, **균일 랜덤** 중 하나로 배치합니다. 랜덤은
seed로 같은 좌표를 재현하며, 최소 간격과 경계 여백도 지정할 수 있습니다.
4. **추가** 도구로 canvas에 마이크를 놓고, 선택한 점을 드래그하거나 x/y/z 입력으로 수정합니다.
목록에서는 삭제와 채널 순서 변경도 할 수 있습니다.
5. 상태가 **Array JSON 저장 가능**이면 **Array JSON 저장**을 누릅니다.
불러온 Array의 `sensitivityDb`, `phaseOffsetDeg`, `frequencyResponse` 보정 정보는 마이크 위치·ID나
채널 순서를 편집해도 보존됩니다. 프리셋 생성은 마이크 목록 전체를 새로 만드는 작업이므로 기존
채널별 보정값도 교체됩니다.
생성 경계 형상, 프리셋 종류, random seed와 최소 간격은 편집을 돕는 설정이며 표준 Array JSON에
저장되지 않습니다. 저장되는 것은 aperture 너비·높이와 최종 마이크 좌표·순서 및 채널별 보정값입니다.
자세한 사용법은 [`apps/array-builder/README.md`](apps/array-builder/README.md)를 참고하세요.
### 2. Dataset Builder에서 Scene 만들기
1. 시작 화면에서 **Dataset Builder**를 엽니다. 1. 시작 화면에서 **Dataset Builder**를 엽니다.
2. 선택적으로 배경 이미지와 오디오를 추가합니다. 2. 선택적으로 배경 이미지와 오디오를 추가합니다.
@@ -90,12 +108,12 @@ powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File .\scripts\serve.
**Preview WAV 저장**은 source와 ambient를 공간 정보 없이 하나의 mono channel로 합친 청취 **Preview WAV 저장**은 source와 ambient를 공간 정보 없이 하나의 mono channel로 합친 청취
확인용 파일입니다. 192채널 마이크 신호나 beamforming 결과가 아닙니다. 확인용 파일입니다. 192채널 마이크 신호나 beamforming 결과가 아닙니다.
### 2. Acoustic Camera에서 실행하기 ### 3. Acoustic Camera에서 실행하기
1. 홈으로 돌아가 **Acoustic Camera**를 엽니다. 1. 홈으로 돌아가 **Acoustic Camera**를 엽니다.
2. 다음 순서로 입력합니다: **Scene → Array → Experiment → Assets**. 2. 다음 순서로 입력합니다: **Scene → Array → Experiment → Assets**.
3. **Scene**에서 Builder가 저장한 `.scene.json`을 선택합니다. 3. **Scene**에서 Builder가 저장한 `.scene.json`을 선택합니다.
4. 기본 배열을 사용하지 않을 때만 **Array**에서 `.array.json`을 선택합니다. 4. 기본 배열을 사용하지 않을 때는 **Array**에서 Array Builder가 저장한 `.array.json`을 선택합니다.
5. 별도 실험 문서를 사용할 때만 **Experiment**에서 `.experiment.json`을 선택합니다. 5. 별도 실험 문서를 사용할 때만 **Experiment**에서 `.experiment.json`을 선택합니다.
6. Scene에 이미지나 audio asset이 있다면 **Assets**에서 Builder에 사용했던 원본 파일을 다시 6. Scene에 이미지나 audio asset이 있다면 **Assets**에서 Builder에 사용했던 원본 파일을 다시
선택합니다. 여러 파일을 한 번에 선택할 수 있습니다. 선택합니다. 여러 파일을 한 번에 선택할 수 있습니다.
@@ -248,6 +266,7 @@ DAMAS와 CLEAN-SC는 구현하지 않습니다.
Camera의 **배열 선택**에서 8개 fixture를 바로 불러와 같은 Scene/Experiment로 연속 실행할 수 있습니다. Camera의 **배열 선택**에서 8개 fixture를 바로 불러와 같은 Scene/Experiment로 연속 실행할 수 있습니다.
저채널·대형 배열의 grating lobe나 compact 배열의 넓은 main lobe는 배치 trade-off를 관찰하기 위한 저채널·대형 배열의 grating lobe나 compact 배열의 넓은 main lobe는 배치 trade-off를 관찰하기 위한
의도된 예제 특성입니다. 의도된 예제 특성입니다.
새 배치를 직접 만들려면 [Array Builder](apps/array-builder/README.md)를 사용하세요. 저장소 fixture의
상세한 생성법은 [`assets/array-geometries/README.md`](assets/array-geometries/README.md)를 참고하세요. 상세한 생성법은 [`assets/array-geometries/README.md`](assets/array-geometries/README.md)를 참고하세요.
## 테스트와 benchmark ## 테스트와 benchmark
@@ -273,6 +292,7 @@ test.cmd
- [`docs/data-contracts.md`](docs/data-contracts.md): Array·Scene·Experiment·Result 계약 - [`docs/data-contracts.md`](docs/data-contracts.md): Array·Scene·Experiment·Result 계약
- [`docs/acoustic-model.md`](docs/acoustic-model.md): 마이크 응답과 자유음장·shoebox 희소 근사 - [`docs/acoustic-model.md`](docs/acoustic-model.md): 마이크 응답과 자유음장·shoebox 희소 근사
- [`docs/realtime-porting.md`](docs/realtime-porting.md): 실측 다채널 위치추정 API 포팅 - [`docs/realtime-porting.md`](docs/realtime-porting.md): 실측 다채널 위치추정 API 포팅
- [`apps/array-builder/README.md`](apps/array-builder/README.md): 배열 생성·직접 편집과 Camera 전달
- [`schemas/README.md`](schemas/README.md): JSON Schema 구성 - [`schemas/README.md`](schemas/README.md): JSON Schema 구성
- [`docs/execution-plan.md`](docs/execution-plan.md): 구현 단계와 기술 결정 - [`docs/execution-plan.md`](docs/execution-plan.md): 구현 단계와 기술 결정
- [`goal.md`](goal.md): 프로젝트 목표 - [`goal.md`](goal.md): 프로젝트 목표
@@ -283,7 +303,7 @@ test.cmd
## 디렉터리 구조 ## 디렉터리 구조
```text ```text
apps/ Dataset Builder와 Acoustic Camera UI apps/ Array Builder, Dataset Builder와 Acoustic Camera UI
assets/ 마이크 배열과 synthetic Scene/Experiment fixture assets/ 마이크 배열과 synthetic Scene/Experiment fixture
benchmarks/ 현재 브라우저에서 실행하는 WebGPU scaling 측정 benchmarks/ 현재 브라우저에서 실행하는 WebGPU scaling 측정
docs/ 사용자 안내, 데이터 계약, 실행 계획과 기준 실측 docs/ 사용자 안내, 데이터 계약, 실행 계획과 기준 실측
+1 -1
View File
@@ -51,7 +51,7 @@
</div> </div>
<div class="array-card"> <div class="array-card">
<div class="array-heading"><div><span class="compact-label">MIC ARRAY</span><h3 id="array-title">배열</h3></div><strong id="microphone-count">— ch</strong></div> <div class="array-heading"><div><span class="compact-label">MIC ARRAY</span><h3 id="array-title">배열</h3><a class="builder-link" href="/apps/array-builder/">Array Builder에서 만들기 →</a></div><strong id="microphone-count">— ch</strong></div>
<label class="field"> <label class="field">
<span>배열 선택</span> <span>배열 선택</span>
<select id="array-preset"> <select id="array-preset">
+107
View File
@@ -0,0 +1,107 @@
# Array Builder
Array Builder는 `micarrsim.array` JSON을 브라우저에서 만드는 마이크 배열 편집기입니다. aperture
크기와 생성 경계를 정한 뒤 프리셋으로 시작하거나 마이크를 직접 추가·이동해 Acoustic Camera의
배열 입력으로 사용할 수 있습니다.
## 열기
저장소 루트에서 `start.cmd`를 실행하고 시작 화면의 **Array Builder**를 선택합니다. 서버가 이미
실행 중이면 <http://127.0.0.1:4173/apps/array-builder/>를 직접 열 수 있습니다. HTML module을
사용하므로 파일을 탐색기에서 직접 열지 마세요.
## 가장 짧은 사용 순서
1. **크기와 형상**에서 aperture 너비·높이와 생성 경계를 정합니다.
2. 프리셋을 고르고 **현재 배열을 프리셋으로 교체**를 누릅니다.
3. canvas와 마이크 목록에서 좌표와 채널 순서를 다듬습니다.
4. 상태가 **Array JSON 저장 가능**인지 확인하고 **Array JSON 저장**을 누릅니다.
5. Acoustic Camera를 열어 **Array** 입력에서 저장한 `.array.json`을 선택합니다.
## 크기와 생성 경계
- **너비·높이:** Array JSON의 `aperture.width`와 `aperture.height`이며 단위는 m입니다.
- **사각형:** 너비와 높이의 직사각형 안에 preset을 배치합니다.
- **타원:** 같은 너비와 높이를 지름으로 하는 타원 안에 preset을 배치합니다.
- **좌표 중앙 정렬:** 현재 마이크의 무게중심을 원점으로 이동합니다.
- **좌표에 크기 맞춤:** 현재 좌표가 들어가도록 aperture를 조절합니다.
- **경계 밖 마이크를 안으로 이동:** 선택한 생성 경계 안으로 좌표를 제한합니다.
aperture 크기를 입력하는 것만으로 기존 좌표가 자동 확대·축소되지는 않습니다. 크기를 줄인 뒤
경계 밖 좌표가 생겼다면 검증 메시지를 보고 크기를 다시 맞추거나 clamp 작업을 사용하세요.
## 프리셋
| 방식 | 설정 | 배치 |
| --- | --- | --- |
| 격자 | 행·열, 경계 여백 | 아래 행부터 위로, 각 행은 왼쪽에서 오른쪽으로 생성 |
| 선형 | 마이크 수, x/y 축 | 선택한 중심축을 따라 일정 간격으로 생성 |
| 원형 | 마이크 수 | 사각 경계는 사각 둘레, 타원 경계는 타원 둘레에 균일 배치 |
| 나선 | 마이크 수 | golden angle을 사용하는 결정론적 Fermat 나선 |
| 균일 랜덤 | 마이크 수, seed, 최소 간격 | 경계 내부에 seed로 재현되는 좌표 생성 |
**경계 여백**은 preset 좌표를 aperture 가장자리에서 안쪽으로 띄웁니다. 랜덤 배열은 요청한 최소
간격을 만족하는 배치를 제한된 횟수 안에 찾지 못하면 현재 배열을 바꾸지 않고 오류를 표시합니다.
같은 크기·형상·수·seed·간격을 사용하면 같은 랜덤 좌표가 생성됩니다.
프리셋 생성은 현재 마이크 목록 전체를 교체합니다. 필요한 기존 좌표나 채널 보정이 있다면 먼저
Array JSON을 저장하세요.
## 직접 편집
- **선택 도구 (`V`):** 마이크 점을 선택하고 드래그해 x/y를 바꿉니다.
- **추가 도구 (`A`):** 빈 canvas를 클릭해 새 마이크를 만듭니다.
- **좌표 필드:** 선택한 마이크의 ID와 x/y/z를 숫자로 정확히 입력합니다.
- **삭제:** 목록이나 canvas에서 선택한 뒤 **선택 마이크 삭제** 또는 `Delete`를 사용합니다.
- **순서:** **채널 앞으로/뒤로**로 `microphones` 배열의 채널 순서를 바꿉니다.
- **미세 이동:** 방향키로 이동하고 `Shift`를 함께 누르면 10배 거리로 이동합니다.
- **스냅:** 격자 스냅 간격은 화면에서 mm 단위로 입력합니다.
- **실행 취소:** `Ctrl+Z`, 다시 실행은 `Ctrl+Shift+Z`입니다.
확대·축소와 화면 맞춤은 보기만 바꾸며 저장 좌표에는 영향을 주지 않습니다.
## 보정값 보존
선택한 채널의 `sensitivityDb`와 `phaseOffsetDeg`는 보정값 펼침 영역에서 편집할 수 있습니다. 기존
Array JSON을 불러왔을 때 마이크에 `frequencyResponse`가 있으면 응답점 수를 표시하며, 위치·ID,
채널 순서나 배열 메타데이터를 편집해도 그 curve를 그대로 보존합니다.
프리셋으로 마이크 목록을 교체하면 새 채널이 만들어지므로 이전 채널의 sensitivity, phase와
frequency response도 함께 제거됩니다.
## Array JSON에 저장되는 것
저장 파일은 계약 버전 `1.0.0`의 `micarrsim.array` 문서입니다.
- Array ID, 이름과 선택적 설명
- m 단위 aperture 너비·높이
- 오른손 좌표계와 순서가 있는 마이크 ID·`[x, y, z]`
- 채널별 선택적 sensitivity, phase와 frequency-response curve
생성 경계의 사각형/타원 선택, 프리셋 종류, 행·열, random seed·최소 간격·경계 여백, snap과
zoom은 편집기 설정입니다. 표준 Array JSON에는 저장되지 않으며 Camera는 최종 aperture와 실제
마이크 좌표·순서를 사용합니다.
## Camera로 전달하기
1. Array Builder에서 **Array JSON 저장**을 누릅니다.
2. 헤더의 **Camera 열기** 또는 홈의 Acoustic Camera를 엽니다.
3. 입력 준비의 **Array**에서 저장한 파일을 선택합니다.
4. 채널 수와 aperture 요약을 확인한 뒤 Scene과 Experiment를 준비해 실행합니다.
Experiment를 별도 파일로 가져올 때는 그 문서의 `arrayId`가 방금 불러온 Array ID와 같아야 합니다.
Camera에서 배열 선택이 **불러온 Array JSON**으로 바뀌고 입력 검증이 통과했는지 확인하세요.
## 검증과 제한
- 마이크는 1개 이상 1,024개 이하입니다.
- Array ID는 영문 소문자나 숫자로 시작하며 소문자·숫자·점·밑줄·하이픈만 허용합니다.
- 마이크 ID는 비어 있을 수 없고 배열 안에서 고유해야 합니다.
- 좌표는 유한한 수여야 합니다. x/y가 현재 생성 경계와 aperture를 벗어나면 저장 가능한 경고로
표시되므로, 의도한 배치인지 확인하거나 clamp 작업을 사용하세요.
- 마이크 두 개가 같은 x/y/z 좌표를 공유할 수 없습니다.
- 저장되지 않은 변경이 있으면 페이지를 닫을 때 브라우저가 확인합니다.
JSON 구조 자체는 [`../../schemas/array.schema.json`](../../schemas/array.schema.json), 공통 계약은
[`../../docs/data-contracts.md`](../../docs/data-contracts.md), 배열 예제는
[`../../assets/array-geometries/README.md`](../../assets/array-geometries/README.md)를 참고하세요.
+1 -1
View File
@@ -1026,7 +1026,7 @@ body[data-array-tool="add"] #array-canvas,
} }
} }
@container array-controls (max-width: 330px) { @container array-controls (max-width: 340px) {
.preset-grid, .preset-grid,
.button-grid, .button-grid,
.calibration-fields { .calibration-fields {
+36 -10
View File
@@ -17,6 +17,7 @@
<nav aria-label="문서 목차"> <nav aria-label="문서 목차">
<a href="#quick-start">빠른 시작</a> <a href="#quick-start">빠른 시작</a>
<a href="#moving-live">이동 라이브</a> <a href="#moving-live">이동 라이브</a>
<a href="#array-builder">배열 만들기</a>
<a href="#full-workflow">내 데이터</a> <a href="#full-workflow">내 데이터</a>
<a href="#acoustic-model">환경·실측</a> <a href="#acoustic-model">환경·실측</a>
<a href="#results">결과 해석</a> <a href="#results">결과 해석</a>
@@ -28,7 +29,7 @@
<section class="guide-hero"> <section class="guide-hero">
<h1>micarrsim 사용 설명서</h1> <h1>micarrsim 사용 설명서</h1>
<p> <p>
<code>start.cmd</code>로 열고, 장면을 실행하고, 결과를 저장하는 순서만 먼저 확인하세요. <code>start.cmd</code>로 열고, 배열과 장면을 준비하고, 결과를 저장하는 순서만 먼저 확인하세요.
입력과 계산은 로컬 브라우저에서 처리됩니다. 입력과 계산은 로컬 브라우저에서 처리됩니다.
</p> </p>
</section> </section>
@@ -70,8 +71,31 @@
</div> </div>
</section> </section>
<section class="guide-section" id="full-workflow"> <section class="guide-section" id="array-builder">
<div class="section-number">03</div> <div class="section-number">03</div>
<div>
<p class="eyebrow">Microphone geometry</p>
<h2>내 마이크 배열 만들기</h2>
<ol class="guide-steps">
<li><a href="/apps/array-builder/">Array Builder</a>에서 aperture 너비·높이와 사각형 또는 타원 생성 경계를 정합니다.</li>
<li>격자·선형·원형·Fermat 나선·균일 랜덤 중 하나를 고릅니다. 랜덤은 seed와 최소 간격으로 재현성과 채널 간 거리를 제어합니다.</li>
<li>canvas의 <strong>추가</strong> 도구로 마이크를 놓고, 점을 드래그하거나 x/y/z 필드로 좌표를 수정합니다.</li>
<li>목록에서 마이크를 삭제하거나 채널 순서를 바꾸고, 상태가 <strong>Array JSON 저장 가능</strong>이면 저장합니다.</li>
<li><a href="/apps/acoustic-camera/">Acoustic Camera</a>의 <strong>Array</strong> 입력에서 저장한 파일을 선택합니다.</li>
</ol>
<aside class="callout">
가져온 채널의 sensitivity, phase와 frequency-response 보정은 위치·순서 편집 중 보존됩니다.
다만 프리셋으로 마이크 목록을 교체하면 기존 채널별 보정도 교체됩니다.
</aside>
<aside class="callout callout--important">
생성 경계 형상, 프리셋 종류, seed와 최소 간격은 편집기 설정이라 표준 Array JSON에
저장되지 않습니다. Camera가 사용하는 것은 저장된 aperture와 최종 마이크 좌표·순서입니다.
</aside>
</div>
</section>
<section class="guide-section" id="full-workflow">
<div class="section-number">04</div>
<div> <div>
<p class="eyebrow">5분 전체 흐름</p> <p class="eyebrow">5분 전체 흐름</p>
<h2>내 장면을 만들고 Camera에서 실행하기</h2> <h2>내 장면을 만들고 Camera에서 실행하기</h2>
@@ -85,7 +109,8 @@
<li><strong>Scene 저장 후 Camera 열기</strong>를 누릅니다.</li> <li><strong>Scene 저장 후 Camera 열기</strong>를 누릅니다.</li>
<li> <li>
Camera에서 파일을 <strong>Scene → Array → Experiment → Assets</strong> 순서로 Camera에서 파일을 <strong>Scene → Array → Experiment → Assets</strong> 순서로
가져옵니다. 내 Array/Experiment가 없다면 내장 기본값을 그대로 둡니다. 가져옵니다. Array Builder에서 저장한 파일이 있다면 Array 단계에서 선택하고,
내 Array/Experiment가 없다면 내장 기본값을 그대로 둡니다.
</li> </li>
<li> <li>
Assets에서 Builder에 사용한 원본 이미지와 오디오를 한 번에 다시 선택합니다. Assets에서 Builder에 사용한 원본 이미지와 오디오를 한 번에 다시 선택합니다.
@@ -96,7 +121,7 @@
<aside class="callout"> <aside class="callout">
데스크톱에서는 설정과 작업 영역 사이의 선을 드래그하거나 방향키로 패널 폭을 조절할 데스크톱에서는 설정과 작업 영역 사이의 선을 드래그하거나 방향키로 패널 폭을 조절할
수 있습니다. <kbd>Enter</kbd> 또는 더블클릭하면 기본 폭으로 돌아갑니다. 폭은 Builder와 수 있습니다. <kbd>Enter</kbd> 또는 더블클릭하면 기본 폭으로 돌아갑니다. 폭은 각 Builder와
Camera별로 이 브라우저에 저장되며, 좁은 화면에서는 두 영역이 자동으로 위아래로 Camera별로 이 브라우저에 저장되며, 좁은 화면에서는 두 영역이 자동으로 위아래로
배치됩니다. 배치됩니다.
</aside> </aside>
@@ -117,7 +142,7 @@
</section> </section>
<section class="guide-section"> <section class="guide-section">
<div class="section-number">04</div> <div class="section-number">05</div>
<div> <div>
<p class="eyebrow">Localization &amp; compute</p> <p class="eyebrow">Localization &amp; compute</p>
<h2>알고리즘, 속도와 격자 선택</h2> <h2>알고리즘, 속도와 격자 선택</h2>
@@ -166,7 +191,7 @@
</section> </section>
<section class="guide-section" id="acoustic-model"> <section class="guide-section" id="acoustic-model">
<div class="section-number">05</div> <div class="section-number">06</div>
<div> <div>
<p class="eyebrow">Acoustics & measured input</p> <p class="eyebrow">Acoustics & measured input</p>
<h2>환경과 마이크 응답 선택</h2> <h2>환경과 마이크 응답 선택</h2>
@@ -188,7 +213,7 @@
</section> </section>
<section class="guide-section" id="results"> <section class="guide-section" id="results">
<div class="section-number">06</div> <div class="section-number">07</div>
<div> <div>
<p class="eyebrow">Result JSON</p> <p class="eyebrow">Result JSON</p>
<h2>무엇이 저장되나요?</h2> <h2>무엇이 저장되나요?</h2>
@@ -228,7 +253,7 @@
</section> </section>
<section class="guide-section" id="troubleshooting"> <section class="guide-section" id="troubleshooting">
<div class="section-number">07</div> <div class="section-number">08</div>
<div> <div>
<p class="eyebrow">Troubleshooting</p> <p class="eyebrow">Troubleshooting</p>
<h2>막혔을 때 확인할 것</h2> <h2>막혔을 때 확인할 것</h2>
@@ -268,7 +293,7 @@
</section> </section>
<section class="guide-section"> <section class="guide-section">
<div class="section-number">08</div> <div class="section-number">09</div>
<div> <div>
<p class="eyebrow">Scope & privacy</p> <p class="eyebrow">Scope & privacy</p>
<h2>현재 모델의 범위</h2> <h2>현재 모델의 범위</h2>
@@ -289,7 +314,8 @@
<section class="reference-grid" aria-label="상세 문서"> <section class="reference-grid" aria-label="상세 문서">
<a href="/README.md"><strong>전체 README</strong><span>설치·실행·구조·제약</span></a> <a href="/README.md"><strong>전체 README</strong><span>설치·실행·구조·제약</span></a>
<a href="/apps/dataset-builder/README.md"><strong>Builder 설명서</strong><span>source·ambient·Preview WAV</span></a> <a href="/apps/array-builder/README.md"><strong>Array Builder 설명서</strong><span>마이크 배치·프리셋·JSON</span></a>
<a href="/apps/dataset-builder/README.md"><strong>Dataset Builder 설명서</strong><span>source·ambient·Preview WAV</span></a>
<a href="/apps/acoustic-camera/README.md"><strong>Camera 설명서</strong><span>backend·결과·비교</span></a> <a href="/apps/acoustic-camera/README.md"><strong>Camera 설명서</strong><span>backend·결과·비교</span></a>
<a href="/docs/acoustic-model.md"><strong>음향 모델</strong><span>마이크 응답·반사·산란·회절</span></a> <a href="/docs/acoustic-model.md"><strong>음향 모델</strong><span>마이크 응답·반사·산란·회절</span></a>
<a href="/docs/realtime-porting.md"><strong>실측 포팅</strong><span>block·streaming 위치추정 API</span></a> <a href="/docs/realtime-porting.md"><strong>실측 포팅</strong><span>block·streaming 위치추정 API</span></a>
+15 -3
View File
@@ -24,7 +24,7 @@
<section class="landing-intro" aria-labelledby="landing-title"> <section class="landing-intro" aria-labelledby="landing-title">
<p class="eyebrow">Microphone array workbench</p> <p class="eyebrow">Microphone array workbench</p>
<h1 id="landing-title">무엇을 하시겠어요?</h1> <h1 id="landing-title">무엇을 하시겠어요?</h1>
<p>배열 위치추정을 바로 실행하거나, 평가에 사용할 음원 장면을 먼저 만드세요.</p> <p>배열을 설계하거나 음원 장면을 만든 뒤, 위치추정 결과를 바로 비교하세요.</p>
</section> </section>
<section class="launcher-grid" aria-label="작업 선택"> <section class="launcher-grid" aria-label="작업 선택">
@@ -47,19 +47,31 @@
</div> </div>
<span class="launcher-card__action">새 Scene 만들기 →</span> <span class="launcher-card__action">새 Scene 만들기 →</span>
</a> </a>
<a class="launcher-card" href="/apps/array-builder/">
<span class="launcher-card__number" aria-hidden="true">03</span>
<div>
<p class="launcher-card__kicker">마이크 배치 설계</p>
<h2>Array Builder</h2>
<p>크기와 생성 경계를 정하고, 프리셋 또는 직접 편집으로 마이크 배열을 만듭니다.</p>
</div>
<span class="launcher-card__action">새 Array 만들기 →</span>
</a>
</section> </section>
<details class="workflow-disclosure"> <details class="workflow-disclosure">
<summary>내 파일로 분석하는 순서</summary> <summary>내 파일로 분석하는 순서</summary>
<ol class="workflow-list"> <ol class="workflow-list">
<li><strong>Array 제작 (선택)</strong><span>크기와 마이크 좌표를 설계해 JSON 저장</span></li>
<li><strong>Scene 제작</strong><span>Builder에서 음원과 위치 지정</span></li> <li><strong>Scene 제작</strong><span>Builder에서 음원과 위치 지정</span></li>
<li><strong>파일 보관</strong><span>Scene JSON과 원본 파일을 함께 보관</span></li> <li><strong>파일 보관</strong><span>Scene JSON과 원본 파일을 함께 보관</span></li>
<li><strong>Camera 입력</strong><span>Scene, 배열, 원본 파일 선택</span></li> <li><strong>Camera 입력</strong><span>Scene, 배열, 원본 파일 선택</span></li>
<li><strong>결과 저장</strong><span>위치 map과 지표를 Result JSON으로 저장</span></li> <li><strong>결과 저장</strong><span>위치 map과 지표를 Result JSON으로 저장</span></li>
</ol> </ol>
<p class="workflow-note"> <p class="workflow-note">
Scene JSON에는 원본 파일이 포함되지 않습니다. 다시 열 때 같은 파일을 선택하면 SHA-256으로 Array Builder가 저장한 JSON은 Camera의 Array 입력에서 바로 선택할 수 있습니다. Scene JSON에는
확인해 연결합니다. <a href="/docs/#asset-reconnect">자세히 보기</a> 원본 파일이 포함되지 않으므로 다시 열 때 같은 파일을 SHA-256으로 확인해 연결합니다.
<a href="/docs/#asset-reconnect">자세히 보기</a>
</p> </p>
</details> </details>
</main> </main>
+2
View File
@@ -514,6 +514,7 @@ body.is-resizing-pane * {
} }
.landing-shell .launcher-grid { .landing-shell .launcher-grid {
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px; gap: 16px;
margin-top: 28px; margin-top: 28px;
} }
@@ -582,6 +583,7 @@ body.is-resizing-pane * {
} }
.workflow-disclosure .workflow-list { .workflow-disclosure .workflow-list {
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
margin: 16px 0 12px; margin: 16px 0 12px;
} }
+6 -3
View File
@@ -36,7 +36,7 @@ async function digestHex(blob) {
return [...new Uint8Array(digest)].map((value) => value.toString(16).padStart(2, "0")).join(""); return [...new Uint8Array(digest)].map((value) => value.toString(16).padStart(2, "0")).join("");
} }
test("시작 화면과 Dataset Builder가 작업 우선순위 및 390px 레이아웃을 유지한다", async () => { test("시작 화면이 세 앱 진입점과 390px 레이아웃을 유지한다", async () => {
const landingFrame = document.createElement("iframe"); const landingFrame = document.createElement("iframe");
landingFrame.src = "/"; landingFrame.src = "/";
landingFrame.style.cssText = "position:fixed;left:-20000px;top:0;width:390px;height:900px;border:0"; landingFrame.style.cssText = "position:fixed;left:-20000px;top:0;width:390px;height:900px;border:0";
@@ -49,9 +49,12 @@ test("시작 화면과 Dataset Builder가 작업 우선순위 및 390px 레이
try { try {
const landingDocument = landingFrame.contentDocument; const landingDocument = landingFrame.contentDocument;
const launchers = [...landingDocument.querySelectorAll(".launcher-card")]; const launchers = [...landingDocument.querySelectorAll(".launcher-card")];
assertEqual(launchers.length, 2, "landing exposes only two primary tasks"); assertEqual(launchers.length, 3, "landing exposes three primary tasks");
assert(launchers[0].textContent.includes("Acoustic Camera"), "analysis is the first landing task"); assert(launchers[0].textContent.includes("Acoustic Camera"), "analysis is the first landing task");
assert(launchers[1].textContent.includes("Dataset Builder"), "scene authoring is the second landing task"); const launcherPaths = new Set(launchers.map((launcher) => launcher.getAttribute("href")));
assert(launcherPaths.has("/apps/dataset-builder/"), "landing links to Dataset Builder");
assert(launcherPaths.has("/apps/array-builder/"), "landing links to Array Builder");
assert(launcherPaths.has("/apps/acoustic-camera/"), "landing links to Acoustic Camera");
assert(!landingDocument.querySelector(".workflow-disclosure").open, "file workflow starts collapsed"); assert(!landingDocument.querySelector(".workflow-disclosure").open, "file workflow starts collapsed");
assert(!landingDocument.querySelector('a[href="/docs/execution-plan.md"]'), "developer plan is absent from landing"); assert(!landingDocument.querySelector('a[href="/docs/execution-plan.md"]'), "developer plan is absent from landing");
assert( assert(
+17
View File
@@ -35,6 +35,23 @@ const LAYOUT_CASES = [
homePanelWidth: 320, homePanelWidth: 320,
homeGridTracks: 1, homeGridTracks: 1,
}, },
{
label: "Array Builder",
path: "/apps/array-builder/",
panelSide: "start",
storageKey: "micarrsim.array-builder.control-panel-width",
defaultPanelWidth: 420,
layoutId: "array-builder-layout",
panelId: "array-builder-controls",
workspaceId: "array-builder-workspace",
handleId: "array-builder-split-handle",
canvasSelector: ".array-canvas-card",
representativeSelectors: ["#array-name", "#generate-preset-button"],
adaptiveGridSelector: ".preset-grid",
defaultGridTracks: 2,
homePanelWidth: 360,
homeGridTracks: 1,
},
]; ];
let mountSequence = 0; let mountSequence = 0;