BLOCK7 원본 구조선과 표고 재분석 및 불일치 기록

This commit is contained in:
김지영 committed 2026-10-20 10:51:46 +09:00
1 parent 100480b5e7
commit 39231d52c1
8 files changed
+149 -61

No files matched your search

+2
View File
@@ -2,3 +2,5 @@ node_modules
dist
.DS_Store
*.local
docs/extracted/
+47 -61
View File
@@ -1,71 +1,57 @@
# 광터지하차도 BLOCK7 원본 도면 1차 분석
# BLOCK7 원본 재분석 및 모델 적용 근거
## 분석 목적과 파일
원본: `public/drawings/광터지하차도_일반도(BLOCK7).dxf`. 분석 재현: `python3 scripts/analyze_dxf.py`. 이전 문서의 가정과 사용자 지시는 이번 모델의 확정 근거로 사용하지 않는다.
이번 프로젝트는 기존 3D 생성 코드를 가져오지 않고 원본 DXF에서 다시 시작한다. 기존 프로젝트의 분석은 참고 문서(`참고_기존도면분석.md`)에 보관하며, 아래 값은 새 프로젝트에서 원본 DXF를 다시 읽어 확인한 결과다.
## 원본과 도면 연결
| 항목 | 확인 결과 | 상태 |
|---|---|---|
| 원본 | `public/drawings/광터지하차도_일반도(BLOCK7).dxf` | 보유 |
| 형식/문자 코드 | DXF AC1027, ANSI_949 | 파일 헤더에서 확인 |
| `$INSUNITS` | 0 (미지정) | 단위 미확정. 도면 치수·측점·표고와 대조해 환산 필요 |
| 도면 구성 | 일반도 13번·14번에 평면·상부평면·종단면·단면·상세 포함 | 제목 문자에서 확인 |
- AC1027 DXF이며 본문 문자는 UTF-8로 정상 판독된다. 헤더의 ANSI_949와 달리 CP949로 읽으면 한글이 깨진다. `$INSUNITS=0`은 단위 미지정이다.
- 일반도 13: 평면·상부평면·종단 S=1:100. 일반도 14: A-A/B-B S=1:100, 측구 S=1:30, 시공이음 등 S=1:20. 축척은 표현 축척이며 모델 좌표에 100을 다시 곱하지 않는다.
- 14번 단면 폭 치수열 800+8.750+1.000+8.750+800과 구조선 X 차 20100을 대조하면 CAD 1000단위=1m가 된다. 상·하단 EL 차와 Y 차도 같은 환산에 일치한다.
- A-A/B-B는 제목·상단 EL·하단 EL·동일 X 치수열로 대응시켰다. 단면 측점은 299.000/319.000, 종단면 구조 경계 측점은 299.010/318.990이다. 각각 10mm 어긋나며 신축이음 등 경계 기준의 차이인지 확정되지 않았다.
- 평면의 BLOCK7 표기 주변 CS-CONC-MAJR 선 21835/21836은 각각 20.100m이다. 단면 폭도 20.100m여서 이전 문서의 종방향 중심선 판단은 근거가 부족하다. 선형중심 문자는 (1069233.4121,-81231.4642), (1034233.4121,-81231.4642)에 있으나 대응 엔티티와 절단선은 미확정이다. 이 후보 방향을 모델 중심선으로 적용하지 않는다.
- 상부평면과 평면을 포함한 원본 검토 SVG를 제공한다. SVG는 LINE·TEXT·공동구 LINE 발췌이며 전체 CAD 렌더러가 아니다. 다른 블록·해칭·폴리라인·기호는 원본 DXF에서 확인해야 한다.
## 시트와 축척
## 적용값 표
| 도면 | 표기 축척 | 사용할 근거 |
|---|---:|---|
| 일반도 13번 평면도 | 1:100 | 평면 배치와 BLOCK7 선형 후보 확인 |
| 일반도 13번 상부평면도 | 1:100 | 상부 외곽과 평면 배치의 교차 확인 |
| 일반도 13번 종단면도 | 1:100 | 측점, 바닥고, 벽체 상단 EL의 길이방향 기준 |
| 일반도 14번 단면 A-A | 1:100 | 시점 단면의 구조 윤곽·치수·EL |
| 일반도 14번 단면 B-B | 1:100 | 종점 단면의 구조 윤곽·치수·EL |
| 일반도 14번 측구 상세 | 1:30 | 측구 단면과 표기 치수 |
| 일반도 14번 기타 상세 | 1:20 표기 확인 | BLOCK7 적용 대상인지 상세 위치로 대조 전까지 모델에 추가하지 않음 |
| 대상 | 값 | 상태 | 도면 번호·도면 내 위치와 근거 |
|---|---|---|---|
| 구조 경계 측점 | 299.010 / 318.990m | 도면 명시값 | 13 종단, 문자 (1050295.169,-97151.037)/(1073692.169,-97151.037) |
| 경계 사이 거리 | 19.980m | 계산으로 도출한 값 | 위 두 측점 차; 단면 연결에 사용할 경우 배치 확인 필요 |
| 단면 자체 측점 | 299.000 / 319.000m | 도면 명시값 | 14 A-A/B-B 중앙 STA 문자; 종단 경계와 10mm 차이 |
| 단면 원점 X | 1128486.218066491 | 계산으로 도출한 값 | 14 폭 치수 20.100의 양끝 중앙 |
| A-A 상단/하단 | EL 106.675 / 101.636m | 도면 명시값 | 14 A-A 좌측 EL 문자 |
| B-B 상단/하단 | EL 107.307 / 101.069m | 도면 명시값 | 14 B-B 좌측 EL 문자 |
| 상단 CAD Y | A -74271.37855593952 / B -91211.37855593952 | 계산으로 도출한 값 | 14 두 벽체 상단 수평선과 EL 표기의 대응 |
| 벽체 전체 높이 | A 5.039m / B 6.238m | 계산으로 도출한 값 | 각 상·하단 차; 바닥판을 포함한 높이 |
| 종단 상면 표기 | A 102.436 / B 101.869m | 도면 명시값 | 13 종단 좌우 EL 문자 |
| 상면-하단 차 | 0.800m | 계산으로 도출한 값 | 위 종단 EL과 14 하단 EL 차 |
| 폭 치수열 | 0.800+8.750+1.000+8.750+0.800=20.100m | 도면 명시값 | 14 단면 아래 치수열 |
| 가장 바깥 보조선 폭 | 20.300m | 계산으로 도출한 값 | 14 좌표 측정 X=중심±10150; 표기 폭 20.100과 기준이 다름 |
| 횡단경사 | 양쪽 바깥으로 -2% | 도면 명시값・계산으로 도출한 값 | 14 S=-2.000% 및 포장선 X ±0.5→±7.75에서 Y 145mm 하강 / 7250mm |
| 단면 중심 기준 EL | 미확정 | 확인 필요 | 벽체 상단 EL을 도로 중심 EL로 대체하지 않음 |
| 공동구·상부 상세 | 폭 경계 1.000m, 전체 높이 경계 2.100m | 계산으로 도출한 값 | 14 블록 A$C2017021411375356, 로컬 X -500~500/Y -900~1200. 좌표 측정이며 명시 치수 아님 |
| 공동구 좌우 위치 | 중심에서 ±8.750m | 계산으로 도출한 값 | 14 네 INSERT의 X 좌표, 우측은 X반전 |
| 공동구 기준 Y | A -77447.89255584464 / B -95669.3155558447 | 계산으로 도출한 값 | 14 INSERT 원점. 선·반원 전체를 원형대로 변환 |
| 측구 단면 내부 폭 | 0.400m | 계산으로 도출한 값 | 14 단면 CS-CONC-MAJR X ±7.8~±8.2 좌표 측정 |
| 측구 상세 표기 | 50 / 200 등 | 도면 명시값 | 14 측구 상세, S=1:30。단면 내 깊이와 같다고 단정하지 않음 |
| 평면 중심선·단면 방향 | 미확정 | 확인 필요 | 13 평면·상부평면의 절단 위치와 엔티티 대응 미완료 |
| 중간 단면·연장 | 미확정 | 확인 필요 | 두 단면 사이 선형 보간은 별도 가정, 실제 중간 형상 검증 안 됨 |
## 기준 측점·표고·치수
## 구조선 대조
| 대상/값 | 값 | 상태 | 근거와 위치 |
|---|---:|---|---|
| A-A 측점 | STA. 0K+299.010 | 도면 명시값 | 일반도 13번 종단면도 시작 측점 및 14번 A-A 대응 |
| B-B 측점 | STA. 0K+318.990 | 도면 명시값 | 일반도 13번 종단면도 끝 측점 및 14번 B-B 대응 |
| A-A~B-B 길이 | 19.980m | 계산으로 도출한 값 | 두 측점 차 `318.990 - 299.010` |
| A-A 벽체 상단/하단 | EL. 106.675 / 101.636 | 도면 명시값 | 일반도 14번 단면 A-A |
| B-B 벽체 상단/하단 | EL. 107.307 / 101.069 | 도면 명시값 | 일반도 14번 단면 B-B |
| A-A 벽체 높이 | 5.039m | 계산으로 도출한 값 | 상·하단 EL 차 |
| B-B 벽체 높이 | 6.238m | 계산으로 도출한 값 | 상·하단 EL 차 |
| 단면 주요 폭 치수열 | 0.800 + 8.750 + 1.000 + 8.750 + 0.800 = 20.100m | 도면 표기값·계산 | 일반도 14번 A-A/B-B 폭 치수. 20.100m가 가장 바깥 CAD 선의 전체 폭과 같은 기준인지는 추가 대조 필요 |
| 종단 별도 구간 표기 | L=19.882m 등 | 확인 필요 | 측점 차 19.980m와 다른 기준점 또는 구간일 수 있어 A-A~B-B 모델 길이로 사용하지 않음 |
| 횡단 경사 | S=-2.000% | 도면 명시값 | 평면·단면 주변 주기. 낮아지는 방향은 도면 방향과 좌표를 교차 확인해야 함 |
| 측구 표기 치수 | 50, 200 (도면 단위 확인 필요) | 도면 명시값/단위 확인 필요 | 일반도 14번 측구 상세. 상세 축척으로 잰 값과 별도 관리 |
각 단면의 X 1118000~1139000, 상단 Y부터 아래 7500·위 100 범위에 양 끝이 들어오는 LINE 중 두 레이어를 추출했다. 이 범위는 주요 단면과 하부 100mm 보조 윤곽을 포함한다.
## 같은 구조물을 도면 간 연결하는 방법
- A-A/B-B 각각 MAJR 108개 + MINR 6개 = 114개. 기존 90+6 기록에는 하부·벽체 등의 선이 빠져 있었다.
- 각 대응 선의 레이어와 X 끝점이 일치한다. 원본 HANDLE을 모델에 저장해 다시 찾을 수 있다.
- 포장 반복선·겹친 선도 삭제하지 않는다. 반복선을 모두 실제 콘크리트 표면으로 판단하지 않으며 기본 보기에서는 끌 수 있는 별도 그룹이다.
- 좌우 공동구·상부 상세는 블록당 LINE 18개+ARC 2개. 우측 X반전, 각 단면 INSERT Y를 적용한다. 반원은 36분할 표시하므로 현 높이 오차는 약 0.03mm 이하이다. 임의 100mm 벽두께를 만들지 않는다.
- 원본 선 기반 객체 154개(단면 LINE 114쌍, 블록 엔티티 20쌍×좌우). 부재 체적이 아니라 대응 윤곽 객체의 수이다.
- 평면도에서 BLOCK7 표기와 종방향 구조 윤곽 및 단면 절단 표식을 식별한다.
- 평면도 A-A/B-B 위치를 종단면도의 STA. 0K+299.010 / 0K+318.990과 대조한다.
- 해당 측점에 일반도 14번 단면 A-A/B-B를 대응시키고, 상·하단 EL 및 폭 치수를 확인한다.
- 측구 상세는 단면 하부의 측구 후보와 위치·폭·깊이·방향을 비교한 뒤 적용 범위를 확정한다.
- `CS-CONC-MAJR`, `CS-CONC-MINR`는 모델 윤곽 후보 레이어다. 해당 레이어의 선 전부를 구조 경계로 단정하지 않고, 치수선·해칭·중복선 여부를 시트 화면 및 주변 치수와 구분한다.
## 확인 항목과 모델 제한
## 평면 선형 1차 판독
평면도에서 `(BLOCK7)` 표기와 폭 치수열 부근의 `CS-CONC-MAJR` 종방향 선 후보를 찾았다. 후보 끝 좌표는 A-A측 `(1055963.4484, -71293.2356)`, B-B측 `(1056081.6830, -91392.8878)`이고, CAD 좌표 길이는 약 20.100m다. 종단 측점 차 19.980m보다 0.120m 길다.
이 선은 DXF에서 직선 엔티티로 저장되어 있고, 이 구간을 따라가는 중간 원호나 추가 곡선점을 아직 확인하지 못했다. 따라서 이 선을 실제 평면 선형 중심선으로 확정하지 않는다. 별도의 `선형중심` 문자도 있으나, BLOCK7 구간 후보 및 A-A/B-B 절단 위치와 같은 선을 가리키는지 도면 화면에서 대조가 필요하다. 기존 프로젝트의 모델 방향을 그대로 복사하지 않고 이 대응을 새로 확인한다.
## 정보 부족·오류 후보
- DXF 헤더 단위는 미지정이므로 CAD 좌표를 실제 단위로 그대로 간주할 수 없다.
- 기준선 후보의 길이 20.100m와 측점 차 19.980m가 다르다. 기준점, 선의 종류, 치수 보조선 끝을 확인해야 한다.
- 단면 좌표 범위에는 주 구조선·반복선·상세선이 함께 있어 좌표창만으로 선 개수를 확정할 수 없다. A-A/B-B별 엔티티를 도면 창 기준으로 정밀하게 선별하고 중복·해칭을 판별해야 한다.
- 벽체 상단 EL이 종단면 시작·끝과 단면 A-A/B-B에서 일치하는지 표기 위치별로 최종 대조한다.
- 0.800m 치수의 부재 기준, 공동구 외곽의 상세 적용, 측구의 BLOCK7 구간 연장 범위는 추가 확인이 필요하다.
- 평면 중심선의 곡률 자료가 원본에 없거나 판독되지 않으면 임의 곡선을 만들지 않고 사용자 확인 대상으로 둔다.
## 다음 모델링 단계
1. 평면도에서 BLOCK7 중심선 후보와 A-A/B-B 절단 위치를 화면 및 DXF 엔티티로 확정한다.
2. 단면 A-A/B-B에서 두 콘크리트 레이어를 부재·해칭별로 나누고 실제 윤곽과 치수열을 대조한다.
3. 치수와 근거가 연결된 주요 단면 형상부터 별도 객체로 생성한다.
4. 중간 구간 정보가 없는 표면은 추정 여부를 표시하고, 닫힌 체적처럼 오해되지 않게 한다.
5. 주요 치수·표고·부재 배치를 원도면과 대조하고 결과를 기록한다.
1. 평면 선형중심에 해당하는 엔티티·A/B 절단 위치·중심 EL이 미확정이다. 확인되지 않은 위치·곡률을 도면 확정값으로 만들지 않는다.
2. 단면 자체 측점 20.000m 간격과 종단 구조 경계 19.980m 차이의 원인을 확인해야 한다.
3. 종단 벽체 상단 EL 106.675/107.307은 단면 상단 표기와 각각 일치한다. 도로 중심 EL과의 일치는 검증하지 못했다.
4. 포장 반복선, 하부·외측 보조선의 재료와 체적 경계 해석은 미검증이다. 닫힌 체적·수량 산출용 모델로 해석하지 않는다.
5. 측구 상세 S=1:30은 별도 검토 자료로 제공한다. 단면 측구와의 깊이·시종점 적용은 미확정이며 상세 치수로 단면을 임의 교체하지 않는다.
6. 철근, 지반, 되메우기, 이음 상세의 구간 적용, 재료·정착 상세는 모델링하지 않았다. 공동구 블록의 가는 상부 윤곽은 존재하는 그대로 보존하며 근거 없는 금속 난간을 추가하지 않는다.
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="1113000 69500 29000 14800"><rect x="1113000" y="69500" width="29000" height="14800" fill="#fff"/><line x1="1127986.218066491" y1="78202.89240393949" x2="1128986.218066491" y2="78202.89240393949" stroke="#8d969e" stroke-width="15"><title>16483 / CS-CNTL</title></line><line x1="1128486.218066491" y1="78702.89240393949" x2="1128486.218066491" y2="77702.89240393949" stroke="#8d969e" stroke-width="15"><title>16484 / CS-CNTL</title></line><line x1="1118436.218066491" y1="79310.37855593952" x2="1119236.218066491" y2="79310.37855593952" stroke="#326c5d" stroke-width="15"><title>21420 / CS-CONC-MAJR</title></line><line x1="1119236.218066491" y1="79310.37855593952" x2="1127986.218066491" y2="79310.37855593952" stroke="#326c5d" stroke-width="15"><title>21421 / CS-CONC-MAJR</title></line><line x1="1127986.218066491" y1="79310.37855593952" x2="1128986.218066491" y2="79310.37855593952" stroke="#326c5d" stroke-width="15"><title>21422 / CS-CONC-MAJR</title></line><line x1="1128986.218066491" y1="79310.37855593952" x2="1137736.218066491" y2="79310.37855593952" stroke="#326c5d" stroke-width="15"><title>21423 / CS-CONC-MAJR</title></line><line x1="1137736.218066491" y1="79310.37855593952" x2="1138536.218066491" y2="79310.37855593952" stroke="#326c5d" stroke-width="15"><title>21424 / CS-CONC-MAJR</title></line><line x1="1120236.218066491" y1="78397.89255593948" x2="1120286.218066491" y2="78397.89255593948" stroke="#326c5d" stroke-width="15"><title>21425 / CS-CONC-MAJR</title></line><line x1="1120286.218066491" y1="78397.89255593948" x2="1120286.218066491" y2="78710.37855593952" stroke="#326c5d" stroke-width="15"><title>21426 / CS-CONC-MAJR</title></line><line x1="1120286.218066491" y1="78710.37855593952" x2="1120686.218066491" y2="78710.37855593952" stroke="#326c5d" stroke-width="15"><title>21427 / CS-CONC-MAJR</title></line><line x1="1120686.218066491" y1="78710.37855593952" x2="1120686.218066491" y2="78397.89255593948" stroke="#326c5d" stroke-width="15"><title>21428 / CS-CONC-MAJR</title></line><line x1="1120686.218066491" y1="78397.89255593948" x2="1120736.218066491" y2="78397.89255593948" stroke="#326c5d" stroke-width="15"><title>21429 / CS-CONC-MAJR</title></line><line x1="1120736.218066491" y1="78397.89255593948" x2="1120736.218066491" y2="78347.89255593948" stroke="#326c5d" stroke-width="15"><title>21430 / CS-CONC-MAJR</title></line><line x1="1120736.218066491" y1="78347.89255593948" x2="1120736.218066491" y2="78427.89255593948" stroke="#326c5d" stroke-width="15"><title>21431 / CS-CONC-MAJR</title></line><line x1="1120736.218066491" y1="78427.89255593948" x2="1120986.218066491" y2="78422.89255593948" stroke="#326c5d" stroke-width="15"><title>21432 / CS-CONC-MAJR</title></line><line x1="1120986.218066491" y1="78422.89255593948" x2="1127486.218066491" y2="78292.89255593948" stroke="#326c5d" stroke-width="15"><title>21433 / CS-CONC-MAJR</title></line><line x1="1127486.218066491" y1="78292.89255593948" x2="1127986.218066491" y2="78282.89255593948" stroke="#326c5d" stroke-width="15"><title>21434 / CS-CONC-MAJR</title></line><line x1="1127986.218066491" y1="78282.89255593948" x2="1127986.218066491" y2="78510.37855593952" stroke="#326c5d" stroke-width="15"><title>21435 / CS-CONC-MAJR</title></line><line x1="1136236.218066491" y1="78347.89255593948" x2="1136236.218066491" y2="78427.89255593948" stroke="#326c5d" stroke-width="15"><title>21436 / CS-CONC-MAJR</title></line><line x1="1136236.218066491" y1="78347.89255593948" x2="1136236.218066491" y2="78397.89255593948" stroke="#326c5d" stroke-width="15"><title>21437 / CS-CONC-MAJR</title></line><line x1="1136236.218066491" y1="78397.89255593948" x2="1136286.218066491" y2="78397.89255593948" stroke="#326c5d" stroke-width="15"><title>21438 / CS-CONC-MAJR</title></line><line x1="1136286.218066491" y1="78397.89255593948" x2="1136286.218066491" y2="78710.37855593952" stroke="#326c5d" stroke-width="15"><title>21439 / CS-CONC-MAJR</title></line><line x1="1136286.218066491" y1="78710.37855593952" x2="1136686.218066491" y2="78710.37855593952" stroke="#326c5d" stroke-width="15"><title>21440 / CS-CONC-MAJR</title></line><line x1="1136686.218066491" y1="78710.37855593952" x2="1136686.218066491" y2="78397.89255593948" stroke="#326c5d" stroke-width="15"><title>21441 / CS-CONC-MAJR</title></line><line x1="1136686.218066491" y1="78397.89255593948" x2="1136736.218066491" y2="78397.89255593948" stroke="#326c5d" stroke-width="15"><title>21442 / CS-CONC-MAJR</title></line><line x1="1128986.218066491" y1="78510.37855593952" x2="1128986.218066491" y2="78282.89255593948" stroke="#326c5d" stroke-width="15"><title>21443 / CS-CONC-MAJR</title></line><line x1="1128986.218066491" y1="78282.89255593948" x2="1129486.218066491" y2="78292.89255593948" stroke="#326c5d" stroke-width="15"><title>21444 / CS-CONC-MAJR</title></line><line x1="1129486.218066491" y1="78292.89255593948" x2="1135986.218066491" y2="78422.89255593948" stroke="#326c5d" stroke-width="15"><titlLine truncated

