Compare commits

..

2 Commits

Author SHA1 Message Date
6a12d213e8 not right 2024-07-31 13:15:29 -07:00
4e4adcb934 timers 2024-07-31 12:10:35 -07:00
14 changed files with 1063 additions and 982 deletions

73
Cargo.lock generated
View File

@ -2,41 +2,17 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.6.0" version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bnum"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50202def95bf36cb7d1d7a7962cea1c36a3f8ad42425e5d2b71d7acb8041b5b8"
[[package]]
name = "fixed_wide"
version = "0.1.1"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "d9c2cf115b3785ede870fada07e8b1aeba3378345b4ca86fe3c772ecabc05c0f"
dependencies = [
"arrayvec",
"bnum",
"paste",
"ratio_ops",
]
[[package]] [[package]]
name = "glam" name = "glam"
version = "0.29.0" version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28091a37a5d09b555cb6628fd954da299b536433834f5b8e59eba78e0cbbf8a" checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94"
[[package]] [[package]]
name = "id" name = "id"
@ -49,65 +25,38 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "linear_ops"
version = "0.1.0"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "b2e6977ac24f47086d8a7a2d4ae1c720e86dfdc8407cf5e34c18bfa01053c456"
dependencies = [
"fixed_wide",
"paste",
"ratio_ops",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.86" version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.37" version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "ratio_ops"
version = "0.1.0"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "01239195d6afe0509e7e3511b716c0540251dfe7ece0a9a5a27116afb766c42c"
[[package]] [[package]]
name = "strafesnet_common" name = "strafesnet_common"
version = "0.5.2" version = "0.2.0"
dependencies = [ dependencies = [
"arrayvec",
"bitflags", "bitflags",
"fixed_wide",
"glam", "glam",
"id", "id",
"linear_ops",
"ratio_ops",
] ]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.79" version = "2.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -116,6 +65,6 @@ dependencies = [
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.13" version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "strafesnet_common" name = "strafesnet_common"
version = "0.5.2" version = "0.2.0"
edition = "2021" edition = "2021"
repository = "https://git.itzana.me/StrafesNET/common" repository = "https://git.itzana.me/StrafesNET/common"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
@ -10,10 +10,6 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
arrayvec = "0.7.4"
bitflags = "2.6.0" bitflags = "2.6.0"
fixed_wide = { version = "0.1.1", registry = "strafesnet", features = ["deferred-division","zeroes","wide-mul"] } glam = "0.28.0"
linear_ops = { version = "0.1.0", registry = "strafesnet", features = ["deferred-division","named-fields"] }
ratio_ops = { version = "0.1.0", registry = "strafesnet" }
glam = "0.29.0"
id = { version = "0.1.0", registry = "strafesnet" } id = { version = "0.1.0", registry = "strafesnet" }

View File

@ -1,4 +1,4 @@
use crate::integer::{vec3,Planar64Vec3}; use crate::integer::Planar64Vec3;
#[derive(Clone)] #[derive(Clone)]
pub struct Aabb{ pub struct Aabb{
@ -8,7 +8,7 @@ pub struct Aabb{
impl Default for Aabb{ impl Default for Aabb{
fn default()->Self{ fn default()->Self{
Self{min:vec3::MAX,max:vec3::MIN} Self{min:Planar64Vec3::MAX,max:Planar64Vec3::MIN}
} }
} }
@ -35,14 +35,13 @@ impl Aabb{
self.max+=hs; self.max+=hs;
} }
pub fn intersects(&self,aabb:&Aabb)->bool{ pub fn intersects(&self,aabb:&Aabb)->bool{
let bvec=self.min.lt(aabb.max)&aabb.min.lt(self.max); (self.min.cmplt(aabb.max)&aabb.min.cmplt(self.max)).all()
bvec.all()
} }
pub fn size(&self)->Planar64Vec3{ pub fn size(&self)->Planar64Vec3{
self.max-self.min self.max-self.min
} }
pub fn center(&self)->Planar64Vec3{ pub fn center(&self)->Planar64Vec3{
self.min+(self.max-self.min)>>1 self.min.midpoint(self.max)
} }
//probably use floats for area & volume because we don't care about precision //probably use floats for area & volume because we don't care about precision
// pub fn area_weight(&self)->f32{ // pub fn area_weight(&self)->f32{

View File

@ -19,18 +19,11 @@ impl<R,T> Default for RecursiveContent<R,T>{
Self::Branch(Vec::new()) Self::Branch(Vec::new())
} }
} }
#[derive(Default)]
pub struct BvhNode<T>{ pub struct BvhNode<T>{
content:RecursiveContent<BvhNode<T>,T>, content:RecursiveContent<BvhNode<T>,T>,
aabb:Aabb, aabb:Aabb,
} }
impl<T> Default for BvhNode<T>{
fn default()->Self{
Self{
content:Default::default(),
aabb:Aabb::default(),
}
}
}
pub struct BvhWeightNode<W,T>{ pub struct BvhWeightNode<W,T>{
content:RecursiveContent<BvhWeightNode<W,T>,T>, content:RecursiveContent<BvhWeightNode<W,T>,T>,
weight:W, weight:W,
@ -121,14 +114,16 @@ fn generate_bvh_node<T>(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode<T>{
aabb, aabb,
} }
}else{ }else{
let mut octant=std::collections::HashMap::with_capacity(n);//this ids which octant the boxen is put in
let mut sort_x=Vec::with_capacity(n); let mut sort_x=Vec::with_capacity(n);
let mut sort_y=Vec::with_capacity(n); let mut sort_y=Vec::with_capacity(n);
let mut sort_z=Vec::with_capacity(n); let mut sort_z=Vec::with_capacity(n);
for (i,(_,aabb)) in boxen.iter().enumerate(){ for (i,(_,aabb)) in boxen.iter().enumerate(){
let center=aabb.center(); let center=aabb.center();
sort_x.push((i,center.x)); octant.insert(i,0);
sort_y.push((i,center.y)); sort_x.push((i,center.x()));
sort_z.push((i,center.z)); sort_y.push((i,center.y()));
sort_z.push((i,center.z()));
} }
sort_x.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1)); sort_x.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1));
sort_y.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1)); sort_y.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1));
@ -137,34 +132,26 @@ fn generate_bvh_node<T>(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode<T>{
let median_x=sort_x[h].1; let median_x=sort_x[h].1;
let median_y=sort_y[h].1; let median_y=sort_y[h].1;
let median_z=sort_z[h].1; let median_z=sort_z[h].1;
//locate a run of values equal to the median for (i,c) in sort_x{
//partition point gives the first index for which the predicate evaluates to false if median_x<c{
let first_index_eq_median_x=sort_x.partition_point(|&(_,x)|x<median_x); octant.insert(i,octant[&i]+1<<0);
let first_index_eq_median_y=sort_y.partition_point(|&(_,y)|y<median_y);
let first_index_eq_median_z=sort_z.partition_point(|&(_,z)|z<median_z);
let first_index_gt_median_x=sort_x.partition_point(|&(_,x)|x<=median_x);
let first_index_gt_median_y=sort_y.partition_point(|&(_,y)|y<=median_y);
let first_index_gt_median_z=sort_z.partition_point(|&(_,z)|z<=median_z);
//pick which side median value copies go into such that both sides are as balanced as possible based on distance from n/2
let partition_point_x=if n.abs_diff(2*first_index_eq_median_x)<n.abs_diff(2*first_index_gt_median_x){first_index_eq_median_x}else{first_index_gt_median_x};
let partition_point_y=if n.abs_diff(2*first_index_eq_median_y)<n.abs_diff(2*first_index_gt_median_y){first_index_eq_median_y}else{first_index_gt_median_y};
let partition_point_z=if n.abs_diff(2*first_index_eq_median_z)<n.abs_diff(2*first_index_gt_median_z){first_index_eq_median_z}else{first_index_gt_median_z};
//this ids which octant the boxen is put in
let mut octant=vec![0;n];
for &(i,_) in &sort_x[partition_point_x..]{
octant[i]+=1<<0;
} }
for &(i,_) in &sort_y[partition_point_y..]{
octant[i]+=1<<1;
} }
for &(i,_) in &sort_z[partition_point_z..]{ for (i,c) in sort_y{
octant[i]+=1<<2; if median_y<c{
octant.insert(i,octant[&i]+1<<1);
}
}
for (i,c) in sort_z{
if median_z<c{
octant.insert(i,octant[&i]+1<<2);
}
} }
//generate lists for unique octant values //generate lists for unique octant values
let mut list_list=Vec::with_capacity(8); let mut list_list=Vec::with_capacity(8);
let mut octant_list=Vec::with_capacity(8); let mut octant_list=Vec::with_capacity(8);
for (i,(data,aabb)) in boxen.into_iter().enumerate(){ for (i,(data,aabb)) in boxen.into_iter().enumerate(){
let octant_id=octant[i]; let octant_id=octant[&i];
let list_id=if let Some(list_id)=octant_list.iter().position(|&id|id==octant_id){ let list_id=if let Some(list_id)=octant_list.iter().position(|&id|id==octant_id){
list_id list_id
}else{ }else{

View File

@ -31,23 +31,6 @@ pub enum Booster{
//Affine(crate::integer::Planar64Affine3),//capable of SetVelocity,DotVelocity,normal booster,bouncy part,redirect velocity, and much more //Affine(crate::integer::Planar64Affine3),//capable of SetVelocity,DotVelocity,normal booster,bouncy part,redirect velocity, and much more
Velocity(Planar64Vec3),//straight up boost velocity adds to your current velocity Velocity(Planar64Vec3),//straight up boost velocity adds to your current velocity
Energy{direction:Planar64Vec3,energy:Planar64},//increase energy in direction Energy{direction:Planar64Vec3,energy:Planar64},//increase energy in direction
AirTime(Time),//increase airtime, invariant across mass and gravity changes
Height(Planar64),//increase height, invariant across mass and gravity changes
}
impl Booster{
pub fn boost(&self,velocity:Planar64Vec3)->Planar64Vec3{
match self{
&Booster::Velocity(boost_velocity)=>velocity+boost_velocity,
&Booster::Energy{..}=>{
todo!()
//let d=direction.dot(velocity);
//TODO: think about negative
//velocity+direction.with_length((d*d+energy).sqrt()-d)
},
Booster::AirTime(_)=>todo!(),
Booster::Height(_)=>todo!(),
}
}
} }
#[derive(Clone,Hash,Eq,PartialEq)] #[derive(Clone,Hash,Eq,PartialEq)]
pub enum TrajectoryChoice{ pub enum TrajectoryChoice{
@ -151,24 +134,23 @@ impl IntersectingAttributes{
} }
#[derive(Clone,Copy,id::Id,Hash,Eq,PartialEq)] #[derive(Clone,Copy,id::Id,Hash,Eq,PartialEq)]
pub struct CollisionAttributesId(u32); pub struct CollisionAttributesId(u32);
#[derive(Clone,Default,Hash,Eq,PartialEq)]
pub struct ContactAttributes{
pub contacting:ContactingAttributes,
pub general:GeneralAttributes,
}
#[derive(Clone,Default,Hash,Eq,PartialEq)]
pub struct IntersectAttributes{
pub intersecting:IntersectingAttributes,
pub general:GeneralAttributes,
}
#[derive(Clone,Hash,Eq,PartialEq)] #[derive(Clone,Hash,Eq,PartialEq)]
pub enum CollisionAttributes{ pub enum CollisionAttributes{
Decoration,//visual only Decoration,//visual only
Contact(ContactAttributes),//track whether you are contacting the object Contact{//track whether you are contacting the object
Intersect(IntersectAttributes),//track whether you are intersecting the object contacting:ContactingAttributes,
general:GeneralAttributes,
},
Intersect{//track whether you are intersecting the object
intersecting:IntersectingAttributes,
general:GeneralAttributes,
},
} }
impl CollisionAttributes{ impl CollisionAttributes{
pub fn contact_default()->Self{ pub fn contact_default()->Self{
Self::Contact(ContactAttributes::default()) Self::Contact{
contacting:ContactingAttributes::default(),
general:GeneralAttributes::default()
}
} }
} }

View File

@ -1,6 +1,6 @@
const VALVE_SCALE:Planar64=Planar64::raw(1<<28);// 1/16 const VALVE_SCALE:Planar64=Planar64::raw(1<<28);// 1/16
use crate::integer::{int,vec3::int as int3,Time,Ratio64,Planar64,Planar64Vec3}; use crate::integer::{Time,Ratio64,Planar64,Planar64Vec3};
use crate::controls_bitflag::Controls; use crate::controls_bitflag::Controls;
#[derive(Clone,Debug)] #[derive(Clone,Debug)]
@ -41,140 +41,33 @@ impl std::default::Default for StyleModifiers{
#[derive(Clone,Debug)] #[derive(Clone,Debug)]
pub enum JumpCalculation{ pub enum JumpCalculation{
Max,//Roblox: jumped_speed=max(velocity.boost(),velocity.jump()) Capped,//roblox
BoostThenJump,//jumped_speed=velocity.boost().jump() Energy,//new
JumpThenBoost,//jumped_speed=velocity.jump().boost() Linear,//source
} }
#[derive(Clone,Debug)] #[derive(Clone,Debug)]
pub enum JumpImpulse{ pub enum JumpImpulse{
Time(Time),//jump time is invariant across mass and gravity changes FromTime(Time),//jump time is invariant across mass and gravity changes
Height(Planar64),//jump height is invariant across mass and gravity changes FromHeight(Planar64),//jump height is invariant across mass and gravity changes
Linear(Planar64),//jump velocity is invariant across mass and gravity changes FromDeltaV(Planar64),//jump velocity is invariant across mass and gravity changes
Energy(Planar64),// :) FromEnergy(Planar64),// :)
} }
//Jumping acts on dot(walks_state.normal,body.velocity) //Jumping acts on dot(walks_state.normal,body.velocity)
//Capped means it increases the dot to the cap
//Energy means it adds energy //Energy means it adds energy
//Linear means it linearly adds on //Linear means it linearly adds on
impl JumpImpulse{ impl JumpImpulse{
pub fn jump( //fn get_jump_time(&self)->Planar64
&self, //fn get_jump_height(&self)->Planar64
velocity:Planar64Vec3, //fn get_jump_energy(&self)->Planar64
jump_dir:Planar64Vec3,
gravity:&Planar64Vec3,
mass:Planar64,
)->Planar64Vec3{
match self{
&JumpImpulse::Time(time)=>velocity-(*gravity*time).map(|t|t.divide().fix_1()),
&JumpImpulse::Height(height)=>{
//height==-v.y*v.y/(2*g.y);
//use energy to determine max height
let gg=gravity.length_squared();
let g=gg.sqrt().fix_1();
let v_g=gravity.dot(velocity);
//do it backwards
let radicand=v_g*v_g+(g*height*2).fix_4();
velocity-(*gravity*(radicand.sqrt().fix_2()+v_g)/gg).divide().fix_1()
},
&JumpImpulse::Linear(jump_speed)=>velocity+(jump_dir*jump_speed/jump_dir.length()).divide().fix_1(),
&JumpImpulse::Energy(energy)=>{
//calculate energy
//let e=gravity.dot(velocity);
//add
//you get the idea
todo!()
},
}
}
//TODO: remove this and implement JumpCalculation properly
pub fn get_jump_deltav(&self,gravity:&Planar64Vec3,mass:Planar64)->Planar64{ pub fn get_jump_deltav(&self,gravity:&Planar64Vec3,mass:Planar64)->Planar64{
//gravity.length() is actually the proper calculation because the jump is always opposite the gravity direction //gravity.length() is actually the proper calculation because the jump is always opposite the gravity direction
match self{ match self{
&JumpImpulse::Time(time)=>(gravity.length().fix_1()*time/2).divide().fix_1(), &JumpImpulse::FromTime(time)=>gravity.length()*(time/2),
&JumpImpulse::Height(height)=>(gravity.length()*height*2).sqrt().fix_1(), &JumpImpulse::FromHeight(height)=>(gravity.length()*height*2).sqrt(),
&JumpImpulse::Linear(deltav)=>deltav, &JumpImpulse::FromDeltaV(deltav)=>deltav,
&JumpImpulse::Energy(energy)=>(energy.sqrt()*2/mass.sqrt()).divide().fix_1(), &JumpImpulse::FromEnergy(energy)=>(energy*2/mass).sqrt(),
}
}
}
#[derive(Clone,Debug)]
pub struct JumpSettings{
//information used to calculate jump power
pub impulse:JumpImpulse,
//information used to calculate jump behaviour
pub calculation:JumpCalculation,
//limit the minimum jump power when combined with downwards momentum
//This is true in both roblox and source
pub limit_minimum:bool,
}
impl JumpSettings{
pub fn jumped_velocity(
&self,
style:&StyleModifiers,
jump_dir:Planar64Vec3,
rel_velocity:Planar64Vec3,
booster:Option<&crate::gameplay_attributes::Booster>,
)->Planar64Vec3{
let jump_speed=self.impulse.get_jump_deltav(&style.gravity,style.mass);
match (self.limit_minimum,&self.calculation){
(true,JumpCalculation::Max)=>{
//the roblox calculation
let boost_vel=match booster{
Some(booster)=>booster.boost(rel_velocity),
None=>rel_velocity,
};
let j=boost_vel.dot(jump_dir);
let js=jump_speed.fix_2();
if j<js{
//weak booster: just do a regular jump
boost_vel+jump_dir.with_length(js-j).divide().fix_1()
}else{
//activate booster normally, jump does nothing
boost_vel
}
},
(true,_)=>{
//the source calculation (?)
let boost_vel=match booster{
Some(booster)=>booster.boost(rel_velocity),
None=>rel_velocity,
};
let j=boost_vel.dot(jump_dir);
let js=jump_speed.fix_2();
if j<js{
//speed in direction of jump cannot be lower than amount
boost_vel+jump_dir.with_length(js-j).divide().fix_1()
}else{
//boost and jump add together
boost_vel+jump_dir.with_length(js).divide().fix_1()
}
}
(false,JumpCalculation::Max)=>{
//??? calculation
//max(boost_vel,jump_vel)
let boost_vel=match booster{
Some(booster)=>booster.boost(rel_velocity),
None=>rel_velocity,
};
let boost_dot=boost_vel.dot(jump_dir);
let js=jump_speed.fix_2();
if boost_dot<js{
//weak boost is extended to jump speed
boost_vel+jump_dir.with_length(js-boost_dot).divide().fix_1()
}else{
//activate booster normally, jump does nothing
boost_vel
}
},
//the strafe client calculation
(false,_)=>{
let boost_vel=match booster{
Some(booster)=>booster.boost(rel_velocity),
None=>rel_velocity,
};
boost_vel+jump_dir.with_length(jump_speed).divide().fix_1()
},
} }
} }
} }
@ -182,14 +75,34 @@ impl JumpSettings{
#[derive(Clone,Debug)] #[derive(Clone,Debug)]
pub struct ControlsActivation{ pub struct ControlsActivation{
//allowed keys //allowed keys
pub controls_mask:Controls, controls_mask:Controls,
//allow strafing only if any of the masked controls are held, eg W|S for shsw //allow strafing only if any of the masked controls are held, eg W|S for shsw
pub controls_intersects:Controls, controls_intersects:Controls,
//allow strafing only if all of the masked controls are held, eg W for hsw, w-only //allow strafing only if all of the masked controls are held, eg W for hsw, w-only
pub controls_contains:Controls, controls_contains:Controls,
//Function(Box<dyn Fn(u32)->bool>), //Function(Box<dyn Fn(u32)->bool>),
} }
impl ControlsActivation{ impl ControlsActivation{
pub const fn new(
controls_mask:Controls,
controls_intersects:Controls,
controls_contains:Controls,
)->Self{
Self{
controls_mask,
controls_intersects,
controls_contains,
}
}
pub const fn controls_mask(&self)->Controls{
self.controls_mask
}
pub const fn controls_intersects(&self)->Controls{
self.controls_intersects
}
pub const fn controls_contains(&self)->Controls{
self.controls_contains
}
pub const fn mask(&self,controls:Controls)->Controls{ pub const fn mask(&self,controls:Controls)->Controls{
controls.intersection(self.controls_mask) controls.intersection(self.controls_mask)
} }
@ -258,17 +171,27 @@ impl ControlsActivation{
#[derive(Clone,Debug)] #[derive(Clone,Debug)]
pub struct StrafeSettings{ pub struct StrafeSettings{
pub enable:ControlsActivation, enable:ControlsActivation,
pub mv:Planar64, mv:Planar64,
pub air_accel_limit:Option<Planar64>, air_accel_limit:Option<Planar64>,
pub tick_rate:Ratio64, tick_rate:Ratio64,
} }
impl StrafeSettings{ impl StrafeSettings{
pub const fn new(
enable:ControlsActivation,
mv:Planar64,
air_accel_limit:Option<Planar64>,
tick_rate:Ratio64,
)->Self{
Self{enable,mv,air_accel_limit,tick_rate}
}
pub fn into_inner(self)->(ControlsActivation,Planar64,Option<Planar64>,Ratio64){
(self.enable,self.mv,self.air_accel_limit,self.tick_rate)
}
pub fn tick_velocity(&self,velocity:Planar64Vec3,control_dir:Planar64Vec3)->Option<Planar64Vec3>{ pub fn tick_velocity(&self,velocity:Planar64Vec3,control_dir:Planar64Vec3)->Option<Planar64Vec3>{
let d=velocity.dot(control_dir); let d=velocity.dot(control_dir);
let mv=self.mv.fix_2(); match d<self.mv{
match d<mv{ true=>Some(velocity+control_dir*self.air_accel_limit.map_or(self.mv-d,|limit|limit.min(self.mv-d))),
true=>Some(velocity+(control_dir*self.air_accel_limit.map_or(mv-d,|limit|limit.fix_2().min(mv-d))).fix_1()),
false=>None, false=>None,
} }
} }
@ -285,109 +208,173 @@ impl StrafeSettings{
#[derive(Clone,Debug)] #[derive(Clone,Debug)]
pub struct PropulsionSettings{ pub struct PropulsionSettings{
pub magnitude:Planar64, magnitude:Planar64,
} }
impl PropulsionSettings{ impl PropulsionSettings{
pub const fn new(magnitude:Planar64)->Self{
Self{magnitude}
}
pub fn magnitude(&self)->Planar64{
self.magnitude
}
pub fn acceleration(&self,control_dir:Planar64Vec3)->Planar64Vec3{ pub fn acceleration(&self,control_dir:Planar64Vec3)->Planar64Vec3{
(control_dir*self.magnitude).fix_1() control_dir*self.magnitude
}
}
#[derive(Clone,Debug)]
pub struct JumpSettings{
//information used to calculate jump power
impulse:JumpImpulse,
//information used to calculate jump behaviour
calculation:JumpCalculation,
}
impl JumpSettings{
pub const fn new(
impulse:JumpImpulse,
calculation:JumpCalculation,
)->Self{
Self{impulse,calculation}
}
pub fn into_inner(self)->(JumpImpulse,JumpCalculation){
(self.impulse,self.calculation)
}
pub fn jumped_velocity(&self,style:&StyleModifiers,jump_dir:Planar64Vec3,velocity:Planar64Vec3)->Planar64Vec3{
match self.calculation{
//roblox style
JumpCalculation::Capped=>todo!(),
//something different
JumpCalculation::Energy=>todo!(),
//source style
JumpCalculation::Linear=>velocity+jump_dir*(self.impulse.get_jump_deltav(&style.gravity,style.mass)/jump_dir.length()),
}
} }
} }
#[derive(Clone,Debug)] #[derive(Clone,Debug)]
pub struct AccelerateSettings{ pub struct AccelerateSettings{
pub accel:Planar64, accel:Planar64,
pub topspeed:Planar64, topspeed:Planar64,
}
impl AccelerateSettings{
pub const fn new(
accel:Planar64,
topspeed:Planar64,
)->Self{
Self{accel,topspeed}
}
pub const fn accel(&self)->Planar64{
self.accel
}
pub const fn topspeed(&self)->Planar64{
self.topspeed
}
} }
#[derive(Clone,Debug)] #[derive(Clone,Debug)]
pub struct WalkSettings{ pub struct WalkSettings{
pub accelerate:AccelerateSettings, accelerate:AccelerateSettings,
pub static_friction:Planar64, static_friction:Planar64,
pub kinetic_friction:Planar64, kinetic_friction:Planar64,
//if a surf slope angle does not exist, then everything is slippery and walking is impossible //if a surf slope angle does not exist, then everything is slippery and walking is impossible
pub surf_dot:Planar64,//surf_dot<n.dot(up)/n.length() surf_dot:Planar64,//surf_dot<n.dot(up)/n.length()
} }
impl WalkSettings{ impl WalkSettings{
pub const fn new(
accelerate:AccelerateSettings,
static_friction:Planar64,
kinetic_friction:Planar64,
surf_dot:Planar64,
)->Self{
Self{accelerate,static_friction,kinetic_friction,surf_dot}
}
pub fn into_inner(self)->(AccelerateSettings,Planar64,Planar64,Planar64){
(self.accelerate,self.static_friction,self.kinetic_friction,self.surf_dot)
}
pub fn accel(&self,target_diff:Planar64Vec3,gravity:Planar64Vec3)->Planar64{ pub fn accel(&self,target_diff:Planar64Vec3,gravity:Planar64Vec3)->Planar64{
//TODO: fallible walk accel //TODO: fallible walk accel
let diff_len=target_diff.length().fix_1(); let diff_len=target_diff.length();
let friction=if diff_len<self.accelerate.topspeed{ let friction=if diff_len<self.accelerate.topspeed{
self.static_friction self.static_friction
}else{ }else{
self.kinetic_friction self.kinetic_friction
}; };
self.accelerate.accel.min((-gravity.y*friction).fix_1()) self.accelerate.accel.min(-Planar64Vec3::Y.dot(gravity)*friction)
} }
pub fn get_walk_target_velocity(&self,control_dir:Planar64Vec3,normal:Planar64Vec3)->Planar64Vec3{ pub fn get_walk_target_velocity(&self,control_dir:Planar64Vec3,normal:Planar64Vec3)->Planar64Vec3{
if control_dir==crate::integer::vec3::ZERO{ if control_dir==Planar64Vec3::ZERO{
return control_dir; return control_dir;
} }
let nn=normal.length_squared(); let n=normal.length();
let mm=control_dir.length_squared(); let m=control_dir.length();
let nnmm=nn*mm; let d=normal.dot(control_dir)/m;
let d=normal.dot(control_dir); if d<n{
let dd=d*d;
if dd<nnmm{
let cr=normal.cross(control_dir); let cr=normal.cross(control_dir);
if cr==crate::integer::vec3::ZERO_2{ if cr==Planar64Vec3::ZERO{
crate::integer::vec3::ZERO Planar64Vec3::ZERO
}else{ }else{
(cr.cross(normal)*self.accelerate.topspeed/((nn*(nnmm-dd)).sqrt())).divide().fix_1() cr.cross(normal)*(self.accelerate.topspeed/(n*(n*n-d*d).sqrt()*m))
} }
}else{ }else{
crate::integer::vec3::ZERO Planar64Vec3::ZERO
} }
} }
pub fn is_slope_walkable(&self,normal:Planar64Vec3,up:Planar64Vec3)->bool{ pub fn is_slope_walkable(&self,normal:Planar64Vec3,up:Planar64Vec3)->bool{
//normal is not guaranteed to be unit length //normal is not guaranteed to be unit length
let ny=normal.dot(up); let ny=normal.dot(up);
let h=normal.length().fix_1(); let h=normal.length();
//remember this is a normal vector //remember this is a normal vector
ny.is_positive()&&h*self.surf_dot<ny Planar64::ZERO<ny&&h*self.surf_dot<ny
} }
} }
#[derive(Clone,Debug)] #[derive(Clone,Debug)]
pub struct LadderSettings{ pub struct LadderSettings{
pub accelerate:AccelerateSettings, accelerate:AccelerateSettings,
//how close to pushing directly into/out of the ladder normal //how close to pushing directly into/out of the ladder normal
//does your input need to be to redirect straight up/down the ladder //does your input need to be to redirect straight up/down the ladder
pub dot:Planar64, dot:Planar64,
} }
impl LadderSettings{ impl LadderSettings{
pub const fn new(
accelerate:AccelerateSettings,
dot:Planar64,
)->Self{
Self{accelerate,dot}
}
pub fn into_inner(self)->(AccelerateSettings,Planar64){
(self.accelerate,self.dot)
}
pub const fn accel(&self,target_diff:Planar64Vec3,gravity:Planar64Vec3)->Planar64{ pub const fn accel(&self,target_diff:Planar64Vec3,gravity:Planar64Vec3)->Planar64{
//TODO: fallible ladder accel //TODO: fallible ladder accel
self.accelerate.accel self.accelerate.accel
} }
pub fn get_ladder_target_velocity(&self,mut control_dir:Planar64Vec3,normal:Planar64Vec3)->Planar64Vec3{ pub fn get_ladder_target_velocity(&self,mut control_dir:Planar64Vec3,normal:Planar64Vec3)->Planar64Vec3{
if control_dir==crate::integer::vec3::ZERO{ if control_dir==Planar64Vec3::ZERO{
return control_dir; return control_dir;
} }
let nn=normal.length_squared(); let n=normal.length();
let mm=control_dir.length_squared(); let m=control_dir.length();
let nnmm=nn*mm; let mut d=normal.dot(control_dir)/m;
let d=normal.dot(control_dir); if d< -self.dot*n{
let mut dd=d*d; control_dir=Planar64Vec3::Y*m;
if (self.dot*self.dot*nnmm).fix_4()<dd{ d=normal.y();
if d.is_negative(){ }else if self.dot*n<d{
control_dir=Planar64Vec3::new([Planar64::ZERO,mm.fix_1(),Planar64::ZERO]); control_dir=Planar64Vec3::NEG_Y*m;
}else{ d=-normal.y();
control_dir=Planar64Vec3::new([Planar64::ZERO,-mm.fix_1(),Planar64::ZERO]);
}
dd=(normal.y*normal.y).fix_4();
} }
//n=d if you are standing on top of a ladder and press E. //n=d if you are standing on top of a ladder and press E.
//two fixes: //two fixes:
//- ladder movement is not allowed on walkable surfaces //- ladder movement is not allowed on walkable surfaces
//- fix the underlying issue //- fix the underlying issue
if dd<nnmm{ if d.get().unsigned_abs()<n.get().unsigned_abs(){
let cr=normal.cross(control_dir); let cr=normal.cross(control_dir);
if cr==crate::integer::vec3::ZERO_2{ if cr==Planar64Vec3::ZERO{
crate::integer::vec3::ZERO Planar64Vec3::ZERO
}else{ }else{
(cr.cross(normal)*self.accelerate.topspeed/((nn*(nnmm-dd)).sqrt())).divide().fix_1() cr.cross(normal)*(self.accelerate.topspeed/(n*(n*n-d*d).sqrt()))
} }
}else{ }else{
crate::integer::vec3::ZERO Planar64Vec3::ZERO
} }
} }
} }
@ -410,22 +397,22 @@ pub struct Hitbox{
impl Hitbox{ impl Hitbox{
pub fn roblox()->Self{ pub fn roblox()->Self{
Self{ Self{
halfsize:int3(2,5,2)>>1, halfsize:Planar64Vec3::int(2,5,2)/2,
mesh:HitboxMesh::Cylinder, mesh:HitboxMesh::Cylinder,
} }
} }
pub fn source()->Self{ pub fn source()->Self{
Self{ Self{
halfsize:((int3(33,73,33)>>1)*VALVE_SCALE).fix_1(), halfsize:Planar64Vec3::int(33,73,33)/2*VALVE_SCALE,
mesh:HitboxMesh::Box, mesh:HitboxMesh::Box,
} }
} }
} }
impl StyleModifiers{ impl StyleModifiers{
pub const RIGHT_DIR:Planar64Vec3=crate::integer::vec3::X; pub const RIGHT_DIR:Planar64Vec3=Planar64Vec3::X;
pub const UP_DIR:Planar64Vec3=crate::integer::vec3::Y; pub const UP_DIR:Planar64Vec3=Planar64Vec3::Y;
pub const FORWARD_DIR:Planar64Vec3=crate::integer::vec3::NEG_Z; pub const FORWARD_DIR:Planar64Vec3=Planar64Vec3::NEG_Z;
pub fn neo()->Self{ pub fn neo()->Self{
Self{ Self{
@ -434,38 +421,37 @@ impl StyleModifiers{
strafe:Some(StrafeSettings{ strafe:Some(StrafeSettings{
enable:ControlsActivation::full_2d(), enable:ControlsActivation::full_2d(),
air_accel_limit:None, air_accel_limit:None,
mv:int(3), mv:Planar64::int(3),
tick_rate:Ratio64::new(64,Time::ONE_SECOND.nanos() as u64).unwrap(), tick_rate:Ratio64::new(64,Time::ONE_SECOND.nanos() as u64).unwrap(),
}), }),
jump:Some(JumpSettings{ jump:Some(JumpSettings{
impulse:JumpImpulse::Energy(int(512)), impulse:JumpImpulse::FromEnergy(Planar64::int(512)),
calculation:JumpCalculation::JumpThenBoost, calculation:JumpCalculation::Energy,
limit_minimum:false,
}), }),
gravity:int3(0,-80,0), gravity:Planar64Vec3::int(0,-80,0),
mass:int(1), mass:Planar64::int(1),
rocket:None, rocket:None,
walk:Some(WalkSettings{ walk:Some(WalkSettings{
accelerate:AccelerateSettings{ accelerate:AccelerateSettings{
topspeed:int(16), topspeed:Planar64::int(16),
accel:int(80), accel:Planar64::int(80),
}, },
static_friction:int(2), static_friction:Planar64::int(2),
kinetic_friction:int(3),//unrealistic: kinetic friction is typically lower than static kinetic_friction:Planar64::int(3),//unrealistic: kinetic friction is typically lower than static
surf_dot:int(3)/4, surf_dot:Planar64::int(3)/4,
}), }),
ladder:Some(LadderSettings{ ladder:Some(LadderSettings{
accelerate:AccelerateSettings{ accelerate:AccelerateSettings{
topspeed:int(16), topspeed:Planar64::int(16),
accel:int(160), accel:Planar64::int(160),
}, },
dot:(int(1)/2).sqrt(), dot:(Planar64::int(1)/2).sqrt(),
}), }),
swim:Some(PropulsionSettings{ swim:Some(PropulsionSettings{
magnitude:int(12), magnitude:Planar64::int(12),
}), }),
hitbox:Hitbox::roblox(), hitbox:Hitbox::roblox(),
camera_offset:int3(0,2,0),//4.5-2.5=2 camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2
} }
} }
@ -476,43 +462,42 @@ impl StyleModifiers{
strafe:Some(StrafeSettings{ strafe:Some(StrafeSettings{
enable:ControlsActivation::full_2d(), enable:ControlsActivation::full_2d(),
air_accel_limit:None, air_accel_limit:None,
mv:int(27)/10, mv:Planar64::int(27)/10,
tick_rate:Ratio64::new(100,Time::ONE_SECOND.nanos() as u64).unwrap(), tick_rate:Ratio64::new(100,Time::ONE_SECOND.nanos() as u64).unwrap(),
}), }),
jump:Some(JumpSettings{ jump:Some(JumpSettings{
impulse:JumpImpulse::Time(Time::from_micros(715_588)), impulse:JumpImpulse::FromTime(Time::from_micros(715_588)),
calculation:JumpCalculation::Max, calculation:JumpCalculation::Linear,//Should be capped
limit_minimum:true,
}), }),
gravity:int3(0,-100,0), gravity:Planar64Vec3::int(0,-100,0),
mass:int(1), mass:Planar64::int(1),
rocket:None, rocket:None,
walk:Some(WalkSettings{ walk:Some(WalkSettings{
accelerate:AccelerateSettings{ accelerate:AccelerateSettings{
topspeed:int(18), topspeed:Planar64::int(18),
accel:int(90), accel:Planar64::int(90),
}, },
static_friction:int(2), static_friction:Planar64::int(2),
kinetic_friction:int(3),//unrealistic: kinetic friction is typically lower than static kinetic_friction:Planar64::int(3),//unrealistic: kinetic friction is typically lower than static
surf_dot:int(3)/4,// normal.y=0.75 surf_dot:Planar64::int(3)/4,// normal.y=0.75
}), }),
ladder:Some(LadderSettings{ ladder:Some(LadderSettings{
accelerate:AccelerateSettings{ accelerate:AccelerateSettings{
topspeed:int(18), topspeed:Planar64::int(18),
accel:int(180), accel:Planar64::int(180),
}, },
dot:(int(1)/2).sqrt(), dot:(Planar64::int(1)/2).sqrt(),
}), }),
swim:Some(PropulsionSettings{ swim:Some(PropulsionSettings{
magnitude:int(12), magnitude:Planar64::int(12),
}), }),
hitbox:Hitbox::roblox(), hitbox:Hitbox::roblox(),
camera_offset:int3(0,2,0),//4.5-2.5=2 camera_offset:Planar64Vec3::int(0,2,0),//4.5-2.5=2
} }
} }
pub fn roblox_surf()->Self{ pub fn roblox_surf()->Self{
Self{ Self{
gravity:int3(0,-50,0), gravity:Planar64Vec3::int(0,-50,0),
..Self::roblox_bhop() ..Self::roblox_bhop()
} }
} }
@ -520,7 +505,7 @@ impl StyleModifiers{
Self{ Self{
strafe:None, strafe:None,
rocket:Some(PropulsionSettings{ rocket:Some(PropulsionSettings{
magnitude:int(200), magnitude:Planar64::int(200),
}), }),
..Self::roblox_bhop() ..Self::roblox_bhop()
} }
@ -533,38 +518,37 @@ impl StyleModifiers{
strafe:Some(StrafeSettings{ strafe:Some(StrafeSettings{
enable:ControlsActivation::full_2d(), enable:ControlsActivation::full_2d(),
air_accel_limit:Some(Planar64::raw(150<<28)*100), air_accel_limit:Some(Planar64::raw(150<<28)*100),
mv:(Planar64::raw(30)*VALVE_SCALE).fix_1(), mv:Planar64::raw(30)*VALVE_SCALE,
tick_rate:Ratio64::new(100,Time::ONE_SECOND.nanos() as u64).unwrap(), tick_rate:Ratio64::new(100,Time::ONE_SECOND.nanos() as u64).unwrap(),
}), }),
jump:Some(JumpSettings{ jump:Some(JumpSettings{
impulse:JumpImpulse::Height((int(52)*VALVE_SCALE).fix_1()), impulse:JumpImpulse::FromHeight(Planar64::int(52)*VALVE_SCALE),
calculation:JumpCalculation::JumpThenBoost, calculation:JumpCalculation::Linear,
limit_minimum:true,
}), }),
gravity:(int3(0,-800,0)*VALVE_SCALE).fix_1(), gravity:Planar64Vec3::int(0,-800,0)*VALVE_SCALE,
mass:int(1), mass:Planar64::int(1),
rocket:None, rocket:None,
walk:Some(WalkSettings{ walk:Some(WalkSettings{
accelerate:AccelerateSettings{ accelerate:AccelerateSettings{
topspeed:int(18),//? topspeed:Planar64::int(18),//?
accel:int(90),//? accel:Planar64::int(90),//?
}, },
static_friction:int(2),//? static_friction:Planar64::int(2),//?
kinetic_friction:int(3),//? kinetic_friction:Planar64::int(3),//?
surf_dot:int(3)/4,// normal.y=0.75 surf_dot:Planar64::int(3)/4,// normal.y=0.75
}), }),
ladder:Some(LadderSettings{ ladder:Some(LadderSettings{
accelerate:AccelerateSettings{ accelerate:AccelerateSettings{
topspeed:int(18),//? topspeed:Planar64::int(18),//?
accel:int(180),//? accel:Planar64::int(180),//?
}, },
dot:(int(1)/2).sqrt(),//? dot:(Planar64::int(1)/2).sqrt(),//?
}), }),
swim:Some(PropulsionSettings{ swim:Some(PropulsionSettings{
magnitude:int(12),//? magnitude:Planar64::int(12),//?
}), }),
hitbox:Hitbox::source(), hitbox:Hitbox::source(),
camera_offset:((int3(0,64,0)-(int3(0,73,0)>>1))*VALVE_SCALE).fix_1(), camera_offset:(Planar64Vec3::int(0,64,0)-Planar64Vec3::int(0,73,0)/2)*VALVE_SCALE,
} }
} }
pub fn source_surf()->Self{ pub fn source_surf()->Self{
@ -573,39 +557,38 @@ impl StyleModifiers{
controls_mask_state:Controls::all(), controls_mask_state:Controls::all(),
strafe:Some(StrafeSettings{ strafe:Some(StrafeSettings{
enable:ControlsActivation::full_2d(), enable:ControlsActivation::full_2d(),
air_accel_limit:Some((int(150)*66*VALVE_SCALE).fix_1()), air_accel_limit:Some(Planar64::int(150)*66*VALVE_SCALE),
mv:(int(30)*VALVE_SCALE).fix_1(), mv:Planar64::int(30)*VALVE_SCALE,
tick_rate:Ratio64::new(66,Time::ONE_SECOND.nanos() as u64).unwrap(), tick_rate:Ratio64::new(66,Time::ONE_SECOND.nanos() as u64).unwrap(),
}), }),
jump:Some(JumpSettings{ jump:Some(JumpSettings{
impulse:JumpImpulse::Height((int(52)*VALVE_SCALE).fix_1()), impulse:JumpImpulse::FromHeight(Planar64::int(52)*VALVE_SCALE),
calculation:JumpCalculation::JumpThenBoost, calculation:JumpCalculation::Linear,
limit_minimum:true,
}), }),
gravity:(int3(0,-800,0)*VALVE_SCALE).fix_1(), gravity:Planar64Vec3::int(0,-800,0)*VALVE_SCALE,
mass:int(1), mass:Planar64::int(1),
rocket:None, rocket:None,
walk:Some(WalkSettings{ walk:Some(WalkSettings{
accelerate:AccelerateSettings{ accelerate:AccelerateSettings{
topspeed:int(18),//? topspeed:Planar64::int(18),//?
accel:int(90),//? accel:Planar64::int(90),//?
}, },
static_friction:int(2),//? static_friction:Planar64::int(2),//?
kinetic_friction:int(3),//? kinetic_friction:Planar64::int(3),//?
surf_dot:int(3)/4,// normal.y=0.75 surf_dot:Planar64::int(3)/4,// normal.y=0.75
}), }),
ladder:Some(LadderSettings{ ladder:Some(LadderSettings{
accelerate:AccelerateSettings{ accelerate:AccelerateSettings{
topspeed:int(18),//? topspeed:Planar64::int(18),//?
accel:int(180),//? accel:Planar64::int(180),//?
}, },
dot:(int(1)/2).sqrt(),//? dot:(Planar64::int(1)/2).sqrt(),//?
}), }),
swim:Some(PropulsionSettings{ swim:Some(PropulsionSettings{
magnitude:int(12),//? magnitude:Planar64::int(12),//?
}), }),
hitbox:Hitbox::source(), hitbox:Hitbox::source(),
camera_offset:((int3(0,64,0)-(int3(0,73,0)>>1))*VALVE_SCALE).fix_1(), camera_offset:(Planar64Vec3::int(0,64,0)-Planar64Vec3::int(0,73,0)/2)*VALVE_SCALE,
} }
} }
} }

View File

@ -19,14 +19,14 @@ pub struct InstructionCollector<I>{
instruction:Option<I>, instruction:Option<I>,
} }
impl<I> InstructionCollector<I>{ impl<I> InstructionCollector<I>{
pub const fn new(time:Time)->Self{ pub fn new(time:Time)->Self{
Self{ Self{
time, time,
instruction:None instruction:None
} }
} }
#[inline] #[inline]
pub const fn time(&self)->Time{ pub fn time(&self)->Time{
self.time self.time
} }
pub fn collect(&mut self,instruction:Option<TimedInstruction<I>>){ pub fn collect(&mut self,instruction:Option<TimedInstruction<I>>){

View File

@ -1,6 +1,3 @@
pub use fixed_wide::fixed::{Fixed,Fix};
pub use ratio_ops::ratio::{Ratio,Divide};
//integer units //integer units
#[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)] #[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)]
pub struct Time(i64); pub struct Time(i64);
@ -41,26 +38,11 @@ impl Time{
pub const fn nanos(self)->i64{ pub const fn nanos(self)->i64{
self.0 self.0
} }
#[inline]
pub const fn to_ratio(self)->Ratio<Planar64,Planar64>{
Ratio::new(Planar64::raw(self.0),Planar64::raw(1_000_000_000))
}
} }
impl From<Planar64> for Time{ impl From<Planar64> for Time{
#[inline] #[inline]
fn from(value:Planar64)->Self{ fn from(value:Planar64)->Self{
Time((value*Planar64::raw(1_000_000_000)).fix_1().to_raw()) Time((((value.0 as i128)*1_000_000_000)>>32) as i64)
}
}
impl<Num,Den,N1,T1> From<Ratio<Num,Den>> for Time
where
Num:core::ops::Mul<Planar64,Output=N1>,
N1:Divide<Den,Output=T1>,
T1:Fix<Planar64>,
{
#[inline]
fn from(value:Ratio<Num,Den>)->Self{
Time((value*Planar64::raw(1_000_000_000)).divide().fix().to_raw())
} }
} }
impl std::fmt::Display for Time{ impl std::fmt::Display for Time{
@ -81,81 +63,34 @@ impl std::ops::Neg for Time{
Time(-self.0) Time(-self.0)
} }
} }
macro_rules! impl_time_additive_operator { impl std::ops::Add<Time> for Time{
($trait:ty, $method:ident) => {
impl $trait for Time{
type Output=Time; type Output=Time;
#[inline] #[inline]
fn $method(self,rhs:Self)->Self::Output { fn add(self,rhs:Self)->Self::Output {
Time(self.0.$method(rhs.0)) Time(self.0+rhs.0)
} }
}
};
} }
impl_time_additive_operator!(core::ops::Add,add); impl std::ops::Sub<Time> for Time{
impl_time_additive_operator!(core::ops::Sub,sub); type Output=Time;
impl_time_additive_operator!(core::ops::Rem,rem);
macro_rules! impl_time_additive_assign_operator {
($trait:ty, $method:ident) => {
impl $trait for Time{
#[inline] #[inline]
fn $method(&mut self,rhs:Self){ fn sub(self,rhs:Self)->Self::Output {
self.0.$method(rhs.0) Time(self.0-rhs.0)
} }
}
};
} }
impl_time_additive_assign_operator!(core::ops::AddAssign,add_assign); impl std::ops::Mul<Time> for Time{
impl_time_additive_assign_operator!(core::ops::SubAssign,sub_assign); type Output=Time;
impl_time_additive_assign_operator!(core::ops::RemAssign,rem_assign);
impl std::ops::Mul for Time{
type Output=Ratio<fixed_wide::fixed::Fixed<2,64>,fixed_wide::fixed::Fixed<2,64>>;
#[inline] #[inline]
fn mul(self,rhs:Self)->Self::Output{ fn mul(self,rhs:Time)->Self::Output{
Ratio::new(Fixed::raw(self.0)*Fixed::raw(rhs.0),Fixed::raw_digit(1_000_000_000i64.pow(2))) Self((((self.0 as i128)*(rhs.0 as i128))/1_000_000_000) as i64)
} }
} }
impl std::ops::Div<i64> for Time{ impl std::ops::Div<i64> for Time{
type Output=Time; type Output=Time;
#[inline] #[inline]
fn div(self,rhs:i64)->Self::Output{ fn div(self,rhs:i64)->Self::Output {
Time(self.0/rhs) Time(self.0/rhs)
} }
} }
impl std::ops::Mul<i64> for Time{
type Output=Time;
#[inline]
fn mul(self,rhs:i64)->Self::Output{
Time(self.0*rhs)
}
}
impl core::ops::Mul<Time> for Planar64{
type Output=Ratio<Fixed<2,64>,Planar64>;
fn mul(self,rhs:Time)->Self::Output{
Ratio::new(self*Fixed::raw(rhs.0),Planar64::raw(1_000_000_000))
}
}
#[test]
fn time_from_planar64(){
let a:Time=Planar64::from(1).into();
assert_eq!(a,Time::ONE_SECOND);
}
#[test]
fn time_from_ratio(){
let a:Time=Ratio::new(Planar64::from(1),Planar64::from(1)).into();
assert_eq!(a,Time::ONE_SECOND);
}
#[test]
fn time_squared(){
let a=Time::from_secs(2);
assert_eq!(a*a,Ratio::new(Fixed::<2,64>::raw_digit(1_000_000_000i64.pow(2))*4,Fixed::<2,64>::raw_digit(1_000_000_000i64.pow(2))));
}
#[test]
fn time_times_planar64(){
let a=Time::from_secs(2);
let b=Planar64::from(2);
assert_eq!(b*a,Ratio::new(Fixed::<2,64>::raw_digit(1_000_000_000*(1<<32))<<2,Fixed::<1,32>::raw_digit(1_000_000_000)));
}
#[inline] #[inline]
const fn gcd(mut a:u64,mut b:u64)->u64{ const fn gcd(mut a:u64,mut b:u64)->u64{
@ -374,7 +309,6 @@ impl std::ops::Mul<i64> for Ratio64Vec2{
#[derive(Clone,Copy,Hash)] #[derive(Clone,Copy,Hash)]
pub struct Angle32(i32); pub struct Angle32(i32);
impl Angle32{ impl Angle32{
const ANGLE32_TO_FLOAT64_RADIANS:f64=std::f64::consts::PI/((1i64<<31) as f64);
pub const FRAC_PI_2:Self=Self(1<<30); pub const FRAC_PI_2:Self=Self(1<<30);
pub const NEG_FRAC_PI_2:Self=Self(-1<<30); pub const NEG_FRAC_PI_2:Self=Self(-1<<30);
pub const PI:Self=Self(-1<<31); pub const PI:Self=Self(-1<<31);
@ -413,36 +347,24 @@ impl Angle32{
.wrapping_add(midpoint) .wrapping_add(midpoint)
) )
} }
#[inline]
pub fn cos_sin(&self)->(Planar64,Planar64){
/* /*
//cordic #[inline]
let a=self.0 as u32; pub fn cos(&self)->Unit32{
//initialize based on the quadrant //TODO: fix this rounding towards 0
let (mut x,mut y)=match (a&(1<<31)!=0,a&(1<<30)!=0){ Unit32(unsafe{((self.0 as f64*ANGLE32_TO_FLOAT64_RADIANS).cos()*UNIT32_ONE_FLOAT64).to_int_unchecked()})
(false,false)=>( 1i64<<32, 0i64 ),//TR
(false,true )=>( 0i64 , 1i64<<32),//TL
(true ,false)=>(-1i64<<32, 0i64 ),//BL
(true ,true )=>( 0i64 ,-1i64<<32),//BR
};
println!("x={} y={}",Planar64::raw(x),Planar64::raw(y));
for i in 0..30{
if a&(1<<(29-i))!=0{
(x,y)=(x-(y>>i),y+(x>>i));
} }
println!("i={i} t={} x={} y={}",(a&(1<<(29-i))!=0) as u8,Planar64::raw(x),Planar64::raw(y)); #[inline]
pub fn sin(&self)->Unit32{
//TODO: fix this rounding towards 0
Unit32(unsafe{((self.0 as f64*ANGLE32_TO_FLOAT64_RADIANS).sin()*UNIT32_ONE_FLOAT64).to_int_unchecked()})
} }
//don't forget the gain
(Planar64::raw(x),Planar64::raw(y))
*/ */
let (s,c)=(self.0 as f64*Self::ANGLE32_TO_FLOAT64_RADIANS).sin_cos();
(Planar64::raw((c*((1u64<<32) as f64)) as i64),Planar64::raw((s*((1u64<<32) as f64)) as i64))
}
} }
const ANGLE32_TO_FLOAT64_RADIANS:f64=std::f64::consts::PI/((1i64<<31) as f64);
impl Into<f32> for Angle32{ impl Into<f32> for Angle32{
#[inline] #[inline]
fn into(self)->f32{ fn into(self)->f32{
(self.0 as f64*Self::ANGLE32_TO_FLOAT64_RADIANS) as f32 (self.0 as f64*ANGLE32_TO_FLOAT64_RADIANS) as f32
} }
} }
impl std::ops::Neg for Angle32{ impl std::ops::Neg for Angle32{
@ -480,26 +402,6 @@ impl std::ops::Mul<Angle32> for Angle32{
Angle32(self.0.wrapping_mul(rhs.0)) Angle32(self.0.wrapping_mul(rhs.0))
} }
} }
#[test]
fn angle_sin_cos(){
fn close_enough(lhs:Planar64,rhs:Planar64)->bool{
(lhs-rhs).abs()<Planar64::EPSILON*4
}
fn test_angle(f:f64){
let a=Angle32((f/Angle32::ANGLE32_TO_FLOAT64_RADIANS) as i32);
println!("a={:#034b}",a.0);
let (c,s)=a.cos_sin();
let h=(s*s+c*c).sqrt();
println!("cordic s={} c={}",(s/h).divide(),(c/h).divide());
let (fs,fc)=f.sin_cos();
println!("float s={} c={}",fs,fc);
assert!(close_enough((c/h).divide().fix_1(),Planar64::raw((fc*((1u64<<32) as f64)) as i64)));
assert!(close_enough((s/h).divide().fix_1(),Planar64::raw((fs*((1u64<<32) as f64)) as i64)));
}
test_angle(1.0);
test_angle(std::f64::consts::PI/4.0);
test_angle(std::f64::consts::PI/8.0);
}
/* Unit type unused for now, may revive it for map files /* Unit type unused for now, may revive it for map files
///[-1.0,1.0] = [-2^30,2^30] ///[-1.0,1.0] = [-2^30,2^30]
@ -525,140 +427,651 @@ impl TryFrom<[f32;3]> for Unit32Vec3{
} }
*/ */
pub type Planar64TryFromFloatError=fixed_wide::fixed::FixedFromFloatError; ///[-1.0,1.0] = [-2^32,2^32]
pub type Planar64=fixed_wide::types::I32F32; #[derive(Clone,Copy,Debug,Hash,Eq,Ord,PartialEq,PartialOrd)]
pub type Planar64Vec3=linear_ops::types::Vector3<Planar64>; pub struct Planar64(i64);
pub type Planar64Mat3=linear_ops::types::Matrix3<Planar64>; impl Planar64{
pub mod vec3{ pub const ZERO:Self=Self(0);
use super::*; pub const ONE:Self=Self(1<<32);
pub use linear_ops::types::Vector3; pub const MAX:Self=Self(i64::MAX);
pub const MIN:Planar64Vec3=Planar64Vec3::new([Planar64::MIN;3]); pub const MIN:Self=Self(i64::MIN);
pub const MAX:Planar64Vec3=Planar64Vec3::new([Planar64::MAX;3]);
pub const ZERO:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO;3]);
pub const ZERO_2:linear_ops::types::Vector3<Fixed::<2,64>>=linear_ops::types::Vector3::new([Fixed::<2,64>::ZERO;3]);
pub const X:Planar64Vec3=Planar64Vec3::new([Planar64::ONE,Planar64::ZERO,Planar64::ZERO]);
pub const Y:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::ONE,Planar64::ZERO]);
pub const Z:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::ZERO,Planar64::ONE]);
pub const ONE:Planar64Vec3=Planar64Vec3::new([Planar64::ONE,Planar64::ONE,Planar64::ONE]);
pub const NEG_X:Planar64Vec3=Planar64Vec3::new([Planar64::NEG_ONE,Planar64::ZERO,Planar64::ZERO]);
pub const NEG_Y:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::NEG_ONE,Planar64::ZERO]);
pub const NEG_Z:Planar64Vec3=Planar64Vec3::new([Planar64::ZERO,Planar64::ZERO,Planar64::NEG_ONE]);
pub const NEG_ONE:Planar64Vec3=Planar64Vec3::new([Planar64::NEG_ONE,Planar64::NEG_ONE,Planar64::NEG_ONE]);
#[inline] #[inline]
pub const fn int(x:i32,y:i32,z:i32)->Planar64Vec3{ pub const fn int(num:i32)->Self{
Planar64Vec3::new([Planar64::raw((x as i64)<<32),Planar64::raw((y as i64)<<32),Planar64::raw((z as i64)<<32)]) Self(Self::ONE.0*num as i64)
} }
#[inline] #[inline]
pub fn raw_array(array:[i64;3])->Planar64Vec3{ pub const fn raw(num:i64)->Self{
Planar64Vec3::new(array.map(Planar64::raw)) Self(num)
} }
#[inline] #[inline]
pub fn raw_xyz(x:i64,y:i64,z:i64)->Planar64Vec3{ pub const fn get(&self)->i64{
Planar64Vec3::new([Planar64::raw(x),Planar64::raw(y),Planar64::raw(z)]) self.0
} }
#[inline] #[inline]
pub fn try_from_f32_array([x,y,z]:[f32;3])->Result<Planar64Vec3,Planar64TryFromFloatError>{ pub fn sqrt(&self)->Self{
Ok(Planar64Vec3::new([ Planar64(unsafe{(((self.0 as i128)<<32) as f64).sqrt().to_int_unchecked()})
try_from_f32(x)?, }
try_from_f32(y)?, #[inline]
try_from_f32(z)?, pub const fn signum_i64(&self)->i64{
])) ((self.0&(1<<63)!=0) as i64)*2-1
}
}
const PLANAR64_ONE_FLOAT32:f32=(1u64<<32) as f32;
const PLANAR64_CONVERT_TO_FLOAT32:f32=1.0/PLANAR64_ONE_FLOAT32;
const PLANAR64_ONE_FLOAT64:f64=(1u64<<32) as f64;
impl Into<f32> for Planar64{
#[inline]
fn into(self)->f32{
self.0 as f32*PLANAR64_CONVERT_TO_FLOAT32
}
}
impl From<Ratio64> for Planar64{
#[inline]
fn from(ratio:Ratio64)->Self{
Self((((ratio.num as i128)<<32)/(ratio.den as i128)) as i64)
}
}
#[derive(Debug)]
pub enum Planar64TryFromFloatError{
Nan,
Infinite,
Subnormal,
HighlyNegativeExponent,
HighlyPositiveExponent,
}
impl TryFrom<f32> for Planar64{
type Error=Planar64TryFromFloatError;
#[inline]
fn try_from(value:f32)->Result<Self,Self::Error>{
match value.classify(){
std::num::FpCategory::Nan=>Err(Self::Error::Nan),
std::num::FpCategory::Infinite=>Err(Self::Error::Infinite),
std::num::FpCategory::Zero=>Ok(Self::ZERO),
std::num::FpCategory::Subnormal
|std::num::FpCategory::Normal=>{
let planar=value*PLANAR64_ONE_FLOAT32;
if planar<(i64::MIN as f32)||(i64::MAX as f32)<planar{
Err(Self::Error::HighlyPositiveExponent)
}else{
Ok(Planar64(unsafe{planar.to_int_unchecked()}))
}
}
}
}
}
impl TryFrom<f64> for Planar64{
type Error=Planar64TryFromFloatError;
#[inline]
fn try_from(value:f64)->Result<Self,Self::Error>{
match value.classify(){
std::num::FpCategory::Nan=>Err(Self::Error::Nan),
std::num::FpCategory::Infinite=>Err(Self::Error::Infinite),
std::num::FpCategory::Zero=>Ok(Self::ZERO),
std::num::FpCategory::Subnormal
|std::num::FpCategory::Normal=>{
let planar=value*PLANAR64_ONE_FLOAT64;
if planar<(i64::MIN as f64)||(i64::MAX as f64)<planar{
Err(Self::Error::HighlyPositiveExponent)
}else{
Ok(Planar64(unsafe{planar.to_int_unchecked()}))
}
}
}
}
}
impl std::fmt::Display for Planar64{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{:.3}",
Into::<f32>::into(*self),
)
}
}
impl std::ops::Neg for Planar64{
type Output=Planar64;
#[inline]
fn neg(self)->Self::Output{
Planar64(-self.0)
}
}
impl std::ops::Add<Planar64> for Planar64{
type Output=Planar64;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Planar64(self.0+rhs.0)
}
}
impl std::ops::AddAssign<Planar64> for Planar64{
#[inline]
fn add_assign(&mut self,rhs:Self){
*self=*self+rhs;
}
}
impl std::ops::Sub<Planar64> for Planar64{
type Output=Planar64;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Planar64(self.0-rhs.0)
}
}
impl std::ops::Mul<i64> for Planar64{
type Output=Planar64;
#[inline]
fn mul(self, rhs: i64) -> Self::Output {
Planar64(self.0*rhs)
}
}
impl std::ops::Mul<Planar64> for Planar64{
type Output=Planar64;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
Planar64(((self.0 as i128*rhs.0 as i128)>>32) as i64)
}
}
impl std::ops::Mul<Time> for Planar64{
type Output=Planar64;
#[inline]
fn mul(self,rhs:Time)->Self::Output{
Planar64(((self.0 as i128*rhs.0 as i128)/1_000_000_000) as i64)
}
}
impl std::ops::Div<i64> for Planar64{
type Output=Planar64;
#[inline]
fn div(self, rhs: i64) -> Self::Output {
Planar64(self.0/rhs)
}
}
impl std::ops::Div<Planar64> for Planar64{
type Output=Planar64;
#[inline]
fn div(self, rhs: Planar64) -> Self::Output {
Planar64((((self.0 as i128)<<32)/(rhs.0 as i128)) as i64)
}
}
// impl PartialOrd<i64> for Planar64{
// fn partial_cmp(&self, other: &i64) -> Option<std::cmp::Ordering> {
// self.0.partial_cmp(other)
// }
// }
///[-1.0,1.0] = [-2^32,2^32]
#[derive(Clone,Copy,Debug,Default,Hash,Eq,PartialEq)]
pub struct Planar64Vec3(glam::I64Vec3);
impl Planar64Vec3{
pub const ZERO:Self=Planar64Vec3(glam::I64Vec3::ZERO);
pub const ONE:Self=Self::int(1,1,1);
pub const X:Self=Self::int(1,0,0);
pub const Y:Self=Self::int(0,1,0);
pub const Z:Self=Self::int(0,0,1);
pub const NEG_X:Self=Self::int(-1,0,0);
pub const NEG_Y:Self=Self::int(0,-1,0);
pub const NEG_Z:Self=Self::int(0,0,-1);
pub const MIN:Self=Planar64Vec3(glam::I64Vec3::MIN);
pub const MAX:Self=Planar64Vec3(glam::I64Vec3::MAX);
#[inline]
pub const fn new(x:Planar64,y:Planar64,z:Planar64)->Self{
Self(glam::i64vec3(x.0,y.0,z.0))
}
#[inline]
pub const fn get(self)->glam::I64Vec3{
self.0
}
#[inline]
pub const fn int(x:i32,y:i32,z:i32)->Self{
Self(glam::i64vec3((x as i64)<<32,(y as i64)<<32,(z as i64)<<32))
}
#[inline]
pub const fn raw_xyz(x:i64,y:i64,z:i64)->Self{
Self(glam::i64vec3(x,y,z))
}
#[inline]
pub const fn raw_array(xyz:[i64;3])->Self{
Self(glam::I64Vec3::from_array(xyz))
}
#[inline]
pub const fn raw(xyz:glam::I64Vec3)->Self{
Self(xyz)
}
#[inline]
pub const fn x(&self)->Planar64{
Planar64(self.0.x)
}
#[inline]
pub const fn y(&self)->Planar64{
Planar64(self.0.y)
}
#[inline]
pub const fn z(&self)->Planar64{
Planar64(self.0.z)
}
#[inline]
pub fn min(&self,rhs:Self)->Self{
Self(glam::i64vec3(
self.0.x.min(rhs.0.x),
self.0.y.min(rhs.0.y),
self.0.z.min(rhs.0.z),
))
}
#[inline]
pub fn max(&self,rhs:Self)->Self{
Self(glam::i64vec3(
self.0.x.max(rhs.0.x),
self.0.y.max(rhs.0.y),
self.0.z.max(rhs.0.z),
))
}
#[inline]
pub fn midpoint(&self,rhs:Self)->Self{
Self((self.0+rhs.0)/2)
}
#[inline]
pub fn cmplt(&self,rhs:Self)->glam::BVec3{
self.0.cmplt(rhs.0)
}
#[inline]
pub const fn dot(&self,rhs:Self)->Planar64{
Planar64(((
(self.0.x as i128)*(rhs.0.x as i128)+
(self.0.y as i128)*(rhs.0.y as i128)+
(self.0.z as i128)*(rhs.0.z as i128)
)>>32) as i64)
}
#[inline]
pub const fn dot128(&self,rhs:Self)->i128{
(self.0.x as i128)*(rhs.0.x as i128)+
(self.0.y as i128)*(rhs.0.y as i128)+
(self.0.z as i128)*(rhs.0.z as i128)
}
#[inline]
pub const fn cross(&self,rhs:Self)->Planar64Vec3{
Planar64Vec3(glam::i64vec3(
(((self.0.y as i128)*(rhs.0.z as i128)-(self.0.z as i128)*(rhs.0.y as i128))>>32) as i64,
(((self.0.z as i128)*(rhs.0.x as i128)-(self.0.x as i128)*(rhs.0.z as i128))>>32) as i64,
(((self.0.x as i128)*(rhs.0.y as i128)-(self.0.y as i128)*(rhs.0.x as i128))>>32) as i64,
))
}
#[inline]
pub fn length(&self)->Planar64{
let radicand=(self.0.x as i128)*(self.0.x as i128)+(self.0.y as i128)*(self.0.y as i128)+(self.0.z as i128)*(self.0.z as i128);
Planar64(unsafe{(radicand as f64).sqrt().to_int_unchecked()})
}
#[inline]
pub fn with_length(&self,length:Planar64)->Self{
let radicand=(self.0.x as i128)*(self.0.x as i128)+(self.0.y as i128)*(self.0.y as i128)+(self.0.z as i128)*(self.0.z as i128);
let self_length:i128=unsafe{(radicand as f64).sqrt().to_int_unchecked()};
//self.0*length/self_length
Planar64Vec3(
glam::i64vec3(
((self.0.x as i128)*(length.0 as i128)/self_length) as i64,
((self.0.y as i128)*(length.0 as i128)/self_length) as i64,
((self.0.z as i128)*(length.0 as i128)/self_length) as i64,
)
)
}
}
impl Into<glam::Vec3> for Planar64Vec3{
#[inline]
fn into(self)->glam::Vec3{
glam::vec3(
self.0.x as f32,
self.0.y as f32,
self.0.z as f32,
)*PLANAR64_CONVERT_TO_FLOAT32
}
}
impl TryFrom<[f32;3]> for Planar64Vec3{
type Error=Planar64TryFromFloatError;
#[inline]
fn try_from(value:[f32;3])->Result<Self,Self::Error>{
Ok(Self(glam::i64vec3(
Planar64::try_from(value[0])?.0,
Planar64::try_from(value[1])?.0,
Planar64::try_from(value[2])?.0,
)))
}
}
impl TryFrom<glam::Vec3A> for Planar64Vec3{
type Error=Planar64TryFromFloatError;
#[inline]
fn try_from(value:glam::Vec3A)->Result<Self,Self::Error>{
Ok(Self(glam::i64vec3(
Planar64::try_from(value.x)?.0,
Planar64::try_from(value.y)?.0,
Planar64::try_from(value.z)?.0,
)))
}
}
impl std::fmt::Display for Planar64Vec3{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{:.3},{:.3},{:.3}",
Into::<f32>::into(self.x()),Into::<f32>::into(self.y()),Into::<f32>::into(self.z()),
)
}
}
impl std::ops::Neg for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn neg(self)->Self::Output{
Planar64Vec3(-self.0)
}
}
impl std::ops::Add<Planar64Vec3> for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn add(self,rhs:Planar64Vec3) -> Self::Output {
Planar64Vec3(self.0+rhs.0)
}
}
impl std::ops::AddAssign<Planar64Vec3> for Planar64Vec3{
#[inline]
fn add_assign(&mut self,rhs:Planar64Vec3){
*self=*self+rhs
}
}
impl std::ops::Sub<Planar64Vec3> for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn sub(self,rhs:Planar64Vec3) -> Self::Output {
Planar64Vec3(self.0-rhs.0)
}
}
impl std::ops::SubAssign<Planar64Vec3> for Planar64Vec3{
#[inline]
fn sub_assign(&mut self,rhs:Planar64Vec3){
*self=*self-rhs
}
}
impl std::ops::Mul<Planar64Vec3> for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn mul(self, rhs: Planar64Vec3) -> Self::Output {
Planar64Vec3(glam::i64vec3(
(((self.0.x as i128)*(rhs.0.x as i128))>>32) as i64,
(((self.0.y as i128)*(rhs.0.y as i128))>>32) as i64,
(((self.0.z as i128)*(rhs.0.z as i128))>>32) as i64
))
}
}
impl std::ops::Mul<Planar64> for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn mul(self, rhs: Planar64) -> Self::Output {
Planar64Vec3(glam::i64vec3(
(((self.0.x as i128)*(rhs.0 as i128))>>32) as i64,
(((self.0.y as i128)*(rhs.0 as i128))>>32) as i64,
(((self.0.z as i128)*(rhs.0 as i128))>>32) as i64
))
}
}
impl std::ops::Mul<i64> for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn mul(self,rhs:i64)->Self::Output {
Planar64Vec3(glam::i64vec3(
self.0.x*rhs,
self.0.y*rhs,
self.0.z*rhs
))
}
}
impl std::ops::Mul<Time> for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn mul(self,rhs:Time)->Self::Output{
Planar64Vec3(glam::i64vec3(
(((self.0.x as i128)*(rhs.0 as i128))/1_000_000_000) as i64,
(((self.0.y as i128)*(rhs.0 as i128))/1_000_000_000) as i64,
(((self.0.z as i128)*(rhs.0 as i128))/1_000_000_000) as i64
))
}
}
impl std::ops::Div<Planar64> for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn div(self,rhs:Planar64)->Self::Output{
Planar64Vec3(glam::i64vec3(
(((self.0.x as i128)<<32)/(rhs.0 as i128)) as i64,
(((self.0.y as i128)<<32)/(rhs.0 as i128)) as i64,
(((self.0.z as i128)<<32)/(rhs.0 as i128)) as i64,
))
}
}
impl std::ops::Div<i64> for Planar64Vec3{
type Output=Planar64Vec3;
#[inline]
fn div(self,rhs:i64)->Self::Output{
Planar64Vec3(glam::i64vec3(
self.0.x/rhs,
self.0.y/rhs,
self.0.z/rhs,
))
} }
} }
#[inline] ///[-1.0,1.0] = [-2^32,2^32]
pub fn int(value:i32)->Planar64{ #[derive(Clone,Copy,Hash,Eq,PartialEq)]
Planar64::from(value) pub struct Planar64Mat3{
pub x_axis:Planar64Vec3,
pub y_axis:Planar64Vec3,
pub z_axis:Planar64Vec3,
} }
#[inline] impl Default for Planar64Mat3{
pub fn try_from_f32(value:f32)->Result<Planar64,Planar64TryFromFloatError>{ #[inline]
let result:Result<Planar64,_>=value.try_into(); fn default() -> Self {
match result{ Self{
Ok(ok)=>Ok(ok), x_axis:Planar64Vec3::X,
Err(e)=>e.underflow_to_zero(), y_axis:Planar64Vec3::Y,
z_axis:Planar64Vec3::Z,
}
} }
} }
pub mod mat3{ impl Planar64Mat3{
use super::*;
pub use linear_ops::types::Matrix3;
#[inline] #[inline]
pub const fn identity()->Planar64Mat3{ pub const fn from_cols(x_axis:Planar64Vec3,y_axis:Planar64Vec3,z_axis:Planar64Vec3)->Self{
Planar64Mat3::new([ Self{
[Planar64::ONE,Planar64::ZERO,Planar64::ZERO], x_axis,
[Planar64::ZERO,Planar64::ONE,Planar64::ZERO], y_axis,
[Planar64::ZERO,Planar64::ZERO,Planar64::ONE], z_axis,
]) }
}
pub const fn int_from_cols_array(array:[i32;9])->Self{
Self{
x_axis:Planar64Vec3::int(array[0],array[1],array[2]),
y_axis:Planar64Vec3::int(array[3],array[4],array[5]),
z_axis:Planar64Vec3::int(array[6],array[7],array[8]),
}
} }
#[inline] #[inline]
pub fn from_diagonal(diag:Planar64Vec3)->Planar64Mat3{ pub const fn from_diagonal(diagonal:Planar64Vec3)->Self{
Planar64Mat3::new([ Self{
[diag.x,Planar64::ZERO,Planar64::ZERO], x_axis:Planar64Vec3::raw_xyz(diagonal.0.x,0,0),
[Planar64::ZERO,diag.y,Planar64::ZERO], y_axis:Planar64Vec3::raw_xyz(0,diagonal.0.y,0),
[Planar64::ZERO,Planar64::ZERO,diag.z], z_axis:Planar64Vec3::raw_xyz(0,0,diagonal.0.z),
]) }
} }
#[inline] #[inline]
pub fn from_rotation_yx(x:Angle32,y:Angle32)->Planar64Mat3{ pub fn from_rotation_yx(yaw:Angle32,pitch:Angle32)->Self{
let (xc,xs)=x.cos_sin(); let xtheta=yaw.0 as f64*ANGLE32_TO_FLOAT64_RADIANS;
let (yc,ys)=y.cos_sin(); let (xs,xc)=xtheta.sin_cos();
Planar64Mat3::from_cols([ let (xc,xs)=(xc*PLANAR64_ONE_FLOAT64,xs*PLANAR64_ONE_FLOAT64);
Planar64Vec3::new([xc,Planar64::ZERO,-xs]), let ytheta=pitch.0 as f64*ANGLE32_TO_FLOAT64_RADIANS;
Planar64Vec3::new([(xs*ys).fix_1(),yc,(xc*ys).fix_1()]), let (ys,yc)=ytheta.sin_cos();
Planar64Vec3::new([(xs*yc).fix_1(),-ys,(xc*yc).fix_1()]), let (yc,ys)=(yc*PLANAR64_ONE_FLOAT64,ys*PLANAR64_ONE_FLOAT64);
]) //TODO: fix this rounding towards 0
let (xc,xs):(i64,i64)=(unsafe{xc.to_int_unchecked()},unsafe{xs.to_int_unchecked()});
let (yc,ys):(i64,i64)=(unsafe{yc.to_int_unchecked()},unsafe{ys.to_int_unchecked()});
Self::from_cols(
Planar64Vec3(glam::i64vec3(xc,0,-xs)),
Planar64Vec3(glam::i64vec3(((xs as i128*ys as i128)>>32) as i64,yc,((xc as i128*ys as i128)>>32) as i64)),
Planar64Vec3(glam::i64vec3(((xs as i128*yc as i128)>>32) as i64,-ys,((xc as i128*yc as i128)>>32) as i64)),
)
} }
#[inline] #[inline]
pub fn from_rotation_y(y:Angle32)->Planar64Mat3{ pub fn from_rotation_y(angle:Angle32)->Self{
let (c,s)=y.cos_sin(); let theta=angle.0 as f64*ANGLE32_TO_FLOAT64_RADIANS;
Planar64Mat3::from_cols([ let (s,c)=theta.sin_cos();
Planar64Vec3::new([c,Planar64::ZERO,-s]), let (c,s)=(c*PLANAR64_ONE_FLOAT64,s*PLANAR64_ONE_FLOAT64);
vec3::Y, //TODO: fix this rounding towards 0
Planar64Vec3::new([s,Planar64::ZERO,c]), let (c,s):(i64,i64)=(unsafe{c.to_int_unchecked()},unsafe{s.to_int_unchecked()});
]) Self::from_cols(
Planar64Vec3(glam::i64vec3(c,0,-s)),
Planar64Vec3::Y,
Planar64Vec3(glam::i64vec3(s,0,c)),
)
} }
#[inline] #[inline]
pub fn try_from_f32_array_2d([x_axis,y_axis,z_axis]:[[f32;3];3])->Result<Planar64Mat3,Planar64TryFromFloatError>{ pub const fn inverse(&self)->Self{
Ok(Planar64Mat3::new([ let det=(
vec3::try_from_f32_array(x_axis)?.to_array(), -self.x_axis.0.z as i128*self.y_axis.0.y as i128*self.z_axis.0.x as i128
vec3::try_from_f32_array(y_axis)?.to_array(), +self.x_axis.0.y as i128*self.y_axis.0.z as i128*self.z_axis.0.x as i128
vec3::try_from_f32_array(z_axis)?.to_array(), +self.x_axis.0.z as i128*self.y_axis.0.x as i128*self.z_axis.0.y as i128
])) -self.x_axis.0.x as i128*self.y_axis.0.z as i128*self.z_axis.0.y as i128
-self.x_axis.0.y as i128*self.y_axis.0.x as i128*self.z_axis.0.z as i128
+self.x_axis.0.x as i128*self.y_axis.0.y as i128*self.z_axis.0.z as i128
)>>32;
Self{
x_axis:Planar64Vec3::raw_xyz((((-(self.y_axis.0.z as i128*self.z_axis.0.y as i128)+self.y_axis.0.y as i128*self.z_axis.0.z as i128)<<32)/det) as i64,(((self.x_axis.0.z as i128*self.z_axis.0.y as i128-self.x_axis.0.y as i128*self.z_axis.0.z as i128)<<32)/det) as i64,(((-(self.x_axis.0.z as i128*self.y_axis.0.y as i128)+self.x_axis.0.y as i128*self.y_axis.0.z as i128)<<32)/det) as i64),
y_axis:Planar64Vec3::raw_xyz((((self.y_axis.0.z as i128*self.z_axis.0.x as i128-self.y_axis.0.x as i128*self.z_axis.0.z as i128)<<32)/det) as i64,(((-(self.x_axis.0.z as i128*self.z_axis.0.x as i128)+self.x_axis.0.x as i128*self.z_axis.0.z as i128)<<32)/det) as i64,(((self.x_axis.0.z as i128*self.y_axis.0.x as i128-self.x_axis.0.x as i128*self.y_axis.0.z as i128)<<32)/det) as i64),
z_axis:Planar64Vec3::raw_xyz((((-(self.y_axis.0.y as i128*self.z_axis.0.x as i128)+self.y_axis.0.x as i128*self.z_axis.0.y as i128)<<32)/det) as i64,(((self.x_axis.0.y as i128*self.z_axis.0.x as i128-self.x_axis.0.x as i128*self.z_axis.0.y as i128)<<32)/det) as i64,(((-(self.x_axis.0.y as i128*self.y_axis.0.x as i128)+self.x_axis.0.x as i128*self.y_axis.0.y as i128)<<32)/det) as i64),
}
}
#[inline]
pub const fn inverse_times_det(&self)->Self{
Self{
x_axis:Planar64Vec3::raw_xyz(((-(self.y_axis.0.z as i128*self.z_axis.0.y as i128)+self.y_axis.0.y as i128*self.z_axis.0.z as i128)>>32) as i64,((self.x_axis.0.z as i128*self.z_axis.0.y as i128-self.x_axis.0.y as i128*self.z_axis.0.z as i128)>>32) as i64,((-(self.x_axis.0.z as i128*self.y_axis.0.y as i128)+self.x_axis.0.y as i128*self.y_axis.0.z as i128)>>32) as i64),
y_axis:Planar64Vec3::raw_xyz(((self.y_axis.0.z as i128*self.z_axis.0.x as i128-self.y_axis.0.x as i128*self.z_axis.0.z as i128)>>32) as i64,((-(self.x_axis.0.z as i128*self.z_axis.0.x as i128)+self.x_axis.0.x as i128*self.z_axis.0.z as i128)>>32) as i64,((self.x_axis.0.z as i128*self.y_axis.0.x as i128-self.x_axis.0.x as i128*self.y_axis.0.z as i128)>>32) as i64),
z_axis:Planar64Vec3::raw_xyz(((-(self.y_axis.0.y as i128*self.z_axis.0.x as i128)+self.y_axis.0.x as i128*self.z_axis.0.y as i128)>>32) as i64,((self.x_axis.0.y as i128*self.z_axis.0.x as i128-self.x_axis.0.x as i128*self.z_axis.0.y as i128)>>32) as i64,((-(self.x_axis.0.y as i128*self.y_axis.0.x as i128)+self.x_axis.0.x as i128*self.y_axis.0.y as i128)>>32) as i64),
}
}
#[inline]
pub const fn transpose(&self)->Self{
Self{
x_axis:Planar64Vec3::raw_xyz(self.x_axis.0.x,self.y_axis.0.x,self.z_axis.0.x),
y_axis:Planar64Vec3::raw_xyz(self.x_axis.0.y,self.y_axis.0.y,self.z_axis.0.y),
z_axis:Planar64Vec3::raw_xyz(self.x_axis.0.z,self.y_axis.0.z,self.z_axis.0.z),
}
}
#[inline]
pub const fn determinant(&self)->Planar64{
Planar64(((
-self.x_axis.0.z as i128*self.y_axis.0.y as i128*self.z_axis.0.x as i128
+self.x_axis.0.y as i128*self.y_axis.0.z as i128*self.z_axis.0.x as i128
+self.x_axis.0.z as i128*self.y_axis.0.x as i128*self.z_axis.0.y as i128
-self.x_axis.0.x as i128*self.y_axis.0.z as i128*self.z_axis.0.y as i128
-self.x_axis.0.y as i128*self.y_axis.0.x as i128*self.z_axis.0.z as i128
+self.x_axis.0.x as i128*self.y_axis.0.y as i128*self.z_axis.0.z as i128
)>>64) as i64)
}
}
impl Into<glam::Mat3> for Planar64Mat3{
#[inline]
fn into(self)->glam::Mat3{
glam::Mat3::from_cols(
self.x_axis.into(),
self.y_axis.into(),
self.z_axis.into(),
)
}
}
impl TryFrom<glam::Mat3A> for Planar64Mat3{
type Error=Planar64TryFromFloatError;
#[inline]
fn try_from(value:glam::Mat3A)->Result<Self,Self::Error>{
Ok(Self{
x_axis:Planar64Vec3::try_from(value.x_axis)?,
y_axis:Planar64Vec3::try_from(value.y_axis)?,
z_axis:Planar64Vec3::try_from(value.z_axis)?,
})
}
}
impl std::fmt::Display for Planar64Mat3{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"\n{:.3},{:.3},{:.3}\n{:.3},{:.3},{:.3}\n{:.3},{:.3},{:.3}",
Into::<f32>::into(self.x_axis.x()),Into::<f32>::into(self.x_axis.y()),Into::<f32>::into(self.x_axis.z()),
Into::<f32>::into(self.y_axis.x()),Into::<f32>::into(self.y_axis.y()),Into::<f32>::into(self.y_axis.z()),
Into::<f32>::into(self.z_axis.x()),Into::<f32>::into(self.z_axis.y()),Into::<f32>::into(self.z_axis.z()),
)
}
}
impl std::ops::Mul<Planar64Vec3> for Planar64Mat3{
type Output=Planar64Vec3;
#[inline]
fn mul(self,rhs:Planar64Vec3) -> Self::Output {
self.x_axis*rhs.x()
+self.y_axis*rhs.y()
+self.z_axis*rhs.z()
}
}
impl std::ops::Div<i64> for Planar64Mat3{
type Output=Planar64Mat3;
#[inline]
fn div(self,rhs:i64)->Self::Output{
Planar64Mat3{
x_axis:self.x_axis/rhs,
y_axis:self.y_axis/rhs,
z_axis:self.z_axis/rhs,
}
} }
} }
///[-1.0,1.0] = [-2^32,2^32]
#[derive(Clone,Copy,Default,Hash,Eq,PartialEq)] #[derive(Clone,Copy,Default,Hash,Eq,PartialEq)]
pub struct Planar64Affine3{ pub struct Planar64Affine3{
pub matrix3:Planar64Mat3,//includes scale above 1 pub matrix3:Planar64Mat3,//includes scale above 1
pub translation:Planar64Vec3, pub translation:Planar64Vec3,
} }
impl Planar64Affine3{ impl Planar64Affine3{
#[inline] #[inline]
pub const fn new(matrix3:Planar64Mat3,translation:Planar64Vec3)->Self{ pub fn new(matrix3:Planar64Mat3,translation:Planar64Vec3)->Self{
Self{matrix3,translation} Self{matrix3,translation}
} }
#[inline] #[inline]
pub fn transform_point3(&self,point:Planar64Vec3)->vec3::Vector3<Fixed<2,64>>{ pub fn transform_point3(&self,point:Planar64Vec3) -> Planar64Vec3{
self.translation.fix_2()+self.matrix3*point Planar64Vec3(
self.translation.0
+(self.matrix3.x_axis*point.x()).0
+(self.matrix3.y_axis*point.y()).0
+(self.matrix3.z_axis*point.z()).0
)
} }
} }
impl Into<glam::Mat4> for Planar64Affine3{ impl Into<glam::Mat4> for Planar64Affine3{
#[inline] #[inline]
fn into(self)->glam::Mat4{ fn into(self)->glam::Mat4{
let matrix3=self.matrix3.to_array().map(|row|row.map(Into::<f32>::into));
let translation=self.translation.to_array().map(Into::<f32>::into);
glam::Mat4::from_cols_array(&[ glam::Mat4::from_cols_array(&[
matrix3[0][0],matrix3[0][1],matrix3[0][2],0.0, self.matrix3.x_axis.0.x as f32,self.matrix3.x_axis.0.y as f32,self.matrix3.x_axis.0.z as f32,0.0,
matrix3[1][0],matrix3[1][1],matrix3[1][2],0.0, self.matrix3.y_axis.0.x as f32,self.matrix3.y_axis.0.y as f32,self.matrix3.y_axis.0.z as f32,0.0,
matrix3[2][0],matrix3[2][1],matrix3[2][2],0.0, self.matrix3.z_axis.0.x as f32,self.matrix3.z_axis.0.y as f32,self.matrix3.z_axis.0.z as f32,0.0,
translation[0],translation[1],translation[2],1.0 self.translation.0.x as f32,self.translation.0.y as f32,self.translation.0.z as f32,PLANAR64_ONE_FLOAT32
]) ])*PLANAR64_CONVERT_TO_FLOAT32
}
}
impl TryFrom<glam::Affine3A> for Planar64Affine3{
type Error=Planar64TryFromFloatError;
fn try_from(value: glam::Affine3A)->Result<Self, Self::Error> {
Ok(Self{
matrix3:Planar64Mat3::try_from(value.matrix3)?,
translation:Planar64Vec3::try_from(value.translation)?
})
}
}
impl std::fmt::Display for Planar64Affine3{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"translation: {:.3},{:.3},{:.3}\nmatrix3:\n{:.3},{:.3},{:.3}\n{:.3},{:.3},{:.3}\n{:.3},{:.3},{:.3}",
Into::<f32>::into(self.translation.x()),Into::<f32>::into(self.translation.y()),Into::<f32>::into(self.translation.z()),
Into::<f32>::into(self.matrix3.x_axis.x()),Into::<f32>::into(self.matrix3.x_axis.y()),Into::<f32>::into(self.matrix3.x_axis.z()),
Into::<f32>::into(self.matrix3.y_axis.x()),Into::<f32>::into(self.matrix3.y_axis.y()),Into::<f32>::into(self.matrix3.y_axis.z()),
Into::<f32>::into(self.matrix3.z_axis.x()),Into::<f32>::into(self.matrix3.z_axis.y()),Into::<f32>::into(self.matrix3.z_axis.z()),
)
} }
} }
#[test] #[test]
fn test_sqrt(){ fn test_sqrt(){
let r=int(400); let r=Planar64::int(400);
assert_eq!(r,Planar64::raw(1717986918400)); assert_eq!(1717986918400,r.get());
let s=r.sqrt(); let s=r.sqrt();
assert_eq!(s,Planar64::raw(85899345920)); assert_eq!(85899345920,s.get());
} }

