diff --git a/README.md b/README.md index ab063e3f5..4d084ad79 100644 --- a/README.md +++ b/README.md @@ -54,18 +54,12 @@ pub extern "C" fn model(args: &HashMap) -> fj::Shape { .parse() .unwrap(); - let outer_edge = fj::Circle { radius: outer }; - let inner_edge = fj::Circle { radius: inner }; - - let footprint = fj::Difference { - a: outer_edge.into(), - b: inner_edge.into(), - }; - - let spacer = fj::Sweep { - shape: footprint.into(), - length: height, - }; + let outer_edge = fj::Circle::from_radius(outer); + let inner_edge = fj::Circle::from_radius(inner); + + let footprint = fj::Difference2d::from_objects(outer_edge.into(), inner_edge.into()); + + let spacer = fj::Sweep::from_shape_and_length(footprint.into(), height); spacer.into() } diff --git a/fj/src/shape_2d.rs b/fj/src/shape_2d.rs index 1e1dab5ba..61b4de9dd 100644 --- a/fj/src/shape_2d.rs +++ b/fj/src/shape_2d.rs @@ -16,17 +16,60 @@ pub enum Shape2d { Sketch(Sketch), } +impl Shape2d { + /// Get the rendering color of the larger object in RGBA + pub fn color(&self) -> [u8; 4] { + match &self { + Shape2d::Circle(c) => c.color(), + Shape2d::Sketch(s) => s.color(), + Shape2d::Difference(d) => d.color(), + } + } +} + /// A circle #[derive(Clone, Debug)] #[repr(C)] pub struct Circle { /// The radius of the circle - pub radius: f64, + radius: f64, + // The color of the circle in RGBA + color: [u8; 4], +} + +impl Circle { + /// Construct a new circle with a specific radius + pub fn from_radius(radius: f64) -> Self { + Self { + radius, + color: [255, 0, 0, 255], + } + } + + pub fn radius(&self) -> f64 { + self.radius + } + + /// Set the rendering color of the circle in RGBA + pub fn with_color(mut self, color: [u8; 4]) -> Self { + self.color = color; + self + } + + /// Set the rendering color of the circle in RGBA + pub fn set_color(&mut self, color: [u8; 4]) { + self.color = color; + } + + /// Get the rendering color of the circle in RGBA + pub fn color(&self) -> [u8; 4] { + self.color + } } impl From for Shape { fn from(shape: Circle) -> Self { - Self::Shape2d(Shape2d::Circle(shape)) + Self::Shape2d(shape.into()) } } @@ -41,15 +84,34 @@ impl From for Shape2d { #[repr(C)] pub struct Difference2d { /// The original shape - pub a: Shape2d, + a: Shape2d, /// The shape being subtracted - pub b: Shape2d, + b: Shape2d, +} + +impl Difference2d { + pub fn from_objects(a: Shape2d, b: Shape2d) -> Self { + Self { a, b } + } + + /// Get the rendering color of the larger object in RGBA + pub fn color(&self) -> [u8; 4] { + self.a.color() + } + + pub fn a(&self) -> &Shape2d { + &self.a + } + + pub fn b(&self) -> &Shape2d { + &self.b + } } impl From for Shape { fn from(shape: Difference2d) -> Self { - Self::Shape2d(Shape2d::Difference(Box::new(shape))) + Self::Shape2d(shape.into()) } } @@ -76,6 +138,8 @@ pub struct Sketch { ptr: *mut [f64; 2], length: usize, capacity: usize, + // The color of the sketch in RGBA + color: [u8; 4], } impl Sketch { @@ -94,6 +158,7 @@ impl Sketch { ptr, length, capacity, + color: [255, 0, 0, 255], } } @@ -117,17 +182,33 @@ impl Sketch { ret } + + /// Set the rendering color of the sketch in RGBA + pub fn with_color(mut self, color: [u8; 4]) -> Self { + self.color = color; + self + } + + /// Set the rendering color of the sketch in RGBA + pub fn set_color(&mut self, color: [u8; 4]) { + self.color = color; + } + + /// Get the rendering color of the sketch in RGBA + pub fn color(&self) -> [u8; 4] { + self.color + } } impl From for Shape { fn from(shape: Sketch) -> Self { - Self::Shape2d(Shape2d::Sketch(shape)) + Self::Shape2d(shape.into()) } } impl From for Shape2d { fn from(shape: Sketch) -> Self { - Self::Sketch(shape) + Shape2d::Sketch(shape) } } diff --git a/fj/src/shape_3d.rs b/fj/src/shape_3d.rs index c884b7db3..5c49d92e8 100644 --- a/fj/src/shape_3d.rs +++ b/fj/src/shape_3d.rs @@ -62,15 +62,33 @@ impl From for Shape3d { #[repr(C)] pub struct Sweep { /// The 2-dimensional shape being swept - pub shape: Shape2d, + shape: Shape2d, /// The length of the sweep - pub length: f64, + length: f64, +} + +impl Sweep { + pub fn from_shape_and_length(shape: Shape2d, length: f64) -> Self { + Self { shape, length } + } + + pub fn shape(&self) -> &Shape2d { + &self.shape + } + + pub fn length(&self) -> f64 { + self.length + } + + pub fn color(&self) -> [u8; 4] { + self.shape().color() + } } impl From for Shape { fn from(shape: Sweep) -> Self { - Self::Shape3d(Shape3d::Sweep(shape)) + Self::Shape3d(shape.into()) } } diff --git a/fj/src/syntax.rs b/fj/src/syntax.rs index 31e57d8a5..b62a693cf 100644 --- a/fj/src/syntax.rs +++ b/fj/src/syntax.rs @@ -44,7 +44,7 @@ where { fn sweep(&self, length: f64) -> crate::Sweep { let shape = self.clone().into(); - crate::Sweep { shape, length } + crate::Sweep::from_shape_and_length(shape, length) } } diff --git a/models/cuboid/src/lib.rs b/models/cuboid/src/lib.rs index e7146d90e..bf527c43a 100644 --- a/models/cuboid/src/lib.rs +++ b/models/cuboid/src/lib.rs @@ -12,12 +12,9 @@ pub extern "C" fn model(args: &HashMap) -> fj::Shape { [ x / 2., -y / 2.], [ x / 2., y / 2.], [-x / 2., y / 2.], - ]); + ]).with_color([100,255,0,200]); - let cuboid = fj::Sweep { - shape: rectangle.into(), - length: z, - }; + let cuboid = fj::Sweep::from_shape_and_length(rectangle.into(), z); cuboid.into() } diff --git a/models/spacer/src/lib.rs b/models/spacer/src/lib.rs index 3a84e8e98..46cdf27f1 100644 --- a/models/spacer/src/lib.rs +++ b/models/spacer/src/lib.rs @@ -12,24 +12,20 @@ pub extern "C" fn model(args: &HashMap) -> fj::Shape { .unwrap_or(&"0.5".to_owned()) .parse() .unwrap(); - let height = args + let height: f64 = args .get("height") .unwrap_or(&"1.0".to_owned()) .parse() .unwrap(); - let outer_edge = fj::Circle { radius: outer }; - let inner_edge = fj::Circle { radius: inner }; + let outer_edge = + fj::Circle::from_radius(outer).with_color([0, 0, 255, 255]); + let inner_edge = fj::Circle::from_radius(inner); - let footprint = fj::Difference2d { - a: outer_edge.into(), - b: inner_edge.into(), - }; + let footprint = + fj::Difference2d::from_objects(outer_edge.into(), inner_edge.into()); - let spacer = fj::Sweep { - shape: footprint.into(), - length: height, - }; + let spacer = fj::Sweep::from_shape_and_length(footprint.into(), height); spacer.into() } diff --git a/models/star/src/lib.rs b/models/star/src/lib.rs index 8bfb958ad..30b5b8ad4 100644 --- a/models/star/src/lib.rs +++ b/models/star/src/lib.rs @@ -50,18 +50,12 @@ pub extern "C" fn model(args: &HashMap) -> fj::Shape { inner.push([x / 2., y / 2.]); } - let outer = fj::Sketch::from_points(outer); + let outer = fj::Sketch::from_points(outer).with_color ([0, 255, 0, 200]); let inner = fj::Sketch::from_points(inner); - let footprint = fj::Difference2d { - a: outer.into(), - b: inner.into(), - }; + let footprint = fj::Difference2d::from_objects(outer.into(), inner.into()); - let star = fj::Sweep { - shape: footprint.into(), - length: h, - }; + let star = fj::Sweep::from_shape_and_length(footprint.into(), h); star.into() } diff --git a/src/kernel/algorithms/approximation.rs b/src/kernel/algorithms/approximation.rs index 36f5cc5b1..f70569895 100644 --- a/src/kernel/algorithms/approximation.rs +++ b/src/kernel/algorithms/approximation.rs @@ -275,6 +275,7 @@ mod tests { let face = Face::Face { surface, cycles: vec![abcd], + color: [255, 0, 0, 255], }; assert_eq!( diff --git a/src/kernel/algorithms/sweep.rs b/src/kernel/algorithms/sweep.rs index fcddf959e..c13a3f222 100644 --- a/src/kernel/algorithms/sweep.rs +++ b/src/kernel/algorithms/sweep.rs @@ -9,7 +9,7 @@ use crate::{ vertices::Vertex, }, }, - math::{Scalar, Transform, Vector}, + math::{Scalar, Transform, Triangle, Vector}, }; use super::approximation::Approximation; @@ -19,6 +19,7 @@ pub fn sweep_shape( mut shape_orig: Shape, path: Vector<3>, tolerance: Scalar, + color: [u8; 4], ) -> Shape { let mut shape = shape_orig.clone(); @@ -96,7 +97,11 @@ pub fn sweep_shape( shape .topology() - .add_face(Face::Face { surface, cycles }) + .add_face(Face::Face { + surface, + cycles, + color, + }) .unwrap(); } @@ -122,12 +127,18 @@ pub fn sweep_shape( quads.push([v0, v1, v2, v3]); } - let mut side_face = Vec::new(); + let mut side_face: Vec> = Vec::new(); for [v0, v1, v2, v3] in quads { side_face.push([v0, v1, v2].into()); side_face.push([v0, v2, v3].into()); } + // FIXME: We probably want to allow the use of custom colors for the "walls" of the swept + // object. + for s in side_face.iter_mut() { + s.set_color(color); + } + side_faces.push(Face::Triangles(side_face)); } @@ -159,6 +170,7 @@ mod tests { sketch.shape, Vector::from([0., 0., 1.]), Scalar::from_f64(0.), + [255, 0, 0, 255], ); let bottom_face = sketch.face.get().clone(); @@ -234,6 +246,7 @@ mod tests { let abc = Face::Face { surface, cycles: vec![cycles], + color: [255, 0, 0, 255], }; let face = shape.topology().add_face(abc).unwrap(); diff --git a/src/kernel/algorithms/transform.rs b/src/kernel/algorithms/transform.rs index b09d7cc22..8d82f8d35 100644 --- a/src/kernel/algorithms/transform.rs +++ b/src/kernel/algorithms/transform.rs @@ -25,7 +25,11 @@ pub fn transform_shape(mut original: Shape, transform: &Transform) -> Shape { for face in original.topology().faces() { let face = match face.get().clone() { - Face::Face { cycles, surface } => { + Face::Face { + cycles, + surface, + color, + } => { let mut cycles_trans = Vec::new(); for cycle in cycles { @@ -75,6 +79,7 @@ pub fn transform_shape(mut original: Shape, transform: &Transform) -> Shape { Face::Face { cycles: cycles_trans, surface, + color, } } Face::Triangles(mut triangles) => { diff --git a/src/kernel/shape/topology.rs b/src/kernel/shape/topology.rs index 41fb5532a..50230ba1c 100644 --- a/src/kernel/shape/topology.rs +++ b/src/kernel/shape/topology.rs @@ -190,7 +190,10 @@ impl Topology<'_> { /// cycles it refers to are part of the shape). Returns an error, if that is /// not the case. pub fn add_face(&mut self, face: Face) -> ValidationResult { - if let Face::Face { surface, cycles } = &face { + if let Face::Face { + surface, cycles, .. + } = &face + { let mut missing_surface = None; let mut missing_cycles = HashSet::new(); @@ -371,6 +374,7 @@ mod tests { .add_face(Face::Face { surface: surface.clone(), cycles: vec![cycle.clone()], + color: [255, 0, 0, 255], }) .unwrap_err(); assert!(err.missing_surface(&surface)); @@ -383,6 +387,7 @@ mod tests { shape.topology().add_face(Face::Face { surface, cycles: vec![cycle], + color: [255, 0, 0, 255], })?; Ok(()) diff --git a/src/kernel/shapes/circle.rs b/src/kernel/shapes/circle.rs index a94fbcfa8..a3a107f4c 100644 --- a/src/kernel/shapes/circle.rs +++ b/src/kernel/shapes/circle.rs @@ -19,7 +19,7 @@ impl ToShape for fj::Circle { let edge = shape .topology() - .add_circle(Scalar::from_f64(self.radius)) + .add_circle(Scalar::from_f64(self.radius())) .unwrap(); shape .topology() @@ -30,7 +30,11 @@ impl ToShape for fj::Circle { let surface = shape.geometry().add_surface(Surface::x_y_plane()); shape .topology() - .add_face(Face::Face { cycles, surface }) + .add_face(Face::Face { + cycles, + surface, + color: self.color(), + }) .unwrap(); shape @@ -38,8 +42,8 @@ impl ToShape for fj::Circle { fn bounding_volume(&self) -> Aabb<3> { Aabb { - min: Point::from([-self.radius, -self.radius, 0.0]), - max: Point::from([self.radius, self.radius, 0.0]), + min: Point::from([-self.radius(), -self.radius(), 0.0]), + max: Point::from([self.radius(), self.radius(), 0.0]), } } } diff --git a/src/kernel/shapes/difference_2d.rs b/src/kernel/shapes/difference_2d.rs index 556a91a95..ae2ee5bf1 100644 --- a/src/kernel/shapes/difference_2d.rs +++ b/src/kernel/shapes/difference_2d.rs @@ -22,7 +22,7 @@ impl ToShape for fj::Difference2d { let mut shape = Shape::new(); - let [mut a, mut b] = [&self.a, &self.b] + let [mut a, mut b] = [&self.a(), &self.b()] .map(|shape| shape.to_shape(tolerance, debug_info)); for shape in [&mut a, &mut b] { @@ -95,7 +95,11 @@ impl ToShape for fj::Difference2d { shape .topology() - .add_face(Face::Face { cycles, surface }) + .add_face(Face::Face { + cycles, + surface, + color: self.color(), + }) .unwrap(); shape @@ -105,6 +109,6 @@ impl ToShape for fj::Difference2d { // This is a conservative estimate of the bounding box: It's never going // to be bigger than the bounding box of the original shape that another // is being subtracted from. - self.a.bounding_volume() + self.a().bounding_volume() } } diff --git a/src/kernel/shapes/sketch.rs b/src/kernel/shapes/sketch.rs index 915b4d2c2..b92d74bfb 100644 --- a/src/kernel/shapes/sketch.rs +++ b/src/kernel/shapes/sketch.rs @@ -49,6 +49,7 @@ impl ToShape for fj::Sketch { let face = Face::Face { cycles: shape.topology().cycles().collect(), surface, + color: self.color(), }; shape.topology().add_face(face).unwrap(); diff --git a/src/kernel/shapes/sweep.rs b/src/kernel/shapes/sweep.rs index 9b2c06e01..9ba88eb63 100644 --- a/src/kernel/shapes/sweep.rs +++ b/src/kernel/shapes/sweep.rs @@ -9,15 +9,16 @@ use super::ToShape; impl ToShape for fj::Sweep { fn to_shape(&self, tolerance: Scalar, debug_info: &mut DebugInfo) -> Shape { sweep_shape( - self.shape.to_shape(tolerance, debug_info), - Vector::from([0., 0., self.length]), + self.shape().to_shape(tolerance, debug_info), + Vector::from([0., 0., self.length()]), tolerance, + self.color(), ) } fn bounding_volume(&self) -> Aabb<3> { - let mut aabb = self.shape.bounding_volume(); - aabb.max.z = self.length.into(); + let mut aabb = self.shape().bounding_volume(); + aabb.max.z = self.length().into(); aabb } } diff --git a/src/kernel/shapes/union.rs b/src/kernel/shapes/union.rs index a7066b06f..c3bf57040 100644 --- a/src/kernel/shapes/union.rs +++ b/src/kernel/shapes/union.rs @@ -103,6 +103,7 @@ fn copy_shape(mut orig: Shape, target: &mut Shape) { Face::Face { surface, cycles: cs, + color, } => { target .topology() @@ -112,6 +113,7 @@ fn copy_shape(mut orig: Shape, target: &mut Shape) { .iter() .map(|cycle| cycles[cycle].clone()) .collect(), + color: *color, }) .unwrap(); } diff --git a/src/kernel/topology/faces.rs b/src/kernel/topology/faces.rs index f3e451cae..8d5814dfb 100644 --- a/src/kernel/topology/faces.rs +++ b/src/kernel/topology/faces.rs @@ -47,6 +47,7 @@ pub enum Face { /// It might be less error-prone to specify the edges in surface /// coordinates. cycles: Vec>, + color: [u8; 4], }, /// The triangles of the face @@ -98,7 +99,7 @@ impl Face { debug_info: &mut DebugInfo, ) { match self { - Self::Face { surface, .. } => { + Self::Face { surface, color, .. } => { let approx = Approximation::for_face(self, tolerance); let points: Vec<_> = approx @@ -223,7 +224,9 @@ impl Face { out.extend(triangles.into_iter().map(|triangle| { let [a, b, c] = triangle.map(|point| point.canonical()); - Triangle::from([a, b, c]) + let mut t = Triangle::from([a, b, c]); + t.set_color(*color); + t })); } Self::Triangles(triangles) => out.extend(triangles),