Initial commit

This commit is contained in:
Michael Hübner 2023-01-16 10:47:56 +01:00
commit 73943b299d
15 changed files with 801 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
Cargo.lock
target/

7
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,7 @@
{
"workbench.colorCustomizations": {
"activityBar.background": "#551A02",
"titleBar.activeBackground": "#772503",
"titleBar.activeForeground": "#FFF9F6"
}
}

17
Cargo.toml Normal file
View file

@ -0,0 +1,17 @@
[package]
name = "utilities"
version = "0.1.0"
authors = ["hodasemi <michaelh.95@t-online.de>"]
edition = "2021"
[dependencies]
cgmath = { version = "0.18.0", features = ["swizzle"] }
rand = "0.8.5"
backtrace = "0.3.67"
assetpath = { git = "https://gavania.de/hodasemi/vulkan_lib.git" }
anyhow = { version = "1.0.68", features = ["backtrace"] }
# networking
serde = { version = "1.0.152", features = ["derive"] }

59
src/arc_unique_vec.rs Normal file
View file

@ -0,0 +1,59 @@
use std::sync::Arc;
/// Wrapper around a `Vec` if `Rc`,
/// that ensures pointer uniqueness of its elements
#[derive(Debug, Clone, Default)]
pub struct ArcUniqueVec<T> {
data: Vec<Arc<T>>,
}
impl<T> ArcUniqueVec<T> {
/// Creates an empty `RcUniqueVec`
pub fn new() -> ArcUniqueVec<T> {
ArcUniqueVec { data: Vec::new() }
}
/// Checks for pointer collision while inserting.
/// Returns the index for the position of the inserted element.
pub fn insert(&mut self, element: Arc<T>) -> usize {
match self.data.iter().position(|t| Arc::ptr_eq(t, &element)) {
Some(index) => index,
None => {
let index = self.data.len();
self.data.push(element);
index
}
}
}
/// Checks if the given element is in the vector, then returns true,
/// otherwise false.
pub fn remove(&mut self, element: &Arc<T>) -> bool {
match self.data.iter().position(|t| Arc::ptr_eq(t, element)) {
Some(index) => {
self.data.remove(index);
true
}
None => false,
}
}
/// Checks if that element is in this `RcUniqueVec`
/// and returns its index if possible
pub fn get_index(&self, element: &Arc<T>) -> Option<usize> {
match self.data.iter().position(|t| Arc::ptr_eq(t, element)) {
Some(index) => Some(index),
None => None,
}
}
/// Clears all elements from this vector
pub fn clear(&mut self) {
self.data.clear();
}
/// Returns the reference to the internal vector
pub fn as_vec(&self) -> &Vec<Arc<T>> {
&self.data
}
}

92
src/asyncthread.rs Normal file
View file

@ -0,0 +1,92 @@
//! Asynchronous thread to check if result is returned (non-blocking)
use std::sync::{mpsc, RwLock};
use std::thread;
/// Asynchronous thread handle
pub struct AsyncThread<T: Send + Sync + 'static> {
receiver: mpsc::Receiver<T>,
result: RwLock<Option<T>>,
}
impl<T: Send + Sync + 'static> AsyncThread<T> {
/// Spawns a thread
///
/// # Arguments
///
/// `f` is a function to be executed in a separate thread
pub fn spawn<F>(f: F) -> AsyncThread<T>
where
F: FnOnce() -> T,
F: Send + 'static,
{
let (sender, receiver) = mpsc::channel();
thread::spawn(move || {
if let Err(err) = sender.send((f)()) {
panic!("sending failed! ({})", err);
}
});
AsyncThread {
receiver,
result: RwLock::new(None),
}
}
/// Checks if the thread is already returned
/// Returns the Some(result) if the thread has finished its work,
/// otherwise None
pub fn check(&self) -> Result<bool, anyhow::Error> {
let mut result = self
.result
.write()
.map_err(|_err| anyhow::Error::msg("Asyncthread: Failed getting write lock"))?;
match result.as_ref() {
Some(_) => Ok(true),
None => match self.receiver.try_recv() {
Ok(res) => {
*result = Some(res);
Ok(true)
}
Err(_) => Ok(false),
},
}
}
/// consumes the result
pub fn take(&self) -> Result<T, anyhow::Error> {
let mut result = self
.result
.write()
.map_err(|_err| anyhow::Error::msg("Asyncthread: Failed getting write lock"))?;
if result.is_some() {
// actually safe to not panic, since we just checked
let res = result.take().unwrap();
Ok(res)
} else {
Err(anyhow::Error::msg("Asyncthread: Result not present"))
}
}
}
impl<T: Send + Sync + 'static> AsyncThread<T>
where
T: Clone,
{
pub fn get(&self) -> Result<T, anyhow::Error> {
let result = self
.result
.read()
.map_err(|_err| anyhow::Error::msg("Asyncthread: Failed getting write lock"))?;
match result.as_ref() {
Some(res) => Ok(res.clone()),
None => Err(anyhow::Error::msg("Asyncthread: Result not present")),
}
}
}

