nx_mechparts.nx source
↩ module page · 176 lines · 9689 B
1// nx_mechparts.nx -- proper procedural MECHANICAL PRIMITIVES for the CAD car-twin viewer (cadtwin R1b-lite):
2// real tessellated cylinders (24-gon), hex nuts WITH a real through-hole, bolts (shaft + hex head), washers,
3// L-brackets, plates -- so /explodelab parts read as actual HARDWARE instead of the crude octagon-prism
4// stand-ins. Emits straight into the nx_explode3d part buffers (ex3_vert/ex3_tri), all <=16 args, wasm-safe
5// (no mmap in loops -- uses the e[13] scratch), deterministic integer. NOT a STEP B-rep tessellation (that
6// needs a fixed-point B-spline evaluator = the deeper R1b rung); these are parametric primitives, the way CAD
7// part catalogs themselves generate standard hardware. license_tier: ORIGINAL
8import "nx_explode3d.nx"
9const K_MAGIC_16384: i64 = 16384
10const K_MAGIC_15827: i64 = 15827
11const K_MAGIC_4240: i64 = 4240
12const K_MAGIC_14189: i64 = 14189
13const K_MAGIC_8192: i64 = 8192
14const K_MAGIC_11585: i64 = 11585
15const K_MAGIC_1183: i64 = 1183
16const K_MAGIC_1024: i64 = 1024
17
18// 24-gon unit ring (Q14, 16384=1.0) at 15deg steps. nseg must divide 24 (6/8/12/24). out2[0]=cos out2[1]=sin.
19func mp_ring(k: i64, nseg: i64, out2: *i64) -> i64 {
20 let step: i64 = 24 / nseg
21 let i: i64 = (k % nseg) * step
22 if i == 0 { out2[0] = K_MAGIC_16384; out2[1] = 0; return 0 }
23 if i == 1 { out2[0] = K_MAGIC_15827; out2[1] = K_MAGIC_4240; return 0 }
24 if i == 2 { out2[0] = K_MAGIC_14189; out2[1] = K_MAGIC_8192; return 0 }
25 if i == 3 { out2[0] = K_MAGIC_11585; out2[1] = K_MAGIC_11585; return 0 }
26 if i == 4 { out2[0] = K_MAGIC_8192; out2[1] = K_MAGIC_14189; return 0 }
27 if i == 5 { out2[0] = K_MAGIC_4240; out2[1] = K_MAGIC_15827; return 0 }
28 if i == 6 { out2[0] = 0; out2[1] = K_MAGIC_16384; return 0 }
29 if i == 7 { out2[0] = 0 - K_MAGIC_4240; out2[1] = K_MAGIC_15827; return 0 }
30 if i == 8 { out2[0] = 0 - K_MAGIC_8192; out2[1] = K_MAGIC_14189; return 0 }
31 if i == 9 { out2[0] = 0 - K_MAGIC_11585; out2[1] = K_MAGIC_11585; return 0 }
32 if i == 10 { out2[0] = 0 - K_MAGIC_14189; out2[1] = K_MAGIC_8192; return 0 }
33 if i == 11 { out2[0] = 0 - K_MAGIC_15827; out2[1] = K_MAGIC_4240; return 0 }
34 if i == 12 { out2[0] = 0 - K_MAGIC_16384; out2[1] = 0; return 0 }
35 if i == 13 { out2[0] = 0 - K_MAGIC_15827; out2[1] = 0 - K_MAGIC_4240; return 0 }
36 if i == 14 { out2[0] = 0 - K_MAGIC_14189; out2[1] = 0 - K_MAGIC_8192; return 0 }
37 if i == 15 { out2[0] = 0 - K_MAGIC_11585; out2[1] = 0 - K_MAGIC_11585; return 0 }
38 if i == 16 { out2[0] = 0 - K_MAGIC_8192; out2[1] = 0 - K_MAGIC_14189; return 0 }
39 if i == 17 { out2[0] = 0 - K_MAGIC_4240; out2[1] = 0 - K_MAGIC_15827; return 0 }
40 if i == 18 { out2[0] = 0; out2[1] = 0 - K_MAGIC_16384; return 0 }
41 if i == 19 { out2[0] = K_MAGIC_4240; out2[1] = 0 - K_MAGIC_15827; return 0 }
42 if i == 20 { out2[0] = K_MAGIC_8192; out2[1] = 0 - K_MAGIC_14189; return 0 }
43 if i == 21 { out2[0] = K_MAGIC_11585; out2[1] = 0 - K_MAGIC_11585; return 0 }
44 if i == 22 { out2[0] = K_MAGIC_14189; out2[1] = 0 - K_MAGIC_8192; return 0 }
45 out2[0] = K_MAGIC_15827; out2[1] = 0 - K_MAGIC_4240
46 return 0
47}
48
49// 3D point of ring angle (rc,rs = r*cos, r*sin already scaled) at cap offset 'along' on 'axis' (0=y 1=x 2=z)
50func mp_pt(e: *i64, cx: i64, cy: i64, cz: i64, rc: i64, rs: i64, along: i64, axis: i64, color: i64) -> i64 {
51 if axis == 0 { return ex3_vert(e, cx + rc, cy + along, cz + rs, color) }
52 if axis == 1 { return ex3_vert(e, cx + along, cy + rc, cz + rs, color) }
53 return ex3_vert(e, cx + rc, cy + rs, cz + along, color)
54}
55
56// tessellated prism/cylinder: nseg-gon of radius r, half-height hh, centered at c, along axis. Emits side
57// quads + 2 fan caps. Set nseg=24 -> cylinder; nseg=6 -> hex bar; nseg=8 -> octagon.
58func mp_prism(e: *i64, cx: i64, cy: i64, cz: i64, r: i64, hh: i64, axis: i64, nseg: i64, color: i64) -> i64 {
59 let out2: *i64 = e[13] as *i64
60 // top + bottom center verts
61 var tc: i64 = 0
62 var bc: i64 = 0
63 if axis == 0 { tc = ex3_vert(e, cx, cy + hh, cz, color); bc = ex3_vert(e, cx, cy - hh, cz, color) }
64 if axis == 1 { tc = ex3_vert(e, cx + hh, cy, cz, color); bc = ex3_vert(e, cx - hh, cy, cz, color) }
65 if axis == 2 { tc = ex3_vert(e, cx, cy, cz + hh, color); bc = ex3_vert(e, cx, cy, cz - hh, color) }
66 // rim verts: for each k, a top and bottom vert. first = index of k=0 top.
67 var first: i64 = 0 - 1
68 var k: i64 = 0
69 while k < nseg {
70 mp_ring(k, nseg, out2)
71 let rc: i64 = (out2[0] * r) / K_MAGIC_16384
72 let rs: i64 = (out2[1] * r) / K_MAGIC_16384
73 let t: i64 = mp_pt(e, cx, cy, cz, rc, rs, hh, axis, color)
74 let b: i64 = mp_pt(e, cx, cy, cz, rc, rs, 0 - hh, axis, color)
75 if k == 0 { first = t }
76 k = k + 1
77 }
78 // faces: rim vert of k -> top=first+2k, bottom=first+2k+1
79 k = 0
80 while k < nseg {
81 let k1: i64 = (k + 1) % nseg
82 let tk: i64 = first + 2 * k
83 let bk: i64 = first + 2 * k + 1
84 let tk1: i64 = first + 2 * k1
85 let bk1: i64 = first + 2 * k1 + 1
86 let s1: i64 = ex3_tri(e, tk, bk, bk1)
87 let s2: i64 = ex3_tri(e, tk, bk1, tk1)
88 let c1: i64 = ex3_tri(e, tc, tk1, tk) // top cap fan
89 let c2: i64 = ex3_tri(e, bc, bk, bk1) // bottom cap fan
90 if c2 < 0 { return 0 - 1 }
91 k = k + 1
92 }
93 return 0
94}
95
96// ANNULAR prism (hex/round outer, round hole) = a NUT or WASHER. Outer nseg-gon radius or, inner 24-gon
97// hole radius ir, half-height hh, hole along 'axis'. Emits outer wall + inner wall (reversed) + 2 ring caps.
98func mp_annulus(e: *i64, cx: i64, cy: i64, cz: i64, or_: i64, ir: i64, hh: i64, axis: i64, nseg: i64, color: i64) -> i64 {
99 let out2: *i64 = e[13] as *i64
100 // emit 4 rings of verts: outer-top, outer-bot, inner-top, inner-bot -- interleaved per k on the OUTER nseg
101 // (inner sampled at the same nseg for matching cap quads). first = outer-top of k=0.
102 var first: i64 = 0 - 1
103 var k: i64 = 0
104 while k < nseg {
105 mp_ring(k, nseg, out2)
106 let oc: i64 = (out2[0] * or_) / K_MAGIC_16384
107 let os: i64 = (out2[1] * or_) / K_MAGIC_16384
108 let ic: i64 = (out2[0] * ir) / K_MAGIC_16384
109 let is: i64 = (out2[1] * ir) / K_MAGIC_16384
110 let ot: i64 = mp_pt(e, cx, cy, cz, oc, os, hh, axis, color)
111 let ob: i64 = mp_pt(e, cx, cy, cz, oc, os, 0 - hh, axis, color)
112 let it: i64 = mp_pt(e, cx, cy, cz, ic, is, hh, axis, color)
113 let ib: i64 = mp_pt(e, cx, cy, cz, ic, is, 0 - hh, axis, color)
114 if k == 0 { first = ot }
115 k = k + 1
116 }
117 // per k: ot=first+4k ob=+1 it=+2 ib=+3
118 k = 0
119 while k < nseg {
120 let k1: i64 = (k + 1) % nseg
121 let ot: i64 = first + 4 * k
122 let ob: i64 = first + 4 * k + 1
123 let it: i64 = first + 4 * k + 2
124 let ib: i64 = first + 4 * k + 3
125 let ot1: i64 = first + 4 * k1
126 let ob1: i64 = first + 4 * k1 + 1
127 let it1: i64 = first + 4 * k1 + 2
128 let ib1: i64 = first + 4 * k1 + 3
129 let w1: i64 = ex3_tri(e, ot, ob, ob1) // outer wall
130 let w2: i64 = ex3_tri(e, ot, ob1, ot1)
131 let n1: i64 = ex3_tri(e, it, ib1, ib) // inner wall (reversed)
132 let n2: i64 = ex3_tri(e, it, it1, ib1)
133 let t1: i64 = ex3_tri(e, ot, ot1, it1) // top ring cap
134 let t2: i64 = ex3_tri(e, ot, it1, it)
135 let b1: i64 = ex3_tri(e, ob, ib1, ob1) // bottom ring cap
136 let b2: i64 = ex3_tri(e, ob, ib, ib1)
137 if b2 < 0 { return 0 - 1 }
138 k = k + 1
139 }
140 return 0
141}
142
143// HEX NUT: hex outer (nseg=6) + round through-hole. axis = hole axis.
144func mp_hexnut(e: *i64, cx: i64, cy: i64, cz: i64, across: i64, hh: i64, axis: i64, color: i64) -> i64 {
145 // 'across' = half across-flats; hex circumradius ~ across*2/sqrt3 ~ across*1183/1024; hole ~ 0.6*across
146 let orad: i64 = (across * K_MAGIC_1183) / K_MAGIC_1024
147 let ir: i64 = (across * 600) / K_MAGIC_1024
148 return mp_annulus(e, cx, cy, cz, orad, ir, hh, axis, 6, color)
149}
150
151// BOLT: cylindrical shaft (radius sr, half-len sl) along axis + a hex head (radius hr, half-height hd) on the
152// +axis end. head_end = +1 -> head at +axis, shaft extends -axis.
153func mp_bolt(e: *i64, cx: i64, cy: i64, cz: i64, sr: i64, sl: i64, hr: i64, hd: i64, axis: i64, color: i64) -> i64 {
154 // shaft centered so its +axis top meets the head bottom. head sits just beyond +sl.
155 var shx: i64 = cx
156 var shy: i64 = cy
157 var shz: i64 = cz
158 var hcx: i64 = cx
159 var hcy: i64 = cy
160 var hcz: i64 = cz
161 if axis == 0 { shy = cy - hd; hcy = cy + sl }
162 if axis == 1 { shx = cx - hd; hcx = cx + sl }
163 if axis == 2 { shz = cz - hd; hcz = cz + sl }
164 let a: i64 = mp_prism(e, shx, shy, shz, sr, sl, axis, 24, color) // shaft (round)
165 let b: i64 = mp_prism(e, hcx, hcy, hcz, hr, hd, axis, 6, color) // hex head
166 return b
167}
168
169// L-BRACKET: an L cross-section extruded. Two boxes sharing a corner: a base slab + an upright slab.
170// leg = arm half-length, th = wall half-thickness, w = half-width (extrusion depth in z).
171func mp_lbracket(e: *i64, cx: i64, cy: i64, cz: i64, leg: i64, th: i64, w: i64, color: i64) -> i64 {
172 // base slab: sits along +x, thin in y (bottom). upright slab: along +y, thin in x (back).
173 let a: i64 = ex3_add_box(e, cx - leg + th, cy - leg + th, cz, leg, th, w, color) // horizontal arm
174 let b: i64 = ex3_add_box(e, cx - leg + th, cy - leg + th, cz, th, leg, w, color) // vertical arm
175 return b
176}