Skip to content

Instantly share code, notes, and snippets.

@guiyomh
Created August 31, 2023 09:12
Show Gist options
  • Save guiyomh/6d6f812023c15109f0c004480c5ae19c to your computer and use it in GitHub Desktop.
Save guiyomh/6d6f812023c15109f0c004480c5ae19c to your computer and use it in GitHub Desktop.
RUST features for Cartesian mathematics, vectors, points and physical operations
// 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);
}
}
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