Files
dwg-dxf-viewer-sample/docs/entity-selection-property-layer-ui.md

12 KiB

Entity 선택, 속성 보기(Property Inspector), 레이어 관리(Layer Panel) & UI 패널 통합 가이드

이 문서는 DWG/DXF 뷰어 및 다른 2D/3D CAD 웹 뷰어 프로젝트에서 엔티티 선택(Picking), 상세 속성 패널(Property Inspector), 레이어 관리 패널(Layer Panel), 그리고 **드래그·접기·리사이즈 가능한 UI 윈도우(Panel Utilities)**를 구축하고 이식하기 위한 통합 개발 및 재사용 가이드입니다.


📌 1. 전체 아키텍처 & 모듈 구성

본 시스템은 뷰어 코어 엔진과 UI 구성요소가 완벽히 분리된 모듈형 구조로 설계되어 있어, 다른 Three.js/WebGL 기반 뷰어에도 손쉽게 이식할 수 있습니다.

┌─────────────────────────────────────────────────────────────────────────────┐
│                            Canvas Viewport (Viewer2D)                       │
│  - Entity Raycasting & Pick Index (_pickSegs, _pickFills)                    │
│  - Highlight & Accent recoloring (_highlight)                               │
│  - Selected Entity Fit Camera (zoomToSelection)                             │
│  - Layer Visibility & Info (getLayerInfo, setHiddenLayers)                  │
└──────────────────────┬──────────────────────────────┬───────────────────────┘
                       │ Event: onSelect(entity)      │ Layer Info API
                       ▼                              ▼
┌────────────────────────────────────────┐ ┌──────────────────────────────────┐
│      propertyInspector.ts              │ │         layerPanel.ts            │
│  - formatEntityProperties()            │ │  - LayerPanelManager class       │
│  - Entity Type Specific Geometry Math  │ │  - Real-time Keyword Search      │
│  - Dynamic Property Card Rendering     │ │  - Eye Toggle & Show/Hide All    │
└───────────────────┬────────────────────┘ └─────────────────┬────────────────┘
                    │                                        │
                    ▼                                        ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                               panelUtils.ts                                 │
│  - makePanelDraggableAndCollapsible(panelEl, options)                       │
│  - Header Mouse/Pointer Dragging & Screen Clamping                          │
│  - Z-Index Top Focus Management                                             │
│  - Height Preservation Collapse / Expand Toggle                             │
│  - CSS3 resize: both Integration                                            │
└─────────────────────────────────────────────────────────────────────────────┘

📑 2. 주요 모듈 명세 및 역할

모듈 파일 역할 및 제공 기능 비고
src/viewer2d/Viewer2D.js 렌더링 시 피킹 인덱스 구축, 레이캐스팅, 강조 표시, zoomToSelection() 카메라 핏팅 뷰어 코어 엔진
src/propertyInspector.ts 엔티티 유형(LINE, CIRCLE, ARC, POLYLINE, TEXT, INSERT 등)별 속성 추출 및 포맷팅, DOM 카드 렌더링 속성 패널 모듈
src/layerPanel.ts LayerPanelManager 클래스 - 검색 필터, 가시성 눈동자 토글, 전체 켜기/끄기, 색상/개수 표시 레이어 패널 모듈
src/panelUtils.ts makePanelDraggableAndCollapsible - 헤더 드래그, 화면 이탈 방지, z-index 최상단 전환, 접기/펼치기 UI 패널 공통 유틸
index.html & src/main.ts 패널 HTML 구조, 글래스모피즘 CSS 스타일, 메인 이벤트 바인딩 애플리케이션 글루

🔍 3. 핵심 기능별 상세 구현 원리