View File

@ -1,12 +1,10 @@
pub mod bvh; pub mod bvh;
pub mod map; pub mod map;
pub mod run;
pub mod aabb; pub mod aabb;
pub mod model; pub mod model;
pub mod mouse;
pub mod timer; pub mod timer;
pub mod zeroes;
pub mod integer; pub mod integer;
pub mod physics;
pub mod updatable; pub mod updatable;
pub mod instruction; pub mod instruction;
pub mod gameplay_attributes; pub mod gameplay_attributes;

View File

@ -1,26 +0,0 @@
use crate::integer::Time;
#[derive(Clone,Debug)]
pub struct MouseState{
pub pos:glam::IVec2,
pub time:Time,
}
impl Default for MouseState{
fn default()->Self{
Self{
time:Time::ZERO,
pos:glam::IVec2::ZERO,
}
}
}
impl MouseState{
pub fn lerp(&self,target:&MouseState,time:Time)->glam::IVec2{
let m0=self.pos.as_i64vec2();
let m1=target.pos.as_i64vec2();
//these are deltas
let t1t=(target.time-time).nanos();
let tt0=(time-self.time).nanos();
let dt=(target.time-self.time).nanos();
((m0*t1t+m1*tt0)/dt).as_ivec2()
}
}

View File

@ -1,27 +0,0 @@
#[derive(Clone,Debug)]
pub enum Instruction{
ReplaceMouse(crate::mouse::MouseState,crate::mouse::MouseState),
SetNextMouse(crate::mouse::MouseState),
SetMoveRight(bool),
SetMoveUp(bool),
SetMoveBack(bool),
SetMoveLeft(bool),
SetMoveDown(bool),
SetMoveForward(bool),
SetJump(bool),
SetZoom(bool),
/// Reset: fully replace the physics state.
/// This forgets all inputs and settings which need to be reapplied.
Reset,
/// Restart: Teleport to the start zone.
Restart,
/// Spawn: Teleport to a specific mode's spawn
/// Sets current mode & spawn
Spawn(crate::gameplay_modes::ModeId,crate::gameplay_modes::StageId),
Idle,
//Idle: there were no input events, but the simulation is safe to advance to this timestep
//for interpolation / networking / playback reasons, most playback heads will always want
//to be 1 instruction ahead to generate the next state for interpolation.
PracticeFly,
SetSensitivity(crate::integer::Ratio64Vec2),
}

