Created
August 31, 2023 09:12
-
-
Save guiyomh/6d6f812023c15109f0c004480c5ae19c to your computer and use it in GitHub Desktop.
RUST features for Cartesian mathematics, vectors, points and physical operations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Module for handling basic Cartesian geometry operations. | |
use std::ops::{Add, Mul, Sub}; | |
/// Represents a point in Cartesian coordinates. | |
#[derive(Debug, Copy, Clone, PartialEq)] | |
pub struct Point { | |
pub x: f64, | |
pub y: f64, | |
} | |
impl Point { | |
/// Creates a new point with the specified coordinates. | |
pub fn new(x: f64, y: f64) -> Self { | |
Point { x, y } | |
} | |
/// Calculates the distance to another point. | |
pub fn distance_to(&self, point: Point) -> f64 { | |
(point.x - self.x).hypot(point.y - self.y) | |
} | |
} | |
impl Sub<Point> for Point { | |
type Output = Vector; | |
fn sub(self, other: Point) -> Vector { | |
Vector::new(self.x - other.x, self.y - other.y) | |
} | |
} | |
/// Represents a vector in Cartesian coordinates. | |
#[derive(Debug, Copy, Clone)] | |
pub struct Vector { | |
pub dx: f64, | |
pub dy: f64, | |
} | |
impl Add<Vector> for Point { | |
type Output = Point; | |
fn add(self, vector: Vector) -> Point { | |
Point::new(self.x + vector.dx, self.y + vector.dy) | |
} | |
} | |
impl Sub<Vector> for Point { | |
type Output = Point; | |
fn sub(self, vector: Vector) -> Point { | |
Point::new(self.x - vector.dx, self.y - vector.dy) | |
} | |
} | |
impl Vector { | |
/// Creates a new vector with the specified components. | |
pub fn new(dx: f64, dy: f64) -> Self { | |
Vector { dx, dy } | |
} | |
/// Calculates the length of the vector. | |
pub fn length(&self) -> f64 { | |
(self.dx * self.dx + self.dy * self.dy).sqrt() | |
} | |
/// Rotates the vector by the given angle in degree. | |
pub fn rotate(&self, angle_degree: f64) -> Vector { | |
let angle_radians = angle_degree.to_radians(); | |
Vector::new( | |
self.dx * angle_radians.cos() - self.dy * angle_radians.sin(), | |
self.dx * angle_radians.sin() + self.dy * angle_radians.cos(), | |
) | |
} | |
} | |
impl Add<Vector> for Vector { | |
type Output = Vector; | |
fn add(self, other: Vector) -> Vector { | |
Vector::new(self.dx + other.dx, self.dy + other.dy) | |
} | |
} | |
impl Mul<f64> for Vector { | |
type Output = Vector; | |
fn mul(self, times: f64) -> Vector { | |
Vector::new(self.dx * times, self.dy * times) | |
} | |
} | |
#[derive(Debug, Clone)] | |
pub struct Line { | |
pub points: Vec<Point>, | |
} | |
impl Line { | |
pub fn new(points: Vec<Point>) -> Self { | |
if points.len() < 2 { | |
panic!("Should have 2 points at minimum."); | |
} | |
Line { points } | |
} | |
fn compare_to(&self, point: Point) -> i32 { | |
(self.get_y_for_x(point.x) - point.y).round() as i32 | |
} | |
pub fn is_horizontal_at_x(&self, x: f64) -> bool { | |
let segment = self.get_segment_for(x); | |
segment.start().y == segment.end().y | |
} | |
fn get_segment_for(&self, x: f64) -> Segment { | |
let index = (1..self.points.len()) | |
.find(|&i| self.points[i - 1].x <= x && x <= self.points[i].x) | |
.unwrap(); | |
Segment::new(self.points[index - 1], self.points[index]) | |
} | |
pub fn get_y_for_x(&self, x: f64) -> f64 { | |
let segment = self.get_segment_for(x); | |
let dx = segment.end().x - segment.start().x; | |
let dy = segment.end().y - segment.start().y; | |
segment.start().y + (x - segment.start().x) * (dy / dx) | |
} | |
pub fn landing_zone(&self) -> (Point, Point) { | |
let index = self | |
.points | |
.windows(2) | |
.position(|pair| pair[0].y == pair[1].y) | |
.expect("No landing zone found"); | |
(self.points[index], self.points[index + 1]) | |
} | |
} | |
impl PartialOrd<Point> for Line { | |
fn partial_cmp(&self, other: &Point) -> Option<std::cmp::Ordering> { | |
Some(self.compare_to(*other).cmp(&0)) | |
} | |
} | |
impl PartialEq<Point> for Line { | |
fn eq(&self, other: &Point) -> bool { | |
self.compare_to(*other) == 0 | |
} | |
} | |
#[derive(Debug, Clone, Copy)] | |
struct Segment { | |
start: Point, | |
end: Point, | |
} | |
impl Segment { | |
fn new(start: Point, end: Point) -> Self { | |
Segment { start, end } | |
} | |
fn start(&self) -> Point { | |
self.start | |
} | |
fn end(&self) -> Point { | |
self.end | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
use approx::assert_abs_diff_eq; | |
#[test] | |
fn test_point_distance() { | |
let point1 = Point::new(0.0, 0.0); | |
let point2 = Point::new(3.0, 4.0); | |
assert_eq!(point1.distance_to(point2), 5.0); | |
} | |
#[test] | |
fn test_vector_length() { | |
let vector = Vector::new(3.0, 4.0); | |
assert_eq!(vector.length(), 5.0); | |
} | |
#[test] | |
fn test_vector_rotation() { | |
let vector = Vector::new(1.0, 0.0); | |
let rotated_vector = vector.rotate(180.0); | |
assert_abs_diff_eq!(rotated_vector.dx, -1.0, epsilon = 1e-10); | |
assert_abs_diff_eq!(rotated_vector.dy, 0.0, epsilon = 1e-10); | |
let rotated_vector = vector.rotate(90.0); | |
assert_abs_diff_eq!(rotated_vector.dx, 0.0, epsilon = 1e-10); | |
assert_abs_diff_eq!(rotated_vector.dy, 1.0, epsilon = 1e-10); | |
} | |
#[test] | |
fn test_line_landing_zone() { | |
// Define points for a landing zone | |
let points = vec![ | |
Point::new(0.0, 1.0), | |
Point::new(1.0, 2.0), | |
Point::new(2.0, 1.0), | |
Point::new(4.0, 1.0), | |
Point::new(5.0, 3.0), | |
]; | |
let line = Line::new(points); | |
// Calculate the expected landing zone points | |
let expected_start = Point::new(2.0, 1.0); | |
let expected_end = Point::new(4.0, 1.0); | |
// Get the landing zone points from the Line | |
let (start, end) = line.landing_zone(); | |
// Compare the calculated points with the expected points | |
assert_eq!(start, expected_start); | |
assert_eq!(end, expected_end); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use crate::cartesian::{Point, Vector}; | |
use std::ops::{Add, Mul}; | |
use std::{fmt, fmt::Display}; | |
/// Represents time in seconds. | |
#[derive(Debug, Copy, Clone)] | |
pub struct Time { | |
sec: f64, | |
} | |
impl Time { | |
/// Creates a new Time instance with the specified seconds. | |
pub fn new(sec: f64) -> Self { | |
Time { sec } | |
} | |
} | |
impl From<f64> for Time { | |
fn from(seconds: f64) -> Self { | |
Time::new(seconds) | |
} | |
} | |
/// Represents a double-precision floating-point value. | |
#[derive(Debug, Copy, Clone)] | |
pub struct Double { | |
value: f64, | |
} | |
impl Double { | |
pub fn new(value: f64) -> Self { | |
Double { value } | |
} | |
pub fn sec(&self) -> Time { | |
Time::new(self.value) | |
} | |
} | |
impl From<Double> for Time { | |
fn from(double: Double) -> Self { | |
double.sec() | |
} | |
} | |
impl fmt::Display for Double { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
write!(f, "{:.2}", self.value) | |
} | |
} | |
/// Represents speed as a vector with direction. | |
#[derive(Debug, Copy, Clone)] | |
pub struct Speed { | |
direction: Vector, | |
} | |
impl Speed { | |
/// Creates a new Speed instance with the specified direction vector. | |
pub fn new(direction: Vector) -> Self { | |
Speed { direction } | |
} | |
pub fn new_from_components(x_speed: f64, y_speed: f64) -> Self { | |
Speed::new(Vector::new(x_speed, y_speed)) | |
} | |
/// Returns the horizontal component of the speed. | |
pub fn x_speed(&self) -> f64 { | |
self.direction.dx | |
} | |
/// Returns the vertical component of the speed. | |
pub fn y_speed(&self) -> f64 { | |
self.direction.dy | |
} | |
} | |
impl Add<Speed> for Speed { | |
type Output = Speed; | |
fn add(self, other: Speed) -> Speed { | |
Speed::new(self.direction + other.direction) | |
} | |
} | |
impl Display for Speed { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
write!(f, "({:.2}, {:.2})", self.x_speed(), self.y_speed()) | |
} | |
} | |
#[derive(Debug, Copy, Clone)] | |
pub struct Acceleration { | |
pub vector: Vector, | |
} | |
impl Acceleration { | |
pub fn new(vector: Vector) -> Self { | |
Acceleration { vector } | |
} | |
} | |
impl Mul<Time> for Acceleration { | |
type Output = Speed; | |
fn mul(self, time: Time) -> Speed { | |
Speed::new(self.vector * time.sec) | |
} | |
} | |
impl Add<Acceleration> for Acceleration { | |
type Output = Acceleration; | |
fn add(self, other: Acceleration) -> Acceleration { | |
Acceleration::new(self.vector + other.vector) | |
} | |
} | |
#[derive(Debug, Copy, Clone)] | |
pub struct Particule { | |
pub position: Point, | |
pub speed: Speed, | |
} | |
impl Particule { | |
pub fn new(position: Point, speed: Speed) -> Self { | |
Particule { position, speed } | |
} | |
pub fn accelerate(&self, acceleration: Acceleration, time: Time) -> Particule { | |
let new_speed = self.speed + acceleration * time; | |
let new_position = self.position | |
+ self.speed.direction * time.sec | |
+ acceleration.vector * time.sec * time.sec * 0.5; | |
Particule::new(new_position, new_speed) | |
} | |
} | |
impl fmt::Display for Particule { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
write!( | |
f, | |
"x={:.2} y={:.2} speed= {}", | |
self.position.x, self.position.y, self.speed | |
) | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
#[test] | |
fn test_speed_creation() { | |
let speed = Speed::new_from_components(3.0, 4.0); | |
assert_eq!(speed.x_speed(), 3.0); | |
assert_eq!(speed.y_speed(), 4.0); | |
} | |
#[test] | |
fn test_speed_addition() { | |
let speed1 = Speed::new_from_components(1.0, 2.0); | |
let speed2 = Speed::new_from_components(3.0, 4.0); | |
let sum_speed = speed1 + speed2; | |
assert_eq!(sum_speed.x_speed(), 4.0); | |
assert_eq!(sum_speed.y_speed(), 6.0); | |
} | |
#[test] | |
fn test_acceleration_multiplication() { | |
let acceleration = Acceleration::new(Vector::new(2.0, 3.0)); | |
let time = Time::new(2.0); | |
let speed = acceleration * time; | |
assert_eq!(speed.x_speed(), 4.0); | |
assert_eq!(speed.y_speed(), 6.0); | |
} | |
#[test] | |
fn test_acceleration_addition() { | |
let acceleration1 = Acceleration::new(Vector::new(1.0, 2.0)); | |
let acceleration2 = Acceleration::new(Vector::new(3.0, 4.0)); | |
let sum_acceleration = acceleration1 + acceleration2; | |
assert_eq!(sum_acceleration.vector.dx, 4.0); | |
assert_eq!(sum_acceleration.vector.dy, 6.0); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment