From 2bdd88a7e8537d168ebd6f5803a20469dabf14c8 Mon Sep 17 00:00:00 2001 From: Rico Riedel Date: Fri, 29 Jul 2022 16:50:27 +0200 Subject: [PATCH] Add patterns --- src/pattern/circle.rs | 58 +++++++++++++++++++++++++++++++++++++++++ src/pattern/line.rs | 57 ++++++++++++++++++++++++++++++++++++++++ src/pattern/mod.rs | 22 ++++++++++++++++ src/pattern/rhombus.rs | 59 ++++++++++++++++++++++++++++++++++++++++++ src/pattern/wheel.rs | 58 +++++++++++++++++++++++++++++++++++++++++ src/vec.rs | 18 +++++++++++++ 6 files changed, 272 insertions(+) create mode 100644 src/pattern/circle.rs create mode 100644 src/pattern/line.rs create mode 100644 src/pattern/mod.rs create mode 100644 src/pattern/rhombus.rs create mode 100644 src/pattern/wheel.rs diff --git a/src/pattern/circle.rs b/src/pattern/circle.rs new file mode 100644 index 0000000..9c250c5 --- /dev/null +++ b/src/pattern/circle.rs @@ -0,0 +1,58 @@ +use crate::pattern::{Config, Pattern, PatternFactory}; +use crate::vec::Vector; + +#[derive(Default)] +pub struct CircleFactory; +pub struct Circle { + center: Vector, + radius: f32, +} + +impl PatternFactory for CircleFactory { + fn name(&self) -> String { + stringify!(Circle).to_lowercase() + } + + fn create(&self, config: &Config) -> Box { + Box::new(Circle::new(config)) + } +} + +impl Circle { + pub fn new(config: &Config) -> Self { + let center = config.size.center(); + let radius = center.len(); + + Self { center, radius } + } +} + +impl Pattern for Circle { + fn sample(&self, pos: Vector) -> f32 { + (pos - self.center).len() / self.radius + } +} + +#[cfg(test)] +mod test { + use super::*; + use approx::*; + + #[test] + fn name() { + assert_eq!("circle", CircleFactory::default().name()); + } + + #[test] + fn sample() { + let config = Config { + size: Vector::new(10.0, 20.0), + step: 0.0, + }; + let pattern = Circle::new(&config); + + assert_abs_diff_eq!(1.0, pattern.sample(Vector::new(0.0, 0.0)), epsilon = 0.1); + assert_abs_diff_eq!(0.0, pattern.sample(Vector::new(5.0, 10.0)), epsilon = 0.1); + assert_abs_diff_eq!(0.5, pattern.sample(Vector::new(7.5, 15.0)), epsilon = 0.1); + } +} diff --git a/src/pattern/line.rs b/src/pattern/line.rs new file mode 100644 index 0000000..5254625 --- /dev/null +++ b/src/pattern/line.rs @@ -0,0 +1,57 @@ +use crate::pattern::{Config, Pattern, PatternFactory}; +use crate::vec::Vector; + +#[derive(Default)] +pub struct LineFactory; +pub struct Line { + width: f32, +} + +impl PatternFactory for LineFactory { + fn name(&self) -> String { + stringify!(Line).to_lowercase() + } + + fn create(&self, config: &Config) -> Box { + Box::new(Line::new(config)) + } +} + +impl Line { + pub fn new(config: &Config) -> Self { + let width = config.size.x; + + Self { width } + } +} + +impl Pattern for Line { + fn sample(&self, pos: Vector) -> f32 { + pos.x / self.width + } +} + +#[cfg(test)] +mod test { + use super::*; + use approx::*; + + #[test] + fn name() { + assert_eq!("line", LineFactory::default().name()); + } + + #[test] + fn sample() { + let config = Config { + size: Vector::new(20.0, 0.0), + step: 0.0, + }; + let pattern = Line::new(&config); + + assert_abs_diff_eq!(0.0, pattern.sample(Vector::new(0.0, 4.0)), epsilon = 0.1); + assert_abs_diff_eq!(0.4, pattern.sample(Vector::new(8.0, 8.0)), epsilon = 0.1); + assert_abs_diff_eq!(0.8, pattern.sample(Vector::new(16.0, 7.0)), epsilon = 0.1); + assert_abs_diff_eq!(1.0, pattern.sample(Vector::new(20.0, 3.0)), epsilon = 0.1); + } +} diff --git a/src/pattern/mod.rs b/src/pattern/mod.rs new file mode 100644 index 0000000..b7886aa --- /dev/null +++ b/src/pattern/mod.rs @@ -0,0 +1,22 @@ +mod circle; +mod line; +mod rhombus; +mod wheel; + +use crate::vec::Vector; + +#[derive(Copy, Clone)] +pub struct Config { + pub size: Vector, + pub step: f32, +} + +pub trait PatternFactory { + fn name(&self) -> String; + + fn create(&self, config: &Config) -> Box; +} + +pub trait Pattern { + fn sample(&self, pos: Vector) -> f32; +} diff --git a/src/pattern/rhombus.rs b/src/pattern/rhombus.rs new file mode 100644 index 0000000..c176ffd --- /dev/null +++ b/src/pattern/rhombus.rs @@ -0,0 +1,59 @@ +use crate::pattern::{Config, Pattern, PatternFactory}; +use crate::vec::Vector; + +#[derive(Default)] +pub struct RhombusFactory; +pub struct Rhombus { + center: Vector, + distance: f32, +} + +impl PatternFactory for RhombusFactory { + fn name(&self) -> String { + stringify!(Rhombus).to_lowercase() + } + + fn create(&self, config: &Config) -> Box { + Box::new(Rhombus::new(config)) + } +} + +impl Rhombus { + pub fn new(config: &Config) -> Rhombus { + let center = config.size.center(); + let distance = center.sum(); + + Self { center, distance } + } +} + +impl Pattern for Rhombus { + fn sample(&self, pos: Vector) -> f32 { + (pos - self.center).abs().sum() / self.distance + } +} + +#[cfg(test)] +mod test { + use super::*; + use approx::*; + + #[test] + fn name() { + assert_eq!("rhombus", RhombusFactory::default().name()); + } + + #[test] + fn sample() { + let config = Config { + size: Vector::new(10.0, 5.0), + step: 0.0, + }; + let pattern = Rhombus::new(&config); + + assert_abs_diff_eq!(1.0, pattern.sample(Vector::new(0.0, 0.0)), epsilon = 0.1); + assert_abs_diff_eq!(1.0, pattern.sample(Vector::new(10.0, 5.0)), epsilon = 0.1); + assert_abs_diff_eq!(0.0, pattern.sample(Vector::new(5.0, 2.5)), epsilon = 0.1); + assert_abs_diff_eq!(0.5, pattern.sample(Vector::new(7.0, 0.5)), epsilon = 0.1); + } +} diff --git a/src/pattern/wheel.rs b/src/pattern/wheel.rs new file mode 100644 index 0000000..a4463f5 --- /dev/null +++ b/src/pattern/wheel.rs @@ -0,0 +1,58 @@ +use crate::pattern::{Config, Pattern, PatternFactory}; +use crate::vec::Vector; +use std::f32::consts::PI; + +#[derive(Default)] +pub struct WheelFactory; +pub struct Wheel { + center: Vector, +} + +impl PatternFactory for WheelFactory { + fn name(&self) -> String { + stringify!(Wheel).to_lowercase() + } + + fn create(&self, config: &Config) -> Box { + Box::new(Wheel::new(config)) + } +} + +impl Wheel { + pub fn new(config: &Config) -> Self { + let center = config.size.center(); + + Self { center } + } +} + +impl Pattern for Wheel { + fn sample(&self, pos: Vector) -> f32 { + ((pos - self.center).angle() + PI) / PI / 2.0 + } +} + +#[cfg(test)] +mod test { + use super::*; + use approx::*; + + #[test] + fn name() { + assert_eq!("wheel", WheelFactory::default().name()); + } + + #[test] + fn sample() { + let config = Config { + size: Vector::new(10.0, 20.0), + step: 0.0, + }; + let pattern = Wheel::new(&config); + + assert_abs_diff_eq!(0.0, pattern.sample(Vector::new(0.0, 9.0)), epsilon = 0.1); + assert_abs_diff_eq!(1.0, pattern.sample(Vector::new(0.0, 10.0)), epsilon = 0.1); + assert_abs_diff_eq!(0.5, pattern.sample(Vector::new(10.0, 10.0)), epsilon = 0.1); + assert_abs_diff_eq!(0.75, pattern.sample(Vector::new(5.0, 20.0)), epsilon = 0.1); + } +} diff --git a/src/vec.rs b/src/vec.rs index abf8e33..f81f78f 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -15,6 +15,14 @@ impl Vector { (self.x * self.x + self.y * self.y).sqrt() } + pub fn abs(&self) -> Vector { + Self::new(self.x.abs(), self.y.abs()) + } + + pub fn sum(&self) -> f32 { + self.x + self.y + } + pub fn center(&self) -> Vector { Self::new(self.x / 2.0, self.y / 2.0) } @@ -49,6 +57,16 @@ mod test { assert_abs_diff_eq!(8.5, Vector::new(3.0, 8.0).len(), epsilon = 0.1); } + #[test] + fn abs() { + assert_eq!(Vector::new(3.0, 7.0), Vector::new(-3.0, -7.0).abs()); + } + + #[test] + fn sum() { + assert_eq!(11.0, Vector::new(3.0, 8.0).sum()); + } + #[test] fn center() { assert_eq!(Vector::new(4.0, 9.0), Vector::new(8.0, 18.0).center());