3.1 Entity 선택 (Picking) & Zoom to Selection

  1. 픽 데이터 수집 (Render Time)

    • 렌더링 시 각 엔티티의 실선 선분은 _pickSegs ([ax, ay, bx, by, ...]) 배열에 추가하고, owner 엔티티 메타 인덱스는 _pickSegMeta에 기록합니다.
    • 해치(HATCH), 솔리드(SOLID), 텍스트(TEXT) 박스 등 면적을 가진 도형은 _pickFills ({ metaIdx, loops }) 배열에 추가합니다.
  2. 히트 테스트 및 선택 (_onClick)

    • 캔버스 클릭 시 브라우저 좌표를 월드 WCS 좌표로 변환 (screenToWorld).
    • 클릭 지점으로부터 화면 픽셀 임계값(CLICK_THRESHOLD_PX) 이내에 있는 가장 가까운 선분을 우선 탐색합니다 (_segDist).
    • 선분이 없을 경우 면적 도형 내부인지 다각형 포함 테스트(_fillHit)를 수행하여 엔티티 메타데이터를 결정합니다.
    • _highlight(meta)를 통해 이전 선택 객체 색상을 복원하고 새 선택 객체의 Vertex Color를 선택 강조색(_selColor)으로 변경합니다.
    • onSelect(cb) 콜백을 발생시켜 선택된 entity 객체를 외부 UI로 전달합니다.
  3. 선택 객체로 Zoom (zoomToSelection())

    zoomToSelection() {
      if (!this._selMeta) return;
      const idx = this._entityMeta.indexOf(this._selMeta);
      if (idx < 0) return;
      const box = new THREE.Box3();
    
      // 선택된 메타 인덱스의 선분 및 꽉 찬 루프 좌표로 Box3 확장
      for (let i = 0, j = 0; i < S.length; i += 4, j++) {
        if (SM[j] === idx) {
          box.expandByPoint(new THREE.Vector3(S[i], S[i + 1], 0));
          box.expandByPoint(new THREE.Vector3(S[i + 2], S[i + 3], 0));
        }
      }
      if (!box.isEmpty()) this._fit(box);
    }
    

3.2 속성 보기 모듈 (propertyInspector.ts)

엔티티의 DXF/DWG 데이터 속성을 사용자가 읽기 편한 일반 정보(General)와 기하학적 정보(Geometry)로 분류하여 직관적으로 렌더링합니다.

주요 엔티티별 계산 속성:

  • LINE: 시작점, 끝점, \Delta X, \Delta Y, 길이 (L = \sqrt{\Delta x^2 + \Delta y^2}), 각도 (\theta = \text{atan2}(\Delta y, \Delta x))
  • CIRCLE: 중심점, 반지름 (r), 지름 (2r), 둘레 (2\pi r), 넓이 (\pi r^2)
  • ARC: 중심점, 반지름, 시작/끝 각도, 중심각 (\Delta \theta), 호 길이 (r \cdot \Delta \theta)
  • LWPOLYLINE / POLYLINE: 정점 개수, 닫힘 여부, 총 세그먼트 길이, 면적 (Shoelace 공식을 이용한 A = \frac{1}{2} |\sum (x_i y_{i+1} - x_{i+1} y_i)|)
  • TEXT / MTEXT: 텍스트 내용, 높이, 회전각, 정렬, 삽입점
  • INSERT (블록): 블록 이름, 삽입점, 축척 (S_x, S_y, S_z), 회전각

3.3 레이어 관리 모듈 (layerPanel.ts)

LayerPanelManager 클래스는 Viewer2D의 레이어 테이블 데이터와 동기화되어 다음 기능을 수행합니다:

  1. 실시간 키워드 필터링:
    const filtered = layers.filter(l => l.name.toLowerCase().includes(this.searchQuery));
    
  2. 레이어 숨김/표시 제어:
    public toggleLayer(name: string): void {
      if (this.hiddenLayers.has(name)) this.hiddenLayers.delete(name);
      else this.hiddenLayers.add(name);
      this.viewer.setHiddenLayers(this.hiddenLayers);
      this.render();
    }
    
  3. 일괄 동작: showAllLayers()hideAllLayers() 지원.

