initial commit

This commit is contained in:
missing 2022-05-25 21:15:09 -05:00
commit a0a0a04176
6 changed files with 542 additions and 0 deletions

18
.gitignore vendored Normal file
View file

@ -0,0 +1,18 @@
# :)
.DS_Store
# ---> Rust
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

8
Cargo.toml Normal file
View file

@ -0,0 +1,8 @@
[package]
name = "vectorn"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

17
LICENSE Normal file
View file

@ -0,0 +1,17 @@
Permission is hereby granted, without written agreement and without
license or royalty fees, to use, copy, modify, and distribute this
software and its documentation for any purpose, provided that the
above copyright notice and the following two paragraphs appear in
all copies of this software.
IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

13
README.md Normal file
View file

@ -0,0 +1,13 @@
Types representing directions and locations in 2d and 3d space.
NOTE: `Vec3`, `Vec3Int`, and `Direction3` don't exist yet. Coming soon!
This crate contains 6 major types:
- `Vec2`, a 2d float vector
- `Vec2Int`, a 2d integer vector
- `Direction2`, a 2d cardinal direction
- `Vec3`, a 3d float vector (TODO)
- `Vec3Int`, a 3d integer vector (TODO)
- `Direction3`, a 3d cardinal direction (TODO)
All the types implement the expected `From`s and all the relevant operator traits.

473
src/lib.rs Normal file
View file

