# Get Nodes (조회 API)

우클릭 Select 메뉴의 일반 7항목(`[Debug]` 제외)에 대응하는 **조회 전용 API**입니다. 동사는 `get`이며, 기본 목적은 핸들/이름 조회입니다. `select=1`을 줄 때만 부수효과로 씬 선택을 수행합니다 (`select=0` 기본값 = 조회만, 씬 상태 불변).

---

## 메서드 목록

| 메서드 | 우클릭 메뉴 | 데이터 소스 |
| --- | --- | --- |
| `get_all_objects(slot, select=0)` | All Objects | 레퍼런스 전체 노드 핸들 |
| `get_skin_meshes(slot, select=0)` | Skin Meshes | 스킨 메시 핸들 |
| `get_skin_bones(slot, select=0)` | Skin Bones | 스킨 본 핸들 |
| `get_all_skin_objects(slot, select=0)` | All Skin Objects | 메시 + 본 (중복 제거, 메시 먼저) |
| `get_duplicate_nodes(slot, select=0)` | Duplicate Nodes | 이름이 중복된 노드만 |
| `get_biped_com(slot, select=0)` | Biped COM | Biped COM 노드 핸들 |
| `get_biped_nodes(slot, select=0)` | Biped Nodes | Biped 전체 노드 핸들 |

---

## 공통 파라미터

| 파라미터 | 타입 | 필수 | 설명 |
| --- | --- | --- | --- |
| `slot` | `int` | O | 슬롯 번호(0~9). **bool/str/float는 즉시 `TypeError`** |
| `select` | `int` (0/1) 또는 `bool` | X (기본 `0`) | `0`=조회만, `1`=조회 후 씬 선택. `False`→`0`, `True`→`1`로 정규화. 그 외는 즉시 `ValueError` |

```text
select=0 (기본) → 조회만. 씬 선택 상태 변경 없음.
select=1        → 조회 후 반환 handles로 Max 씬 선택까지 수행.
```

---

## 공통 반환 계약

```python
{
    "success": True,
    "slot": 0,
    "category": "skin_meshes",
    "count": 12,
    "handles": [101, 102, ...],
    "names": ["Mesh_A", ...],
    "selected": 0,
    "selected_count": 0,
    "message": "skin_meshes: 12개",
}
```

| 메서드 | `category` | 추가 필드 |
| --- | --- | --- |
| `get_all_objects` | `"all_objects"` | `reference_name` |
| `get_skin_meshes` | `"skin_meshes"` | — |
| `get_skin_bones` | `"skin_bones"` | — |
| `get_all_skin_objects` | `"all_skin_objects"` | — |
| `get_duplicate_nodes` | `"duplicate_nodes"` | `duplicate_names` (`list[str]`) |
| `get_biped_com` | `"biped_com"` | — |
| `get_biped_nodes` | `"biped_nodes"` | — |

---

## 빈 결과와 유효 핸들 재수집

- 저장된 핸들이 없거나 전부 무효(삭제된 노드)면 → `success=True`, `count=0`, `handles=[]`, `names=[]` (**에러가 아님**).
- 중복이 없으면 `get_duplicate_nodes`는 `duplicate_names=[]`, `count=0`.
- `select=1`이어도 선택할 대상이 없으면 `selected_count=0` (에러 아님).
- 반환 `handles`/`names`/`count`는 **현재 씬에 존재하는 유효 노드만** 기준으로 재수집된다.

---

## 예외 처리 (FAIL FAST)

| 상황 | 예외 |
| --- | --- |
| `slot`이 `int`가 아님 (bool 포함) | `TypeError` |
| `slot`이 범위 밖 / 빈 슬롯(레퍼런스 미로드) | `ValueError` |
| `select`가 0/1/bool 이외 | `ValueError` |

잘못된 `slot`/`select`는 `{"success": False}`가 아니라 **예외**로 즉시 드러납니다.

---

## 삭제된 select_* → get_* 치환

옛 `select_*` API는 **완전 삭제**되었습니다 (shim 없음). `AttributeError`가 나면 아래로 교체하세요.

| 삭제됨 | 대체 |
| --- | --- |
| `select_reference_nodes` | `get_all_objects(select=1)` |
| `select_duplicate_nodes` | `get_duplicate_nodes(select=1)` |
| `select_skin_mesh` | `get_skin_meshes(select=1)` |
| `select_skin_bones` | `get_skin_bones(select=1)` |

---

## 예제

```python
from os_fast_ref.api import FastRefAPI

api = FastRefAPI()

# 조회만 (씬 선택 안 함)
r = api.get_all_objects(slot=0)
print(r["reference_name"], r["names"])

# 조회 + 씬 선택 (우클릭 메뉴와 동일)
api.get_skin_meshes(slot=0, select=1)

# 중복 이름 검증 (파이프라인용 — select=0 권장)
dup = api.get_duplicate_nodes(slot=0)
if dup["duplicate_names"]:
    print("duplicates:", dup["duplicate_names"])
```

외부 자동화는 항상 `FastRefAPI`를 사용하세요. semver 보증 대상은 `FastRefAPI`뿐입니다.