#![allow(async_fn_in_trait)]
use serde::Deserialize;
use serde::Serialize;
use crate::ExecutorContext;
use crate::KclErrorWithOutputs;
use crate::front::Plane;
use crate::frontend::api::Expr;
use crate::frontend::api::FileId;
use crate::frontend::api::Number;
use crate::frontend::api::ObjectId;
use crate::frontend::api::ProjectId;
use crate::frontend::api::SceneGraph;
use crate::frontend::api::SceneGraphDelta;
use crate::frontend::api::SourceDelta;
use crate::frontend::api::Version;
pub type ExecResult<T> = std::result::Result<T, KclErrorWithOutputs>;
#[derive(Debug, Clone)]
pub struct NewSegmentInfo {
pub segment_id: ObjectId,
pub start_point_id: ObjectId,
pub end_point_id: ObjectId,
pub center_point_id: Option<ObjectId>,
}
pub trait SketchApi {
async fn execute_mock(
&mut self,
ctx: &ExecutorContext,
version: Version,
sketch: ObjectId,
) -> ExecResult<(SourceDelta, SceneGraphDelta)>;
async fn new_sketch(
&mut self,
ctx: &ExecutorContext,
project: ProjectId,
file: FileId,
version: Version,
args: SketchCtor,
) -> ExecResult<(SourceDelta, SceneGraphDelta, ObjectId)>;
async fn edit_sketch(
&mut self,
ctx: &ExecutorContext,
project: ProjectId,
file: FileId,
version: Version,
sketch: ObjectId,
) -> ExecResult<SceneGraphDelta>;
async fn exit_sketch(
&mut self,
ctx: &ExecutorContext,
version: Version,
sketch: ObjectId,
) -> ExecResult<SceneGraph>;
async fn delete_sketch(
&mut self,
ctx: &ExecutorContext,
version: Version,
sketch: ObjectId,
) -> ExecResult<(SourceDelta, SceneGraphDelta)>;
async fn add_segment(
&mut self,
ctx: &ExecutorContext,
version: Version,
sketch: ObjectId,
segment: SegmentCtor,
label: Option<String>,
) -> ExecResult<(SourceDelta, SceneGraphDelta)>;
async fn edit_segments(
&mut self,
ctx: &ExecutorContext,
version: Version,
sketch: ObjectId,
segments: Vec<ExistingSegmentCtor>,
) -> ExecResult<(SourceDelta, SceneGraphDelta)>;
async fn delete_objects(
&mut self,
ctx: &ExecutorContext,
version: Version,
sketch: ObjectId,
constraint_ids: Vec<ObjectId>,
segment_ids: Vec<ObjectId>,
) -> ExecResult<(SourceDelta, SceneGraphDelta)>;
async fn add_constraint(
&mut self,
ctx: &ExecutorContext,
version: Version,
sketch: ObjectId,
constraint: Constraint,
) -> ExecResult<(SourceDelta, SceneGraphDelta)>;
async fn chain_segment(
&mut self,
ctx: &ExecutorContext,
version: Version,
sketch: ObjectId,
previous_segment_end_point_id: ObjectId,
segment: SegmentCtor,
label: Option<String>,
) -> ExecResult<(SourceDelta, SceneGraphDelta)>;
async fn edit_constraint(
&mut self,
ctx: &ExecutorContext,
version: Version,
sketch: ObjectId,
constraint_id: ObjectId,
value_expression: String,
) -> ExecResult<(SourceDelta, SceneGraphDelta)>;
#[allow(clippy::too_many_arguments)]
async fn batch_split_segment_operations(
&mut self,
ctx: &ExecutorContext,
version: Version,
sketch: ObjectId,
edit_segments: Vec<ExistingSegmentCtor>,
add_constraints: Vec<Constraint>,
delete_constraint_ids: Vec<ObjectId>,
new_segment_info: NewSegmentInfo,
) -> ExecResult<(SourceDelta, SceneGraphDelta)>;
async fn batch_tail_cut_operations(
&mut self,
ctx: &ExecutorContext,
version: Version,
sketch: ObjectId,
edit_segments: Vec<ExistingSegmentCtor>,
add_constraints: Vec<Constraint>,
delete_constraint_ids: Vec<ObjectId>,
) -> ExecResult<(SourceDelta, SceneGraphDelta)>;
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[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)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct SketchCtor {
pub on: Plane,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[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>,
}
impl Point {
pub fn freedom(&self) -> Freedom {
self.freedom
}
}
#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[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)]
#[ts(export, export_to = "FrontendApi.ts", rename = "ApiSegment")]
#[serde(tag = "type")]
pub enum Segment {
Point(Point),
Line(Line),
Arc(Arc),
Circle(Circle),
}
impl Segment {
pub fn human_friendly_kind_with_article(&self) -> &'static str {
match self {
Self::Point(_) => "a Point",
Self::Line(_) => "a Line",
Self::Arc(_) => "an Arc",
Self::Circle(_) => "a Circle",
}
}
pub fn freedom(&self, lookup: impl Fn(ObjectId) -> Option<Freedom>) -> Option<Freedom> {
match self {
Self::Point(p) => Some(p.freedom()),
Self::Line(l) => l.freedom(&lookup),
Self::Arc(a) => a.freedom(&lookup),
Self::Circle(c) => c.freedom(&lookup),
}
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct ExistingSegmentCtor {
pub id: ObjectId,
pub ctor: SegmentCtor,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[ts(export, export_to = "FrontendApi.ts")]
#[serde(tag = "type")]
pub enum SegmentCtor {
Point(PointCtor),
Line(LineCtor),
Arc(ArcCtor),
Circle(CircleCtor),
}
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",
}
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct PointCtor {
pub position: Point2d<Expr>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[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)]
#[ts(export, export_to = "FrontendApi.ts", rename = "ApiLine")]
pub struct Line {
pub start: ObjectId,
pub end: ObjectId,
pub ctor: SegmentCtor,
pub ctor_applicable: bool,
pub construction: bool,
}
impl Line {
pub fn freedom(&self, lookup: impl Fn(ObjectId) -> Option<Freedom>) -> Option<Freedom> {
let start = lookup(self.start)?;
let end = lookup(self.end)?;
Some(start.merge(end))
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[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, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[ts(export, export_to = "FrontendApi.ts", rename = "ApiStartOrEnd")]
#[serde(tag = "type")]
pub enum StartOrEnd<T> {
Start(T),
End(T),
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[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,
}
impl Arc {
pub fn freedom(&self, lookup: impl Fn(ObjectId) -> Option<Freedom>) -> Option<Freedom> {
let start = lookup(self.start)?;
let end = lookup(self.end)?;
let center = lookup(self.center)?;
Some(start.merge(end).merge(center))
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct ArcCtor {
pub start: Point2d<Expr>,
pub end: 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)]
#[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,
}
impl Circle {
pub fn freedom(&self, lookup: impl Fn(ObjectId) -> Option<Freedom>) -> Option<Freedom> {
let start = lookup(self.start)?;
let center = lookup(self.center)?;
Some(start.merge(center))
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[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)]
#[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),
Parallel(Parallel),
Perpendicular(Perpendicular),
Radius(Radius),
Tangent(Tangent),
Vertical(Vertical),
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct Coincident {
pub segments: Vec<ConstraintSegment>,
}
impl Coincident {
pub fn get_segments(&self) -> Vec<ObjectId> {
self.segments
.iter()
.filter_map(|segment| match segment {
ConstraintSegment::Segment(id) => Some(*id),
ConstraintSegment::Origin(_) => None,
})
.collect()
}
pub fn segment_ids(&self) -> impl Iterator<Item = ObjectId> + '_ {
self.segments.iter().filter_map(|segment| match segment {
ConstraintSegment::Segment(id) => Some(*id),
ConstraintSegment::Origin(_) => None,
})
}
pub fn contains_segment(&self, segment_id: ObjectId) -> bool {
self.segment_ids().any(|id| id == segment_id)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[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)]
#[ts(export, export_to = "FrontendApi.ts")]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum OriginLiteral {
Origin,
}
impl From<ObjectId> for ConstraintSegment {
fn from(value: ObjectId) -> Self {
Self::Segment(value)
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct Distance {
pub points: Vec<ConstraintSegment>,
pub distance: Number,
pub source: ConstraintSource,
}
impl Distance {
pub fn point_ids(&self) -> impl Iterator<Item = ObjectId> + '_ {
self.points.iter().filter_map(|point| match point {
ConstraintSegment::Segment(id) => Some(*id),
ConstraintSegment::Origin(_) => None,
})
}
pub fn contains_point(&self, point_id: ObjectId) -> bool {
self.point_ids().any(|id| id == point_id)
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct Angle {
pub lines: Vec<ObjectId>,
pub angle: Number,
pub source: ConstraintSource,
}
#[derive(Debug, Clone, Default, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct ConstraintSource {
pub expr: String,
pub is_literal: bool,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct Radius {
pub arc: ObjectId,
pub radius: Number,
#[serde(default)]
pub source: ConstraintSource,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct Diameter {
pub arc: ObjectId,
pub diameter: Number,
#[serde(default)]
pub source: ConstraintSource,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[ts(export, export_to = "FrontendApi.ts", optional_fields)]
pub struct EqualRadius {
pub input: Vec<ObjectId>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct Fixed {
pub points: Vec<FixedPoint>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[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)]
#[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)]
#[ts(export, export_to = "FrontendApi.ts")]
pub struct LinesEqualLength {
pub lines: Vec<ObjectId>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[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)]
#[ts(export, export_to = "FrontendApi.ts", optional_fields)]
pub struct Parallel {
pub lines: Vec<ObjectId>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[ts(export, export_to = "FrontendApi.ts", optional_fields)]
pub struct Perpendicular {
pub lines: Vec<ObjectId>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS)]
#[ts(export, export_to = "FrontendApi.ts", optional_fields)]
pub struct Tangent {
pub input: Vec<ObjectId>,
}