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 메타 태그를 찾을 수 없습니다');
|
||
|
|
}
|
||
|
|
});
|