import type { Entity, Point, ViewBreak } from './types'; const id=(p:Point)=>`${Math.round(p[0]*1000)},${Math.round(p[1]*1000)}`; export function hatchGeometry(entities:Entity[],scale:number):Entity[] { const out:Entity[]=[],groups=new Map[]>(); for(const e of entities){ if(!['STRUCTURE','BEARING'].includes(e.layer))continue; if(e.kind==='polyline'&&e.closed)out.push({kind:'hatch',points:e.points,pattern:e.source.member==='pile'?'ANSI31':'SOLID',angle:45,spacing:2*scale,color:e.source.member==='blinding'?'#e7e9ec':'#cccccc',source:e.source,layer:'HATCH'}); if(e.kind==='circle'&&e.source.member==='column')out.push({kind:'hatch',points:Array.from({length:48},(_,i)=>[e.center[0]+e.radius*Math.cos(i*Math.PI/24),e.center[1]+e.radius*Math.sin(i*Math.PI/24)] as Point),pattern:'SOLID',angle:45,spacing:2*scale,color:'#cccccc',source:e.source,layer:'HATCH'}); if(e.kind==='line'){const key=`${e.source.member}:${e.source.index}`;if(!groups.has(key))groups.set(key,[]);groups.get(key)!.push(e);} } for(const group of groups.values()){ const unused=new Set(group); while(unused.size){const first=unused.values().next().value!;unused.delete(first);const points=[first.a,first.b]; while(id(points[points.length-1])!==id(points[0])){ const last=id(points[points.length-1]),next=[...unused].find(e=>id(e.a)===last||id(e.b)===last); if(!next)break;unused.delete(next);points.push(id(next.a)===last?next.b:next.a); } if(points.length>3&&id(points[0])===id(points[points.length-1]))out.push({kind:'hatch',points:points.slice(0,-1),pattern:first.source.member==='blinding'?'ANSI31':'SOLID',spacing:2*scale,angle:45,color:'#cccccc',layer:'HATCH',source:first.source}); } } return out; } const clipY=(points:Point[],y:number,above:boolean):Point[]=>{ const out:Point[]=[]; for(let i=0;i=0)out.push(a); if((da>=0)!==(db>=0)){const t=(y-a[1])/(b[1]-a[1]);out.push([a[0]+(b[0]-a[0])*t,y]);} }return out; }; export function cutHatch(points:Point[],cuts:readonly ViewBreak[]):Point[][] { let polygons=[points]; for(const cut of cuts)polygons=polygons.flatMap(p=>[clipY(p,cut.from,false),clipY(p,cut.to,true)]).filter(p=>p.length>2); return polygons; }