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}