After

Width:  |  Height:  |  Size: 53 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="1113000 85500 29000 16800"><rect x="1113000" y="85500" width="29000" height="16800" fill="#fff"/><line x1="1127986.218066491" y1="96424.31539693956" x2="1128986.218066491" y2="96424.31539693956" stroke="#8d969e" stroke-width="15"><title>16485 / CS-CNTL</title></line><line x1="1128486.218066491" y1="96924.31539693956" x2="1128486.218066491" y2="95924.31539693956" stroke="#8d969e" stroke-width="15"><title>16486 / CS-CNTL</title></line><line x1="1118436.218066491" y1="97449.37855593952" x2="1119236.218066491" y2="97449.37855593952" stroke="#326c5d" stroke-width="15"><title>21624 / CS-CONC-MAJR</title></line><line x1="1119236.218066491" y1="97449.37855593952" x2="1127986.218066491" y2="97449.37855593952" stroke="#326c5d" stroke-width="15"><title>21625 / CS-CONC-MAJR</title></line><line x1="1127986.218066491" y1="97449.37855593952" x2="1128986.218066491" y2="97449.37855593952" stroke="#326c5d" stroke-width="15"><title>21626 / CS-CONC-MAJR</title></line><line x1="1128986.218066491" y1="97449.37855593952" x2="1137736.218066491" y2="97449.37855593952" stroke="#326c5d" stroke-width="15"><title>21627 / CS-CONC-MAJR</title></line><line x1="1137736.218066491" y1="97449.37855593952" x2="1138536.218066491" y2="97449.37855593952" stroke="#326c5d" stroke-width="15"><title>21628 / CS-CONC-MAJR</title></line><line x1="1120236.218066491" y1="96619.31555593957" x2="1120286.218066491" y2="96619.31555593957" stroke="#326c5d" stroke-width="15"><title>21629 / CS-CONC-MAJR</title></line><line x1="1120286.218066491" y1="96619.31555593957" x2="1120286.218066491" y2="96849.37855593952" stroke="#326c5d" stroke-width="15"><title>21630 / CS-CONC-MAJR</title></line><line x1="1120286.218066491" y1="96849.37855593952" x2="1120686.218066491" y2="96849.37855593952" stroke="#326c5d" stroke-width="15"><title>21631 / CS-CONC-MAJR</title></line><line x1="1120686.218066491" y1="96849.37855593952" x2="1120686.218066491" y2="96619.31555593957" stroke="#326c5d" stroke-width="15"><title>21632 / CS-CONC-MAJR</title></line><line x1="1120686.218066491" y1="96619.31555593957" x2="1120736.218066491" y2="96619.31555593957" stroke="#326c5d" stroke-width="15"><title>21633 / CS-CONC-MAJR</title></line><line x1="1120736.218066491" y1="96619.31555593957" x2="1120736.218066491" y2="96569.31555593957" stroke="#326c5d" stroke-width="15"><title>21634 / CS-CONC-MAJR</title></line><line x1="1120736.218066491" y1="96569.31555593957" x2="1120736.218066491" y2="96649.31555593957" stroke="#326c5d" stroke-width="15"><title>21635 / CS-CONC-MAJR</title></line><line x1="1120736.218066491" y1="96649.31555593957" x2="1120986.218066491" y2="96644.31555593957" stroke="#326c5d" stroke-width="15"><title>21636 / CS-CONC-MAJR</title></line><line x1="1120986.218066491" y1="96644.31555593957" x2="1127486.218066491" y2="96514.31555593957" stroke="#326c5d" stroke-width="15"><title>21637 / CS-CONC-MAJR</title></line><line x1="1127486.218066491" y1="96514.31555593957" x2="1127986.218066491" y2="96504.31555593957" stroke="#326c5d" stroke-width="15"><title>21638 / CS-CONC-MAJR</title></line><line x1="1127986.218066491" y1="96504.31555593957" x2="1127986.218066491" y2="96649.37855593952" stroke="#326c5d" stroke-width="15"><title>21639 / CS-CONC-MAJR</title></line><line x1="1136236.218066491" y1="96569.31555593957" x2="1136236.218066491" y2="96649.31555593957" stroke="#326c5d" stroke-width="15"><title>21640 / CS-CONC-MAJR</title></line><line x1="1136236.218066491" y1="96569.31555593957" x2="1136236.218066491" y2="96619.31555593957" stroke="#326c5d" stroke-width="15"><title>21641 / CS-CONC-MAJR</title></line><line x1="1136236.218066491" y1="96619.31555593957" x2="1136286.218066491" y2="96619.31555593957" stroke="#326c5d" stroke-width="15"><title>21642 / CS-CONC-MAJR</title></line><line x1="1136286.218066491" y1="96619.31555593957" x2="1136286.218066491" y2="96849.37855593952" stroke="#326c5d" stroke-width="15"><title>21643 / CS-CONC-MAJR</title></line><line x1="1136286.218066491" y1="96849.37855593952" x2="1136686.218066491" y2="96849.37855593952" stroke="#326c5d" stroke-width="15"><title>21644 / CS-CONC-MAJR</title></line><line x1="1136686.218066491" y1="96849.37855593952" x2="1136686.218066491" y2="96619.31555593957" stroke="#326c5d" stroke-width="15"><title>21645 / CS-CONC-MAJR</title></line><line x1="1136686.218066491" y1="96619.31555593957" x2="1136736.218066491" y2="96619.31555593957" stroke="#326c5d" stroke-width="15"><title>21646 / CS-CONC-MAJR</title></line><line x1="1128986.218066491" y1="96649.37855593952" x2="1128986.218066491" y2="96504.31555593957" stroke="#326c5d" stroke-width="15"><title>21647 / CS-CONC-MAJR</title></line><line x1="1128986.218066491" y1="96504.31555593957" x2="1129486.218066491" y2="96514.31555593957" stroke="#326c5d" stroke-width="15"><title>21648 / CS-CONC-MAJR</title></line><line x1="1129486.218066491" y1="96514.31555593957" x2="1135986.218066491" y2="96644.31555593957" stroke="#326c5d" stroke-width="15"><titlLine truncated

