Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,526 changes: 1,047 additions & 479 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ glam = "0.30.9"
rayon = { version = "1.11.0", optional = true }

tobj = {version = "4.0.3", optional = true}
bevy = { version = "0.16.1", optional = true }
bevy_panorbit_camera = { version = "0.26.0", optional = true }
bevy = { version = "0.18.0", optional = true }
bevy_panorbit_camera = { version = "0.34.0", optional = true }

[features]
default = []
verbose = []
f32 = []
rayon = ["dep:rayon"]

Expand Down
30 changes: 16 additions & 14 deletions examples/menger_sponge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::time::Instant;
use bevy::prelude::*;
use bevy::pbr::wireframe::{WireframePlugin, Wireframe, WireframeColor};
use bevy::asset::RenderAssetUsages;
use bevy::render::mesh::PrimitiveTopology;
use bevy::mesh::PrimitiveTopology;
use bevy::color::palettes::css::*;
use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin};
use boolmesh::prelude::*;
Expand Down Expand Up @@ -70,27 +70,29 @@ fn setup(
));
}

pub fn menger_sponge(n: usize) -> Manifold {
pub fn menger_sponge(n: usize) -> Manifold<()> {
let res = generate_cube().unwrap();
let mut holes = vec![];
fractal(&res, &mut holes, 0., 0., 1., 1, n);
let holes_z = compose(&holes).unwrap();

let rot = |rx: f64, ry: f64, rz: f64| {
let ts = holes_z.hs.iter().map(|h| h.tail).collect::<Vec<_>>();
let mut ps = holes_z.ps.clone();
let x = rx as boolmesh::Real;
let y = ry as boolmesh::Real;
let z = rz as boolmesh::Real;
let r = boolmesh::Mat3::from_euler(glam::EulerRot::XYZ, x, y, z);
for p in ps.iter_mut() { *p = r * *p; }
let mut flat = vec![];
for p in ps {
flat.push(if (p.x - 0.5).abs() < 1e-4 { 0.5 } else if (p.x + 0.5).abs() < 1e-4 { -0.5 } else { p.x as f64});
flat.push(if (p.y - 0.5).abs() < 1e-4 { 0.5 } else if (p.y + 0.5).abs() < 1e-4 { -0.5 } else { p.y as f64});
flat.push(if (p.z - 0.5).abs() < 1e-4 { 0.5 } else if (p.z + 0.5).abs() < 1e-4 { -0.5 } else { p.z as f64});
let r = boolmesh::Mat3::from_euler(
glam::EulerRot::XYZ,
rx as boolmesh::Real,
ry as boolmesh::Real,
rz as boolmesh::Real
);
for p in ps.iter_mut() {
let mut v = r * *p;
v.x = if (v.x - 0.5).abs() < 1e-4 { 0.5 } else if (v.x + 0.5).abs() < 1e-4 { -0.5 } else { v.x };
v.y = if (v.y - 0.5).abs() < 1e-4 { 0.5 } else if (v.y + 0.5).abs() < 1e-4 { -0.5 } else { v.y };
v.z = if (v.z - 0.5).abs() < 1e-4 { 0.5 } else if (v.z + 0.5).abs() < 1e-4 { -0.5 } else { v.z };
*p = v;

}
Manifold::new(&flat, &ts).unwrap()
Manifold::new(ps, holes_z.hs.iter().map(|h| h.tail).collect::<Vec<_>>(), None, None, None).unwrap()
};

let holes_x = rot(PI / 2., 0., 0.);
Expand Down
9 changes: 5 additions & 4 deletions examples/multiple_models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use bevy::prelude::*;
use bevy::pbr::wireframe::{WireframePlugin, Wireframe, WireframeColor};
use bevy::color::palettes::css::*;
use bevy::asset::RenderAssetUsages;
use bevy::render::mesh::PrimitiveTopology;
use bevy::mesh::PrimitiveTopology;
use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin};

#[derive(Component)]
Expand Down Expand Up @@ -36,11 +36,12 @@ fn setup(
let (m0, _) = tobj::load_obj(obj_path_1, &tobj::LoadOptions { ..Default::default() }).expect("Failed to load the first obj file");
let (m1, _) = tobj::load_obj(obj_path_2, &tobj::LoadOptions { ..Default::default() }).expect("Failed to load the second obj file");

let mut mfs = vec![];
let mut mfs: Vec<Manifold<()>> = vec![];
for m in vec![&m0[0].mesh, &m1[0].mesh] {
mfs.push(Manifold::new(
&m.positions.iter().map(|&v| v as f64).collect::<Vec<_>>(),
&m.indices.iter().map(|&v| v as usize).collect::<Vec<_>>(),
&m.positions,
&m.indices,
None, None, None
).unwrap());
}
mfs.push(compute_boolean(&mfs[0], &mfs[1], OpType::Subtract).unwrap());
Expand Down
51 changes: 40 additions & 11 deletions examples/primitives_demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use bevy::prelude::*;
use bevy::pbr::wireframe::{WireframePlugin, Wireframe, WireframeColor};
use bevy::asset::RenderAssetUsages;
use bevy::render::mesh::PrimitiveTopology;
use bevy::mesh::PrimitiveTopology;
use bevy::color::palettes::css::*;
use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin};
use boolmesh::prelude::*;
Expand All @@ -30,38 +30,67 @@ fn setup(
mut mats: ResMut<Assets<StandardMaterial>>,
mut meshes: ResMut<Assets<Mesh>>
) {
let mut mfd0 = generate_cube().unwrap();
let mut mfd1 = generate_cylinder(0.4, 1., 50, 10).unwrap();
let mut mfd2 = generate_cube().unwrap();
let idcs1 = mfd1.hs.chunks(3).map(|cs| [cs[0].tail, cs[1].tail, cs[2].tail]).collect::<Vec<_>>();

let mut mfd0 = generate_cylinder(1., 1., 30, 10).unwrap();
let mut mfd1 = generate_uv_sphere(30, 30).unwrap();
let mut mfd2 = generate_torus(1., 0.1, 30, 30).unwrap();
mfd1.translate(0., 0.5, 0.);
let res = compute_boolean(&mfd0, &mfd1, OpType::Add).unwrap();
mfd0.set_variable((0..mfd0.nf).map(|i| Vec2::new(1., i as f32)).collect::<Vec<_>>());
mfd1.set_variable((0..mfd1.nf).map(|i| {
let idcs = idcs1[i];
let p0 = mfd1.ps[idcs[0]];
let p1 = mfd1.ps[idcs[1]];
let p2 = mfd1.ps[idcs[2]];
Vec2::new(2., (p0 + p1 + p2).y as f32 / 3. + 0.5)
}).collect::<Vec<_>>());

mfd2.set_variable((0..mfd2.nf).map(|i| Vec2::new(3., i as f32)).collect::<Vec<_>>());
mfd1.translate(0.5, 0., 0.25);
mfd2.translate(-0.5, 0.5, 0.5);

let res = compute_boolean(&mfd0, &mfd1, OpType::Subtract).unwrap();
let res = compute_boolean(&res, &mfd2, OpType::Subtract).unwrap();

cmds.spawn((DirectionalLight::default(), Transform::from_xyz(30., 40., 30.)));
cmds.spawn((PointLight { shadows_enabled: false, ..default() },
Transform::from_xyz(4.0, 5.0, 4.0).looking_at(Vec3::ZERO, Vec3::Y),
));

cmds.spawn((Transform::from_translation(Vec3::new(0., 0., 2.)), PanOrbitCamera::default(),));

let mut m = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default());

let mut pos = vec![];
let mut col = vec![];
let mut vns = vec![];
for (fid, hs) in res.hs.chunks(3).enumerate() {
let p0 = res.ps[hs[0].tail];
let p1 = res.ps[hs[1].tail];
let p2 = res.ps[hs[2].tail];
let n = res.face_normals[fid];
let p0 = res.ps[hs[0].tail];
let p1 = res.ps[hs[1].tail];
let p2 = res.ps[hs[2].tail];
let n = res.face_normals[fid];
let var = res.variable.as_ref().unwrap()[fid];
let mut c = [0., 0., 0., 1.];
if var.x as usize == 1 { c[0] = 1.; }
if var.x as usize == 2 { c[1] = var.y as f32; }
if var.x as usize == 3 { c[2] = 1.; }

pos.push([p0.x as f32, p0.y as f32, p0.z as f32]);
pos.push([p1.x as f32, p1.y as f32, p1.z as f32]);
pos.push([p2.x as f32, p2.y as f32, p2.z as f32]);
col.push(c);
col.push(c);
col.push(c);
vns.push([n.x as f32, n.y as f32, n.z as f32]);
vns.push([n.x as f32, n.y as f32, n.z as f32]);
vns.push([n.x as f32, n.y as f32, n.z as f32]);
}
m.insert_attribute(Mesh::ATTRIBUTE_POSITION, pos);
m.insert_attribute(Mesh::ATTRIBUTE_NORMAL, vns);
m.insert_attribute(Mesh::ATTRIBUTE_COLOR, col);

cmds.spawn((
Mesh3d(meshes.add(m).clone()),
MeshMaterial3d(mats.add(StandardMaterial { base_color: GRAY.into(), ..default() })),
MeshMaterial3d(mats.add(StandardMaterial { unlit: true, base_color: GRAY.into(), ..default() })),
Transform::default(),
Wireframe,
WireframeColor { color: Srgba::rgb(0.3, 0.3, 0.3).into() },
Expand Down
8 changes: 4 additions & 4 deletions src/boolean03/kernel03.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

use super::kernel02::Kernel02;
use crate::bounds::{BPos, Query};
use crate::{Real, Vec2, Manifold};
use crate::{Data, Real, Vec2, Manifold};

pub fn winding03(
mp: &Manifold,
mq: &Manifold,
pub fn winding03<T: Data>(
mp: &Manifold<T>,
mq: &Manifold<T>,
expand: Real,
fwd: bool
) -> Vec<i32> {
Expand Down
8 changes: 4 additions & 4 deletions src/boolean03/kernel12.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//--- Copyright (C) 2025 Saki Komikado <komietty@gmail.com>,
//--- This Source Code Form is subject to the terms of the Mozilla Public License v.2.0.

use crate::{Manifold, Half, Real, Vec3};
use crate::{Data, Manifold, Half, Real, Vec3};
use crate::bounds::{BBox, Query};
use super::kernel01::intersect;
use super::kernel02::Kernel02;
Expand Down Expand Up @@ -72,9 +72,9 @@ impl<'a> Kernel12<'a> {
}
}

pub fn intersect12 (
mp: &Manifold,
mq: &Manifold,
pub fn intersect12<T: Data> (
mp: &Manifold<T>,
mq: &Manifold<T>,
p1q2: &mut Vec<[usize; 2]>,
expand: Real,
fwd: bool
Expand Down
10 changes: 5 additions & 5 deletions src/boolean03/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ pub mod kernel02;
pub mod kernel11;
pub mod kernel12;
pub mod kernel03;

use crate::boolean03::kernel03::winding03;
use crate::boolean03::kernel12::intersect12;
use crate::common::{OpType, Vec3};
use crate::manifold::Manifold;
use crate::{Data, Manifold, OpType, Vec3};

pub struct Boolean03 {
pub p1q2: Vec<[usize; 2]>,
Expand All @@ -22,9 +22,9 @@ pub struct Boolean03 {
pub v21: Vec<Vec3>,
}

pub fn boolean03(
mp: &Manifold,
mq: &Manifold,
pub fn boolean03<T: Data>(
mp: &Manifold<T>,
mq: &Manifold<T>,
op: &OpType,
) -> Boolean03 {
let e = if op == &OpType::Add { 1. } else { -1. };
Expand Down
14 changes: 7 additions & 7 deletions src/boolean45/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use std::mem;
use std::collections::HashMap;
use crate::{Manifold, Real, Vec3, Half, Tref, face_of};
use crate::{Data, Manifold, Real, Vec3, Half, Tref, face_of};
use crate::boolean03::Boolean03;
use crate::bounds::BBox;
use crate::OpType;
Expand Down Expand Up @@ -40,9 +40,9 @@ fn exclusive_scan(input: &[i32], output: &mut [i32], offset: i32) {
}
}

fn size_output(
mp: &Manifold,
mq: &Manifold,
fn size_output<T: Data>(
mp: &Manifold<T>,
mq: &Manifold<T>,
i03: &[i32],
i30: &[i32],
i12: &[i32],
Expand Down Expand Up @@ -325,9 +325,9 @@ pub struct Boolean45 {
pub nv_from_q: usize,
}

pub fn boolean45(
mp: &Manifold,
mq: &Manifold,
pub fn boolean45<T: Data>(
mp: &Manifold<T>,
mq: &Manifold<T>,
b03: &Boolean03,
op: &OpType
) -> Boolean45 {
Expand Down
40 changes: 40 additions & 0 deletions src/cleanup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//--- Copyright (C) 2025 Saki Komikado <komietty@gmail.com>,
//--- This Source Code Form is subject to the terms of the Mozilla Public License v.2.0.

use crate::common::{Half, Tref};
use crate::manifold::bounds::BBox;
use crate::manifold::collider::{morton_code, K_NO_CODE};
use crate::Vec3;

pub fn cleanup_unused_verts(
ps: &mut Vec<Vec3>,
hs: &mut Vec<Half>,
rs: &mut Vec<Tref>,
) {
let bb = BBox::new(None, ps);
let mt = ps.iter().map(|p| morton_code(p, &bb)).collect::<Vec<_>>();

let mut new2old = (0..ps.len()).collect::<Vec<_>>();
let mut old2new = vec![0; ps.len()];
new2old.sort_by_key(|&i| mt[i]);
for (new, &old) in new2old.iter().enumerate() { old2new[old] = new; }

// reindex verts
for h in hs.iter_mut() {
if h.pair().is_none() { continue; }
h.tail = old2new[h.tail];
h.head = old2new[h.head];
}

// truncate pos container
let nv = new2old
.iter()
.position(|&v| mt[v] >= K_NO_CODE)
.unwrap_or(new2old.len());

new2old.truncate(nv);

*ps = new2old.iter().map(|&i| ps[i]).collect();
*rs = hs.chunks(3).enumerate().filter_map(|(i, t)| t[0].pair().map(|_| rs[i].clone())).collect();
*hs = hs.iter().filter(|h| h.pair().is_some()).cloned().collect();
}
11 changes: 0 additions & 11 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ pub fn next_of(hid: usize) -> usize { let mut i = hid + 1; if i.is_multiple_of(3

#[derive(Clone, Debug, Copy)]
pub struct Tref {
pub oid: usize, // original instance id,
pub mid: usize, // mesh id
pub fid: usize, // face id
pub pid: i32, // planer id
Expand All @@ -65,7 +64,6 @@ pub struct Tref {
impl Default for Tref {
fn default() -> Self {
Self {
oid: usize::MAX,
mid: usize::MAX,
fid: usize::MAX,
pid: -1
Expand Down Expand Up @@ -132,12 +130,3 @@ pub fn compute_orthogonal(n: Vec3) -> Vec3 {
else { Vec3::new(0., 1., 0.) };
n.cross(b).normalize()
}

/*
enum CsgNodeType { Union, Intersection, Difference, Leaf }
trait CsgNode { fn ToLeafNode () { } }
struct CsgOpNode { }
struct CsgLeafNode { }
impl CsgNode for CsgOpNode { fn ToLeafNode () { } }
impl CsgNode for CsgLeafNode { }
*/
8 changes: 4 additions & 4 deletions src/compose/cone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
//--- This Source Code Form is subject to the terms of the Mozilla Public License v.2.0.

use std::f64::consts::PI;
use crate::{Manifold, Vec3, Real, compute_orthogonal};
use crate::{Manifold, Vec3, Real, compute_orthogonal, Data};
use crate::common::Vec3u;

pub fn generate_cone(
pub fn generate_cone<T: Data>(
apex: Vec3,
center: Vec3,
radius: Real,
divide: usize,
) -> Result<Manifold, String> {
) -> Result<Manifold<T>, String> {
let d = (PI * 2. / divide as f64) as Real;
let n = (center - apex).normalize();
let b1 = compute_orthogonal(n);
Expand All @@ -29,5 +29,5 @@ pub fn generate_cone(

ps.push(apex);
ps.push(center);
Manifold::new_impl(ps, ts, None, None)
Manifold::new(ps, ts, None, None, None)
}
Loading