From 599696883f95c5c816b296f3a811759fa607f086 Mon Sep 17 00:00:00 2001 From: tdc_engineer Date: Thu, 29 Oct 2026 14:43:32 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=A2=8C/=EC=9A=B0=20=EA=B3=B5=EB=8F=99?= =?UTF-8?q?=EA=B5=AC=20=EB=B2=BD=EC=B2=B4=20=EB=86=92=EC=9D=B4=20660mm=20?= =?UTF-8?q?=EA=B3=A0=EC=A0=95=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0=20?= =?UTF-8?q?=EB=B0=8F=20=EC=A0=84=EC=88=98=20=EA=B2=80=EC=A6=9D=20(?= =?UTF-8?q?=EC=A2=8C=20700mm,=20=EC=9A=B0=201000mm=20=ED=91=9C=EC=A4=80=20?= =?UTF-8?q?=EB=8F=99=EA=B8=B0=ED=99=94)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.js | 33 ++++++++++---- index.html | 2 + tests/analyze_wall_geom.js | 88 ++++++++++++++++++++++++++++++++++++++ tests/search_dim_texts.js | 32 ++++++++++++++ 4 files changed, 147 insertions(+), 8 deletions(-) create mode 100644 tests/analyze_wall_geom.js create mode 100644 tests/search_dim_texts.js diff --git a/app.js b/app.js index c653633..875f0eb 100644 --- a/app.js +++ b/app.js @@ -52,9 +52,9 @@ window.ugData = { type: 'T1', vw1: 300, vw2: 300, vh: 300, lw: 400, lh: 60, gap: 50, - ww1: 144, ww2: 36, ww3: 120, - wh1: 360, wh2: 175, wh3: 125, - bcw: 650, bch: 500, dw: 100, dh: 150, + ww1: 150, ww2: 30, ww3: 120, + wh1: 450, wh2: 175, wh3: 75, + bcw: 550, bch: 530, dw: 100, dh: 150, bw1: 125, bw2: 300, bw3: 300, bd1: 50, bd2: 300, bd3: 50, pipeD: 300 @@ -63,9 +63,9 @@ window.ugData = { type: 'T1', vw1: 300, vw2: 300, vh: 300, lw: 400, lh: 60, gap: 50, - ww1: 144, ww2: 36, ww3: 120, - wh1: 360, wh2: 175, wh3: 125, - bcw: 650, bch: 500, dw: 100, dh: 150, + ww1: 150, ww2: 80, ww3: 120, + wh1: 750, wh2: 175, wh3: 75, + bcw: 550, bch: 500, dw: 100, dh: 150, bw1: 125, bw2: 300, bw3: 300, bd1: 50, bd2: 300, bd3: 50, pipeD: 300 @@ -1628,6 +1628,10 @@ function syncParamsToInputs() { setIf('p_pave_t1', p.pave_t1); setIf('p_pave_t2', p.pave_t2); setIf('p_pave_t3', p.pave_t3); + const dL = window.ugData.left || {}; + const dR = window.ugData.right || {}; + setIf('p_ug_wh_l', (dL.wh1 || 450) + (dL.wh2 || 175) + (dL.wh3 || 75)); + setIf('p_ug_wh_r', (dR.wh1 || 750) + (dR.wh2 || 175) + (dR.wh3 || 75)); } function readParamsFromInputs() { @@ -1650,6 +1654,19 @@ function readParamsFromInputs() { p.pave_t1 = getNum('p_pave_t1', p.pave_t1); p.pave_t2 = getNum('p_pave_t2', p.pave_t2); p.pave_t3 = getNum('p_pave_t3', p.pave_t3); + + const whTotL = getNum('p_ug_wh_l', 700); + const whTotR = getNum('p_ug_wh_r', 1000); + if (window.ugData.left) { + window.ugData.left.wh3 = 75; + window.ugData.left.wh2 = 175; + window.ugData.left.wh1 = Math.max(100, Math.round(whTotL - 250)); + } + if (window.ugData.right) { + window.ugData.right.wh3 = 75; + window.ugData.right.wh2 = 175; + window.ugData.right.wh1 = Math.max(100, Math.round(whTotR - 250)); + } } function renderDetectionList(sourceName, entityCount) { @@ -1674,8 +1691,8 @@ function renderDetectionList(sourceName, entityCount) { }, { color: '#cbd5e1', - title: '공동구 및 프리캐스트 뚜껑', - detail: `좌 ${dL.type || 'T2'} (${dL.lw}×${dL.lh}mm, 내공 ${dL.vw1}×${dL.vh}mm) · 우 ${dR.type || 'T2'} (${dR.lw}×${dR.lh}mm, 내공 ${dR.vw1}×${dR.vh}mm)`, + title: '공동구 벽체 및 프리캐스트 뚜껑', + detail: `좌 벽체 ${dL.wh1 + dL.wh2 + dL.wh3}mm(뚜껑 ${dL.lw}×${dL.lh}) · 우 벽체 ${dR.wh1 + dR.wh2 + dR.wh3}mm(뚜껑 ${dR.lw}×${dR.lh}) · 내공 ${dR.vw1}×${dR.vh}mm`, badge: '100%' }, { diff --git a/index.html b/index.html index 53e5c65..1ef0d8a 100644 --- a/index.html +++ b/index.html @@ -93,6 +93,8 @@ + + diff --git a/tests/analyze_wall_geom.js b/tests/analyze_wall_geom.js new file mode 100644 index 0000000..e415847 --- /dev/null +++ b/tests/analyze_wall_geom.js @@ -0,0 +1,88 @@ +const fs = require('fs'); +const DxfParser = require('c:/Users/user/.gemini/tunnelsection/TunnelModeler3D/dxf-parser.js'); + +function analyzeWallGeometry(dxfPath, label) { + if (!fs.existsSync(dxfPath)) return; + console.log(`\n======================================================`); + console.log(`[${label}] 공동구 선분 기하 정밀 실측: ${dxfPath}`); + const dxf = new DxfParser().parseSync(fs.readFileSync(dxfPath, 'utf8')); + + // app.js와 동일한 방식으로 targetCenterX, targetCenterY, pavement 선분 찾기 + let appCode = fs.readFileSync('c:/Users/user/.gemini/tunnelsection/TunnelModeler3D/app.js', 'utf8'); + + // mock 환경 + global.window = { + current3DTunnelLength: 10, current3DOpacity: 1.0, current3DOuterOpacity: 1.0, + current3DViewMode: 'bird', current3DTheme: 'dark', current3DVisibility: {}, + sectionParams: {}, ugData: { left: {}, right: {} }, + addEventListener: () => {}, lastGeneratedLoops2D: [], view2D: { zoom: 1, panX: 0, panY: 0 } + }; + const mockCtx = { + fillRect: ()=>{}, strokeRect: ()=>{}, beginPath: ()=>{}, closePath: ()=>{}, + moveTo: ()=>{}, lineTo: ()=>{}, stroke: ()=>{}, fill: ()=>{}, + save: ()=>{}, restore: ()=>{}, setTransform: ()=>{}, fillText: ()=>{} + }; + global.document = { + getElementById: () => ({ + classList: { add: ()=>{}, remove: ()=>{} }, + addEventListener: () => {}, value: '', disabled: false, + getContext: () => mockCtx, getBoundingClientRect: () => ({ width: 800, height: 600 }) + }), + querySelectorAll: () => [] + }; + global.$ = (id) => global.document.getElementById(id); + global.Plotly = { react: () => {} }; + + eval(appCode); + extractParamsFromDXF(dxf, label); + + const sp = window.sectionParams; + const pL_X = sp.pavementLeftX; + const pR_X = sp.pavementRightX; + + // 단면 선분들 중 좌측 공동구 영역(X: pL_X - 1500 ~ pL_X)과 우측 공동구 영역(X: pR_X ~ pR_X + 1500) + const allLines = dxf.entities.filter(e => e.type === 'LINE'); + + // 좌측 공동구 상단선(수평선) 탐색 + const leftHLines = allLines.filter(l => { + const minX = Math.min(l.vertices[0].x, l.vertices[1].x); + const maxX = Math.max(l.vertices[0].x, l.vertices[1].x); + const dy = Math.abs(l.vertices[0].y - l.vertices[1].y); + const dx = Math.abs(l.vertices[0].x - l.vertices[1].x); + return minX >= pL_X - 2500 && maxX <= pL_X + 100 && dy <= 3.0 && dx >= 40; + }); + + // 우측 공동구 상단선(수평선) 탐색 + const rightHLines = allLines.filter(l => { + const minX = Math.min(l.vertices[0].x, l.vertices[1].x); + const maxX = Math.max(l.vertices[0].x, l.vertices[1].x); + const dy = Math.abs(l.vertices[0].y - l.vertices[1].y); + const dx = Math.abs(l.vertices[0].x - l.vertices[1].x); + return minX >= pR_X - 100 && maxX <= pR_X + 2500 && dy <= 3.0 && dx >= 40; + }); + + // 도로 표면 기준 높이(포장 끝단 Y) + // roadOriginY는 대략 포장 끝점 Y + const pL_Y = sp.roadOriginY || 0; // sp에는 없음, app.js 내 pL_Y 찾기 + console.log(` 포장 좌측단 X=${Math.round(pL_X)}, 우측단 X=${Math.round(pR_X)}`); + console.log(` [좌측 수평선들 (X, Y, 길이 dx)]:`); + leftHLines.sort((a,b) => b.vertices[0].y - a.vertices[0].y).slice(0, 10).forEach(l => { + const x1 = Math.round(l.vertices[0].x), x2 = Math.round(l.vertices[1].x); + const y = Math.round((l.vertices[0].y + l.vertices[1].y)/2); + const len = Math.round(Math.abs(x1 - x2)); + console.log(` Y=${y}, X=[${Math.min(x1,x2)} ~ ${Math.max(x1,x2)}], dx=${len}, layer=${l.layer}`); + }); + + console.log(` [우측 수평선들 (X, Y, 길이 dx)]:`); + rightHLines.sort((a,b) => b.vertices[0].y - a.vertices[0].y).slice(0, 10).forEach(l => { + const x1 = Math.round(l.vertices[0].x), x2 = Math.round(l.vertices[1].x); + const y = Math.round((l.vertices[0].y + l.vertices[1].y)/2); + const len = Math.round(Math.abs(x1 - x2)); + console.log(` Y=${y}, X=[${Math.min(x1,x2)} ~ ${Math.max(x1,x2)}], dx=${len}, layer=${l.layer}`); + }); +} + +analyzeWallGeometry('c:/Users/user/.gemini/tunnelsection/TunnelModeler3D/Test Sample/세종안성_서운1.dxf', '서운1'); +analyzeWallGeometry('c:/Users/user/.gemini/tunnelsection/TunnelModeler3D/Test Sample/안성구리3_고삼.dxf', '고삼'); +analyzeWallGeometry('c:/Users/user/.gemini/antigravity/brain/9c348cd5-3f07-4ade-adba-e28b2709935f/scratch/wansan_out/wansan.dxf', '완산'); +analyzeWallGeometry('c:/Users/user/.gemini/antigravity/brain/9c348cd5-3f07-4ade-adba-e28b2709935f/scratch/namhan_out/namhan.dxf', '남한산성'); diff --git a/tests/search_dim_texts.js b/tests/search_dim_texts.js new file mode 100644 index 0000000..c90e2f8 --- /dev/null +++ b/tests/search_dim_texts.js @@ -0,0 +1,32 @@ +const fs = require('fs'); +const DxfParser = require('c:/Users/user/.gemini/tunnelsection/TunnelModeler3D/dxf-parser.js'); + +function searchDimensionTexts(dxfPath, label) { + if (!fs.existsSync(dxfPath)) return; + console.log(`\n========================================`); + console.log(`[${label}] 치수 텍스트 정밀 탐색: ${dxfPath}`); + const dxf = new DxfParser().parseSync(fs.readFileSync(dxfPath, 'utf8')); + const texts = dxf.entities.filter(e => e.type === 'TEXT' || e.type === 'MTEXT'); + + // 공동구 벽체 높이 관련 숫자 검색: 660, 700, 750, 800, 900, 950, 1000, 1050, 1100, 450, 360, 175, 125, 75 등 + const targetNums = ['660', '700', '750', '800', '900', '950', '1000', '1050', '1100', '450', '360', '175', '125', '75', '144', '150', '160']; + + const found = texts.filter(t => { + const text = t.text || ''; + return targetNums.some(num => new RegExp(`(^|[^0-9])${num}([^0-9]|$)`).test(text)); + }); + + console.log(`발견된 관련 텍스트 수: ${found.length}`); + // X좌표 기준으로 좌측 공동구 영역, 우측 공동구 영역 텍스트만 필터링해보기 + found.forEach(t => { + const txt = (t.text || '').replace(/\\P/g, ' ').trim(); + const px = Math.round(t.x || (t.position && t.position.x) || 0); + const py = Math.round(t.y || (t.position && t.position.y) || 0); + console.log(` [${t.layer}] (${px}, ${py}): "${txt}"`); + }); +} + +searchDimensionTexts('c:/Users/user/.gemini/tunnelsection/TunnelModeler3D/Test Sample/세종안성_서운1.dxf', '서운1'); +searchDimensionTexts('c:/Users/user/.gemini/tunnelsection/TunnelModeler3D/Test Sample/안성구리3_고삼.dxf', '고삼'); +searchDimensionTexts('c:/Users/user/.gemini/antigravity/brain/9c348cd5-3f07-4ade-adba-e28b2709935f/scratch/wansan_out/wansan.dxf', '완산'); +searchDimensionTexts('c:/Users/user/.gemini/antigravity/brain/9c348cd5-3f07-4ade-adba-e28b2709935f/scratch/namhan_out/namhan.dxf', '남한산성');