use crate::ids::{CurveId, PcurveId, ProceduralCurveId, ProceduralSurfaceId, SurfaceId, UnknownId};
use crate::math::{Point2, Point3, Vector3};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct NurbsSurface {
pub u_degree: u32,
pub v_degree: u32,
pub u_knots: Vec<f64>,
pub v_knots: Vec<f64>,
pub u_count: u32,
pub v_count: u32,
pub control_points: Vec<Point3>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub weights: Option<Vec<f64>>,
pub u_periodic: bool,
pub v_periodic: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct NurbsCurve {
pub degree: u32,
pub knots: Vec<f64>,
pub control_points: Vec<Point3>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub weights: Option<Vec<f64>>,
pub periodic: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum SurfaceGeometry {
Plane {
origin: Point3,
normal: Vector3,
u_axis: Vector3,
},
Cylinder {
origin: Point3,
axis: Vector3,
ref_direction: Vector3,
radius: f64,
},
Cone {
origin: Point3,
axis: Vector3,
ref_direction: Vector3,
radius: f64,
half_angle: f64,
},
Sphere {
center: Point3,
axis: Vector3,
ref_direction: Vector3,
radius: f64,
},
Torus {
center: Point3,
axis: Vector3,
ref_direction: Vector3,
major_radius: f64,
minor_radius: f64,
},
Nurbs(NurbsSurface),
Unknown {
#[serde(default, skip_serializing_if = "Option::is_none")]
record: Option<UnknownId>,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct Surface {
pub id: SurfaceId,
pub geometry: SurfaceGeometry,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum CurveGeometry {
Line {
origin: Point3,
direction: Vector3,
},
Circle {
center: Point3,
axis: Vector3,
ref_direction: Vector3,
radius: f64,
},
Ellipse {
center: Point3,
axis: Vector3,
major_direction: Vector3,
major_radius: f64,
minor_radius: f64,
},
Parabola {
vertex: Point3,
axis: Vector3,
major_direction: Vector3,
focal_distance: f64,
},
Hyperbola {
center: Point3,
axis: Vector3,
major_direction: Vector3,
major_radius: f64,
minor_radius: f64,
},
Degenerate {
point: Point3,
},
Nurbs(NurbsCurve),
Unknown {
#[serde(default, skip_serializing_if = "Option::is_none")]
record: Option<UnknownId>,
},
}
pub fn derive_reference_direction(axis: Vector3) -> Vector3 {
let norm = axis.norm();
if !norm.is_finite() || norm == 0.0 {
return Vector3::new(1.0, 0.0, 0.0);
}
let axis = Vector3::new(axis.x / norm, axis.y / norm, axis.z / norm);
let basis = if axis.x.abs() <= axis.y.abs() && axis.x.abs() <= axis.z.abs() {
Vector3::new(1.0, 0.0, 0.0)
} else if axis.y.abs() <= axis.z.abs() {
Vector3::new(0.0, 1.0, 0.0)
} else {
Vector3::new(0.0, 0.0, 1.0)
};
let dot = axis.x * basis.x + axis.y * basis.y + axis.z * basis.z;
let projected = Vector3::new(
basis.x - dot * axis.x,
basis.y - dot * axis.y,
basis.z - dot * axis.z,
);
let projected_norm = projected.norm();
Vector3::new(
projected.x / projected_norm,
projected.y / projected_norm,
projected.z / projected_norm,
)
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct Curve {
pub id: CurveId,
pub geometry: CurveGeometry,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct ProceduralSurface {
pub id: ProceduralSurfaceId,
pub surface: SurfaceId,
pub definition: ProceduralSurfaceDefinition,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cache_fit_tolerance: Option<f64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ProceduralSurfaceDefinition {
Extrusion {
directrix: CurveId,
direction: Vector3,
},
Revolution {
directrix: CurveId,
axis_origin: Point3,
axis_direction: Vector3,
},
Sweep {
profile: CurveId,
spine: CurveId,
},
Offset {
support: SurfaceId,
distance: f64,
},
Ruled {
first: CurveId,
second: CurveId,
},
Blend {
supports: [Option<BlendSupport>; 2],
#[serde(default, skip_serializing_if = "Option::is_none")]
spine: Option<CurveId>,
radius: BlendRadiusLaw,
cross_section: BlendCrossSection,
},
Unknown {
#[serde(default, skip_serializing_if = "Option::is_none")]
record: Option<UnknownId>,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct BlendSupport {
pub surface: SurfaceId,
#[serde(default)]
pub reversed: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum BlendCrossSection {
Circular,
Conic,
Polynomial,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum BlendRadiusLaw {
Constant {
signed_radius: f64,
},
Linear {
start: f64,
end: f64,
},
Law {
curve: NurbsCurve,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct ProceduralCurve {
pub id: ProceduralCurveId,
pub curve: CurveId,
pub definition: ProceduralCurveDefinition,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cache_fit_tolerance: Option<f64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct IntcurveSupportSide {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub surface: Option<SurfaceId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pcurve: Option<PcurveGeometry>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct IntcurveSupportContext {
pub sides: [IntcurveSupportSide; 2],
pub parameter_range: [f64; 2],
pub discontinuities: [Vec<f64>; 3],
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ProjectionTail {
EarlyClose {
flag: bool,
},
Ranged {
flag: bool,
parameter_range: [f64; 2],
role: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum SurfaceCurveFamily {
Blend,
SurfaceConstrained,
Parametric,
Skin,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ProceduralCurveDefinition {
Exact,
Compound {
parameters: Vec<f64>,
component_parameters: Vec<f64>,
components: Vec<CurveId>,
},
Helix {
angle_range: [f64; 2],
center: Point3,
major: Vector3,
minor: Vector3,
pitch: Vector3,
apex_factor: f64,
axis: Vector3,
},
Intersection {
context: IntcurveSupportContext,
},
ThreeSurfaceIntersection {
context: IntcurveSupportContext,
selector: i64,
third: IntcurveSupportSide,
},
SurfaceCurve {
family: SurfaceCurveFamily,
context: IntcurveSupportContext,
},
Projection {
context: IntcurveSupportContext,
source: CurveId,
tail: ProjectionTail,
},
Offset {
source: CurveId,
distance: f64,
#[serde(default, skip_serializing_if = "Option::is_none")]
support: Option<SurfaceId>,
},
TwoSidedOffset {
context: IntcurveSupportContext,
offsets: [f64; 2],
},
VectorOffset {
source: CurveId,
parameter_range: [f64; 2],
offset: Vector3,
labels: [String; 2],
codes: [i64; 2],
},
Subset {
source: CurveId,
parameter_range: [f64; 2],
},
BlendSpine {
#[serde(default, skip_serializing_if = "Option::is_none")]
blend_surface: Option<SurfaceId>,
},
Unknown {
#[serde(default, skip_serializing_if = "Option::is_none")]
record: Option<UnknownId>,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum PcurveGeometry {
Line {
origin: Point2,
direction: Point2,
},
Nurbs {
degree: u32,
knots: Vec<f64>,
control_points: Vec<Point2>,
#[serde(default, skip_serializing_if = "Option::is_none")]
weights: Option<Vec<f64>>,
#[serde(default)]
periodic: bool,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct Pcurve {
pub id: PcurveId,
pub geometry: PcurveGeometry,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub wrapper_reversed: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parameter_range: Option<[f64; 2]>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub fit_tolerance: Option<f64>,
}