@ -0,0 +1,473 @@
//! Types representing directions and locations in 2d and 3d space.
//!
//! NOTE: `Vec3`, `Vec3Int`, and `Direction3` don't exist yet. Coming soon!
//!
//! This crate contains 6 major types:
//! - [`Vec2`], a 2d float vector
//! - [`Vec2Int`], a 2d integer vector
//! - [`Direction2`], a 2d cardinal direction
//! - [`Vec3`], a 3d float vector (TODO)
//! - [`Vec3Int`], a 3d integer vector (TODO)
//! - [`Direction3`], a 3d cardinal direction (TODO)
//!
//! All the types implement the expected [`From`]s and all the relevant operator traits.
#![warn(clippy::pedantic)]
#![warn(missing_docs)]
#![allow(clippy::must_use_candidate)]
#[cfg(test)]
mod test;
use std::{ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, fmt::Debug};
// Direction
/// A cardinal direction in a 2d plane.
///
/// Conversions to a [`Vec2`] or [`Vec2Int`] assume that East is positive-x and South is positive-y.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Direction2 {
/// North, or Vec2::from((0, -1))
North,
/// North, or Vec2::from((0, 1))
South,
/// North, or Vec2::from((1, 0))
East,
/// North, or Vec2::from((-1, 0))
West,
}
#[allow(clippy::enum_glob_use)]
impl Direction2 {
/// Flips this `Direction` around both the x- and y-axes.
pub fn flipped(self) -> Self {
self.flip_x().flip_y()
}
/// Flips this `Direction` around the x-axis.
pub fn flip_x(self) -> Self {
use Direction2::*;
match self {
East => West,
West => East,
v => v,
}
}
/// Flips this `Direction` around the y-axis.
pub fn flip_y(self) -> Self {
use Direction2::*;
match self {
North => South,
South => North,
v => v,
}
}
}
// ...and related op impls
impl Neg for Direction2 {
type Output = Self;
fn neg(self) -> Self::Output {
self.flipped()
}
}
#[allow(clippy::enum_glob_use)]
impl From<Direction2> for Vec2 {
fn from(v: Direction2) -> Self {
use Direction2::*;
match v {
North => (0.0, -1.0).into(),
South => (0.0, 1.0).into(),
East => (1.0, 0.0).into(),
West => (-1.0, 0.0).into(),
}
}
}
#[allow(clippy::enum_glob_use)]
impl From<Direction2> for Vec2Int {
fn from(v: Direction2) -> Self {
use Direction2::*;
match v {
North => (0, -1).into(),
South => (0, 1).into(),
East => (1, 0).into(),
West => (-1, 0).into(),
}
}
}
impl Mul<f32> for Direction2 {
type Output = Vec2;
fn mul(self, rhs: f32) -> Self::Output {
Vec2::from(self) * rhs
}
}
impl Mul<i32> for Direction2 {
type Output = Vec2Int;
fn mul(self, rhs: i32) -> Self::Output {
Vec2Int::from(self) * rhs
}
}
// Vec2
/// A set of 2 [`f32`]s representing a location or direction in the 2d plane.
#[derive(Clone, Copy, Default, PartialEq)]
pub struct Vec2 {
/// The x component of the vector.
pub x: f32,
/// The y component of the vector.
pub y: f32,
}
impl Debug for Vec2 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Vec2").field(&self.x).field(&self.y).finish()
}
}
impl Vec2 {
/// Creates a new `Vec2` with the given x- and y-values.
///
/// It is often simpler, and preferred, to just write `(x, y).into()`.
pub const fn new(x: f32, y: f32) -> Vec2 {
Self { x, y }
}
/// Gets the squared magnitude of the vector.
///
/// Useful for comparisons as it is faster to calculate than `magnitude`.
pub fn sq_magnitude(self) -> f32 {
self.x * self.x + self.y * self.y
}
/// Gets the magnitude of the vector.
pub fn magnitude(self) -> f32 {
self.sq_magnitude().sqrt()
}
/// Gets the squared distance from this vector to `rhs`.
///
/// Useful for comparisons as it is faster to calculate than `dist`.
pub fn sq_dist(self, rhs: Self) -> f32 {
(self - rhs).sq_magnitude()
}
/// Gets the distance from this vector to `rhs`.
pub fn dist(self, rhs: Self) -> f32 {
(self - rhs).magnitude()
}
/// Normalizes the vector, making its magnitude `1`.
pub fn normalized(self) -> Self {
self / self.magnitude()
}
/// Rounds the vector to a [`Vec2Int`].
///
/// This uses `as i32` under the hood, and as such comes with all the same unfortunate edge cases. Beware.
pub fn rounded(self) -> Vec2Int {
#[allow(clippy::cast_possible_truncation)]
Vec2Int {
x: self.x as i32,
y: self.y as i32,
}
}
}
impl From<(i32, i32)> for Vec2 {
fn from(v: (i32, i32)) -> Self {
Vec2Int::from(v).to_f32()
}
}
impl From<(f32, f32)> for Vec2 {
fn from(v: (f32, f32)) -> Self {
Self { x: v.0, y: v.1 }
}
}
impl From<Vec2> for (f32, f32) {
fn from(v: Vec2) -> Self {
(v.x, v.y)
}
}
impl PartialEq<(i32, i32)> for Vec2 {
fn eq(&self, other: &(i32, i32)) -> bool {
self == &Self::from(*other)
}
}
impl PartialEq<(f32, f32)> for Vec2 {
fn eq(&self, other: &(f32, f32)) -> bool {
self == &Self::from(*other)
}
}
// ...and related op impls
impl Neg for Vec2 {
type Output = Self;
fn neg(self) -> Self::Output {
self * -1.0
}
}
impl Add for Vec2 {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
impl Add<Direction2> for Vec2 {
type Output = Self;
fn add(self, rhs: Direction2) -> Self::Output {
self + Self::from(rhs)
}
}
impl<T> AddAssign<T> for Vec2
where
Vec2: Add<T, Output = Self>,
{
fn add_assign(&mut self, rhs: T) {
*self = *self + rhs;
}
}
impl<T> Sub<T> for Vec2
where
Vec2: Add<T, Output = Self>,
{
type Output = Self;
fn sub(self, rhs: T) -> Self::Output {
-(-self + rhs)
}
}
impl<T> SubAssign<T> for Vec2
where
Vec2: Sub<T, Output = Self>,
{
fn sub_assign(&mut self, rhs: T) {
*self = *self - rhs;
}
}
impl Mul<f32> for Vec2 {
type Output = Self;
fn mul(self, rhs: f32) -> Self::Output {
Self {
x: self.x * rhs,
y: self.y * rhs,
}
}
}
impl Div<f32> for Vec2 {
type Output = Self;
fn div(self, rhs: f32) -> Self::Output {
Self {
x: self.x / rhs,
y: self.y / rhs,
}
}
}
impl MulAssign<f32> for Vec2 {
fn mul_assign(&mut self, rhs: f32) {
*self = *self * rhs;
}
}
impl DivAssign<f32> for Vec2 {
fn div_assign(&mut self, rhs: f32) {
*self = *self / rhs;
}
}
// Vec2Int
/// A set of 2 [`i32`]s representing a location or direction in the 2d plane.
#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Vec2Int {
/// The x component of the vector.
pub x: i32,
/// The y component of the vector.
pub y: i32,
}
impl Debug for Vec2Int {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Vec2Int").field(&self.x).field(&self.y).finish()
}
}
impl Vec2Int {
/// Creates a new `Vec2` with the given x- and y-values.
///
/// It is often simpler, and preferred, to just write `(x, y).into()`.
pub const fn new(x: i32, y: i32) -> Vec2Int {
Self { x, y }
}
/// Gets the squared magnitude of the vector.
///
/// Useful for comparisons as it is faster to calculate than `magnitude`.
pub fn sq_magnitude(self) -> i32 {
self.x * self.x + self.y * self.y
}
/// Gets the magnitude of the vector.
pub fn magnitude(self) -> f32 {
#[allow(clippy::cast_precision_loss)]
(self.sq_magnitude() as f32).sqrt()
}
/// Gets the squared distance from this vector to `rhs`.
///
/// Useful for comparisons as it is faster to calculate than `dist`.
pub fn sq_dist(self, rhs: Self) -> i32 {
(self - rhs).sq_magnitude()
}
/// Gets the distance from this vector to `rhs`.
pub fn dist(self, rhs: Self) -> f32 {
(self - rhs).magnitude()
}
/// Casts this vector to a [`Vec2`].
///
/// This uses `as f32` under the hood, and as such comes with all the same unfortunate edge cases. Beware.
pub fn to_f32(self) -> Vec2 {
#[allow(clippy::cast_precision_loss)]
Vec2 {
x: self.x as f32,
y: self.y as f32,
}
}
}
impl From<(i32, i32)> for Vec2Int {
fn from(v: (i32, i32)) -> Self {
Self { x: v.0, y: v.1 }
}
}
impl From<Vec2Int> for (i32, i32) {
fn from(v: Vec2Int) -> Self {
(v.x, v.y)
}
}
impl PartialEq<(i32, i32)> for Vec2Int {
fn eq(&self, other: &(i32, i32)) -> bool {
self == &Self::from(*other)
}
}
// ...and related op impls
impl Neg for Vec2Int {
type Output = Self;
fn neg(self) -> Self::Output {
self * -1
}
}
impl Add for Vec2Int {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
impl Add<Direction2> for Vec2Int {
type Output = Self;
fn add(self, rhs: Direction2) -> Self::Output {
self + Self::from(rhs)
}
}
impl<T> AddAssign<T> for Vec2Int
where
Vec2Int: Add<T, Output = Self>,
{
fn add_assign(&mut self, rhs: T) {
*self = *self + rhs;
}
}
impl<T> Sub<T> for Vec2Int
where
Vec2Int: Add<T, Output = Self>,
{
type Output = Self;
fn sub(self, rhs: T) -> Self::Output {
-(-self + rhs)
}
}
impl<T> SubAssign<T> for Vec2Int
where
Vec2Int: Sub<T, Output = Self>,
{
fn sub_assign(&mut self, rhs: T) {
*self = *self - rhs;
}
}
impl Mul<i32> for Vec2Int {
type Output = Self;
fn mul(self, rhs: i32) -> Self::Output {
Self {
x: self.x * rhs,
y: self.y * rhs,
}
}
}
impl Div<i32> for Vec2Int {
type Output = Self;
fn div(self, rhs: i32) -> Self::Output {
Self {
x: self.x / rhs,
y: self.y / rhs,
}
}
}
impl MulAssign<i32> for Vec2Int {
fn mul_assign(&mut self, rhs: i32) {
*self = *self * rhs;
}
}
impl DivAssign<i32> for Vec2Int {
fn div_assign(&mut self, rhs: i32) {
*self = *self / rhs;
}
}

13
src/test.rs Normal file
View file

@ -0,0 +1,13 @@
use super::*;
#[test]
fn test() {
let vec1: Vec2 = (3, 5).into();
let vec2: Vec2 = (7, 2).into();
assert_eq!(vec1, (3, 5));
assert_eq!(vec2, (7, 2));
assert_eq!(vec1 + vec2, (10, 7));
assert_eq!(vec1 - vec2, (-4, 3));
assert_eq!(vec1 * 4.0, (12, 20));
}