code wiki / _hdl_build / nx_stl_view_html.nx

nx_stl_view_html.nx source

↩ module page · 121 lines · 8844 B

1// nx_stl_view_html.nx -- "SEE THE CAD OF THE STL" rung (operator: a polyrook.com-class 2// browser view of an STL that can later be edited). SOVEREIGN: the team emits its OWN 3// self-contained HTML + hand-authored WebGL orbit viewer -- NO three.js, nothing injected. 4// Composes nx_fab_slice's struct-free STL readers (fstl_tri_count / fstl_vert_um). Output is 5// a single .html: embedded triangle geometry (mm) as a JS array + a flat-shaded orbit/zoom 6// renderer that auto-fits the model. Editing (sculpt/boolean/repair) = the NEXT rung, backlog. 7// LAWS: struct-free, integer-only in NishiLang (the browser does float math, not us). 8// license_tier: ORIGINAL 9import "nx_fab_slice.nx" 10import "nx_syscalls.nx" 11 12// append NUL-terminated s into dst at off; return new offset 13func sv_cat(dst: *u8, off: i64, s: *u8) -> i64 { 14 var i: i64 = 0 15 while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 } 16 return off + i 17} 18 19// append a signed integer (decimal) into dst at off 20func sv_cati(dst: *u8, o: i64, vin: i64) -> i64 { 21 var off: i64 = o 22 var v: i64 = vin 23 if v < 0 { dst[off] = 45 as u8; off = off + 1; v = 0 - v } 24 let t: *u8 = sys_mmap(32); var k: i64 = 0 25 if v == 0 { t[0] = 48 as u8; k = 1 } 26 while v > 0 { t[k] = (48 + (v % 10)) as u8; v = v / 10; k = k + 1 } 27 var i: i64 = 0 28 while i < k { dst[off] = t[k-1-i]; off = off + 1; i = i + 1 } 29 return off 30} 31 32// append micrometers as fixed mm.mmm (3 decimals) into dst at off 33func sv_catmm(dst: *u8, o: i64, vin: i64) -> i64 { 34 var off: i64 = o 35 var v: i64 = vin 36 if v < 0 { dst[off] = 45 as u8; off = off + 1; v = 0 - v } 37 off = sv_cati(dst, off, v / 1000) 38 dst[off] = 46 as u8; off = off + 1 39 let fr: i64 = v % 1000 40 dst[off] = (48 + (fr / 100) % 10) as u8; off = off + 1 41 dst[off] = (48 + (fr / 10) % 10) as u8; off = off + 1 42 dst[off] = (48 + fr % 10) as u8; off = off + 1 43 return off 44} 45 46// emit the JS triangle-vertex array body "x,y,z,..." (mm) for every vertex of every triangle. 47// returns the new offset. n triangles * 3 verts * 3 axes numbers. 48func sv_emit_geometry(stl: *u8, ntri: i64, out: *u8, o: i64) -> i64 { 49 var off: i64 = o 50 var t: i64 = 0 51 while t < ntri { 52 var vtx: i64 = 0 53 while vtx < 3 { 54 var ax: i64 = 0 55 while ax < 3 { 56 off = sv_catmm(out, off, fstl_vert_um(stl, t, vtx, ax)) 57 let last: i64 = (t == ntri-1) & (vtx == 2) & (ax == 2) 58 if last == 0 { out[off] = 44 as u8; off = off + 1 } // comma between, not after last 59 ax = ax + 1 60 } 61 vtx = vtx + 1 62 } 63 t = t + 1 64 } 65 return off 66} 67 68// emit the complete self-contained viewer HTML for the parsed STL into out; return total length. 69// The WebGL viewer is authored here as static chunks; only the geometry array is data-driven. 70func nx_stl_view_emit(stl: *u8, out: *u8) -> i64 { 71 let ntri: i64 = fstl_tri_count(stl) 72 var off: i64 = 0 73 off = sv_cat(out, off, "<!doctype html><html><head><meta charset=utf-8><title>Nishi STL Viewer</title>" as *u8) 74 off = sv_cat(out, off, "<style>html,body{margin:0;height:100%;background:#0b0d10;overflow:hidden;font:13px system-ui,sans-serif;color:#9aa}" as *u8) 75 off = sv_cat(out, off, "#c{width:100vw;height:100vh;display:block;cursor:grab}#hud{position:fixed;left:10px;top:8px}</style></head><body>" as *u8) 76 off = sv_cat(out, off, "<canvas id=c></canvas><div id=hud>Nishi sovereign viewer &middot; drag=orbit wheel=zoom &middot; tris=" as *u8) 77 off = sv_cati(out, off, ntri) 78 off = sv_cat(out, off, "</div><script>\nvar V=[" as *u8) 79 off = sv_emit_geometry(stl, ntri, out, off) 80 off = sv_cat(out, off, "];\n" as *u8) 81 // ---- sovereign WebGL orbit viewer (flat shading, CPU per-face normals, auto-fit) ---- 82 off = sv_cat(out, off, "var cv=document.getElementById('c'),gl=cv.getContext('webgl');\n" as *u8) 83 off = sv_cat(out, off, "function rs(){cv.width=cv.clientWidth;cv.height=cv.clientHeight;gl.viewport(0,0,cv.width,cv.height);}window.onresize=rs;rs();\n" as *u8) 84 // build interleaved position+normal buffer with flat per-face normals; track bbox for auto-fit 85 off = sv_cat(out, off, "var lo=[1e9,1e9,1e9],hi=[-1e9,-1e9,-1e9],data=[];\n" as *u8) 86 off = sv_cat(out, off, "for(var i=0;i<V.length;i+=9){var ax=V[i],ay=V[i+1],az=V[i+2],bx=V[i+3],by=V[i+4],bz=V[i+5],cx=V[i+6],cy=V[i+7],cz=V[i+8];" as *u8) 87 off = sv_cat(out, off, "var ux=bx-ax,uy=by-ay,uz=bz-az,vx=cx-ax,vy=cy-ay,vz=cz-az;var nx=uy*vz-uz*vy,ny=uz*vx-ux*vz,nz=ux*vy-uy*vx;" as *u8) 88 off = sv_cat(out, off, "var l=Math.hypot(nx,ny,nz)||1;nx/=l;ny/=l;nz/=l;var tri=[ax,ay,az,bx,by,bz,cx,cy,cz];" as *u8) 89 off = sv_cat(out, off, "for(var j=0;j<9;j+=3){var px=tri[j],py=tri[j+1],pz=tri[j+2];data.push(px,py,pz,nx,ny,nz);" as *u8) 90 off = sv_cat(out, off, "lo[0]=Math.min(lo[0],px);lo[1]=Math.min(lo[1],py);lo[2]=Math.min(lo[2],pz);hi[0]=Math.max(hi[0],px);hi[1]=Math.max(hi[1],py);hi[2]=Math.max(hi[2],pz);}}\n" as *u8) 91 off = sv_cat(out, off, "var ctr=[(lo[0]+hi[0])/2,(lo[1]+hi[1])/2,(lo[2]+hi[2])/2];var rad=Math.max(hi[0]-lo[0],hi[1]-lo[1],hi[2]-lo[2])/2||1;\n" as *u8) 92 off = sv_cat(out, off, "var buf=gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER,buf);gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(data),gl.STATIC_DRAW);\n" as *u8) 93 // shaders 94 off = sv_cat(out, off, "var vs='attribute vec3 p;attribute vec3 n;uniform mat4 m;uniform mat4 mv;varying vec3 vn;void main(){vn=mat3(mv)*n;gl_Position=m*vec4(p,1.0);}';\n" as *u8) 95 off = sv_cat(out, off, "var fs='precision mediump float;varying vec3 vn;void main(){vec3 N=normalize(vn);float d=max(dot(N,normalize(vec3(0.4,0.7,0.6))),0.0)*0.8+0.2;gl_FragColor=vec4(0.62*d,0.70*d,0.80*d,1.0);}';\n" as *u8) 96 off = sv_cat(out, off, "function sh(t,s){var o=gl.createShader(t);gl.shaderSource(o,s);gl.compileShader(o);return o;}\n" as *u8) 97 off = sv_cat(out, off, "var pg=gl.createProgram();gl.attachShader(pg,sh(gl.VERTEX_SHADER,vs));gl.attachShader(pg,sh(gl.FRAGMENT_SHADER,fs));gl.linkProgram(pg);gl.useProgram(pg);\n" as *u8) 98 off = sv_cat(out, off, "var ap=gl.getAttribLocation(pg,'p'),an=gl.getAttribLocation(pg,'n');gl.enableVertexAttribArray(ap);gl.enableVertexAttribArray(an);\n" as *u8) 99 off = sv_cat(out, off, "gl.vertexAttribPointer(ap,3,gl.FLOAT,false,24,0);gl.vertexAttribPointer(an,3,gl.FLOAT,false,24,12);gl.enable(gl.DEPTH_TEST);\n" as *u8) 100 // matrix helpers 101 off = sv_cat(out, off, "function mul(a,b){var o=new Float32Array(16);for(var r=0;r<4;r++)for(var c=0;c<4;c++){var s=0;for(var k=0;k<4;k++)s+=a[k*4+r]*b[c*4+k];o[c*4+r]=s;}return o;}\n" as *u8) 102 off = sv_cat(out, off, "function persp(f,as,n,fa){var t=1/Math.tan(f/2);return new Float32Array([t/as,0,0,0, 0,t,0,0, 0,0,(fa+n)/(n-fa),-1, 0,0,2*fa*n/(n-fa),0]);}\n" as *u8) 103 off = sv_cat(out, off, "var yaw=0.6,pit=0.4,dist=rad*3.2;\n" as *u8) 104 off = sv_cat(out, off, "function draw(){gl.clearColor(0.043,0.051,0.063,1);gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);" as *u8) 105 off = sv_cat(out, off, "var cy=Math.cos(yaw),sy=Math.sin(yaw),cp=Math.cos(pit),sp=Math.sin(pit);" as *u8) 106 off = sv_cat(out, off, "var ex=ctr[0]+dist*cp*sy,ey=ctr[1]+dist*sp,ez=ctr[2]+dist*cp*cy;" as *u8) 107 // lookAt: f=fwd(ctr-eye), s=right(f x up), u=s x f ; column-major view matrix 108 off = sv_cat(out, off, "var fx=ctr[0]-ex,fy=ctr[1]-ey,fz=ctr[2]-ez,fl=Math.hypot(fx,fy,fz)||1;fx/=fl;fy/=fl;fz/=fl;" as *u8) 109 off = sv_cat(out, off, "var sx=fy*0-fz*1,sy2=fz*0-fx*0,sz=fx*1-fy*0,sl=Math.hypot(sx,sy2,sz)||1;sx/=sl;sy2/=sl;sz/=sl;" as *u8) 110 off = sv_cat(out, off, "var ux=sy2*fz-sz*fy,uy=sz*fx-sx*fz,uz=sx*fy-sy2*fx;" as *u8) 111 off = sv_cat(out, off, "var view=new Float32Array([sx,ux,-fx,0, sy2,uy,-fy,0, sz,uz,-fz,0, -(sx*ex+sy2*ey+sz*ez),-(ux*ex+uy*ey+uz*ez),fx*ex+fy*ey+fz*ez,1]);" as *u8) 112 off = sv_cat(out, off, "var pr=persp(1.0,cv.width/cv.height,rad*0.05,dist+rad*4);var mvp=mul(pr,view);" as *u8) 113 off = sv_cat(out, off, "gl.uniformMatrix4fv(gl.getUniformLocation(pg,'m'),false,mvp);gl.uniformMatrix4fv(gl.getUniformLocation(pg,'mv'),false,view);" as *u8) 114 off = sv_cat(out, off, "gl.drawArrays(gl.TRIANGLES,0,data.length/6);}\n" as *u8) 115 off = sv_cat(out, off, "var dn=false,lx=0,ly=0;cv.onmousedown=function(e){dn=true;lx=e.clientX;ly=e.clientY;};window.onmouseup=function(){dn=false;};" as *u8) 116 off = sv_cat(out, off, "window.onmousemove=function(e){if(!dn)return;yaw+=(e.clientX-lx)*0.01;pit+=(e.clientY-ly)*0.01;pit=Math.max(-1.5,Math.min(1.5,pit));lx=e.clientX;ly=e.clientY;draw();};" as *u8) 117 off = sv_cat(out, off, "cv.onwheel=function(e){e.preventDefault();dist*=(e.deltaY>0?1.1:0.9);dist=Math.max(rad*1.2,dist);draw();};\n" as *u8) 118 off = sv_cat(out, off, "draw();\n</script></body></html>\n" as *u8) 119 out[off] = 0 as u8 120 return off 121}