use super::{
BlendCrossSection, BlendRadiusLaw, BlendSupport, CompoundComponent, CompoundLoftConstruction,
CompoundLoftTail, DeformableSurfaceConstruction, ExactSpline, G2BlendConstruction,
LawSurfaceConstruction, LoftBridgeToken, LoftRevisionForm, LoftSection, NetSurfaceConstruction,
RollingBallConstruction, ScaledCompoundLoftConstruction, ScaledCompoundLoftShape,
SkinSurfaceConstruction, SplineSurfaceParameters, SweepSurfaceConstruction,
VariableBlendConstruction, VertexBlendBoundaryGeometry, VertexBlendConstruction,
};
use super::{CacheContract, LegacyCache, LegacyCacheSlot};
use crate::features::{FinitePoint3, FiniteVector3};
use crate::ids::{CurveId, SurfaceId};
use crate::math::{Point3, Vector3};
use crate::scalar::FiniteReal;
use crate::topology::IncreasingParameterInterval;
use crate::units::{DirectionAboveEpsilon, FiniteVector, UnitVector3};
#[cfg(feature = "schema")]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use {
super::{
DirectedParameterRange, OffsetExtension, ProceduralGeometryError, RevisionSurfaceForm,
TaperSurfaceKind,
},
crate::geometry::pcurve::PcurveGeometry,
};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "SubSurfaceConstructionWire"))]
#[serde(try_from = "SubSurfaceConstructionWire")]
pub struct SubSurfaceConstruction {
support: SurfaceId,
parameter_ranges: [FiniteVector<2>; 2],
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct SubSurfaceConstructionWire {
support: SurfaceId,
parameter_ranges: [[f64; 2]; 2],
}
impl SubSurfaceConstruction {
pub fn try_new(
support: SurfaceId,
parameter_ranges: [[f64; 2]; 2],
) -> Result<Self, ProceduralGeometryError> {
Ok(Self {
support,
parameter_ranges: [
FiniteVector::new(parameter_ranges[0]).ok_or(ProceduralGeometryError::Payload(
"SubSurface.parameter_ranges is not finite",
))?,
FiniteVector::new(parameter_ranges[1]).ok_or(ProceduralGeometryError::Payload(
"SubSurface.parameter_ranges is not finite",
))?,
],
})
}
pub fn support(&self) -> &SurfaceId {
&self.support
}
pub fn parameter_ranges(&self) -> [FiniteVector<2>; 2] {
self.parameter_ranges
}
}
impl TryFrom<SubSurfaceConstructionWire> for SubSurfaceConstruction {
type Error = ProceduralGeometryError;
fn try_from(wire: SubSurfaceConstructionWire) -> Result<Self, Self::Error> {
Self::try_new(wire.support, wire.parameter_ranges)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "TaperSurfaceConstructionWire"))]
#[serde(try_from = "TaperSurfaceConstructionWire")]
pub struct TaperSurfaceConstruction {
support: SurfaceId,
reference: CurveId,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_pcurve"
)]
pcurve: Option<PcurveGeometry>,
parameter: FiniteReal,
taper: TaperSurfaceKind<FiniteReal, FiniteVector3>,
#[serde(default, skip_serializing_if = "CacheContract::is_bare_legacy")]
cache: CacheContract<RevisionSurfaceForm<Vec<bool>, FiniteReal>>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct TaperSurfaceConstructionWire {
support: SurfaceId,
reference: CurveId,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_pcurve"
)]
pcurve: Option<PcurveGeometry>,
parameter: f64,
taper: TaperSurfaceKind,
#[serde(default)]
cache: CacheContract<RevisionSurfaceForm>,
}
impl TaperSurfaceConstruction {
pub(super) fn write_revision_fit_tolerance(
&mut self,
write: super::ToleranceWrite,
) -> super::RevisionCacheWrite {
super::write_revision_form_tolerance(
self.cache.form_mut().map(|form| &mut form.cache),
write,
)
}
pub fn try_new(
support: SurfaceId,
reference: CurveId,
pcurve: Option<PcurveGeometry>,
parameter: f64,
taper: TaperSurfaceKind,
cache: CacheContract<RevisionSurfaceForm>,
) -> Result<Self, ProceduralGeometryError> {
let (Some(taper), Some(cache)) =
(taper.admit(), cache.admit_form(RevisionSurfaceForm::admit))
else {
return Err(ProceduralGeometryError::Payload(
"taper surface parameter or subtype tail is invalid",
));
};
Ok(Self {
support,
reference,
pcurve,
parameter: FiniteReal::new(parameter).ok_or(ProceduralGeometryError::Payload(
"Taper.parameter is not finite",
))?,
taper,
cache,
})
}
pub fn support(&self) -> &SurfaceId {
&self.support
}
pub fn reference(&self) -> &CurveId {
&self.reference
}
pub fn pcurve(&self) -> &Option<PcurveGeometry> {
&self.pcurve
}
pub fn parameter(&self) -> FiniteReal {
self.parameter
}
pub fn taper(&self) -> &TaperSurfaceKind<FiniteReal, FiniteVector3> {
&self.taper
}
pub const fn revision_form(&self) -> Option<&RevisionSurfaceForm<Vec<bool>, FiniteReal>> {
self.cache.form()
}
}
impl TryFrom<TaperSurfaceConstructionWire> for TaperSurfaceConstruction {
type Error = ProceduralGeometryError;
fn try_from(wire: TaperSurfaceConstructionWire) -> Result<Self, Self::Error> {
Self::try_new(
wire.support,
wire.reference,
wire.pcurve,
wire.parameter,
wire.taper,
wire.cache,
)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(
feature = "schema",
schemars(with = "ExtrusionSurfaceConstructionWire")
)]
#[serde(try_from = "ExtrusionSurfaceConstructionWire")]
pub struct ExtrusionSurfaceConstruction {
directrix: CurveId,
#[serde(default, skip_serializing_if = "Option::is_none")]
parameter_interval: Option<FiniteVector<2>>,
direction: FiniteVector3,
#[serde(default, skip_serializing_if = "Option::is_none")]
native_position: Option<FinitePoint3>,
#[serde(default, skip_serializing_if = "CacheContract::is_bare_legacy")]
cache: CacheContract<RevisionSurfaceForm<Vec<bool>, FiniteReal>>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct ExtrusionSurfaceConstructionWire {
directrix: CurveId,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_parameter_interval"
)]
parameter_interval: Option<[f64; 2]>,
direction: Vector3,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_native_position"
)]
native_position: Option<Point3>,
#[serde(default)]
cache: CacheContract<RevisionSurfaceForm>,
}
impl ExtrusionSurfaceConstruction {
pub(super) fn write_revision_fit_tolerance(
&mut self,
write: super::ToleranceWrite,
) -> super::RevisionCacheWrite {
super::write_revision_form_tolerance(
self.cache.form_mut().map(|form| &mut form.cache),
write,
)
}
pub fn try_new(
directrix: CurveId,
parameter_interval: Option<[f64; 2]>,
direction: Vector3,
native_position: Option<Point3>,
cache: CacheContract<RevisionSurfaceForm>,
) -> Result<Self, ProceduralGeometryError> {
let cache = cache.admit_form(RevisionSurfaceForm::admit).ok_or(
ProceduralGeometryError::Payload("Extrusion.cache is invalid"),
)?;
Ok(Self {
directrix,
parameter_interval: parameter_interval
.map(|range| {
FiniteVector::new(range).ok_or(ProceduralGeometryError::Payload(
"Extrusion.parameter_interval is not finite",
))
})
.transpose()?,
direction: FiniteVector3::new(direction).ok_or(ProceduralGeometryError::Payload(
"Extrusion.direction is not finite",
))?,
native_position: native_position
.map(|point| {
FinitePoint3::new(point).ok_or(ProceduralGeometryError::Payload(
"Extrusion.native_position is not finite",
))
})
.transpose()?,
cache,
})
}
#[must_use]
pub const fn legacy(
directrix: CurveId,
parameter_interval: Option<FiniteVector<2>>,
direction: FiniteVector3,
native_position: Option<FinitePoint3>,
cache: Option<LegacyCache>,
) -> Self {
Self {
directrix,
parameter_interval,
direction,
native_position,
cache: CacheContract::Legacy { cache },
}
}
pub fn directrix(&self) -> &CurveId {
&self.directrix
}
pub fn parameter_interval(&self) -> Option<FiniteVector<2>> {
self.parameter_interval
}
pub fn direction(&self) -> &FiniteVector3 {
&self.direction
}
pub fn set_direction(&mut self, direction: FiniteVector3) {
self.direction = direction;
}
pub fn native_position(&self) -> Option<FinitePoint3> {
self.native_position
}
pub fn set_native_position(&mut self, native_position: Option<FinitePoint3>) {
self.native_position = native_position;
}
pub const fn revision_form(&self) -> Option<&RevisionSurfaceForm<Vec<bool>, FiniteReal>> {
self.cache.form()
}
}
impl TryFrom<ExtrusionSurfaceConstructionWire> for ExtrusionSurfaceConstruction {
type Error = ProceduralGeometryError;
fn try_from(wire: ExtrusionSurfaceConstructionWire) -> Result<Self, Self::Error> {
Self::try_new(
wire.directrix,
wire.parameter_interval,
wire.direction,
wire.native_position,
wire.cache,
)
}
}
const INVALID_REVOLUTION_AXIS: ProceduralGeometryError = ProceduralGeometryError::Payload(
"revolution axis_origin and axis_direction must be finite, with unit axis_direction",
);
pub fn admit_revolution_axis(
origin: Point3,
direction: Vector3,
) -> Result<(FinitePoint3, UnitVector3), ProceduralGeometryError> {
let direction = UnitVector3::new(direction).ok_or(INVALID_REVOLUTION_AXIS)?;
Ok((
FinitePoint3::new(origin).ok_or(INVALID_REVOLUTION_AXIS)?,
direction,
))
}
pub fn admit_revolution_axis_from_parts(
origin: FinitePoint3,
direction: FiniteVector3,
) -> Result<(FinitePoint3, UnitVector3), ProceduralGeometryError> {
let direction = UnitVector3::new(direction.get()).ok_or(INVALID_REVOLUTION_AXIS)?;
Ok((origin, direction))
}
fn admit_revolution_interval(
range: [f64; 2],
message: &'static str,
) -> Result<IncreasingParameterInterval, ProceduralGeometryError> {
IncreasingParameterInterval::new(range).ok_or(ProceduralGeometryError::Payload(message))
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(
feature = "schema",
schemars(with = "RevolutionSurfaceConstructionWire")
)]
#[serde(try_from = "RevolutionSurfaceConstructionWire")]
pub struct RevolutionSurfaceConstruction {
directrix: CurveId,
axis_origin: FinitePoint3,
axis_direction: UnitVector3,
angular_interval: IncreasingParameterInterval,
#[serde(default, skip_serializing_if = "Option::is_none")]
angular_parameter_interval: Option<IncreasingParameterInterval>,
#[serde(default, skip_serializing_if = "Option::is_none")]
parameter_interval: Option<IncreasingParameterInterval>,
transposed: bool,
#[serde(default, skip_serializing_if = "CacheContract::is_bare_legacy")]
cache: CacheContract<RevisionSurfaceForm<Vec<bool>, FiniteReal>>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct RevolutionSurfaceConstructionWire {
directrix: CurveId,
axis_origin: Point3,
axis_direction: Vector3,
angular_interval: [f64; 2],
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_angular_parameter_interval"
)]
angular_parameter_interval: Option<[f64; 2]>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_parameter_interval"
)]
parameter_interval: Option<[f64; 2]>,
transposed: bool,
#[serde(default)]
cache: CacheContract<RevisionSurfaceForm>,
}
impl RevolutionSurfaceConstruction {
pub(super) fn write_revision_fit_tolerance(
&mut self,
write: super::ToleranceWrite,
) -> super::RevisionCacheWrite {
super::write_revision_form_tolerance(
self.cache.form_mut().map(|form| &mut form.cache),
write,
)
}
pub fn try_new(
directrix: CurveId,
(axis_origin, axis_direction): (FinitePoint3, UnitVector3),
angular_interval: [f64; 2],
angular_parameter_interval: Option<[f64; 2]>,
parameter_interval: Option<[f64; 2]>,
transposed: bool,
cache: CacheContract<RevisionSurfaceForm>,
) -> Result<Self, ProceduralGeometryError> {
let cache = cache.admit_form(RevisionSurfaceForm::admit).ok_or(
ProceduralGeometryError::Payload("revolution cache form is invalid"),
)?;
let angular_interval = admit_revolution_interval(
angular_interval,
"revolution angular_interval must be finite and strictly increasing",
)?;
let angular_parameter_interval = angular_parameter_interval
.map(|range| {
admit_revolution_interval(
range,
"revolution angular_parameter_interval must be finite and strictly increasing",
)
})
.transpose()?;
let parameter_interval = parameter_interval
.map(|range| {
admit_revolution_interval(
range,
"revolution parameter_interval must be finite and strictly increasing",
)
})
.transpose()?;
Ok(Self {
directrix,
axis_origin,
axis_direction,
angular_interval,
angular_parameter_interval,
parameter_interval,
transposed,
cache,
})
}
#[must_use]
pub const fn legacy(
directrix: CurveId,
(axis_origin, axis_direction): (FinitePoint3, UnitVector3),
angular_interval: IncreasingParameterInterval,
angular_parameter_interval: Option<IncreasingParameterInterval>,
parameter_interval: Option<IncreasingParameterInterval>,
transposed: bool,
cache: Option<LegacyCache>,
) -> Self {
Self {
directrix,
axis_origin,
axis_direction,
angular_interval,
angular_parameter_interval,
parameter_interval,
transposed,
cache: CacheContract::Legacy { cache },
}
}
pub fn directrix(&self) -> &CurveId {
&self.directrix
}
#[must_use]
pub const fn axis_origin(&self) -> FinitePoint3 {
self.axis_origin
}
pub fn set_axis_origin(&mut self, axis_origin: FinitePoint3) {
self.axis_origin = axis_origin;
}
pub const fn axis_direction(&self) -> UnitVector3 {
self.axis_direction
}
pub const fn angular_interval(&self) -> IncreasingParameterInterval {
self.angular_interval
}
pub const fn angular_parameter_interval(&self) -> Option<IncreasingParameterInterval> {
self.angular_parameter_interval
}
pub const fn parameter_interval(&self) -> Option<IncreasingParameterInterval> {
self.parameter_interval
}
pub fn transposed(&self) -> &bool {
&self.transposed
}
pub const fn revision_form(&self) -> Option<&RevisionSurfaceForm<Vec<bool>, FiniteReal>> {
self.cache.form()
}
}
impl TryFrom<RevolutionSurfaceConstructionWire> for RevolutionSurfaceConstruction {
type Error = ProceduralGeometryError;
fn try_from(wire: RevolutionSurfaceConstructionWire) -> Result<Self, Self::Error> {
Self::try_new(
wire.directrix,
admit_revolution_axis(wire.axis_origin, wire.axis_direction)?,
wire.angular_interval,
wire.angular_parameter_interval,
wire.parameter_interval,
wire.transposed,
wire.cache,
)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "OffsetSurfaceConstructionWire"))]
#[serde(try_from = "OffsetSurfaceConstructionWire")]
pub struct OffsetSurfaceConstruction {
support: SurfaceId,
distance: FiniteReal,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_u_sense"
)]
u_sense: Option<i64>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_v_sense"
)]
v_sense: Option<i64>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
linear_support_extension: bool,
extension: OffsetExtension<FiniteReal>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct OffsetSurfaceConstructionWire {
support: SurfaceId,
distance: f64,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_u_sense"
)]
u_sense: Option<i64>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_v_sense"
)]
v_sense: Option<i64>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
linear_support_extension: bool,
extension: OffsetExtension,
}
impl OffsetSurfaceConstruction {
pub fn set_support(&mut self, support: SurfaceId) {
self.support = support;
}
pub(super) fn write_revision_fit_tolerance(
&mut self,
write: super::ToleranceWrite,
) -> super::RevisionCacheWrite {
super::write_revision_form_tolerance(
match &mut self.extension {
OffsetExtension::Revision { form } => Some(&mut form.cache),
OffsetExtension::Legacy { .. } => None,
},
write,
)
}
pub fn try_new(
support: SurfaceId,
distance: f64,
u_sense: Option<i64>,
v_sense: Option<i64>,
linear_support_extension: bool,
extension: OffsetExtension,
) -> Result<Self, ProceduralGeometryError> {
let extension = extension.admit().ok_or(ProceduralGeometryError::Payload(
"Offset.extension is invalid",
))?;
Ok(Self {
support,
distance: FiniteReal::new(distance).ok_or(ProceduralGeometryError::Payload(
"Offset.distance is not finite",
))?,
u_sense,
v_sense,
linear_support_extension,
extension,
})
}
#[must_use]
pub const fn legacy(
support: SurfaceId,
distance: FiniteReal,
u_sense: Option<i64>,
v_sense: Option<i64>,
linear_support_extension: bool,
flags: super::LegacyExtensionFlags,
cache: Option<LegacyCache>,
) -> Self {
Self {
support,
distance,
u_sense,
v_sense,
linear_support_extension,
extension: OffsetExtension::Legacy { flags, cache },
}
}
pub fn support(&self) -> &SurfaceId {
&self.support
}
pub fn distance(&self) -> FiniteReal {
self.distance
}
pub fn u_sense(&self) -> &Option<i64> {
&self.u_sense
}
pub fn v_sense(&self) -> &Option<i64> {
&self.v_sense
}
pub fn linear_support_extension(&self) -> bool {
self.linear_support_extension
}
pub fn extension(&self) -> &OffsetExtension<FiniteReal> {
&self.extension
}
}
impl TryFrom<OffsetSurfaceConstructionWire> for OffsetSurfaceConstruction {
type Error = ProceduralGeometryError;
fn try_from(wire: OffsetSurfaceConstructionWire) -> Result<Self, Self::Error> {
Self::try_new(
wire.support,
wire.distance,
wire.u_sense,
wire.v_sense,
wire.linear_support_extension,
wire.extension,
)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "SubsetSurfaceConstructionWire"))]
#[serde(try_from = "SubsetSurfaceConstructionWire")]
pub struct SubsetSurfaceConstruction {
support: SurfaceId,
parameter_ranges: [DirectedParameterRange; 2],
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_subset_surface_construction_u_sense"
)]
u_sense: Option<bool>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_subset_surface_construction_v_sense"
)]
v_sense: Option<bool>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_cache"
)]
cache: Option<LegacyCache>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct SubsetSurfaceConstructionWire {
support: SurfaceId,
parameter_ranges: [[f64; 2]; 2],
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_subset_surface_construction_u_sense"
)]
u_sense: Option<bool>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_subset_surface_construction_v_sense"
)]
v_sense: Option<bool>,
#[serde(default, deserialize_with = "deserialize_cache")]
cache: Option<LegacyCache>,
}
impl SubsetSurfaceConstruction {
pub fn try_new(
support: SurfaceId,
parameter_ranges: [[f64; 2]; 2],
u_sense: Option<bool>,
v_sense: Option<bool>,
cache: Option<LegacyCache>,
) -> Result<Self, ProceduralGeometryError> {
let parameter_ranges = [
DirectedParameterRange::new(parameter_ranges[0]).map_err(|_| {
ProceduralGeometryError::Payload(
"surface subset ranges are not finite and non-zero",
)
})?,
DirectedParameterRange::new(parameter_ranges[1]).map_err(|_| {
ProceduralGeometryError::Payload(
"surface subset ranges are not finite and non-zero",
)
})?,
];
Ok(Self::from_parts(
support,
parameter_ranges,
u_sense,
v_sense,
cache,
))
}
pub fn from_parts(
support: SurfaceId,
parameter_ranges: [DirectedParameterRange; 2],
u_sense: Option<bool>,
v_sense: Option<bool>,
cache: Option<LegacyCache>,
) -> Self {
Self {
support,
parameter_ranges,
u_sense,
v_sense,
cache,
}
}
pub fn support(&self) -> &SurfaceId {
&self.support
}
pub fn parameter_ranges(&self) -> [DirectedParameterRange; 2] {
self.parameter_ranges
}
pub fn u_sense(&self) -> &Option<bool> {
&self.u_sense
}
pub fn v_sense(&self) -> &Option<bool> {
&self.v_sense
}
}
impl TryFrom<SubsetSurfaceConstructionWire> for SubsetSurfaceConstruction {
type Error = ProceduralGeometryError;
fn try_from(wire: SubsetSurfaceConstructionWire) -> Result<Self, Self::Error> {
Self::try_new(
wire.support,
wire.parameter_ranges,
wire.u_sense,
wire.v_sense,
wire.cache,
)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(
feature = "schema",
schemars(with = "ParallelOffsetSurfaceConstructionWire")
)]
#[serde(try_from = "ParallelOffsetSurfaceConstructionWire")]
pub struct ParallelOffsetSurfaceConstruction {
support: SurfaceId,
distance: FiniteReal,
self_intersect: Option<bool>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct ParallelOffsetSurfaceConstructionWire {
support: SurfaceId,
distance: f64,
#[serde(deserialize_with = "cadmpeg_core::absent_key::nullable")]
self_intersect: Option<bool>,
}
impl ParallelOffsetSurfaceConstruction {
pub fn try_new(
support: SurfaceId,
distance: f64,
self_intersect: Option<bool>,
) -> Result<Self, ProceduralGeometryError> {
Ok(Self {
support,
distance: FiniteReal::new(distance).ok_or(ProceduralGeometryError::Payload(
"ParallelOffset.distance is not finite",
))?,
self_intersect,
})
}
pub fn support(&self) -> &SurfaceId {
&self.support
}
pub fn distance(&self) -> FiniteReal {
self.distance
}
pub fn self_intersect(&self) -> &Option<bool> {
&self.self_intersect
}
}
impl TryFrom<ParallelOffsetSurfaceConstructionWire> for ParallelOffsetSurfaceConstruction {
type Error = ProceduralGeometryError;
fn try_from(wire: ParallelOffsetSurfaceConstructionWire) -> Result<Self, Self::Error> {
Self::try_new(wire.support, wire.distance, wire.self_intersect)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(
feature = "schema",
schemars(with = "LinearSweepSurfaceConstructionWire")
)]
#[serde(try_from = "LinearSweepSurfaceConstructionWire")]
pub struct LinearSweepSurfaceConstruction {
directrix: CurveId,
direction: DirectionAboveEpsilon,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct LinearSweepSurfaceConstructionWire {
directrix: CurveId,
direction: Vector3,
}
impl LinearSweepSurfaceConstruction {
pub fn try_new(
directrix: CurveId,
direction: Vector3,
) -> Result<Self, ProceduralGeometryError> {
let direction = DirectionAboveEpsilon::new(direction).ok_or(
ProceduralGeometryError::Payload("invalid linear-sweep direction"),
)?;
Ok(Self {
directrix,
direction,
})
}
pub fn directrix(&self) -> &CurveId {
&self.directrix
}
pub fn direction(&self) -> &DirectionAboveEpsilon {
&self.direction
}
pub fn set_direction(&mut self, direction: DirectionAboveEpsilon) {
self.direction = direction;
}
}
impl TryFrom<LinearSweepSurfaceConstructionWire> for LinearSweepSurfaceConstruction {
type Error = ProceduralGeometryError;
fn try_from(wire: LinearSweepSurfaceConstructionWire) -> Result<Self, Self::Error> {
Self::try_new(wire.directrix, wire.direction)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(
feature = "schema",
schemars(with = "AxisRevolutionSurfaceConstructionWire")
)]
#[serde(try_from = "AxisRevolutionSurfaceConstructionWire")]
pub struct AxisRevolutionSurfaceConstruction {
directrix: CurveId,
axis_origin: FinitePoint3,
axis_direction: UnitVector3,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct AxisRevolutionSurfaceConstructionWire {
directrix: CurveId,
axis_origin: Point3,
axis_direction: Vector3,
}
impl AxisRevolutionSurfaceConstruction {
pub fn from_parts(
directrix: CurveId,
axis_origin: FinitePoint3,
axis_direction: UnitVector3,
) -> Self {
Self {
directrix,
axis_origin,
axis_direction,
}
}
pub fn try_new(
directrix: CurveId,
axis_origin: Point3,
axis_direction: Vector3,
) -> Result<Self, ProceduralGeometryError> {
let (axis_origin, axis_direction) = admit_revolution_axis(axis_origin, axis_direction)?;
Ok(Self::from_parts(directrix, axis_origin, axis_direction))
}
pub fn directrix(&self) -> &CurveId {
&self.directrix
}
#[must_use]
pub const fn axis_origin(&self) -> FinitePoint3 {
self.axis_origin
}
pub fn set_axis_origin(&mut self, axis_origin: FinitePoint3) {
self.axis_origin = axis_origin;
}
pub const fn axis_direction(&self) -> UnitVector3 {
self.axis_direction
}
}
impl TryFrom<AxisRevolutionSurfaceConstructionWire> for AxisRevolutionSurfaceConstruction {
type Error = ProceduralGeometryError;
fn try_from(wire: AxisRevolutionSurfaceConstructionWire) -> Result<Self, Self::Error> {
Self::try_new(wire.directrix, wire.axis_origin, wire.axis_direction)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "SumSurfaceConstructionWire"))]
#[serde(try_from = "SumSurfaceConstructionWire")]
pub struct SumSurfaceConstruction {
first: CurveId,
second: CurveId,
basepoint: FiniteVector3,
#[serde(default, skip_serializing_if = "CacheContract::is_bare_legacy")]
cache: CacheContract<RevisionSurfaceForm<Vec<bool>, FiniteReal>>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct SumSurfaceConstructionWire {
first: CurveId,
second: CurveId,
basepoint: Vector3,
#[serde(default)]
cache: CacheContract<RevisionSurfaceForm>,
}
impl SumSurfaceConstruction {
pub(super) fn write_revision_fit_tolerance(
&mut self,
write: super::ToleranceWrite,
) -> super::RevisionCacheWrite {
super::write_revision_form_tolerance(
self.cache.form_mut().map(|form| &mut form.cache),
write,
)
}
pub fn try_new(
first: CurveId,
second: CurveId,
basepoint: Vector3,
cache: CacheContract<RevisionSurfaceForm>,
) -> Result<Self, ProceduralGeometryError> {
let cache = cache.admit_form(RevisionSurfaceForm::admit).ok_or(
ProceduralGeometryError::Payload("sum cache form is invalid"),
)?;
Ok(Self {
first,
second,
basepoint: FiniteVector3::new(basepoint).ok_or(ProceduralGeometryError::Payload(
"sum basepoint must be finite",
))?,
cache,
})
}
pub fn first(&self) -> &CurveId {
&self.first
}
pub fn second(&self) -> &CurveId {
&self.second
}
pub fn basepoint(&self) -> &FiniteVector3 {
&self.basepoint
}
pub fn set_basepoint(&mut self, basepoint: FiniteVector3) {
self.basepoint = basepoint;
}
pub const fn revision_form(&self) -> Option<&RevisionSurfaceForm<Vec<bool>, FiniteReal>> {
self.cache.form()
}
}
impl TryFrom<SumSurfaceConstructionWire> for SumSurfaceConstruction {
type Error = ProceduralGeometryError;
fn try_from(wire: SumSurfaceConstructionWire) -> Result<Self, Self::Error> {
Self::try_new(wire.first, wire.second, wire.basepoint, wire.cache)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "ExactSurfacePayloadWire"))]
#[serde(try_from = "ExactSurfacePayloadWire")]
pub struct ExactSurfacePayload {
spline: ExactSpline<FiniteReal>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct ExactSurfacePayloadWire {
spline: ExactSpline,
}
impl ExactSurfacePayload {
pub fn try_new(spline: ExactSpline) -> Result<Self, ProceduralGeometryError> {
let spline = spline
.admit()
.filter(|spline| match spline {
ExactSpline::Legacy { ranges, .. } => ranges.iter().all(ordered),
ExactSpline::Revision { .. } => true,
})
.ok_or(ProceduralGeometryError::Payload(
"exact spline surface parameter fields are invalid",
))?;
Ok(Self { spline })
}
#[must_use]
pub fn from_legacy_intervals(
u: IncreasingParameterInterval,
v: IncreasingParameterInterval,
extension: i64,
cache: Option<LegacyCache>,
) -> Self {
Self {
spline: ExactSpline::Legacy {
ranges: [u.finite_endpoints(), v.finite_endpoints()],
extension,
cache,
},
}
}
pub fn spline(&self) -> &ExactSpline<FiniteReal> {
&self.spline
}
}
impl TryFrom<ExactSurfacePayloadWire> for ExactSurfacePayload {
type Error = ProceduralGeometryError;
fn try_from(wire: ExactSurfacePayloadWire) -> Result<Self, Self::Error> {
Self::try_new(wire.spline)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "CompoundSurfacePayloadWire"))]
#[serde(try_from = "CompoundSurfacePayloadWire")]
pub struct CompoundSurfacePayload {
components: Vec<CompoundComponent<SurfaceId, FiniteReal>>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_cache"
)]
cache: Option<LegacyCache>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct CompoundSurfacePayloadWire {
components: Vec<CompoundComponent<SurfaceId>>,
#[serde(default, deserialize_with = "deserialize_cache")]
cache: Option<LegacyCache>,
}
impl CompoundSurfacePayload {
pub fn try_new(
components: Vec<CompoundComponent<SurfaceId>>,
cache: Option<LegacyCache>,
) -> Result<Self, ProceduralGeometryError> {
let components = components
.into_iter()
.map(CompoundComponent::admit)
.collect::<Option<Vec<_>>>()
.ok_or(ProceduralGeometryError::Payload(
"compound surface parameters and components are inconsistent",
))?;
Ok(Self { components, cache })
}
pub fn components(&self) -> &Vec<CompoundComponent<SurfaceId, FiniteReal>> {
&self.components
}
}
impl TryFrom<CompoundSurfacePayloadWire> for CompoundSurfacePayload {
type Error = ProceduralGeometryError;
fn try_from(wire: CompoundSurfacePayloadWire) -> Result<Self, Self::Error> {
Self::try_new(wire.components, wire.cache)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "LoftSurfacePayloadWire"))]
#[serde(try_from = "LoftSurfacePayloadWire")]
pub struct LoftSurfacePayload {
sections: [LoftSection<FiniteReal, FiniteVector3>; 2],
parameters: SplineSurfaceParameters<FiniteReal>,
closures: [i64; 2],
singularities: [i64; 2],
mode: i64,
bridge: Vec<LoftBridgeToken<FiniteReal>>,
#[serde(default, skip_serializing_if = "CacheContract::is_bare_legacy")]
cache: CacheContract<LoftRevisionForm<FiniteReal>>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct LoftSurfacePayloadWire {
sections: [LoftSection; 2],
parameters: SplineSurfaceParameters,
closures: [i64; 2],
singularities: [i64; 2],
mode: i64,
bridge: Vec<LoftBridgeToken>,
#[serde(default)]
cache: CacheContract<LoftRevisionForm>,
}
impl LoftSurfacePayload {
pub fn try_new(
sections: [LoftSection; 2],
parameters: SplineSurfaceParameters,
closures: [i64; 2],
singularities: [i64; 2],
mode: i64,
bridge: Vec<LoftBridgeToken>,
cache: CacheContract<LoftRevisionForm>,
) -> Result<Self, ProceduralGeometryError> {
let [first, second] = sections;
let (Some(parameters), Some(first), Some(second), Some(cache), Some(bridge)) = (
parameters.admit().filter(|parameters| match parameters {
SplineSurfaceParameters::OrderedRanges { ranges } => ranges.iter().all(ordered),
SplineSurfaceParameters::RevisionRanges { .. } => true,
}),
first.admit(),
second.admit(),
cache.admit_form(LoftRevisionForm::admit),
bridge
.into_iter()
.map(LoftBridgeToken::admit)
.collect::<Option<Vec<_>>>(),
) else {
return Err(ProceduralGeometryError::Payload(
"loft construction payload is invalid",
));
};
let sections = [first, second];
Ok(Self {
sections,
parameters,
closures,
singularities,
mode,
bridge,
cache,
})
}
pub fn sections(&self) -> &[LoftSection<FiniteReal, FiniteVector3>; 2] {
&self.sections
}
pub fn parameters(&self) -> &SplineSurfaceParameters<FiniteReal> {
&self.parameters
}
pub fn closures(&self) -> &[i64; 2] {
&self.closures
}
pub fn singularities(&self) -> &[i64; 2] {
&self.singularities
}
pub fn mode(&self) -> &i64 {
&self.mode
}
pub fn bridge(&self) -> &Vec<LoftBridgeToken<FiniteReal>> {
&self.bridge
}
pub const fn revision_form(&self) -> Option<&LoftRevisionForm<FiniteReal>> {
self.cache.form()
}
}
impl TryFrom<LoftSurfacePayloadWire> for LoftSurfacePayload {
type Error = ProceduralGeometryError;
fn try_from(wire: LoftSurfacePayloadWire) -> Result<Self, Self::Error> {
Self::try_new(
wire.sections,
wire.parameters,
wire.closures,
wire.singularities,
wire.mode,
wire.bridge,
wire.cache,
)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "CompoundLoftSurfacePayloadWire"))]
#[serde(try_from = "CompoundLoftSurfacePayloadWire")]
pub struct CompoundLoftSurfacePayload {
construction: Box<CompoundLoftConstruction<FiniteReal, FiniteVector3>>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_cache"
)]
cache: Option<LegacyCache>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct CompoundLoftSurfacePayloadWire {
construction: Box<CompoundLoftConstruction>,
#[serde(default, deserialize_with = "deserialize_cache")]
cache: Option<LegacyCache>,
}
impl CompoundLoftSurfacePayload {
pub fn try_new(
construction: CompoundLoftConstruction,
cache: Option<LegacyCache>,
) -> Result<Self, ProceduralGeometryError> {
let construction = construction
.admit()
.filter(|construction| match &construction.tail {
CompoundLoftTail::Six {
parameter_range, ..
} => ordered(parameter_range),
CompoundLoftTail::Seven { .. } | CompoundLoftTail::Zero { .. } => true,
})
.ok_or(ProceduralGeometryError::Payload(
"compound loft construction payload is invalid",
))?;
Ok(Self {
construction: Box::new(construction),
cache,
})
}
pub fn construction(&self) -> &CompoundLoftConstruction<FiniteReal, FiniteVector3> {
&self.construction
}
}
impl TryFrom<CompoundLoftSurfacePayloadWire> for CompoundLoftSurfacePayload {
type Error = ProceduralGeometryError;
fn try_from(wire: CompoundLoftSurfacePayloadWire) -> Result<Self, Self::Error> {
Self::try_new(*wire.construction, wire.cache)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(
feature = "schema",
schemars(with = "ScaledCompoundLoftSurfacePayloadWire")
)]
#[serde(try_from = "ScaledCompoundLoftSurfacePayloadWire")]
pub struct ScaledCompoundLoftSurfacePayload {
construction: Box<ScaledCompoundLoftConstruction<FiniteReal, FiniteVector3>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
cache: Option<LegacyCache>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct ScaledCompoundLoftSurfacePayloadWire {
construction: Box<ScaledCompoundLoftConstruction>,
#[serde(default, deserialize_with = "deserialize_cache")]
cache: Option<LegacyCache>,
}
impl ScaledCompoundLoftSurfacePayload {
pub fn try_new(
construction: Box<ScaledCompoundLoftConstruction>,
cache: Option<LegacyCache>,
) -> Result<Self, ProceduralGeometryError> {
let construction = (*construction)
.admit()
.filter(|construction| match &construction.shape {
ScaledCompoundLoftShape::Full {} => true,
ScaledCompoundLoftShape::None {
parameter_ranges, ..
} => parameter_ranges.iter().all(ordered),
})
.ok_or(ProceduralGeometryError::Payload(
"scaled compound loft construction payload is invalid",
))?;
Ok(Self {
construction: Box::new(construction),
cache,
})
}
pub fn construction(&self) -> &ScaledCompoundLoftConstruction<FiniteReal, FiniteVector3> {
&self.construction
}
}
impl TryFrom<ScaledCompoundLoftSurfacePayloadWire> for ScaledCompoundLoftSurfacePayload {
type Error = ProceduralGeometryError;
fn try_from(wire: ScaledCompoundLoftSurfacePayloadWire) -> Result<Self, Self::Error> {
Self::try_new(wire.construction, wire.cache)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "LawSurfacePayloadWire"))]
#[serde(try_from = "LawSurfacePayloadWire")]
pub struct LawSurfacePayload {
construction: Box<LawSurfaceConstruction<FiniteReal, FiniteVector3, FinitePoint3>>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct LawSurfacePayloadWire {
construction: Box<LawSurfaceConstruction>,
}
impl LawSurfacePayload {
pub fn try_new(
construction: Box<LawSurfaceConstruction>,
) -> Result<Self, ProceduralGeometryError> {
let construction = (*construction)
.admit()
.ok_or(ProceduralGeometryError::Payload(
"law surface construction payload is invalid",
))?;
Ok(Self {
construction: Box::new(construction),
})
}
pub fn construction(&self) -> &LawSurfaceConstruction<FiniteReal, FiniteVector3, FinitePoint3> {
&self.construction
}
}
impl TryFrom<LawSurfacePayloadWire> for LawSurfacePayload {
type Error = ProceduralGeometryError;
fn try_from(wire: LawSurfacePayloadWire) -> Result<Self, Self::Error> {
Self::try_new(wire.construction)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "SkinSurfacePayloadWire"))]
#[serde(try_from = "SkinSurfacePayloadWire")]
pub struct SkinSurfacePayload {
construction: Box<SkinSurfaceConstruction<FiniteReal, FiniteVector3, FinitePoint3>>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_cache"
)]
cache: Option<LegacyCache>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct SkinSurfacePayloadWire {
construction: Box<SkinSurfaceConstruction>,
#[serde(default, deserialize_with = "deserialize_cache")]
cache: Option<LegacyCache>,
}
impl SkinSurfacePayload {
pub fn try_new(
construction: Box<SkinSurfaceConstruction>,
cache: Option<LegacyCache>,
) -> Result<Self, ProceduralGeometryError> {
let construction = (*construction)
.admit()
.ok_or(ProceduralGeometryError::Payload(
"skin surface construction payload is invalid",
))?;
Ok(Self {
construction: Box::new(construction),
cache,
})
}
pub fn construction(
&self,
) -> &SkinSurfaceConstruction<FiniteReal, FiniteVector3, FinitePoint3> {
&self.construction
}
}
impl TryFrom<SkinSurfacePayloadWire> for SkinSurfacePayload {
type Error = ProceduralGeometryError;
fn try_from(wire: SkinSurfacePayloadWire) -> Result<Self, Self::Error> {
Self::try_new(wire.construction, wire.cache)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "NetSurfacePayloadWire"))]
#[serde(try_from = "NetSurfacePayloadWire")]
pub struct NetSurfacePayload {
construction: Box<NetSurfaceConstruction<FiniteReal, FiniteVector3, FinitePoint3>>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_cache"
)]
cache: Option<LegacyCache>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct NetSurfacePayloadWire {
construction: Box<NetSurfaceConstruction>,
#[serde(default, deserialize_with = "deserialize_cache")]
cache: Option<LegacyCache>,
}
impl NetSurfacePayload {
pub fn try_new(
construction: Box<NetSurfaceConstruction>,
cache: Option<LegacyCache>,
) -> Result<Self, ProceduralGeometryError> {
let construction = (*construction)
.admit()
.ok_or(ProceduralGeometryError::Payload(
"net surface construction payload is invalid",
))?;
Ok(Self {
construction: Box::new(construction),
cache,
})
}
pub fn construction(&self) -> &NetSurfaceConstruction<FiniteReal, FiniteVector3, FinitePoint3> {
&self.construction
}
}
impl TryFrom<NetSurfacePayloadWire> for NetSurfacePayload {
type Error = ProceduralGeometryError;
fn try_from(wire: NetSurfacePayloadWire) -> Result<Self, Self::Error> {
Self::try_new(wire.construction, wire.cache)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "SweepSurfacePayloadWire"))]
#[serde(try_from = "SweepSurfacePayloadWire")]
pub struct SweepSurfacePayload {
profile: CurveId,
spine: CurveId,
#[serde(default, skip_serializing_if = "Option::is_none")]
native: Option<Box<SweepSurfaceConstruction<FiniteReal, FiniteVector3, FinitePoint3>>>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct SweepSurfacePayloadWire {
profile: CurveId,
spine: CurveId,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_native"
)]
native: Option<Box<SweepSurfaceConstruction>>,
}
impl SweepSurfacePayload {
pub fn try_new(
profile: CurveId,
spine: CurveId,
native: Option<Box<SweepSurfaceConstruction>>,
) -> Result<Self, ProceduralGeometryError> {
let native = match native {
None => None,
Some(construction) => Some(Box::new((*construction).admit().ok_or(
ProceduralGeometryError::Payload("sweep surface construction payload is invalid"),
)?)),
};
Ok(Self {
profile,
spine,
native,
})
}
pub fn profile(&self) -> &CurveId {
&self.profile
}
pub fn spine(&self) -> &CurveId {
&self.spine
}
pub fn native(
&self,
) -> &Option<Box<SweepSurfaceConstruction<FiniteReal, FiniteVector3, FinitePoint3>>> {
&self.native
}
}
impl TryFrom<SweepSurfacePayloadWire> for SweepSurfacePayload {
type Error = ProceduralGeometryError;
fn try_from(wire: SweepSurfacePayloadWire) -> Result<Self, Self::Error> {
Self::try_new(wire.profile, wire.spine, wire.native)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "DeformableSurfacePayloadWire"))]
#[serde(try_from = "DeformableSurfacePayloadWire")]
pub struct DeformableSurfacePayload {
construction: Box<DeformableSurfaceConstruction<FiniteReal, FiniteVector3, FinitePoint3>>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct DeformableSurfacePayloadWire {
construction: Box<DeformableSurfaceConstruction>,
}
impl DeformableSurfacePayload {
pub fn try_new(
construction: Box<DeformableSurfaceConstruction>,
) -> Result<Self, ProceduralGeometryError> {
let construction = (*construction)
.admit()
.ok_or(ProceduralGeometryError::Payload(
"deformable surface construction payload is invalid",
))?;
Ok(Self {
construction: Box::new(construction),
})
}
pub fn construction(
&self,
) -> &DeformableSurfaceConstruction<FiniteReal, FiniteVector3, FinitePoint3> {
&self.construction
}
}
impl TryFrom<DeformableSurfacePayloadWire> for DeformableSurfacePayload {
type Error = ProceduralGeometryError;
fn try_from(wire: DeformableSurfacePayloadWire) -> Result<Self, Self::Error> {
Self::try_new(wire.construction)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "G2BlendSurfacePayloadWire"))]
#[serde(try_from = "G2BlendSurfacePayloadWire")]
pub struct G2BlendSurfacePayload {
construction: Box<G2BlendConstruction<FiniteReal, FiniteVector3>>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_cache"
)]
cache: Option<LegacyCache>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct G2BlendSurfacePayloadWire {
construction: Box<G2BlendConstruction>,
#[serde(default, deserialize_with = "deserialize_cache")]
cache: Option<LegacyCache>,
}
impl G2BlendSurfacePayload {
pub fn try_new(
construction: Box<G2BlendConstruction>,
cache: Option<LegacyCache>,
) -> Result<Self, ProceduralGeometryError> {
let construction = (*construction)
.admit()
.filter(|construction| construction.parameter_ranges.iter().all(ordered))
.ok_or(ProceduralGeometryError::Payload(
"G2 blend construction payload is invalid",
))?;
Ok(Self {
construction: Box::new(construction),
cache,
})
}
pub fn construction(&self) -> &G2BlendConstruction<FiniteReal, FiniteVector3> {
&self.construction
}
}
impl TryFrom<G2BlendSurfacePayloadWire> for G2BlendSurfacePayload {
type Error = ProceduralGeometryError;
fn try_from(wire: G2BlendSurfacePayloadWire) -> Result<Self, Self::Error> {
Self::try_new(wire.construction, wire.cache)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "VariableBlendSurfacePayloadWire"))]
#[serde(try_from = "VariableBlendSurfacePayloadWire")]
pub struct VariableBlendSurfacePayload {
construction: Box<VariableBlendConstruction<FiniteReal, FiniteVector3, FinitePoint3>>,
#[serde(skip)]
slice_range: OrderedOptionalRange,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct VariableBlendSurfacePayloadWire {
construction: Box<VariableBlendConstruction>,
}
impl VariableBlendSurfacePayload {
pub fn try_new(
construction: Box<VariableBlendConstruction>,
) -> Result<Self, ProceduralGeometryError> {
const INVALID: &str = "variable blend construction payload is invalid";
let construction = (*construction)
.admit()
.ok_or(ProceduralGeometryError::Payload(INVALID))?;
if !ordered(&construction.u_range) || !optional_ordered(&construction.post_range) {
return Err(ProceduralGeometryError::Payload(INVALID));
}
let slice_range = OrderedOptionalRange::new(construction.slice_range)
.ok_or(ProceduralGeometryError::Payload(INVALID))?;
if construction
.secondary_curve
.as_ref()
.is_some_and(|curve| !optional_ordered(&curve.parameter_range))
{
return Err(ProceduralGeometryError::Payload(INVALID));
}
Ok(Self {
construction: Box::new(construction),
slice_range,
})
}
pub fn construction(
&self,
) -> &VariableBlendConstruction<FiniteReal, FiniteVector3, FinitePoint3> {
&self.construction
}
pub(crate) fn slice_range(&self) -> OrderedOptionalRange {
self.slice_range
}
}
impl TryFrom<VariableBlendSurfacePayloadWire> for VariableBlendSurfacePayload {
type Error = ProceduralGeometryError;
fn try_from(wire: VariableBlendSurfacePayloadWire) -> Result<Self, Self::Error> {
Self::try_new(wire.construction)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "VertexBlendSurfacePayloadWire"))]
#[serde(try_from = "VertexBlendSurfacePayloadWire")]
pub struct VertexBlendSurfacePayload {
construction: Box<VertexBlendConstruction<FiniteReal, FiniteVector3, FinitePoint3>>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct VertexBlendSurfacePayloadWire {
construction: Box<VertexBlendConstruction>,
}
impl VertexBlendSurfacePayload {
pub fn try_new(construction: VertexBlendConstruction) -> Result<Self, ProceduralGeometryError> {
let construction = construction
.admit()
.filter(|construction| {
construction
.boundaries
.iter()
.all(|boundary| match &boundary.geometry {
VertexBlendBoundaryGeometry::Degenerate { normals, .. } => {
normals.iter().all(|normal| normal.norm() > f64::EPSILON)
}
VertexBlendBoundaryGeometry::Plane { normal, .. } => {
normal.norm() > f64::EPSILON
}
VertexBlendBoundaryGeometry::Circle { .. }
| VertexBlendBoundaryGeometry::Pcurve { .. } => true,
})
})
.ok_or(ProceduralGeometryError::Payload(
"vertex blend construction payload is invalid",
))?;
Ok(Self {
construction: Box::new(construction),
})
}
pub fn construction(
&self,
) -> &VertexBlendConstruction<FiniteReal, FiniteVector3, FinitePoint3> {
&self.construction
}
}
impl TryFrom<VertexBlendSurfacePayloadWire> for VertexBlendSurfacePayload {
type Error = ProceduralGeometryError;
fn try_from(wire: VertexBlendSurfacePayloadWire) -> Result<Self, Self::Error> {
Self::try_new(*wire.construction)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct OrderedOptionalRange([Option<FiniteReal>; 2]);
impl OrderedOptionalRange {
fn new(endpoints: [Option<FiniteReal>; 2]) -> Option<Self> {
optional_ordered(&endpoints).then_some(Self(endpoints))
}
pub(crate) fn endpoints(self) -> [Option<FiniteReal>; 2] {
self.0
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "schema", schemars(with = "BlendSurfacePayloadWire"))]
#[serde(try_from = "BlendSurfacePayloadWire")]
pub struct BlendSurfacePayload {
supports: [Option<BlendSupport>; 2],
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_spine"
)]
spine: Option<CurveId>,
radius: BlendRadiusLaw,
cross_section: BlendCrossSection,
#[serde(default, skip_serializing_if = "CacheContract::is_bare_legacy")]
cache: CacheContract<Box<RollingBallConstruction<FiniteReal, FiniteVector3, FinitePoint3>>>,
#[serde(skip)]
native_ranges: Option<[OrderedOptionalRange; 2]>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(deny_unknown_fields)]
struct BlendSurfacePayloadWire {
supports: [Option<BlendSupport>; 2],
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_spine"
)]
spine: Option<CurveId>,
radius: BlendRadiusLaw,
cross_section: BlendCrossSection,
#[serde(default)]
cache: CacheContract<Box<RollingBallConstruction>>,
}
impl BlendSurfacePayload {
pub fn try_new(
supports: [Option<BlendSupport>; 2],
spine: Option<CurveId>,
radius: BlendRadiusLaw,
cross_section: BlendCrossSection,
cache: CacheContract<Box<RollingBallConstruction>>,
) -> Result<Self, ProceduralGeometryError> {
let mut native_ranges = None;
let cache = cache
.admit_form(|construction| {
let construction = (*construction).admit()?;
native_ranges = Some([
OrderedOptionalRange::new(construction.u_range)?,
OrderedOptionalRange::new(construction.v_range)?,
]);
Some(Box::new(construction))
})
.ok_or(ProceduralGeometryError::Payload(
"rolling-ball blend construction payload is invalid",
))?;
Ok(Self {
supports,
spine,
radius,
cross_section,
cache,
native_ranges,
})
}
pub fn supports(&self) -> &[Option<BlendSupport>; 2] {
&self.supports
}
pub fn spine(&self) -> &Option<CurveId> {
&self.spine
}
pub fn radius(&self) -> &BlendRadiusLaw {
&self.radius
}
pub fn cross_section(&self) -> &BlendCrossSection {
&self.cross_section
}
pub fn native(
&self,
) -> Option<&RollingBallConstruction<FiniteReal, FiniteVector3, FinitePoint3>> {
self.cache.form().map(Box::as_ref)
}
pub(crate) fn native_ranges(&self) -> Option<[OrderedOptionalRange; 2]> {
self.native_ranges
}
}
impl TryFrom<BlendSurfacePayloadWire> for BlendSurfacePayload {
type Error = ProceduralGeometryError;
fn try_from(wire: BlendSurfacePayloadWire) -> Result<Self, Self::Error> {
Self::try_new(
wire.supports,
wire.spine,
wire.radius,
wire.cross_section,
wire.cache,
)
}
}
impl ExactSurfacePayload {
pub(super) fn revision_cache(
&self,
) -> Option<&super::RevisionCacheForm<super::RevisionSurfaceParameterization<FiniteReal>>> {
match &self.spline {
ExactSpline::Revision { form, .. } => Some(&form.cache),
ExactSpline::Legacy { .. } => None,
}
}
pub(super) fn write_revision_fit_tolerance(
&mut self,
write: super::ToleranceWrite,
) -> super::RevisionCacheWrite {
super::write_revision_form_tolerance(
match &mut self.spline {
ExactSpline::Revision { form, .. } => Some(&mut form.cache),
ExactSpline::Legacy { .. } => None,
},
write,
)
}
}
impl LoftSurfacePayload {
pub(super) fn revision_cache(
&self,
) -> Option<&super::RevisionCacheForm<super::RevisionSurfaceParameterization<FiniteReal>>> {
self.cache.form().map(|form| &form.cache)
}
pub(super) fn write_revision_fit_tolerance(
&mut self,
write: super::ToleranceWrite,
) -> super::RevisionCacheWrite {
super::write_revision_form_tolerance(
self.cache.form_mut().map(|form| &mut form.cache),
write,
)
}
}
impl SweepSurfacePayload {
pub(super) fn revision_cache(
&self,
) -> Option<&super::RevisionCacheForm<super::RevisionSurfaceParameterization<FiniteReal>>> {
self.native
.as_ref()
.and_then(|construction| construction.cache.form())
.map(|form| &form.cache)
}
pub(super) fn write_revision_fit_tolerance(
&mut self,
write: super::ToleranceWrite,
) -> super::RevisionCacheWrite {
super::write_revision_form_tolerance(
self.native
.as_mut()
.and_then(|construction| construction.cache.form_mut())
.map(|form| &mut form.cache),
write,
)
}
}
impl DeformableSurfacePayload {
pub(super) fn revision_cache(
&self,
) -> Option<&super::RevisionCacheForm<super::RevisionSurfaceParameterization<FiniteReal>>> {
self.construction.cache.form().map(|form| &form.cache)
}
pub(super) fn write_revision_fit_tolerance(
&mut self,
write: super::ToleranceWrite,
) -> super::RevisionCacheWrite {
super::write_revision_form_tolerance(
self.construction
.cache
.form_mut()
.map(|form| &mut form.cache),
write,
)
}
}
impl BlendSurfacePayload {
pub(super) fn revision_cache(
&self,
) -> Option<&super::RevisionCacheForm<super::RevisionSurfaceParameterization<FiniteReal>>> {
self.cache.form().map(|construction| &construction.cache)
}
pub(super) fn write_revision_fit_tolerance(
&mut self,
write: super::ToleranceWrite,
) -> super::RevisionCacheWrite {
super::write_revision_form_tolerance(
self.cache
.form_mut()
.map(|construction| &mut construction.cache),
write,
)
}
}
impl VariableBlendSurfacePayload {
pub(super) fn write_cache_fit_tolerance(
&mut self,
value: Option<super::FitTolerance>,
) -> Result<(), super::CacheContractError> {
super::set_variable_blend_cache(&mut self.construction.cache, value)
}
pub(super) fn scale_cache_fit_tolerance(
&mut self,
scale: crate::scalar::PositiveReal,
) -> Result<(), super::CacheContractError> {
self.construction.cache.scale_fit_tolerance(scale)
}
}
impl BlendSurfacePayload {
pub fn set_supports(&mut self, supports: [Option<BlendSupport>; 2]) {
self.supports = supports;
}
pub fn set_spine(&mut self, spine: Option<CurveId>) {
self.spine = spine;
}
}
fn ordered(range: &[FiniteReal; 2]) -> bool {
range[0] <= range[1]
}
fn optional_ordered(range: &[Option<FiniteReal>; 2]) -> bool {
match range {
[Some(lower), Some(upper)] => lower <= upper,
[None | Some(_), None] | [None, Some(_)] => true,
}
}
impl CompoundSurfacePayload {
#[must_use]
pub const fn legacy_cache(&self) -> Option<LegacyCache> {
self.cache
}
pub(super) const fn legacy_cache_slot_mut(&mut self) -> LegacyCacheSlot<'_> {
LegacyCacheSlot::Optional(&mut self.cache)
}
}
impl CompoundLoftSurfacePayload {
#[must_use]
pub const fn legacy_cache(&self) -> Option<LegacyCache> {
self.cache
}
pub(super) const fn legacy_cache_slot_mut(&mut self) -> LegacyCacheSlot<'_> {
LegacyCacheSlot::Optional(&mut self.cache)
}
}
impl ScaledCompoundLoftSurfacePayload {
#[must_use]
pub const fn legacy_cache(&self) -> Option<LegacyCache> {
self.cache
}
pub(super) const fn legacy_cache_slot_mut(&mut self) -> LegacyCacheSlot<'_> {
LegacyCacheSlot::Optional(&mut self.cache)
}
}
impl SkinSurfacePayload {
#[must_use]
pub const fn legacy_cache(&self) -> Option<LegacyCache> {
self.cache
}
pub(super) const fn legacy_cache_slot_mut(&mut self) -> LegacyCacheSlot<'_> {
LegacyCacheSlot::Optional(&mut self.cache)
}
}
impl LawSurfacePayload {
#[must_use]
pub const fn legacy_cache(&self) -> Option<LegacyCache> {
match &self.construction.tail {
crate::geometry::LawSurfaceTail::Full { cache } => Some(*cache),
_ => None,
}
}
pub(super) fn legacy_cache_slot_mut(&mut self) -> Option<LegacyCacheSlot<'_>> {
match &mut self.construction.tail {
crate::geometry::LawSurfaceTail::Full { cache } => {
Some(LegacyCacheSlot::Required(cache))
}
_ => None,
}
}
}
impl NetSurfacePayload {
#[must_use]
pub const fn legacy_cache(&self) -> Option<LegacyCache> {
self.cache
}
pub(super) const fn legacy_cache_slot_mut(&mut self) -> LegacyCacheSlot<'_> {
LegacyCacheSlot::Optional(&mut self.cache)
}
}
impl G2BlendSurfacePayload {
#[must_use]
pub const fn legacy_cache(&self) -> Option<LegacyCache> {
self.cache
}
pub(super) const fn legacy_cache_slot_mut(&mut self) -> LegacyCacheSlot<'_> {
LegacyCacheSlot::Optional(&mut self.cache)
}
}
impl SubsetSurfaceConstruction {
#[must_use]
pub const fn legacy_cache(&self) -> Option<LegacyCache> {
self.cache
}
pub(super) const fn legacy_cache_slot_mut(&mut self) -> LegacyCacheSlot<'_> {
LegacyCacheSlot::Optional(&mut self.cache)
}
}
impl TaperSurfaceConstruction {
#[must_use]
pub const fn legacy_cache(&self) -> Option<LegacyCache> {
match self.cache.legacy_fit_tolerance() {
Some(fit_tolerance) => Some(LegacyCache { fit_tolerance }),
None => None,
}
}
pub(super) fn legacy_cache_slot_mut(&mut self) -> Option<LegacyCacheSlot<'_>> {
self.cache.legacy_cache_mut().map(LegacyCacheSlot::Optional)
}
}
impl ExtrusionSurfaceConstruction {
#[must_use]
pub const fn legacy_cache(&self) -> Option<LegacyCache> {
match self.cache.legacy_fit_tolerance() {
Some(fit_tolerance) => Some(LegacyCache { fit_tolerance }),
None => None,
}
}
pub(super) fn legacy_cache_slot_mut(&mut self) -> Option<LegacyCacheSlot<'_>> {
self.cache.legacy_cache_mut().map(LegacyCacheSlot::Optional)
}
}
impl RevolutionSurfaceConstruction {
#[must_use]
pub const fn legacy_cache(&self) -> Option<LegacyCache> {
match self.cache.legacy_fit_tolerance() {
Some(fit_tolerance) => Some(LegacyCache { fit_tolerance }),
None => None,
}
}
pub(super) fn legacy_cache_slot_mut(&mut self) -> Option<LegacyCacheSlot<'_>> {
self.cache.legacy_cache_mut().map(LegacyCacheSlot::Optional)
}
}
impl SumSurfaceConstruction {
#[must_use]
pub const fn legacy_cache(&self) -> Option<LegacyCache> {
match self.cache.legacy_fit_tolerance() {
Some(fit_tolerance) => Some(LegacyCache { fit_tolerance }),
None => None,
}
}
pub(super) fn legacy_cache_slot_mut(&mut self) -> Option<LegacyCacheSlot<'_>> {
self.cache.legacy_cache_mut().map(LegacyCacheSlot::Optional)
}
}
impl LoftSurfacePayload {
#[must_use]
pub const fn legacy_cache(&self) -> Option<LegacyCache> {
match self.cache.legacy_fit_tolerance() {
Some(fit_tolerance) => Some(LegacyCache { fit_tolerance }),
None => None,
}
}
pub(super) fn legacy_cache_slot_mut(&mut self) -> Option<LegacyCacheSlot<'_>> {
self.cache.legacy_cache_mut().map(LegacyCacheSlot::Optional)
}
}
impl ExactSurfacePayload {
#[must_use]
pub const fn legacy_cache(&self) -> Option<LegacyCache> {
match &self.spline {
ExactSpline::Legacy { cache, .. } => *cache,
ExactSpline::Revision { .. } => None,
}
}
pub(super) fn legacy_cache_slot_mut(&mut self) -> Option<LegacyCacheSlot<'_>> {
match &mut self.spline {
ExactSpline::Legacy { cache, .. } => Some(LegacyCacheSlot::Optional(cache)),
ExactSpline::Revision { .. } => None,
}
}
}
impl OffsetSurfaceConstruction {
#[must_use]
pub const fn legacy_cache(&self) -> Option<LegacyCache> {
match &self.extension {
OffsetExtension::Legacy { cache, .. } => *cache,
OffsetExtension::Revision { .. } => None,
}
}
pub(super) fn legacy_cache_slot_mut(&mut self) -> Option<LegacyCacheSlot<'_>> {
match &mut self.extension {
OffsetExtension::Legacy { cache, .. } => Some(LegacyCacheSlot::Optional(cache)),
OffsetExtension::Revision { .. } => None,
}
}
}
impl DeformableSurfacePayload {
#[must_use]
pub const fn legacy_cache(&self) -> Option<LegacyCache> {
match self.construction.cache.legacy_fit_tolerance() {
Some(fit_tolerance) => Some(LegacyCache { fit_tolerance }),
None => None,
}
}
pub(super) fn legacy_cache_slot_mut(&mut self) -> Option<LegacyCacheSlot<'_>> {
self.construction
.cache
.legacy_cache_mut()
.map(LegacyCacheSlot::Optional)
}
}
impl SweepSurfacePayload {
#[must_use]
pub fn legacy_cache(&self) -> Option<LegacyCache> {
self.native
.as_ref()
.and_then(|construction| construction.cache.legacy_fit_tolerance())
.map(|fit_tolerance| LegacyCache { fit_tolerance })
}
pub(super) fn legacy_cache_slot_mut(&mut self) -> Option<LegacyCacheSlot<'_>> {
self.native
.as_mut()
.and_then(|construction| construction.cache.legacy_cache_mut())
.map(LegacyCacheSlot::Optional)
}
}
impl BlendSurfacePayload {
#[must_use]
pub const fn legacy_cache(&self) -> Option<LegacyCache> {
match self.cache.legacy_fit_tolerance() {
Some(fit_tolerance) => Some(LegacyCache { fit_tolerance }),
None => None,
}
}
pub(super) fn legacy_cache_slot_mut(&mut self) -> Option<LegacyCacheSlot<'_>> {
self.cache.legacy_cache_mut().map(LegacyCacheSlot::Optional)
}
}
#[cfg(test)]
mod tests;
cadmpeg_core::named_optional_field!(deserialize_pcurve, PcurveGeometry, "pcurve");
cadmpeg_core::named_optional_field!(
deserialize_parameter_interval,
[f64; 2],
"parameter_interval"
);
cadmpeg_core::named_optional_field!(deserialize_native_position, Point3, "native_position");
cadmpeg_core::named_optional_field!(
deserialize_angular_parameter_interval,
[f64; 2],
"angular_parameter_interval"
);
cadmpeg_core::named_optional_field!(deserialize_u_sense, i64, "u_sense");
cadmpeg_core::named_optional_field!(deserialize_v_sense, i64, "v_sense");
cadmpeg_core::named_optional_field!(
deserialize_subset_surface_construction_u_sense,
bool,
"u_sense"
);
cadmpeg_core::named_optional_field!(
deserialize_subset_surface_construction_v_sense,
bool,
"v_sense"
);
cadmpeg_core::named_optional_field!(deserialize_cache, LegacyCache, "cache");
cadmpeg_core::named_optional_field!(deserialize_native, Box<SweepSurfaceConstruction>, "native");
cadmpeg_core::named_optional_field!(deserialize_spine, CurveId, "spine");
mod identity_rewrite;