use crate::features::FinitePoint3;
use crate::math::{Point3, Vector3};
use crate::scalar::{Angle, NonNegativeLength, NonZeroLength, PositiveLength, PositiveReal};
use crate::sketches::OrderedMajorRadius;
use crate::units::{OrthonormalFrame3, UnitVector3};
#[cfg(feature = "schema")]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(try_from = "PlaneSurfaceWire", into = "PlaneSurfaceWire")]
pub struct PlaneSurface {
origin: FinitePoint3,
frame: OrthonormalFrame3,
}
#[derive(Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct PlaneSurfaceWire {
origin: Point3,
normal: Vector3,
u_axis: Vector3,
}
impl PlaneSurface {
#[must_use]
pub const fn new(origin: FinitePoint3, frame: OrthonormalFrame3) -> Self {
Self { origin, frame }
}
pub fn try_new(origin: Point3, normal: Vector3, u_axis: Vector3) -> Result<Self, &'static str> {
let frame = OrthonormalFrame3::new(normal, u_axis)
.ok_or("PlaneSurface.normal/u_axis must form an orthonormal frame")?;
let origin = FinitePoint3::new(origin).ok_or("PlaneSurface.origin must be finite")?;
Ok(Self::new(origin, frame))
}
#[must_use]
pub const fn origin(&self) -> FinitePoint3 {
self.origin
}
#[must_use]
pub const fn frame(&self) -> &OrthonormalFrame3 {
&self.frame
}
}
impl From<PlaneSurface> for PlaneSurfaceWire {
fn from(value: PlaneSurface) -> Self {
Self {
origin: value.origin().get(),
normal: *value.frame().axis().as_raw(),
u_axis: *value.frame().reference().as_raw(),
}
}
}
impl TryFrom<PlaneSurfaceWire> for PlaneSurface {
type Error = &'static str;
fn try_from(wire: PlaneSurfaceWire) -> Result<Self, Self::Error> {
Self::try_new(wire.origin, wire.normal, wire.u_axis)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(try_from = "CylinderSurfaceWire", into = "CylinderSurfaceWire")]
pub struct CylinderSurface {
origin: FinitePoint3,
radius: PositiveLength,
frame: OrthonormalFrame3,
}
#[derive(Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct CylinderSurfaceWire {
origin: Point3,
axis: Vector3,
ref_direction: Vector3,
radius: f64,
}
impl CylinderSurface {
#[must_use]
pub const fn new(
origin: FinitePoint3,
frame: OrthonormalFrame3,
radius: PositiveLength,
) -> Self {
Self {
origin,
radius,
frame,
}
}
pub fn try_new(
origin: Point3,
axis: Vector3,
ref_direction: Vector3,
radius: f64,
) -> Result<Self, &'static str> {
let frame = OrthonormalFrame3::new(axis, ref_direction)
.ok_or("CylinderSurface.axis/ref_direction must form an orthonormal frame")?;
let origin = FinitePoint3::new(origin).ok_or("CylinderSurface.origin must be finite")?;
let radius = PositiveLength::new(radius)
.ok_or("CylinderSurface.radius must be positive and finite")?;
Ok(Self::new(origin, frame, radius))
}
#[must_use]
pub const fn origin(&self) -> FinitePoint3 {
self.origin
}
#[must_use]
pub const fn frame(&self) -> &OrthonormalFrame3 {
&self.frame
}
#[must_use]
pub const fn radius(&self) -> PositiveLength {
self.radius
}
}
impl From<CylinderSurface> for CylinderSurfaceWire {
fn from(value: CylinderSurface) -> Self {
Self {
origin: value.origin().get(),
axis: *value.frame().axis().as_raw(),
ref_direction: *value.frame().reference().as_raw(),
radius: value.radius().get(),
}
}
}
impl TryFrom<CylinderSurfaceWire> for CylinderSurface {
type Error = &'static str;
fn try_from(wire: CylinderSurfaceWire) -> Result<Self, Self::Error> {
Self::try_new(wire.origin, wire.axis, wire.ref_direction, wire.radius)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(try_from = "ConeSurfaceWire", into = "ConeSurfaceWire")]
pub struct ConeSurface {
origin: FinitePoint3,
radius: NonNegativeLength,
ratio: PositiveReal,
half_angle: Angle,
frame: OrthonormalFrame3,
}
#[derive(Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct ConeSurfaceWire {
origin: Point3,
axis: Vector3,
ref_direction: Vector3,
radius: f64,
ratio: f64,
half_angle: f64,
}
impl ConeSurface {
#[must_use]
pub const fn new(
origin: FinitePoint3,
frame: OrthonormalFrame3,
radius: NonNegativeLength,
ratio: PositiveReal,
half_angle: Angle,
) -> Self {
Self {
origin,
radius,
ratio,
half_angle,
frame,
}
}
pub fn try_new(
origin: Point3,
axis: Vector3,
ref_direction: Vector3,
radius: f64,
ratio: f64,
half_angle: f64,
) -> Result<Self, &'static str> {
let frame = OrthonormalFrame3::new(axis, ref_direction)
.ok_or("ConeSurface.axis/ref_direction must form an orthonormal frame")?;
let origin = FinitePoint3::new(origin).ok_or("ConeSurface.origin must be finite")?;
let radius = NonNegativeLength::new(radius)
.ok_or("ConeSurface.radius must be nonnegative and finite")?;
let ratio =
PositiveReal::new(ratio).ok_or("ConeSurface.ratio must be positive and finite")?;
let half_angle = Angle::new(half_angle).ok_or("ConeSurface.half_angle must be finite")?;
Ok(Self::new(origin, frame, radius, ratio, half_angle))
}
#[must_use]
pub const fn origin(&self) -> FinitePoint3 {
self.origin
}
#[must_use]
pub const fn frame(&self) -> &OrthonormalFrame3 {
&self.frame
}
#[must_use]
pub const fn radius(&self) -> NonNegativeLength {
self.radius
}
#[must_use]
pub const fn ratio(&self) -> PositiveReal {
self.ratio
}
#[must_use]
pub const fn half_angle(&self) -> Angle {
self.half_angle
}
}
impl From<ConeSurface> for ConeSurfaceWire {
fn from(value: ConeSurface) -> Self {
Self {
origin: value.origin().get(),
axis: *value.frame().axis().as_raw(),
ref_direction: *value.frame().reference().as_raw(),
radius: value.radius().get(),
ratio: value.ratio().get(),
half_angle: value.half_angle().get(),
}
}
}
impl TryFrom<ConeSurfaceWire> for ConeSurface {
type Error = &'static str;
fn try_from(wire: ConeSurfaceWire) -> Result<Self, Self::Error> {
Self::try_new(
wire.origin,
wire.axis,
wire.ref_direction,
wire.radius,
wire.ratio,
wire.half_angle,
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(try_from = "SphereSurfaceWire", into = "SphereSurfaceWire")]
pub struct SphereSurface {
center: FinitePoint3,
radius: NonZeroLength,
frame: OrthonormalFrame3,
}
#[derive(Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct SphereSurfaceWire {
center: Point3,
axis: Vector3,
ref_direction: Vector3,
radius: f64,
}
impl SphereSurface {
#[must_use]
pub const fn new(
center: FinitePoint3,
frame: OrthonormalFrame3,
radius: NonZeroLength,
) -> Self {
Self {
center,
radius,
frame,
}
}
pub fn try_new(
center: Point3,
axis: Vector3,
ref_direction: Vector3,
radius: f64,
) -> Result<Self, &'static str> {
let (center, frame) = Self::admit_placement(center, axis, ref_direction)?;
let radius =
NonZeroLength::new(radius).ok_or("SphereSurface.radius must be finite and nonzero")?;
Ok(Self::new(center, frame, radius))
}
pub fn try_with_radius(
center: Point3,
axis: Vector3,
ref_direction: Vector3,
radius: NonZeroLength,
) -> Result<Self, &'static str> {
let (center, frame) = Self::admit_placement(center, axis, ref_direction)?;
Ok(Self::new(center, frame, radius))
}
fn admit_placement(
center: Point3,
axis: Vector3,
ref_direction: Vector3,
) -> Result<(FinitePoint3, OrthonormalFrame3), &'static str> {
let frame = OrthonormalFrame3::new(axis, ref_direction)
.ok_or("SphereSurface.axis/ref_direction must form an orthonormal frame")?;
let center = FinitePoint3::new(center).ok_or("SphereSurface.center must be finite")?;
Ok((center, frame))
}
#[must_use]
pub const fn center(&self) -> FinitePoint3 {
self.center
}
#[must_use]
pub const fn frame(&self) -> &OrthonormalFrame3 {
&self.frame
}
#[must_use]
pub const fn radius(&self) -> NonZeroLength {
self.radius
}
}
impl From<SphereSurface> for SphereSurfaceWire {
fn from(value: SphereSurface) -> Self {
Self {
center: value.center().get(),
axis: *value.frame().axis().as_raw(),
ref_direction: *value.frame().reference().as_raw(),
radius: value.radius().get(),
}
}
}
impl TryFrom<SphereSurfaceWire> for SphereSurface {
type Error = &'static str;
fn try_from(wire: SphereSurfaceWire) -> Result<Self, Self::Error> {
Self::try_new(wire.center, wire.axis, wire.ref_direction, wire.radius)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(try_from = "TorusSurfaceWire", into = "TorusSurfaceWire")]
pub struct TorusSurface {
center: FinitePoint3,
major_radius: PositiveLength,
minor_radius: NonZeroLength,
frame: OrthonormalFrame3,
}
#[derive(Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct TorusSurfaceWire {
center: Point3,
axis: Vector3,
ref_direction: Vector3,
major_radius: f64,
minor_radius: f64,
}
impl TorusSurface {
#[must_use]
pub const fn new(
center: FinitePoint3,
frame: OrthonormalFrame3,
major_radius: PositiveLength,
minor_radius: NonZeroLength,
) -> Self {
Self {
center,
major_radius,
minor_radius,
frame,
}
}
pub fn try_new(
center: Point3,
axis: Vector3,
ref_direction: Vector3,
major_radius: f64,
minor_radius: f64,
) -> Result<Self, &'static str> {
let frame = OrthonormalFrame3::new(axis, ref_direction)
.ok_or("TorusSurface.axis/ref_direction must form an orthonormal frame")?;
let center = FinitePoint3::new(center).ok_or("TorusSurface.center must be finite")?;
let major_radius = PositiveLength::new(major_radius)
.ok_or("TorusSurface.major_radius must be positive and finite")?;
let minor_radius = NonZeroLength::new(minor_radius)
.ok_or("TorusSurface.minor_radius must be finite and nonzero")?;
Ok(Self::new(center, frame, major_radius, minor_radius))
}
#[must_use]
pub const fn center(&self) -> FinitePoint3 {
self.center
}
#[must_use]
pub const fn frame(&self) -> &OrthonormalFrame3 {
&self.frame
}
#[must_use]
pub const fn major_radius(&self) -> PositiveLength {
self.major_radius
}
#[must_use]
pub const fn minor_radius(&self) -> NonZeroLength {
self.minor_radius
}
}
impl From<TorusSurface> for TorusSurfaceWire {
fn from(value: TorusSurface) -> Self {
Self {
center: value.center().get(),
axis: *value.frame().axis().as_raw(),
ref_direction: *value.frame().reference().as_raw(),
major_radius: value.major_radius().get(),
minor_radius: value.minor_radius().get(),
}
}
}
impl TryFrom<TorusSurfaceWire> for TorusSurface {
type Error = &'static str;
fn try_from(wire: TorusSurfaceWire) -> Result<Self, Self::Error> {
Self::try_new(
wire.center,
wire.axis,
wire.ref_direction,
wire.major_radius,
wire.minor_radius,
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(try_from = "LineCurveWire")]
pub struct LineCurve {
origin: FinitePoint3,
direction: UnitVector3,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct LineCurveWire {
origin: Point3,
direction: Vector3,
}
impl LineCurve {
pub fn reverse_parameterization(&mut self) {
self.direction = self.direction.reversed();
}
#[must_use]
pub const fn new(origin: FinitePoint3, direction: UnitVector3) -> Self {
Self { origin, direction }
}
pub fn try_new(origin: Point3, direction: Vector3) -> Result<Self, &'static str> {
let origin = FinitePoint3::new(origin).ok_or("LineCurve.origin must be finite")?;
let direction =
UnitVector3::new(direction).ok_or("LineCurve.direction must have unit length")?;
Ok(Self::new(origin, direction))
}
#[must_use]
pub const fn origin(&self) -> FinitePoint3 {
self.origin
}
#[must_use]
pub const fn direction(&self) -> UnitVector3 {
self.direction
}
}
impl TryFrom<LineCurveWire> for LineCurve {
type Error = &'static str;
fn try_from(wire: LineCurveWire) -> Result<Self, Self::Error> {
Self::try_new(wire.origin, wire.direction)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(try_from = "CircleCurveWire", into = "CircleCurveWire")]
pub struct CircleCurve {
center: FinitePoint3,
radius: PositiveLength,
frame: OrthonormalFrame3,
}
#[derive(Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct CircleCurveWire {
center: Point3,
axis: Vector3,
ref_direction: Vector3,
radius: f64,
}
impl CircleCurve {
pub fn reverse_parameterization(&mut self) {
self.frame.reverse_axis();
}
#[must_use]
pub const fn new(
center: FinitePoint3,
frame: OrthonormalFrame3,
radius: PositiveLength,
) -> Self {
Self {
center,
radius,
frame,
}
}
pub fn try_new(
center: Point3,
axis: Vector3,
ref_direction: Vector3,
radius: f64,
) -> Result<Self, &'static str> {
let frame = OrthonormalFrame3::new(axis, ref_direction)
.ok_or("CircleCurve.axis/ref_direction must form an orthonormal frame")?;
let center = FinitePoint3::new(center).ok_or("CircleCurve.center must be finite")?;
let radius =
PositiveLength::new(radius).ok_or("CircleCurve.radius must be positive and finite")?;
Ok(Self::new(center, frame, radius))
}
#[must_use]
pub const fn center(&self) -> FinitePoint3 {
self.center
}
#[must_use]
pub const fn frame(&self) -> &OrthonormalFrame3 {
&self.frame
}
#[must_use]
pub const fn radius(&self) -> PositiveLength {
self.radius
}
}
impl From<CircleCurve> for CircleCurveWire {
fn from(value: CircleCurve) -> Self {
Self {
center: value.center().get(),
axis: *value.frame().axis().as_raw(),
ref_direction: *value.frame().reference().as_raw(),
radius: value.radius().get(),
}
}
}
impl TryFrom<CircleCurveWire> for CircleCurve {
type Error = &'static str;
fn try_from(wire: CircleCurveWire) -> Result<Self, Self::Error> {
Self::try_new(wire.center, wire.axis, wire.ref_direction, wire.radius)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(try_from = "EllipseCurveWire", into = "EllipseCurveWire")]
pub struct EllipseCurve {
center: FinitePoint3,
radii: OrderedMajorRadius,
frame: OrthonormalFrame3,
}
#[derive(Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct EllipseCurveWire {
center: Point3,
axis: Vector3,
major_direction: Vector3,
major_radius: f64,
minor_radius: f64,
}
impl EllipseCurve {
pub fn reverse_parameterization(&mut self) {
self.frame.reverse_axis();
}
#[must_use]
pub const fn new(
center: FinitePoint3,
frame: OrthonormalFrame3,
radii: OrderedMajorRadius,
) -> Self {
Self {
center,
radii,
frame,
}
}
pub fn try_from_parts(
center: FinitePoint3,
frame: OrthonormalFrame3,
major_radius: PositiveLength,
minor_radius: PositiveLength,
) -> Result<Self, &'static str> {
let radii = OrderedMajorRadius::new(major_radius, minor_radius)
.ok_or("EllipseCurve.major_radius must be at least minor_radius")?;
Ok(Self::new(center, frame, radii))
}
#[must_use]
pub fn from_source_radii(
center: FinitePoint3,
frame: OrthonormalFrame3,
radii: [f64; 2],
scale: PositiveReal,
) -> Option<Self> {
let [major, minor] = radii;
(minor <= major).then_some(())?;
let [major_radius, minor_radius] = PositiveLength::scale_ordered_pair(radii, scale).ok()?;
Some(Self::new(
center,
frame,
OrderedMajorRadius::new(major_radius, minor_radius)?,
))
}
pub(crate) fn scaled(&self, scale: PositiveReal) -> Result<Self, &'static str> {
let center = self
.center
.scaled(scale)
.ok_or("EllipseCurve.center must be finite")?;
let [major_radius, minor_radius] = PositiveLength::scale_ordered_pair(
[self.radii.major().get(), self.radii.minor().get()],
scale,
)
.map_err(|index| {
if index == 0 {
"EllipseCurve.major_radius must be positive and finite"
} else {
"EllipseCurve.minor_radius must be positive and finite"
}
})?;
let radii = OrderedMajorRadius::new(major_radius, minor_radius)
.ok_or("EllipseCurve.major_radius must be at least minor_radius")?;
Ok(Self::new(center, self.frame, radii))
}
pub fn try_new(
center: Point3,
axis: Vector3,
major_direction: Vector3,
major_radius: f64,
minor_radius: f64,
) -> Result<Self, &'static str> {
let frame = OrthonormalFrame3::new(axis, major_direction)
.ok_or("EllipseCurve.axis/major_direction must form an orthonormal frame")?;
let center = FinitePoint3::new(center).ok_or("EllipseCurve.center must be finite")?;
let major_radius = PositiveLength::new(major_radius)
.ok_or("EllipseCurve.major_radius must be positive and finite")?;
let minor_radius = PositiveLength::new(minor_radius)
.ok_or("EllipseCurve.minor_radius must be positive and finite")?;
Self::try_from_parts(center, frame, major_radius, minor_radius)
}
#[must_use]
pub const fn center(&self) -> FinitePoint3 {
self.center
}
#[must_use]
pub const fn frame(&self) -> &OrthonormalFrame3 {
&self.frame
}
#[must_use]
pub const fn radii(&self) -> OrderedMajorRadius {
self.radii
}
#[must_use]
pub const fn major_radius(&self) -> PositiveLength {
self.radii.major()
}
#[must_use]
pub const fn minor_radius(&self) -> PositiveLength {
self.radii.minor()
}
}
impl From<EllipseCurve> for EllipseCurveWire {
fn from(value: EllipseCurve) -> Self {
Self {
center: value.center().get(),
axis: *value.frame().axis().as_raw(),
major_direction: *value.frame().reference().as_raw(),
major_radius: value.major_radius().get(),
minor_radius: value.minor_radius().get(),
}
}
}
impl TryFrom<EllipseCurveWire> for EllipseCurve {
type Error = &'static str;
fn try_from(wire: EllipseCurveWire) -> Result<Self, Self::Error> {
Self::try_new(
wire.center,
wire.axis,
wire.major_direction,
wire.major_radius,
wire.minor_radius,
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(try_from = "ParabolaCurveWire", into = "ParabolaCurveWire")]
pub struct ParabolaCurve {
vertex: FinitePoint3,
focal_distance: PositiveLength,
frame: OrthonormalFrame3,
}
#[derive(Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct ParabolaCurveWire {
vertex: Point3,
axis: Vector3,
major_direction: Vector3,
focal_distance: f64,
}
impl ParabolaCurve {
#[must_use]
pub const fn new(
vertex: FinitePoint3,
frame: OrthonormalFrame3,
focal_distance: PositiveLength,
) -> Self {
Self {
vertex,
focal_distance,
frame,
}
}
pub fn try_new(
vertex: Point3,
axis: Vector3,
major_direction: Vector3,
focal_distance: f64,
) -> Result<Self, &'static str> {
let frame = OrthonormalFrame3::new(axis, major_direction)
.ok_or("ParabolaCurve.axis/major_direction must form an orthonormal frame")?;
let vertex = FinitePoint3::new(vertex).ok_or("ParabolaCurve.vertex must be finite")?;
let focal_distance = PositiveLength::new(focal_distance)
.ok_or("ParabolaCurve.focal_distance must be positive and finite")?;
Ok(Self::new(vertex, frame, focal_distance))
}
#[must_use]
pub const fn vertex(&self) -> FinitePoint3 {
self.vertex
}
#[must_use]
pub const fn frame(&self) -> &OrthonormalFrame3 {
&self.frame
}
#[must_use]
pub const fn focal_distance(&self) -> PositiveLength {
self.focal_distance
}
}
impl From<ParabolaCurve> for ParabolaCurveWire {
fn from(value: ParabolaCurve) -> Self {
Self {
vertex: value.vertex().get(),
axis: *value.frame().axis().as_raw(),
major_direction: *value.frame().reference().as_raw(),
focal_distance: value.focal_distance().get(),
}
}
}
impl TryFrom<ParabolaCurveWire> for ParabolaCurve {
type Error = &'static str;
fn try_from(wire: ParabolaCurveWire) -> Result<Self, Self::Error> {
Self::try_new(
wire.vertex,
wire.axis,
wire.major_direction,
wire.focal_distance,
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(try_from = "HyperbolaCurveWire", into = "HyperbolaCurveWire")]
pub struct HyperbolaCurve {
center: FinitePoint3,
major_radius: PositiveLength,
minor_radius: PositiveLength,
frame: OrthonormalFrame3,
}
#[derive(Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct HyperbolaCurveWire {
center: Point3,
axis: Vector3,
major_direction: Vector3,
major_radius: f64,
minor_radius: f64,
}
impl HyperbolaCurve {
#[must_use]
pub fn opposite_branch(mut self) -> Self {
self.frame.reverse_reference();
self
}
#[must_use]
pub const fn new(
center: FinitePoint3,
frame: OrthonormalFrame3,
major_radius: PositiveLength,
minor_radius: PositiveLength,
) -> Self {
Self {
center,
major_radius,
minor_radius,
frame,
}
}
pub fn try_new(
center: Point3,
axis: Vector3,
major_direction: Vector3,
major_radius: f64,
minor_radius: f64,
) -> Result<Self, &'static str> {
let frame = OrthonormalFrame3::new(axis, major_direction)
.ok_or("HyperbolaCurve.axis/major_direction must form an orthonormal frame")?;
let center = FinitePoint3::new(center).ok_or("HyperbolaCurve.center must be finite")?;
let major_radius = PositiveLength::new(major_radius)
.ok_or("HyperbolaCurve.major_radius must be positive and finite")?;
let minor_radius = PositiveLength::new(minor_radius)
.ok_or("HyperbolaCurve.minor_radius must be positive and finite")?;
Ok(Self::new(center, frame, major_radius, minor_radius))
}
#[must_use]
pub const fn center(&self) -> FinitePoint3 {
self.center
}
#[must_use]
pub const fn frame(&self) -> &OrthonormalFrame3 {
&self.frame
}
#[must_use]
pub const fn major_radius(&self) -> PositiveLength {
self.major_radius
}
#[must_use]
pub const fn minor_radius(&self) -> PositiveLength {
self.minor_radius
}
}
impl From<HyperbolaCurve> for HyperbolaCurveWire {
fn from(value: HyperbolaCurve) -> Self {
Self {
center: value.center().get(),
axis: *value.frame().axis().as_raw(),
major_direction: *value.frame().reference().as_raw(),
major_radius: value.major_radius().get(),
minor_radius: value.minor_radius().get(),
}
}
}
impl TryFrom<HyperbolaCurveWire> for HyperbolaCurve {
type Error = &'static str;
fn try_from(wire: HyperbolaCurveWire) -> Result<Self, Self::Error> {
Self::try_new(
wire.center,
wire.axis,
wire.major_direction,
wire.major_radius,
wire.minor_radius,
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(try_from = "DegenerateCurveWire")]
pub struct DegenerateCurve {
point: FinitePoint3,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct DegenerateCurveWire {
point: Point3,
}
impl DegenerateCurve {
#[must_use]
pub const fn new(point: FinitePoint3) -> Self {
Self { point }
}
pub fn try_new(point: Point3) -> Result<Self, &'static str> {
let point = FinitePoint3::new(point).ok_or("DegenerateCurve.point must be finite")?;
Ok(Self::new(point))
}
#[must_use]
pub const fn point(&self) -> FinitePoint3 {
self.point
}
}
impl TryFrom<DegenerateCurveWire> for DegenerateCurve {
type Error = &'static str;
fn try_from(wire: DegenerateCurveWire) -> Result<Self, Self::Error> {
Self::try_new(wire.point)
}
}
#[cfg(test)]
mod tests;
mod identity_rewrite;