Files
Pier_v1/도면작성/breaks.ts
T

66 lines
3.8 KiB
TypeScript

import type { PierModel } from '../src/core/geometry';
import type { Bounds, Entity, Point, ViewBreak } from './types';
import { cutHatch } from './hatches';
/** Break only a shared, constant-section interior: never the cap or foundation interfaces. */
export function modelBreaks(model:PierModel,scale:number): ViewBreak[] {
const result:ViewBreak[]=[];
for(const [member,items] of [['column',model.columns],['pile',model.piles]] as const){
if(!items.length)continue;
const diameter=Math.max(...items.map(c=>c.diameter))*1000;
const bottom=Math.max(...items.map(c=>c.bottom))*1000,top=Math.min(...items.map(c=>c.top))*1000;
const keepBottom=Math.max(member==='column'?900:350,diameter*.65);
const keepTop=Math.max(member==='column'?1300:500,diameter*.9);
const from=bottom+keepBottom,to=top-keepTop;
const gap=member==='column'?diameter+6*scale:1.5*scale;
if(to-from>Math.max(gap+500,scale*10))result.push({from,to,gap,member});
}
return result.sort((a,b)=>a.from-b.from).filter((b,i,all)=>i===0||b.from>=all[i-1].to);
}
export function shortenedY(y:number,breaks:readonly ViewBreak[]=[]):number {
let removed=0;
for(const b of breaks){
if(y<=b.from)break;
if(y<b.to)return b.from-removed+(y-b.from)/(b.to-b.from)*b.gap;
removed+=b.to-b.from-b.gap;
}
return y-removed;
}
export const shortenedBounds=(b:Bounds,breaks:readonly ViewBreak[]=[]):Bounds=>({...b,minY:shortenedY(b.minY,breaks),maxY:shortenedY(b.maxY,breaks)});
/** Ordinary entities keep full source coordinates. Only their presentation is shortened. */
export function shortenEntity(entity:Entity,breaks:readonly ViewBreak[],clip=true):Entity[] {
const point=(p:Point):Point=>[p[0],shortenedY(p[1],breaks)];
if(!breaks.length)return[entity];
if(entity.kind==='text')return[{...entity,point:point(entity.point)}];
if(entity.kind==='circle')return[{...entity,center:point(entity.center)}];
if(entity.kind==='dimension')throw new Error('Expand dimension graphics before presentation.');
if(entity.kind==='hatch')return cutHatch(entity.points,breaks).map(points=>({...entity,points:points.map(point)}));
const line=(a:Point,b:Point):Entity[]=>{
if(!clip)return[{...entity,kind:'line',a:point(a),b:point(b)}];
const dy=b[1]-a[1],ts=[0,1];
if(Math.abs(dy)>1e-9)for(const cut of breaks)for(const y of [cut.from,cut.to]){const t=(y-a[1])/dy;if(t>0&&t<1)ts.push(t);}
ts.sort((x,y)=>x-y);
const at=(t:number):Point=>[a[0]+(b[0]-a[0])*t,a[1]+dy*t];
return ts.slice(1).flatMap((t,i)=>{
const middle=at((ts[i]+t)/2)[1];
if(breaks.some(c=>middle>c.from+1e-7&&middle<c.to-1e-7))return[];
return[{...entity,kind:'line' as const,a:point(at(ts[i])),b:point(at(t))}];
});
};
if(entity.kind==='line')return line(entity.a,entity.b);
return entity.points.slice(1).flatMap((p,i)=>line(entity.points[i],p)).concat(entity.closed?line(entity.points[entity.points.length-1],entity.points[0]):[]);
}
export function breakSymbols(entities:Entity[],breaks:readonly ViewBreak[],scale:number):Entity[] {
return entities.filter(e=>e.kind==='polyline'&&['column','pile'].includes(e.source.member)).flatMap(e=>{
if(e.kind!=='polyline')return[];
const xs=e.points.map(p=>p[0]),ys=e.points.map(p=>p[1]),left=Math.min(...xs),right=Math.max(...xs),width=right-left;
return breaks.filter(b=>e.source.member===b.member&&Math.min(...ys)<b.from&&Math.max(...ys)>b.to).flatMap(b=>[b.from,b.to].map(y=>({
kind:'polyline' as const,closed:false,source:e.source,layer:'STRUCTURE' as const,
points:[[left-.5*scale,shortenedY(y,breaks)],[left+width*.37,shortenedY(y,breaks)],[left+width*.48,shortenedY(y,breaks)+.65*scale],
[left+width*.58,shortenedY(y,breaks)-.65*scale],[left+width*.7,shortenedY(y,breaks)],[right+.5*scale,shortenedY(y,breaks)]] as Point[],
})));
});
}