use super::{SatDocument, SatPointer, SatToken, Sense, Sidedness};
fn ptr(i: i32) -> SatPointer {
SatPointer::new(i)
}
pub fn build_box(center: [f64; 3], length: f64, width: f64, height: f64) -> SatDocument {
let mut sat = SatDocument::new_body();
let body_idx = SatPointer::new(0);
let hl = length / 2.0;
let hw = width / 2.0;
let hh = height / 2.0;
let [cx, cy, cz] = center;
let p0 = sat.add_point(cx - hl, cy - hw, cz - hh);
let p1 = sat.add_point(cx + hl, cy - hw, cz - hh);
let p2 = sat.add_point(cx + hl, cy + hw, cz - hh);
let p3 = sat.add_point(cx - hl, cy + hw, cz - hh);
let p4 = sat.add_point(cx - hl, cy - hw, cz + hh);
let p5 = sat.add_point(cx + hl, cy - hw, cz + hh);
let p6 = sat.add_point(cx + hl, cy + hw, cz + hh);
let p7 = sat.add_point(cx - hl, cy + hw, cz + hh);
let surf_top = sat.add_plane_surface([cx, cy, cz + hh], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]);
let surf_bottom = sat.add_plane_surface([cx, cy, cz - hh], [0.0, 0.0, -1.0], [1.0, 0.0, 0.0]);
let surf_front = sat.add_plane_surface([cx, cy - hw, cz], [0.0, -1.0, 0.0], [1.0, 0.0, 0.0]);
let surf_back = sat.add_plane_surface([cx, cy + hw, cz], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0]);
let surf_right = sat.add_plane_surface([cx + hl, cy, cz], [ 1.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
let surf_left = sat.add_plane_surface([cx - hl, cy, cz], [-1.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
let crv_b0 = sat.add_straight_curve([cx - hl, cy - hw, cz - hh], [ 1.0, 0.0, 0.0]);
let crv_b1 = sat.add_straight_curve([cx + hl, cy - hw, cz - hh], [ 0.0, 1.0, 0.0]);
let crv_b2 = sat.add_straight_curve([cx + hl, cy + hw, cz - hh], [-1.0, 0.0, 0.0]);
let crv_b3 = sat.add_straight_curve([cx - hl, cy + hw, cz - hh], [ 0.0,-1.0, 0.0]);
let crv_t0 = sat.add_straight_curve([cx - hl, cy - hw, cz + hh], [ 1.0, 0.0, 0.0]);
let crv_t1 = sat.add_straight_curve([cx + hl, cy - hw, cz + hh], [ 0.0, 1.0, 0.0]);
let crv_t2 = sat.add_straight_curve([cx + hl, cy + hw, cz + hh], [-1.0, 0.0, 0.0]);
let crv_t3 = sat.add_straight_curve([cx - hl, cy + hw, cz + hh], [ 0.0,-1.0, 0.0]);
let crv_v0 = sat.add_straight_curve([cx - hl, cy - hw, cz - hh], [0.0, 0.0, 1.0]);
let crv_v1 = sat.add_straight_curve([cx + hl, cy - hw, cz - hh], [0.0, 0.0, 1.0]);
let crv_v2 = sat.add_straight_curve([cx + hl, cy + hw, cz - hh], [0.0, 0.0, 1.0]);
let crv_v3 = sat.add_straight_curve([cx - hl, cy + hw, cz - hh], [0.0, 0.0, 1.0]);
let v0 = sat.add_vertex(SatPointer::NULL, ptr(p0));
let v1 = sat.add_vertex(SatPointer::NULL, ptr(p1));
let v2 = sat.add_vertex(SatPointer::NULL, ptr(p2));
let v3 = sat.add_vertex(SatPointer::NULL, ptr(p3));
let v4 = sat.add_vertex(SatPointer::NULL, ptr(p4));
let v5 = sat.add_vertex(SatPointer::NULL, ptr(p5));
let v6 = sat.add_vertex(SatPointer::NULL, ptr(p6));
let v7 = sat.add_vertex(SatPointer::NULL, ptr(p7));
let e0 = sat.add_edge(ptr(v0), 0.0, ptr(v1), length, SatPointer::NULL, ptr(crv_b0), Sense::Forward);
let e1 = sat.add_edge(ptr(v1), 0.0, ptr(v2), width, SatPointer::NULL, ptr(crv_b1), Sense::Forward);
let e2 = sat.add_edge(ptr(v2), 0.0, ptr(v3), length, SatPointer::NULL, ptr(crv_b2), Sense::Forward);
let e3 = sat.add_edge(ptr(v3), 0.0, ptr(v0), width, SatPointer::NULL, ptr(crv_b3), Sense::Forward);
let e4 = sat.add_edge(ptr(v4), 0.0, ptr(v5), length, SatPointer::NULL, ptr(crv_t0), Sense::Forward);
let e5 = sat.add_edge(ptr(v5), 0.0, ptr(v6), width, SatPointer::NULL, ptr(crv_t1), Sense::Forward);
let e6 = sat.add_edge(ptr(v6), 0.0, ptr(v7), length, SatPointer::NULL, ptr(crv_t2), Sense::Forward);
let e7 = sat.add_edge(ptr(v7), 0.0, ptr(v4), width, SatPointer::NULL, ptr(crv_t3), Sense::Forward);
let e8 = sat.add_edge(ptr(v0), 0.0, ptr(v4), height, SatPointer::NULL, ptr(crv_v0), Sense::Forward);
let e9 = sat.add_edge(ptr(v1), 0.0, ptr(v5), height, SatPointer::NULL, ptr(crv_v1), Sense::Forward);
let e10 = sat.add_edge(ptr(v2), 0.0, ptr(v6), height, SatPointer::NULL, ptr(crv_v2), Sense::Forward);
let e11 = sat.add_edge(ptr(v3), 0.0, ptr(v7), height, SatPointer::NULL, ptr(crv_v3), Sense::Forward);
let base = sat.records.len() as i32;
let co_base = base;
let loop_base = base + 24;
let face_base = base + 30;
let shell_idx = base + 36;
let lump_idx = base + 37;
let co = |i: i32| co_base + i;
sat.add_coedge(ptr(co(1)), ptr(co(3)), ptr(co(8)), ptr(e0), Sense::Reversed, ptr(loop_base));
sat.add_coedge(ptr(co(2)), ptr(co(0)), ptr(co(20)), ptr(e3), Sense::Reversed, ptr(loop_base));
sat.add_coedge(ptr(co(3)), ptr(co(1)), ptr(co(12)), ptr(e2), Sense::Reversed, ptr(loop_base));
sat.add_coedge(ptr(co(0)), ptr(co(2)), ptr(co(16)), ptr(e1), Sense::Reversed, ptr(loop_base));
sat.add_coedge(ptr(co(5)), ptr(co(7)), ptr(co(10)), ptr(e4), Sense::Forward, ptr(loop_base + 1));
sat.add_coedge(ptr(co(6)), ptr(co(4)), ptr(co(18)), ptr(e5), Sense::Forward, ptr(loop_base + 1));
sat.add_coedge(ptr(co(7)), ptr(co(5)), ptr(co(14)), ptr(e6), Sense::Forward, ptr(loop_base + 1));
sat.add_coedge(ptr(co(4)), ptr(co(6)), ptr(co(22)), ptr(e7), Sense::Forward, ptr(loop_base + 1));
sat.add_coedge(ptr(co(9)), ptr(co(11)), ptr(co(0)), ptr(e0), Sense::Forward, ptr(loop_base + 2));
sat.add_coedge(ptr(co(10)), ptr(co(8)), ptr(co(19)), ptr(e9), Sense::Forward, ptr(loop_base + 2));
sat.add_coedge(ptr(co(11)), ptr(co(9)), ptr(co(4)), ptr(e4), Sense::Reversed, ptr(loop_base + 2));
sat.add_coedge(ptr(co(8)), ptr(co(10)), ptr(co(21)), ptr(e8), Sense::Reversed, ptr(loop_base + 2));
sat.add_coedge(ptr(co(13)), ptr(co(15)), ptr(co(2)), ptr(e2), Sense::Forward, ptr(loop_base + 3));
sat.add_coedge(ptr(co(14)), ptr(co(12)), ptr(co(23)), ptr(e11), Sense::Forward, ptr(loop_base + 3));
sat.add_coedge(ptr(co(15)), ptr(co(13)), ptr(co(6)), ptr(e6), Sense::Reversed, ptr(loop_base + 3));
sat.add_coedge(ptr(co(12)), ptr(co(14)), ptr(co(17)), ptr(e10), Sense::Reversed, ptr(loop_base + 3));
sat.add_coedge(ptr(co(17)), ptr(co(19)), ptr(co(3)), ptr(e1), Sense::Forward, ptr(loop_base + 4));
sat.add_coedge(ptr(co(18)), ptr(co(16)), ptr(co(15)), ptr(e10), Sense::Forward, ptr(loop_base + 4));
sat.add_coedge(ptr(co(19)), ptr(co(17)), ptr(co(5)), ptr(e5), Sense::Reversed, ptr(loop_base + 4));
sat.add_coedge(ptr(co(16)), ptr(co(18)), ptr(co(9)), ptr(e9), Sense::Reversed, ptr(loop_base + 4));
sat.add_coedge(ptr(co(21)), ptr(co(23)), ptr(co(1)), ptr(e3), Sense::Forward, ptr(loop_base + 5));
sat.add_coedge(ptr(co(22)), ptr(co(20)), ptr(co(11)), ptr(e8), Sense::Forward, ptr(loop_base + 5));
sat.add_coedge(ptr(co(23)), ptr(co(21)), ptr(co(7)), ptr(e7), Sense::Reversed, ptr(loop_base + 5));
sat.add_coedge(ptr(co(20)), ptr(co(22)), ptr(co(13)), ptr(e11), Sense::Reversed, ptr(loop_base + 5));
sat.add_loop(SatPointer::NULL, ptr(co(0)), ptr(face_base));
sat.add_loop(SatPointer::NULL, ptr(co(4)), ptr(face_base + 1));
sat.add_loop(SatPointer::NULL, ptr(co(8)), ptr(face_base + 2));
sat.add_loop(SatPointer::NULL, ptr(co(12)), ptr(face_base + 3));
sat.add_loop(SatPointer::NULL, ptr(co(16)), ptr(face_base + 4));
sat.add_loop(SatPointer::NULL, ptr(co(20)), ptr(face_base + 5));
sat.add_face(ptr(face_base + 1), ptr(loop_base), ptr(shell_idx), ptr(surf_bottom), Sense::Forward, Sidedness::Single);
sat.add_face(ptr(face_base + 2), ptr(loop_base + 1), ptr(shell_idx), ptr(surf_top), Sense::Forward, Sidedness::Single);
sat.add_face(ptr(face_base + 3), ptr(loop_base + 2), ptr(shell_idx), ptr(surf_front), Sense::Forward, Sidedness::Single);
sat.add_face(ptr(face_base + 4), ptr(loop_base + 3), ptr(shell_idx), ptr(surf_back), Sense::Forward, Sidedness::Single);
sat.add_face(ptr(face_base + 5), ptr(loop_base + 4), ptr(shell_idx), ptr(surf_right), Sense::Forward, Sidedness::Single);
sat.add_face(SatPointer::NULL, ptr(loop_base + 5), ptr(shell_idx), ptr(surf_left), Sense::Forward, Sidedness::Single);
sat.add_shell(ptr(face_base), ptr(lump_idx));
sat.add_lump(ptr(shell_idx), body_idx);
if let Some(body_rec) = sat.record_mut(0) {
body_rec.tokens[1] = SatToken::Pointer(ptr(lump_idx));
}
sat
}
pub fn build_wedge(origin: [f64; 3], length: f64, width: f64, height: f64) -> SatDocument {
let mut sat = SatDocument::new_body();
let body_idx = SatPointer::new(0);
let [ox, oy, oz] = origin;
let hyp_len = (length * length + width * width).sqrt();
let s = 1.0 / hyp_len; let nx = width * s; let ny = length * s;
let p_a = sat.add_point(ox, oy, oz);
let p_b = sat.add_point(ox + length, oy, oz);
let p_c = sat.add_point(ox, oy + width, oz);
let p_d = sat.add_point(ox, oy, oz + height);
let p_e = sat.add_point(ox + length, oy, oz + height);
let p_f = sat.add_point(ox, oy + width, oz + height);
let surf_bot = sat.add_plane_surface([ox, oy, oz], [0.0, 0.0, -1.0], [1.0, 0.0, 0.0]);
let surf_top = sat.add_plane_surface([ox, oy, oz + height], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]);
let surf_frt = sat.add_plane_surface([ox, oy, oz], [0.0, -1.0, 0.0], [1.0, 0.0, 0.0]);
let surf_lft = sat.add_plane_surface([ox, oy, oz], [-1.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
let hyp_mid = [ox + length / 2.0, oy + width / 2.0, oz];
let surf_hyp = sat.add_plane_surface(hyp_mid, [nx, ny, 0.0], [0.0, 0.0, 1.0]);
let hx = -length / hyp_len;
let hy = width / hyp_len;
let crv0 = sat.add_straight_curve([ox, oy, oz], [1.0, 0.0, 0.0]);
let crv1 = sat.add_straight_curve([ox + length, oy, oz], [hx, hy, 0.0]);
let crv2 = sat.add_straight_curve([ox, oy + width, oz], [0.0, -1.0, 0.0]);
let crv3 = sat.add_straight_curve([ox, oy, oz + height], [1.0, 0.0, 0.0]);
let crv4 = sat.add_straight_curve([ox + length, oy, oz + height], [hx, hy, 0.0]);
let crv5 = sat.add_straight_curve([ox, oy + width, oz + height], [0.0, -1.0, 0.0]);
let crv6 = sat.add_straight_curve([ox, oy, oz], [0.0, 0.0, 1.0]);
let crv7 = sat.add_straight_curve([ox + length, oy, oz], [0.0, 0.0, 1.0]);
let crv8 = sat.add_straight_curve([ox, oy + width, oz], [0.0, 0.0, 1.0]);
let v_a = sat.add_vertex(SatPointer::NULL, ptr(p_a));
let v_b = sat.add_vertex(SatPointer::NULL, ptr(p_b));
let v_c = sat.add_vertex(SatPointer::NULL, ptr(p_c));
let v_d = sat.add_vertex(SatPointer::NULL, ptr(p_d));
let v_e = sat.add_vertex(SatPointer::NULL, ptr(p_e));
let v_f = sat.add_vertex(SatPointer::NULL, ptr(p_f));
let e0 = sat.add_edge(ptr(v_a), 0.0, ptr(v_b), length, SatPointer::NULL, ptr(crv0), Sense::Forward);
let e1 = sat.add_edge(ptr(v_b), 0.0, ptr(v_c), hyp_len, SatPointer::NULL, ptr(crv1), Sense::Forward);
let e2 = sat.add_edge(ptr(v_c), 0.0, ptr(v_a), width, SatPointer::NULL, ptr(crv2), Sense::Forward);
let e3 = sat.add_edge(ptr(v_d), 0.0, ptr(v_e), length, SatPointer::NULL, ptr(crv3), Sense::Forward);
let e4 = sat.add_edge(ptr(v_e), 0.0, ptr(v_f), hyp_len, SatPointer::NULL, ptr(crv4), Sense::Forward);
let e5 = sat.add_edge(ptr(v_f), 0.0, ptr(v_d), width, SatPointer::NULL, ptr(crv5), Sense::Forward);
let e6 = sat.add_edge(ptr(v_a), 0.0, ptr(v_d), height, SatPointer::NULL, ptr(crv6), Sense::Forward);
let e7 = sat.add_edge(ptr(v_b), 0.0, ptr(v_e), height, SatPointer::NULL, ptr(crv7), Sense::Forward);
let e8 = sat.add_edge(ptr(v_c), 0.0, ptr(v_f), height, SatPointer::NULL, ptr(crv8), Sense::Forward);
let base = sat.records.len() as i32;
let co = |i: i32| base + i;
let loop_base = base + 18;
let face_base = base + 23;
let shell_idx = base + 28;
let lump_idx = base + 29;
sat.add_coedge(ptr(co(1)), ptr(co(2)), ptr(co(13)), ptr(e2), Sense::Reversed, ptr(loop_base));
sat.add_coedge(ptr(co(2)), ptr(co(0)), ptr(co(14)), ptr(e1), Sense::Reversed, ptr(loop_base));
sat.add_coedge(ptr(co(0)), ptr(co(1)), ptr(co(6)), ptr(e0), Sense::Reversed, ptr(loop_base));
sat.add_coedge(ptr(co(4)), ptr(co(5)), ptr(co(8)), ptr(e3), Sense::Forward, ptr(loop_base + 1));
sat.add_coedge(ptr(co(5)), ptr(co(3)), ptr(co(16)), ptr(e4), Sense::Forward, ptr(loop_base + 1));
sat.add_coedge(ptr(co(3)), ptr(co(4)), ptr(co(11)), ptr(e5), Sense::Forward, ptr(loop_base + 1));
sat.add_coedge(ptr(co(7)), ptr(co(9)), ptr(co(2)), ptr(e0), Sense::Forward, ptr(loop_base + 2));
sat.add_coedge(ptr(co(8)), ptr(co(6)), ptr(co(17)), ptr(e7), Sense::Forward, ptr(loop_base + 2));
sat.add_coedge(ptr(co(9)), ptr(co(7)), ptr(co(3)), ptr(e3), Sense::Reversed, ptr(loop_base + 2));
sat.add_coedge(ptr(co(6)), ptr(co(8)), ptr(co(10)), ptr(e6), Sense::Reversed, ptr(loop_base + 2));
sat.add_coedge(ptr(co(11)), ptr(co(13)), ptr(co(9)), ptr(e6), Sense::Forward, ptr(loop_base + 3));
sat.add_coedge(ptr(co(12)), ptr(co(10)), ptr(co(5)), ptr(e5), Sense::Reversed, ptr(loop_base + 3));
sat.add_coedge(ptr(co(13)), ptr(co(11)), ptr(co(15)), ptr(e8), Sense::Reversed, ptr(loop_base + 3));
sat.add_coedge(ptr(co(10)), ptr(co(12)), ptr(co(0)), ptr(e2), Sense::Forward, ptr(loop_base + 3));
sat.add_coedge(ptr(co(15)), ptr(co(17)), ptr(co(1)), ptr(e1), Sense::Forward, ptr(loop_base + 4));
sat.add_coedge(ptr(co(16)), ptr(co(14)), ptr(co(12)), ptr(e8), Sense::Forward, ptr(loop_base + 4));
sat.add_coedge(ptr(co(17)), ptr(co(15)), ptr(co(4)), ptr(e4), Sense::Reversed, ptr(loop_base + 4));
sat.add_coedge(ptr(co(14)), ptr(co(16)), ptr(co(7)), ptr(e7), Sense::Reversed, ptr(loop_base + 4));
sat.add_loop(SatPointer::NULL, ptr(co(0)), ptr(face_base));
sat.add_loop(SatPointer::NULL, ptr(co(3)), ptr(face_base + 1));
sat.add_loop(SatPointer::NULL, ptr(co(6)), ptr(face_base + 2));
sat.add_loop(SatPointer::NULL, ptr(co(10)), ptr(face_base + 3));
sat.add_loop(SatPointer::NULL, ptr(co(14)), ptr(face_base + 4));
sat.add_face(ptr(face_base + 1), ptr(loop_base), ptr(shell_idx), ptr(surf_bot), Sense::Forward, Sidedness::Single);
sat.add_face(ptr(face_base + 2), ptr(loop_base + 1), ptr(shell_idx), ptr(surf_top), Sense::Forward, Sidedness::Single);
sat.add_face(ptr(face_base + 3), ptr(loop_base + 2), ptr(shell_idx), ptr(surf_frt), Sense::Forward, Sidedness::Single);
sat.add_face(ptr(face_base + 4), ptr(loop_base + 3), ptr(shell_idx), ptr(surf_lft), Sense::Forward, Sidedness::Single);
sat.add_face(SatPointer::NULL, ptr(loop_base + 4), ptr(shell_idx), ptr(surf_hyp), Sense::Forward, Sidedness::Single);
sat.add_shell(ptr(face_base), ptr(lump_idx));
sat.add_lump(ptr(shell_idx), body_idx);
if let Some(body_rec) = sat.record_mut(0) {
body_rec.tokens[1] = SatToken::Pointer(ptr(lump_idx));
}
sat
}
pub fn build_pyramid(center: [f64; 3], base_size: f64, height: f64) -> SatDocument {
let mut sat = SatDocument::new_body();
let body_idx = SatPointer::new(0);
let [cx, cy, cz] = center;
let hb = base_size / 2.0;
let s5 = (1.0 + (height / hb).powi(2)).sqrt(); let n_horiz = 1.0 / s5;
let n_vert = (height / hb) / s5;
let lat_len = (hb * hb + hb * hb + height * height).sqrt();
let s6 = lat_len;
let inv_lat = 1.0 / lat_len;
let p_a = sat.add_point(cx - hb, cy - hb, cz);
let p_b = sat.add_point(cx + hb, cy - hb, cz);
let p_c = sat.add_point(cx + hb, cy + hb, cz);
let p_d = sat.add_point(cx - hb, cy + hb, cz);
let p_e = sat.add_point(cx, cy, cz + height);
let surf_base = sat.add_plane_surface([cx, cy, cz], [0.0, 0.0, -1.0], [1.0, 0.0, 0.0]);
let surf_front = sat.add_plane_surface([cx, cy - hb, cz], [0.0, -n_horiz, n_vert], [1.0, 0.0, 0.0]);
let surf_right = sat.add_plane_surface([cx + hb, cy, cz], [n_horiz, 0.0, n_vert], [0.0, 1.0, 0.0]);
let surf_back = sat.add_plane_surface([cx, cy + hb, cz], [0.0, n_horiz, n_vert], [1.0, 0.0, 0.0]);
let surf_left = sat.add_plane_surface([cx - hb, cy, cz], [-n_horiz,0.0, n_vert], [0.0, 1.0, 0.0]);
let crv0 = sat.add_straight_curve([cx - hb, cy - hb, cz], [1.0, 0.0, 0.0]);
let crv1 = sat.add_straight_curve([cx + hb, cy - hb, cz], [0.0, 1.0, 0.0]);
let crv2 = sat.add_straight_curve([cx + hb, cy + hb, cz], [-1.0, 0.0, 0.0]);
let crv3 = sat.add_straight_curve([cx - hb, cy + hb, cz], [0.0, -1.0, 0.0]);
let crv4 = sat.add_straight_curve([cx - hb, cy - hb, cz], [ hb * inv_lat, hb * inv_lat, height * inv_lat]);
let crv5 = sat.add_straight_curve([cx + hb, cy - hb, cz], [-hb * inv_lat, hb * inv_lat, height * inv_lat]);
let crv6 = sat.add_straight_curve([cx + hb, cy + hb, cz], [-hb * inv_lat, -hb * inv_lat, height * inv_lat]);
let crv7 = sat.add_straight_curve([cx - hb, cy + hb, cz], [ hb * inv_lat, -hb * inv_lat, height * inv_lat]);
let v_a = sat.add_vertex(SatPointer::NULL, ptr(p_a));
let v_b = sat.add_vertex(SatPointer::NULL, ptr(p_b));
let v_c = sat.add_vertex(SatPointer::NULL, ptr(p_c));
let v_d = sat.add_vertex(SatPointer::NULL, ptr(p_d));
let v_e = sat.add_vertex(SatPointer::NULL, ptr(p_e));
let e0 = sat.add_edge(ptr(v_a), 0.0, ptr(v_b), base_size, SatPointer::NULL, ptr(crv0), Sense::Forward);
let e1 = sat.add_edge(ptr(v_b), 0.0, ptr(v_c), base_size, SatPointer::NULL, ptr(crv1), Sense::Forward);
let e2 = sat.add_edge(ptr(v_c), 0.0, ptr(v_d), base_size, SatPointer::NULL, ptr(crv2), Sense::Forward);
let e3 = sat.add_edge(ptr(v_d), 0.0, ptr(v_a), base_size, SatPointer::NULL, ptr(crv3), Sense::Forward);
let e4 = sat.add_edge(ptr(v_a), 0.0, ptr(v_e), s6, SatPointer::NULL, ptr(crv4), Sense::Forward);
let e5 = sat.add_edge(ptr(v_b), 0.0, ptr(v_e), s6, SatPointer::NULL, ptr(crv5), Sense::Forward);
let e6 = sat.add_edge(ptr(v_c), 0.0, ptr(v_e), s6, SatPointer::NULL, ptr(crv6), Sense::Forward);
let e7 = sat.add_edge(ptr(v_d), 0.0, ptr(v_e), s6, SatPointer::NULL, ptr(crv7), Sense::Forward);
let base = sat.records.len() as i32;
let co = |i: i32| base + i;
let loop_base = base + 16;
let face_base = base + 21;
let shell_idx = base + 26;
let lump_idx = base + 27;
sat.add_coedge(ptr(co(1)), ptr(co(3)), ptr(co(15)), ptr(e3), Sense::Reversed, ptr(loop_base));
sat.add_coedge(ptr(co(2)), ptr(co(0)), ptr(co(10)), ptr(e2), Sense::Reversed, ptr(loop_base));
sat.add_coedge(ptr(co(3)), ptr(co(1)), ptr(co(7)), ptr(e1), Sense::Reversed, ptr(loop_base));
sat.add_coedge(ptr(co(0)), ptr(co(2)), ptr(co(4)), ptr(e0), Sense::Reversed, ptr(loop_base));
sat.add_coedge(ptr(co(5)), ptr(co(6)), ptr(co(3)), ptr(e0), Sense::Forward, ptr(loop_base + 1));
sat.add_coedge(ptr(co(6)), ptr(co(4)), ptr(co(9)), ptr(e5), Sense::Forward, ptr(loop_base + 1));
sat.add_coedge(ptr(co(4)), ptr(co(5)), ptr(co(14)), ptr(e4), Sense::Reversed, ptr(loop_base + 1));
sat.add_coedge(ptr(co(8)), ptr(co(9)), ptr(co(2)), ptr(e1), Sense::Forward, ptr(loop_base + 2));
sat.add_coedge(ptr(co(9)), ptr(co(7)), ptr(co(12)), ptr(e6), Sense::Forward, ptr(loop_base + 2));
sat.add_coedge(ptr(co(7)), ptr(co(8)), ptr(co(5)), ptr(e5), Sense::Reversed, ptr(loop_base + 2));
sat.add_coedge(ptr(co(11)), ptr(co(12)), ptr(co(1)), ptr(e2), Sense::Forward, ptr(loop_base + 3));
sat.add_coedge(ptr(co(12)), ptr(co(10)), ptr(co(15)), ptr(e7), Sense::Forward, ptr(loop_base + 3));
sat.add_coedge(ptr(co(10)), ptr(co(11)), ptr(co(8)), ptr(e6), Sense::Reversed, ptr(loop_base + 3));
sat.add_coedge(ptr(co(14)), ptr(co(15)), ptr(co(0)), ptr(e3), Sense::Forward, ptr(loop_base + 4));
sat.add_coedge(ptr(co(15)), ptr(co(13)), ptr(co(6)), ptr(e4), Sense::Forward, ptr(loop_base + 4));
sat.add_coedge(ptr(co(13)), ptr(co(14)), ptr(co(11)), ptr(e7), Sense::Reversed, ptr(loop_base + 4));
sat.add_loop(SatPointer::NULL, ptr(co(0)), ptr(face_base));
sat.add_loop(SatPointer::NULL, ptr(co(4)), ptr(face_base + 1));
sat.add_loop(SatPointer::NULL, ptr(co(7)), ptr(face_base + 2));
sat.add_loop(SatPointer::NULL, ptr(co(10)), ptr(face_base + 3));
sat.add_loop(SatPointer::NULL, ptr(co(13)), ptr(face_base + 4));
sat.add_face(ptr(face_base + 1), ptr(loop_base), ptr(shell_idx), ptr(surf_base), Sense::Forward, Sidedness::Single);
sat.add_face(ptr(face_base + 2), ptr(loop_base + 1), ptr(shell_idx), ptr(surf_front), Sense::Forward, Sidedness::Single);
sat.add_face(ptr(face_base + 3), ptr(loop_base + 2), ptr(shell_idx), ptr(surf_right), Sense::Forward, Sidedness::Single);
sat.add_face(ptr(face_base + 4), ptr(loop_base + 3), ptr(shell_idx), ptr(surf_back), Sense::Forward, Sidedness::Single);
sat.add_face(SatPointer::NULL, ptr(loop_base + 4), ptr(shell_idx), ptr(surf_left), Sense::Forward, Sidedness::Single);
sat.add_shell(ptr(face_base), ptr(lump_idx));
sat.add_lump(ptr(shell_idx), body_idx);
if let Some(body_rec) = sat.record_mut(0) {
body_rec.tokens[1] = SatToken::Pointer(ptr(lump_idx));
}
sat
}
pub fn build_cylinder(center: [f64; 3], radius: f64, height: f64) -> SatDocument {
let mut sat = SatDocument::new_body();
let body_idx = SatPointer::new(0);
let [cx, cy, cz] = center;
let tau = std::f64::consts::TAU;
let p0 = sat.add_point(cx + radius, cy, cz);
let p1 = sat.add_point(cx + radius, cy, cz + height);
let surf_bot = sat.add_plane_surface([cx, cy, cz], [0.0, 0.0, -1.0], [1.0, 0.0, 0.0]);
let surf_top = sat.add_plane_surface([cx, cy, cz + height], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]);
let surf_cyl = sat.add_cone_surface(
[cx, cy, cz], [0.0, 0.0, 1.0], [radius, 0.0, 0.0],
1.0, 1.0, 0.0,
);
let crv_bot = sat.add_ellipse_curve([cx, cy, cz], [0.0, 0.0, -1.0], [radius, 0.0, 0.0], 1.0);
let crv_top = sat.add_ellipse_curve([cx, cy, cz + height], [0.0, 0.0, 1.0], [radius, 0.0, 0.0], 1.0);
let v0 = sat.add_vertex(SatPointer::NULL, ptr(p0));
let v1 = sat.add_vertex(SatPointer::NULL, ptr(p1));
let e_bot = sat.add_edge(ptr(v0), 0.0, ptr(v0), tau, SatPointer::NULL, ptr(crv_bot), Sense::Forward);
let e_top = sat.add_edge(ptr(v1), 0.0, ptr(v1), tau, SatPointer::NULL, ptr(crv_top), Sense::Forward);
let base = sat.records.len() as i32;
let co = |i: i32| base + i;
let loop_base = base + 4;
let face_base = base + 8;
let shell_idx = base + 11;
let lump_idx = base + 12;
sat.add_coedge(ptr(co(0)), ptr(co(0)), ptr(co(2)), ptr(e_bot), Sense::Forward, ptr(loop_base));
sat.add_coedge(ptr(co(1)), ptr(co(1)), ptr(co(3)), ptr(e_top), Sense::Forward, ptr(loop_base + 1));
sat.add_coedge(ptr(co(2)), ptr(co(2)), ptr(co(0)), ptr(e_bot), Sense::Reversed, ptr(loop_base + 2));
sat.add_coedge(ptr(co(3)), ptr(co(3)), ptr(co(1)), ptr(e_top), Sense::Reversed, ptr(loop_base + 3));
sat.add_loop(SatPointer::NULL, ptr(co(0)), ptr(face_base));
sat.add_loop(SatPointer::NULL, ptr(co(1)), ptr(face_base + 1));
sat.add_loop(ptr(loop_base + 3), ptr(co(2)), ptr(face_base + 2)); sat.add_loop(SatPointer::NULL, ptr(co(3)), ptr(face_base + 2));
sat.add_face(ptr(face_base + 1), ptr(loop_base), ptr(shell_idx), ptr(surf_bot), Sense::Forward, Sidedness::Single);
sat.add_face(ptr(face_base + 2), ptr(loop_base + 1), ptr(shell_idx), ptr(surf_top), Sense::Forward, Sidedness::Single);
sat.add_face(SatPointer::NULL, ptr(loop_base + 2), ptr(shell_idx), ptr(surf_cyl), Sense::Forward, Sidedness::Single);
sat.add_shell(ptr(face_base), ptr(lump_idx));
sat.add_lump(ptr(shell_idx), body_idx);
if let Some(body_rec) = sat.record_mut(0) {
body_rec.tokens[1] = SatToken::Pointer(ptr(lump_idx));
}
sat
}
pub fn build_cone(center: [f64; 3], radius: f64, height: f64) -> SatDocument {
let mut sat = SatDocument::new_body();
let body_idx = SatPointer::new(0);
let [cx, cy, cz] = center;
let tau = std::f64::consts::TAU;
let hyp = (radius * radius + height * height).sqrt();
let sin_half = -radius / hyp;
let cos_half = height / hyp;
let p_base = sat.add_point(cx + radius, cy, cz);
let p_apex = sat.add_point(cx, cy, cz + height);
let surf_base = sat.add_plane_surface([cx, cy, cz], [0.0, 0.0, -1.0], [1.0, 0.0, 0.0]);
let surf_cone = sat.add_cone_surface(
[cx, cy, cz], [0.0, 0.0, 1.0], [radius, 0.0, 0.0],
1.0, cos_half, sin_half,
);
let crv_base = sat.add_ellipse_curve(
[cx, cy, cz], [0.0, 0.0, -1.0], [radius, 0.0, 0.0], 1.0,
);
let v0 = sat.add_vertex(SatPointer::NULL, ptr(p_base));
let v_apex = sat.add_vertex(SatPointer::NULL, ptr(p_apex));
let e_base = sat.add_edge(ptr(v0), 0.0, ptr(v0), tau, SatPointer::NULL, ptr(crv_base), Sense::Forward);
let e_apex = sat.add_edge(ptr(v_apex), 1.0, ptr(v_apex), 0.0, SatPointer::NULL, SatPointer::NULL, Sense::Forward);
let base = sat.records.len() as i32;
let co = |i: i32| base + i;
let loop_base = base + 3;
let face_base = base + 6;
let shell_idx = base + 8;
let lump_idx = base + 9;
sat.add_coedge(ptr(co(0)), ptr(co(0)), ptr(co(1)), ptr(e_base), Sense::Reversed, ptr(loop_base));
sat.add_coedge(ptr(co(1)), ptr(co(1)), ptr(co(0)), ptr(e_base), Sense::Forward, ptr(loop_base + 1));
sat.add_coedge(ptr(co(2)), ptr(co(2)), SatPointer::NULL, ptr(e_apex), Sense::Reversed, ptr(loop_base + 2));
sat.add_loop(SatPointer::NULL, ptr(co(0)), ptr(face_base));
sat.add_loop(ptr(loop_base + 2), ptr(co(1)), ptr(face_base + 1)); sat.add_loop(SatPointer::NULL, ptr(co(2)), ptr(face_base + 1));
sat.add_face(ptr(face_base + 1), ptr(loop_base), ptr(shell_idx), ptr(surf_base), Sense::Forward, Sidedness::Single);
sat.add_face(SatPointer::NULL, ptr(loop_base + 1), ptr(shell_idx), ptr(surf_cone), Sense::Forward, Sidedness::Single);
sat.add_shell(ptr(face_base), ptr(lump_idx));
sat.add_lump(ptr(shell_idx), body_idx);
if let Some(body_rec) = sat.record_mut(0) {
body_rec.tokens[1] = SatToken::Pointer(ptr(lump_idx));
}
sat
}
pub fn build_sphere(center: [f64; 3], radius: f64) -> SatDocument {
let mut sat = SatDocument::new_body();
let body_idx = SatPointer::new(0);
let surf = sat.add_sphere_surface(center, radius, [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]);
let base = sat.records.len() as i32;
let face_idx = base;
let shell_idx = base + 1;
let lump_idx = base + 2;
sat.add_face(SatPointer::NULL, SatPointer::NULL, ptr(shell_idx), ptr(surf), Sense::Forward, Sidedness::Single);
sat.add_shell(ptr(face_idx), ptr(lump_idx));
sat.add_lump(ptr(shell_idx), body_idx);
if let Some(body_rec) = sat.record_mut(0) {
body_rec.tokens[1] = SatToken::Pointer(ptr(lump_idx));
}
sat
}
pub fn build_torus(center: [f64; 3], major_radius: f64, minor_radius: f64) -> SatDocument {
let mut sat = SatDocument::new_body();
let body_idx = SatPointer::new(0);
let surf = sat.add_torus_surface(
center, [0.0, 0.0, 1.0], major_radius, minor_radius, [1.0, 0.0, 0.0],
);
let base = sat.records.len() as i32;
let face_idx = base;
let shell_idx = base + 1;
let lump_idx = base + 2;
sat.add_face(SatPointer::NULL, SatPointer::NULL, ptr(shell_idx), ptr(surf), Sense::Forward, Sidedness::Single);
sat.add_shell(ptr(face_idx), ptr(lump_idx));
sat.add_lump(ptr(shell_idx), body_idx);
if let Some(body_rec) = sat.record_mut(0) {
body_rec.tokens[1] = SatToken::Pointer(ptr(lump_idx));
}
sat
}
fn vsub(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
[a[0] - b[0], a[1] - b[1], a[2] - b[2]]
}
fn vcross(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
[
a[1] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0],
]
}
fn vnorm(a: [f64; 3]) -> f64 {
(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]).sqrt()
}
fn face_plane(vertices: &[[f64; 3]], ring: &[usize]) -> Option<([f64; 3], [f64; 3], [f64; 3])> {
let v0 = vertices[ring[0]];
let a = vsub(vertices[ring[1]], v0);
let alen = vnorm(a);
if alen < 1e-12 {
return None;
}
let u = [a[0] / alen, a[1] / alen, a[2] / alen];
for &k in &ring[2..] {
let b = vsub(vertices[k], v0);
let n = vcross(a, b);
let nl = vnorm(n);
if nl > 1e-9 {
return Some((v0, [n[0] / nl, n[1] / nl, n[2] / nl], u));
}
}
None
}
fn set_coedge_partner(sat: &mut SatDocument, coedge_idx: i32, partner_idx: i32) {
if let Some(rec) = sat.record_mut(coedge_idx as usize) {
rec.tokens[3] = SatToken::Pointer(SatPointer::new(partner_idx));
}
}
pub fn build_planar_body(vertices: &[[f64; 3]], faces: &[Vec<usize>]) -> Option<SatDocument> {
use std::collections::HashMap;
if faces.is_empty() || vertices.len() < 4 {
return None;
}
for f in faces {
if f.len() < 3 {
return None;
}
for &vi in f {
if vi >= vertices.len() {
return None;
}
}
}
let mut sat = SatDocument::new_body();
let body_idx = SatPointer::new(0);
let mut vert_idx = Vec::with_capacity(vertices.len());
for &[x, y, z] in vertices {
let p = sat.add_point(x, y, z);
vert_idx.push(sat.add_vertex(SatPointer::NULL, ptr(p)));
}
struct EdgeInfo {
edge_idx: i32,
start: usize,
end: usize,
coedges: Vec<i32>,
}
let mut edges: HashMap<(usize, usize), EdgeInfo> = HashMap::new();
let mut edge_order: Vec<(usize, usize)> = Vec::new();
for f in faces {
let n = f.len();
for i in 0..n {
let a = f[i];
let b = f[(i + 1) % n];
if a == b {
return None;
}
let key = if a < b { (a, b) } else { (b, a) };
if !edges.contains_key(&key) {
let sp = vertices[a];
let dir = vsub(vertices[b], sp);
let len = vnorm(dir);
if len < 1e-12 {
return None;
}
let ud = [dir[0] / len, dir[1] / len, dir[2] / len];
let crv = sat.add_straight_curve(sp, ud);
let e = sat.add_edge(
ptr(vert_idx[a]),
0.0,
ptr(vert_idx[b]),
len,
SatPointer::NULL,
ptr(crv),
Sense::Forward,
);
edges.insert(
key,
EdgeInfo { edge_idx: e, start: a, end: b, coedges: Vec::new() },
);
edge_order.push(key);
}
}
}
let mut surf_idx = Vec::with_capacity(faces.len());
for f in faces {
let (root, normal, u_dir) = face_plane(vertices, f)?;
surf_idx.push(sat.add_plane_surface(root, normal, u_dir));
}
let co_base = sat.records.len() as i32;
let num_coedges: usize = faces.iter().map(|f| f.len()).sum();
let loop_base = co_base + num_coedges as i32;
let face_base = loop_base + faces.len() as i32;
let shell_idx = face_base + faces.len() as i32;
let lump_idx = shell_idx + 1;
let mut co_cursor = co_base;
let mut face_first_co = Vec::with_capacity(faces.len());
for (fi, f) in faces.iter().enumerate() {
let n = f.len() as i32;
let start = co_cursor;
face_first_co.push(start);
for i in 0..f.len() {
let a = f[i];
let b = f[(i + 1) % f.len()];
let key = if a < b { (a, b) } else { (b, a) };
let info = edges.get_mut(&key).unwrap();
let sense = if info.start == a && info.end == b {
Sense::Forward
} else {
Sense::Reversed
};
let next = start + ((i as i32 + 1) % n);
let prev = start + ((i as i32 + n - 1) % n);
let owner_loop = loop_base + fi as i32;
let idx = sat.add_coedge(
ptr(next),
ptr(prev),
SatPointer::NULL,
ptr(info.edge_idx),
sense,
ptr(owner_loop),
);
info.coedges.push(idx);
co_cursor += 1;
}
}
for key in &edge_order {
let info = &edges[key];
if info.coedges.len() != 2 {
return None; }
let (c0, c1) = (info.coedges[0], info.coedges[1]);
set_coedge_partner(&mut sat, c0, c1);
set_coedge_partner(&mut sat, c1, c0);
}
for fi in 0..faces.len() {
sat.add_loop(SatPointer::NULL, ptr(face_first_co[fi]), ptr(face_base + fi as i32));
}
for fi in 0..faces.len() {
let next_face = if fi + 1 < faces.len() {
ptr(face_base + fi as i32 + 1)
} else {
SatPointer::NULL
};
sat.add_face(
next_face,
ptr(loop_base + fi as i32),
ptr(shell_idx),
ptr(surf_idx[fi]),
Sense::Forward,
Sidedness::Single,
);
}
sat.add_shell(ptr(face_base), ptr(lump_idx));
sat.add_lump(ptr(shell_idx), body_idx);
if let Some(body_rec) = sat.record_mut(0) {
body_rec.tokens[1] = SatToken::Pointer(ptr(lump_idx));
}
if sat.validate().is_empty() {
Some(sat)
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn box_topology() {
let sat = build_box([0.0, 0.0, 0.0], 10.0, 10.0, 10.0);
assert_eq!(sat.faces().len(), 6);
assert_eq!(sat.edges().len(), 12);
assert_eq!(sat.vertices().len(), 8);
assert!(sat.validate().is_empty(), "box validation errors: {:?}", sat.validate());
}
#[test]
fn box_offset_center() {
let sat = build_box([5.0, 5.0, 5.0], 4.0, 6.0, 8.0);
assert_eq!(sat.faces().len(), 6);
assert!(sat.validate().is_empty());
}
#[test]
fn wedge_topology() {
let sat = build_wedge([0.0, 0.0, 0.0], 10.0, 10.0, 10.0);
assert_eq!(sat.faces().len(), 5);
assert_eq!(sat.edges().len(), 9);
assert_eq!(sat.vertices().len(), 6);
assert!(sat.validate().is_empty(), "wedge validation errors: {:?}", sat.validate());
}
#[test]
fn pyramid_topology() {
let sat = build_pyramid([0.0, 0.0, 0.0], 10.0, 10.0);
assert_eq!(sat.faces().len(), 5);
assert_eq!(sat.edges().len(), 8);
assert_eq!(sat.vertices().len(), 5);
assert!(sat.validate().is_empty(), "pyramid validation errors: {:?}", sat.validate());
}
#[test]
fn cylinder_topology() {
let sat = build_cylinder([0.0, 0.0, 0.0], 5.0, 10.0);
assert_eq!(sat.faces().len(), 3);
assert_eq!(sat.edges().len(), 2);
assert_eq!(sat.vertices().len(), 2);
assert!(sat.validate().is_empty(), "cylinder validation errors: {:?}", sat.validate());
}
#[test]
fn cone_topology() {
let sat = build_cone([0.0, 0.0, 0.0], 5.0, 10.0);
assert_eq!(sat.faces().len(), 2);
assert_eq!(sat.edges().len(), 2);
assert_eq!(sat.vertices().len(), 2);
assert!(sat.validate().is_empty(), "cone validation errors: {:?}", sat.validate());
}
#[test]
fn sphere_topology() {
let sat = build_sphere([0.0, 0.0, 0.0], 5.0);
assert_eq!(sat.faces().len(), 1);
assert_eq!(sat.edges().len(), 0);
assert_eq!(sat.vertices().len(), 0);
assert!(sat.validate().is_empty(), "sphere validation errors: {:?}", sat.validate());
}
#[test]
fn torus_topology() {
let sat = build_torus([0.0, 0.0, 0.0], 5.0, 2.0);
assert_eq!(sat.faces().len(), 1);
assert_eq!(sat.edges().len(), 0);
assert_eq!(sat.vertices().len(), 0);
assert!(sat.validate().is_empty(), "torus validation errors: {:?}", sat.validate());
}
#[test]
fn planar_body_cube() {
let vertices = [
[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 1.0], [1.0, 1.0, 1.0], [0.0, 1.0, 1.0], ];
let faces = vec![
vec![0, 3, 2, 1], vec![4, 5, 6, 7], vec![0, 1, 5, 4], vec![3, 7, 6, 2], vec![1, 2, 6, 5], vec![0, 4, 7, 3], ];
let sat = build_planar_body(&vertices, &faces).expect("cube should build");
assert_eq!(sat.faces().len(), 6);
assert_eq!(sat.edges().len(), 12);
assert_eq!(sat.vertices().len(), 8);
assert!(sat.validate().is_empty(), "cube validation errors: {:?}", sat.validate());
}
#[test]
fn planar_body_rejects_open_mesh() {
let vertices = [
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[1.0, 1.0, 0.0],
[0.0, 1.0, 0.0],
];
let faces = vec![vec![0, 1, 2, 3]];
assert!(build_planar_body(&vertices, &faces).is_none());
}
}