Improve drawing export and foundation quantity views

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
CopilotandCopilot committed 2026-11-06 15:35:56 +09:00
1 parent a0ec74b09b
commit d22c5008f0
72 files changed
+24346 -12

No files matched your search

+10 -6
View File
@@ -1,3 +1,4 @@
import { foundationNotes } from './foundation-notes';
import { sectionCut } from './reference-symbols';
import { Vector3 } from 'three';
import type { PierInput } from '../src/core/types';
@@ -6,7 +7,7 @@ import { entityBounds, localPoint, modelPrimitives, projectModel, projectPoint }
import { modelBreaks, shortenedBounds } from './breaks';
import { bearingDetail, scheduleEntities } from './bearing-detail';
import { templateSlots, defaultSheet, wrappedLines } from './template';
import { isometricEntities, keyplanEntities } from './project-views';
import { isometricEntities, isometricInfo, keyplanEntities } from './project-views';
import { hatchGeometry } from './hatches';
import { componentDimensions } from './component-dimensions';
import { optimiseLayout } from './quality-layout';
@@ -15,7 +16,7 @@ import type { Bounds, DrawingDocument, DrawingSettings, DrawingView, Entity, Poi
export const DEFAULT_SETTINGS: DrawingSettings = { paper:'A3',scale:'auto',dimensions:true,centers:true,elevations:true,shorten:true,hatches:true,isoDirection:0 };
const SCALES=[10,20,25,30,40,50,75,100,150,200,250,300,400,500,750,1000,1500,2000];
export const VIEW_TITLES: Record<ViewId,string>={front:'횡단면도',side:'단면도 · 측면',top:'단면도 · 두부보 평면',footing:'단면도 · 기초 평면',section:'A-A 기둥 단면',detail:'상세 A · 받침 및 교좌면',schedule:'받침 및 교좌면 상세 변화 치수표',heights:'받침 높이·표고표',iso:'ISOMETRIC VIEW',keyplan:'KEY PLAN'};
export const VIEW_TITLES: Record<ViewId,string>={front:'횡단면도',side:'단면도 · 측면',top:'두부보 평면도',footing:'단면도 C-C · 기초 평면',section:'A-A 기둥 단면',detail:'상세 A · 받침 및 교좌면',schedule:'받침 및 교좌면 상세 변화 치수표',heights:'받침 높이·표고표',iso:'ISOMETRIC VIEW',keyplan:'KEY PLAN'};
function slots(width: number,height: number): Record<ViewId,Rect> {
return templateSlots(width,height);
@@ -25,7 +26,7 @@ function recommend(bounds: Bounds,slot: Rect,id: ViewId): number {
const need=Math.max((bounds.maxX-bounds.minX)/Math.max(10,slot.width-(side?36:24)),(bounds.maxY-bounds.minY)/Math.max(10,slot.height-(id==='top'?47:31)));
return SCALES.find(s=>s>=need)??Math.ceil(need/500)*500;
}
function annotate(model: PierModel,id: ViewId,b: Bounds,scale: number,settings: DrawingSettings,geometry?:Entity[]): Entity[] {
function annotate(model: PierModel,id: ViewId,b: Bounds,scale: number,settings: DrawingSettings,input:PierInput,geometry?:Entity[]): Entity[] {
const out: Entity[]=[],source: Source={member:'annotation',index:0,pier:model.name},h=2.3*scale;
const line=(a:Point,c:Point,layer:Entity['layer']='CENTER')=>out.push({kind:'line',a,b:c,layer,source});
const text=(p:Point,value:string,layer:Entity['layer']='TEXT',align:'left'|'center'='left')=>out.push({kind:'text',point:p,text:value,height:h,layer,source,align});
@@ -110,6 +111,8 @@ function annotate(model: PierModel,id: ViewId,b: Bounds,scale: number,settings:
out.push({kind:'hatch',points:Array.from({length:16},(_,i)=>[p[0]+.5*scale*Math.cos(i*Math.PI/8),p[1]+.5*scale*Math.sin(i*Math.PI/8)] as Point),pattern:'SOLID',spacing:scale,angle:0,color:'#ff0000',layer:'SECTION',source});
text([end[0]+scale,end[1]+scale],`말뚝 Ø${formatMm(pile.diameter*1000)} · ${model.piles.length}본`);
}
out.push(...foundationNotes(model,input,id,b,scale));
if(id==='front'&&model.footings[0]){const y=model.footings[0].top*1000;line([b.minX-3*scale,y],[b.maxX+3*scale,y],'SECTION');out.push(...sectionCut([b.minX-3*scale,y],scale,'C',false,source).filter(e=>e.kind!=='text'),...sectionCut([b.maxX+3*scale,y],scale,'C',true,source).filter(e=>e.kind!=='text'));out.push({kind:'text',point:[b.minX-5*scale,y+2*scale],text:'C',height:2.5*scale,align:'right',layer:'SECTION',source});}
if(settings.dimensions)out.push(...componentDimensions(model,id,geometry??projectModel(model,id),scale));
return settings.hatches===false?out.filter(e=>e.kind!=='hatch'):out;
}
@@ -129,7 +132,7 @@ export function generateDrawing(model: PierModel,input: PierInput,project: {name
let scale=settings.scale==='auto'?recommend(geometryBounds,slot,id):settings.scale;
if(settings.scale==='auto'&&wide&&settings.shorten!==false)for(let i=0;i<3;i++)scale=recommend(shortenedBounds(geometryBounds,modelBreaks(model,scale)),slot,id);
const breaks=wide&&settings.shorten!==false?modelBreaks(model,scale):undefined;
const entities=[...geometry,...annotate(model,id,geometryBounds,scale,settings)];
const entities=[...geometry,...annotate(model,id,geometryBounds,scale,settings,input)];
const bounds={minX:geometryBounds.minX-(wide?17:12)*scale,maxX:geometryBounds.maxX+(wide&&settings.elevations?18:10)*scale,
minY:geometryBounds.minY-(id==='top'?27:20)*scale,maxY:geometryBounds.maxY+(id==='top'?19:10)*scale};
return{id,title:VIEW_TITLES[id],entities,geometryBounds,bounds,scale,slot,breaks};
@@ -137,7 +140,7 @@ export function generateDrawing(model: PierModel,input: PierInput,project: {name
if(settings.scale==='auto'){
const common=Math.max(views[0].scale,views[1].scale);
for(const v of views.slice(0,2)){
const b=v.geometryBounds;v.entities=[...v.entities.filter(e=>e.source.member!=='annotation'),...annotate(model,v.id,b,common,settings)];
const b=v.geometryBounds;v.entities=[...v.entities.filter(e=>e.source.member!=='annotation'),...annotate(model,v.id,b,common,settings,input)];
v.scale=common;v.breaks=settings.shorten===false?undefined:modelBreaks(model,common);
v.bounds={minX:b.minX-17*common,maxX:b.maxX+(settings.elevations?18:10)*common,minY:b.minY-20*common,maxY:b.maxY+10*common};
}
@@ -173,8 +176,9 @@ export function generateDrawing(model: PierModel,input: PierInput,project: {name
const originals=new Map(views.map(v=>[v.id,v])),geometry=new Map(views.slice(0,5).map(v=>[v.id,v.entities.filter(e=>e.kind!=='hatch'&&e.source.member!=='annotation'&&e.source.member!=='bearing-location')]));
optimiseLayout(doc,(id,scale)=>{
const original=originals.get(id)!;let entities=original.entities;
if(geometry.has(id))entities=[...geometry.get(id)!,...annotate(model,id,original.geometryBounds,scale,settings,geometry.get(id))];
if(geometry.has(id))entities=[...geometry.get(id)!,...annotate(model,id,original.geometryBounds,scale,settings,input,geometry.get(id))];
else if(id==='detail')entities=bearingDetail(model,scale,settings.detailBearing,settings.dimensions,settings.centers).entities;
else if(id==='iso')entities=[...original.entities,...(settings.elevations?isometricInfo(model,input,original.entities,scale,settings.isoDirection??0,settings.dimensions):[])];
else if(id==='keyplan')entities=keyplanEntities(models,model,scale,cfg.startLabel,cfg.endLabel);
const breaks=['front','side'].includes(id)&&settings.shorten!==false?modelBreaks(model,scale):undefined;
if(settings.hatches!==false&&id!=='iso')entities=[...hatchGeometry(entities,scale),...entities];