575 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			575 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
// DXF Viewer for Gitea - 캐시 무효화 버전 (2025-06-27 08:00)
 | 
						|
// Using Three.js and dxf-parser
 | 
						|
// 캐시버스팅을 위한 고유 주석
 | 
						|
 | 
						|
// 메인 뷰어 클래스
 | 
						|
/* 업데이트 버전: 2025-06-27 - drawEntity 문제 수정 */
 | 
						|
class DXFViewer {
 | 
						|
  constructor(container) {
 | 
						|
    console.log('뷰어 초기화 시작, 컨테이너:', container);
 | 
						|
    console.log('컨테이너 크기:', container.clientWidth, 'x', container.clientHeight);
 | 
						|
    this.container = container;
 | 
						|
    this.dxfGroup = new THREE.Group();
 | 
						|
    this.init();
 | 
						|
    
 | 
						|
    // 디버그용 직접 테스트 호출
 | 
						|
    this.addTestObjects();
 | 
						|
  }
 | 
						|
 | 
						|
  init() {
 | 
						|
    console.log('초기화 범위:', this.container.offsetWidth, 'x', this.container.offsetHeight);
 | 
						|
    
 | 
						|
    // 씬 초기화
 | 
						|
    this.scene = new THREE.Scene();
 | 
						|
    this.scene.background = new THREE.Color(0xf0f0f0);
 | 
						|
    
 | 
						|
    // 카메라 초기화
 | 
						|
    const width = this.container.offsetWidth || window.innerWidth;
 | 
						|
    const height = this.container.offsetHeight || window.innerHeight;
 | 
						|
    const aspect = width / height;
 | 
						|
    console.log('화면 비율:', aspect, '크기:', width, 'x', height);
 | 
						|
    
 | 
						|
    this.camera = new THREE.PerspectiveCamera(45, aspect, 0.1, 1000);
 | 
						|
    this.camera.position.set(10, 10, 10); // 더 가까운 초기 거리로 설정
 | 
						|
    this.camera.lookAt(0, 0, 0);
 | 
						|
    
 | 
						|
    // 렌더러 초기화 - 크기와 스타일 명확히 설정
 | 
						|
    this.renderer = new THREE.WebGLRenderer({ antialias: true });
 | 
						|
    this.renderer.setSize(width, height);
 | 
						|
    this.renderer.setPixelRatio(window.devicePixelRatio);
 | 
						|
    
 | 
						|
    // 렌더러 요소 추가 및 스타일 설정
 | 
						|
    const canvas = this.renderer.domElement;
 | 
						|
    canvas.style.display = 'block';
 | 
						|
    this.container.innerHTML = ''; // 기존 콘텐츠 제거
 | 
						|
    this.container.appendChild(canvas);
 | 
						|
    
 | 
						|
    // 조명 추가
 | 
						|
    const ambientLight = new THREE.AmbientLight(0x404040, 0.7); // 밝기 증가
 | 
						|
    this.scene.add(ambientLight);
 | 
						|
    
 | 
						|
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
 | 
						|
    directionalLight.position.set(1, 1, 1).normalize();
 | 
						|
    this.scene.add(directionalLight);
 | 
						|
    
 | 
						|
    // 카메라 위치 설정
 | 
						|
    this.camera.position.set(50, 50, 50);
 | 
						|
    this.camera.lookAt(0, 0, 0);
 | 
						|
    
 | 
						|
    // OrbitControls 초기화 - 여러 방법 시도
 | 
						|
    console.log('OrbitControls 초기화 시도 (버전 08:03)...');
 | 
						|
    try {
 | 
						|
      // 방법 1: 일반 OrbitControls 사용
 | 
						|
      if (typeof OrbitControls !== 'undefined') {
 | 
						|
        console.log('방법 1: 일반 OrbitControls 사용');
 | 
						|
        this.controls = new OrbitControls(this.camera, this.renderer.domElement);
 | 
						|
        this.controls.enableDamping = true;
 | 
						|
        this.controls.dampingFactor = 0.25;
 | 
						|
        this.controls.enableZoom = true;
 | 
						|
        this.controls.minDistance = 1;
 | 
						|
        this.controls.maxDistance = 500;
 | 
						|
        console.log('OrbitControls 초기화 성공 (방법 1)');
 | 
						|
      } 
 | 
						|
      // 방법 2: THREE.OrbitControls 사용
 | 
						|
      else if (THREE && THREE.OrbitControls) {
 | 
						|
        console.log('방법 2: THREE.OrbitControls 사용');
 | 
						|
        this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
 | 
						|
        this.controls.enableDamping = true;
 | 
						|
        this.controls.dampingFactor = 0.25;
 | 
						|
        this.controls.enableZoom = true;
 | 
						|
        this.controls.minDistance = 1;
 | 
						|
        this.controls.maxDistance = 500;
 | 
						|
        console.log('OrbitControls 초기화 성공 (방법 2)');
 | 
						|
      }
 | 
						|
      // 실패 시 로그만 출력
 | 
						|
      else {
 | 
						|
        console.warn('OrbitControls를 찾을 수 없습니다. 기본 모드로 실행합니다.');
 | 
						|
      }
 | 
						|
    } catch (error) {
 | 
						|
      console.error('OrbitControls 초기화 오류:', error);
 | 
						|
    }
 | 
						|
    
 | 
						|
    // OrbitControls 상태 확인 및 로그 출력
 | 
						|
    if (this.controls) {
 | 
						|
      console.log('OrbitControls 성공적으로 초기화됨');
 | 
						|
    } else {
 | 
						|
      console.warn('OrbitControls 초기화 실패, 기본 카메라 컨트롤만 사용');
 | 
						|
    }
 | 
						|
    
 | 
						|
    // dxfGroup 초기화
 | 
						|
    this.dxfGroup = new THREE.Group();
 | 
						|
    this.scene.add(this.dxfGroup);
 | 
						|
    
 | 
						|
    // 애니메이션 시작
 | 
						|
    console.log('애니메이션 루프 시작...');
 | 
						|
    this.animate();
 | 
						|
    
 | 
						|
    // 윈도우 크기 조절 이벤트 처리
 | 
						|
    console.log('윈도우 리사이즈 이벤트 리스너 추가');
 | 
						|
    window.addEventListener('resize', this.onWindowResize.bind(this), false);
 | 
						|
  }
 | 
						|
 | 
						|
  onWindowResize() {
 | 
						|
    console.log('윈도우 크기 변경 처리 중...');
 | 
						|
    const width = this.container.offsetWidth || window.innerWidth;
 | 
						|
    const height = this.container.offsetHeight || window.innerHeight;
 | 
						|
    
 | 
						|
    console.log('새 크기:', width, 'x', height);
 | 
						|
    
 | 
						|
    this.camera.aspect = width / height;
 | 
						|
    this.camera.updateProjectionMatrix();
 | 
						|
    this.renderer.setSize(width, height);
 | 
						|
    
 | 
						|
    // 즉시 렌더링 강제
 | 
						|
    this.renderer.render(this.scene, this.camera);
 | 
						|
  }
 | 
						|
 | 
						|
  animate() {
 | 
						|
    // 너무 많은 로그가 발생하므로 첫 10프레임에만 로그 출력
 | 
						|
    if (!this._frameCount) this._frameCount = 0;
 | 
						|
    if (this._frameCount < 10) {
 | 
						|
      console.log(`애니메이션 프레임 #${this._frameCount}`);
 | 
						|
      this._frameCount++;
 | 
						|
    }
 | 
						|
    
 | 
						|
    requestAnimationFrame(this.animate.bind(this));
 | 
						|
    if (this.controls) {
 | 
						|
      this.controls.update();
 | 
						|
    }
 | 
						|
    this.renderer.render(this.scene, this.camera);
 | 
						|
  }
 | 
						|
  