3.4 UI 패널 드래그 · 접기 · 크기 조절 모듈 (panelUtils.ts)

모든 플로팅 UI 패널에 자유로운 드래그 이동, 접기/펼치기, **크기 조절(Resize)**을 부여합니다.

export function makePanelDraggableAndCollapsible(
  panelEl: HTMLElement,
  options?: PanelControlOptions
)

동작 원리:

  1. 드래그 이동 & 화면 클램핑:
    • 패널 헤더(handleEl) pointerdown 시 포인터 시작 좌표와 패널의 상대 위치를 기록합니다.
    • 이동 시 Math.max(10, Math.min(newLeft, maxLeft))로 클램핑하여 패널이 화면 밖으로 이탈하지 않도록 조절합니다.
    • 드래그 시 패널 클릭과 함께 globalMaxZIndex를 증가시켜 선택된 패널이 다른 창 위에 표시되도록 처리합니다.
  2. 접기/펼치기 (Collapsible):
    • 접기(isCollapsed = true) 시 원래 높이(lastHeight)를 저장하고 height = 42px로 변경 및 내용 영역(bodyEl)을 display: none 처리합니다.
    • 펼침 시 저장해둔 lastHeightdisplay: flex를 복원합니다.
  3. 크기 조절 (Resizable):
    • CSS .cad-panelresize: both; overflow: hidden;을 적용하여 우하단 리사이즈 핸들을 활성화합니다.
    • .cad-panel.collapsed 시에는 resize: none !important;를 적용하여 접힘 상태 시 리사이즈를 차단합니다.

🎨 4. CSS 패널 스타일링 가이드 (index.html)

다른 프로젝트 이식 시 필요한 공통 패널 스타일 정의:

/* 공통 패널 윈도우 스타일 (Properties & Layers - Draggable & Resizable) */
.cad-panel {
  position: fixed;
  top: 56px;
  width: 320px;
  height: 480px;
  min-width: 250px;
  min-height: 140px;
  max-width: 90vw;
  max-height: 90vh;
  z-index: 15;
  background: rgba(22, 27, 34, 0.96);
  backdrop-filter: blur(12px);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  resize: both;
  transition: border-color 0.15s;
}

/* 패널 접힘 상태 */
.cad-panel.collapsed {
  height: 42px !important;
  min-height: 42px !important;
  max-height: 42px !important;
  overflow: hidden !important;
  resize: none !important;
}

/* 드래그 핸들 헤더 */
.cad-panel .hdr {
  padding: 10px 14px;
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: rgba(13, 17, 23, 0.7);
  position: sticky;
  top: 0;
  z-index: 5;
  cursor: grab;
  user-select: none;
  flex-shrink: 0;
}
.cad-panel .hdr:active {
  cursor: grabbing;
}

🚀 5. 다른 뷰어 프로젝트로의 이식 단계 (Migration Steps)

  1. 파일 복사:
    • src/panelUtils.ts
    • src/propertyInspector.ts
    • src/layerPanel.ts
  2. HTML 창 구조 추가:
    • index.html.cad-panel 클래스를 가진 #properties#layers 요소 추가.
  3. 타겟 뷰어 이벤트 바인딩 (main.ts):
    import { makePanelDraggableAndCollapsible } from './panelUtils';
    import { renderPropertyInspector } from './propertyInspector';
    import { LayerPanelManager } from './layerPanel';
    
    // 1. 패널 드래그/접기 적용
    makePanelDraggableAndCollapsible(document.getElementById('properties')!);
    makePanelDraggableAndCollapsible(document.getElementById('layers')!);
    
    // 2. 레이어 매니저 바인딩
    const layerManager = new LayerPanelManager(viewer, document.getElementById('layerList')!);
    
    // 3. 엔티티 선택 바인딩
    viewer.onSelect((entity) => {
      renderPropertyInspector(document.getElementById('propBody')!, entity);
    });