use super::super::circular_arc_first_order;
use super::super::curve_tangent_solved;
use super::super::minor_circular_arc_point;
use super::super::pcurve_tangent;
use super::contact_track;
use crate::geometry::analytic::{CircleCurve, HyperbolaCurve, ParabolaCurve};
use crate::geometry::pcurve::{HyperbolaPcurve, ParabolaPcurve};
use crate::geometry::{pcurve::PcurveGeometry, SolvedCurveGeometry};
use crate::math::{Point2, Point3, Vector3};
const EPS_RELATIVE: f64 = 1024.0 * f64::EPSILON;
#[test]
fn numerical_ranges_parabola_point_and_tangent_stay_finite() {
let curve = SolvedCurveGeometry::Parabola(
ParabolaCurve::try_new(
Point3::new(0., 0., 0.),
Vector3::new(0., 0., 1.),
Vector3::new(1., 0., 0.),
1e308,
)
.unwrap(),
);
let point = crate::eval::decode::curve_point_solved(
crate::eval::admission::EvaluationAdmission::Standard,
&curve,
0.5,
)
.unwrap();
assert_eq!(point, Point3::new(2.5e307, 1e308, 0.));
let pcurve = PcurveGeometry::Parabola(
ParabolaPcurve::try_new(
Point2::new(0., 0.),
Point2::new(1., 0.),
Point2::new(0., 1.),
1e200,
)
.unwrap(),
);
let point = crate::eval::decode::pcurve_uv(
crate::eval::admission::EvaluationAdmission::Standard,
&pcurve,
1e200,
)
.unwrap();
assert!((point.u / 2.5e199 - 1.).abs() < EPS_RELATIVE);
assert_eq!(point.v, 1e200);
assert_eq!(
pcurve_tangent(
crate::eval::admission::EvaluationAdmission::Standard,
&pcurve,
1e200
)
.map(crate::units::FinitePoint2::get),
Ok(Point2::new(0.5, 1.))
);
}
#[test]
fn numerical_ranges_hyperbolas_scale_before_exponentiation_overflows() {
let curve = SolvedCurveGeometry::Hyperbola(
HyperbolaCurve::try_new(
Point3::new(0., 0., 0.),
Vector3::new(0., 0., 1.),
Vector3::new(1., 0., 0.),
1e-10,
1e-10,
)
.unwrap(),
);
let pcurve = PcurveGeometry::Hyperbola(
HyperbolaPcurve::try_new(
Point2::new(0., 0.),
Point2::new(1., 0.),
Point2::new(0., 1.),
1e-10,
1e-10,
)
.unwrap(),
);
for t in [-720.0_f64, 720.] {
let expected = (t.abs() + 1e-10_f64.ln()).exp() * 0.5;
let point = crate::eval::decode::curve_point_solved(
crate::eval::admission::EvaluationAdmission::Standard,
&curve,
t,
)
.unwrap();
assert!((point.x / expected - 1.).abs() < EPS_RELATIVE);
assert!((point.y / (t.signum() * expected) - 1.).abs() < EPS_RELATIVE);
assert!(curve_tangent_solved(
crate::eval::admission::EvaluationAdmission::Standard,
&curve,
t
)
.unwrap()
.is_finite());
assert!(crate::eval::decode::pcurve_uv(
crate::eval::admission::EvaluationAdmission::Standard,
&pcurve,
t
)
.unwrap()
.is_finite());
assert!(pcurve_tangent(
crate::eval::admission::EvaluationAdmission::Standard,
&pcurve,
t
)
.unwrap()
.is_finite());
}
}
#[test]
fn numerical_ranges_offset_keeps_finite_cancellation() {
let c = std::f64::consts::FRAC_1_SQRT_2;
let curve = SolvedCurveGeometry::Circle(
CircleCurve::try_new(
Point3::new(1e308, 0., 0.),
Vector3::new(0., 0., 1.),
Vector3::new(c, c, 0.),
1.6e308,
)
.unwrap(),
);
let point = crate::eval::decode::curve_point_solved(
crate::eval::admission::EvaluationAdmission::Standard,
&curve,
std::f64::consts::FRAC_PI_4,
)
.unwrap();
assert!((point.x / 1e308 - 1.).abs() < EPS_RELATIVE);
assert!((point.y / 1.6e308 - 1.).abs() < EPS_RELATIVE);
}
#[test]
fn numerical_ranges_shallow_arc_point_and_partials_keep_curvature() {
let radius = 1e9;
let angle = 1e-9_f64;
let zero = Vector3::new(0., 0., 0.);
let center = Point3::new(0., 0., 0.);
let first = contact_track(Point3::new(radius, 0., 0.), zero);
let second = contact_track(
Point3::new(radius * angle.cos(), radius * angle.sin(), 0.),
zero,
);
let point =
minor_circular_arc_point(center, first.point(), second.point(), radius, 0.5).unwrap();
assert!((point.y - 0.5).abs() < EPS_RELATIVE);
let partials = circular_arc_first_order(center, Ok(zero), &first, &second, radius, Ok(0.), 0.5)
.and_then(super::super::SurfaceFirstOrder::partials)
.unwrap()
.into_raw();
assert!((partials.point.y - 0.5).abs() < EPS_RELATIVE);
assert!((partials.du.y - 1.).abs() < EPS_RELATIVE);
assert!((partials.du.x / (-0.5 * angle) - 1.).abs() < EPS_RELATIVE);
assert_eq!(partials.dv, zero);
}
#[test]
fn numerical_ranges_hyperbolic_point_survives_unrepresentable_derivatives() {
let curve = PcurveGeometry::Hyperbolic(
crate::geometry::pcurve::HyperbolicPcurve::try_new(
Point2::new(-1e308, 0.),
Point2::new(1e308, 0.),
Point2::new(1e308, 0.),
)
.unwrap(),
);
let point = crate::eval::decode::pcurve_uv(
crate::eval::admission::EvaluationAdmission::Standard,
&curve,
1.,
)
.unwrap();
let expected = (1.0_f64.exp() - 1.) * 1e308;
assert!((point.u / expected - 1.).abs() < EPS_RELATIVE);
assert_eq!(point.v, 0.);
assert_eq!(
pcurve_tangent(
crate::eval::admission::EvaluationAdmission::Standard,
&curve,
1.
),
Err(crate::eval::EvaluationFailure::NonFinite(Point2::new(
f64::INFINITY,
0.
)))
);
}
#[test]
fn numerical_ranges_nurbs_basis_supports_spans_wider_than_f64() {
let pcurve = PcurveGeometry::Nurbs {
nurbs: crate::geometry::pcurve::PcurveNurbs::from_lanes(
&cadmpeg_test_support::service_decode_context(),
1,
vec![-1e308, -1e308, 1e308, 1e308],
vec![Point2::new(0., 0.), Point2::new(1., 0.)],
None,
false,
)
.expect("fixture pcurve construction admission")
.unwrap(),
};
for (parameter, expected) in [(-1e308, 0.), (0., 0.5), (1e308, 1.)] {
assert_eq!(
crate::eval::decode::pcurve_uv(
crate::eval::admission::EvaluationAdmission::Standard,
&pcurve,
parameter
)
.map(crate::units::FinitePoint2::get),
Ok(Point2::new(expected, 0.))
);
let tangent = pcurve_tangent(
crate::eval::admission::EvaluationAdmission::Standard,
&pcurve,
parameter,
)
.unwrap();
assert!((tangent.u / 5e-309 - 1.).abs() < EPS_RELATIVE);
assert_eq!(tangent.v, 0.);
}
}