83
src/closures.rs Normal file
View file

@ -0,0 +1,83 @@
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!! very unstable, use at your own risk !!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
use super::prelude::*;
use std::ops::Fn;
#[allow(non_upper_case_globals)]
pub const void: () = ();
pub struct Callback {
function: FunctionType,
}
impl Callback {
pub fn new<F, Args>(function: F) -> Callback
where
F: Fn(Args) -> (),
F: 'static,
FunctionType: FunctionTypeCreator<F, Args>,
{
Callback {
function: FunctionType::new(function),
}
}
pub fn execute<Args>(&self, arguments: Args)
where
FunctionType: FunctionTypeExecutorOneArg<Args>,
{
display_error!(self.function.execute(arguments));
}
}
pub trait FunctionTypeCreator<F: Fn(Args) -> () + 'static, Args> {
fn new(function: F) -> FunctionType;
}
pub trait FunctionTypeExecutorOneArg<Args> {
fn execute(&self, execute: Args) -> VerboseResult<()>;
}
macro_rules! build_function_type {
($struct_name: ident, $($ty: ty, $name: ident), *) => {
pub enum $struct_name {
$($name(Box<dyn Fn($ty)>)),*
}
$(
impl<F: Fn($ty) -> () + 'static> FunctionTypeCreator<F, $ty> for $struct_name {
fn new(function: F) -> FunctionType {
$struct_name::$name(Box::new(function))
}
}
impl FunctionTypeExecutorOneArg<$ty> for $struct_name {
fn execute(&self, args: $ty) -> VerboseResult<()> {
match self {
FunctionType::$name(ref function) => {
(function)(args);
Ok(())
}
_ => {
create_error!("wrong argument type for this callback");
},
}
}
}
) *
}
}
#[rustfmt::skip]
build_function_type!(
FunctionType,
String, String,
i32, I32,
u32, U32,
f32, F32,
(), Void
);

167
src/color.rs Normal file
View file

