Start integrating asset loading and animations
This commit is contained in:
@@ -3,3 +3,4 @@ use bevy::math::{Vec2, primitives::Segment2d};
|
||||
use fixed::prelude::*;
|
||||
|
||||
pub type FGi32 = fixed::FixedI32<fixed::types::extra::U16>;
|
||||
pub type FGWide = fixed::FixedI128<fixed::types::extra::U32>;
|
||||
|
||||
+57
-15
@@ -3,9 +3,10 @@ use bevy::math::{Vec2, primitives::Segment2d};
|
||||
use bevy::prelude::*;
|
||||
use fixed::prelude::*;
|
||||
|
||||
use crate::math::vec::FGVec2;
|
||||
|
||||
type FGi32 = fixed::FixedI32<fixed::types::extra::U16>;
|
||||
use crate::math::{
|
||||
int::{FGWide, FGi32},
|
||||
vec::FGVec2,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Reflect)]
|
||||
pub struct FGSegment2d {
|
||||
@@ -81,7 +82,14 @@ impl FGSegment2d {
|
||||
|
||||
// Point lies somewhere in the middle, we compute the closest point by finding the parameter along the line.
|
||||
let t = projection_scaled / length_squared;
|
||||
self.point1 + segment_vector * t
|
||||
FGVec2::new(
|
||||
FGi32::from_num(
|
||||
FGWide::from_num(self.point1.x) + FGWide::from_num(segment_vector.x) * t,
|
||||
),
|
||||
FGi32::from_num(
|
||||
FGWide::from_num(self.point1.y) + FGWide::from_num(segment_vector.y) * t,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/// Compute the normalized counterclockwise normal on the left-hand side of the line segment.
|
||||
@@ -100,22 +108,34 @@ impl FGSegment2d {
|
||||
let p1 = self.point2();
|
||||
let p2 = rhs.point1();
|
||||
let p3 = rhs.point2();
|
||||
|
||||
let s1_x = p1.x - p0.x;
|
||||
let s1_y = p1.y - p0.y;
|
||||
let s2_x = p3.x - p2.x;
|
||||
let s2_y = p3.y - p2.y;
|
||||
|
||||
let p0_x = FGWide::from_num(p0.x);
|
||||
let p0_y = FGWide::from_num(p0.y);
|
||||
let p1_x = FGWide::from_num(p1.x);
|
||||
let p1_y = FGWide::from_num(p1.y);
|
||||
let p2_x = FGWide::from_num(p2.x);
|
||||
let p2_y = FGWide::from_num(p2.y);
|
||||
let p3_x = FGWide::from_num(p3.x);
|
||||
let p3_y = FGWide::from_num(p3.y);
|
||||
|
||||
let s1_x = p1_x - p0_x;
|
||||
let s1_y = p1_y - p0_y;
|
||||
let s2_x = p3_x - p2_x;
|
||||
let s2_y = p3_y - p2_y;
|
||||
|
||||
let denom = -s2_x * s1_y + s1_x * s2_y;
|
||||
if denom == FGi32::ZERO {
|
||||
if denom == FGWide::ZERO {
|
||||
return None; // Parallel or collinear
|
||||
}
|
||||
info!("{p0}, {p1}, {p2}, {p3}");
|
||||
let s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / denom;
|
||||
let t = (s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / denom;
|
||||
|
||||
let s = (-s1_y * (p0.x - p2.x) + s1_x * (p0.y - p2.y)) / denom;
|
||||
let t = ( s2_x * (p0.y - p2.y) - s2_y * (p0.x - p2.x)) / denom;
|
||||
|
||||
if s >= FGi32::ZERO && s <= FGi32::ONE && t >= FGi32::ZERO && t <= FGi32::ONE {
|
||||
Some(FGVec2::new(p0.x + (t * s1_x), p0.y + (t * s1_y)))
|
||||
if s >= FGWide::ZERO && s <= FGWide::ONE && t >= FGWide::ZERO && t <= FGWide::ONE {
|
||||
Some(FGVec2::new(
|
||||
FGi32::from_num(p0_x + t * s1_x),
|
||||
FGi32::from_num(p0_y + t * s1_y),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -134,4 +154,26 @@ mod tests {
|
||||
assert_eq!(segment.closest_point(FGVec2::lit("-6", "1")), segment.point1());
|
||||
assert_eq!(segment.closest_point(FGVec2::lit("6", "1")), segment.point2());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn segment_intersection_avoids_cross_product_overflow() {
|
||||
let segment = FGSegment2d::new(FGVec2::lit("56", "-3.5"), FGVec2::lit("56", "-200"));
|
||||
let rhs = FGSegment2d::new(
|
||||
FGVec2::lit("-111.26334", "-130.22586"),
|
||||
FGVec2::lit("-112.36334", "-133.02586"),
|
||||
);
|
||||
|
||||
assert_eq!(segment.segment_intersection(&rhs), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn segment_intersection_handles_large_crossing_segments() {
|
||||
let horizontal = FGSegment2d::new(FGVec2::lit("-200", "0"), FGVec2::lit("200", "0"));
|
||||
let vertical = FGSegment2d::new(FGVec2::lit("0", "-200"), FGVec2::lit("0", "200"));
|
||||
|
||||
assert_eq!(
|
||||
horizontal.segment_intersection(&vertical),
|
||||
Some(FGVec2::ZERO)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+29
-10
@@ -4,7 +4,7 @@ use fixed::types::I32F32;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ops::{AddAssign, Add, Div, Mul, Sub};
|
||||
|
||||
use super::int::FGi32;
|
||||
use super::int::{FGWide, FGi32};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Default, Debug, Reflect, Serialize, Deserialize)]
|
||||
#[reflect(opaque)]
|
||||
@@ -41,13 +41,15 @@ impl FGVec2 {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn length_squared(&self) -> FGi32 {
|
||||
self.x * self.x + self.y * self.y
|
||||
pub fn length_squared(&self) -> FGWide {
|
||||
let x = FGWide::from_num(self.x);
|
||||
let y = FGWide::from_num(self.y);
|
||||
x * x + y * y
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn length(self) -> FGi32 {
|
||||
(self.x * self.x + self.y * self.y).sqrt()
|
||||
FGi32::from_num(self.length_squared().sqrt())
|
||||
}
|
||||
|
||||
/// Returns the vector projection of `self` onto `rhs`.
|
||||
@@ -62,7 +64,11 @@ impl FGVec2 {
|
||||
pub fn project_onto_normalized(self, rhs: Self) -> Self {
|
||||
assert!(rhs.is_normalized());
|
||||
|
||||
rhs * self.dot(rhs)
|
||||
let scale = self.dot(rhs);
|
||||
Self::new(
|
||||
FGi32::from_num(FGWide::from_num(rhs.x) * scale),
|
||||
FGi32::from_num(FGWide::from_num(rhs.y) * scale),
|
||||
)
|
||||
}
|
||||
|
||||
const EPS: FGi32 = FGi32::from_bits(6);
|
||||
@@ -76,16 +82,21 @@ impl FGVec2 {
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn dot(self, rhs: Self) -> FGi32 {
|
||||
(self.x * rhs.x) + (self.y * rhs.y)
|
||||
pub fn dot(self, rhs: Self) -> FGWide {
|
||||
FGWide::from_num(self.x) * FGWide::from_num(rhs.x)
|
||||
+ FGWide::from_num(self.y) * FGWide::from_num(rhs.y)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn project_onto(self, rhs: Self) -> Self {
|
||||
let other_len_sq = rhs.dot(rhs);
|
||||
assert!(other_len_sq != FGi32::ZERO);
|
||||
rhs * (self.dot(rhs) / other_len_sq)
|
||||
assert!(other_len_sq != FGWide::ZERO);
|
||||
let scale = self.dot(rhs) / other_len_sq;
|
||||
Self::new(
|
||||
FGi32::from_num(FGWide::from_num(rhs.x) * scale),
|
||||
FGi32::from_num(FGWide::from_num(rhs.y) * scale),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -246,7 +257,7 @@ mod is_normalized_tests {
|
||||
let v = FGVec2 { x: fx, y: fy };
|
||||
|
||||
if let Some(normalized) = v.normalize() {
|
||||
let ls = normalized.length_squared();
|
||||
let ls = FGi32::from_num(normalized.length_squared());
|
||||
let diff_bits = ls.to_bits() - FGi32::ONE.to_bits();
|
||||
let abs_diff_bits = diff_bits.abs();
|
||||
|
||||
@@ -310,4 +321,12 @@ mod is_normalized_tests {
|
||||
let non_unit_small = FGVec2 { x: FGi32::from_num(0.5), y: FGi32::from_num(0.0) };
|
||||
assert!(!non_unit_small.is_normalized());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dot_and_length_squared_use_wide_intermediates() {
|
||||
let vector = FGVec2::lit("200", "200");
|
||||
|
||||
assert_eq!(vector.dot(vector), FGWide::from_num(80_000));
|
||||
assert_eq!(vector.length_squared(), FGWide::from_num(80_000));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user