feat: 프로젝트 공통 지반모델·교대 지반 통합·재료표/수량 진입·옥성교 랜딩 히어로 (11-06~11-09 작업분)

공용 파일(main.ts·index.html·style.css)에서 서로 얽혀 있어 한 커밋으로 묶음.
- 지반: ① 형상 프로젝트 지반모델 관리창(생성·JSON 불러오기·교각별 비교·선택 적용),
  프로젝트 JSON 내장 groundModel 추출, 교대 8행 통합 적용·공통지형 중복격자 제외
- 재료표·수량: 상단 ⑤ 메뉴·배근 바로가기, 결과 교각/탭 기억, 수량 워크북·정합 보정
- 랜딩: 옥성교 6교각 히어로 이미지·투영 강조점·좌→우 광선·지형 라운드/도로 연결
- 검증 스크립트(verify-project-ground/ground-abutments/okseong-user-files/quantity-*/landing-hero 등)
정본: DOC/04_실행/실행_*_2026-11-09_v0.1.00.md

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-11-09 18:01:02 +09:00
1 parent b2bebe47eb
commit fc520ec03d
47 files changed
+2848 -149

No files matched your search

+1 -1
View File
@@ -39,6 +39,6 @@ export class FormulaBook{
const args=n.args.flatMap(a=>this.evaluate(a,sheet));const f:Record<string,()=>number>={SUM:()=>args.reduce((a,b)=>a+b,0),MAX:()=>Math.max(...args),MIN:()=>Math.min(...args),SQRT:()=>Math.sqrt(args[0]),PI:()=>Math.PI,ASIN:()=>Math.asin(args[0]),SIN:()=>Math.sin(args[0])};if(!f[n.name])throw new FormulaError('지원하지 않는 함수 '+n.name);return f[n.name]();
}
substitution(sheet:string,formula:string):string{return formula.replace(/'(?:[^']|'')+'!\$?[A-Z]+\$?\d+|\$?[A-Z]+\$?\d+/g,t=>{const p=t.lastIndexOf('!');try{return String(this.cell(p>=0?t.slice(1,p-1).replace(/''/g,"'"):sheet,t.slice(p+1).replace(/\$/g,'')));}catch{return '미입력';}});}
references(sheet:string,cell:string):string[]{const result=new Set<string>(),visiting=new Set<string>();const walk=(s:string,c:string)=>{const key=cellKey(s,c);if(visiting.has(key))return;visiting.add(key);result.add(key);const f=this.formulas[key]??this.data[s]?.[c]?.value;if(typeof f!=='string'||(!f.startsWith('=')||f.length===1))return;
references(sheet:string,cell:string):string[]{const result=new Set<string>(),visiting=new Set<string>();const walk=(s:string,c:string)=>{const key=cellKey(s,c);if(visiting.has(key))return;visiting.add(key);result.add(key);if(Object.hasOwn(this.overrides,key))return;const f=this.formulas[key]??this.data[s]?.[c]?.value;if(typeof f!=='string'||(!f.startsWith('=')||f.length===1))return;
for(const t of f.slice(1).match(/'(?:[^']|'')+'!\$?[A-Z]+\$?\d+|\$?[A-Z]+\$?\d+/g)??[]){const p=t.lastIndexOf('!');walk(p>=0?t.slice(1,p-1).replace(/''/g,"'"):s,t.slice(p+1).replace(/\$/g,''));}};walk(sheet,cell);return [...result];}
}