After

Width:  |  Height:  |  Size: 53 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="1040000 95000 45000 18000"><rect x="1040000" y="95000" width="45000" height="18000" fill="#fff"/><line x1="1069527.623228672" y1="101620.1265643799" x2="1073517.623228672" y2="101503.6778453799" stroke="#326c5d" stroke-width="15"><title>21828 / CS-CONC-MAJR</title></line><line x1="1069527.623228672" y1="107618.3265643799" x2="1073517.623228672" y2="107741.0783453799" stroke="#326c5d" stroke-width="15"><title>21829 / CS-CONC-MAJR</title></line><line x1="1053537.623228672" y1="102134.6376983799" x2="1053537.623228672" y2="107174.2371983799" stroke="#326c5d" stroke-width="15"><title>21830 / CS-CONC-MAJR</title></line><line x1="1073517.623228672" y1="101503.6780093799" x2="1073517.623228672" y2="107741.0785093799" stroke="#326c5d" stroke-width="15"><title>21831 / CS-CONC-MAJR</title></line><line x1="1069527.623228672" y1="107618.0631093799" x2="1073527.623228672" y2="107741.0375093799" stroke="#326c5d" stroke-width="15"><title>21832 / CS-CONC-MAJR</title></line><line x1="1069527.623228672" y1="107718.0631093799" x2="1073527.623228672" y2="107841.0375093799" stroke="#326c5d" stroke-width="15"><title>21833 / CS-CONC-MAJR</title></line><line x1="1063527.623228672" y1="108522.0983143799" x2="1066743.623228672" y2="108522.0983143799" stroke="#8d969e" stroke-width="15"><title>53087 / CS-DIML</title></line><line x1="1063527.623228672" y1="107522.0983143799" x2="1063527.623228672" y2="108522.0983143799" stroke="#8d969e" stroke-width="15"><title>53088 / CS-DIML</title></line><line x1="1050527.623228672" y1="107174.0375093799" x2="1050527.623228672" y2="102135.0375093799" stroke="#8d969e" stroke-width="15"><title>53089 / CS-DIML</title></line><line x1="1051527.623228672" y1="102135.0375093799" x2="1050477.623228672" y2="102135.0375093799" stroke="#8d969e" stroke-width="15"><title>53090 / CS-DIML</title></line><line x1="1051527.623228672" y1="107174.0375093799" x2="1050477.623228672" y2="107174.0375093799" stroke="#8d969e" stroke-width="15"><title>53091 / CS-DIML</title></line><line x1="1051527.623228672" y1="106374.0375093799" x2="1051527.623228672" y2="102135.0375093799" stroke="#8d969e" stroke-width="15"><title>53092 / CS-DIML</title></line><line x1="1052527.623228672" y1="102135.0375093799" x2="1051477.623228672" y2="102135.0375093799" stroke="#8d969e" stroke-width="15"><title>53093 / CS-DIML</title></line><line x1="1051527.623228672" y1="107174.0375093799" x2="1051527.623228672" y2="106374.0375093799" stroke="#8d969e" stroke-width="15"><title>53094 / CS-DIML</title></line><line x1="1052527.623228672" y1="106374.0375093799" x2="1051477.623228672" y2="106374.0375093799" stroke="#8d969e" stroke-width="15"><title>53095 / CS-DIML</title></line><line x1="1052527.623228672" y1="107174.0375093799" x2="1051477.623228672" y2="107174.0375093799" stroke="#8d969e" stroke-width="15"><title>53096 / CS-DIML</title></line><line x1="1053537.623228672" y1="97801.03711951047" x2="1053537.623228672" y2="96801.03711951047" stroke="#8d969e" stroke-width="15"><title>53097 / CS-DIML</title></line><line x1="1053537.623228672" y1="96801.03711951047" x2="1050120.623228672" y2="96801.03711951047" stroke="#8d969e" stroke-width="15"><title>53098 / CS-DIML</title></line><line x1="1073517.623228672" y1="97801.03711951047" x2="1073517.623228672" y2="96801.03711951047" stroke="#8d969e" stroke-width="15"><title>53099 / CS-DIML</title></line><line x1="1073517.623228672" y1="96801.03711951047" x2="1076934.623228672" y2="96801.03711951047" stroke="#8d969e" stroke-width="15"><title>53100 / CS-DIML</title></line><line x1="1073527.623228672" y1="97801.03711951047" x2="1074127.623228672" y2="97801.03711951047" stroke="#8d969e" stroke-width="15"><title>53101 / CS-DIML</title></line><line x1="1073517.623228672" y1="97801.03711951047" x2="1073527.623228672" y2="97801.03711951047" stroke="#8d969e" stroke-width="15"><title>53102 / CS-DIML</title></line><line x1="1073527.623228672" y1="98801.03711951047" x2="1073527.623228672" y2="97751.03711951047" stroke="#8d969e" stroke-width="15"><title>53103 / CS-DIML</title></line><line x1="1053537.623228672" y1="97801.03711951047" x2="1073517.623228672" y2="97801.03711951047" stroke="#8d969e" stroke-width="15"><title>53104 / CS-DIML</title></line><line x1="1073517.623228672" y1="98801.03711951047" x2="1073517.623228672" y2="97751.03711951047" stroke="#8d969e" stroke-width="15"><title>53105 / CS-DIML</title></line><line x1="1053527.623228672" y1="97801.03711951047" x2="1053537.623228672" y2="97801.03711951047" stroke="#8d969e" stroke-width="15"><title>53106 / CS-DIML</title></line><line x1="1053537.623228672" y1="98801.03711951047" x2="1053537.623228672" y2="97751.03711951047" stroke="#8d969e" stroke-width="15"><title>53107 / CS-DIML</title></line><line x1="1053527.623228672" y1="97801.03711951047" x2="1052927.623228672" y2="97801.03711951047" stroke="#8d969e" stroke-width="15"><title>53108 / CS-DIML</title></line><line x1="1053527.623228672" y1="98801.03711951047" x2="1053527Line truncated