View File

@ -1,103 +0,0 @@
use crate::timer::{TimerFixed,Realtime,Paused,Unpaused};
use crate::integer::Time;
#[derive(Clone,Copy,Debug)]
pub enum FlagReason{
Anticheat,
StyleChange,
Clock,
Pause,
Flying,
Gravity,
Timescale,
TimeTravel,
Teleport,
}
impl ToString for FlagReason{
fn to_string(&self)->String{
match self{
FlagReason::Anticheat=>"Passed through anticheat zone.",
FlagReason::StyleChange=>"Changed style.",
FlagReason::Clock=>"Incorrect clock. (This can be caused by internet hiccups)",
FlagReason::Pause=>"Pausing is not allowed in this style.",
FlagReason::Flying=>"Flying is not allowed in this style.",
FlagReason::Gravity=>"Gravity modification is not allowed in this style.",
FlagReason::Timescale=>"Timescale is not allowed in this style.",
FlagReason::TimeTravel=>"Time travel is not allowed in this style.",
FlagReason::Teleport=>"Illegal teleport.",
}.to_owned()
}
}
#[derive(Debug)]
pub enum Error{
NotStarted,
AlreadyStarted,
AlreadyFinished,
}
impl std::fmt::Display for Error{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for Error{}
#[derive(Clone,Copy,Debug)]
enum RunState{
Created,
Started{timer:TimerFixed<Realtime,Unpaused>},
Finished{timer:TimerFixed<Realtime,Paused>},
}
#[derive(Clone,Copy,Debug)]
pub struct Run{
state:RunState,
flagged:Option<FlagReason>,
}
impl Run{
pub fn new()->Self{
Self{
state:RunState::Created,
flagged:None,
}
}
pub fn time(&self,time:Time)->Time{
match &self.state{
RunState::Created=>Time::ZERO,
RunState::Started{timer}=>timer.time(time),
RunState::Finished{timer}=>timer.time(time),
}
}
pub fn start(&mut self,time:Time)->Result<(),Error>{
match &self.state{
RunState::Created=>{
self.state=RunState::Started{
timer:TimerFixed::new(time,Time::ZERO),
};
Ok(())
},
RunState::Started{..}=>Err(Error::AlreadyStarted),
RunState::Finished{..}=>Err(Error::AlreadyFinished),
}
}
pub fn finish(&mut self,time:Time)->Result<(),Error>{
//this uses Copy
match &self.state{
RunState::Created=>Err(Error::NotStarted),
RunState::Started{timer}=>{
self.state=RunState::Finished{
timer:timer.into_paused(time),
};
Ok(())
},
RunState::Finished{..}=>Err(Error::AlreadyFinished),
}
}
pub fn flag(&mut self,flag_reason:FlagReason){
//don't replace the first reason the run was flagged
if self.flagged.is_none(){
self.flagged=Some(flag_reason);
}
}
}

View File

@ -1,35 +1,15 @@
use crate::integer::{Time,Ratio64}; use crate::integer::{Time,Ratio64};
#[derive(Clone,Copy,Debug)] //this could be about half as long if I only had
pub struct Paused; //scaled timers and just used a scale of 1
#[derive(Clone,Copy,Debug)] //but I thought the concept of a timer that could
pub struct Unpaused; //only be paused and not scaled was cool
pub trait PauseState:Copy+std::fmt::Debug{ trait TimerState:Copy{
const IS_PAUSED:bool; fn get_time(&self,time:Time)->Time;
fn new()->Self; fn set_time(&mut self,time:Time,new_time:Time);
} fn get_offset(&self)->Time;
impl PauseState for Paused{ fn set_offset(&mut self,offset:Time);
const IS_PAUSED:bool=true;
fn new()->Self{
Self
}
}
impl PauseState for Unpaused{
const IS_PAUSED:bool=false;
fn new()->Self{
Self
}
}
#[derive(Clone,Copy,Debug)]
pub struct Realtime{
offset:Time,
}
impl Realtime{
pub const fn new(offset:Time)->Self{
Self{offset}
}
} }
#[derive(Clone,Copy,Debug)] #[derive(Clone,Copy,Debug)]
@ -38,8 +18,8 @@ pub struct Scaled{
offset:Time, offset:Time,
} }
impl Scaled{ impl Scaled{
pub const fn new(scale:Ratio64,offset:Time)->Self{ const fn identity()->Self{
Self{scale,offset} Self{scale:Ratio64::ONE,offset:Time::ZERO}
} }
const fn with_scale(scale:Ratio64)->Self{ const fn with_scale(scale:Ratio64)->Self{
Self{scale,offset:Time::ZERO} Self{scale,offset:Time::ZERO}
@ -56,35 +36,7 @@ impl Scaled{
self.set_time(time,new_time); self.set_time(time,new_time);
} }
} }
pub trait TimerState:Copy+std::fmt::Debug{
fn identity()->Self;
fn get_time(&self,time:Time)->Time;
fn set_time(&mut self,time:Time,new_time:Time);
fn get_offset(&self)->Time;
fn set_offset(&mut self,offset:Time);
}
impl TimerState for Realtime{
fn identity()->Self{
Self{offset:Time::ZERO}
}
fn get_time(&self,time:Time)->Time{
time+self.offset
}
fn set_time(&mut self,time:Time,new_time:Time){
self.offset=new_time-time;
}
fn get_offset(&self)->Time{
self.offset
}
fn set_offset(&mut self,offset:Time){
self.offset=offset;
}
}
impl TimerState for Scaled{ impl TimerState for Scaled{
fn identity()->Self{
Self{scale:Ratio64::ONE,offset:Time::ZERO}
}
fn get_time(&self,time:Time)->Time{ fn get_time(&self,time:Time)->Time{
self.scale(time)+self.offset self.scale(time)+self.offset
} }
@ -100,82 +52,48 @@ impl TimerState for Scaled{
} }
#[derive(Clone,Copy,Debug)] #[derive(Clone,Copy,Debug)]
pub struct TimerFixed<T:TimerState,P:PauseState>{ pub struct Realtime{
offset:Time,
}
impl Realtime{
const fn identity()->Self{
Self{offset:Time::ZERO}
}
}
impl TimerState for Realtime{
fn get_time(&self,time:Time)->Time{
time+self.offset
}
fn set_time(&mut self,time:Time,new_time:Time){
self.offset=new_time-time;
}
fn get_offset(&self)->Time{
self.offset
}
fn set_offset(&mut self,offset:Time){
self.offset=offset;
}
}
#[derive(Clone,Debug)]
pub struct Timer<T>{
state:T, state:T,
_paused:P, paused:bool,
} }
//scaled timer methods are generic across PauseState impl Timer<Realtime>{
impl<P:PauseState> TimerFixed<Scaled,P>{ pub fn realtime(time:Time,new_time:Time)->Self{
pub fn scaled(time:Time,new_time:Time,scale:Ratio64)->Self{
let mut timer=Self{ let mut timer=Self{
state:Scaled::with_scale(scale), state:Realtime::identity(),
_paused:P::new(), paused:false,
}; };
timer.set_time(time,new_time); timer.set_time(time,new_time);
timer timer
} }
pub const fn get_scale(&self)->Ratio64{ pub fn realtime_paused(offset:Time)->Self{
self.state.get_scale()
}
pub fn set_scale(&mut self,time:Time,new_scale:Ratio64){
self.state.set_scale(time,new_scale)
}
}
//pause and unpause is generic across TimerState
impl<T:TimerState> TimerFixed<T,Paused>{
pub fn into_unpaused(self,time:Time)->TimerFixed<T,Unpaused>{
let new_time=self.time(time);
let mut timer=TimerFixed{
state:self.state,
_paused:Unpaused,
};
timer.set_time(time,new_time);
timer
}
}
impl<T:TimerState> TimerFixed<T,Unpaused>{
pub fn into_paused(self,time:Time)->TimerFixed<T,Paused>{
let new_time=self.time(time);
let mut timer=TimerFixed{
state:self.state,
_paused:Paused,
};
timer.set_time(time,new_time);
timer
}
}
//the new constructor and time queries are generic across both
impl<T:TimerState,P:PauseState> TimerFixed<T,P>{
pub fn new(time:Time,new_time:Time)->Self{
let mut timer=Self{
state:T::identity(),
_paused:P::new(),
};
timer.set_time(time,new_time);
timer
}
pub fn from_state(state:T)->Self{
Self{ Self{
state, state:Realtime{offset},
_paused:P::new(), paused:true,
}
}
pub fn into_state(self)->T{
self.state
}
pub fn time(&self,time:Time)->Time{
match P::IS_PAUSED{
true=>self.state.get_offset(),
false=>self.state.get_time(time),
}
}
pub fn set_time(&mut self,time:Time,new_time:Time){
match P::IS_PAUSED{
true=>self.state.set_offset(new_time),
false=>self.state.set_time(time,new_time),
} }
} }
} }
@ -192,115 +110,87 @@ impl std::fmt::Display for Error{
} }
impl std::error::Error for Error{} impl std::error::Error for Error{}
//wrapper type which holds type state internally
#[derive(Clone,Debug)]
pub enum Timer<T:TimerState>{
Paused(TimerFixed<T,Paused>),
Unpaused(TimerFixed<T,Unpaused>),
}
impl<T:TimerState> Timer<T>{
pub fn from_state(state:T,paused:bool)->Self{
match paused{
true=>Self::Paused(TimerFixed::from_state(state)),
false=>Self::Unpaused(TimerFixed::from_state(state)),
}
}
pub fn into_state(self)->(T,bool){
match self{
Self::Paused(timer)=>(timer.into_state(),true),
Self::Unpaused(timer)=>(timer.into_state(),false),
}
}
pub fn paused(time:Time,new_time:Time)->Self{
Self::Paused(TimerFixed::new(time,new_time))
}
pub fn unpaused(time:Time,new_time:Time)->Self{
Self::Unpaused(TimerFixed::new(time,new_time))
}
pub fn time(&self,time:Time)->Time{
match self{
Self::Paused(timer)=>timer.time(time),
Self::Unpaused(timer)=>timer.time(time),
}
}
pub fn set_time(&mut self,time:Time,new_time:Time){
match self{
Self::Paused(timer)=>timer.set_time(time,new_time),
Self::Unpaused(timer)=>timer.set_time(time,new_time),
}
}
pub fn pause(&mut self,time:Time)->Result<(),Error>{
*self=match *self{
Self::Paused(_)=>return Err(Error::AlreadyPaused),
Self::Unpaused(timer)=>Self::Paused(timer.into_paused(time)),
};
Ok(())
}
pub fn unpause(&mut self,time:Time)->Result<(),Error>{
*self=match *self{
Self::Paused(timer)=>Self::Unpaused(timer.into_unpaused(time)),
Self::Unpaused(_)=>return Err(Error::AlreadyUnpaused),
};
Ok(())
}
pub fn is_paused(&self)->bool{
match self{
Self::Paused(_)=>true,
Self::Unpaused(_)=>false,
}
}
pub fn set_paused(&mut self,time:Time,paused:bool)->Result<(),Error>{
match paused{
true=>self.pause(time),
false=>self.unpause(time),
}
}
}
//scaled timer methods are generic across PauseState
impl Timer<Scaled>{ impl Timer<Scaled>{
pub const fn get_scale(&self)->Ratio64{ pub fn new(time:Time,new_time:Time,scale:Ratio64,paused:bool)->Self{
match self{ let mut timer=Self{
Self::Paused(timer)=>timer.get_scale(), state:Scaled::with_scale(scale),
Self::Unpaused(timer)=>timer.get_scale(), paused,
};
timer.set_time(time,new_time);
timer
} }
pub fn scaled(time:Time,new_time:Time,scale:Ratio64)->Self{
let mut timer=Self{
state:Scaled::with_scale(scale),
paused:false,
};
timer.set_time(time,new_time);
timer
}
pub fn scaled_paused(time:Time,new_time:Time,scale:Ratio64)->Self{
let mut timer=Self{
state:Scaled::with_scale(scale),
paused:true,
};
timer.set_time(time,new_time);
timer
}
pub const fn get_scale(&self)->Ratio64{
self.state.get_scale()
} }
pub fn set_scale(&mut self,time:Time,new_scale:Ratio64){ pub fn set_scale(&mut self,time:Time,new_scale:Ratio64){
match self{ self.state.set_scale(time,new_scale)
Self::Paused(timer)=>timer.set_scale(time,new_scale),
Self::Unpaused(timer)=>timer.set_scale(time,new_scale),
}
} }
} }
impl<T:TimerState> Timer<T>{
pub fn time(&self,time:Time)->Time{
match self.paused{
true=>self.state.get_offset(),
false=>self.state.get_time(time),
}
}
pub fn set_time(&mut self,time:Time,new_time:Time){
match self.paused{
true=>self.state.set_offset(new_time),
false=>self.state.set_time(time,new_time),
}
}
pub fn pause(&mut self,time:Time)->Result<(),Error>{
match self.paused{
true=>Err(Error::AlreadyPaused),
false=>{
let new_time=self.time(time);
self.state.set_offset(new_time);
self.paused=true;
Ok(())
},
}
}
pub fn unpause(&mut self,time:Time)->Result<(),Error>{
match self.paused{
true=>{
let new_time=self.time(time);
self.state.set_time(time,new_time);
self.paused=false;
Ok(())
},
false=>Err(Error::AlreadyUnpaused),
}
}
}
#[cfg(test)] #[cfg(test)]
mod test{ mod test{
use super::*; use super::{Time,Timer,Error};
macro_rules! sec { macro_rules! sec {
($s: expr) => { ($s: expr) => {
Time::from_secs($s) Time::from_secs($s)
}; };
} }
#[test] #[test]
fn test_timerfixed_scaled(){
//create a paused timer that reads 0s
let timer=TimerFixed::<Scaled,Paused>::from_state(Scaled{scale:0.5f32.try_into().unwrap(),offset:sec!(0)});
//the paused timer at 1 second should read 0s
assert_eq!(timer.time(sec!(1)),sec!(0));
//unpause it after one second
let timer=timer.into_unpaused(sec!(1));
//the timer at 6 seconds should read 2.5s
assert_eq!(timer.time(sec!(6)),Time::from_millis(2500));
//pause the timer after 11 seconds
let timer=timer.into_paused(sec!(11));
//the paused timer at 20 seconds should read 5s
assert_eq!(timer.time(sec!(20)),sec!(5));
}
#[test]
fn test_timer()->Result<(),Error>{ fn test_timer()->Result<(),Error>{
//create a paused timer that reads 0s //create a paused timer that reads 0s
let mut timer=Timer::<Realtime>::paused(sec!(0),sec!(0)); let mut timer=Timer::realtime_paused(sec!(0));
//the paused timer at 1 second should read 0s //the paused timer at 1 second should read 0s
assert_eq!(timer.time(sec!(1)),sec!(0)); assert_eq!(timer.time(sec!(1)),sec!(0));

40
src/zeroes.rs Normal file
View File

@ -0,0 +1,40 @@
//find roots of polynomials
use crate::integer::Planar64;
#[inline]
pub fn zeroes2(a0:Planar64,a1:Planar64,a2:Planar64) -> Vec<Planar64>{
if a2==Planar64::ZERO{
return zeroes1(a0, a1);
}
let radicand=a1.get() as i128*a1.get() as i128-a2.get() as i128*a0.get() as i128*4;
if 0<radicand {
//start with f64 sqrt
//failure case: 2^63 < sqrt(2^127)
let planar_radicand=Planar64::raw(unsafe{(radicand as f64).sqrt().to_int_unchecked()});
//TODO: one or two newtons
//sort roots ascending and avoid taking the difference of large numbers
match (Planar64::ZERO<a2,Planar64::ZERO<a1){
(true, true )=>vec![(-a1-planar_radicand)/(a2*2),(a0*2)/(-a1-planar_radicand)],
(true, false)=>vec![(a0*2)/(-a1+planar_radicand),(-a1+planar_radicand)/(a2*2)],
(false,true )=>vec![(a0*2)/(-a1-planar_radicand),(-a1-planar_radicand)/(a2*2)],
(false,false)=>vec![(-a1+planar_radicand)/(a2*2),(a0*2)/(-a1+planar_radicand)],
}
} else if radicand==0 {
return vec![a1/(a2*-2)];
} else {
return vec![];
}
}
#[inline]
pub fn zeroes1(a0:Planar64,a1:Planar64) -> Vec<Planar64> {
if a1==Planar64::ZERO{
return vec![];
}else{
let q=((-a0.get() as i128)<<32)/(a1.get() as i128);
if i64::MIN as i128<=q&&q<=i64::MAX as i128{
return vec![Planar64::raw(q as i64)];
}else{
return vec![];
}
}
}