 | 
						|
  // 테스트용 객체 추가
 | 
						|
  addTestObjects() {
 | 
						|
    console.log('테스트 객체 추가 중...');
 | 
						|
    
 | 
						|
    // 그리드 헬퍼 추가
 | 
						|
    const gridHelper = new THREE.GridHelper(20, 20, 0x0000ff, 0x808080);
 | 
						|
    this.scene.add(gridHelper);
 | 
						|
    
 | 
						|
    // 축 헬퍼 추가
 | 
						|
    const axesHelper = new THREE.AxesHelper(10);
 | 
						|
    this.scene.add(axesHelper);
 | 
						|
    
 | 
						|
    // 테스트용 큐브 추가
 | 
						|
    const geometry = new THREE.BoxGeometry(5, 5, 5);
 | 
						|
    const material = new THREE.MeshBasicMaterial({ 
 | 
						|
      color: 0xff0000, 
 | 
						|
      wireframe: true,
 | 
						|
      wireframeLinewidth: 2
 | 
						|
    });
 | 
						|
    const cube = new THREE.Mesh(geometry, material);
 | 
						|
    cube.position.set(0, 2.5, 0); // 그리드 위에 위치
 | 
						|
    this.scene.add(cube);
 | 
						|
    
 | 
						|
    console.log('테스트 객체 추가 완료');
 | 
						|
    
 | 
						|
    // 즉시 렌더링
 | 
						|
    this.renderer.render(this.scene, this.camera);
 | 
						|
  }
 | 
						|
 | 
						|
  loadDXF(url, options = {}) {
 | 
						|
    // 기존 DXF 엔티티 제거
 | 
						|
    this.clearDXF();
 | 
						|
    
 | 
						|
    console.log('DXF 파일 로드 중...', url);
 | 
						|
    
 | 
						|
    // dxf-parser 라이브러리는 'window.DxfParser'로 전역 스코프에 추가됩니다
 | 
						|
    if (!window.DxfParser) {
 | 
						|
      console.error('DxfParser 라이브러리를 찾을 수 없습니다');
 | 
						|
      if (options.onError) options.onError(new Error('DxfParser 라이브러리를 찾을 수 없습니다'));
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    const parser = new window.DxfParser();
 | 
						|
    
 | 
						|
    fetch(url)
 | 
						|
      .then(response => {
 | 
						|
        if (!response.ok) {
 | 
						|
          throw new Error(`HTTP 오류! 상태: ${response.status}`);
 | 
						|
        }
 | 
						|
        console.log('파일 다운로드 성공');
 | 
						|
        return response.text();
 | 
						|
      })
 | 
						|
      .then(data => {
 | 
						|
        console.log('DXF 파일 크기:', data.length, '바이트');
 | 
						|
        try {
 | 
						|
          console.log('DXF 파싱 시작...');
 | 
						|
          const dxf = parser.parseSync(data);
 | 
						|
          console.log('DXF 파싱 완료, 엔티티 수:', dxf.entities ? dxf.entities.length : 0);
 | 
						|
          this.renderDXF(dxf);
 | 
						|
          
 | 
						|
          // 성공 콜백 호출
 | 
						|
          if (options.onSuccess) options.onSuccess(dxf);
 | 
						|
          
 | 
						|
        } catch (error) {
 | 
						|
          console.error('DXF 파싱 오류:', error);
 | 
						|
          if (options.onError) options.onError(error);
 | 
						|
        }
 | 
						|
      })
 | 
						|
      .catch(error => {
 | 
						|
        console.error('DXF 로딩 오류:', error);
 | 
						|
        if (options.onError) options.onError(error);
 | 
						|
      });
 | 
						|
  }
 | 
						|
 | 
						|
  clearDXF() {
 | 
						|
    // 그리드와 조명을 제외한 모든 객체 제거
 | 
						|
    this.scene.children = this.scene.children.filter(child => 
 | 
						|
      child instanceof THREE.GridHelper || 
 | 
						|
      child instanceof THREE.Light
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  renderDXF(dxf) {
 | 
						|
    console.log('DXF 렌더링 시작 - 최신 버전 (캐시버스팅 08:00)...');
 | 
						|
    console.log('이 문구가 콘솔에 표시되면 최신 버전이 로드된 것입니다.');
 | 
						|
    console.log('DXF 객체 내용:', dxf);
 | 
						|
    
 | 
						|
    if (!dxf || !dxf.entities || !Array.isArray(dxf.entities)) {
 | 
						|
      console.error('DXF 파일에 유효한 엔티티가 없습니다.');
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    console.log('엔티티 수:', dxf.entities.length);
 | 
						|
    console.log('엔티티 유형:', dxf.entities.map(e => e.type).filter((v, i, a) => a.indexOf(v) === i));
 | 
						|
    
 | 
						|
    // 범위 계산을 위한 변수 초기화
 | 
						|
    const min = new THREE.Vector3(Infinity, Infinity, Infinity);
 | 
						|
    const max = new THREE.Vector3(-Infinity, -Infinity, -Infinity);
 | 
						|
    
 | 
						|
    // 엔티티 로드
 | 
						|
    let loadedEntityCount = 0;
 | 
						|
    let skippedEntities = 0;
 | 
						|
    let entityTypeCount = {};
 | 
						|
    
 | 
						|
    // 각 엔티티 처리 디버깅 추가
 | 
						|
    dxf.entities.forEach((entity, index) => {
 | 
						|
      if (index < 10) {
 | 
						|
        console.log(`엔티티 ${index} 정보:`, JSON.stringify(entity).substring(0, 200) + '...');
 | 
						|
      }
 | 
						|
      
 | 
						|
      // 엔티티 유형 카운트
 | 
						|
      entityTypeCount[entity.type] = (entityTypeCount[entity.type] || 0) + 1;
 | 
						|
      
 | 
						|
      try {
 | 
						|
        const object = this.createObjectFromEntity(entity);
 | 
						|
        if (object) {
 | 
						|
          this.dxfGroup.add(object);
 | 
						|
          loadedEntityCount++;
 | 
						|
          
 | 
						|
          // 범위 계산 업데이트
 | 
						|
          if (object.geometry) {
 | 
						|
            object.geometry.computeBoundingBox();
 | 
						|
            min.min(object.geometry.boundingBox.min);
 | 
						|
            max.max(object.geometry.boundingBox.max);
 | 
						|
          }
 | 
						|
        } else {
 | 
						|
          skippedEntities++;
 | 
						|
        }
 | 
						|
      } catch (err) {
 | 
						|
        console.error(`엔티티 ${index} (${entity.type}) 처리 오류:`, err);
 | 
						|
        skippedEntities++;
 | 
						|
      }
 | 
						|
    });
 | 
						|
    
 | 
						|
    console.log('로드된 엔티티 수:', loadedEntityCount);
 | 
						|
    console.log('챴너뜨린 엔티티 수:', skippedEntities);
 | 
						|
    console.log('엔티티 유형별 통계:', entityTypeCount);
 | 
						|
    console.log('범위:', 'min:', min.toArray(), 'max:', max.toArray());
 | 
						|
    
 | 
						|
    // 더미 오브젝트 추가 - 테스트용
 | 
						|
    console.log('더미 오브젝트 추가...');
 | 
						|
    const geometry = new THREE.BoxGeometry(10, 10, 10);
 | 
						|
    const material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true });
 | 
						|
    const cube = new THREE.Mesh(geometry, material);
 | 
						|
    this.dxfGroup.add(cube);
 | 
						|
    
 | 
						|
    // 좌표축 생성
 | 
						|
    const axesHelper = new THREE.AxesHelper(20);
 | 
						|
    this.dxfGroup.add(axesHelper);
 | 
						|
    
 | 
						|
    this.scene.add(this.dxfGroup);
 | 
						|
    
 | 
						|
    // 카메라 위치 조정
 | 
						|
    if (loadedEntityCount > 0) {
 | 
						|
      // 바운딩 박스 계산
 | 
						|
      const bbox = {
 | 
						|
        min: new THREE.Vector3(min.x, min.y, min.z),
 | 
						|
        max: new THREE.Vector3(max.x, max.y, max.z)
 | 
						|
      };
 | 
						|
      
 | 
						|
      // 유효한 바운딩 박스인지 확인
 | 
						|
      if (isFinite(min.x) && isFinite(max.x) && isFinite(min.y) && isFinite(max.y)) {
 | 
						|
        this.fitCameraToObject(this.dxfGroup, bbox);
 | 
						|
      } else {
 | 
						|
        console.warn('유효한 범위가 계산되지 않음, 기본 카메라 뷰 사용');
 | 
						|
        // 기본 뷰 사용
 | 
						|
        this.camera.position.set(50, 50, 50);
 | 
						|
        this.camera.lookAt(0, 0, 0);
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      // 엔티티가 없는 경우 기본 뷰 사용
 | 
						|
      this.camera.position.set(50, 50, 50);
 | 
						|
      this.camera.lookAt(0, 0, 0);
 | 
						|
    }
 | 
						|
    
 | 
						|
    console.log('DXF 렌더링 완료');
 | 
						|
  }
 | 
						|
 | 
						|
  getBoundingBox(entities) {
 | 
						|
    // DXF 엔티티의 바운딩 박스 계산
 | 
						|
    const bbox = {
 | 
						|
      min: { x: Infinity, y: Infinity, z: Infinity },
 | 
						|
      max: { x: -Infinity, y: -Infinity, z: -Infinity }
 | 
						|
    };
 | 
						|
    
 | 
						|
    entities.forEach(entity => {
 | 
						|
      if (entity.vertices) {
 | 
						|
        entity.vertices.forEach(vertex => {
 | 
						|
          bbox.min.x = Math.min(bbox.min.x, vertex.x);
 | 
						|
          bbox.min.y = Math.min(bbox.min.y, vertex.y);
 | 
						|
          bbox.min.z = Math.min(bbox.min.z, vertex.z || 0);
 | 
						|
          
 | 
						|
          bbox.max.x = Math.max(bbox.max.x, vertex.x);
 | 
						|
          bbox.max.y = Math.max(bbox.max.y, vertex.y);
 | 
						|
          bbox.max.z = Math.max(bbox.max.z, vertex.z || 0);
 | 
						|
        });
 | 
						|
      } else if (entity.position) {
 | 
						|
        bbox.min.x = Math.min(bbox.min.x, entity.position.x);
 | 
						|
        bbox.min.y = Math.min(bbox.min.y, entity.position.y);
 | 
						|
        bbox.min.z = Math.min(bbox.min.z, entity.position.z || 0);
 | 
						|
        
 | 
						|
        bbox.max.x = Math.max(bbox.max.x, entity.position.x);
 | 
						|
        bbox.max.y = Math.max(bbox.max.y, entity.position.y);
 | 
						|
        bbox.max.z = Math.max(bbox.max.z, entity.position.z || 0);
 | 
						|
      }
 | 
						|
    });
 | 
						|
    
 | 
						|
    // 바운딩 박스가 너무 작은 경우 기본값 설정
 | 
						|
    if (!isFinite(bbox.min.x)) {
 | 
						|
      bbox.min = { x: -10, y: -10, z: -10 };
 | 
						|
      bbox.max = { x: 10, y: 10, z: 10 };
 | 
						|
    }
 | 
						|
    
 | 
						|
    return bbox;
 | 
						|
  }
 | 
						|
 | 
						|
  createObjectFromEntity(entity) {
 | 
						|
    const material = new THREE.LineBasicMaterial({ color: 0x0000ff });
 | 
						|
    
 | 
						|
    switch (entity.type) {
 | 
						|
      case 'LINE':
 | 
						|
        return this.createLine(entity, material);
 | 
						|
      
 | 
						|
      case 'CIRCLE':
 | 
						|
        return this.createCircle(entity, material);
 | 
						|
        
 | 
						|
      case 'ARC':
 | 
						|
        return this.createArc(entity, material);
 | 
						|
        
 | 
						|
      case 'POLYLINE':
 | 
						|
      case 'LWPOLYLINE':
 | 
						|
        return this.createPolyline(entity, material);
 | 
						|
      
 | 
						|
      case 'TEXT':
 | 
						|
        // 텍스트는 현재 구현하지 않음
 | 
						|
        return null;
 | 
						|
        
 | 
						|
      default:
 | 
						|
        console.log('미지원 엔티티 타입:', entity.type);
 | 
						|
        return null;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  createLine(entity, material) {
 | 
						|
    const geometry = new THREE.BufferGeometry().setFromPoints([
 | 
						|
      new THREE.Vector3(entity.vertices[0].x, entity.vertices[0].y, entity.vertices[0].z || 0),
 | 
						|
      new THREE.Vector3(entity.vertices[1].x, entity.vertices[1].y, entity.vertices[1].z || 0)
 | 
						|
    ]);
 | 
						|
    
 | 
						|
    return new THREE.Line(geometry, material);
 | 
						|
  }
 | 
						|
 | 
						|
  createCircle(entity, material) {
 | 
						|
    const curve = new THREE.EllipseCurve(
 | 
						|
      entity.center.x, entity.center.y,
 | 
						|
      entity.radius, entity.radius,
 | 
						|
      0, 2 * Math.PI,
 | 
						|
      false,
 | 
						|
      0
 | 
						|
    );
 | 
						|
    
 | 
						|
    const points = curve.getPoints(50);
 | 
						|
    const geometry = new THREE.BufferGeometry().setFromPoints(points);
 | 
						|
    
 | 
						|
    return new THREE.Line(geometry, material);
 | 
						|
  }
 | 
						|
 | 
						|
  createArc(entity, material) {
 | 
						|
    const startAngle = entity.startAngle;
 | 
						|
    const endAngle = entity.endAngle;
 | 
						|
    
 | 
						|
    const curve = new THREE.EllipseCurve(
 | 
						|
      entity.center.x, entity.center.y,
 | 
						|
      entity.radius, entity.radius,
 | 
						|
      startAngle, endAngle,
 | 
						|
      false,
 | 
						|
      0
 | 
						|
    );
 | 
						|
    
 | 
						|
    const points = curve.getPoints(50);
 | 
						|
    const geometry = new THREE.BufferGeometry().setFromPoints(points);
 | 
						|
    
 | 
						|
    return new THREE.Line(geometry, material);
 | 
						|
  }
 | 
						|
 | 
						|
  createPolyline(entity, material) {
 | 
						|
    const points = entity.vertices.map(vertex => 
 | 
						|
      new THREE.Vector3(vertex.x, vertex.y, vertex.z || 0)
 | 
						|
    );
 | 
						|
    
 | 
						|
    const geometry = new THREE.BufferGeometry().setFromPoints(points);
 | 
						|
    
 | 
						|
    return new THREE.Line(geometry, material);
 | 
						|
  }
 | 
						|
 | 
						|
  fitCameraToObject(object, bbox) {
 | 
						|
    // 바운딩 박스 안전 검사
 | 
						|
    if (!bbox || !bbox.min || !bbox.max) {
 | 
						|
      console.warn('유효한 바운딩 박스가 없습니다. 기본 카메라 뷰 사용');
 | 
						|
      // 기본 카메라 위치 사용
 | 
						|
      this.camera.position.set(50, 50, 50);
 | 
						|
      this.camera.lookAt(0, 0, 0);
 | 
						|
      if (this.controls) {
 | 
						|
        this.controls.target.set(0, 0, 0);
 | 
						|
        this.controls.update();
 | 
						|
      }
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    // controls가 없는 경우 처리
 | 
						|
    if (!this.controls) {
 | 
						|
      console.warn('OrbitControls가 정의되지 않았습니다. 기본 카메라 설정 사용');
 | 
						|
      this.camera.position.set(bbox.max.x, bbox.max.y, bbox.max.z * 3);
 | 
						|
      this.camera.lookAt(new THREE.Vector3(
 | 
						|
        (bbox.min.x + bbox.max.x) / 2,
 | 
						|
        (bbox.min.y + bbox.max.y) / 2,
 | 
						|
        (bbox.min.z + bbox.max.z) / 2
 | 
						|
      ));
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    const size = new THREE.Vector3(
 | 
						|
      bbox.max.x - bbox.min.x,
 | 
						|
      bbox.max.y - bbox.min.y,
 | 
						|
      bbox.max.z - bbox.min.z
 | 
						|
    );
 | 
						|
    
 | 
						|
    const center = new THREE.Vector3(
 | 
						|
      (bbox.min.x + bbox.max.x) / 2,
 | 
						|
      (bbox.min.y + bbox.max.y) / 2,
 | 
						|
      (bbox.min.z + bbox.max.z) / 2
 | 
						|
    );
 | 
						|
    
 | 
						|
    // 객체가 너무 작거나 큰 경우 조정
 | 
						|
    const maxSize = Math.max(size.x, size.y, size.z);
 | 
						|
    const fitHeightDistance = maxSize / (2 * Math.atan(Math.PI * this.camera.fov / 360));
 | 
						|
    const fitWidthDistance = fitHeightDistance / this.camera.aspect;
 | 
						|
    const distance = 1.2 * Math.max(fitHeightDistance, fitWidthDistance);
 | 
						|
    
 | 
						|
    // 카메라 위치 설정
 | 
						|
    const direction = this.controls.target.clone()
 | 
						|
      .sub(this.camera.position)
 | 
						|
      .normalize()
 | 
						|
      .multiplyScalar(distance);
 | 
						|
    
 | 
						|
    this.controls.maxDistance = distance * 10;
 | 
						|
    this.camera.position.copy(center).sub(direction);
 | 
						|
    this.controls.target.copy(center);
 | 
						|
    
 | 
						|
    // 컨트롤 업데이트
 | 
						|
    this.camera.near = distance / 100;
 | 
						|
    this.camera.far = distance * 100;
 | 
						|
    this.camera.updateProjectionMatrix();
 | 
						|
    
 | 
						|
    this.controls.update();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// 페이지 초기화
 | 
						|
document.addEventListener('DOMContentLoaded', () => {
 | 
						|
  // 디버깅 정보 출력
 | 
						|
  console.log('DXF 뷰어 초기화 시작');
 | 
						|
  
 | 
						|
  // 지정된 컨테이너를 찾아서 뷰어 초기화
 | 
						|
  const container = document.getElementById('dxf-viewer-container');
 | 
						|
  if (!container) {
 | 
						|
    console.error('dxf-viewer-container를 찾을 수 없습니다');
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  
 | 
						|
  console.log('컨테이너를 찾음:', container);
 | 
						|
  const viewer = new DXFViewer(container);
 | 
						|
  
 | 
						|
  // 다른 뷰어 데이터 소스 시도
 | 
						|
  const viewerData = document.getElementById('dxf-viewer-data');
 | 
						|
  if (viewerData && viewerData.getAttribute('data-url')) {
 | 
						|
    const fileURL = viewerData.getAttribute('data-url');
 | 
						|
    console.log('dxf-viewer-data에서 URL 찾음:', fileURL);
 | 
						|
    viewer.loadDXF(fileURL);
 | 
						|
  }
 | 
						|
 | 
						|
  // dxf-file-url 메타 태그에서 URL 가져오기
 | 
						|
  console.log('메타 태그에서 DXF 파일 URL 검색 중...');
 | 
						|
  const fileURLMeta = document.querySelector('meta[name="dxf-file-url"]');
 | 
						|
  
 | 
						|
  if (fileURLMeta) {
 | 
						|
    const fileURL = fileURLMeta.getAttribute('content');
 | 
						|
    console.log('메타 태그에서 찾은 URL:', fileURL);
 | 
						|
    
 | 
						|
    if (fileURL && fileURL.trim() !== '') {
 | 
						|
      console.log('유효한 URL 발견, DXF 파일 로드 시작');
 | 
						|
      
 | 
						|
      // URL을 사용하여 DXF 파일 로드
 | 
						|
      viewer.loadDXF(fileURL, {
 | 
						|
        onSuccess: (dxf) => {
 | 
						|
          console.log('DXF 파일 로드 성공:', fileURL);
 | 
						|
          console.log('DXF 엔티티 수:', dxf.entities ? dxf.entities.length : 0);
 | 
						|
        },
 | 
						|
        onError: (error) => {
 | 
						|
          console.error('DXF 파일 로드 실패:', error);
 | 
						|
          // 실패 시 추가적인 방법 시도
 | 
						|
          console.log('대체 URL 형식으로 시도 중...');
 | 
						|
          
 | 
						|
          // 기본 URL에서 구성된 대체 URL 생성
 | 
						|
          const commitIdMeta = document.querySelector('meta[name="dxf-commit-id"]');
 | 
						|
          const repoPathMeta = document.querySelector('meta[name="dxf-repo-path"]');
 | 
						|
          const repoLinkMeta = document.querySelector('meta[name="dxf-repo-link"]');
 | 
						|
          
 | 
						|
          if (commitIdMeta && repoPathMeta && repoLinkMeta) {
 | 
						|
            const commitId = commitIdMeta.getAttribute('content');
 | 
						|
            const repoPath = repoPathMeta.getAttribute('content');
 | 
						|
            const repoLink = repoLinkMeta.getAttribute('content');
 | 
						|
            
 | 
						|
            if (commitId && repoPath && repoLink) {
 | 
						|
              const alternativeURL = `/api/v1/repos${repoLink}/raw/${repoPath}?ref=${commitId}`;
 | 
						|
              console.log('대체 URL:', alternativeURL);
 | 
						|
              
 | 
						|
              // 대체 URL로 로드 시도
 | 
						|
              viewer.loadDXF(alternativeURL, {
 | 
						|
                onSuccess: (dxf) => console.log('대체 URL 성공:', dxf.entities ? dxf.entities.length : 0),
 | 
						|
                onError: (err) => console.error('대체 URL 로드 실패:', err)
 | 
						|
              });
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
      });
 | 
						|
    } else {
 | 
						|
      console.error('dxf-file-url 메타 태그의 content가 비어 있습니다');
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    console.error('dxf-file-url 메타 태그를 찾을 수 없습니다');
 | 
						|
  }
 | 
						|
});
 |