From 62fb197291ace54f2b004164bb0827226a3e8810 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Sun, 13 Mar 2022 13:44:44 +0100 Subject: [PATCH] Implement basic coloring support for shapes The examples have been extended to demonstrate the coloring of objects. A few bits and pieces are probably still missing or could be improved but let's look at those in another step. Closes #291 Signed-off-by: Daniel Egger --- fj/src/shape_2d.rs | 64 ++++++++++++++++++++++++-- fj/src/shape_3d.rs | 4 ++ models/cuboid/src/lib.rs | 2 +- models/spacer/src/lib.rs | 8 ++-- models/star/src/lib.rs | 2 +- src/kernel/algorithms/approximation.rs | 1 + src/kernel/algorithms/sweep.rs | 19 ++++++-- src/kernel/algorithms/transform.rs | 7 ++- src/kernel/shape/topology.rs | 7 ++- src/kernel/shapes/circle.rs | 6 ++- src/kernel/shapes/difference_2d.rs | 6 ++- src/kernel/shapes/sketch.rs | 1 + src/kernel/shapes/sweep.rs | 1 + src/kernel/shapes/union.rs | 2 + src/kernel/topology/faces.rs | 7 ++- 15 files changed, 119 insertions(+), 18 deletions(-) diff --git a/fj/src/shape_2d.rs b/fj/src/shape_2d.rs index ec10d6d0ce..4cac6876d5 100644 --- a/fj/src/shape_2d.rs +++ b/fj/src/shape_2d.rs @@ -16,27 +16,59 @@ 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 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 } + 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()) } } @@ -62,6 +94,11 @@ impl Difference2d { 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 } @@ -100,6 +137,8 @@ pub struct Sketch { ptr: *mut [f64; 2], length: usize, capacity: usize, + // The color of the sketch in RGBA + color: [u8; 4], } impl Sketch { @@ -118,6 +157,7 @@ impl Sketch { ptr, length, capacity, + color: [255, 0, 0, 255], } } @@ -141,17 +181,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 4d7d96f9ee..5c49d92e82 100644 --- a/fj/src/shape_3d.rs +++ b/fj/src/shape_3d.rs @@ -80,6 +80,10 @@ impl Sweep { pub fn length(&self) -> f64 { self.length } + + pub fn color(&self) -> [u8; 4] { + self.shape().color() + } } impl From for Shape { diff --git a/models/cuboid/src/lib.rs b/models/cuboid/src/lib.rs index 73046c55b9..bf527c43ad 100644 --- a/models/cuboid/src/lib.rs +++ b/models/cuboid/src/lib.rs @@ -12,7 +12,7 @@ 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::from_shape_and_length(rectangle.into(), z); diff --git a/models/spacer/src/lib.rs b/models/spacer/src/lib.rs index 45d1a9af7b..46cdf27f1d 100644 --- a/models/spacer/src/lib.rs +++ b/models/spacer/src/lib.rs @@ -12,16 +12,18 @@ 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::from_radius(outer); + 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::from_objects(outer_edge.into(), inner_edge.into()); + let footprint = + fj::Difference2d::from_objects(outer_edge.into(), inner_edge.into()); let spacer = fj::Sweep::from_shape_and_length(footprint.into(), height); diff --git a/models/star/src/lib.rs b/models/star/src/lib.rs index 9193483201..30b5b8ad4a 100644 --- a/models/star/src/lib.rs +++ b/models/star/src/lib.rs @@ -50,7 +50,7 @@ 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::from_objects(outer.into(), inner.into()); diff --git a/src/kernel/algorithms/approximation.rs b/src/kernel/algorithms/approximation.rs index 36f5cc5b13..f705698953 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 fcddf959ee..c13a3f222e 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 b09d7cc22b..8d82f8d35e 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 41fb5532aa..50230ba1c2 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 e7a8b5bbaf..a3a107f4c2 100644 --- a/src/kernel/shapes/circle.rs +++ b/src/kernel/shapes/circle.rs @@ -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 diff --git a/src/kernel/shapes/difference_2d.rs b/src/kernel/shapes/difference_2d.rs index 81233fc0c0..ae2ee5bf14 100644 --- a/src/kernel/shapes/difference_2d.rs +++ b/src/kernel/shapes/difference_2d.rs @@ -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 diff --git a/src/kernel/shapes/sketch.rs b/src/kernel/shapes/sketch.rs index 915b4d2c2f..b92d74bfb2 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 d9834a208e..9ba88eb63a 100644 --- a/src/kernel/shapes/sweep.rs +++ b/src/kernel/shapes/sweep.rs @@ -12,6 +12,7 @@ impl ToShape for fj::Sweep { self.shape().to_shape(tolerance, debug_info), Vector::from([0., 0., self.length()]), tolerance, + self.color(), ) } diff --git a/src/kernel/shapes/union.rs b/src/kernel/shapes/union.rs index a7066b06f1..c3bf570402 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 f3e451cae5..8d5814dfba 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),