use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
use crate::Expr;
use crate::Number;
use crate::ObjectId;
use crate::front::Plane;
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts", rename = "ApiSketch")]
pub struct Sketch {
pub args: SketchCtor,
pub plane: ObjectId,
pub segments: Vec<ObjectId>,
pub constraints: Vec<ObjectId>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct SketchCtor {
pub on: Plane,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts", rename = "ApiSegment")]
#[serde(tag = "type")]
pub enum Segment {
Point(Point),
Line(Line),
Arc(Arc),
Circle(Circle),
ControlPointSpline(ControlPointSpline),
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts", rename = "ApiPoint")]
pub struct Point {
pub position: Point2d<Number>,
pub ctor: Option<PointCtor>,
pub owner: Option<ObjectId>,
pub freedom: Freedom,
pub constraints: Vec<ObjectId>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts", rename = "ApiLine")]
pub struct Line {
pub start: ObjectId,
pub end: ObjectId,
#[serde(skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub owner: Option<ObjectId>,
pub ctor: SegmentCtor,
pub ctor_applicable: bool,
pub construction: bool,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts", rename = "ApiArc")]
pub struct Arc {
pub start: ObjectId,
pub end: ObjectId,
pub center: ObjectId,
pub ctor: SegmentCtor,
pub ctor_applicable: bool,
pub construction: bool,
#[serde(default, skip_serializing_if = "ArcDirection::is_ccw")]
#[ts(as = "Option<ArcDirection>")]
#[ts(optional)]
pub direction: ArcDirection,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts", rename = "ApiCircle")]
pub struct Circle {
pub start: ObjectId,
pub center: ObjectId,
pub ctor: SegmentCtor,
pub ctor_applicable: bool,
pub construction: bool,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
#[serde(tag = "type")]
pub enum SegmentCtor {
Point(PointCtor),
Line(LineCtor),
Arc(ArcCtor),
Circle(CircleCtor),
ControlPointSpline(ControlPointSplineCtor),
}
impl SegmentCtor {
pub fn human_friendly_kind_with_article(&self) -> &'static str {
match self {
Self::Point(_) => "a Point constructor",
Self::Line(_) => "a Line constructor",
Self::Arc(_) => "an Arc constructor",
Self::Circle(_) => "a Circle constructor",
Self::ControlPointSpline(_) => "a Control Point Spline constructor",
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
#[serde(rename_all = "lowercase")]
pub enum ArcDirection {
#[default]
Ccw,
Cw,
}
impl ArcDirection {
pub fn is_ccw(&self) -> bool {
matches!(self, ArcDirection::Ccw)
}
pub fn is_clockwise(self) -> bool {
matches!(self, ArcDirection::Cw)
}
pub fn ccw_order<T>(self, start: T, end: T) -> (T, T) {
match self {
ArcDirection::Ccw => (start, end),
ArcDirection::Cw => (end, start),
}
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts", rename = "ApiControlPointSpline")]
pub struct ControlPointSpline {
pub controls: Vec<ObjectId>,
pub degree: u32,
pub ctor: SegmentCtor,
pub ctor_applicable: bool,
pub construction: bool,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts", rename = "ApiPoint2d")]
pub struct Point2d<U: std::fmt::Debug + Clone + ts_rs::TS> {
pub x: U,
pub y: U,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct PointCtor {
pub position: Point2d<Expr>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct LineCtor {
pub start: Point2d<Expr>,
pub end: Point2d<Expr>,
#[serde(skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub construction: Option<bool>,
}
#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
pub enum Freedom {
Free,
Fixed,
Conflict,
}
impl Freedom {
pub fn merge(self, other: Self) -> Self {
match (self, other) {
(Self::Conflict, _) | (_, Self::Conflict) => Self::Conflict,
(Self::Free, _) | (_, Self::Free) => Self::Free,
(Self::Fixed, Self::Fixed) => Self::Fixed,
}
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct ArcCtor {
pub start: Point2d<Expr>,
pub end: Point2d<Expr>,
pub center: Point2d<Expr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub direction: Option<ArcDirection>,
#[serde(skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub construction: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct CircleCtor {
pub start: Point2d<Expr>,
pub center: Point2d<Expr>,
#[serde(skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub construction: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct ControlPointSplineCtor {
pub points: Vec<Point2d<Expr>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub construction: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts", rename = "ApiConstraint")]
#[serde(tag = "type")]
pub enum Constraint {
Coincident(Coincident),
Distance(Distance),
Angle(Angle),
Diameter(Diameter),
EqualRadius(EqualRadius),
Fixed(Fixed),
HorizontalDistance(Distance),
VerticalDistance(Distance),
Horizontal(Horizontal),
LinesEqualLength(LinesEqualLength),
Midpoint(Midpoint),
Parallel(Parallel),
Perpendicular(Perpendicular),
Radius(Radius),
Symmetric(Symmetric),
Tangent(Tangent),
Vertical(Vertical),
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct Diameter {
pub arc: ObjectId,
pub diameter: Number,
#[serde(rename = "labelPosition")]
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(rename = "labelPosition")]
#[ts(optional)]
pub label_position: Option<Point2d<Number>>,
#[serde(default)]
pub source: ConstraintSource,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts", optional_fields)]
pub struct EqualRadius {
pub input: Vec<ObjectId>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct Fixed {
pub points: Vec<FixedPoint>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct FixedPoint {
pub point: ObjectId,
pub position: Point2d<Number>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
#[serde(untagged)]
pub enum Horizontal {
Line { line: ObjectId },
Points { points: Vec<ConstraintSegment> },
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct LinesEqualLength {
pub lines: Vec<ObjectId>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct Midpoint {
pub point: ConstraintSegment,
#[serde(alias = "line")]
pub segment: ObjectId,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
#[serde(untagged)]
pub enum Vertical {
Line { line: ObjectId },
Points { points: Vec<ConstraintSegment> },
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts", optional_fields)]
pub struct Parallel {
pub lines: Vec<ObjectId>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts", optional_fields)]
pub struct Perpendicular {
pub lines: Vec<ObjectId>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts", optional_fields)]
pub struct Symmetric {
pub input: Vec<ObjectId>,
pub axis: ObjectId,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts", optional_fields)]
pub struct Tangent {
pub input: Vec<ObjectId>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct Coincident {
pub segments: Vec<ConstraintSegment>,
}
#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
#[serde(untagged)]
pub enum ConstraintSegment {
Segment(ObjectId),
Origin(OriginLiteral),
}
impl ConstraintSegment {
pub const ORIGIN: Self = Self::Origin(OriginLiteral::Origin);
}
#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum OriginLiteral {
Origin,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct Distance {
pub segments: Vec<ConstraintSegment>,
pub distance: Number,
#[serde(rename = "labelPosition")]
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(rename = "labelPosition")]
#[ts(optional)]
pub label_position: Option<Point2d<Number>>,
pub source: ConstraintSource,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct Angle {
pub lines: Vec<ObjectId>,
pub angle: Number,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub sector: Option<u8>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub inverse: Option<bool>,
#[serde(rename = "labelPosition")]
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(rename = "labelPosition")]
#[ts(optional)]
pub label_position: Option<Point2d<Number>>,
pub source: ConstraintSource,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct Radius {
pub arc: ObjectId,
pub radius: Number,
#[serde(rename = "labelPosition")]
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(rename = "labelPosition")]
#[ts(optional)]
pub label_position: Option<Point2d<Number>>,
#[serde(default)]
pub source: ConstraintSource,
}
#[derive(Debug, Clone, Default, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct ConstraintSource {
pub expr: String,
pub is_literal: bool,
}
impl From<ObjectId> for ConstraintSegment {
fn from(value: ObjectId) -> Self {
Self::Segment(value)
}
}