From d8fdc142e69db2feaf68e94dc5ea47a47d3c7536 Mon Sep 17 00:00:00 2001 From: Nicolas <> Date: Sat, 9 Apr 2022 19:29:30 +0200 Subject: [PATCH] Add rhombus rotation and stripes --- src/animation/mod.rs | 2 ++ src/animation/rhombus.rs | 51 +++++++++++++++++++++++++++++++++++++++ src/animation/rotation.rs | 46 +++++++++++++++++++++++++++++++++++ src/fill/mod.rs | 1 + src/fill/stripes.rs | 35 +++++++++++++++++++++++++++ src/main.rs | 17 ++++++++++--- src/vec.rs | 12 +++++++++ 7 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 src/animation/rhombus.rs create mode 100644 src/animation/rotation.rs create mode 100644 src/fill/stripes.rs diff --git a/src/animation/mod.rs b/src/animation/mod.rs index 74e01d4..f8aedba 100644 --- a/src/animation/mod.rs +++ b/src/animation/mod.rs @@ -1,4 +1,6 @@ pub mod circle; +pub mod rotation; +pub mod rhombus; use crate::vec::Vector; diff --git a/src/animation/rhombus.rs b/src/animation/rhombus.rs new file mode 100644 index 0000000..4ac73e7 --- /dev/null +++ b/src/animation/rhombus.rs @@ -0,0 +1,51 @@ +use crate::animation::Animation; +use crate::vec::Vector; + +const THICKNESS: f32 = 0.2; +const FINAL_DISTANCE: f32 = 1.0 + THICKNESS * 2.0; + +pub struct RhombusAnimation { + center: Vector, + thickness: f32, + final_distance: f32, +} + +impl RhombusAnimation { + pub fn new(size: Vector) -> Self { + let center = size.center(); + let distance = center.sum(); + + Self { + center, + thickness: distance * THICKNESS, + final_distance: distance * FINAL_DISTANCE, + } + } +} + +impl Animation for RhombusAnimation { + fn sample(&self, step: f32, pos: Vector) -> f32 { + let dist = self.final_distance * step - self.thickness; + let pos_dist = (self.center - pos).abs().sum(); + + (pos_dist - dist) / self.thickness + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn sample() { + let anim = RhombusAnimation::new(Vector::new(30.0, 10.0)); + + let sample_1 = anim.sample(0.2, Vector::new(5.0, 16.0)); + let sample_2 = anim.sample(0.7, Vector::new(22.0, 2.0)); + let sample_3 = anim.sample(0.5, Vector::new(4.0, 7.0)); + + assert!(4.8 < sample_1 && sample_1 < 4.9); + assert!(-1.5 < sample_2 && sample_2 < -1.4); + assert!(0.7 < sample_3 && sample_3 < 0.8); + } +} \ No newline at end of file diff --git a/src/animation/rotation.rs b/src/animation/rotation.rs new file mode 100644 index 0000000..db64ddc --- /dev/null +++ b/src/animation/rotation.rs @@ -0,0 +1,46 @@ +use std::f32::consts::PI; +use crate::animation::Animation; +use crate::vec::Vector; + +const TWO_PI: f32 = PI * 2.0; +const THICKNESS: f32 = TWO_PI * 0.1; +const FULL_ROTATION: f32 = TWO_PI + THICKNESS * 2.0; + +pub struct RotationAnimation { + center: Vector +} + +impl RotationAnimation { + pub fn new(size: Vector) -> Self { + Self { + center: size.center() + } + } +} + +impl Animation for RotationAnimation { + fn sample(&self, step: f32, pos: Vector) -> f32 { + let angle = FULL_ROTATION * step - PI - THICKNESS; + let pos_angle = (pos - self.center).angle(); + + (pos_angle - angle) / THICKNESS + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn sample() { + let anim = RotationAnimation::new(Vector::new(30.0, 10.0)); + + let sample_1 = anim.sample(0.2, Vector::new(5.0, 16.0)); + let sample_2 = anim.sample(0.7, Vector::new(22.0, 2.0)); + let sample_3 = anim.sample(0.5, Vector::new(4.0, 7.0)); + + assert!(2.4 < sample_1 && sample_1 < 2.5); + assert!(0.7 < sample_2 && sample_2 < 0.8); + assert!(-2.3 < sample_3 && sample_3 < -2.2); + } +} \ No newline at end of file diff --git a/src/fill/mod.rs b/src/fill/mod.rs index 6d12c07..d4f73b6 100644 --- a/src/fill/mod.rs +++ b/src/fill/mod.rs @@ -1,5 +1,6 @@ pub mod level; pub mod circle; +pub mod stripes; use crate::vec::Vector; diff --git a/src/fill/stripes.rs b/src/fill/stripes.rs new file mode 100644 index 0000000..1f2f090 --- /dev/null +++ b/src/fill/stripes.rs @@ -0,0 +1,35 @@ +use crate::FillMode; +use crate::vec::Vector; + +const INTERVAL: f32 = 4.0; + +pub struct StripesFillMode { + interval: f32 +} + +impl StripesFillMode { + pub fn new(size: Vector) -> Self { + Self { + interval: size.smaller() / INTERVAL + } + } +} + +impl FillMode for StripesFillMode { + fn sample(&self, _: f32, pos: Vector) -> f32 { + (pos.sum() % self.interval) / self.interval + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn sample() { + let mode = StripesFillMode::new(Vector::new(8.0, 4.0)); + + assert_eq!(0.25, mode.sample(0.0, Vector::new(1.5, 0.75))); + assert_eq!(0.5, mode.sample(0.0, Vector::new(4.0, 2.5))); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index d7b4eaa..7b2e488 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,12 +6,15 @@ use clap::ArgEnum; use rand::rngs::OsRng; use crate::animation::Animation; use crate::animation::circle::CircleAnimation; +use crate::animation::rhombus::RhombusAnimation; +use crate::animation::rotation::RotationAnimation; use crate::char::SimpleCharSampler; use crate::choose::{Chooser, Options}; use crate::color::{ColorSampler, SimpleColorSampler}; use crate::fill::circle::CircleFillMode; use crate::fill::FillMode; use crate::fill::level::LevelFillMode; +use crate::fill::stripes::StripesFillMode; use crate::render::{Renderer, SamplerRenderer}; use crate::runner::Runner; use crate::sampler::ComposedSampler; @@ -34,14 +37,16 @@ mod choose; #[derive(Copy, Clone, ArgEnum)] enum AnimationType { - Circle + Circle, + Rhombus, + Rotation } impl Options for AnimationType { fn all() -> Vec where Self: Sized { use AnimationType::*; - vec![Circle] + vec![Circle, Rhombus, Rotation] } } @@ -68,7 +73,8 @@ impl Options for ColorType { #[derive(Copy, Clone, ArgEnum)] enum FillModeType { Circle, - Level + Level, + Stripes } impl Options for FillModeType { @@ -128,7 +134,9 @@ fn main() -> Result<(), Error> { fn create_animation(animation: AnimationType, size: Vector) -> Box { match animation { - AnimationType::Circle => Box::new(CircleAnimation::new(size)) + AnimationType::Circle => Box::new(CircleAnimation::new(size)), + AnimationType::Rhombus => Box::new(RhombusAnimation::new(size)), + AnimationType::Rotation => Box::new(RotationAnimation::new(size)), } } @@ -136,6 +144,7 @@ fn create_fill(fill: FillModeType, size: Vector) -> Box { match fill { FillModeType::Circle => Box::new(CircleFillMode::new(size)), FillModeType::Level => Box::new(LevelFillMode::new()), + FillModeType::Stripes => Box::new(StripesFillMode::new(size)) } } diff --git a/src/vec.rs b/src/vec.rs index b5f1d8e..d4a317a 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -20,10 +20,22 @@ impl Vector { (self.x * self.x + self.y * self.y).sqrt() } + pub fn angle(self) -> f32 { + self.x.atan2(self.y) + } + pub fn smaller(self) -> f32 { self.x.min(self.y) } + pub fn abs(self) -> Vector { + Self::new(self.x.abs(), self.y.abs()) + } + + pub fn sum(self) -> f32 { + self.x + self.y + } + /// Creates a vector with the on screen coordinates based on the terminal coordinates. /// # Arguments /// * `x`: The x axis of the terminal character.