use crate::geometry::{CompoundLoftDirection, RevisionCompoundLoftTail};
use crate::ids::CurveId;
use crate::math::Vector3;
use serde::{Deserialize, Serialize};
use serde_json::json;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct TailWire {
tail: RevisionCompoundLoftTail<CurveId>,
}
#[test]
fn the_revision_compound_loft_tail_names_its_form() {
let id = CurveId::mint("test:model:curve#tail").expect("valid identity");
for (tail, expected) in [
(
RevisionCompoundLoftTail::Unbounded {},
json!({"tail": {"kind": "unbounded"}}),
),
(
RevisionCompoundLoftTail::LowerBound { lower: 2.0 },
json!({"tail": {"kind": "lower_bound", "lower": 2.0}}),
),
(
RevisionCompoundLoftTail::UpperBound { upper: 3.0 },
json!({"tail": {"kind": "upper_bound", "upper": 3.0}}),
),
(
RevisionCompoundLoftTail::Curve {
interval: [2.0, 3.0],
curve: id.clone(),
},
json!({"tail": {"kind": "curve", "interval": [2.0, 3.0], "curve": id}}),
),
] {
let value = TailWire { tail };
let wire = serde_json::to_value(&value).unwrap();
assert_eq!(wire, expected);
assert_eq!(serde_json::from_value::<TailWire>(wire).unwrap(), value);
}
}
#[test]
fn a_detached_bound_and_curve_have_no_encoding_in_the_loft_tail() {
for tail in [
json!({"kind": "unbounded", "curve": "test:model:curve#tail"}),
json!({"kind": "lower_bound", "lower": 2.0, "curve": "test:model:curve#tail"}),
json!({"kind": "curve", "interval": [2.0, 3.0]}),
json!({"kind": "curve", "curve": "test:model:curve#tail"}),
] {
assert!(
serde_json::from_value::<TailWire>(json!({"tail": tail.clone()})).is_err(),
"{tail}"
);
}
let bogus = json!({"tail": {"kind": "unbounded", "zz_bogus": 1}});
let error = serde_json::from_value::<TailWire>(bogus)
.unwrap_err()
.to_string();
assert!(error.contains("zz_bogus"), "{error}");
}
#[test]
fn a_compound_loft_direction_carries_a_selector_only_on_its_curve_form() {
let vector = super::RevisionCompoundLoftDirectionWireTest {
direction: CompoundLoftDirection::Vector {
value: Vector3::new(0.0, 0.0, 1.0),
},
};
let wire = serde_json::to_value(&vector).unwrap();
assert_eq!(wire["direction"]["kind"], "vector");
assert!(wire["direction"].get("selector").is_none());
assert_eq!(
serde_json::from_value::<super::RevisionCompoundLoftDirectionWireTest>(wire).unwrap(),
vector
);
let curve = super::RevisionCompoundLoftDirectionWireTest {
direction: CompoundLoftDirection::Curve {
curve: CurveId::mint("test:model:curve#direction").expect("valid identity"),
selector: std::num::NonZeroI64::new(3).expect("three is nonzero"),
},
};
let wire = serde_json::to_value(&curve).unwrap();
assert_eq!(wire["direction"]["selector"], 3);
assert_eq!(
serde_json::from_value::<super::RevisionCompoundLoftDirectionWireTest>(wire.clone())
.unwrap(),
curve
);
let mut zero = wire.clone();
zero["direction"]["selector"] = json!(0);
assert!(serde_json::from_value::<super::RevisionCompoundLoftDirectionWireTest>(zero).is_err());
let mut bogus = wire;
bogus["direction"]["zz_bogus"] = json!(1);
let error = serde_json::from_value::<super::RevisionCompoundLoftDirectionWireTest>(bogus)
.unwrap_err()
.to_string();
assert!(error.contains("zz_bogus"), "{error}");
}
#[test]
fn an_admitted_loft_tail_holds_finite_bounds() {
use crate::scalar::FiniteReal;
let id = CurveId::mint("test:model:curve#tail").expect("valid identity");
let raw = RevisionCompoundLoftTail::Curve {
interval: [2.0, 3.0],
curve: id.clone(),
};
let admitted = raw.clone().admit().expect("finite bounds");
assert_eq!(
admitted.interval(),
[FiniteReal::new(2.0), FiniteReal::new(3.0)]
);
assert_eq!(admitted.curve(), Some(&id));
assert_eq!(
serde_json::to_value(&admitted).unwrap(),
serde_json::to_value(&raw).unwrap()
);
for tail in [
RevisionCompoundLoftTail::LowerBound { lower: f64::NAN },
RevisionCompoundLoftTail::UpperBound {
upper: f64::INFINITY,
},
RevisionCompoundLoftTail::Curve {
interval: [2.0, f64::NEG_INFINITY],
curve: id.clone(),
},
] {
assert!(tail.admit().is_none());
}
assert_eq!(
RevisionCompoundLoftTail::<CurveId>::Unbounded {}
.admit()
.expect("no bound to admit")
.interval(),
[None, None]
);
}