1#![doc(
2 html_logo_url = "https://raw.githubusercontent.com/virtualritz/libfive-rs/HEAD/libfive/libfive-logo.png"
3)]
4use core::{
73 ffi::c_void,
74 ops::{Add, Div, Mul, Neg, Rem, Sub},
75 ptr, result, slice,
76};
77use libfive_sys as sys;
78use std::{ffi::CString, path::Path};
79use derive_more::{Display, Error, From};
80
81#[cfg(feature = "ahash")]
82type HashMap<K, V> = ahash::AHashMap<K, V>;
83#[cfg(not(feature = "ahash"))]
84type HashMap<K, V> = std::collections::HashMap<K, V>;
85
86#[cfg(feature = "stdlib")]
87mod stdlib;
88#[cfg(feature = "stdlib")]
89pub use stdlib::*;
90
91pub type Result<T> = result::Result<T, Error>;
99
100#[derive(Clone, Copy, Debug, Display, Eq, Error, From, Hash, Ord, PartialEq, PartialOrd)]
107#[non_exhaustive]
108pub enum Error {
109 VariablesCouldNotBeUpdated,
111 VariableNotFound,
113 VariableAlreadyAdded,
115 FileWriteFailed,
117 FileReadFailed,
119 TreeIsNotConstant,
121}
122
123pub trait Point2 {
125 fn new(x: f32, y: f32) -> Self;
126 fn x(&self) -> f32;
127 fn y(&self) -> f32;
128}
129
130pub trait Point3 {
132 fn new(x: f32, y: f32, z: f32) -> Self;
133 fn x(&self) -> f32;
134 fn y(&self) -> f32;
135 fn z(&self) -> f32;
136}
137
138pub type Contour<T> = Vec<T>;
141
142pub struct Bitmap(*mut sys::libfive_pixels);
146
147impl Bitmap {
148 pub fn as_slice(&self) -> &[bool] {
152 let bitmap = unsafe { self.0.as_ref() }.unwrap();
153 unsafe {
154 slice::from_raw_parts(
155 bitmap.pixels,
156 (bitmap.width * bitmap.height) as _,
157 )
158 }
159 }
160
161 pub fn as_slice_mut(&mut self) -> &mut [bool] {
165 let bitmap = unsafe { self.0.as_mut() }.unwrap();
166 unsafe {
167 slice::from_raw_parts_mut(
168 bitmap.pixels,
169 (bitmap.width * bitmap.height) as _,
170 )
171 }
172 }
173
174 pub fn pixel(&self, x: u32, y: u32) -> bool {
176 assert!(x < self.width() && y < self.height());
177 self.as_slice()[(y * self.height() + x) as usize]
178 }
179
180 pub fn width(&self) -> u32 {
182 unsafe { self.0.as_ref() }.unwrap().width
183 }
184
185 pub fn height(&self) -> u32 {
187 unsafe { self.0.as_ref() }.unwrap().height
188 }
189}
190
191impl Drop for Bitmap {
192 fn drop(&mut self) {
193 unsafe { sys::libfive_pixels_delete(&mut self.0 as *mut _ as _) };
194 }
195}
196
197pub struct TriangleMesh<T: Point3> {
204 pub positions: Vec<T>,
205 pub triangles: Vec<[u32; 3]>,
206}
207
208pub struct FlatTriangleMesh {
215 pub positions: Vec<f32>,
216 pub triangles: Vec<u32>,
217}
218
219impl<T: Point3> From<TriangleMesh<T>> for FlatTriangleMesh {
220 fn from(mesh: TriangleMesh<T>) -> FlatTriangleMesh {
221 FlatTriangleMesh {
222 positions: mesh
223 .positions
224 .into_iter()
225 .flat_map(|point| [point.x(), point.y(), point.z()])
226 .collect(),
227 triangles: mesh.triangles.into_iter().flatten().collect(),
228 }
229 }
230}
231
232pub struct Variables {
234 map: HashMap<String, usize>,
235 variables: Vec<*const c_void>,
236 values: Vec<f32>,
237 sys_variables: sys::libfive_vars,
238}
239
240impl Default for Variables {
241 fn default() -> Self {
242 Variables::new()
243 }
244}
245
246impl Variables {
247 pub fn new() -> Self {
249 Self {
250 map: HashMap::new(),
251 variables: Vec::new(),
252 values: Vec::new(),
253 sys_variables: sys::libfive_vars {
254 vars: ptr::null(),
255 values: ptr::null_mut(),
256 size: 0,
257 },
258 }
259 }
260
261 pub fn add(&mut self, name: &str, value: f32) -> Result<Tree> {
268 let name = name.to_string();
269 if self.map.contains_key(&name) {
270 Err(Error::VariableAlreadyAdded)
271 } else {
272 let tree = unsafe { sys::libfive_tree_var() };
273 let id = unsafe { sys::libfive_tree_id(tree) };
274
275 self.map.insert(name, self.variables.len());
276 self.variables.push(id);
277 self.values.push(value);
278 self.sys_variables.vars = self.variables.as_ptr() as *const _ as _;
280 self.sys_variables.values = self.values.as_ptr() as *const _ as _;
281 self.sys_variables.size = self.variables.len().try_into().unwrap();
282
283 Ok(Tree(tree))
284 }
285 }
286
287 pub fn set(&mut self, name: &str, value: f32) -> Result<()> {
294 if let Some(&index) = self.map.get(name) {
295 self.values[index] = value;
296 Ok(())
297 } else {
298 Err(Error::VariableNotFound)
299 }
300 }
301}
302
303impl Drop for Variables {
304 fn drop(&mut self) {
305 unsafe {
306 sys::libfive_vars_delete(&mut self.sys_variables as *mut _ as _)
307 };
308 }
309}
310
311pub struct Evaluator(sys::libfive_evaluator);
313
314impl Evaluator {
315 pub fn new(tree: &Tree, variables: &Variables) -> Self {
316 Self(unsafe {
317 sys::libfive_tree_evaluator(tree.0, variables.sys_variables)
318 })
319 }
320
321 pub fn update(&mut self, variables: &Variables) -> Result<()> {
322 if unsafe {
323 sys::libfive_evaluator_update_vars(self.0, variables.sys_variables)
324 } {
325 Err(Error::VariablesCouldNotBeUpdated)
326 } else {
327 Ok(())
328 }
329 }
330
331 pub fn write_stl(
334 &self,
335 path: impl AsRef<Path>,
336 region: &Region3,
337 ) -> Result<()> {
338 let path = c_string_from_path(path);
339
340 if unsafe {
341 sys::libfive_evaluator_save_mesh(self.0, region.0, path.as_ptr())
342 } {
343 Ok(())
344 } else {
345 Err(Error::FileWriteFailed)
346 }
347 }
348}
349
350impl Drop for Evaluator {
351 fn drop(&mut self) {
352 unsafe { sys::libfive_evaluator_delete(self.0) };
353 }
354}
355
356#[derive(Clone, Copy, Debug, PartialEq)]
358pub struct Region2(sys::libfive_region2);
359
360impl Region2 {
361 pub fn new(x_min: f32, x_max: f32, y_min: f32, y_max: f32) -> Self {
362 Self(sys::libfive_region2 {
363 X: sys::libfive_interval {
364 lower: x_min,
365 upper: x_max,
366 },
367 Y: sys::libfive_interval {
368 lower: y_min,
369 upper: y_max,
370 },
371 })
372 }
373}
374
375#[derive(Clone, Copy, Debug, PartialEq)]
377pub struct Region3(sys::libfive_region3);
378
379impl Region3 {
380 pub fn new(
381 x_min: f32,
382 x_max: f32,
383 y_min: f32,
384 y_max: f32,
385 z_min: f32,
386 z_max: f32,
387 ) -> Self {
388 Self(sys::libfive_region3 {
389 X: sys::libfive_interval {
390 lower: x_min,
391 upper: x_max,
392 },
393 Y: sys::libfive_interval {
394 lower: y_min,
395 upper: y_max,
396 },
397 Z: sys::libfive_interval {
398 lower: z_min,
399 upper: z_max,
400 },
401 })
402 }
403}
404
405#[allow(dead_code)]
406#[repr(i32)]
407enum Op {
408 Invalid = 0,
409
410 Constant = 1,
411 VarX = 2,
412 VarY = 3,
413 VarZ = 4,
414 VarFree = 5,
415 ConstVar = 6,
416
417 Square = 7,
418 Sqrt = 8,
419 Neg = 9,
420 Sin = 10,
421 Cos = 11,
422 Tan = 12,
423 Asin = 13,
424 Acos = 14,
425 Atan = 15,
426 Exp = 16,
427 Abs = 28,
428 Log = 30,
429 Recip = 29,
430
431 Add = 17,
432 Mul = 18,
433 Min = 19,
434 Max = 20,
435 Sub = 21,
436 Div = 22,
437 Atan2 = 23,
438 Pow = 24,
439 NthRoot = 25,
440 Mod = 26,
441 NanFill = 27,
442 Compare = 31,
443
444 Oracle = 32,
445}
446
447macro_rules! fn_unary {
448 ($func_name:ident, $op_code:ident) => {
449 #[inline]
450 pub fn $func_name(&self) -> Self {
451 Self(unsafe { sys::libfive_tree_unary(Op::$op_code as _, self.0) })
452 }
453 };
454}
455
456macro_rules! fn_binary {
457 ($func_name:ident, $op_code:ident, $other:ident) => {
458 #[inline]
459 pub fn $func_name(self, $other: Self) -> Self {
460 Self(unsafe {
461 sys::libfive_tree_binary(Op::$op_code as _, self.0, $other.0)
462 })
463 }
464 };
465}
466
467macro_rules! op_binary {
468 ($func_name:ident, $op_code:ident) => {
469 impl $op_code for Tree {
470 type Output = Tree;
471 #[inline]
472 fn $func_name(self, rhs: Tree) -> Self::Output {
473 self.$func_name(rhs)
474 }
475 }
476 };
477}
478
479#[derive(Eq, PartialEq)]
498pub struct Tree(sys::libfive_tree);
499
500pub type TreeFloat = Tree;
504
505impl From<f32> for Tree {
507 fn from(constant: f32) -> Self {
509 Self(unsafe { sys::libfive_tree_const(constant) })
510 }
511}
512
513impl Tree {
515 #[inline]
516 pub fn x() -> Self {
517 Self(unsafe { sys::libfive_tree_x() })
518 }
519
520 #[inline]
521 pub fn y() -> Self {
522 Self(unsafe { sys::libfive_tree_y() })
523 }
524
525 #[inline]
526 pub fn z() -> Self {
527 Self(unsafe { sys::libfive_tree_z() })
528 }
529
530 }
532
533impl Tree {
535 fn_unary!(square, Square);
536 fn_unary!(sqrt, Sqrt);
537 fn_unary!(neg, Neg);
538 fn_unary!(sin, Sin);
539 fn_unary!(cos, Cos);
540 fn_unary!(tan, Tan);
541 fn_unary!(asin, Asin);
542 fn_unary!(acos, Acos);
543 fn_unary!(atan, Atan);
544 fn_unary!(exp, Exp);
545 fn_unary!(abs, Abs);
546 fn_unary!(log, Log);
547 fn_unary!(recip, Recip);
548
549 fn_binary!(add, Add, rhs);
550 fn_binary!(mul, Mul, rhs);
551 fn_binary!(min, Min, rhs);
552 fn_binary!(max, Max, rhs);
553 fn_binary!(sub, Sub, rhs);
554 fn_binary!(div, Div, rhs);
555 fn_binary!(atan2, Atan2, other);
556 fn_binary!(pow, Pow, exp);
557 fn_binary!(nth_root, NthRoot, n);
558 fn_binary!(rem, Mod, rhs);
559 fn_binary!(nan_fill, NanFill, rhs);
560 fn_binary!(compare, Compare, rhs);
561
562 pub fn is_variable(&self) -> bool {
564 unsafe { sys::libfive_tree_is_var(self.0) }
565 }
566
567 pub fn as_f32(&self) -> Result<f32> {
576 let mut success = false;
577 let value = unsafe {
578 sys::libfive_tree_get_const(self.0, &mut success as *mut _)
579 };
580
581 if success {
582 Ok(value)
583 } else {
584 Err(Error::TreeIsNotConstant)
585 }
586 }
587}
588
589impl Tree {
606 #[inline]
609 pub fn to_bitmap(
610 &self,
611 region: &Region2,
612 z: f32,
613 resolution: f32,
614 ) -> Bitmap {
615 Bitmap(unsafe {
616 sys::libfive_tree_render_pixels(self.0, region.0, z, resolution)
617 })
618 }
619
620 pub fn to_triangle_mesh<T: Point3>(
622 &self,
623 region: &Region3,
624 resolution: f32,
625 ) -> Option<TriangleMesh<T>> {
626 match unsafe {
627 sys::libfive_tree_render_mesh(self.0, region.0, resolution).as_mut()
628 } {
629 Some(raw_mesh) => {
630 let mesh = TriangleMesh::<T> {
631 positions: (0..raw_mesh.vert_count)
632 .map(|index| {
633 let vertex =
634 &unsafe { *raw_mesh.verts.add(index as _) };
635 T::new(vertex.x, vertex.y, vertex.z)
636 })
637 .collect(),
638 triangles: (0..raw_mesh.tri_count)
639 .map(|index| {
640 let triangle =
641 &unsafe { *raw_mesh.tris.add(index as _) };
642 [triangle.a, triangle.b, triangle.c]
643 })
644 .collect(),
645 };
646
647 unsafe {
648 sys::libfive_mesh_delete(raw_mesh as *mut _ as _);
649 }
650
651 Some(mesh)
652 }
653 None => None,
654 }
655 }
656
657 pub fn to_contour_2d<T: Point2>(
660 &self,
661 region: Region2,
662 z: f32,
663 resolution: f32,
664 ) -> Option<Vec<Contour<T>>> {
665 match unsafe {
666 sys::libfive_tree_render_slice(self.0, region.0, z, resolution)
667 .as_mut()
668 } {
669 Some(raw_contours) => {
670 let contours = (0..raw_contours.count)
671 .map(|index| {
672 let contour =
673 unsafe { raw_contours.cs.add(index as _).as_ref() }
674 .unwrap();
675 (0..contour.count)
676 .map(|index| {
677 let point = unsafe {
678 contour.pts.add(index as _).as_ref()
679 }
680 .unwrap();
681 T::new(point.x, point.y)
682 })
683 .collect()
684 })
685 .collect();
686
687 unsafe {
688 sys::libfive_contours_delete(raw_contours as *mut _ as _);
689 }
690
691 Some(contours)
692 }
693 None => None,
694 }
695 }
696
697 pub fn to_contour_3d<T: Point3>(
699 &self,
700 region: Region2,
701 z: f32,
702 resolution: f32,
703 ) -> Option<Vec<Contour<T>>> {
704 let raw_contours = unsafe {
705 sys::libfive_tree_render_slice3(self.0, region.0, z, resolution)
706 .as_ref()
707 };
708
709 if let Some(raw_contours) = raw_contours {
710 let contours = (0..raw_contours.count)
711 .map(|index| {
712 let contour =
713 unsafe { raw_contours.cs.add(index as _).as_ref() }
714 .unwrap();
715
716 (0..contour.count)
717 .map(|index| {
718 let point =
719 unsafe { contour.pts.add(index as _).as_ref() }
720 .unwrap();
721 T::new(point.x, point.y, point.z)
722 })
723 .collect()
724 })
725 .collect();
726
727 unsafe {
728 sys::libfive_contours_delete(&raw_contours as *const _ as _);
729 }
730
731 Some(contours)
732 } else {
733 None
734 }
735 }
736
737 pub fn write_svg(
741 &self,
742 path: impl AsRef<Path>,
743 region: &Region2,
744 z: f32,
745 resolution: f32,
746 ) {
747 let path = c_string_from_path(path);
748
749 unsafe {
750 sys::libfive_tree_save_slice(
751 self.0,
752 region.0,
753 z,
754 resolution,
755 path.as_ptr(),
756 );
757 }
758 }
759
760 pub fn write_stl(
763 &self,
764 path: impl AsRef<Path>,
765 region: &Region3,
766 resolution: f32,
767 ) -> Result<()> {
768 let path = c_string_from_path(path);
769
770 println!("Foobar! {:?}", path);
771
772 if unsafe {
773 sys::libfive_tree_save_mesh(
774 self.0,
775 region.0,
776 resolution,
777 path.as_ptr(),
778 )
779 } {
780 Ok(())
781 } else {
782 Err(Error::FileWriteFailed)
783 }
784 }
785
786 pub fn save(&self, path: impl AsRef<Path>) -> Result<()> {
797 let path = c_string_from_path(path);
798
799 if unsafe { sys::libfive_tree_save(self.0, path.as_ptr()) } {
800 Ok(())
801 } else {
802 Err(Error::FileWriteFailed)
803 }
804 }
805
806 pub fn load(&self, path: impl AsRef<Path>) -> Result<Tree> {
814 let path = c_string_from_path(path);
815
816 match unsafe { sys::libfive_tree_load(path.as_ptr()).as_mut() } {
817 Some(tree) => Ok(Self(tree as _)),
818 None => Err(Error::FileReadFailed),
819 }
820 }
821}
822
823impl Drop for Tree {
824 fn drop(&mut self) {
825 unsafe { sys::libfive_tree_delete(self.0) };
826 }
827}
828
829op_binary!(add, Add);
830op_binary!(div, Div);
831op_binary!(mul, Mul);
832op_binary!(rem, Rem);
833op_binary!(sub, Sub);
834
835impl Neg for Tree {
836 type Output = Tree;
837
838 fn neg(self) -> Self::Output {
839 Self(unsafe { sys::libfive_tree_unary(Op::Neg as _, self.0) })
840 }
841}
842
843fn c_string_from_path<P: AsRef<Path>>(path: P) -> CString {
844 CString::new(path.as_ref().as_os_str().as_encoded_bytes()).unwrap()
845}
846
847#[test]
848fn test_2d() -> Result<()> {
849 let circle = Tree::x().square() + Tree::y().square() - 1.0.into();
850
851 circle.write_svg(
852 "circle.svg",
853 &Region2::new(-2.0, 2.0, -2.0, 2.0),
854 0.0,
855 10.0,
856 );
857
858 Ok(())
859}
860
861#[test]
862#[cfg(feature = "stdlib")]
863fn test_3d() -> Result<()> {
864 let f_rep_shape = Tree::sphere(1.0.into(), TreeVec3::default())
865 .difference_multi(vec![
866 Tree::sphere(0.6.into(), TreeVec3::default()),
867 Tree::cylinder_z(
868 0.6.into(),
869 2.0.into(),
870 TreeVec3::new(0.0, 0.0, -1.0),
871 ),
872 Tree::cylinder_z(
873 0.6.into(),
874 2.0.into(),
875 TreeVec3::new(0.0, 0.0, -1.0),
876 )
877 .reflect_xz(),
878 Tree::cylinder_z(
879 0.6.into(),
880 2.0.into(),
881 TreeVec3::new(0.0, 0.0, -1.0),
882 )
883 .reflect_yz(),
884 ]);
885
886 f_rep_shape.write_stl(
887 "f-rep-shape.stl",
888 &Region3::new(-2.0, 2.0, -2.0, 2.0, -2.0, 2.0),
889 10.0,
891 )?;
892
893 Ok(())
894}
895
896