After

Width:  |  Height:  |  Size: 12 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="1165000 77000 20000 8000"><rect x="1165000" y="77000" width="20000" height="8000" fill="#fff"/><line x1="1170283.393169491" y1="77512.37855593946" x2="1171033.393169491" y2="77512.37855593946" stroke="#8d969e" stroke-width="15"><title>15CFB8 / CS-DIML</title></line><line x1="1171033.393169491" y1="77512.37855593946" x2="1171283.393169491" y2="77512.37855593946" stroke="#8d969e" stroke-width="15"><title>15CFBA / CS-DIML</title></line><line x1="1171283.393169491" y1="77512.37855593946" x2="1171783.393169491" y2="77512.37855593946" stroke="#8d969e" stroke-width="15"><title>15CFBD / CS-DIML</title></line><line x1="1171783.393169491" y1="77512.37855593946" x2="1172033.393169491" y2="77512.37855593946" stroke="#8d969e" stroke-width="15"><title>15CFBF / CS-DIML</title></line><line x1="1172033.393169491" y1="77512.37855593946" x2="1172933.393169491" y2="77512.37855593946" stroke="#8d969e" stroke-width="15"><title>15CFC0 / CS-DIML</title></line><line x1="1171033.393169491" y1="77512.37855593946" x2="1171033.393169491" y2="78562.37855593946" stroke="#8d969e" stroke-width="15"><title>15CFC1 / CS-DIML</title></line><line x1="1172033.393169491" y1="77512.37855593946" x2="1172033.393169491" y2="78562.37855593946" stroke="#8d969e" stroke-width="15"><title>15CFC2 / CS-DIML</title></line><line x1="1171033.393169491" y1="78512.37855593946" x2="1172033.393169491" y2="78512.37855593946" stroke="#8d969e" stroke-width="15"><title>15CFC3 / CS-DIML</title></line><text x="1170383.393169491" y="77462.37855593946" font-size="200" fill="#28334b">50</text><text x="1171226.602224084" y="77462.37855593946" font-size="200" fill="#28334b">100</text><text x="1172278.865872553" y="77462.37855593946" font-size="200" fill="#28334b">50</text><text x="1171226.602224084" y="78462.37855593946" font-size="200" fill="#28334b">200</text><text x="1170506.971082648" y="84508.21131017663" font-size="200" fill="#28334b">측구 상세</text></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="1020000 64000 85000 32000"><rect x="1020000" y="64000" width="85000" height="32000" fill="#fff"/><line x1="1090963.44842508" y1="71293.23557867091" x2="1091081.68303708" y2="91392.8878296709" stroke="#326c5d" stroke-width="15"><title>21834 / CS-CONC-MAJR</title></line><line x1="1071101.79803108" y1="71293.23557867091" x2="1070983.56341908" y2="91392.8878296709" stroke="#326c5d" stroke-width="15"><title>21835 / CS-CONC-MAJR</title></line><line x1="1055963.44842508" y1="71293.23557967138" x2="1056081.68303708" y2="91392.88783067137" stroke="#326c5d" stroke-width="15"><title>21836 / CS-CONC-MAJR</title></line><line x1="1036101.79803208" y1="71293.23557967138" x2="1035983.56341908" y2="91392.88782967137" stroke="#326c5d" stroke-width="15"><title>21837 / CS-CONC-MAJR</title></line><line x1="1073526.02993208" y1="82855.85590167092" x2="1073516.27555208" y2="85066.83438467093" stroke="#8d969e" stroke-width="15"><title>53136 / CS-DIML</title></line><line x1="1073516.27555208" y1="85066.83438467093" x2="1073515.39320208" y2="85266.8324386709" stroke="#8d969e" stroke-width="15"><title>53137 / CS-DIML</title></line><line x1="1073539.26518308" y1="79855.88509667093" x2="1073549.01956308" y2="77644.90661367092" stroke="#8d969e" stroke-width="15"><title>53138 / CS-DIML</title></line><line x1="1073549.01956308" y1="77644.90661367092" x2="1073549.90191308" y2="77444.9085606709" stroke="#8d969e" stroke-width="15"><title>53139 / CS-DIML</title></line><line x1="1092089.90086708" y1="91086.95190667092" x2="1093139.88270108" y2="91080.77547167095" stroke="#8d969e" stroke-width="15"><title>53140 / CS-DIML</title></line><line x1="1092086.95970808" y1="90586.9605576709" x2="1093136.94154208" y2="90580.78412267091" stroke="#8d969e" stroke-width="15"><title>53141 / CS-DIML</title></line><line x1="1093089.88356608" y1="91081.0695876709" x2="1093086.94240708" y2="90581.07823767092" stroke="#8d969e" stroke-width="15"><title>53142 / CS-DIML</title></line><line x1="1092086.95970808" y1="90586.9605576709" x2="1093136.94154208" y2="90580.78412267091" stroke="#8d969e" stroke-width="15"><title>53143 / CS-DIML</title></line><line x1="1092035.48941608" y1="81837.11194067092" x2="1093085.47125008" y2="81830.93550567089" stroke="#8d969e" stroke-width="15"><title>53144 / CS-DIML</title></line><line x1="1093086.94240708" y1="90581.07823767092" x2="1093035.47211508" y2="81831.2296216709" stroke="#8d969e" stroke-width="15"><title>53145 / CS-DIML</title></line><line x1="1092035.48941608" y1="81837.11194067092" x2="1093085.47125008" y2="81830.93550567089" stroke="#8d969e" stroke-width="15"><title>53146 / CS-DIML</title></line><line x1="1092029.60709708" y1="80837.12924167093" x2="1093079.58893108" y2="80830.9528066709" stroke="#8d969e" stroke-width="15"><title>53147 / CS-DIML</title></line><line x1="1093035.47211508" y1="81831.2296216709" x2="1093029.58979608" y2="80831.2469226709" stroke="#8d969e" stroke-width="15"><title>53148 / CS-DIML</title></line><line x1="1092029.60709708" y1="80837.12924167093" x2="1093079.58893108" y2="80830.9528066709" stroke="#8d969e" stroke-width="15"><title>53149 / CS-DIML</title></line><line x1="1091978.13680608" y1="72087.2806256709" x2="1093028.11864008" y2="72081.10419067093" stroke="#8d969e" stroke-width="15"><title>53150 / CS-DIML</title></line><line x1="1093029.58979608" y1="80831.2469226709" x2="1092978.11950508" y2="72081.39830667093" stroke="#8d969e" stroke-width="15"><title>53151 / CS-DIML</title></line><line x1="1091978.13680608" y1="72087.2806256709" x2="1093028.11864008" y2="72081.10419067093" stroke="#8d969e" stroke-width="15"><title>53152 / CS-DIML</title></line><line x1="1091975.19564708" y1="71587.28927567093" x2="1093025.17748008" y2="71581.1128406709" stroke="#8d969e" stroke-width="15"><title>53153 / CS-DIML</title></line><line x1="1092978.11950508" y1="72081.39830667093" x2="1092975.17834508" y2="71581.40695667091" stroke="#8d969e" stroke-width="15"><title>53154 / CS-DIML</title></line><line x1="1093089.88356608" y1="91081.0695876709" x2="1094139.86540008" y2="91074.89315267092" stroke="#8d969e" stroke-width="15"><title>53155 / CS-DIML</title></line><line x1="1092975.17834508" y1="71581.40695667091" x2="1094025.16017908" y2="71575.23052167094" stroke="#8d969e" stroke-width="15"><title>53156 / CS-DIML</title></line><line x1="1094089.86626508" y1="91075.18726867092" x2="1093975.16104408" y2="71575.52463767094" stroke="#8d969e" stroke-width="15"><title>53157 / CS-DIML</title></line><line x1="1070087.10965008" y1="72087.2806256709" x2="1069037.12781608" y2="72081.10419067093" stroke="#8d969e" stroke-width="15"><title>53158 / CS-DIML</title></line><line x1="1070090.05080908" y1="71587.28927567093" x2="1069040.06897508" y2="71581.1128406709" stroke="#8d969e" stroke-width="15"><title>53159 / CS-DIML</title></line><line x1="1069087.12695108" y1="72081.39830667093" x2="1069090.06811008" y2="71581.40695667091" stroke="#8d969e" stroke-width="15"><title>53160 / CSLine truncated

