Add DXF verification: update src/dxf.js

This commit is contained in:
bjkim committed 2026-10-29 12:13:25 +09:00
1 parent 207a5a300a
commit 67f3845014
1 file changed
+1 -1
+1 -1
View File
@@ -1,5 +1,5 @@
// DXF parsing and plan geometry helpers.
function parseDxf(text){const entities=[],dimensionTexts=[];const raw=text.replace(/^\ufeff/,'').split(/\r?\n/),pairs=[];for(let i=0;i+1<raw.length;i+=2){const code=Number(raw[i].trim());if(Number.isFinite(code))pairs.push([code,raw[i+1].trim()])}let inEntities=false,section='',layers=new Set();for(let i=0;i<pairs.length;){const [code,val]=pairs[i];if(code===0&&val==='SECTION'){section=pairs[i+1]?.[1]==='ENTITIES'?'ENTITIES':pairs[i+1]?.[1]||'';inEntities=section==='ENTITIES';i+=2;continue}if(code===0&&val==='ENDSEC'){inEntities=false;section='';i++;continue}if(code!==0||!inEntities){i++;continue}const type=val;i++;const rec=[];while(i<pairs.length&&pairs[i][0]!==0)rec.push(pairs[i++]);let layer=rec.find(p=>p[0]===8)?.[1]||'0',entity={type,layer};layers.add(layer);if(type==='LINE'){entity.pts=[{x:num(rec,10),y:num(rec,20)},{x:num(rec,11),y:num(rec,21)}]}else if(type==='LWPOLYLINE'){const pts=[];let point=null;for(const [code,value] of rec){if(code===10){if(point)pts.push(point);point={x:Number(value),y:0}}else if(code===20&&point)point.y=Number(value)}if(point)pts.push(point);entity.pts=pts;entity.closed=!!(Number(rec.find(p=>p[0]===70)?.[1]||0)&1)}else if(type==='POLYLINE'){let pts=[];while(i<pairs.length&&pairs[i][0]===0&&pairs[i][1]==='VERTEX'){i++;const vr=[];while(i<pairs.length&&pairs[i][0]!==0)vr.push(pairs[i++]);pts.push({x:num(vr,10),y:num(vr,20)})}entity.pts=pts;entity.closed=!!(Number(rec.find(p=>p[0]===70)?.[1]||0)&1);if(pairs[i]?.[0]===0&&pairs[i][1]==='SEQEND')i++}else if(type==='TEXT'||type==='MTEXT'){entity.text=rec.filter(p=>p[0]===1||p[0]===3).map(p=>p[1]).join('').replace(/\\P/g,' ').replace(/[{}]/g,'').trim();entity.pos={x:num(rec,10),y:num(rec,20)};entity.rotation=num(rec,50);if(entity.text&&layer==='DIM')dimensionTexts.push(entity)}if(entity.pts)entities.push(entity)}if(!entities.length)throw Error('도면의 ENTITIES 섹션에서 지원되는 선 도형을 찾지 못했습니다. ASCII DXF인지 확인해 주세요.');return {entities,dimensionTexts,layers:[...layers].sort((a,b)=>a.localeCompare(b,undefined,{numeric:true))}}}
function parseDxf(text){const entities=[],dimensionTexts=[];const raw=text.replace(/^\ufeff/,'').split(/\r?\n/),pairs=[];for(let i=0;i+1<raw.length;i+=2){const code=Number(raw[i].trim());if(Number.isFinite(code))pairs.push([code,raw[i+1].trim()])}let inEntities=false,section='',layers=new Set();for(let i=0;i<pairs.length;){const [code,val]=pairs[i];if(code===0&&val==='SECTION'){section=pairs[i+1]?.[1]==='ENTITIES'?'ENTITIES':pairs[i+1]?.[1]||'';inEntities=section==='ENTITIES';i+=2;continue}if(code===0&&val==='ENDSEC'){inEntities=false;section='';i++;continue}if(code!==0||!inEntities){i++;continue}const type=val;i++;const rec=[];while(i<pairs.length&&pairs[i][0]!==0)rec.push(pairs[i++]);let layer=rec.find(p=>p[0]===8)?.[1]||'0',entity={type,layer};layers.add(layer);if(type==='LINE'){entity.pts=[{x:num(rec,10),y:num(rec,20)},{x:num(rec,11),y:num(rec,21)}]}else if(type==='LWPOLYLINE'){const pts=[];let point=null;for(const [code,value] of rec){if(code===10){if(point)pts.push(point);point={x:Number(value),y:0}}else if(code===20&&point)point.y=Number(value)}if(point)pts.push(point);entity.pts=pts;entity.closed=!!(Number(rec.find(p=>p[0]===70)?.[1]||0)&1)}else if(type==='POLYLINE'){let pts=[];while(i<pairs.length&&pairs[i][0]===0&&pairs[i][1]==='VERTEX'){i++;const vr=[];while(i<pairs.length&&pairs[i][0]!==0)vr.push(pairs[i++]);pts.push({x:num(vr,10),y:num(vr,20)})}entity.pts=pts;entity.closed=!!(Number(rec.find(p=>p[0]===70)?.[1]||0)&1);if(pairs[i]?.[0]===0&&pairs[i][1]==='SEQEND')i++}else if(type==='TEXT'||type==='MTEXT'){entity.text=rec.filter(p=>p[0]===1||p[0]===3).map(p=>p[1]).join('').replace(/\\P/g,' ').replace(/[{}]/g,'').trim();entity.pos={x:num(rec,10),y:num(rec,20)};entity.rotation=num(rec,50);if(entity.text&&layer==='DIM')dimensionTexts.push(entity)}if(entity.pts)entities.push(entity)}if(!entities.length)throw Error('도면의 ENTITIES 섹션에서 지원되는 선 도형을 찾지 못했습니다. ASCII DXF인지 확인해 주세요.');return {entities,dimensionTexts,layers:[...layers].sort((a,b)=>a.localeCompare(b,undefined,{numeric:true}))}}
function num(rec,code){return Number(rec.find(p=>p[0]===code)?.[1]||0)}
function eq(a,b){return Math.hypot(a.x-b.x,a.y-b.y)<1e-5}function closedLoops(entities,layer){const found=[];let lines=[];for(const e of entities.filter(x=>x.layer===layer)){if(e.pts?.length<2)continue;if(e.type==='LINE'||!e.closed){for(let i=0;i<e.pts.length-1;i++)lines.push([e.pts[i],e.pts[i+1]]);if(e.closed&&e.pts.length>2)lines.push([e.pts.at(-1),e.pts[0]]);continue}if(e.pts.length>=3)found.push(e.pts.slice())}while(lines.length){let chain=lines.pop().map(p=>({...p})),changed=true;while(changed){changed=false;for(let i=0;i<lines.length;i++){const p=lines[i];if(eq(chain.at(-1),p[0]))chain.push(p[1]);else if(eq(chain.at(-1),p[1]))chain.push(p[0]);else if(eq(chain[0],p[1]))chain.unshift(p[0]);else if(eq(chain[0],p[0]))chain.unshift(p[1]);else continue;lines.splice(i,1);changed=true;break}}if(chain.length>=4&&eq(chain[0],chain.at(-1)))found.push(chain.slice(0,-1))}return found}