import { Vector3 } from 'three'; export type DepthFace={vertices:Vector3[];edges:boolean[];data:T}; /** BSP painter for an orthographic camera at negative depth. Split crossing faces, * preserving only original edges so partition boundaries never become CAD outlines. */ export function visibleOrder(input:DepthFace[]):DepthFace[] { const visit=(faces:DepthFace[]):DepthFace[]=>{ if(!faces.length)return []; const seed=faces[0],a=seed.vertices[0];let normal=new Vector3(); for(let i=1;i[]=[],back:DepthFace[]=[],same:DepthFace[]=[seed]; for(const face of faces.slice(1)){ const ds=face.vertices.map(p=>normal.dot(p)-offset),positive=ds.some(d=>d>1e-5),negative=ds.some(d=>d< -1e-5); if(!positive&&!negative){same.push(face);continue;} if(!negative){front.push(face);continue;}if(!positive){back.push(face);continue;} for(const [sign,list] of [[1,front],[-1,back]] as const){ const vertices:Vector3[]=[],incoming:boolean[]=[]; for(let i=0;i=-1e-5,next=ds[j]*sign>=-1e-5; if(inside&&next){vertices.push(q);incoming.push(face.edges[i]);} else if(inside&&!next){vertices.push(p.clone().lerp(q,ds[i]/(ds[i]-ds[j])));incoming.push(face.edges[i]);} else if(!inside&&next){vertices.push(p.clone().lerp(q,ds[i]/(ds[i]-ds[j])),q);incoming.push(false,face.edges[i]);} } if(vertices.length>=3)list.push({vertices,edges:incoming.map((_,i)=>incoming[(i+1)%incoming.length]),data:face.data}); } } return normal.z<0?[...visit(back),...same,...visit(front)]:[...visit(front),...same,...visit(back)]; }; return visit(input.flatMap(face=>Array.from({length:face.vertices.length-2},(_,j)=>{const i=j+1;return {vertices:[face.vertices[0],face.vertices[i],face.vertices[i+1]],edges:[i===1&&face.edges[0],face.edges[i],i===face.vertices.length-2&&face.edges[i+1]],data:face.data};}))); }