캡처 앱·재구성 도구
31b2dc9. 메타데이터는 2026-10-23 GitHub 기준이며, 구현 설명은 커밋 31b2dc9 기준입니다.3d-model-anything은 LiDAR가 달린 iPhone·iPad로 실내 공간을 기록하고, 측정값과 사진을 근거로 수정 가능한 Three.js 방을 재구성하는 프로젝트입니다. 스캔은 정답 모델을 만드는 버튼이 아니라, 모델을 만들고 검증할 때 계속 되돌아볼 증거입니다.
저장소는 네 부품을 함께 제공합니다. Swift로 작성한 캡처 앱, 브라우저 뷰어, 에이전트 작업 지침인 scan-to-threejs 스킬, 그리고 사무실 재구성 예제입니다. 모델의 벽·책상·의자·조명은 TypeScript 함수와 수치로 표현됩니다. 따라서 결과를 코드 리뷰하고, 특정 가구의 치수나 재질만 바꾸는 학습이 가능합니다.
현장 조사원은 줄자로 잰 치수와 여러 방향의 사진을 모읍니다. 모형 제작자는 그 자료를 보고 벽과 가구를 다시 만들고, 검수대에서는 원래 사진과 같은 위치에서 모형을 바라봅니다.
조사 자료에 의자가 직육면체로 기록됐다고 해서 실제 의자도 상자는 아닙니다. 상자는 대략적인 자리와 크기를 알려주며, 다리·등받이·재질은 사진을 보고 보완해야 합니다.
| 입력 증거 | 담고 있는 정보 | 재구성에서 쓰는 자리 |
|---|---|---|
| RoomPlan JSON | 벽·문·창문·가구 범주와 치수·위치 | 방 외곽과 가구 배치의 기준 |
| LiDAR PLY | 삼각형 표면, 법선, 분류, 사진 커버리지 | 천장 단차와 실제 표면의 형태 확인 |
| 위치가 붙은 사진 | JPEG, 카메라 자세, 내부 파라미터 | 재질·윤곽 모델링과 같은 시점 비교 |
| 깊이 프레임 | 거리·신뢰도·동시점 저해상도 RGB | 가려짐 검사, 선택적 표면 재구성 |
| Three.js 코드 | 기하·재질·조명·카메라 설정 | 증거를 해석해서 만든 편집 가능한 결과 |
demos/index.ts의 ROOMS에는 office-v4 하나가 등록돼 있습니다. 그 구현은 office-latest를, 다시 그 구현은 office-v2를 사용합니다. 폴더 세 개는 서로 무관한 공개 방 세 개가 아니라, 현재 사무실을 구성하는 이전 모델링 계층입니다.
demos/office-v4/index.ts는 room.photos = []와 room.referenceAvailable = false를 설정합니다. 공개 예제에서 Scan·Overlay를 누를 수 없는 이유는 이 설정과 원본 자료의 비공개 처리입니다. 변환기가 존재한다는 사실과, 데모에 변환용 원본이 들어 있다는 주장은 구별해야 합니다.
Swift가 저장한 행렬을 Python이 해석하고, TypeScript가 같은 단위로 렌더링해야 합니다. 이 연결을 이해하면 센서 앱·이미지 처리·웹 시각화를 하나의 시스템으로 읽는 연습이 됩니다.
이 글의 선정 근거는 TrendShift Daily #13입니다. 2026-10-23 GitHub 기준으로 2026-10-21 생성, 10-22 최근 push, 10-23 조회 시 스타 629·포크 106입니다. 생성일과 조회일 사이에 관심을 모았다는 해석은 가능하지만, 이 수치만으로 스타 증가 추이나 유입 경로를 알 수는 없습니다.
커밋 31b2dc9의 메시지는 Link the project video from README using thumb-1입니다. README에도 프로젝트 소개 영상과 공개 뷰어가 연결돼 있습니다. 시각적인 결과를 바로 확인할 수 있고, 그 결과를 만드는 코드까지 열어 볼 수 있다는 점이 진입 장벽을 낮춥니다. 이것은 저장소 구성에서 도출한 해석입니다.
스킬의 지침은 측정 → 모델링 → 비교입니다. 특히 .agents/skills/scan-to-threejs/scripts/cmp_photo.sh는 원본 사진과 같은 카메라의 렌더를 나란히 붙입니다. 비교 이미지를 만드는 도구이지, 자동으로 유사도를 판정하거나 정확성을 보증하는 도구는 아닙니다. 어느 벽이 길고 어느 가구가 큰지 사람이 판단할 수 있는 검수 재료를 만드는 것입니다.
이 저장소의 교육적 장점은 멋진 결과 화면보다, 결과가 틀렸는지 확인하는 경로까지 코드로 제공한다는 데 있습니다. 생성형 도구를 쓸 때도 입력 증거·중간 표현·결과 검수를 남겨야 한다는 원칙을 구체적으로 배울 수 있습니다.
| 비교 대상 | 주로 제공하는 것 | 이 레포의 상대적 장점과 비용 |
|---|---|---|
| Apple RoomPlan | 앱에 넣는 실내 측정 API와 파라메트릭 출력 | 캡처·사진 관리·웹 모델링·비교까지 이어지는 예제. 대신 앱 개발과 모델링 작업이 추가됨 |
| Polycam | 공식 안내 기준, 공간 메시·평면도 등 캡처 모드 | 센서 처리와 재구성 코드를 직접 추적·수정할 수 있음. 완성된 제품의 사용 편의성을 같은 수준으로 보장하지 않음 |
| 범용 3D 모델 파일 | 뷰어가 읽어 표시하는 기하·재질 데이터 | 방 요소를 함수와 매개변수로 고치고 변경 내역을 검토하기 좋음. 모델링 규칙을 개발자가 관리해야 함 |
| 3d-model-anything | 캡처 앱 + 스킬 + 뷰어 + 상세 사무실 코드 | 측정 근거와 코드 편집을 연결하는 학습에 적합. 한 번의 입력으로 완성되는 서비스는 아님 |
RoomPlan의 역할은 Apple 공식 개요, Polycam의 모드 구분은 Polycam 공식 안내 기준입니다. 속도·정밀도·가격의 우열을 가리는 비교는 아닙니다.
완성된 가구를 사는 것과 가구 제작 도면·공구·검수법을 함께 받는 것의 차이에 가깝습니다. 당장 편하게 쓰는 목적과, 작동 원리를 배우고 내 방식으로 고치는 목적은 선택 기준이 다릅니다.
docs/release-status.md는 공개 저장소를 새 이력으로 시작했고 사무실만 배포한다고 밝힙니다. 소스에는 공개 생성일보다 앞선 스캔 날짜가 등장합니다. 따라서 공개 후 경과일이나 새 공개 이력의 커밋 수만으로 전체 개발 기간을 판단할 수는 없습니다.
또한 2026-10-23 GitHub 기준 주 언어는 Swift이지만, 아래의 물리적 줄 수 집계에서는 상세 가구 모델링을 포함한 TypeScript가 더 많습니다. GitHub의 언어 판정과 학습용 줄 수 집계는 측정 방법이 같지 않습니다. 이 글은 두 값을 각각의 출처와 함께 표시합니다.
루트 package.json에는 React·Vue가 없습니다. 화면은 DOM 이벤트와 Three.js로 구성하며, 뷰어 진입점은 apps/room-viewer/src/main.ts입니다. 패키지 자체는 private: true, 버전 0.0.0, ES 모듈 방식인 type: module로 설정돼 있습니다.
| 의존성 | package.json 범위 | package-lock.json 확정값 | 역할 |
|---|---|---|---|
| three | ^0.186.0 | 0.186.0 | 장면·기하·조명·렌더러 |
| @dimforge/rapier3d-compat | ^0.20.0 | 0.20.0 | 선택적 물리 상호작용 |
| typescript | ^7.0.2 | 7.0.2 | 정적 타입 검사 |
| vite | ^8.3.0 | 8.3.5 | 개발 서버·프로덕션 빌드 |
| @types/three | ^0.186.0 | 0.186.0 | Three.js 타입 선언 |
| @gltf-transform/core | ^4.5.0 | 4.5.0 | glTF 측정 도구 |
| gl-matrix / pngjs | ^3.4.4 / ^7.0.0 | 3.4.4 / 7.0.0 | 행렬 계산·이미지 도구 의존성 |
package.json은 허용할 버전의 범위를, package-lock.json은 이 스냅샷에서 선택된 버전을 적습니다. 예를 들어 Vite는 요구 범위가 ^8.3.0이지만 실제 잠금값은 8.3.5입니다. 재현할 환경을 설명할 때 둘을 혼동하면 안 됩니다.| 영역 | 소스에서 확인한 구성 | 버전의 근거와 한계 |
|---|---|---|
| iOS 앱 | SwiftUI, ARKit, RoomPlan, SceneKit, CoreImage, simd | apps/house-capture/project.yml: iOS 17.0, Swift 언어 모드 5.0 |
| 앱 프로젝트 생성 | XcodeGen의 apps/house-capture/project.yml | xcodeVersion: 27.0. 실제 사용한 컴파일러 버전의 증명은 아님 |
| 변환·집 가져오기 | Python 표준 라이브러리, JSON, 행렬 계산 | 루트에 Python 의존성을 고정하는 파일은 없음. CI는 Python 3.12 |
| 사진 처리·비교 | Pillow, zsh, agent-browser CLI | 버전 고정 없음. 비교 실행 스크립트는 #!/bin/zsh로 시작하며, 공통 설정은 별도 파일에서 불러옴 |
| 선택적 깊이 재구성 | NumPy, Open3D, Pillow | 소스 import와 README 기준. 설치 버전 잠금 없음 |
SWIFT_VERSION: 5.0은 Swift 언어 호환 모드 설정입니다. “Swift 5.0 컴파일러만 설치해야 한다”는 뜻으로 읽지 않습니다. Apple 프레임워크들은 운영체제 SDK가 제공하며, 별도의 Swift 패키지 의존성 명세는 없습니다.
이 스냅샷에는 서비스용 API 서버·데이터베이스·로그인 계층이 없습니다. 캡처 앱이 로컬 폴더를 만들고, 사용자가 내보낸 파일을 Python 도구가 읽으며, 정적 뷰어가 결과를 표시합니다. 개발 중 Vite 서버가 파일을 제공하지만 캡처를 처리하는 애플리케이션 백엔드와는 역할이 다릅니다.
.agents/skills/scan-to-threejs/scripts/fetch_textures.py는 외부 재질 사이트를 조회하는 선택적 도구입니다. 앱의 로컬 캡처와 스킬 실행 전체의 네트워크 사용을 같은 것으로 취급하면 안 됩니다. AI 에이전트의 실행 환경과 모델 서비스도 이 저장소가 자동으로 제공하는 서버가 아닙니다.
# package.json에 정의된 명령
"dev": "vite --config apps/room-viewer/vite.config.ts"
"build": "tsc && vite build --config apps/room-viewer/vite.config.ts"
"preview": "vite preview --config apps/room-viewer/vite.config.ts"
tsconfig.json은 target: ES2022, moduleResolution: bundler, strict: true, noEmit: true입니다. 즉 TypeScript 단계는 타입을 검사하고, 실제 배포 번들은 Vite가 만듭니다. “타입 검사 통과”는 사진과 모델이 일치한다는 시각 검증을 대신하지 않습니다.
apps/room-viewer/vite.config.ts는 개발 서버를 127.0.0.1:5188, strictPort: true로 고정합니다. publicDir은 data/web-assets, 출력은 apps/room-viewer/dist이고, 일반 뷰어·놀이 화면·비교 캡처 화면을 각각 빌드 입력으로 둡니다.
.github/workflows/check.yml은 Ubuntu에서 공개 자산 검사, 의존성 설치, 웹 빌드, Python 가져오기 테스트를 순서대로 선언합니다. .github/workflows/pages.yml은 VIEWER_BASE=/3d-model-anything/을 주고 GitHub Pages에 배포합니다. Dockerfile·Cargo.toml·go.mod·pyproject.toml은 없습니다.
이 레포를 읽을 때는 서버 요청의 흐름보다, 어떤 파일을 누가 쓰고 어떤 좌표계로 누가 읽는지를 먼저 추적하면 좋습니다.
깊이 기반 .agents/skills/scan-to-threejs/scripts/reconstruct_bundle.py는 이 흐름에서 선택적으로 참조 메시를 만드는 도구입니다. 그 결과와 에이전트가 작성하는 편집 가능한 방 모델은 별개입니다. 또한 .agents/skills/scan-to-threejs/scripts/house_to_rooms.py의 집 배치 정보 출력이 곧 웹 뷰어의 집 전체 보기 기능을 뜻하지는 않습니다.
apps/house-capture/HouseCapture/CaptureController.swift의 Phase는 작업 단계를 열거형으로 표현합니다. “스캔 중인지”와 “저장 중인지”를 여러 불리언 조합으로 추측하는 대신 현재 단계를 명시합니다. 대표 흐름은 다음과 같습니다.
betweenRooms -> scanning -> finishingRoom -> betweenRooms
|
세션 종료 요청
v
finishing -> done
// 재개할 때는 relocalizing에서 시작할 수 있음
// 일부 세션 저장 실패는 failed(String)으로 표현
finish(with:error:)는 사진 쓰기가 끝나기를 기다린 뒤 원시 CapturedRoomData를 먼저 저장하고, RoomBuilder로 레이아웃 생성을 시도합니다. 생성에 실패하면 record.file = ""로 표시하면서 메시·사진 저장을 계속 진행합니다. 좋은 예외 처리란 성공으로 위장하는 것이 아니라, 남은 증거와 실패한 결과를 구분하는 것입니다.
drainPhotos(): 수락한 JPEG 쓰기를 기다리고, 늦은 고해상도 콜백의 요청 ID를 무효화합니다.engine.exportPLY로 메시를 확보합니다.HouseProject.modify, writeManifest()를 통해 방 기록을 남깁니다.saveWorldMap(final: false)로 이후 재개에 쓸 지도를 갱신하고 다음 방으로 넘어갑니다.ScanBundle.writeManifest(_:)는 Data.write(..., options: .atomic)를 사용합니다. 이는 해당 파일 교체의 안전장치이지, 여러 JSON·메시·사진을 한꺼번에 묶는 데이터베이스 트랜잭션은 아닙니다. apps/house-capture/README.md의 “진행 중인 방 정도만 잃는다”는 설명은 저장이 성공하는 정상 경로의 의도이며, 코드에는 저장 실패 알림과 재시도 경로도 있습니다.
직접 읽을 곳: CaptureController.swift와 ScanBundle.swift. 특히 Manifest.currentFormat은 house-capture/5입니다. 루트 README 기준, 앱 표시 이름이 바뀌어도 이 형식 식별자는 유지합니다.
각 캡처 세션의 AR 원점은 달라질 수 있습니다. 같은 이름의 방을 다른 날 스캔했다고 같은 좌표가 되는 것은 아닙니다. .agents/skills/scan-to-threejs/scripts/bundle_to_room.py의 main()은 바닥 높이를 찾고, 벽 방향을 정렬하고, 방을 가운데로 옮기는 worldToRoom 행렬을 출력합니다.
# 실제 구현의 핵심: 점은 w=1, 방향은 w=0
W2R = mul(T, R)
to_room = lambda p: apply(W2R, p)
dir_room = lambda d: apply(W2R, d, 0.0)
# .agents/skills/scan-to-threejs/scripts/house_to_rooms.py에서 합성하는 관계
sessionToHouse = zone.toHouse × session.alignment.toZone
roomToHouse = sessionToHouse × inverse(worldToRoom)
pointHouse = roomToHouse × pointRoom
T는 이동, R은 회전입니다. 위치에는 이동을 적용하지만 카메라의 앞 방향·벽의 법선에는 이동을 적용하지 않습니다. 방향 벡터에 이동까지 더하면 카메라가 잘못된 곳을 보게 됩니다. 행렬을 저장할 때는 16개 수를 열 단위로 나열하는 column-major 규칙도 맞춰야 합니다.
벽 방향은 단순 평균하지 않습니다. atan2(x[2], x[0]) * 4로 직교하는 벽의 90° 대칭을 접고, 벽 길이로 가중한 뒤 각도를 4로 나눕니다. 긴 벽들이 가리키는 주된 방향을 기준으로 축을 잡는 방식입니다. 바닥 다각형이 있으면 내부 판정으로 법선 방향도 보정합니다.
.agents/skills/scan-to-threejs/scripts/house_to_rooms.py의 export_house()는 각 방의 active 참조를 선택합니다. zone.aligned가 거짓이거나 세션 정렬 방식이 허용 목록 밖이면 blocked로 기록합니다. rigid()는 유한한 16개 값, 회전의 직교성, 행렬식 등을 검사해 스케일·전단·반사를 배제합니다.
여러 사람이 각자 다른 곳에 줄자의 0점을 놓고 방을 쟀다고 생각해 보세요. 숫자를 그대로 합치면 방들이 겹칩니다. “이 줄자의 원점은 집 현관에서 어느 방향으로 얼마나 떨어져 있는가”까지 기록해야 측정값을 함께 쓸 수 있습니다.
test_active_scan_and_noncommuting_transform은 회전과 이동의 순서를 검증합니다. 테스트의 세션 점 (4, 2.5, 2)는 구역 좌표 (6, 2.5, 5)를 거쳐 집 좌표 (15, 3.5, 14)가 돼야 합니다. “행렬이 16개 수로 돼 있다”보다 “알려진 점이 예상 위치로 옮겨진다”가 더 유용한 검증입니다.
직접 읽을 곳: bundle_to_room.py, house_to_rooms.py, .agents/skills/scan-to-threejs/scripts/test_house_import.py.
apps/house-capture/HouseCapture/CoverageEngine.swift의 CoverageEngine은 표면이 사진에 얼마나 잘 담겼는지 계산합니다. 카메라가 움직일 때마다 모든 갱신을 큐에 쌓으면, 사용자는 이미 지나간 장면의 계산을 기다리게 됩니다. 그래서 같은 앵커의 대기 데이터는 최신 스냅샷 하나로 대체합니다.
// CoverageMailbox.offer(_:_:), 일부 발췌
if values[key] == nil { order.append(key) }
else { replacements += 1 }
values[key] = value
// CoverageEngine.drain(), 일부 발췌
process(pending.take(limit: 2))
if pending.finish() { queue.async { self.drain() } }
CoverageMailbox<Key: Hashable, Value>는 잠금으로 사전·순서 목록·스케줄 상태를 보호합니다. 새 키는 순서 뒤에 붙이고, 기존 키는 값만 바꿉니다. 소비자는 한 번에 앵커 두 개만 처리한 뒤 큐에 차례를 넘겨 사진 평가나 진단 작업이 오래 밀리는 일을 줄입니다.
update(snapshot:)는 최초 소비를 0.15초 뒤에 예약합니다. exportPLY(to:completion:)는 내보내기 전에 대기 중인 데이터를 반영하므로, 화면 갱신이 합쳐져도 저장할 최종 기하를 놓치지 않도록 설계돼 있습니다. pendingMeshUpdates와 coalescedMeshUpdates는 이 동작을 관측하는 수치입니다.
NSLock을 사용합니다. @unchecked Sendable은 컴파일러가 안전성을 전부 증명했다는 뜻이 아니라, 개발자가 공유 상태의 동기화를 책임지겠다는 선언입니다.직접 읽을 곳: CoverageEngine.swift의 update(snapshot:)·drain()·CoverageMailbox.offer·exportPLY. 이 아이디어는 센서 화면뿐 아니라 검색 제안·진행률·실시간 차트에서도 응용할 수 있습니다.
packages/room-runtime/types.ts는 방이 뷰어에 무엇을 제공해야 하는지 정합니다. RoomModule.build()가 BuiltRoom을 반환하면, 뷰어는 특정 책상이나 의자의 만드는 법을 몰라도 그 방을 표시할 수 있습니다.
// 실제 타입에서 핵심만 발췌
export interface RoomModule {
id: string;
label: string;
build(): Promise<BuiltRoom>;
}
// BuiltRoom에는 다음과 같은 항목이 포함됨
group: THREE.Group;
lights: THREE.Group;
walkable(x: number, z: number): boolean;
loadReference(onProgress?: (p: number) => void): Promise<THREE.Object3D>;
officeV4.build()는 이전 계층의 방을 만든 뒤 책상·실내·건축 요소를 수정합니다. demos/office-v2/index.ts의 buildDetailedOffice()는 import.meta.glob으로 부품 모듈을 읽고, 파일별 build(ctx)를 호출합니다. 한 부품 로드가 실패하면 로그를 남기고 다른 부품은 계속 만드는 경로도 있습니다.
packages/room-runtime/batch.ts의 batchStatic()은 같은 재질을 사용하는 정적 메시를 합칩니다. 그러나 재질만 같다고 모두 합치지는 않습니다. 벽을 숨길 때 함께 사라져야 하는 가구, 천장, 그림자 설정, 물리 분류 등도 그룹 경계로 사용합니다. 투명 재질과 일부 특수 메시는 제외합니다.
일반 뷰어는 room.optimize?.({ static: true })로 움직이지 않는 물체까지 합칠 수 있습니다. 물리 화면에서는 물체별 움직임이 필요하므로 같은 최적화 선택을 그대로 적용하면 안 됩니다. “덜 그리기”가 물체의 기능을 망가뜨리지 않는지 함께 살펴야 합니다.
좋은 최적화는 계산량만 줄이는 것이 아니라, 이후에도 따로 움직이거나 숨겨야 할 경계를 보존합니다.
직접 읽을 곳: types.ts, batch.ts, apps/room-viewer/src/main.ts의 loadRoom()과 renderFrame().
packages/room-runtime/types.ts: 완성된 방의 계약부터 파악합니다.demos/index.ts: 뷰어가 어떤 방을 선택하는지 확인합니다.demos/office-v4/index.ts: 기존 모델을 조립하고 바꾸는 흐름을 봅니다..agents/skills/scan-to-threejs/scripts/bundle_to_room.py: 측정 데이터를 모델링하기 좋은 좌표로 바꾸는 과정을 봅니다. 여기서 .agents/skills/scan-to-threejs/scripts/는 변환·비교 도구 폴더입니다.apps/house-capture/HouseCapture/ScanBundle.swift: 변환기의 입력이 어디서 생기는지 거슬러 올라갑니다.가구 파일부터 무작정 전부 읽으면 기하 생성 함수가 너무 많이 나옵니다. 먼저 입출력 타입을 보고, 원하는 물체 한 개를 선택한 뒤 그 물체가 쓰는 재질·좌표·헬퍼만 추적하는 편이 이해하기 쉽습니다.
| 언어 / 위치 | 파일 수 | 물리적 줄 수 | 주로 읽는 내용 |
|---|---|---|---|
| TypeScript 전체 | 75 | 21,509 | 뷰어·런타임·상세 사무실 |
| Swift | 29 | 11,779 | 캡처·저장·복구·미리보기 |
| Python | 14 | 1,376 | 변환·이미지 처리·검사 |
| JavaScript (.mjs) | 2 | 465 | 측정 보조·예제 생성 도구 |
| CSS / HTML | 2 / 7 | 189 / 171 | 뷰어 스타일·페이지·도움말 |
| 셸 / YAML | 4 / 3 | 97 / 96 | 비교 캡처·CI·앱 프로젝트 설정 |
집계 대상은 커밋 31b2dc9의 파일이며 .git은 제외했습니다. 숨김 폴더의 스킬 스크립트, 테스트, 빈 줄, 주석을 포함하고 마지막 개행 없는 줄도 한 줄로 계산합니다. JSON 데이터·Markdown 문서·이미지·바이너리 자산은 위 언어 표에서 제외했습니다. 전체 비 Git 파일은 374개입니다.
TypeScript 중 demos/가 48파일·16,520줄, packages/가 17파일·3,108줄, apps/room-viewer/가 10파일·1,881줄입니다. 큰 비중은 앱 프레임워크보다 사물의 모양을 재현하는 코드에 있습니다. 주 언어 메타만 보고 웹 부분을 작게 생각하면 중요한 학습 대상을 놓칩니다.
.gitignore는 개인 입력인 data/captures-in/, 중간 산출물인 data/scans/·data/captures/ 등을 제외합니다. 반면 data/web-assets/는 Vite의 공개 자산 폴더입니다. 원본 사진을 “프로젝트 안”에 넣는 것과 “공개 자산 안”에 넣는 것은 다릅니다.
CaptureController는 @MainActor이며 @Published 속성으로 화면에 상태 변화를 알립니다. 한편 CoverageEngine은 전용 직렬 큐에서 표면 계산을 수행합니다. UI가 쓰는 상태와 무거운 계산의 소유자를 구분하는 예제입니다. 여기에 Task.detached로 원시 캡처 데이터를 저장하는 경로도 있으므로 “비동기 함수는 전부 같은 큐에서 돈다”고 생각하면 안 됩니다.
작은 실습: phase가 바뀌는 곳과 recordingSaveFailed가 설정되는 곳을 각각 찾아 표로 적어 보세요. 방 저장과 세션 저장의 실패가 UI에 어떤 선택지를 남기는지 설명하는 것이 목표입니다. 현 코드를 Swift 6 전용 문법으로 바꾸는 것은 이 읽기 실습의 전제 조건이 아닙니다.
.agents/skills/scan-to-threejs/scripts/bundle_to_room.py의 apply(m, p, w=1.0)에서 w를 바꾸며 위치와 방향을 구분합니다. demos/office-v4/index.ts도 위치에는 applyMatrix4, 방향에는 transformDirection을 사용합니다. 같은 정렬 행렬을 쓰더라도 벡터의 의미에 따라 연산이 달라집니다.
작은 실습: 회전 없이 X축으로 2m 이동하는 행렬을 종이에 적고, 점 (1, 0, 0)은 (3, 0, 0)으로, 방향 (1, 0, 0)은 그대로 남는지 확인하세요. 그다음 90° 회전을 앞뒤로 붙여 이동과 회전의 순서가 결과를 바꾸는지 비교합니다.
CoverageEngine.contribution(_:_:_:twoSided:)는 표면 점을 카메라 좌표로 바꾼 뒤 카메라 기준 깊이·화면 안 위치·법선 방향·깊이 데이터에 따른 가려짐을 검사합니다. 카메라 앞 방향이 −Z이므로 깊이 항은 d = -c.z입니다. 아래 식은 해당 함수의 핵심 계산을 풀어 쓴 것입니다.
# 화면에 투영되는 픽셀 좌표
u = fx * cameraX / d + cx
v = fy * (-cameraY) / d + cy
# 사진의 세부 표현 능력을 추정
pxPerCm = fx * cosT / d / 100
score = min(1, pxPerCm / 4) * sharpness * occlusionFactor
# 카메라 축 방향 깊이 조건: 0.25m 초과, 5m 미만
# 화면 가장자리 3%는 제외, cosT는 0.2 초과
fx는 픽셀 단위 초점거리, cosT는 표면을 정면에서 보는 정도입니다. d는 카메라 축 방향 깊이로, 카메라와 표면 점 사이의 직선거리와는 다릅니다. 가려짐을 확인할 깊이가 없으면 계수 0.9를 사용하고, 앞에 다른 표면이 있으면 점수를 0으로 만듭니다. 사진 수가 많아도 멀거나 비스듬하거나 흔들린 사진이면 점수가 낮을 수 있습니다.
fx, fy, cx, cy는 렌즈가 공간의 점을 이미지 어디에 맺히게 하는지 설명합니다. 자세는 카메라가 어디에 있고 어느 방향을 보는지 설명합니다. “같은 사진 시점”을 재현하려면 둘 다 필요합니다.작은 실습: 초점거리·정면 정도를 고정하고 카메라 기준 깊이를 두 배로 바꾸면 pxPerCm가 절반이 되는지 계산하세요. 이어 Stats.percent를 읽고, 점수가 높아도 아직 스캔하지 않은 방 뒤편의 존재는 알 수 없다는 이유를 설명해 보세요.
앱의 PhotoRecord는 원래 센서 방향의 픽셀에 맞는 내부 파라미터를 보관합니다. .agents/skills/scan-to-threejs/scripts/prepare_photos.py의 prepare()는 변환된 방 JSON의 photos 배열 순서대로 읽고, ROTATE_270으로 시계 방향 90° 회전한 다음 기본 긴 변 1280px·JPEG 품질 90으로 내보냅니다.
파일명 정렬만으로 사진 인덱스를 다시 만들지 않는 것이 중요합니다. 원본이 0009.jpg 다음 0003.jpg 순서로 선택됐다면 새 결과의 0000.jpg는 원본 0009.jpg여야 합니다. EXIF 자동 회전을 또 적용하면 카메라 방향과 이미지가 어긋납니다.
.agents/skills/scan-to-threejs/references/house-capture.md에는 비교 도구가 여백을 자른다는 설명이 남아 있지만, 현재 .agents/skills/scan-to-threejs/scripts/cmp_photo.sh는 검은 장면 픽셀을 자르지 않고 원본 사진을 종횡비를 유지한 채 렌더 높이에 맞춰 조정합니다. 픽셀 비교에서는 문서의 설명과 실제 스크립트를 함께 확인해야 합니다. 비교 도구의 기본 주소는 http://localhost:5173이므로, 이 저장소의 개발 서버에 연결하려면 스킬 안내대로 CAP_URL=http://localhost:5188을 지정합니다.
packages/room-runtime/walls.ts의 wallShape()는 창문처럼 벽 내부에 있는 개구부는 구멍으로, kind가 rect이고 v0가 정확히 0인 개구부는 외곽선의 패인 부분으로 만듭니다. 내부 구멍이 외곽선에 닿는 모양은 기하 생성에서 문제가 될 수 있기 때문입니다. buildWall()은 이 2D 형태를 두께 방향으로 밀어 벽을 만듭니다.
작은 실습: 종이에 같은 직사각형 벽을 두 번 그리고, 창문은 닫힌 내부 사각형으로, 문은 아래 변에서 이어지는 외곽선으로 표시해 보세요. 이후 Opening의 판별 유니언과 o.kind === 'rect' 조건이 모델링 규칙을 어떻게 좁히는지 읽습니다.
packages/room-runtime/render.ts의 SceneAOPass.render()는 불투명 표면을 그린 깊이를 AO 계산에 재사용하고, 투명 물체는 그 뒤에 그립니다. 투명 의자 등받이 뒤의 물체가 엉뚱한 어두운 윤곽으로 번지는 문제를 줄이려는 구성입니다. bloom 버퍼 크기도 가로·세로를 절반으로 줄입니다.
apps/room-viewer/src/main.ts의 renderFrame()은 dirty || moved일 때 렌더하고, 움직임이 잦아들면 다음 프레임 예약을 멈춥니다. 자동 품질은 이동 중 픽셀 수를 줄이고 정지하면 최대 기기 픽셀 비율 2로 돌아갑니다. 정적인 방 화면에서 배울 만한 것은 “항상 60번 그리기”보다 “필요할 때 그리기”입니다.
화면이 맞는지, 기능이 동작하는지, 빠르게 그려지는지는 각각 별도의 증거로 확인해야 합니다. 사진 비교는 모양의 근거이고, 타입 검사·테스트는 코드 동작의 근거이며, 고정된 조건의 프레임 측정은 성능의 근거입니다.
| 하려는 일 | 필요한 환경 | 확인 근거 |
|---|---|---|
| 소스 읽기 | 텍스트 편집기. LiDAR·Xcode·패키지 설치 불필요 | 정적 소스와 공개 측정 JSON으로 학습 가능 |
| 웹 뷰어 개발 | README 권장 Node.js 22.12+와 npm | Vite 잠금값의 엔진 범위는 ^20.19.0 || >=22.12.0 |
| 공개 뷰어 보기 | WebGL 2와 GPU 가속을 지원하는 브라우저 | 소스의 WebGLRenderer 사용과 Three.js 공식 문서 |
| 웹 걷기 모드 | 마우스 포인터 잠금을 지원하는 브라우저·입력 장치 | requestPointerLock 미지원이면 버튼 비활성화 |
| 새 방 스캔 | LiDAR가 있는 iPhone·iPad, iOS/iPadOS 17+, 카메라 허용 | apps/house-capture/project.yml, CaptureReadiness.prepare() |
| 앱 빌드·기기 설치 | Mac, 적합한 Xcode·iOS SDK, XcodeGen, 개발 서명 설정 | 앱 README와 apps/house-capture/project.yml. XcodeGen 버전 고정 없음 |
| 사진 처리 | Python·Pillow, 캡처 비교에는 zsh·agent-browser | 각 도구의 import와 실행 파일 선언 |
| 깊이 재구성 | Python·NumPy·Open3D·Pillow, 충분한 메모리·저장 공간 | .agents/skills/scan-to-threejs/scripts/reconstruct_bundle.py. 최소 RAM 수치는 제시되지 않음 |
브라우저 조건은 Three.js WebGLRenderer 공식 문서 기준입니다. 모든 GPU·모바일 브라우저에서 같은 프레임 속도가 보장되지는 않습니다.
apps/house-capture/HouseCapture/ReleaseSupport.swift의 CaptureReadiness.reserve는 500_000_000바이트, pauseReserve는 200_000_000바이트입니다. 시작·재개 때 여유 공간을 확인하고, 스캔 중 후자 아래로 내려가면 일시정지합니다. 이 값은 캡처 한 건의 예상 크기가 아니라 디스크 여유분 기준입니다.
| CaptureBudget 정책 | 일반 상태 | 제약 상태 | 위험한 열 상태 |
|---|---|---|---|
| 깊이 저장 간격 | 0.2초 | 0.5초 | 1초 |
| 평가 간격 | 0.1초 | 0.3초 | 0.3초 |
| 사진 간격 정책값 | 0.4초 | 1.2초 | 1.2초 |
apps/house-capture/HouseCapture/CaptureQuality.swift는 메모리 압박, 방 사진 150장 이상, 심각하거나 위험한 열 상태를 제약 조건으로 삼습니다. 위 숫자는 작업을 시도할 간격 정책이지, 실제 사진 저장이 그 빈도로 보장된다는 뜻은 아닙니다. 사진에는 추적 안정성·선명도·추가 커버리지 조건도 적용됩니다.
CaptureController.start는 재사용할 지도가 35,000,000바이트 이상이거나 해당 구역에 방이 네 개 이상이면 새 구역을 만듭니다. 무조건 한 세션을 길게 유지하기보다 좌표 연결을 기록하면서 작업 단위를 나누는 선택입니다. 물리 스캔에는 최적화가 켜지는 Release 빌드를 사용하라고 앱 README가 안내합니다.
apps/house-capture/HouseCapture/Diagnostics.swift의 TrackingRecovery.update(problem:now:)는 정상 추적이 1.5초 유지돼야 캡처 가능 상태로 되돌립니다. CaptureController.updateRecovery는 프레임이 0.5초 이상 오래됐는지도 확인합니다. 위치를 신뢰할 수 없는 순간에는 사진·깊이·커버리지 입력을 멈추는 방식입니다.
LiDAR 없는 기기는 가져오기·검토·공유를 할 수 있지만 새 스캔은 할 수 없습니다. 시뮬레이터로 저장 형식과 일부 정책을 확인해도 현장의 추적 실패·열·메모리 문제를 재현했다고 볼 수 없습니다. App Store 배포와 실기기 확인도 별도 단계입니다.
앱의 폴더 가져오기는 manifest.json이 들어 있는 방·세션 번들용이며, 집 전체 폴더 가져오기는 지원하지 않는다고 README가 밝힙니다. 반면 Python의 .agents/skills/scan-to-threejs/scripts/house_to_rooms.py는 집 전체 내보내기를 읽습니다. 비슷한 “가져오기”라는 이름 아래 지원 범위가 다릅니다.
아래 과제는 실습용 복사본에서 진행하는 학습안입니다. 원본 캡처가 없는 공개 사무실만으로도 과제 1~3을 시작할 수 있습니다.
목표: 파일을 읽을 때 “무슨 라이브러리인가”보다 “누가 누구를 부르는가”를 먼저 찾는 연습입니다.
demos/index.ts에서 기본 방 ID와 roomById()의 대체 선택 규칙을 찾습니다.officeV4.build() → officeLatest.build() → buildDetailedOffice()를 연결합니다.loadRoom()에서 방의 그룹·조명·참조 자료가 뷰어에 연결되는 지점을 표시합니다.referenceAvailable 설정까지 추적합니다.제출물: 파일 경로 다섯 개 이상과 함수명이 들어 있는 호출 그림, 공개 원본 사진이 없는 이유를 설명하는 세 문장입니다.
완료 기준: 알 수 없는 방 ID를 넣었을 때 무엇이 선택되는지, 폴더가 여러 개여도 공개 선택지가 하나인 이유를 설명할 수 있어야 합니다.
목표: 미터 단위 수치와 화면의 물체를 연결합니다. 시작 파일은 demos/office-v2/items/desk.ts입니다.
// 원본 SLAB 상수의 일부
export const SLAB = {
x0: 0.245, x1: 2.07,
back: -0.865, top: 0.825, thick: 0.052
};
// 읽기 문제: 좌우 폭은 2.07 - 0.245 = 1.825m
// 설계 문제: 상판 높이를 5cm 올리면 다리·장비도 확인해야 함
주의할 점: office-v4는 이후 수정 계층을 적용합니다. 초기 책상 상수를 바꾼 결과가 현재 공개 장면의 최종 치수와 같다고 곧바로 가정하지 말고, demos/office-v4/desk-updates.ts까지 읽어야 합니다.
제출물: 변경 이유, 영향받는 함수 목록, 동일 시점의 전후 이미지 두 장입니다. 장면이 열리는 것만으로 물체 배치 검증을 끝내지 않습니다.
# 독자의 별도 작업용 복사본에서 선택적으로 실행
# README 기준 Node.js 22.12+ 준비 후
npm ci
npm run dev
# 브라우저 주소 입력란: http://localhost:5188
# 타입 검사와 정적 빌드. 시각 정확성은 별도 확인
npm run build
목표: 눈으로 그럴듯한 배치와 수학적으로 검증된 배치를 구별합니다. 기준 파일은 .agents/skills/scan-to-threejs/scripts/test_house_import.py와 .agents/skills/scan-to-threejs/scripts/house_to_rooms.py입니다.
test_active_scan_and_noncommuting_transform의 예상 점 (15, 3.5, 14)를 손으로 계산합니다.active 참조가 우선돼야 하는 예를 만듭니다.roomToHouse가 생기면 안 되는 이유를 설명합니다.| 입력 상황 | 기대 결과 |
|---|---|
| 유효한 정렬과 active 참조 | ready와 변환된 방 파일 |
| 세션 방식이 unaligned | blocked, 집 배치 행렬 미제공 |
| 출력 폴더가 이미 존재 | 덮어쓰기 거부 |
| 집 폴더 밖을 가리키는 세션 경로 | 가져오기 실패 사유 기록 |
제출물: 정상 예 한 개와 실패 예 세 개의 입력·기대 결과 표입니다. 이후 코드를 수정한다면 이 표를 동작 테스트의 근거로 사용할 수 있습니다.
목표: 모델이 틀린 문제와 비교 도구가 틀린 문제를 분리합니다. 공개 사무실에는 원본 사진이 없으므로, 사진이 필요한 부분은 개인 캡처나 글자·색 띠가 있는 합성 이미지로 준비합니다.
prepare()가 배열 순서로 번호를 붙이고 시계 방향으로 한 번 돌리는 과정을 추적합니다.photos를 연결한 경우 apps/room-viewer/tools/capture.ts가 자세·시야각·종횡비를 어떻게 쓰는지 확인합니다.완료 기준: 결과 0000.jpg가 어느 원본인지 설명하고, EXIF 회전을 추가로 적용했을 때 생기는 오류를 그림으로 보일 수 있어야 합니다. 이미지 여백을 임의로 잘라 비교 구도를 바꾸지 않습니다.
목표: “빨라졌다”는 느낌 대신 조건과 관측값을 남깁니다. 읽을 곳은 batchStatic(), apps/room-viewer/tools/capture.ts의 stats 처리, 뷰어의 renderFrame()입니다.
완료 기준: 기기·브라우저·해상도·측정 방법을 함께 적고 전후 이미지까지 제출합니다. 내 기기 결과를 저장소 전체의 보장 FPS로 바꾸어 표현하지 않습니다.
실습의 완성 기준은 코드를 조금 바꾸는 것이 아니라, 어떤 근거로 결과가 맞다고 판단했는지 설명하는 것입니다.
초보자는 처음부터 캡처 앱 전체와 고급 셰이더를 동시에 읽기보다, 매주 한 종류의 입출력을 설명할 수 있게 만드는 편이 좋습니다. 아래 순서는 주 3~5시간 정도를 잡는 학습 제안이며, 프로젝트의 공식 일정이나 수강 시간은 아닙니다.
| 주차 | 배울 개념 | 읽을 코드 | 남길 결과 |
|---|---|---|---|
| 1주 | TypeScript 타입, 장면 그래프, 모듈 | packages/room-runtime/types.ts, demos/index.ts, demos/office-v4/index.ts |
RoomModule 호출 경로와 공개 자료 범위 |
| 2주 | 벡터, 행렬, 점·방향, 로컬 좌표 | .agents/skills/scan-to-threejs/scripts/bundle_to_room.py, wallPoint(), wallMatrix() |
위치와 방향 변환의 손계산 예 |
| 3주 | 카메라 투영, 사진 회전, 가려짐 | PhotoRecord, .agents/skills/scan-to-threejs/scripts/prepare_photos.py, contribution() |
같은 사진 시점을 재현하는 데 필요한 필드 표 |
| 4주 | Swift 상태 머신, 실패 복구, 파일 저장 | CaptureController, ScanBundle, TrackingRecovery |
정상·추적 상실·저장 실패의 상태 그림 |
| 5주 | 작업 큐, 배칭, 깊이 버퍼, 정지 시 렌더 중단 | CoverageMailbox, packages/room-runtime/batch.ts, packages/room-runtime/render.ts |
성능 변경 한 건의 측정·화면 비교 기록 |
| 6주 | 다중 세션 배치, 데이터 검증, 정적 배포 | .agents/skills/scan-to-threejs/scripts/house_to_rooms.py, .agents/skills/scan-to-threejs/scripts/test_house_import.py, tools/audit-public.py |
합성 입력으로 정상·실패 경계를 설명하는 보고서 |
물리 엔진에 관심이 생기면 packages/room-physics/physics.ts를 읽습니다. loadRapier()는 패키지를 동적으로 읽고 R.init()를 기다립니다. FIXED_DT = 1 / 60, GRAVITY = -9.81처럼 시뮬레이션의 시간·단위도 명시돼 있습니다.
깊이 재구성으로 넘어가면 .agents/skills/scan-to-threejs/scripts/reconstruct_bundle.py의 실제 파서 기본값부터 읽습니다. voxel=0.02, trunc=8, max-depth=3.5, min-confidence=1, faces=0입니다. 파일 맨 위 사용 예의 0.015·400000은 현재 기본값과 다릅니다. 주석보다 실행 인자를 정하는 코드를 먼저 확인하는 습관을 들이세요.
한 주가 끝날 때마다 함수 하나의 입력, 출력, 실패 조건을 자기 말로 설명할 수 있으면 다음 단계로 넘어가도 좋습니다.
집을 배울 때 수도 배관·벽돌·전기 회로를 한날에 모두 외우기보다, 먼저 한 방의 평면도를 이해하는 것과 같습니다. 작은 방 하나에서 좌표·카메라·저장을 연결할 수 있으면 집 전체로 넓히기가 쉬워집니다.
| 용어 | 쉬운 설명 |
|---|---|
| LiDAR | 빛을 이용해 주변 표면까지의 거리를 재는 센서. 사진의 색과 별개로 공간의 깊이를 얻습니다. |
| RoomPlan | Apple의 실내 측정 프레임워크. 벽·문·가구 같은 의미와 치수를 함께 제공합니다. |
| ARSession | 카메라·센서의 관측과 기기 위치 추적이 이어지는 작업 단위. 이 앱은 하나를 RoomPlan과 공유합니다. |
| 앵커(anchor) | 추적 좌표계 안에서 특정 공간 정보와 위치를 묶는 기준. 메시 갱신은 앵커별로 관리합니다. |
| 메시(mesh) | 표면을 정점과 삼각형으로 표현한 것. 점만 모인 점군과는 다릅니다. |
| 법선(normal) | 표면이 향하는 방향. 빛 반사나 정면 사진인지 판단할 때 사용합니다. |
| pose | 위치와 방향을 묶은 자세 정보. 사진 파일만으로는 알 수 없는 촬영 위치를 담습니다. |
| intrinsics | 초점거리와 이미지 중심 등 카메라 내부 파라미터. 3D 점을 픽셀로 옮기는 계산에 씁니다. |
| FOV / aspect | 카메라가 보는 각도와 이미지 가로세로 비율. 같은 시점 비교의 구도를 결정합니다. |
| EXIF 방향 | 이미지를 어떻게 돌려 보여 줄지 적는 메타데이터. 실제 픽셀 배열 방향과 다를 수 있습니다. |
| 좌표 등록 | 서로 다른 스캔의 원점과 축을 맞추는 작업. 이름이 같다고 정렬된 것은 아닙니다. |
| 강체 변환 | 크기와 모양을 유지하며 회전·이동만 하는 변환. 스케일 변경이나 찌그러뜨림은 제외합니다. |
| column-major | 행렬 수치를 열 단위로 나열하는 저장 순서. 다른 도구와 교환할 때 순서를 맞춰야 합니다. |
| 커버리지 | 이 앱에서는 관측한 표면에 좋은 사진이 확보된 정도. 방 전체를 얼마나 다 스캔했는지와 다릅니다. |
| 가려짐(occlusion) | 물체가 다른 표면 뒤에 숨어 카메라에 보이지 않는 상태. 숨은 곳에 사진 색을 잘못 칠하지 않게 검사합니다. |
| PLY / USDZ | 3D 데이터 형식들. 이 앱의 PLY에는 메시와 커버리지 등이, USDZ에는 내보낸 3D 결과가 들어갑니다. |
| glTF / GLB | 3D 장면 교환 형식과 그 바이너리 컨테이너. 여기서는 참조 메시 등에도 사용합니다. |
| TSDF / voxel | 여러 깊이 관측을 공간 격자에 모아 표면을 추정하는 방식과 그 격자 단위. 선택적 복원 도구가 사용합니다. |
| 장면 그래프 | 물체를 부모·자식 관계로 묶은 구조. 벽과 부착물을 같은 그룹으로 숨길 수 있습니다. |
| draw call | GPU에 그리기 작업을 요청하는 단위. 작은 물체가 많으면 삼각형 수 외에 요청 횟수도 비용이 됩니다. |
| 배칭 / 인스턴싱 | 여러 기하를 합치거나 같은 기하를 반복 사용해 그리기 요청을 줄이는 방법입니다. |
| AO / bloom | 틈과 접촉면을 어둡게 하는 효과, 밝은 부분이 주변으로 번지는 효과입니다. |
| 깊이 버퍼 | 화면의 각 픽셀이 얼마나 먼 표면인지 저장한 값. 가림 처리와 AO 계산 등에 씁니다. |
| 톤 매핑 | 장면의 넓은 밝기 범위를 화면에 표현 가능한 범위로 바꾸는 과정입니다. |
| dirty 플래그 | 화면이 바뀌어 다시 그려야 한다는 표시. 변화가 없을 때 렌더링을 쉬는 데 씁니다. |
| 메일박스 | 생산자가 넣은 최신 작업 데이터를 소비자가 가져가는 보관함. 여기서는 키별 최신 값으로 대체합니다. |
| 체크포인트 / atomic | 중간 저장 지점과 파일 교체 방식. 한 파일의 안전한 교체가 전체 번들의 일괄 성공은 아닙니다. |
| active 스캔 | 방의 여러 기록 중 현재 선택된 기록. 날짜가 가장 최신인 기록과 항상 같지는 않습니다. |
| 잠금 파일 | 실제로 선택된 의존성 버전을 기록하는 파일. 이 레포는 package-lock.json을 포함합니다. |
| 에이전트 스킬 | 에이전트가 따를 작업 절차와 도구 모음. 이 스킬 자체가 학습된 3D 생성 모델은 아닙니다. |
| 정적 배포 | 미리 빌드한 HTML·JS·자산을 제공하는 방식. 이 레포는 GitHub Pages 배포 설정을 갖고 있습니다. |
| 항목 | 확인 내용 | 출처 / 해석 |
|---|---|---|
| 스타 / 포크 | 629 / 106 | 2026-10-23 GitHub 기준 |
| 주 언어 | Swift | GitHub 메타. 물리적 줄 수 순위와는 별도 |
| 생성일 | 2026-10-21 | 공개 저장소 생성일. 내부 작업 시작일의 증명은 아님 |
| 최근 push | 2026-10-22 | 2026-10-23 GitHub 기준 |
| 분석한 최근 커밋 | 31b2dc9 · 2026-10-22 | Link the project video from README using thumb-1 |
| 전체 커밋 식별자 | 31b2dc9da3fb541e501f2f0936e55b32afb70ee8 | 커밋 31b2dc9의 전체 식별자 |
| 최신 GitHub 릴리스 | 없음 | 2026-10-23 GitHub 기준. 소스·데모는 공개돼 있음 |
| 코드 규모 | TypeScript 21,509줄 / Swift 11,779줄 / Python 1,376줄 | 커밋 31b2dc9의 파일 기준. .git 제외, 빈 줄·주석·테스트 포함 |
| 파일 규모 | 비 Git 파일 374개 | 커밋 31b2dc9의 파일 수, .git 제외 |
| 라이선스 메타 | NOASSERTION | 2026-10-23 GitHub 기준. 이용 조건은 아래 라이선스 파일 참고 |
| 실제 소스 라이선스 | 코드·스킬은 MIT, 데이터·썸네일은 제외 | LICENSE와 ASSETS-LICENSE.md |
| 패키지 / 앱 설정 버전 | 웹 0.0.0 / 앱 1.0, 빌드 1 | 설정 파일의 값. 정식 배포 버전이라는 증거는 아님 |
| 배포 검증 기록 | 웹 빌드·브라우저 확인·합성 테스트·시뮬레이터 빌드 | docs/release-status.md 기준 |
docs/release-status.md에는 깨끗한 의존성 설치와 웹 빌드, 브라우저 확인, 합성 가져오기 테스트 일곱 개, 서명 없는 Release 시뮬레이터 빌드를 로컬에서 검증했다고 적혀 있습니다. 이는 해당 문서에 기록된 검증 범위이며, 실기기 스캔이나 App Review 성공을 의미하지는 않습니다.
독자가 그대로 재현하려면 외부 도구 버전과 실기기 조건을 별도로 맞춰야 합니다. 특히 Python 선택 의존성과 브라우저 캡처 CLI는 버전이 고정돼 있지 않습니다. 최신 공식 문서는 개념 학습에 쓰고, 이 글의 동작 설명은 고정 커밋 소스로 확인하는 방식이 적절합니다.
자동화의 범위: 변환은 측정 기준선을 마련합니다. 가구의 상세 모양·재질·조명과 여러 세션의 연결에는 모델링과 검토가 필요합니다. Python의 집 배치 출력이 자동 정렬이나 웹 집 전체 탐색을 완성해 주지는 않습니다.
데모와 배포: 공개본은 사무실 하나이며 원본 스캔·사진 모음은 배포하지 않습니다. GitHub 릴리스는 아직 없고, 소스 공개만으로 App Store 배포를 가정할 수 없습니다. 공개 예제만으로 원본 사진 비교를 재현할 수는 없습니다.
측정의 한계: 사진 커버리지는 방의 완전성·기하 정확도와 다릅니다. 센서 추적·가려짐·사진 흐림 때문에 누락이나 오차가 생길 수 있습니다. 중요한 치수는 실제 측정과 대조해야 합니다.
자료와 권리: 개인 공간의 사진·메시를 공개 자산 폴더에 옮기기 전에 내용을 검토하세요. 코드의 MIT 조건을 데모 이미지·상표·작품에 그대로 적용할 수 없습니다. 별도 자산 조건은 학습용 예제 실행·연구와 그 밖의 재사용을 구분하며, 파일별 출처 장부도 아직 완전히 복원되지 않았다고 밝힙니다.
검증 범위: docs/release-status.md의 검증 기록만으로 실기기 추적 복구, 처리 시간, 모델 정확도, 모든 브라우저의 호환성이 보장되지는 않습니다.