code wiki / (root) / nx_mesh_edit.nx

nx_mesh_edit.nx source

↩ module page · 160 lines · 6281 B

1// nx_mesh_edit.nx -- DCC modeling verbs on NxMesh (Blender-class), all sovereign. 2// 3// The mesh kernel (nx_mesh) had factories + validate but no EDITING verbs -- the 4// operations a modeler is made of. This adds the fundamentals: rigid transform 5// (translate / uniform scale), bounding box, triangle subdivision (the density 6// lever a sculptor needs), and a square-pyramid factory (a non-cube solid for the 7// model->print capstone). All operate in the pipeline-native Q14-mm units so the 8// output flows straight to nx_stl_write_mesh -> slicer. No dup: grepped the 9// runtime, no nx_mesh_{translate,scale,subdivide,aabb} or pyramid existed. 10// license_tier: ORIGINAL. 11 12import "nx_syscalls.nx" 13import "nx_mesh.nx" 14 15// ===== rigid transform (in-place) ================================ 16 17// translate every vertex by (dx,dy,dz) Q14. In-place. 18func nx_mesh_translate(m: *NxMesh, dx: i64, dy: i64, dz: i64) -> i64 { 19 var i: i64 = 0 20 while i < m.n_verts { 21 let base: i64 = i * NX_MESH_VTX_STRIDE 22 m.verts[base + 0] = m.verts[base + 0] + dx 23 m.verts[base + 1] = m.verts[base + 1] + dy 24 m.verts[base + 2] = m.verts[base + 2] + dz 25 i = i + 1 26 } 27 return NX_MESH_OK 28} 29 30// uniform scale about the origin by the rational factor num/den (exact integer 31// arithmetic -- avoids f64 codegen, which is currently unreliable). In-place. 32func nx_mesh_scale_rat(m: *NxMesh, num: i64, den: i64) -> i64 { 33 if den == 0 { return NX_MESH_ERR_BAD_DIMS } 34 var i: i64 = 0 35 while i < m.n_verts { 36 let base: i64 = i * NX_MESH_VTX_STRIDE 37 m.verts[base + 0] = m.verts[base + 0] * num / den 38 m.verts[base + 1] = m.verts[base + 1] * num / den 39 m.verts[base + 2] = m.verts[base + 2] * num / den 40 i = i + 1 41 } 42 return NX_MESH_OK 43} 44 45// ===== axis-aligned bounding box ================================= 46// 47// Writes [min_x,min_y,min_z, max_x,max_y,max_z] (Q14) into out6. 48// Used to size the slice job + to assert transform invariants. 49 50func nx_mesh_aabb(m: *NxMesh, out6: *i64) -> i64 { 51 if m.n_verts <= 0 { return NX_MESH_ERR_BAD_DIMS } 52 var minx: i64 = nx_mesh_get_vertex_x(m, 0) 53 var maxx: i64 = minx 54 var miny: i64 = nx_mesh_get_vertex_y(m, 0) 55 var maxy: i64 = miny 56 var minz: i64 = nx_mesh_get_vertex_z(m, 0) 57 var maxz: i64 = minz 58 var i: i64 = 1 59 while i < m.n_verts { 60 let x: i64 = nx_mesh_get_vertex_x(m, i) 61 let y: i64 = nx_mesh_get_vertex_y(m, i) 62 let z: i64 = nx_mesh_get_vertex_z(m, i) 63 if x < minx { minx = x } 64 if x > maxx { maxx = x } 65 if y < miny { miny = y } 66 if y > maxy { maxy = y } 67 if z < minz { minz = z } 68 if z > maxz { maxz = z } 69 i = i + 1 70 } 71 out6[0] = minx 72 out6[1] = miny 73 out6[2] = minz 74 out6[3] = maxx 75 out6[4] = maxy 76 out6[5] = maxz 77 return NX_MESH_OK 78} 79 80// ===== triangle subdivision (1 -> 4) ============================= 81// 82// Splits every triangle by its three edge midpoints into 4 sub-triangles, 83// preserving winding + surface exactly (midpoints lie on the original edges, 84// so a flat face stays flat; AABB is unchanged). Quadruples triangle count 85// and is the density lever a sculpt brush needs. Midpoints are appended 86// un-deduped (shared edges produce coincident verts); the STL writer/reader 87// re-dedup on export, so topology stays valid. Returns a NEW mesh. 88 89func nx_mesh_subdivide(m: *NxMesh) -> *NxMesh { 90 let nv0: i64 = m.n_verts 91 let nt0: i64 = m.n_tris 92 let out: *NxMesh = nx_mesh_alloc(nv0 + nt0 * 3, nt0 * 4, 0) 93 if (out as i64) == 0 { return out } 94 95 var i: i64 = 0 96 while i < nv0 { 97 nx_mesh_set_vertex(out, i, 98 nx_mesh_get_vertex_x(m, i), 99 nx_mesh_get_vertex_y(m, i), 100 nx_mesh_get_vertex_z(m, i), 101 nx_mesh_get_color(m, i)) 102 i = i + 1 103 } 104 105 var next_v: i64 = nv0 106 var t: i64 = 0 107 while t < nt0 { 108 let a: i64 = m.indices[t * NX_MESH_IDX_STRIDE + 0] 109 let b: i64 = m.indices[t * NX_MESH_IDX_STRIDE + 1] 110 let c: i64 = m.indices[t * NX_MESH_IDX_STRIDE + 2] 111 let ax: i64 = nx_mesh_get_vertex_x(m, a) 112 let ay: i64 = nx_mesh_get_vertex_y(m, a) 113 let az: i64 = nx_mesh_get_vertex_z(m, a) 114 let bx: i64 = nx_mesh_get_vertex_x(m, b) 115 let by: i64 = nx_mesh_get_vertex_y(m, b) 116 let bz: i64 = nx_mesh_get_vertex_z(m, b) 117 let cx: i64 = nx_mesh_get_vertex_x(m, c) 118 let cy: i64 = nx_mesh_get_vertex_y(m, c) 119 let cz: i64 = nx_mesh_get_vertex_z(m, c) 120 let mab: i64 = next_v 121 let mbc: i64 = next_v + 1 122 let mca: i64 = next_v + 2 123 nx_mesh_set_vertex(out, mab, (ax + bx) / 2, (ay + by) / 2, (az + bz) / 2, nx_mesh_get_color(m, a)) 124 nx_mesh_set_vertex(out, mbc, (bx + cx) / 2, (by + cy) / 2, (bz + cz) / 2, nx_mesh_get_color(m, b)) 125 nx_mesh_set_vertex(out, mca, (cx + ax) / 2, (cy + ay) / 2, (cz + az) / 2, nx_mesh_get_color(m, c)) 126 next_v = next_v + 3 127 nx_mesh_set_triangle(out, t * 4 + 0, a, mab, mca) 128 nx_mesh_set_triangle(out, t * 4 + 1, mab, b, mbc) 129 nx_mesh_set_triangle(out, t * 4 + 2, mca, mbc, c) 130 nx_mesh_set_triangle(out, t * 4 + 3, mab, mbc, mca) 131 t = t + 1 132 } 133 return out 134} 135 136// ===== factory: square pyramid =================================== 137// 138// 5 verts (4 base corners at z=0 +/- half_q14, apex at (0,0,height_q14)), 139// 6 tris (2 base + 4 sides). A genuinely non-cube solid for the capstone. 140 141func nx_mesh_make_pyramid(half_q14: i64, height_q14: i64, color: i64) -> *NxMesh { 142 let m: *NxMesh = nx_mesh_alloc(5, 6, 0) 143 if (m as i64) == 0 { return m } 144 let h: i64 = half_q14 145 let nh: i64 = 0 - half_q14 146 nx_mesh_set_vertex(m, 0, nh, nh, 0, color) 147 nx_mesh_set_vertex(m, 1, h, nh, 0, color) 148 nx_mesh_set_vertex(m, 2, h, h, 0, color) 149 nx_mesh_set_vertex(m, 3, nh, h, 0, color) 150 nx_mesh_set_vertex(m, 4, 0, 0, height_q14, color) 151 // base (downward winding) 152 nx_mesh_set_triangle(m, 0, 0, 2, 1) 153 nx_mesh_set_triangle(m, 1, 0, 3, 2) 154 // 4 sides (outward winding) 155 nx_mesh_set_triangle(m, 2, 0, 1, 4) 156 nx_mesh_set_triangle(m, 3, 1, 2, 4) 157 nx_mesh_set_triangle(m, 4, 2, 3, 4) 158 nx_mesh_set_triangle(m, 5, 3, 0, 4) 159 return m 160}