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.complete_brep_links();
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.complete_brep_links();
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 = (height / hb) / s5;
let n_vert = 1.0 / 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(13)),
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.complete_brep_links();
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.complete_brep_links();
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.complete_brep_links();
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.complete_brep_links();
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.complete_brep_links();
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));
}
sat.complete_brep_links();
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 generated_solid_topology_has_modeler_back_links() {
let sat = build_cylinder([0.0, 0.0, 0.0], 2.0, 5.0);
for vertex in sat.vertices() {
assert!(
!vertex.edge().is_null(),
"persisted ACIS vertices need an incident edge"
);
}
for edge in sat.edges() {
assert!(
!edge.coedge().is_null(),
"persisted ACIS edges need a representative coedge"
);
}
}
#[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());
}
}