@ -0,0 +1,167 @@
use std::fmt;
use std::ops::MulAssign;
use std::str::FromStr;
use std::{convert::TryFrom, ops::Mul};
use serde::{Deserialize, Serialize};
/// `TextColor` describes the color of the text
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub enum Color {
White,
Black,
Red,
Blue,
Green,
Orange,
Yellow,
Custom(u8, u8, u8),
}
impl Into<[f32; 3]> for Color {
fn into(self) -> [f32; 3] {
match self {
Color::White => [1.0, 1.0, 1.0],
Color::Black => [0.0, 0.0, 0.0],
Color::Red => [1.0, 0.0, 0.0],
Color::Blue => [0.0, 0.0, 1.0],
Color::Green => [0.0, 1.0, 0.0],
Color::Orange => [1.0, 0.65, 0.0],
Color::Yellow => [1.0, 1.0, 0.0],
Color::Custom(r, g, b) => [r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0],
}
}
}
impl Into<[u8; 3]> for Color {
fn into(self) -> [u8; 3] {
match self {
Color::White => [255, 255, 255],
Color::Black => [0, 0, 0],
Color::Red => [255, 0, 0],
Color::Blue => [0, 0, 255],
Color::Green => [0, 255, 0],
Color::Orange => [255, 166, 0],
Color::Yellow => [255, 255, 0],
Color::Custom(r, g, b) => [r, g, b],
}
}
}
impl Mul<f32> for Color {
type Output = Self;
fn mul(self, rhs: f32) -> Self::Output {
let values: [f32; 3] = self.into();
let r = (values[0] * rhs).min(1.0);
let g = (values[1] * rhs).min(1.0);
let b = (values[2] * rhs).min(1.0);
Color::from([r, g, b])
}
}
impl MulAssign<f32> for Color {
fn mul_assign(&mut self, rhs: f32) {
*self = *self * rhs;
}
}
impl From<[u8; 3]> for Color {
fn from(array: [u8; 3]) -> Self {
Self::Custom(array[0], array[1], array[2])
}
}
impl From<[f32; 3]> for Color {
fn from(array: [f32; 3]) -> Self {
Self::Custom(
(array[0] * 255.0).min(255.0) as u8,
(array[1] * 255.0).min(255.0) as u8,
(array[2] * 255.0).min(255.0) as u8,
)
}
}
impl TryFrom<&str> for Color {
type Error = anyhow::Error;
fn try_from(text: &str) -> Result<Color, Self::Error> {
// check if color is a hex value
if text.starts_with("#") {
let without_prefix = text.trim_start_matches("#");
if without_prefix.len() != 6 {
return Err(anyhow::Error::msg(format!(
"Hex color format has wrong format: {}",
without_prefix
)));
}
let r = u8::from_str_radix(&without_prefix[0..2], 16)?;
let g = u8::from_str_radix(&without_prefix[2..4], 16)?;
let b = u8::from_str_radix(&without_prefix[4..6], 16)?;
return Ok(Color::Custom(r, g, b));
}
match text {
"white" => Ok(Color::White),
"black" => Ok(Color::Black),
"red" => Ok(Color::Red),
"blue" => Ok(Color::Blue),
"green" => Ok(Color::Green),
"orange" => Ok(Color::Orange),
"yellow" => Ok(Color::Yellow),
_ => Err(anyhow::Error::msg(format!("Unknown color: {}", text))),
}
}
}
impl fmt::Display for Color {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Color::White => write!(f, "white"),
Color::Black => write!(f, "black"),
Color::Red => write!(f, "red"),
Color::Blue => write!(f, "blue"),
Color::Green => write!(f, "green"),
Color::Orange => write!(f, "orange"),
Color::Yellow => write!(f, "yellow"),
Color::Custom(r, g, b) => write!(f, "#{:X?}{:X?}{:X?}", r, g, b),
}
}
}
impl FromStr for Color {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from(s)
}
}
impl Default for Color {
fn default() -> Self {
Self::Black
}
}
#[test]
fn simple_color_conversion() {
let color = Color::try_from("white").unwrap();
assert_eq!(color, Color::White);
}
#[test]
fn hex_color_conversion() {
let color = Color::try_from("#FFFFFF").unwrap();
assert_eq!(color, Color::Custom(255, 255, 255));
let string = format!("{}", color);
assert_eq!(string, "#FFFFFF");
}

36
src/future.rs Normal file
View file

@ -0,0 +1,36 @@
use std::cell::{Ref, RefCell};
use std::thread::{spawn, JoinHandle};
pub struct Future<T: Send + 'static> {
thread: RefCell<Option<JoinHandle<T>>>,
data: RefCell<Option<T>>,
}
impl<T: Send + 'static> Future<T> {
pub fn new<F>(f: F) -> Future<T>
where
F: Fn() -> T,
F: Send + 'static,
{
let thread = spawn(f);
Future {
thread: RefCell::new(Some(thread)),
data: RefCell::new(None),
}
}
pub fn data(&self) -> Ref<'_, Option<T>> {
let mut thread_opt = self.thread.borrow_mut();
if thread_opt.is_some() {
let thread_tmp = thread_opt.take();
let thread = thread_tmp.unwrap();
if let Ok(data) = thread.join() {
self.data.replace(Some(data));
}
}
self.data.borrow()
}
}

142
src/helperfunctions.rs Normal file
View file

