UNPKG

@csi-foxbyte/mesh-dem-to-terrain

Version:

A Node.js library that converts DEM meshes (DXF, GeoJSON, etc.) into Cesium Terrain Tiles (quantized-mesh or heightmap) with flexible zoom-level control and multi-threading support.

2 lines 9.96 kB
import {polygonclip}from'./lineclip/index.js';import {triangulate3DPolygon}from'./helpers.js';import {compactPrimitive}from'@gltf-transform/functions';import W from'delaunator';import {Vector3,Box3}from'three';import K from'flatbush';import H from'earcut';function gt(){return i=>{const g=i.getRoot().listMeshes().flatMap(m=>m.listPrimitives());for(const m of g){const h=m.getIndices(),p=h?.getArray();if(!p)return;for(let x=0;x<p.length;x+=3){const M=p[x+1];p[x+1]=p[x+2],p[x+2]=M;}h?.setArray(p);}}}function pt({maxX:i,maxY:b,minX:g,minY:m,offset:h}){return p=>{for(const[x,M]of p.getRoot().listNodes().flatMap(c=>c.getMesh()?.listPrimitives().map(o=>[c,o])??[])){const c=M.getIndices()?.getArray(),o=M.getAttribute("POSITION")?.getArray();if(!c||!o)continue;const s=new Array(65*65).fill(0).map(()=>[0,0,0]),r=new Float32Array(c.length/3),t=new K(c.length/3),n=new Vector3,f=new Vector3,l=new Vector3,e=new Box3;let y=0;for(let a=0;a<c.length;a+=3,y++){const d=c[a]*3,I=c[a+1]*3,S=c[a+2]*3;n.set(o[d],o[d+1],o[d+2]),f.set(o[I],o[I+1],o[I+2]),l.set(o[S],o[S+1],o[S+2]),e.makeEmpty(),e.setFromPoints([n,f,l]),t.add(e.min.x,e.min.y,e.max.x,e.max.y),r[y]=(n.z+f.z+l.z)/3;}t.finish();const A=i-g,P=b-m;for(let a=0;a<65;a++)for(let d=0;d<65;d++){const I=d+a*65,S=d/64*A+g,T=a/64*P+m,_=(d+1)/64*A+g,O=(a+1)/64*P+m;let E=0;const z=t.search(S,T,_,O);if(z.length===0){s[I]=[S,T,h-x.getTranslation()[2]];continue}z.forEach(B=>E+=r[B]),s[I]=[S,T,E/z.length];}const{triangles:u}=W.from(s,a=>a[0],a=>a[1]);for(let a=0;a<u.length;a+=3){const d=u[a+1];u[a+1]=u[a+2],u[a+2]=d;}M.getAttribute("POSITION")?.setArray(new Float32Array(s.flat())),M.getIndices()?.setArray(new Uint32Array(u));}}}function xt({maxX:i,maxY:b,minX:g,minY:m}){return h=>{const x=h.getRoot().listMeshes().flatMap(c=>c.listPrimitives()),M=[g,m,i,b];for(const c of x){const o=c.getAttribute("POSITION")?.getArray(),s=c.getIndices()?.getArray();if(!o||!s)continue;const r=new Set,t=[],n=[];for(let e=0;e<s.length;e+=3){const y=s[e]*3,A=s[e+1]*3,P=s[e+2]*3,u=[[o[y],o[y+1],o[y+2]],[o[A],o[A+1],o[A+2]],[o[P],o[P+1],o[P+2]]],a=polygonclip(u,M);if(a.length===0)r.add(e);else {r.add(e);const d=triangulate3DPolygon([a]);if(!d)continue;n.push(...d.map(I=>I+t.length/3)),t.push(...a.flat());}}const f=[],l=Array.from(o);for(let e=0;e<s.length;e+=3){if(r.has(e))continue;const y=s[e],A=s[e+1],P=s[e+2];f.push(y,A,P);}c.getAttribute("POSITION")?.setArray(new Float32Array(l.concat(t))),c.getIndices()?.setArray(new Uint32Array(f.concat(n.map(e=>e+o.length/3)))),compactPrimitive(c);}}}function yt({maxX:i,maxY:b,minX:g,minY:m,offset:h}){return p=>{for(const[x,M]of p.getRoot().listNodes().flatMap(c=>c.getMesh()?.listPrimitives().map(o=>[c,o])??[])){const c=M.getAttribute("POSITION"),o=M.getIndices(),s=c?.getArray(),r=o?.getArray();if(!s||!r)continue;const t=[];for(let w=0;w<s.length;w+=3)t.push([s[w],s[w+1]]);const n=Z(t,Array.from(r)).map(w=>w.map($=>[$[0],$[1]]));if(G(n.flat(),[g,m,i,b]))continue;const f=[0,0];c.getMin(f);const l=[0,0];c.getMax(l);const e=Math.min(g,f[0]),y=Math.min(m,f[1]),A=Math.max(i,l[0]),P=Math.max(b,l[1]),u=[[e*2,y*2],[A*2,y*2],[A*2,P*2],[e*2,P*2]],a=s.length/3;let d=u.length;const I=n.map(w=>{const $=d;return d+=w.length,$}),S=u.flat().concat(n.flat().flat()),T=H(S,I,2),_=u.flatMap(w=>[w[0],w[1],h-x.getTranslation()[2]]),O=n.flat().flatMap(w=>[w[0],w[1],h-x.getTranslation()[2]]);c.setArray(new Float32Array([...s,..._,...O]));const E=n.map(w=>w.map($=>t.findIndex(k=>k[0]===$[0]&&k[1]===$[1]))),z=n.map(w=>{let $=0;for(let k=0;k<w.length;k++){const[N,X]=w[k],[Y,j]=w[(k+1)%w.length];$+=N*j-Y*X;}return $/2}),B=[];E.forEach((w,$)=>{const k=z[$]>0,N=n.slice(0,$).reduce((Y,j)=>Y+j.length,0),X=a+u.length+N;for(let Y=0;Y<w.length;Y++){const j=(Y+1)%w.length,v=w[Y],V=w[j],L=X+Y,F=X+j;k?B.push(v,F,V,F,v,L):B.push(v,V,F,F,L,v);}}),o.setArray(new Uint32Array([...r,...T.map(w=>w+a),...B])),compactPrimitive(M);}}}function G(i,b){const[g,m,h,p]=b,x=[[g,m],[h,m],[h,p],[g,p]],M=c=>{let o=false;for(let s=0,r=i.length-1;s<i.length;r=s++){const[t,n]=i[s],[f,l]=i[r],[e,y]=c;n>y!=l>y&&e<(f-t)*(y-n)/(l-n)+t&&(o=!o);}return o};return x.every(M)}function Z(i,b){const g=(c,o)=>c<o?`${c}_${o}`:`${o}_${c}`,m=new Map;for(let c=0;c<b.length;c+=3){const[o,s,r]=[b[c],b[c+1],b[c+2]];[[o,s],[s,r],[r,o]].forEach(([t,n])=>{const f=g(t,n);m.set(f,(m.get(f)||0)+1);});}const h=new Map;for(const[c,o]of m.entries()){if(o!==1)continue;const[s,r]=c.split("_").map(Number);h.has(s)||h.set(s,[]),h.has(r)||h.set(r,[]),h.get(s).push(r),h.get(r).push(s);}const p=new Set,x=[];for(const[c,o]of h.entries())for(const s of o){const r=g(c,s);if(p.has(r))continue;const t=[c];let n=c,f=s;for(p.add(r),t.push(f);f!==c;){const l=h.get(f);let e;for(const y of l){const A=g(f,y);if(y!==n&&!p.has(A)){e=y;break}}if(e===void 0)break;p.add(g(f,e)),t.push(e),n=f,f=e;}x.push(t.map(l=>i[l]));}function M(c,o){const[s,r]=c;let t=false;for(let n=0,f=o.length-1;n<o.length;f=n++){const[l,e]=o[n],[y,A]=o[f];e>r!=A>r&&s<(y-l)*(r-e)/(A-e)+l&&(t=!t);}return t}return x.filter((c,o)=>{const s=c[0];for(let r=0;r<x.length;r++)if(o!==r&&M(s,x[r]))return false;return true})}function At(i,b,g=3,m=1e-6){const h=Math.floor(i.length/g);if(h<1)return {east:[],north:[],west:[],south:[],eastSegments:[],northSegments:[],westSegments:[],southSegments:[]};let p=1/0,x=-1/0,M=1/0,c=-1/0;for(let u=0;u<h;u++){const a=i[u*g],d=i[u*g+1];p=Math.min(p,a),x=Math.max(x,a),M=Math.min(M,d),c=Math.max(c,d);}const o=[],s=[],r=[],t=[];for(let u=0;u<h;u++){const a=i[u*g],d=i[u*g+1];Math.abs(a-x)<=m&&o.push(u),Math.abs(a-p)<=m&&r.push(u),Math.abs(d-c)<=m&&s.push(u),Math.abs(d-M)<=m&&t.push(u);}o.sort((u,a)=>i[u*g+1]-i[a*g+1]),r.sort((u,a)=>i[u*g+1]-i[a*g+1]),s.sort((u,a)=>i[u*g]-i[a*g]),t.sort((u,a)=>i[u*g]-i[a*g]);const n=new Map;function f(u,a){n.has(u)||n.set(u,new Set),n.has(a)||n.set(a,new Set),n.get(u).add(a),n.get(a).add(u);}for(let u=0;u<b.length;u+=3){const[a,d,I]=[b[u],b[u+1],b[u+2]];f(a,d),f(d,I),f(I,a);}function l(u){const a=[];if(u.length===0)return a;let d=[u[0]];for(let I=1;I<u.length;I++){const S=u[I-1],T=u[I];n.get(S)?.has(T)?d.push(T):(a.push(d),d=[T]);}return a.push(d),a}const e=l(o),y=l(r),A=l(s),P=l(t);return {east:o,north:s,west:r,south:t,eastSegments:e,northSegments:A,westSegments:y,southSegments:P}}function wt(i=Math.PI/3){return b=>{for(const g of b.getRoot().listMeshes().flatMap(m=>m.listPrimitives())){const m=g.getIndices(),h=g.getAttribute("POSITION");if(!m||!h)continue;const p=m.getArray(),x=h.getArray();if(!p||!x)continue;const{indices:M,vertices:c}=J(Array.from(x),Array.from(p),i);m.setArray(new Uint32Array(M)),h.setArray(new Float32Array(c)),compactPrimitive(g);}}}function J(i,b,g){const m=b.length/3,h=new Array(m*3);for(let t=0;t<m;t++){const n=b[3*t+0],f=b[3*t+1],l=b[3*t+2],e=i[3*n+0],y=i[3*n+1],A=i[3*n+2],P=i[3*f+0],u=i[3*f+1],a=i[3*f+2],d=i[3*l+0],I=i[3*l+1],S=i[3*l+2],T=P-e,_=u-y,O=a-A,E=d-e,z=I-y,B=S-A;let w=_*B-O*z,$=O*E-T*B,k=T*z-_*E;const N=Math.hypot(w,$,k)||1;w/=N,$/=N,k/=N,h[3*t+0]=w,h[3*t+1]=$,h[3*t+2]=k;}const p=Math.cos(g),x=new Map;for(let t=0;t<m;t++){const[n,f,l]=[b[3*t+0],b[3*t+1],b[3*t+2]];for(const[e,y]of [[n,f],[f,l],[l,n]]){const A=e<y?`${e}_${y}`:`${y}_${e}`,P=x.get(A);P?P.push(t):x.set(A,[t]);}}const M=new Set;for(const[t,n]of x)if(n.length===2){const[f,l]=n,e=h.slice(3*f,3*f+3),y=h.slice(3*l,3*l+3);e[0]*y[0]+e[1]*y[1]+e[2]*y[2]<p&&M.add(t);}const c=new Map;for(let t=0;t<m;t++)for(let n=0;n<3;n++){const f=b[3*t+n],l=c.get(f);l?l.push(t):c.set(f,[t]);}const o=new Map,s=[],r=new Array(b.length);for(const[t,n]of c){const f=new Map;n.forEach(e=>f.set(e,new Set));for(let e=0;e<n.length;e++)for(let y=e+1;y<n.length;y++){const A=n[e],P=n[y],u=[b[3*A],b[3*A+1],b[3*A+2]],a=[b[3*P],b[3*P+1],b[3*P+2]];if(!u.includes(t)||!a.includes(t))continue;const I=u.filter(S=>S!==t).find(S=>a.includes(S));if(I!==void 0){const S=t<I?`${t}_${I}`:`${I}_${t}`;M.has(S)||(f.get(A).add(P),f.get(P).add(A));}}const l=new Set;for(const e of n){if(l.has(e))continue;const y=[e];l.add(e);const A=[];for(;y.length;){const I=y.pop();A.push(I);for(const S of f.get(I))l.has(S)||(l.add(S),y.push(S));}const P=s.length/3,u=i[3*t],a=i[3*t+1],d=i[3*t+2];s.push(u,a,d);for(const I of A)for(let S=0;S<3;S++)b[3*I+S]===t&&o.set(`${I}_${S}`,P);}}for(let t=0;t<m;t++)for(let n=0;n<3;n++){const f=`${t}_${n}`,l=o.get(f);if(l===void 0)throw new Error(`Missing mapping for face ${t} corner ${n}`);r[3*t+n]=l;}return {vertices:s,indices:r}}function Mt(i={eps:.1}){return b=>{for(const g of b.getRoot().listMeshes().flatMap(m=>m.listPrimitives())){const m=g.getAttribute("POSITION"),h=g.getIndices();if(!m||!h)continue;const p=m.getArray(),x=h.getArray();if(!p||!x)continue;const{vertices:M,indices:c}=Q(p,x,i.eps);m.setArray(M),h.setArray(c);}}}function Q(i,b,g=1e-4){const m=i.length/3,h=new Map,p=[],x=new Uint32Array(m),M=g*g;function c(s,r,t){const n=Math.round(s/g),f=Math.round(r/g),l=Math.round(t/g);return `${n}_${f}_${l}`}for(let s=0;s<m;s++){const r=i[s*3],t=i[s*3+1],n=i[s*3+2],f=c(r,t,n);if(h.has(f)){const e=h.get(f),y=p[e*3],A=p[e*3+1],P=p[e*3+2],u=r-y,a=t-A,d=n-P;if(u*u+a*a+d*d<=M){x[s]=e;continue}}const l=p.length/3;h.set(f,l),p.push(r,t,n),x[s]=l;}const o=[];for(let s=0;s<b.length;s+=3){const r=x[b[s]],t=x[b[s+1]],n=x[b[s+2]];r===t||t===n||n===r||o.push(r,t,n);}return tt(o),{vertices:new Float32Array(p),indices:new Uint32Array(o)}}function tt(i){const b=new Map,g=i.length/3;for(let m=0;m<g;m++){const h=m*3,[p,x,M]=[i[h],i[h+1],i[h+2]],c=[[p,x],[x,M],[M,p]];for(const[o,s]of c){const r=o<s?`${o}_${s}`:`${s}_${o}`,t=o<s?1:-1;b.has(r)||b.set(r,[]),b.get(r).push({face:m,dir:t});}}for(const m of b.values())if(m.length===2&&m[0].dir===m[1].dir){const p=m[1].face*3;[i[p+1],i[p+2]]=[i[p+2],i[p+1]];}}export{Mt as betterWeld,xt as cut,yt as expandWithExternalPolygons,Z as extractOuterBoundaryRings,gt as flipWindingOrder,At as getSkirtIndicesFlat,pt as resampleGrid,wt as splitSharpEdges};//# sourceMappingURL=functions.js.map //# sourceMappingURL=functions.js.map