After

Width:  |  Height:  |  Size: 29 KiB

+95
View File
@@ -0,0 +1,95 @@
import json,pathlib,collections,math
ROOT=pathlib.Path(__file__).resolve().parents[1]
raw=(ROOT/'public/drawings/광터지하차도_일반도(BLOCK7).dxf').read_text(encoding='utf8',errors='replace').splitlines()
pairs=[(int(raw[i].strip()),raw[i+1].strip()) for i in range(0,len(raw)-1,2)]
entities=[]; current=None; section=''; block=None; blocks={}
for code,value in pairs:
if code==0:
if current:
if section=='ENTITIES': entities.append(current)
elif section=='BLOCKS' and block and current['type'] not in ('BLOCK','ENDBLK'): blocks.setdefault(block,[]).append(current)
current={'type':value,'data':[]}
if value=='ENDBLK': block=None
elif current:
current['data'].append((code,value))
if current['type']=='SECTION' and code==2: section=value
if current['type']=='BLOCK' and code==2: block=value
def get(e,c,d=None): return next((v for k,v in e['data'] if k==c),d)
def point(e,c=10): return [float(get(e,c,0)),float(get(e,c+10,0))]
def layer(e): return get(e,8,'')
out=ROOT/'docs/extracted';out.mkdir(exist_ok=True)
texts=[{'text':get(e,1,''),'p':point(e),'layer':layer(e)} for e in entities if e['type'] in ('TEXT','MTEXT')]
lines=[{'a':point(e),'b':point(e,11),'layer':layer(e),'handle':get(e,5)} for e in entities if e['type']=='LINE']
for name,data in [('texts',texts),('lines',lines),('blocks',blocks),('inserts',[{'name':get(e,2),'p':point(e),'sx':get(e,41,'1'),'sy':get(e,42,'1'),'rotation':get(e,50,'0'),'layer':layer(e)} for e in entities if e['type']=='INSERT'])]:
(out/(name+'.json')).write_text(json.dumps(data,ensure_ascii=False,indent=2),encoding='utf8')
print('Entities',collections.Counter(e['type'] for e in entities))
for t in texts:
if any(s in t['text'] for s in ('BLOCK7','106.675','107.307','101.636','101.069','102.436','101.869','299.010','318.990','선형','측구','20.100')): print(t)
for name,top in [('A',-74271),('B',-91211)]:
selected=[l for l in lines if l['layer'] in ('CS-CONC-MAJR','CS-CONC-MINR') and all(1118000<p[0]<1139000 and top-7500<p[1]<top+100 for p in (l['a'],l['b']))]
print(name,len(selected),collections.Counter(l['layer'] for l in selected))
(out/f'section-{name}.json').write_text(json.dumps(selected,ensure_ascii=False,indent=2),encoding='utf8')
# 도면 근거를 보존하는 모델 데이터와 원도면 검토 SVG 생성
sections=[]
for name,top,el,sta in [('A',-74271.37855593952,106.675,299.000),('B',-91211.37855593952,107.307,319.000)]:
selected=[l for l in lines if l['layer'] in ('CS-CONC-MAJR','CS-CONC-MINR') and all(1118000<p[0]<1139000 and top-7500<p[1]<top+100 for p in (l['a'],l['b']))]
sections.append({'name':name,'topCad':top,'topEL':el,'station':sta,'lines':selected})
def group(i):
if i<5 or i==100: return '底版'
if 5<=i<=15: return '좌측 측구·포장 경계'
if 16<=i<=26: return '우측 측구·포장 경계'
if 27<=i<=86: return '포장 반복선·내부선'
if 87<=i<=89: return '중앙 분리부'
if 90<=i<=94: return '좌측 벽체'
if 95<=i<=99: return '우측 벽체'
if 101<=i<=107: return '하부·외측 보조 윤곽'
return '바닥판 상면·세부선'
def norm(p,s): return [round((p[0]-1128486.218066491)/1000,7),round(s['topEL']+(p[1]-s['topCad'])/1000,7)]
parts=[]
for i,(a,b) in enumerate(zip(sections[0]['lines'],sections[1]['lines'])):
assert a['layer']==b['layer']
assert all(abs(a[k][0]-b[k][0])<0.01 for k in ['a','b'])
parts.append({'id':'line-'+a['handle'],'name':group(i).replace('底版','바닥판 하단')+' · '+str(i+1),'group':group(i).replace('底版','바닥판 하단'),'layer':a['layer'],'handles':[a['handle'],b['handle']],'a':[norm(a['a'],sections[0]),norm(a['b'],sections[0])],'b':[norm(b['a'],sections[1]),norm(b['b'],sections[1])],'repeat':27<=i<=86,'kind':'line','source':'일반도 14 / 단면 A-A·B-B / '+a['layer']})
# 삽입 블록의 반전과 실제 LINE/ARC 윤곽을 보존한다. 원호는 5도 간격으로 표시한다.
ductName='A$C2017021411375356'
for side,x,sx in [('좌측',1119736.218066468,1),('우측',1137236.218066513,-1)]:
for j,e in enumerate(blocks[ductName]):
if e['type']=='LINE': pts=[point(e),point(e,11)]
elif e['type']=='ARC':
center=point(e); r=float(get(e,40)); start=float(get(e,50)); end=float(get(e,51))
if end<=start:end+=360
pts=[[center[0]+r*math.cos(math.radians(t)),center[1]+r*math.sin(math.radians(t))] for t in [start+(end-start)*k/36 for k in range(37)]]
else: continue
profiles=[]
for s,y in zip(sections,[-77447.89255584464,-95669.3155558447]):profiles.append([norm([x+sx*p[0],y+p[1]],s) for p in pts])
parts.append({'id':f'duct-{side}-{j}','name':f'{side} 공동구·상부 상세 · {j+1}','group':f'{side} 공동구·상부 상세','layer':layer(e),'handles':[get(e,5)],'a':profiles[0],'b':profiles[1],'repeat':False,'kind':e['type'].lower(),'source':'일반도 14 / '+ductName+' / 원본 삽입 블록'})
model={'source':'광터지하차도_일반도(BLOCK7).dxf','units':'m','length':19.98,'sectionStations':[299,319],'longitudinalStations':[299.01,318.99],'sectionTops':[106.675,107.307],'sectionBottoms':[101.636,101.069],'centerEL':[None,None],'alignment':None,'status':'확인 필요','parts':parts}
modelDir=ROOT/'src/model';modelDir.mkdir(exist_ok=True)
(modelDir/'block7.json').write_text(json.dumps(model,ensure_ascii=False,indent=2),encoding='utf8')
import html
previewDir=ROOT/'public/analysis';previewDir.mkdir(exist_ok=True)
views=[('단면A',1113000,-84300,1142000,-69500),('단면B',1113000,-102300,1142000,-85500),('평면검토',1020000,-96000,1105000,-64000),('종단검토',1040000,-113000,1085000,-95000),('측구상세',1165000,-85000,1185000,-77000)]
for title,x0,y0,x1,y1 in views:
content=[]
for l in lines:
if all(x0<p[0]<x1 and y0<p[1]<y1 for p in (l['a'],l['b'])):
color='#326c5d' if l['layer'] in ('CS-CONC-MAJR','CS-CONC-MINR') else '#8d969e'
content.append(f'<line x1="{l["a"][0]}" y1="{-l["a"][1]}" x2="{l["b"][0]}" y2="{-l["b"][1]}" stroke="{color}" stroke-width="15"><title>{l["handle"]} / {l["layer"]}</title></line>')
for t in texts:
x,y=t['p']
if x0<x<x1 and y0<y<y1:content.append(f'<text x="{x}" y="{-y}" font-size="200" fill="#28334b">{html.escape(t["text"])}</text>')
# 공동구 삽입 블록의 실제 선 포함
for e in entities:
if e['type']=='INSERT' and get(e,2)==ductName:
x,y=point(e);sx=float(get(e,41,1));sy=float(get(e,42,1))
if x0<x<x1 and y0<y<y1:
for q in blocks[ductName]:
if q['type']=='LINE':
a,b=point(q),point(q,11);content.append(f'<line x1="{x+sx*a[0]}" y1="{-y-sy*a[1]}" x2="{x+sx*b[0]}" y2="{-y-sy*b[1]}" stroke="#a97635" stroke-width="15"/>')
svg=f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="{x0} {-y1} {x1-x0} {y1-y0}"><rect x="{x0}" y="{-y1}" width="{x1-x0}" height="{y1-y0}" fill="#fff"/>'+''.join(content)+'</svg>'
(previewDir/(title+'.svg')).write_text(svg,encoding='utf8')
print('모델 추출:',len(parts),'객체. 단면 원본 선 114쌍, 공동구 20쌍 × 좌우.')