@ -0,0 +1,142 @@
//! Basic helper functions
use std::fs;
use std::io::ErrorKind;
use std::rc::Rc;
use std::sync::Arc;
use std::ffi::CStr;
use std::os::raw::c_char;
use assetpath::AssetPath;
use cgmath;
use cgmath::One;
pub fn erase_by_ptr<T>(vector: &mut Vec<T>, object: &T) -> bool {
match vector
.iter()
.position(|t| t as *const T == object as *const T)
{
Some(i) => {
vector.remove(i);
true
}
None => false,
}
}
pub fn erase_arc<T: ?Sized>(vector: &mut Vec<Arc<T>>, object: &Arc<T>) -> Option<Arc<T>> {
match vector.iter().position(|t| Arc::ptr_eq(t, object)) {
Some(i) => Some(vector.remove(i)),
None => None,
}
}
pub fn erase_rc<T: ?Sized>(vector: &mut Vec<Rc<T>>, object: &Rc<T>) -> Option<Rc<T>> {
match vector.iter().position(|t| Rc::ptr_eq(t, object)) {
Some(i) => Some(vector.remove(i)),
None => None,
}
}
pub fn ortho(
left: f32,
right: f32,
bottom: f32,
top: f32,
z_near: f32,
z_far: f32,
) -> cgmath::Matrix4<f32> {
let mut mat = cgmath::Matrix4::one();
mat[0][0] = 2.0 / (right - left);
mat[1][1] = -2.0 / (top - bottom);
mat[2][2] = -2.0 / (z_far - z_near);
mat[3][0] = -(right + left) / (right - left);
mat[3][1] = -(top + bottom) / (top - bottom);
mat[3][2] = -(z_far + z_near) / (z_far - z_near);
mat
}
pub fn search_dir_recursively(
base_dir: &str,
suffix: &str,
) -> Result<Vec<AssetPath>, std::io::Error> {
let mut files = Vec::new();
// simply return an empty result when the path could not be found
// or the user has a lack of permissions
let dir_content = match fs::read_dir(base_dir) {
Ok(dir) => dir,
Err(err) => match err.kind() {
ErrorKind::NotFound | ErrorKind::PermissionDenied => return Ok(files),
_ => return Err(err),
},
};
for fs_object in dir_content {
let obj = fs_object?;
let path = obj.path();
if path.is_file() {
if let Some(os_str) = path.file_name() {
if let Some(string) = os_str.to_str() {
if string.ends_with(suffix) {
files.push(AssetPath::from((base_dir, string)));
}
}
}
} else {
if let Some(string) = path.to_str() {
let more_files = search_dir_recursively(string, suffix)?;
files.extend(more_files);
}
}
}
Ok(files)
}
pub unsafe fn c_char_to_string(cchar: *const c_char) -> String {
CStr::from_ptr(cchar).to_str().unwrap().to_string()
}
#[inline]
pub fn perspective(fov_y: f32, aspect: f32, z_near: f32, z_far: f32) -> cgmath::Matrix4<f32> {
debug_assert!(z_near != 0.0);
let zero = 0.0;
let one = 1.0;
let two = 2.0;
let q = one / (fov_y / two).tan();
let a = q / aspect;
let b = (z_near + z_far) / (z_near - z_far);
let c = (two * z_near * z_far) / (z_near - z_far);
#[cfg_attr(rustfmt, rustfmt_skip)]
cgmath::Matrix4::new(
a, zero, zero, zero,
zero, -q, zero, zero,
zero, zero, b, zero - one,
zero, zero, c, zero,
)
}
#[inline]
pub fn rotate_z(v: cgmath::Vector3<f32>, angle: f32) -> cgmath::Vector3<f32> {
let mut result = v;
let cos = angle.cos();
let sin = angle.sin();
result.x = v.x * cos - v.y * sin;
result.y = v.x * sin + v.y * cos;
result
}
#[inline]
pub fn almost_eq(f1: f32, f2: f32) -> bool {
(f1 - f2).abs() <= std::f32::EPSILON
}

17
src/lib.rs Normal file
View file

@ -0,0 +1,17 @@
#![deny(rust_2018_idioms)]
pub mod asyncthread;
pub mod random;
//pub mod hashvector;
pub mod color;
pub mod future;
pub mod helperfunctions;
pub mod prelude;
//pub mod closures;
pub mod try_lock_guard;
pub mod arc_unique_vec;
pub mod rc_unique_vec;
pub mod unsafe_life_time;

29
src/prelude.rs Normal file
View file

@ -0,0 +1,29 @@
// crates
pub use cgmath;
// functions
pub use crate::helperfunctions::*;
// async thread
pub use crate::asyncthread::AsyncThread;
pub use crate::future::Future;
// Coin
pub use crate::random::{Coin, Random};
// Unique vectors
pub use crate::arc_unique_vec::ArcUniqueVec;
pub use crate::rc_unique_vec::RcUniqueVec;
// rand crate
pub use rand;
// color
pub use crate::color::*;
// TryLockGuard
pub use crate::try_lock_guard::TryLockGuard;
// unsafe life time trickery
pub use crate::unsafe_life_time::*;

