Compare commits
14 Commits
bvh-iter
...
to-unsigne
| Author | SHA1 | Date | |
|---|---|---|---|
| b2820ac05a | |||
| 9e887580af | |||
| 92feac572e | |||
| fd02a40783 | |||
| 7c787a0e0f | |||
| 6a7c076203 | |||
| af3abbcb4d | |||
| 4859c37780 | |||
| 19e65802f6 | |||
| 2cf1547423 | |||
| 63305f91c7 | |||
| e875826250 | |||
| 0a44c1630f | |||
| 5cffc03ef6 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -2991,7 +2991,7 @@ checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde"
|
||||
|
||||
[[package]]
|
||||
name = "ratio_ops"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
|
||||
[[package]]
|
||||
name = "rav1e"
|
||||
|
||||
@@ -484,6 +484,9 @@ impl TransformedMesh<'_>{
|
||||
pub fn verts<'a>(&'a self)->impl Iterator<Item=vec3::Vector3<Fixed<2,64>>>+'a{
|
||||
self.view.data.verts.iter().map(|&Vert(pos)|self.transform.vertex.transform_point3(pos))
|
||||
}
|
||||
pub fn faces(&self)->impl Iterator<Item=SubmeshFaceId>{
|
||||
(0..self.view.topology.faces.len() as u32).map(SubmeshFaceId::new)
|
||||
}
|
||||
fn farthest_vert(&self,dir:Planar64Vec3)->SubmeshVertId{
|
||||
//this happens to be well-defined. there are no virtual virtices
|
||||
SubmeshVertId::new(
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use strafesnet_common::integer::{self,vec3::{self,Vector3},Fixed,Planar64,Planar64Vec3,Ratio};
|
||||
use strafesnet_common::integer::vec3::{self,Vector3};
|
||||
use strafesnet_common::integer::{Fixed,Planar64Vec3,Ratio};
|
||||
use strafesnet_common::ray::Ray;
|
||||
|
||||
// This algorithm is based on Lua code
|
||||
// written by Trey Reynolds in 2021
|
||||
@@ -12,24 +14,6 @@ type Conts<'a>=arrayvec::ArrayVec<&'a Contact,4>;
|
||||
// hack to allow comparing ratios to zero
|
||||
const RATIO_ZERO:Ratio<Fixed<1,32>,Fixed<1,32>>=Ratio::new(Fixed::ZERO,Fixed::EPSILON);
|
||||
|
||||
struct Ray{
|
||||
origin:Planar64Vec3,
|
||||
direction:Planar64Vec3,
|
||||
}
|
||||
impl Ray{
|
||||
fn extrapolate<Num,Den,N1,T1>(&self,t:Ratio<Num,Den>)->Planar64Vec3
|
||||
where
|
||||
Num:Copy,
|
||||
Den:Copy,
|
||||
Num:core::ops::Mul<Planar64,Output=N1>,
|
||||
Planar64:core::ops::Mul<Den,Output=N1>,
|
||||
N1:integer::Divide<Den,Output=T1>,
|
||||
T1:integer::Fix<Planar64>,
|
||||
{
|
||||
self.origin+self.direction.map(|elem|(t*elem).divide().fix())
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about a contact restriction
|
||||
pub struct Contact{
|
||||
pub position:Planar64Vec3,
|
||||
|
||||
@@ -5,6 +5,12 @@ mod mesh;
|
||||
pub mod loader;
|
||||
|
||||
const VALVE_SCALE:f32=1.0/16.0;
|
||||
pub(crate) fn valve_transform_dist(d:f32)->strafesnet_common::integer::Planar64{
|
||||
(d*VALVE_SCALE).try_into().unwrap()
|
||||
}
|
||||
pub(crate) fn valve_transform_normal([x,y,z]:[f32;3])->strafesnet_common::integer::Planar64Vec3{
|
||||
strafesnet_common::integer::vec3::try_from_f32_array([x,z,-y]).unwrap()
|
||||
}
|
||||
pub(crate) fn valve_transform([x,y,z]:[f32;3])->strafesnet_common::integer::Planar64Vec3{
|
||||
strafesnet_common::integer::vec3::try_from_f32_array([x*VALVE_SCALE,z*VALVE_SCALE,-y*VALVE_SCALE]).unwrap()
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ pub enum MeshError{
|
||||
Io(std::io::Error),
|
||||
VMDL(vmdl::ModelError),
|
||||
VBSP(vbsp::BspError),
|
||||
MissingMdl,
|
||||
MissingMdl(String),
|
||||
MissingVtx,
|
||||
MissingVvd,
|
||||
}
|
||||
@@ -132,7 +132,7 @@ impl<'bsp,'vpk,'a> Loader for ModelLoader<'bsp,'vpk,'a>
|
||||
vvd_path.set_extension("vvd");
|
||||
vtx_path.set_extension("dx90.vtx");
|
||||
// TODO: search more packs, possibly using an index of multiple packs
|
||||
let mdl=self.finder.find(mdl_path_lower.as_str())?.ok_or(MeshError::MissingMdl)?;
|
||||
let mdl=self.finder.find(mdl_path_lower.as_str())?.ok_or(MeshError::MissingMdl(mdl_path_lower))?;
|
||||
let vtx=self.finder.find(vtx_path.as_os_str().to_str().unwrap())?.ok_or(MeshError::MissingVtx)?;
|
||||
let vvd=self.finder.find(vvd_path.as_os_str().to_str().unwrap())?.ok_or(MeshError::MissingVvd)?;
|
||||
Ok(vmdl::Model::from_parts(
|
||||
|
||||
@@ -34,6 +34,10 @@ impl Aabb{
|
||||
self.min-=hs;
|
||||
self.max+=hs;
|
||||
}
|
||||
pub fn contains(&self,point:Planar64Vec3)->bool{
|
||||
let bvec=self.min.lt(point)&point.lt(self.max);
|
||||
bvec.all()
|
||||
}
|
||||
pub fn intersects(&self,aabb:&Aabb)->bool{
|
||||
let bvec=self.min.lt(aabb.max)&aabb.min.lt(self.max);
|
||||
bvec.all()
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::aabb::Aabb;
|
||||
use crate::ray::Ray;
|
||||
use crate::integer::{Ratio,Planar64};
|
||||
use crate::instruction::{InstructionCollector,TimedInstruction};
|
||||
|
||||
//da algaritum
|
||||
//lista boxens
|
||||
@@ -10,6 +16,96 @@ use crate::aabb::Aabb;
|
||||
//sort the centerpoints on each axis (3 lists)
|
||||
//bv is put into octant based on whether it is upper or lower in each list
|
||||
|
||||
|
||||
pub fn intersect_aabb(ray:&Ray,aabb:&Aabb)->Option<Ratio<Planar64,Planar64>>{
|
||||
// n.(o+d*t)==n.p
|
||||
// n.o + n.d * t == n.p
|
||||
// t == (n.p - n.o)/n.d
|
||||
let mut hit=None;
|
||||
match ray.direction.x.cmp(&Planar64::ZERO){
|
||||
Ordering::Less=>{
|
||||
let rel_min=aabb.min()-ray.origin;
|
||||
let rel_max=aabb.max()-ray.origin;
|
||||
let dy=rel_max.x*ray.direction.y;
|
||||
let dz=rel_max.x*ray.direction.z;
|
||||
// x is negative, so inequalities are flipped
|
||||
if rel_min.y*ray.direction.x>dy&&dy>rel_max.y*ray.direction.x
|
||||
&&rel_min.z*ray.direction.x>dz&&dz>rel_max.z*ray.direction.x{
|
||||
let t=rel_max.x/ray.direction.x;
|
||||
hit=Some(hit.map_or(t,|best_t|t.min(best_t)));
|
||||
}
|
||||
},
|
||||
Ordering::Equal=>(),
|
||||
Ordering::Greater=>{
|
||||
let rel_min=aabb.min()-ray.origin;
|
||||
let rel_max=aabb.max()-ray.origin;
|
||||
let dy=rel_min.x*ray.direction.y;
|
||||
let dz=rel_min.x*ray.direction.z;
|
||||
// x is positive, so inequalities are normal
|
||||
if rel_min.y*ray.direction.x<dy&&dy<rel_max.y*ray.direction.x
|
||||
&&rel_min.z*ray.direction.x<dz&&dz<rel_max.z*ray.direction.x{
|
||||
let t=rel_min.x/ray.direction.x;
|
||||
hit=Some(hit.map_or(t,|best_t|t.min(best_t)));
|
||||
}
|
||||
},
|
||||
}
|
||||
match ray.direction.z.cmp(&Planar64::ZERO){
|
||||
Ordering::Less=>{
|
||||
let rel_min=aabb.min()-ray.origin;
|
||||
let rel_max=aabb.max()-ray.origin;
|
||||
let dx=rel_max.z*ray.direction.x;
|
||||
let dy=rel_max.z*ray.direction.y;
|
||||
// z is negative, so inequalities are flipped
|
||||
if rel_min.x*ray.direction.z>dx&&dx>rel_max.x*ray.direction.z
|
||||
&&rel_min.y*ray.direction.z>dy&&dy>rel_max.y*ray.direction.z{
|
||||
let t=rel_max.z/ray.direction.z;
|
||||
hit=Some(hit.map_or(t,|best_t|t.min(best_t)));
|
||||
}
|
||||
},
|
||||
Ordering::Equal=>(),
|
||||
Ordering::Greater=>{
|
||||
let rel_min=aabb.min()-ray.origin;
|
||||
let rel_max=aabb.max()-ray.origin;
|
||||
let dx=rel_min.z*ray.direction.x;
|
||||
let dy=rel_min.z*ray.direction.y;
|
||||
// z is positive, so inequalities are normal
|
||||
if rel_min.x*ray.direction.z<dx&&dx<rel_max.x*ray.direction.z
|
||||
&&rel_min.y*ray.direction.z<dy&&dy<rel_max.y*ray.direction.z{
|
||||
let t=rel_min.z/ray.direction.z;
|
||||
hit=Some(hit.map_or(t,|best_t|t.min(best_t)));
|
||||
}
|
||||
},
|
||||
}
|
||||
match ray.direction.y.cmp(&Planar64::ZERO){
|
||||
Ordering::Less=>{
|
||||
let rel_min=aabb.min()-ray.origin;
|
||||
let rel_max=aabb.max()-ray.origin;
|
||||
let dz=rel_max.y*ray.direction.z;
|
||||
let dx=rel_max.y*ray.direction.x;
|
||||
// y is negative, so inequalities are flipped
|
||||
if rel_min.z*ray.direction.y>dz&&dz>rel_max.z*ray.direction.y
|
||||
&&rel_min.x*ray.direction.y>dx&&dx>rel_max.x*ray.direction.y{
|
||||
let t=rel_max.y/ray.direction.y;
|
||||
hit=Some(hit.map_or(t,|best_t|t.min(best_t)));
|
||||
}
|
||||
},
|
||||
Ordering::Equal=>(),
|
||||
Ordering::Greater=>{
|
||||
let rel_min=aabb.min()-ray.origin;
|
||||
let rel_max=aabb.max()-ray.origin;
|
||||
let dz=rel_min.y*ray.direction.z;
|
||||
let dx=rel_min.y*ray.direction.x;
|
||||
// y is positive, so inequalities are normal
|
||||
if rel_min.z*ray.direction.y<dz&&dz<rel_max.z*ray.direction.y
|
||||
&&rel_min.x*ray.direction.y<dx&&dx<rel_max.x*ray.direction.y{
|
||||
let t=rel_min.y/ray.direction.y;
|
||||
hit=Some(hit.map_or(t,|best_t|t.min(best_t)));
|
||||
}
|
||||
},
|
||||
}
|
||||
hit
|
||||
}
|
||||
|
||||
pub enum RecursiveContent<N,L>{
|
||||
Branch(Vec<N>),
|
||||
Leaf(L),
|
||||
@@ -44,6 +140,92 @@ impl<L> BvhNode<L>{
|
||||
},
|
||||
}
|
||||
}
|
||||
fn populate_nodes<'a,T,F>(
|
||||
&'a self,
|
||||
collector:&mut InstructionCollector<&'a L,Ratio<Planar64,Planar64>>,
|
||||
nodes:&mut BTreeMap<Ratio<Planar64,Planar64>,&'a BvhNode<L>>,
|
||||
ray:&Ray,
|
||||
start_time:Ratio<Planar64,Planar64>,
|
||||
f:&F,
|
||||
)
|
||||
where
|
||||
T:Ord+Copy,
|
||||
Ratio<Planar64,Planar64>:From<T>,
|
||||
F:Fn(&L,&Ray)->Option<T>,
|
||||
{
|
||||
match &self.content{
|
||||
RecursiveContent::Leaf(leaf)=>if let Some(time)=f(leaf,ray){
|
||||
let ins=TimedInstruction{time:time.into(),instruction:leaf};
|
||||
if start_time.lt_ratio(ins.time)&&ins.time.lt_ratio(collector.time()){
|
||||
collector.collect(Some(ins));
|
||||
}
|
||||
},
|
||||
RecursiveContent::Branch(children)=>for child in children{
|
||||
if child.aabb.contains(ray.origin){
|
||||
child.populate_nodes(collector,nodes,ray,start_time,f);
|
||||
}else{
|
||||
// Am I an upcoming superstar?
|
||||
if let Some(t)=intersect_aabb(ray,&child.aabb){
|
||||
if start_time.lt_ratio(t)&&t.lt_ratio(collector.time()){
|
||||
nodes.insert(t,child);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn sample_ray<T,F>(
|
||||
&self,
|
||||
ray:&Ray,
|
||||
start_time:T,
|
||||
time_limit:T,
|
||||
f:F,
|
||||
)->Option<(T,&L)>
|
||||
where
|
||||
T:Ord+Copy,
|
||||
T:From<Ratio<Planar64,Planar64>>,
|
||||
Ratio<Planar64,Planar64>:From<T>,
|
||||
F:Fn(&L,&Ray)->Option<T>,
|
||||
{
|
||||
// source of nondeterminism when Aabb boundaries are coplanar
|
||||
let mut nodes=BTreeMap::new();
|
||||
|
||||
let start_time=start_time.into();
|
||||
let time_limit=time_limit.into();
|
||||
let mut collector=InstructionCollector::new(time_limit);
|
||||
// break open all nodes that contain ray.origin and populate nodes with future intersection times
|
||||
self.populate_nodes(&mut collector,&mut nodes,ray,start_time,&f);
|
||||
|
||||
// swim through nodes one at a time
|
||||
while let Some((t,node))=nodes.pop_first(){
|
||||
if collector.time()<t{
|
||||
break;
|
||||
}
|
||||
match &node.content{
|
||||
RecursiveContent::Leaf(leaf)=>if let Some(time)=f(leaf,ray){
|
||||
let ins=TimedInstruction{time:time.into(),instruction:leaf};
|
||||
// this lower bound can also be omitted
|
||||
// but it causes type inference errors lol
|
||||
if start_time.lt_ratio(ins.time)&&ins.time.lt_ratio(collector.time()){
|
||||
collector.collect(Some(ins));
|
||||
}
|
||||
},
|
||||
// break open the node and predict collisions with the child nodes
|
||||
RecursiveContent::Branch(children)=>for child in children{
|
||||
// Am I an upcoming superstar?
|
||||
if let Some(t)=intersect_aabb(ray,&child.aabb){
|
||||
// we don't need to check the lower bound
|
||||
// because child aabbs are guaranteed to be within the parent bounds.
|
||||
if t<collector.time(){
|
||||
nodes.insert(t,child);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
collector.take().map(|TimedInstruction{time,instruction:leaf}|(time.into(),leaf))
|
||||
}
|
||||
pub fn into_inner(self)->(RecursiveContent<BvhNode<L>,L>,Aabb){
|
||||
(self.content,self.aabb)
|
||||
}
|
||||
|
||||
@@ -171,4 +171,7 @@ impl CollisionAttributes{
|
||||
pub fn contact_default()->Self{
|
||||
Self::Contact(ContactAttributes::default())
|
||||
}
|
||||
pub fn intersect_default()->Self{
|
||||
Self::Intersect(IntersectAttributes::default())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ pub use ratio_ops::ratio::{Ratio,Divide};
|
||||
//integer units
|
||||
|
||||
/// specific example of a "default" time type
|
||||
#[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)]
|
||||
#[derive(Clone,Copy,Hash,Eq,PartialEq,Ord,PartialOrd,Debug)]
|
||||
pub enum TimeInner{}
|
||||
pub type AbsoluteTime=Time<TimeInner>;
|
||||
|
||||
#[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)]
|
||||
#[derive(Clone,Copy,Hash,Eq,PartialEq,Ord,PartialOrd,Debug)]
|
||||
pub struct Time<T>(i64,core::marker::PhantomData<T>);
|
||||
impl<T> Time<T>{
|
||||
pub const MIN:Self=Self::raw(i64::MIN);
|
||||
@@ -654,11 +654,19 @@ pub struct Planar64Affine3{
|
||||
pub translation:Planar64Vec3,
|
||||
}
|
||||
impl Planar64Affine3{
|
||||
pub const IDENTITY:Self=Self::new(mat3::identity(),vec3::ZERO);
|
||||
#[inline]
|
||||
pub const fn new(matrix3:Planar64Mat3,translation:Planar64Vec3)->Self{
|
||||
Self{matrix3,translation}
|
||||
}
|
||||
#[inline]
|
||||
pub const fn from_translation(translation:Planar64Vec3)->Self{
|
||||
Self{
|
||||
matrix3:mat3::identity(),
|
||||
translation,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn transform_point3(&self,point:Planar64Vec3)->vec3::Vector3<Fixed<2,64>>{
|
||||
self.translation.fix_2()+self.matrix3*point
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod bvh;
|
||||
pub mod map;
|
||||
pub mod ray;
|
||||
pub mod run;
|
||||
pub mod aabb;
|
||||
pub mod model;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::mouse::MouseState;
|
||||
use crate::gameplay_modes::{ModeId,StageId};
|
||||
|
||||
#[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)]
|
||||
#[derive(Clone,Copy,Hash,Eq,PartialEq,Ord,PartialOrd,Debug)]
|
||||
pub enum TimeInner{}
|
||||
pub type Time=crate::integer::Time<TimeInner>;
|
||||
|
||||
|
||||
20
lib/common/src/ray.rs
Normal file
20
lib/common/src/ray.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use ratio_ops::ratio::Ratio;
|
||||
use crate::integer::{self,Planar64,Planar64Vec3};
|
||||
|
||||
pub struct Ray{
|
||||
pub origin:Planar64Vec3,
|
||||
pub direction:Planar64Vec3,
|
||||
}
|
||||
impl Ray{
|
||||
pub fn extrapolate<Num,Den,N1,T1>(&self,t:Ratio<Num,Den>)->Planar64Vec3
|
||||
where
|
||||
Num:Copy,
|
||||
Den:Copy,
|
||||
Num:core::ops::Mul<Planar64,Output=N1>,
|
||||
Planar64:core::ops::Mul<Den,Output=N1>,
|
||||
N1:integer::Divide<Den,Output=T1>,
|
||||
T1:integer::Fix<Planar64>,
|
||||
{
|
||||
self.origin+self.direction.map(|elem|(t*elem).divide().fix())
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ use crate::timer::{TimerFixed,Realtime,Paused,Unpaused};
|
||||
|
||||
use crate::physics::{TimeInner as PhysicsTimeInner,Time as PhysicsTime};
|
||||
|
||||
#[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)]
|
||||
#[derive(Clone,Copy,Hash,Eq,PartialEq,Ord,PartialOrd,Debug)]
|
||||
pub enum TimeInner{}
|
||||
pub type Time=crate::integer::Time<TimeInner>;
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)]
|
||||
#[derive(Clone,Copy,Hash,Eq,PartialEq,Ord,PartialOrd,Debug)]
|
||||
pub enum TimeInner{}
|
||||
pub type Time=crate::integer::Time<TimeInner>;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::vector::Vector;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
|
||||
pub struct Matrix<const X:usize,const Y:usize,T>{
|
||||
pub(crate) array:[[T;Y];X],
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
/// v.x += v.z;
|
||||
/// println!("v.x={}",v.x);
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
|
||||
pub struct Vector<const N:usize,T>{
|
||||
pub(crate) array:[T;N],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ratio_ops"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2024"
|
||||
repository = "https://git.itzana.me/StrafesNET/strafe-project"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
@@ -68,54 +68,57 @@ impl_ratio_method!(Add,add,add_ratio);
|
||||
impl_ratio_method!(Sub,sub,sub_ratio);
|
||||
impl_ratio_method!(Rem,rem,rem_ratio);
|
||||
|
||||
/// Comparing two ratios needs to know the parity of the denominators
|
||||
/// For signed integers this can be implemented with is_negative()
|
||||
pub trait Parity{
|
||||
fn parity(&self)->bool;
|
||||
/// The denominator cannot be negative
|
||||
/// otherwise cross-multiplying changes the comparison
|
||||
pub trait ToUnsigned{
|
||||
type Unsigned;
|
||||
fn to_unsigned(self)->(bool,Self::Unsigned);
|
||||
}
|
||||
macro_rules! impl_parity_unsigned{
|
||||
macro_rules! impl_to_unsigned_unsigned{
|
||||
($($type:ty),*)=>{
|
||||
$(
|
||||
impl Parity for $type{
|
||||
fn parity(&self)->bool{
|
||||
false
|
||||
impl ToUnsigned for $type{
|
||||
type Unsigned=Self;
|
||||
fn to_unsigned(self)->(bool,Self::Unsigned){
|
||||
(false,self)
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
macro_rules! impl_parity_signed{
|
||||
($($type:ty),*)=>{
|
||||
macro_rules! impl_to_unsigned_signed{
|
||||
($(($type:ty,$unsigned:ty)),*)=>{
|
||||
$(
|
||||
impl Parity for $type{
|
||||
fn parity(&self)->bool{
|
||||
self.is_negative()
|
||||
impl ToUnsigned for $type{
|
||||
type Unsigned=$unsigned;
|
||||
fn to_unsigned(self)->(bool,Self::Unsigned){
|
||||
(self.is_negative(),self.unsigned_abs())
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
macro_rules! impl_parity_float{
|
||||
macro_rules! impl_to_unsigned_float{
|
||||
($($type:ty),*)=>{
|
||||
$(
|
||||
impl Parity for $type{
|
||||
fn parity(&self)->bool{
|
||||
self.is_sign_negative()
|
||||
impl ToUnsigned for $type{
|
||||
type Unsigned=Self;
|
||||
fn to_unsigned(self)->(bool,Self::Unsigned){
|
||||
(self.is_sign_negative(),-self)
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_parity_unsigned!(u8,u16,u32,u64,u128,usize);
|
||||
impl_parity_signed!(i8,i16,i32,i64,i128,isize);
|
||||
impl_parity_float!(f32,f64);
|
||||
impl_to_unsigned_unsigned!(u8,u16,u32,u64,u128,usize);
|
||||
impl_to_unsigned_signed!((i8,u8),(i16,u16),(i32,u32),(i64,u64),(i128,u128),(isize,usize));
|
||||
impl_to_unsigned_float!(f32,f64);
|
||||
|
||||
macro_rules! impl_ratio_ord_method{
|
||||
($method:ident, $ratio_method:ident, $output:ty)=>{
|
||||
impl<LhsNum,LhsDen:Parity> Ratio<LhsNum,LhsDen>{
|
||||
impl<LhsNum,LhsDen:ToUnsigned> Ratio<LhsNum,LhsDen>{
|
||||
#[inline]
|
||||
pub fn $ratio_method<RhsNum,RhsDen:Parity,T>(self,rhs:Ratio<RhsNum,RhsDen>)->$output
|
||||
pub fn $ratio_method<RhsNum,RhsDen:ToUnsigned,T>(self,rhs:Ratio<RhsNum,RhsDen>)->$output
|
||||
where
|
||||
LhsNum:core::ops::Mul<RhsDen,Output=T>,
|
||||
LhsDen:core::ops::Mul<RhsNum,Output=T>,
|
||||
@@ -268,30 +271,35 @@ impl<LhsNum,LhsDen,RhsNum,RhsDen,T,U> PartialEq<Ratio<RhsNum,RhsDen>> for Ratio<
|
||||
}
|
||||
impl<Num,Den> Eq for Ratio<Num,Den> where Self:PartialEq{}
|
||||
|
||||
// Wow! These were both completely wrong!
|
||||
// Idea: use a 'signed' trait instead of parity and float the sign to the numerator.
|
||||
impl<LhsNum,LhsDen,RhsNum,RhsDen,T,U> PartialOrd<Ratio<RhsNum,RhsDen>> for Ratio<LhsNum,LhsDen>
|
||||
where
|
||||
LhsNum:Copy,
|
||||
LhsDen:Copy,
|
||||
LhsDen:Copy+ToUnsigned,
|
||||
RhsNum:Copy,
|
||||
RhsDen:Copy,
|
||||
RhsDen:Copy+ToUnsigned,
|
||||
LhsNum:core::ops::Mul<RhsDen,Output=T>,
|
||||
LhsDen:core::ops::Mul<RhsNum,Output=T>,
|
||||
RhsNum:core::ops::Mul<LhsDen,Output=U>,
|
||||
T:PartialOrd<U>,
|
||||
RhsDen:core::ops::Mul<LhsNum,Output=U>,
|
||||
T:PartialOrd<U>+Ord,
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self,other:&Ratio<RhsNum,RhsDen>)->Option<core::cmp::Ordering>{
|
||||
(self.num*other.den).partial_cmp(&(other.num*self.den))
|
||||
fn partial_cmp(&self,&other:&Ratio<RhsNum,RhsDen>)->Option<core::cmp::Ordering>{
|
||||
self.partial_cmp_ratio(other)
|
||||
}
|
||||
}
|
||||
impl<Num,Den,T> Ord for Ratio<Num,Den>
|
||||
where
|
||||
Num:Copy,
|
||||
Den:Copy,
|
||||
Den:Copy+ToUnsigned,
|
||||
Num:core::ops::Mul<Den,Output=T>,
|
||||
Den:core::ops::Mul<Num,Output=T>,
|
||||
T:Ord,
|
||||
{
|
||||
#[inline]
|
||||
fn cmp(&self,other:&Self)->std::cmp::Ordering{
|
||||
(self.num*other.den).cmp(&(other.num*self.den))
|
||||
fn cmp(&self,&other:&Self)->std::cmp::Ordering{
|
||||
self.cmp_ratio(other)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user