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 · drag=orbit wheel=zoom · 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}