43
src/random.rs Normal file
View file

@ -0,0 +1,43 @@
use rand::Rng;
pub struct Coin;
impl Coin {
pub fn flip(probability: f32) -> bool {
if probability >= 1.0 {
return true;
}
if probability <= 0.0 {
return false;
}
let random_number = Self::raw();
probability > random_number
}
pub fn raw() -> f32 {
rand::thread_rng().gen_range(0.0..1.0)
}
}
pub struct Random;
impl Random {
pub fn range(low: u32, high: u32) -> u32 {
if low == high {
return low;
}
rand::thread_rng().gen_range(low..high)
}
pub fn range_f32(low: f32, high: f32) -> f32 {
if low == high {
return low;
}
rand::thread_rng().gen_range(low..high)
}
}

59
src/rc_unique_vec.rs Normal file
View file

@ -0,0 +1,59 @@
use std::rc::Rc;
/// Wrapper around a `Vec` if `Rc`,
/// that ensures pointer uniqueness of its elements
#[derive(Debug, Clone, Default)]
pub struct RcUniqueVec<T> {
data: Vec<Rc<T>>,
}
impl<T> RcUniqueVec<T> {
/// Creates an empty `RcUniqueVec`
pub fn new() -> RcUniqueVec<T> {
RcUniqueVec { data: Vec::new() }
}
/// Checks for pointer collision while inserting.
/// Returns the index for the position of the inserted element.
pub fn insert(&mut self, element: Rc<T>) -> usize {
match self.data.iter().position(|t| Rc::ptr_eq(t, &element)) {
Some(index) => index,
None => {
let index = self.data.len();
self.data.push(element);
index
}
}
}
/// Checks if the given element is in the vector, then returns true,
/// otherwise false.
pub fn remove(&mut self, element: &Rc<T>) -> bool {
match self.data.iter().position(|t| Rc::ptr_eq(t, element)) {
Some(index) => {
self.data.remove(index);
true
}
None => false,
}
}
/// Checks if that element is in this `RcUniqueVec`
/// and returns its index if possible
pub fn get_index(&self, element: &Rc<T>) -> Option<usize> {
match self.data.iter().position(|t| Rc::ptr_eq(t, element)) {
Some(index) => Some(index),
None => None,
}
}
/// Clears all elements from this vector
pub fn clear(&mut self) {
self.data.clear();
}
/// Returns the reference to the internal vector
pub fn as_vec(&self) -> &Vec<Rc<T>> {
&self.data
}
}

41
src/try_lock_guard.rs Normal file
View file

@ -0,0 +1,41 @@
use std::sync::{LockResult, Mutex, MutexGuard, PoisonError, TryLockError};
pub enum TryLockGuard<'a, T> {
Lock(MutexGuard<'a, T>),
WouldBlock(&'a Mutex<T>),
}
impl<'a, T> TryLockGuard<'a, T> {
pub fn try_lock(
d: &'a Mutex<T>,
) -> Result<TryLockGuard<'a, T>, PoisonError<MutexGuard<'a, T>>> {
match d.try_lock() {
Ok(lock) => Ok(TryLockGuard::Lock(lock)),
Err(try_lock_error) => match try_lock_error {
TryLockError::Poisoned(poison_error) => Err(poison_error),
TryLockError::WouldBlock => Ok(TryLockGuard::WouldBlock(d)),
},
}
}
pub fn would_block(&self) -> bool {
match self {
Self::Lock(_) => false,
Self::WouldBlock(_) => true,
}
}
pub fn guard(self) -> Option<MutexGuard<'a, T>> {
match self {
Self::Lock(guard) => Some(guard),
Self::WouldBlock(_) => None,
}
}
pub fn lock(self) -> LockResult<MutexGuard<'a, T>> {
match self {
Self::Lock(guard) => Ok(guard),
Self::WouldBlock(mutex) => Ok(mutex.lock()?),
}
}
}

7
src/unsafe_life_time.rs Normal file
View file

@ -0,0 +1,7 @@
pub unsafe fn remove_life_time_mut<'a, 'b, T>(t: &'a mut T) -> &'b mut T {
std::mem::transmute(t as *mut T)
}
pub unsafe fn remove_life_time<'a, 'b, T>(t: &'a T) -> &'b T {
std::mem::transmute(t as *const T)
}