BREAKING: More units of measure work and keyword args (#6291)
* More units of measure work Signed-off-by: Nick Cameron <nrc@ncameron.org> * Update CSG output since engine change --------- Signed-off-by: Nick Cameron <nrc@ncameron.org> Co-authored-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
@ -37,7 +37,7 @@ use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{ArtifactCommand, DefaultPlanes, IdGenerator, Point3d},
|
||||
execution::{types::UnitLen, ArtifactCommand, DefaultPlanes, IdGenerator, Point3d},
|
||||
SourceRange,
|
||||
};
|
||||
|
||||
@ -517,7 +517,13 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
) -> Result<uuid::Uuid, KclError> {
|
||||
// Create new default planes.
|
||||
let default_size = 100.0;
|
||||
let default_origin = Point3d { x: 0.0, y: 0.0, z: 0.0 }.into();
|
||||
let default_origin = Point3d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
}
|
||||
.into();
|
||||
|
||||
self.batch_modeling_cmd(
|
||||
plane_id,
|
||||
@ -555,8 +561,18 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
(
|
||||
PlaneName::Xy,
|
||||
id_generator.next_uuid(),
|
||||
Point3d { x: 1.0, y: 0.0, z: 0.0 },
|
||||
Point3d { x: 0.0, y: 1.0, z: 0.0 },
|
||||
Point3d {
|
||||
x: 1.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 1.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
Some(Color {
|
||||
r: 0.7,
|
||||
g: 0.28,
|
||||
@ -567,8 +583,18 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
(
|
||||
PlaneName::Yz,
|
||||
id_generator.next_uuid(),
|
||||
Point3d { x: 0.0, y: 1.0, z: 0.0 },
|
||||
Point3d { x: 0.0, y: 0.0, z: 1.0 },
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 1.0,
|
||||
z: 0.,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 1.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
Some(Color {
|
||||
r: 0.28,
|
||||
g: 0.7,
|
||||
@ -579,8 +605,18 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
(
|
||||
PlaneName::Xz,
|
||||
id_generator.next_uuid(),
|
||||
Point3d { x: 1.0, y: 0.0, z: 0.0 },
|
||||
Point3d { x: 0.0, y: 0.0, z: 1.0 },
|
||||
Point3d {
|
||||
x: 1.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 1.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
Some(Color {
|
||||
r: 0.28,
|
||||
g: 0.28,
|
||||
@ -595,8 +631,14 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
x: -1.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 1.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
Point3d { x: 0.0, y: 1.0, z: 0.0 },
|
||||
None,
|
||||
),
|
||||
(
|
||||
@ -606,8 +648,14 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
x: 0.0,
|
||||
y: -1.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 1.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
Point3d { x: 0.0, y: 0.0, z: 1.0 },
|
||||
None,
|
||||
),
|
||||
(
|
||||
@ -617,8 +665,14 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
x: -1.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 1.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
Point3d { x: 0.0, y: 0.0, z: 1.0 },
|
||||
None,
|
||||
),
|
||||
];
|
||||
|
@ -725,12 +725,7 @@ fn apply_ascription(
|
||||
let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, value.into())
|
||||
.map_err(|e| KclError::Semantic(e.into()))?;
|
||||
|
||||
if let KclValue::Number {
|
||||
ty: NumericType::Unknown,
|
||||
value,
|
||||
meta,
|
||||
} = value
|
||||
{
|
||||
if let KclValue::Number { value, meta, .. } = value {
|
||||
// If the number has unknown units but the user is explicitly specifying them, treat the value as having had it's units erased,
|
||||
// rather than forcing the user to explicitly erase them.
|
||||
KclValue::Number {
|
||||
|
@ -184,62 +184,191 @@ pub struct Plane {
|
||||
|
||||
impl Plane {
|
||||
pub(crate) fn into_plane_data(self) -> PlaneData {
|
||||
if self.origin == Point3d::new(0.0, 0.0, 0.0) {
|
||||
if self.origin.is_zero() {
|
||||
match self {
|
||||
Self {
|
||||
origin: Point3d { x: 0.0, y: 0.0, z: 0.0 },
|
||||
x_axis: Point3d { x: 1.0, y: 0.0, z: 0.0 },
|
||||
y_axis: Point3d { x: 0.0, y: 1.0, z: 0.0 },
|
||||
z_axis: Point3d { x: 0.0, y: 0.0, z: 1.0 },
|
||||
origin:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
x_axis:
|
||||
Point3d {
|
||||
x: 1.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
y_axis:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 1.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
z_axis:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 1.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
..
|
||||
} => return PlaneData::XY,
|
||||
Self {
|
||||
origin: Point3d { x: 0.0, y: 0.0, z: 0.0 },
|
||||
x_axis: Point3d { x: 1.0, y: 0.0, z: 0.0 },
|
||||
y_axis: Point3d { x: 0.0, y: 1.0, z: 0.0 },
|
||||
origin:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
x_axis:
|
||||
Point3d {
|
||||
x: 1.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
y_axis:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 1.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
z_axis:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: -1.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
..
|
||||
} => return PlaneData::NegXY,
|
||||
Self {
|
||||
origin: Point3d { x: 0.0, y: 0.0, z: 0.0 },
|
||||
x_axis: Point3d { x: 1.0, y: 0.0, z: 0.0 },
|
||||
y_axis: Point3d { x: 0.0, y: 0.0, z: 1.0 },
|
||||
origin:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
x_axis:
|
||||
Point3d {
|
||||
x: 1.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
y_axis:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 1.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
z_axis:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: -1.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
..
|
||||
} => return PlaneData::XZ,
|
||||
Self {
|
||||
origin: Point3d { x: 0.0, y: 0.0, z: 0.0 },
|
||||
x_axis: Point3d { x: 1.0, y: 0.0, z: 0.0 },
|
||||
y_axis: Point3d { x: 0.0, y: 0.0, z: 1.0 },
|
||||
z_axis: Point3d { x: 0.0, y: 1.0, z: 0.0 },
|
||||
origin:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
x_axis:
|
||||
Point3d {
|
||||
x: 1.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
y_axis:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 1.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
z_axis:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 1.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
..
|
||||
} => return PlaneData::NegXZ,
|
||||
Self {
|
||||
origin: Point3d { x: 0.0, y: 0.0, z: 0.0 },
|
||||
x_axis: Point3d { x: 0.0, y: 1.0, z: 0.0 },
|
||||
y_axis: Point3d { x: 0.0, y: 0.0, z: 1.0 },
|
||||
z_axis: Point3d { x: 1.0, y: 0.0, z: 0.0 },
|
||||
origin:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
x_axis:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 1.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
y_axis:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 1.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
z_axis:
|
||||
Point3d {
|
||||
x: 1.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
..
|
||||
} => return PlaneData::YZ,
|
||||
Self {
|
||||
origin: Point3d { x: 0.0, y: 0.0, z: 0.0 },
|
||||
x_axis: Point3d { x: 0.0, y: 1.0, z: 0.0 },
|
||||
y_axis: Point3d { x: 0.0, y: 0.0, z: 1.0 },
|
||||
origin:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
x_axis:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 1.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
y_axis:
|
||||
Point3d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 1.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
z_axis:
|
||||
Point3d {
|
||||
x: -1.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
},
|
||||
..
|
||||
} => return PlaneData::NegYZ,
|
||||
@ -261,10 +390,10 @@ impl Plane {
|
||||
PlaneData::XY => Plane {
|
||||
id,
|
||||
artifact_id: id.into(),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0),
|
||||
x_axis: Point3d::new(1.0, 0.0, 0.0),
|
||||
y_axis: Point3d::new(0.0, 1.0, 0.0),
|
||||
z_axis: Point3d::new(0.0, 0.0, 1.0),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0, UnitLen::Mm),
|
||||
x_axis: Point3d::new(1.0, 0.0, 0.0, UnitLen::Mm),
|
||||
y_axis: Point3d::new(0.0, 1.0, 0.0, UnitLen::Mm),
|
||||
z_axis: Point3d::new(0.0, 0.0, 1.0, UnitLen::Mm),
|
||||
value: PlaneType::XY,
|
||||
units: exec_state.length_unit(),
|
||||
meta: vec![],
|
||||
@ -272,10 +401,10 @@ impl Plane {
|
||||
PlaneData::NegXY => Plane {
|
||||
id,
|
||||
artifact_id: id.into(),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0),
|
||||
x_axis: Point3d::new(1.0, 0.0, 0.0),
|
||||
y_axis: Point3d::new(0.0, 1.0, 0.0),
|
||||
z_axis: Point3d::new(0.0, 0.0, -1.0),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0, UnitLen::Mm),
|
||||
x_axis: Point3d::new(1.0, 0.0, 0.0, UnitLen::Mm),
|
||||
y_axis: Point3d::new(0.0, 1.0, 0.0, UnitLen::Mm),
|
||||
z_axis: Point3d::new(0.0, 0.0, -1.0, UnitLen::Mm),
|
||||
value: PlaneType::XY,
|
||||
units: exec_state.length_unit(),
|
||||
meta: vec![],
|
||||
@ -283,10 +412,10 @@ impl Plane {
|
||||
PlaneData::XZ => Plane {
|
||||
id,
|
||||
artifact_id: id.into(),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0),
|
||||
x_axis: Point3d::new(1.0, 0.0, 0.0),
|
||||
y_axis: Point3d::new(0.0, 0.0, 1.0),
|
||||
z_axis: Point3d::new(0.0, -1.0, 0.0),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0, UnitLen::Mm),
|
||||
x_axis: Point3d::new(1.0, 0.0, 0.0, UnitLen::Mm),
|
||||
y_axis: Point3d::new(0.0, 0.0, 1.0, UnitLen::Mm),
|
||||
z_axis: Point3d::new(0.0, -1.0, 0.0, UnitLen::Mm),
|
||||
value: PlaneType::XZ,
|
||||
units: exec_state.length_unit(),
|
||||
meta: vec![],
|
||||
@ -294,10 +423,10 @@ impl Plane {
|
||||
PlaneData::NegXZ => Plane {
|
||||
id,
|
||||
artifact_id: id.into(),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0),
|
||||
x_axis: Point3d::new(-1.0, 0.0, 0.0),
|
||||
y_axis: Point3d::new(0.0, 0.0, 1.0),
|
||||
z_axis: Point3d::new(0.0, 1.0, 0.0),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0, UnitLen::Mm),
|
||||
x_axis: Point3d::new(-1.0, 0.0, 0.0, UnitLen::Mm),
|
||||
y_axis: Point3d::new(0.0, 0.0, 1.0, UnitLen::Mm),
|
||||
z_axis: Point3d::new(0.0, 1.0, 0.0, UnitLen::Mm),
|
||||
value: PlaneType::XZ,
|
||||
units: exec_state.length_unit(),
|
||||
meta: vec![],
|
||||
@ -305,10 +434,10 @@ impl Plane {
|
||||
PlaneData::YZ => Plane {
|
||||
id,
|
||||
artifact_id: id.into(),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0),
|
||||
x_axis: Point3d::new(0.0, 1.0, 0.0),
|
||||
y_axis: Point3d::new(0.0, 0.0, 1.0),
|
||||
z_axis: Point3d::new(1.0, 0.0, 0.0),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0, UnitLen::Mm),
|
||||
x_axis: Point3d::new(0.0, 1.0, 0.0, UnitLen::Mm),
|
||||
y_axis: Point3d::new(0.0, 0.0, 1.0, UnitLen::Mm),
|
||||
z_axis: Point3d::new(1.0, 0.0, 0.0, UnitLen::Mm),
|
||||
value: PlaneType::YZ,
|
||||
units: exec_state.length_unit(),
|
||||
meta: vec![],
|
||||
@ -316,10 +445,10 @@ impl Plane {
|
||||
PlaneData::NegYZ => Plane {
|
||||
id,
|
||||
artifact_id: id.into(),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0),
|
||||
x_axis: Point3d::new(0.0, 1.0, 0.0),
|
||||
y_axis: Point3d::new(0.0, 0.0, 1.0),
|
||||
z_axis: Point3d::new(-1.0, 0.0, 0.0),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0, UnitLen::Mm),
|
||||
x_axis: Point3d::new(0.0, 1.0, 0.0, UnitLen::Mm),
|
||||
y_axis: Point3d::new(0.0, 0.0, 1.0, UnitLen::Mm),
|
||||
z_axis: Point3d::new(-1.0, 0.0, 0.0, UnitLen::Mm),
|
||||
value: PlaneType::YZ,
|
||||
units: exec_state.length_unit(),
|
||||
meta: vec![],
|
||||
@ -512,7 +641,7 @@ pub(crate) enum GetTangentialInfoFromPathsResult {
|
||||
}
|
||||
|
||||
impl GetTangentialInfoFromPathsResult {
|
||||
pub(crate) fn tan_previous_point(&self, last_arc_end: crate::std::utils::Coords2d) -> [f64; 2] {
|
||||
pub(crate) fn tan_previous_point(&self, last_arc_end: [f64; 2]) -> [f64; 2] {
|
||||
match self {
|
||||
GetTangentialInfoFromPathsResult::PreviousPoint(p) => *p,
|
||||
GetTangentialInfoFromPathsResult::Arc { center, ccw, .. } => {
|
||||
@ -567,11 +696,10 @@ impl Sketch {
|
||||
/// where the last path segment ends, and the next path segment will begin.
|
||||
pub(crate) fn current_pen_position(&self) -> Result<Point2d, KclError> {
|
||||
let Some(path) = self.latest_path() else {
|
||||
return Ok(self.start.to.into());
|
||||
return Ok(Point2d::new(self.start.to[0], self.start.to[1], self.start.units));
|
||||
};
|
||||
|
||||
let base = path.get_base();
|
||||
Ok(base.to.into())
|
||||
Ok(path.get_to().into())
|
||||
}
|
||||
|
||||
pub(crate) fn get_tangential_info_from_paths(&self) -> GetTangentialInfoFromPathsResult {
|
||||
@ -624,7 +752,7 @@ pub enum EdgeCut {
|
||||
Fillet {
|
||||
/// The id of the engine command that called this fillet.
|
||||
id: uuid::Uuid,
|
||||
radius: f64,
|
||||
radius: TyF64,
|
||||
/// The engine id of the edge to fillet.
|
||||
#[serde(rename = "edgeId")]
|
||||
edge_id: uuid::Uuid,
|
||||
@ -634,7 +762,7 @@ pub enum EdgeCut {
|
||||
Chamfer {
|
||||
/// The id of the engine command that called this chamfer.
|
||||
id: uuid::Uuid,
|
||||
length: f64,
|
||||
length: TyF64,
|
||||
/// The engine id of the edge to chamfer.
|
||||
#[serde(rename = "edgeId")]
|
||||
edge_id: uuid::Uuid,
|
||||
@ -670,23 +798,16 @@ impl EdgeCut {
|
||||
pub struct Point2d {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
}
|
||||
|
||||
impl From<[f64; 2]> for Point2d {
|
||||
fn from(p: [f64; 2]) -> Self {
|
||||
Self { x: p[0], y: p[1] }
|
||||
}
|
||||
pub units: UnitLen,
|
||||
}
|
||||
|
||||
impl From<[TyF64; 2]> for Point2d {
|
||||
fn from(p: [TyF64; 2]) -> Self {
|
||||
Self { x: p[0].n, y: p[1].n }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[f64; 2]> for Point2d {
|
||||
fn from(p: &[f64; 2]) -> Self {
|
||||
Self { x: p[0], y: p[1] }
|
||||
Self {
|
||||
x: p[0].n,
|
||||
y: p[1].n,
|
||||
units: p[0].ty.expect_length(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -703,12 +824,14 @@ impl From<Point2d> for Point2D {
|
||||
}
|
||||
|
||||
impl Point2d {
|
||||
pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
|
||||
pub fn scale(self, scalar: f64) -> Self {
|
||||
Self {
|
||||
x: self.x * scalar,
|
||||
y: self.y * scalar,
|
||||
}
|
||||
pub const ZERO: Self = Self {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
};
|
||||
|
||||
pub fn new(x: f64, y: f64, units: UnitLen) -> Self {
|
||||
Self { x, y, units }
|
||||
}
|
||||
}
|
||||
|
||||
@ -718,12 +841,34 @@ pub struct Point3d {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
pub z: f64,
|
||||
pub units: UnitLen,
|
||||
}
|
||||
|
||||
impl Point3d {
|
||||
pub const ZERO: Self = Self { x: 0.0, y: 0.0, z: 0.0 };
|
||||
pub fn new(x: f64, y: f64, z: f64) -> Self {
|
||||
Self { x, y, z }
|
||||
pub const ZERO: Self = Self {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
units: UnitLen::Mm,
|
||||
};
|
||||
|
||||
pub fn new(x: f64, y: f64, z: f64, units: UnitLen) -> Self {
|
||||
Self { x, y, z, units }
|
||||
}
|
||||
|
||||
pub const fn is_zero(&self) -> bool {
|
||||
self.x == 0.0 && self.y == 0.0 && self.z == 0.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[TyF64; 3]> for Point3d {
|
||||
fn from(p: [TyF64; 3]) -> Self {
|
||||
Self {
|
||||
x: p[0].n,
|
||||
y: p[1].n,
|
||||
z: p[2].n,
|
||||
units: p[0].ty.expect_length(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -746,10 +891,12 @@ impl Add for Point3d {
|
||||
type Output = Point3d;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
// TODO should assert that self and rhs the same units or coerce them
|
||||
Point3d {
|
||||
x: self.x + rhs.x,
|
||||
y: self.y + rhs.y,
|
||||
z: self.z + rhs.z,
|
||||
units: self.units,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -768,6 +915,7 @@ impl Mul<f64> for Point3d {
|
||||
x: self.x * rhs,
|
||||
y: self.y * rhs,
|
||||
z: self.z * rhs,
|
||||
units: self.units,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -791,6 +939,18 @@ pub struct BasePath {
|
||||
pub geo_meta: GeoMeta,
|
||||
}
|
||||
|
||||
impl BasePath {
|
||||
pub fn get_to(&self) -> [TyF64; 2] {
|
||||
let ty: NumericType = self.units.into();
|
||||
[TyF64::new(self.to[0], ty.clone()), TyF64::new(self.to[1], ty)]
|
||||
}
|
||||
|
||||
pub fn get_from(&self) -> [TyF64; 2] {
|
||||
let ty: NumericType = self.units.into();
|
||||
[TyF64::new(self.from[0], ty.clone()), TyF64::new(self.from[1], ty)]
|
||||
}
|
||||
}
|
||||
|
||||
/// Geometry metadata.
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
@ -990,6 +1150,7 @@ impl Path {
|
||||
let ty: NumericType = self.get_base().units.into();
|
||||
[TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)]
|
||||
}
|
||||
|
||||
/// Where does this path segment end?
|
||||
pub fn get_to(&self) -> [TyF64; 2] {
|
||||
let p = &self.get_base().to;
|
||||
@ -1023,11 +1184,14 @@ impl Path {
|
||||
Self::Circle { radius, .. } => 2.0 * std::f64::consts::PI * radius,
|
||||
Self::CircleThreePoint { .. } => {
|
||||
let circle_center = crate::std::utils::calculate_circle_from_3_points([
|
||||
self.get_base().from.into(),
|
||||
self.get_base().to.into(),
|
||||
self.get_base().to.into(),
|
||||
self.get_base().from,
|
||||
self.get_base().to,
|
||||
self.get_base().to,
|
||||
]);
|
||||
let radius = linear_distance(&[circle_center.center.x, circle_center.center.y], &self.get_base().from);
|
||||
let radius = linear_distance(
|
||||
&[circle_center.center[0], circle_center.center[1]],
|
||||
&self.get_base().from,
|
||||
);
|
||||
2.0 * std::f64::consts::PI * radius
|
||||
}
|
||||
Self::Arc { .. } => {
|
||||
@ -1066,10 +1230,9 @@ impl Path {
|
||||
ccw: *ccw,
|
||||
},
|
||||
Path::ArcThreePoint { p1, p2, p3, .. } => {
|
||||
let circle_center =
|
||||
crate::std::utils::calculate_circle_from_3_points([(*p1).into(), (*p2).into(), (*p3).into()]);
|
||||
let radius = linear_distance(&[circle_center.center.x, circle_center.center.y], p1);
|
||||
let center_point = [circle_center.center.x, circle_center.center.y];
|
||||
let circle_center = crate::std::utils::calculate_circle_from_3_points([*p1, *p2, *p3]);
|
||||
let radius = linear_distance(&[circle_center.center[0], circle_center.center[1]], p1);
|
||||
let center_point = [circle_center.center[0], circle_center.center[1]];
|
||||
GetTangentialInfoFromPathsResult::Circle {
|
||||
center: center_point,
|
||||
ccw: true,
|
||||
@ -1084,10 +1247,9 @@ impl Path {
|
||||
radius: *radius,
|
||||
},
|
||||
Path::CircleThreePoint { p1, p2, p3, .. } => {
|
||||
let circle_center =
|
||||
crate::std::utils::calculate_circle_from_3_points([(*p1).into(), (*p2).into(), (*p3).into()]);
|
||||
let radius = linear_distance(&[circle_center.center.x, circle_center.center.y], p1);
|
||||
let center_point = [circle_center.center.x, circle_center.center.y];
|
||||
let circle_center = crate::std::utils::calculate_circle_from_3_points([*p1, *p2, *p3]);
|
||||
let radius = linear_distance(&[circle_center.center[0], circle_center.center[1]], p1);
|
||||
let center_point = [circle_center.center[0], circle_center.center[1]];
|
||||
GetTangentialInfoFromPathsResult::Circle {
|
||||
center: center_point,
|
||||
ccw: true,
|
||||
|
@ -422,23 +422,33 @@ impl KclValue {
|
||||
}
|
||||
|
||||
pub fn as_array(&self) -> Option<&[KclValue]> {
|
||||
if let KclValue::MixedArray { value, meta: _ } = &self {
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
match self {
|
||||
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } => Some(value),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_point2d(&self) -> Option<[f64; 2]> {
|
||||
pub fn as_point2d(&self) -> Option<[TyF64; 2]> {
|
||||
let arr = self.as_array()?;
|
||||
if arr.len() != 2 {
|
||||
return None;
|
||||
}
|
||||
let x = arr[0].as_f64()?;
|
||||
let y = arr[1].as_f64()?;
|
||||
let x = arr[0].as_ty_f64()?;
|
||||
let y = arr[1].as_ty_f64()?;
|
||||
Some([x, y])
|
||||
}
|
||||
|
||||
pub fn as_point3d(&self) -> Option<[TyF64; 3]> {
|
||||
let arr = self.as_array()?;
|
||||
if arr.len() != 3 {
|
||||
return None;
|
||||
}
|
||||
let x = arr[0].as_ty_f64()?;
|
||||
let y = arr[1].as_ty_f64()?;
|
||||
let z = arr[2].as_ty_f64()?;
|
||||
Some([x, y, z])
|
||||
}
|
||||
|
||||
pub fn as_uuid(&self) -> Option<uuid::Uuid> {
|
||||
if let KclValue::Uuid { value, meta: _ } = &self {
|
||||
Some(*value)
|
||||
@ -487,6 +497,7 @@ impl KclValue {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn as_f64(&self) -> Option<f64> {
|
||||
if let KclValue::Number { value, .. } = &self {
|
||||
Some(*value)
|
||||
|
@ -2050,7 +2050,7 @@ fn foo() {
|
||||
|
||||
solid = sketch |> extrude(length = 10)
|
||||
// tag0 tags a face
|
||||
sketch2 = startSketchOn(solid, tag0)
|
||||
sketch2 = startSketchOn(solid, face = tag0)
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line(end = [0, 1])
|
||||
|> line(end = [1, 0])
|
||||
|
@ -62,21 +62,57 @@ impl RuntimeType {
|
||||
RuntimeType::Primitive(PrimitiveType::Solid)
|
||||
}
|
||||
|
||||
pub fn plane() -> Self {
|
||||
RuntimeType::Primitive(PrimitiveType::Plane)
|
||||
}
|
||||
|
||||
pub fn string() -> Self {
|
||||
RuntimeType::Primitive(PrimitiveType::String)
|
||||
}
|
||||
|
||||
pub fn imported() -> Self {
|
||||
RuntimeType::Primitive(PrimitiveType::ImportedGeometry)
|
||||
}
|
||||
|
||||
/// `[number; 2]`
|
||||
pub fn point2d() -> Self {
|
||||
RuntimeType::Array(Box::new(RuntimeType::number_any()), ArrayLen::Known(2))
|
||||
RuntimeType::Array(Box::new(RuntimeType::length()), ArrayLen::Known(2))
|
||||
}
|
||||
|
||||
/// `[number; 3]`
|
||||
pub fn point3d() -> Self {
|
||||
RuntimeType::Array(Box::new(RuntimeType::number_any()), ArrayLen::Known(3))
|
||||
RuntimeType::Array(Box::new(RuntimeType::length()), ArrayLen::Known(3))
|
||||
}
|
||||
|
||||
pub fn number_any() -> Self {
|
||||
pub fn length() -> Self {
|
||||
RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Length(
|
||||
UnitLen::Unknown,
|
||||
))))
|
||||
}
|
||||
|
||||
pub fn angle() -> Self {
|
||||
RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Angle(
|
||||
UnitAngle::Unknown,
|
||||
))))
|
||||
}
|
||||
|
||||
pub fn radians() -> Self {
|
||||
RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Angle(
|
||||
UnitAngle::Radians,
|
||||
))))
|
||||
}
|
||||
|
||||
pub fn degrees() -> Self {
|
||||
RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Angle(
|
||||
UnitAngle::Degrees,
|
||||
))))
|
||||
}
|
||||
|
||||
pub fn count() -> Self {
|
||||
RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Count)))
|
||||
}
|
||||
|
||||
pub fn num_any() -> Self {
|
||||
RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any))
|
||||
}
|
||||
|
||||
@ -397,8 +433,8 @@ impl NumericType {
|
||||
(Default { .. }, Default { .. }) | (_, Unknown) | (Unknown, _) => (a.n, b.n, Unknown),
|
||||
|
||||
// Known types and compatible, but needs adjustment.
|
||||
(t @ Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => (a.n, l2.adjust_to(b.n, l1), t),
|
||||
(t @ Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => (a.n, a2.adjust_to(b.n, a1), t),
|
||||
(t @ Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => (a.n, l2.adjust_to(b.n, l1).0, t),
|
||||
(t @ Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => (a.n, a2.adjust_to(b.n, a1).0, t),
|
||||
|
||||
// Known but incompatible.
|
||||
(Known(_), Known(_)) => (a.n, b.n, Unknown),
|
||||
@ -408,11 +444,11 @@ impl NumericType {
|
||||
(a.n, b.n, Known(UnitType::Count))
|
||||
}
|
||||
|
||||
(t @ Known(UnitType::Length(l1)), Default { len: l2, .. }) => (a.n, l2.adjust_to(b.n, l1), t),
|
||||
(Default { len: l1, .. }, t @ Known(UnitType::Length(l2))) => (l1.adjust_to(a.n, l2), b.n, t),
|
||||
(t @ Known(UnitType::Length(l1)), Default { len: l2, .. }) => (a.n, l2.adjust_to(b.n, l1).0, t),
|
||||
(Default { len: l1, .. }, t @ Known(UnitType::Length(l2))) => (l1.adjust_to(a.n, l2).0, b.n, t),
|
||||
|
||||
(t @ Known(UnitType::Angle(a1)), Default { angle: a2, .. }) => (a.n, a2.adjust_to(b.n, a1), t),
|
||||
(Default { angle: a1, .. }, t @ Known(UnitType::Angle(a2))) => (a1.adjust_to(a.n, a2), b.n, t),
|
||||
(t @ Known(UnitType::Angle(a1)), Default { angle: a2, .. }) => (a.n, a2.adjust_to(b.n, a1).0, t),
|
||||
(Default { angle: a1, .. }, t @ Known(UnitType::Angle(a2))) => (a1.adjust_to(a.n, a2).0, b.n, t),
|
||||
}
|
||||
}
|
||||
|
||||
@ -422,7 +458,7 @@ impl NumericType {
|
||||
let mut result = input.iter().map(|t| t.n).collect();
|
||||
|
||||
let mut ty = Any;
|
||||
// Invariant mismatch is true => ty is Known
|
||||
// Invariant mismatch is true => ty is fully known
|
||||
let mut mismatch = false;
|
||||
for i in input {
|
||||
if i.ty == Any || ty == i.ty {
|
||||
@ -478,10 +514,10 @@ impl NumericType {
|
||||
.zip(input)
|
||||
.map(|(n, i)| match (&ty, &i.ty) {
|
||||
(Known(UnitType::Length(l1)), Known(UnitType::Length(l2)) | Default { len: l2, .. }) => {
|
||||
l2.adjust_to(n, *l1)
|
||||
l2.adjust_to(n, *l1).0
|
||||
}
|
||||
(Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2)) | Default { angle: a2, .. }) => {
|
||||
a2.adjust_to(n, *a1)
|
||||
a2.adjust_to(n, *a1).0
|
||||
}
|
||||
_ => unreachable!(),
|
||||
})
|
||||
@ -495,8 +531,10 @@ impl NumericType {
|
||||
use NumericType::*;
|
||||
match (a.ty, b.ty) {
|
||||
(at @ Default { .. }, bt @ Default { .. }) if at != bt => (a.n, b.n, Unknown),
|
||||
(Known(UnitType::Count) | Default { .. }, bt) => (a.n, b.n, bt),
|
||||
(at, Known(UnitType::Count) | Default { .. }) => (a.n, b.n, at),
|
||||
(Known(UnitType::Count), bt) => (a.n, b.n, bt),
|
||||
(at, Known(UnitType::Count)) => (a.n, b.n, at),
|
||||
(Default { .. }, bt) => (a.n, b.n, bt),
|
||||
(at, Default { .. }) => (a.n, b.n, at),
|
||||
(Any, Any) => (a.n, b.n, Any),
|
||||
_ => (a.n, b.n, Unknown),
|
||||
}
|
||||
@ -506,21 +544,23 @@ impl NumericType {
|
||||
pub fn combine_div(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
|
||||
use NumericType::*;
|
||||
match (a.ty, b.ty) {
|
||||
(at @ Default { .. }, bt @ Default { .. }) if at == bt => (a.n, b.n, at),
|
||||
(at, bt) if at == bt => (a.n, b.n, Known(UnitType::Count)),
|
||||
(Default { .. }, Default { .. }) => (a.n, b.n, Unknown),
|
||||
(at, Known(UnitType::Count) | Default { .. } | Any) => (a.n, b.n, at),
|
||||
(at, Known(UnitType::Count) | Any) => (a.n, b.n, at),
|
||||
(Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => {
|
||||
(a.n, l2.adjust_to(b.n, l1), Known(UnitType::Count))
|
||||
(a.n, l2.adjust_to(b.n, l1).0, Known(UnitType::Count))
|
||||
}
|
||||
(Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => {
|
||||
(a.n, a2.adjust_to(b.n, a1), Known(UnitType::Count))
|
||||
(a.n, a2.adjust_to(b.n, a1).0, Known(UnitType::Count))
|
||||
}
|
||||
(Default { len: l1, .. }, Known(UnitType::Length(l2))) => {
|
||||
(l1.adjust_to(a.n, l2), b.n, Known(UnitType::Count))
|
||||
(l1.adjust_to(a.n, l2).0, b.n, Known(UnitType::Count))
|
||||
}
|
||||
(Default { angle: a1, .. }, Known(UnitType::Angle(a2))) => {
|
||||
(a1.adjust_to(a.n, a2), b.n, Known(UnitType::Count))
|
||||
(a1.adjust_to(a.n, a2).0, b.n, Known(UnitType::Count))
|
||||
}
|
||||
(Known(UnitType::Count), _) => (a.n, b.n, Known(UnitType::Count)),
|
||||
_ => (a.n, b.n, Unknown),
|
||||
}
|
||||
}
|
||||
@ -532,6 +572,8 @@ impl NumericType {
|
||||
angle: settings.default_angle_units,
|
||||
},
|
||||
NumericSuffix::Count => NumericType::Known(UnitType::Count),
|
||||
NumericSuffix::Length => NumericType::Known(UnitType::Length(UnitLen::Unknown)),
|
||||
NumericSuffix::Angle => NumericType::Known(UnitType::Angle(UnitAngle::Unknown)),
|
||||
NumericSuffix::Mm => NumericType::Known(UnitType::Length(UnitLen::Mm)),
|
||||
NumericSuffix::Cm => NumericType::Known(UnitType::Length(UnitLen::Cm)),
|
||||
NumericSuffix::M => NumericType::Known(UnitType::Length(UnitLen::M)),
|
||||
@ -549,6 +591,14 @@ impl NumericType {
|
||||
match (self, other) {
|
||||
(_, Any) => true,
|
||||
(a, b) if a == b => true,
|
||||
(
|
||||
NumericType::Known(UnitType::Length(_)) | NumericType::Default { .. },
|
||||
NumericType::Known(UnitType::Length(UnitLen::Unknown)),
|
||||
)
|
||||
| (
|
||||
NumericType::Known(UnitType::Angle(_)) | NumericType::Default { .. },
|
||||
NumericType::Known(UnitType::Angle(UnitAngle::Unknown)),
|
||||
) => true,
|
||||
(Unknown, _) | (_, Unknown) => false,
|
||||
(_, _) => false,
|
||||
}
|
||||
@ -599,16 +649,22 @@ impl NumericType {
|
||||
}),
|
||||
|
||||
// Known types and compatible, but needs adjustment.
|
||||
(Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => Ok(KclValue::Number {
|
||||
value: l1.adjust_to(*value, *l2),
|
||||
ty: self.clone(),
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
(Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => Ok(KclValue::Number {
|
||||
value: a1.adjust_to(*value, *a2),
|
||||
ty: self.clone(),
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
(Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => {
|
||||
let (value, ty) = l1.adjust_to(*value, *l2);
|
||||
Ok(KclValue::Number {
|
||||
value,
|
||||
ty: Known(UnitType::Length(ty)),
|
||||
meta: meta.clone(),
|
||||
})
|
||||
}
|
||||
(Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2))) => {
|
||||
let (value, ty) = a1.adjust_to(*value, *a2);
|
||||
Ok(KclValue::Number {
|
||||
value,
|
||||
ty: Known(UnitType::Angle(ty)),
|
||||
meta: meta.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
// Known but incompatible.
|
||||
(Known(_), Known(_)) => Err(val.into()),
|
||||
@ -623,22 +679,42 @@ impl NumericType {
|
||||
}
|
||||
|
||||
(Known(UnitType::Length(l1)), Default { len: l2, .. })
|
||||
| (Default { len: l1, .. }, Known(UnitType::Length(l2))) => Ok(KclValue::Number {
|
||||
value: l1.adjust_to(*value, *l2),
|
||||
ty: Known(UnitType::Length(*l2)),
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
| (Default { len: l1, .. }, Known(UnitType::Length(l2))) => {
|
||||
let (value, ty) = l1.adjust_to(*value, *l2);
|
||||
Ok(KclValue::Number {
|
||||
value,
|
||||
ty: Known(UnitType::Length(ty)),
|
||||
meta: meta.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
(Known(UnitType::Angle(a1)), Default { angle: a2, .. })
|
||||
| (Default { angle: a1, .. }, Known(UnitType::Angle(a2))) => Ok(KclValue::Number {
|
||||
value: a1.adjust_to(*value, *a2),
|
||||
ty: Known(UnitType::Angle(*a2)),
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
| (Default { angle: a1, .. }, Known(UnitType::Angle(a2))) => {
|
||||
let (value, ty) = a1.adjust_to(*value, *a2);
|
||||
Ok(KclValue::Number {
|
||||
value,
|
||||
ty: Known(UnitType::Angle(ty)),
|
||||
meta: meta.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
(_, _) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_length(&self) -> UnitLen {
|
||||
match self {
|
||||
Self::Known(UnitType::Length(len)) | Self::Default { len, .. } => *len,
|
||||
_ => unreachable!("Found {self:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_length(&self) -> Option<UnitLen> {
|
||||
match self {
|
||||
Self::Known(UnitType::Length(len)) | Self::Default { len, .. } => Some(*len),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NumericType> for RuntimeType {
|
||||
@ -691,15 +767,21 @@ pub enum UnitLen {
|
||||
Inches,
|
||||
Feet,
|
||||
Yards,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl UnitLen {
|
||||
fn adjust_to(self, value: f64, to: UnitLen) -> f64 {
|
||||
fn adjust_to(self, value: f64, to: UnitLen) -> (f64, UnitLen) {
|
||||
use UnitLen::*;
|
||||
|
||||
if !*CHECK_NUMERIC_TYPES || self == to {
|
||||
return value;
|
||||
return (value, to);
|
||||
}
|
||||
|
||||
if to == Unknown {
|
||||
return (value, self);
|
||||
}
|
||||
|
||||
use UnitLen::*;
|
||||
let (base, base_unit) = match self {
|
||||
Mm => (value, Mm),
|
||||
Cm => (value * 10.0, Mm),
|
||||
@ -707,6 +789,7 @@ impl UnitLen {
|
||||
Inches => (value, Inches),
|
||||
Feet => (value * 12.0, Inches),
|
||||
Yards => (value * 36.0, Inches),
|
||||
Unknown => unreachable!(),
|
||||
};
|
||||
let (base, base_unit) = match (base_unit, to) {
|
||||
(Mm, Inches) | (Mm, Feet) | (Mm, Yards) => (base / 25.4, Inches),
|
||||
@ -714,7 +797,7 @@ impl UnitLen {
|
||||
_ => (base, base_unit),
|
||||
};
|
||||
|
||||
match (base_unit, to) {
|
||||
let value = match (base_unit, to) {
|
||||
(Mm, Mm) => base,
|
||||
(Mm, Cm) => base / 10.0,
|
||||
(Mm, M) => base / 1000.0,
|
||||
@ -722,7 +805,9 @@ impl UnitLen {
|
||||
(Inches, Feet) => base / 12.0,
|
||||
(Inches, Yards) => base / 36.0,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
|
||||
(value, to)
|
||||
}
|
||||
}
|
||||
|
||||
@ -735,6 +820,7 @@ impl std::fmt::Display for UnitLen {
|
||||
UnitLen::Inches => write!(f, "in"),
|
||||
UnitLen::Feet => write!(f, "ft"),
|
||||
UnitLen::Yards => write!(f, "yd"),
|
||||
UnitLen::Unknown => write!(f, "Length"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -777,6 +863,7 @@ impl From<UnitLen> for crate::UnitLength {
|
||||
UnitLen::M => crate::UnitLength::M,
|
||||
UnitLen::Mm => crate::UnitLength::Mm,
|
||||
UnitLen::Yards => crate::UnitLength::Yd,
|
||||
UnitLen::Unknown => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -790,6 +877,7 @@ impl From<UnitLen> for kittycad_modeling_cmds::units::UnitLength {
|
||||
UnitLen::M => kittycad_modeling_cmds::units::UnitLength::Meters,
|
||||
UnitLen::Mm => kittycad_modeling_cmds::units::UnitLength::Millimeters,
|
||||
UnitLen::Yards => kittycad_modeling_cmds::units::UnitLength::Yards,
|
||||
UnitLen::Unknown => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -802,24 +890,32 @@ pub enum UnitAngle {
|
||||
#[default]
|
||||
Degrees,
|
||||
Radians,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl UnitAngle {
|
||||
fn adjust_to(self, value: f64, to: UnitAngle) -> f64 {
|
||||
fn adjust_to(self, value: f64, to: UnitAngle) -> (f64, UnitAngle) {
|
||||
use std::f64::consts::PI;
|
||||
|
||||
use UnitAngle::*;
|
||||
|
||||
if !*CHECK_NUMERIC_TYPES {
|
||||
return value;
|
||||
return (value, to);
|
||||
}
|
||||
|
||||
match (self, to) {
|
||||
if to == Unknown {
|
||||
return (value, self);
|
||||
}
|
||||
|
||||
let value = match (self, to) {
|
||||
(Degrees, Degrees) => value,
|
||||
(Degrees, Radians) => (value / 180.0) * PI,
|
||||
(Radians, Degrees) => 180.0 * value / PI,
|
||||
(Radians, Radians) => value,
|
||||
}
|
||||
(Unknown, _) | (_, Unknown) => unreachable!(),
|
||||
};
|
||||
|
||||
(value, to)
|
||||
}
|
||||
}
|
||||
|
||||
@ -828,6 +924,7 @@ impl std::fmt::Display for UnitAngle {
|
||||
match self {
|
||||
UnitAngle::Degrees => write!(f, "deg"),
|
||||
UnitAngle::Radians => write!(f, "rad"),
|
||||
UnitAngle::Unknown => write!(f, "Angle"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -920,6 +1017,14 @@ impl KclValue {
|
||||
_ => Err(self.into()),
|
||||
},
|
||||
PrimitiveType::Plane => match value {
|
||||
KclValue::String { value: s, .. }
|
||||
if [
|
||||
"xy", "xz", "yz", "-xy", "-xz", "-yz", "XY", "XZ", "YZ", "-XY", "-XZ", "-YZ",
|
||||
]
|
||||
.contains(&&**s) =>
|
||||
{
|
||||
Ok(value.clone())
|
||||
}
|
||||
KclValue::Plane { .. } => Ok(value.clone()),
|
||||
KclValue::Object { value, meta } => {
|
||||
let origin = value
|
||||
@ -985,10 +1090,10 @@ impl KclValue {
|
||||
}
|
||||
|
||||
let origin = values.get("origin").ok_or(self.into()).and_then(|p| {
|
||||
p.coerce_to_array_type(&RuntimeType::number_any(), ArrayLen::Known(2), exec_state, true)
|
||||
p.coerce_to_array_type(&RuntimeType::length(), ArrayLen::Known(2), exec_state, true)
|
||||
})?;
|
||||
let direction = values.get("direction").ok_or(self.into()).and_then(|p| {
|
||||
p.coerce_to_array_type(&RuntimeType::number_any(), ArrayLen::Known(2), exec_state, true)
|
||||
p.coerce_to_array_type(&RuntimeType::length(), ArrayLen::Known(2), exec_state, true)
|
||||
})?;
|
||||
|
||||
Ok(KclValue::Object {
|
||||
@ -1013,10 +1118,10 @@ impl KclValue {
|
||||
}
|
||||
|
||||
let origin = values.get("origin").ok_or(self.into()).and_then(|p| {
|
||||
p.coerce_to_array_type(&RuntimeType::number_any(), ArrayLen::Known(3), exec_state, true)
|
||||
p.coerce_to_array_type(&RuntimeType::length(), ArrayLen::Known(3), exec_state, true)
|
||||
})?;
|
||||
let direction = values.get("direction").ok_or(self.into()).and_then(|p| {
|
||||
p.coerce_to_array_type(&RuntimeType::number_any(), ArrayLen::Known(3), exec_state, true)
|
||||
p.coerce_to_array_type(&RuntimeType::length(), ArrayLen::Known(3), exec_state, true)
|
||||
})?;
|
||||
|
||||
Ok(KclValue::Object {
|
||||
@ -1980,12 +2085,12 @@ u = min(3rad, 4in)
|
||||
assert_value_and_type("j", &result, 20.0, NumericType::default());
|
||||
assert_value_and_type("k", &result, 18.0, NumericType::Unknown);
|
||||
|
||||
assert_value_and_type("l", &result, 0.0, NumericType::count());
|
||||
assert_value_and_type("l", &result, 0.0, NumericType::default());
|
||||
assert_value_and_type("m", &result, 2.0, NumericType::count());
|
||||
if *CHECK_NUMERIC_TYPES {
|
||||
assert_value_and_type("n", &result, 127.0, NumericType::count());
|
||||
}
|
||||
assert_value_and_type("o", &result, 1.0, NumericType::mm());
|
||||
assert_value_and_type("o", &result, 1.0, NumericType::Unknown);
|
||||
assert_value_and_type("p", &result, 1.0, NumericType::count());
|
||||
assert_value_and_type("q", &result, 2.0, NumericType::Known(UnitType::Length(UnitLen::Inches)));
|
||||
|
||||
|
@ -3876,6 +3876,23 @@ mySk1 = startSketchOn(XY)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_numeric() {
|
||||
let test_program = "fn foo(x: number(Length)) {}";
|
||||
let tokens = crate::parsing::token::lex(test_program, ModuleId::default()).unwrap();
|
||||
run_parser(tokens.as_slice()).unwrap();
|
||||
|
||||
let test_program = "42_mm";
|
||||
let tokens = crate::parsing::token::lex(test_program, ModuleId::default()).unwrap();
|
||||
assert_eq!(tokens.iter().count(), 1);
|
||||
run_parser(tokens.as_slice()).unwrap();
|
||||
|
||||
let test_program = "42_Length";
|
||||
let tokens = crate::parsing::token::lex(test_program, ModuleId::default()).unwrap();
|
||||
assert_eq!(tokens.iter().count(), 2);
|
||||
assert_eq!(run_parser(tokens.as_slice()).unwrap_errs().count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parameter_list() {
|
||||
let tests = [
|
||||
|
@ -34,6 +34,8 @@ pub const NUM_SUFFIXES: [&str; 9] = ["mm", "cm", "m", "inch", "in", "ft", "yd",
|
||||
pub enum NumericSuffix {
|
||||
None,
|
||||
Count,
|
||||
Length,
|
||||
Angle,
|
||||
Mm,
|
||||
Cm,
|
||||
M,
|
||||
@ -58,6 +60,8 @@ impl NumericSuffix {
|
||||
match self {
|
||||
NumericSuffix::None => &[],
|
||||
NumericSuffix::Count => b"_",
|
||||
NumericSuffix::Length => b"Length",
|
||||
NumericSuffix::Angle => b"Angle",
|
||||
NumericSuffix::Mm => b"mm",
|
||||
NumericSuffix::Cm => b"cm",
|
||||
NumericSuffix::M => b"m",
|
||||
@ -75,7 +79,9 @@ impl FromStr for NumericSuffix {
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"_" => Ok(NumericSuffix::Count),
|
||||
"_" | "Count" => Ok(NumericSuffix::Count),
|
||||
"Length" => Ok(NumericSuffix::Length),
|
||||
"Angle" => Ok(NumericSuffix::Angle),
|
||||
"mm" | "millimeters" => Ok(NumericSuffix::Mm),
|
||||
"cm" | "centimeters" => Ok(NumericSuffix::Cm),
|
||||
"m" | "meters" => Ok(NumericSuffix::M),
|
||||
@ -94,6 +100,8 @@ impl fmt::Display for NumericSuffix {
|
||||
match self {
|
||||
NumericSuffix::None => Ok(()),
|
||||
NumericSuffix::Count => write!(f, "_"),
|
||||
NumericSuffix::Length => write!(f, "Length"),
|
||||
NumericSuffix::Angle => write!(f, "Angle"),
|
||||
NumericSuffix::Mm => write!(f, "mm"),
|
||||
NumericSuffix::Cm => write!(f, "cm"),
|
||||
NumericSuffix::M => write!(f, "m"),
|
||||
|
@ -7,21 +7,25 @@ use kittycad_modeling_cmds::{self as kcmc, shared::Color};
|
||||
use regex::Regex;
|
||||
use rgba_simple::Hex;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator::Validate;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{types::RuntimeType, ExecState, KclValue, Solid},
|
||||
execution::{
|
||||
types::{NumericType, PrimitiveType, RuntimeType},
|
||||
ExecState, KclValue, Solid,
|
||||
},
|
||||
std::Args,
|
||||
};
|
||||
|
||||
use super::args::TyF64;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref HEX_REGEX: Regex = Regex::new(r"^#[0-9a-fA-F]{6}$").unwrap();
|
||||
}
|
||||
|
||||
/// Data for appearance.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Validate)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct AppearanceData {
|
||||
@ -30,10 +34,10 @@ struct AppearanceData {
|
||||
pub color: String,
|
||||
/// Metalness of the new material, a percentage like 95.7.
|
||||
#[validate(range(min = 0.0, max = 100.0))]
|
||||
pub metalness: Option<f64>,
|
||||
pub metalness: Option<TyF64>,
|
||||
/// Roughness of the new material, a percentage like 95.7.
|
||||
#[validate(range(min = 0.0, max = 100.0))]
|
||||
pub roughness: Option<f64>,
|
||||
pub roughness: Option<TyF64>,
|
||||
// TODO(jess): we can also ambient occlusion here I just don't know what it is.
|
||||
}
|
||||
|
||||
@ -42,22 +46,15 @@ pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclVal
|
||||
let solids = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?;
|
||||
|
||||
let color: String = args.get_kw_arg("color")?;
|
||||
let metalness: Option<f64> = args.get_kw_arg_opt("metalness")?;
|
||||
let roughness: Option<f64> = args.get_kw_arg_opt("roughness")?;
|
||||
let count_ty = RuntimeType::Primitive(PrimitiveType::Number(NumericType::count()));
|
||||
let metalness: Option<TyF64> = args.get_kw_arg_opt_typed("metalness", &count_ty, exec_state)?;
|
||||
let roughness: Option<TyF64> = args.get_kw_arg_opt_typed("roughness", &count_ty, exec_state)?;
|
||||
let data = AppearanceData {
|
||||
color,
|
||||
metalness,
|
||||
roughness,
|
||||
};
|
||||
|
||||
// Validate the data.
|
||||
data.validate().map_err(|err| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Invalid appearance data: {}", err),
|
||||
source_ranges: vec![args.source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
// Make sure the color if set is valid.
|
||||
if !HEX_REGEX.is_match(&data.color) {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
@ -66,7 +63,15 @@ pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclVal
|
||||
}));
|
||||
}
|
||||
|
||||
let result = inner_appearance(solids, data.color, data.metalness, data.roughness, exec_state, args).await?;
|
||||
let result = inner_appearance(
|
||||
solids,
|
||||
data.color,
|
||||
data.metalness.map(|t| t.n),
|
||||
data.roughness.map(|t| t.n),
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(result.into())
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
kcl_value::FunctionSource,
|
||||
types::{NumericType, PrimitiveType, RuntimeType},
|
||||
types::{NumericType, PrimitiveType, RuntimeType, UnitLen},
|
||||
ExecState, ExecutorContext, ExtrudeSurface, Helix, KclObjectFields, KclValue, Metadata, Sketch, SketchSurface,
|
||||
Solid, TagIdentifier,
|
||||
},
|
||||
@ -537,31 +537,10 @@ impl Args {
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn get_number(&self) -> Result<f64, KclError> {
|
||||
FromArgs::from_args(self, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn get_number_with_type(&self) -> Result<TyF64, KclError> {
|
||||
FromArgs::from_args(self, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn get_number_array(&self) -> Result<Vec<f64>, KclError> {
|
||||
let numbers = self
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
let Some(num) = f64::from_kcl_val(&arg.value) else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
source_ranges: arg.source_ranges(),
|
||||
message: format!("Expected a number but found {}", arg.value.human_friendly_type()),
|
||||
}));
|
||||
};
|
||||
Ok(num)
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
Ok(numbers)
|
||||
}
|
||||
|
||||
pub(crate) fn get_number_array_with_types(&self) -> Result<Vec<TyF64>, KclError> {
|
||||
let numbers = self
|
||||
.args
|
||||
@ -663,23 +642,17 @@ impl Args {
|
||||
|
||||
pub(crate) fn get_data<'a, T>(&'a self) -> Result<T, KclError>
|
||||
where
|
||||
T: FromArgs<'a> + serde::de::DeserializeOwned,
|
||||
T: FromArgs<'a>,
|
||||
{
|
||||
FromArgs::from_args(self, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn get_sketch_data_and_optional_tag(
|
||||
&self,
|
||||
) -> Result<(super::sketch::SketchData, Option<FaceTag>), KclError> {
|
||||
FromArgs::from_args(self, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn get_data_and_sketch_and_tag<'a, T>(
|
||||
&'a self,
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<(T, Sketch, Option<TagNode>), KclError>
|
||||
where
|
||||
T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
|
||||
T: FromKclValue<'a> + Sized,
|
||||
{
|
||||
let data: T = FromArgs::from_args(self, 0)?;
|
||||
let Some(arg1) = self.args.get(1) else {
|
||||
@ -708,18 +681,12 @@ impl Args {
|
||||
Ok((data, sketch, tag))
|
||||
}
|
||||
|
||||
pub(crate) fn get_data_and_sketch_surface<'a, T>(&'a self) -> Result<(T, SketchSurface, Option<TagNode>), KclError>
|
||||
where
|
||||
T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
|
||||
{
|
||||
pub(crate) fn get_data_and_sketch_surface(&self) -> Result<([TyF64; 2], SketchSurface, Option<TagNode>), KclError> {
|
||||
FromArgs::from_args(self, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn get_data_and_solid<'a, T>(&'a self, exec_state: &mut ExecState) -> Result<(T, Box<Solid>), KclError>
|
||||
where
|
||||
T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
|
||||
{
|
||||
let data: T = FromArgs::from_args(self, 0)?;
|
||||
pub(crate) fn get_data_and_solid(&self, exec_state: &mut ExecState) -> Result<(TyF64, Box<Solid>), KclError> {
|
||||
let data = FromArgs::from_args(self, 0)?;
|
||||
let Some(arg1) = self.args.get(1) else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected a solid for second argument".to_owned(),
|
||||
@ -745,7 +712,7 @@ impl Args {
|
||||
Ok((data, solid))
|
||||
}
|
||||
|
||||
pub(crate) fn get_tag_to_number_sketch(&self) -> Result<(TagIdentifier, f64, Sketch), KclError> {
|
||||
pub(crate) fn get_tag_to_number_sketch(&self) -> Result<(TagIdentifier, TyF64, Sketch), KclError> {
|
||||
FromArgs::from_args(self, 0)
|
||||
}
|
||||
|
||||
@ -971,59 +938,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for [f64; 2] {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
match arg {
|
||||
KclValue::MixedArray { value, meta: _ } | KclValue::HomArray { value, .. } => {
|
||||
if value.len() != 2 {
|
||||
return None;
|
||||
}
|
||||
let v0 = value.first()?;
|
||||
let v1 = value.get(1)?;
|
||||
let array = [v0.as_f64()?, v1.as_f64()?];
|
||||
Some(array)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for [usize; 3] {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
match arg {
|
||||
KclValue::MixedArray { value, meta: _ } | KclValue::HomArray { value, .. } => {
|
||||
if value.len() != 3 {
|
||||
return None;
|
||||
}
|
||||
let v0 = value.first()?;
|
||||
let v1 = value.get(1)?;
|
||||
let v2 = value.get(2)?;
|
||||
let array = [v0.as_usize()?, v1.as_usize()?, v2.as_usize()?];
|
||||
Some(array)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for [f64; 3] {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
match arg {
|
||||
KclValue::MixedArray { value, meta: _ } | KclValue::HomArray { value, .. } => {
|
||||
if value.len() != 3 {
|
||||
return None;
|
||||
}
|
||||
let v0 = value.first()?;
|
||||
let v1 = value.get(1)?;
|
||||
let v2 = value.get(2)?;
|
||||
let array = [v0.as_f64()?, v1.as_f64()?, v2.as_f64()?];
|
||||
Some(array)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for TagNode {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
arg.get_tag_declarator().ok()
|
||||
@ -1217,10 +1131,10 @@ impl<'a> FromKclValue<'a> for FaceTag {
|
||||
impl<'a> FromKclValue<'a> for super::sketch::ArcData {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let obj = arg.as_object()?;
|
||||
let_field_of!(obj, radius);
|
||||
let case1 = || {
|
||||
let angle_start = obj.get("angleStart")?.as_f64()?;
|
||||
let angle_end = obj.get("angleEnd")?.as_f64()?;
|
||||
let angle_start = obj.get("angleStart")?.as_ty_f64()?;
|
||||
let angle_end = obj.get("angleEnd")?.as_ty_f64()?;
|
||||
let_field_of!(obj, radius, TyF64);
|
||||
Some(Self::AnglesAndRadius {
|
||||
angle_start,
|
||||
angle_end,
|
||||
@ -1231,6 +1145,7 @@ impl<'a> FromKclValue<'a> for super::sketch::ArcData {
|
||||
let obj = arg.as_object()?;
|
||||
let_field_of!(obj, to);
|
||||
let_field_of!(obj, center);
|
||||
let_field_of!(obj, radius, TyF64);
|
||||
Some(Self::CenterToRadius { center, to, radius })
|
||||
};
|
||||
case1().or_else(case2)
|
||||
@ -1259,14 +1174,26 @@ impl<'a> FromKclValue<'a> for crate::execution::Point3d {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
// Case 1: object with x/y/z fields
|
||||
if let Some(obj) = arg.as_object() {
|
||||
let_field_of!(obj, x);
|
||||
let_field_of!(obj, y);
|
||||
let_field_of!(obj, z);
|
||||
return Some(Self { x, y, z });
|
||||
let_field_of!(obj, x, TyF64);
|
||||
let_field_of!(obj, y, TyF64);
|
||||
let_field_of!(obj, z, TyF64);
|
||||
let (a, ty) = NumericType::combine_eq_array(&[x, y, z]);
|
||||
return Some(Self {
|
||||
x: a[0],
|
||||
y: a[1],
|
||||
z: a[2],
|
||||
units: ty.as_length().unwrap_or(UnitLen::Unknown),
|
||||
});
|
||||
}
|
||||
// Case 2: Array of 3 numbers.
|
||||
let [x, y, z]: [f64; 3] = FromKclValue::from_kcl_val(arg)?;
|
||||
Some(Self { x, y, z })
|
||||
let [x, y, z]: [TyF64; 3] = FromKclValue::from_kcl_val(arg)?;
|
||||
let (a, ty) = NumericType::combine_eq_array(&[x, y, z]);
|
||||
Some(Self {
|
||||
x: a[0],
|
||||
y: a[1],
|
||||
z: a[2],
|
||||
units: ty.as_length().unwrap_or(UnitLen::Unknown),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1592,14 +1519,7 @@ impl<'a> FromKclValue<'a> for u64 {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a> FromKclValue<'a> for f64 {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
match arg {
|
||||
KclValue::Number { value, .. } => Some(*value),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for TyF64 {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
match arg {
|
||||
@ -1609,6 +1529,41 @@ impl<'a> FromKclValue<'a> for TyF64 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for [TyF64; 2] {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
match arg {
|
||||
KclValue::MixedArray { value, meta: _ } | KclValue::HomArray { value, .. } => {
|
||||
if value.len() != 2 {
|
||||
return None;
|
||||
}
|
||||
let v0 = value.first()?;
|
||||
let v1 = value.get(1)?;
|
||||
let array = [v0.as_ty_f64()?, v1.as_ty_f64()?];
|
||||
Some(array)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for [TyF64; 3] {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
match arg {
|
||||
KclValue::MixedArray { value, meta: _ } | KclValue::HomArray { value, .. } => {
|
||||
if value.len() != 3 {
|
||||
return None;
|
||||
}
|
||||
let v0 = value.first()?;
|
||||
let v1 = value.get(1)?;
|
||||
let v2 = value.get(2)?;
|
||||
let array = [v0.as_ty_f64()?, v1.as_ty_f64()?, v2.as_ty_f64()?];
|
||||
Some(array)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for Sketch {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let KclValue::Sketch { value } = arg else {
|
||||
|
@ -9,6 +9,8 @@ use crate::{
|
||||
std::Args,
|
||||
};
|
||||
|
||||
use super::args::TyF64;
|
||||
|
||||
async fn _assert(value: bool, message: &str, args: &Args) -> Result<(), KclError> {
|
||||
if !value {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
@ -42,8 +44,8 @@ async fn inner_assert(data: bool, message: &str, args: &Args) -> Result<(), KclE
|
||||
}
|
||||
|
||||
pub async fn assert_lt(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (left, right, description): (f64, f64, String) = args.get_data()?;
|
||||
inner_assert_lt(left, right, &description, &args).await?;
|
||||
let (left, right, description): (TyF64, TyF64, String) = args.get_data()?;
|
||||
inner_assert_lt(left.n, right.n, &description, &args).await?;
|
||||
Ok(KclValue::none())
|
||||
}
|
||||
|
||||
@ -61,8 +63,8 @@ async fn inner_assert_lt(left: f64, right: f64, message: &str, args: &Args) -> R
|
||||
}
|
||||
|
||||
pub async fn assert_gt(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (left, right, description): (f64, f64, String) = args.get_data()?;
|
||||
inner_assert_gt(left, right, &description, &args).await?;
|
||||
let (left, right, description): (TyF64, TyF64, String) = args.get_data()?;
|
||||
inner_assert_gt(left.n, right.n, &description, &args).await?;
|
||||
Ok(KclValue::none())
|
||||
}
|
||||
|
||||
@ -94,8 +96,8 @@ async fn inner_assert_equal(left: f64, right: f64, epsilon: f64, message: &str,
|
||||
}
|
||||
|
||||
pub async fn assert_equal(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (left, right, epsilon, description): (f64, f64, f64, String) = args.get_data()?;
|
||||
inner_assert_equal(left, right, epsilon, &description, &args).await?;
|
||||
let (left, right, epsilon, description): (TyF64, TyF64, TyF64, String) = args.get_data()?;
|
||||
inner_assert_equal(left.n, right.n, epsilon.n, &description, &args).await?;
|
||||
Ok(KclValue::none())
|
||||
}
|
||||
|
||||
@ -113,8 +115,8 @@ async fn inner_assert_gt(left: f64, right: f64, message: &str, args: &Args) -> R
|
||||
}
|
||||
|
||||
pub async fn assert_lte(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (left, right, description): (f64, f64, String) = args.get_data()?;
|
||||
inner_assert_lte(left, right, &description, &args).await?;
|
||||
let (left, right, description): (TyF64, TyF64, String) = args.get_data()?;
|
||||
inner_assert_lte(left.n, right.n, &description, &args).await?;
|
||||
Ok(KclValue::none())
|
||||
}
|
||||
|
||||
@ -133,8 +135,8 @@ async fn inner_assert_lte(left: f64, right: f64, message: &str, args: &Args) ->
|
||||
}
|
||||
|
||||
pub async fn assert_gte(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (left, right, description): (f64, f64, String) = args.get_data()?;
|
||||
inner_assert_gte(left, right, &description, &args).await?;
|
||||
let (left, right, description): (TyF64, TyF64, String) = args.get_data()?;
|
||||
inner_assert_gte(left.n, right.n, &description, &args).await?;
|
||||
Ok(KclValue::none())
|
||||
}
|
||||
|
||||
|
@ -2,20 +2,23 @@
|
||||
|
||||
use crate::std::fillet::EdgeReference;
|
||||
|
||||
use super::args::TyF64;
|
||||
|
||||
/// A 2D axis or tagged edge.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Axis2dOrEdgeReference {
|
||||
/// 2D axis and origin.
|
||||
Axis { direction: [f64; 2], origin: [f64; 2] },
|
||||
Axis { direction: [TyF64; 2], origin: [TyF64; 2] },
|
||||
/// Tagged edge.
|
||||
Edge(EdgeReference),
|
||||
}
|
||||
|
||||
/// A 3D axis or tagged edge.
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Axis3dOrEdgeReference {
|
||||
/// 3D axis and origin.
|
||||
Axis { direction: [f64; 3], origin: [f64; 3] },
|
||||
Axis { direction: [TyF64; 3], origin: [TyF64; 3] },
|
||||
/// Tagged edge.
|
||||
Edge(EdgeReference),
|
||||
}
|
||||
|
@ -15,12 +15,14 @@ use crate::{
|
||||
std::{fillet::EdgeReference, Args},
|
||||
};
|
||||
|
||||
use super::args::TyF64;
|
||||
|
||||
pub(crate) const DEFAULT_TOLERANCE: f64 = 0.0000001;
|
||||
|
||||
/// Create chamfers on tagged paths.
|
||||
pub async fn chamfer(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solid = args.get_unlabeled_kw_arg_typed("solid", &RuntimeType::Primitive(PrimitiveType::Solid), exec_state)?;
|
||||
let length = args.get_kw_arg("length")?;
|
||||
let length: TyF64 = args.get_kw_arg_typed("length", &RuntimeType::length(), exec_state)?;
|
||||
let tags = args.kw_arg_array_and_source::<EdgeReference>("tags")?;
|
||||
let tag = args.get_kw_arg_opt("tag")?;
|
||||
|
||||
@ -43,7 +45,7 @@ pub async fn chamfer(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
/// thickness = 1
|
||||
/// chamferLength = 2
|
||||
///
|
||||
/// mountingPlateSketch = startSketchOn("XY")
|
||||
/// mountingPlateSketch = startSketchOn(XY)
|
||||
/// |> startProfileAt([-width/2, -length/2], %)
|
||||
/// |> line(endAbsolute = [width/2, -length/2], tag = $edge1)
|
||||
/// |> line(endAbsolute = [width/2, length/2], tag = $edge2)
|
||||
@ -65,7 +67,7 @@ pub async fn chamfer(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
/// ```no_run
|
||||
/// // Sketch on the face of a chamfer.
|
||||
/// fn cube(pos, scale) {
|
||||
/// sg = startSketchOn('XY')
|
||||
/// sg = startSketchOn(XY)
|
||||
/// |> startProfileAt(pos, %)
|
||||
/// |> line(end = [0, scale])
|
||||
/// |> line(end = [scale, 0])
|
||||
@ -84,7 +86,7 @@ pub async fn chamfer(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
/// tag = $chamfer1,
|
||||
/// )
|
||||
///
|
||||
/// sketch001 = startSketchOn(part001, chamfer1)
|
||||
/// sketch001 = startSketchOn(part001, face = chamfer1)
|
||||
/// |> startProfileAt([10, 10], %)
|
||||
/// |> line(end = [2, 0])
|
||||
/// |> line(end = [0, 2])
|
||||
@ -107,7 +109,7 @@ pub async fn chamfer(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
}]
|
||||
async fn inner_chamfer(
|
||||
solid: Box<Solid>,
|
||||
length: f64,
|
||||
length: TyF64,
|
||||
tags: Vec<EdgeReference>,
|
||||
tag: Option<TagNode>,
|
||||
exec_state: &mut ExecState,
|
||||
@ -135,7 +137,7 @@ async fn inner_chamfer(
|
||||
ModelingCmd::from(mcmd::Solid3dFilletEdge {
|
||||
edge_id,
|
||||
object_id: solid.id,
|
||||
radius: LengthUnit(length),
|
||||
radius: LengthUnit(length.n),
|
||||
tolerance: LengthUnit(DEFAULT_TOLERANCE), // We can let the user set this in the future.
|
||||
cut_type: CutType::Chamfer,
|
||||
}),
|
||||
@ -145,7 +147,7 @@ async fn inner_chamfer(
|
||||
solid.edge_cuts.push(EdgeCut::Chamfer {
|
||||
id,
|
||||
edge_id,
|
||||
length,
|
||||
length: length.clone(),
|
||||
tag: Box::new(tag.clone()),
|
||||
});
|
||||
|
||||
|
@ -16,13 +16,13 @@ use crate::{
|
||||
std::Args,
|
||||
};
|
||||
|
||||
use super::DEFAULT_TOLERANCE;
|
||||
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
||||
|
||||
/// Union two or more solids into a single solid.
|
||||
pub async fn union(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solids: Vec<Solid> =
|
||||
args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::Union(vec![RuntimeType::solids()]), exec_state)?;
|
||||
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
||||
let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::length(), exec_state)?;
|
||||
|
||||
if solids.len() < 2 {
|
||||
return Err(KclError::UndefinedValue(KclErrorDetails {
|
||||
@ -116,7 +116,7 @@ pub async fn union(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
}]
|
||||
pub(crate) async fn inner_union(
|
||||
solids: Vec<Solid>,
|
||||
tolerance: Option<f64>,
|
||||
tolerance: Option<TyF64>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Vec<Solid>, KclError> {
|
||||
@ -138,7 +138,7 @@ pub(crate) async fn inner_union(
|
||||
solid_out_id,
|
||||
ModelingCmd::from(mcmd::BooleanUnion {
|
||||
solid_ids: solids.iter().map(|s| s.id).collect(),
|
||||
tolerance: LengthUnit(tolerance.unwrap_or(DEFAULT_TOLERANCE)),
|
||||
tolerance: LengthUnit(tolerance.map(|t| t.n).unwrap_or(DEFAULT_TOLERANCE)),
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
@ -166,7 +166,7 @@ pub(crate) async fn inner_union(
|
||||
/// overlapping regions.
|
||||
pub async fn intersect(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solids: Vec<Solid> = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?;
|
||||
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
||||
let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::length(), exec_state)?;
|
||||
|
||||
if solids.len() < 2 {
|
||||
return Err(KclError::UndefinedValue(KclErrorDetails {
|
||||
@ -241,7 +241,7 @@ pub async fn intersect(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
}]
|
||||
pub(crate) async fn inner_intersect(
|
||||
solids: Vec<Solid>,
|
||||
tolerance: Option<f64>,
|
||||
tolerance: Option<TyF64>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Vec<Solid>, KclError> {
|
||||
@ -263,7 +263,7 @@ pub(crate) async fn inner_intersect(
|
||||
solid_out_id,
|
||||
ModelingCmd::from(mcmd::BooleanIntersection {
|
||||
solid_ids: solids.iter().map(|s| s.id).collect(),
|
||||
tolerance: LengthUnit(tolerance.unwrap_or(DEFAULT_TOLERANCE)),
|
||||
tolerance: LengthUnit(tolerance.map(|t| t.n).unwrap_or(DEFAULT_TOLERANCE)),
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
@ -306,7 +306,7 @@ pub async fn subtract(exec_state: &mut ExecState, args: Args) -> Result<KclValue
|
||||
}));
|
||||
}
|
||||
|
||||
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
||||
let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::length(), exec_state)?;
|
||||
|
||||
let solids = inner_subtract(solids, tools, tolerance, exec_state, args).await?;
|
||||
Ok(solids.into())
|
||||
@ -376,7 +376,7 @@ pub async fn subtract(exec_state: &mut ExecState, args: Args) -> Result<KclValue
|
||||
pub(crate) async fn inner_subtract(
|
||||
solids: Vec<Solid>,
|
||||
tools: Vec<Solid>,
|
||||
tolerance: Option<f64>,
|
||||
tolerance: Option<TyF64>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Vec<Solid>, KclError> {
|
||||
@ -400,7 +400,7 @@ pub(crate) async fn inner_subtract(
|
||||
ModelingCmd::from(mcmd::BooleanSubtract {
|
||||
target_ids: solids.iter().map(|s| s.id).collect(),
|
||||
tool_ids: tools.iter().map(|s| s.id).collect(),
|
||||
tolerance: LengthUnit(tolerance.unwrap_or(DEFAULT_TOLERANCE)),
|
||||
tolerance: LengthUnit(tolerance.map(|t| t.n).unwrap_or(DEFAULT_TOLERANCE)),
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
@ -26,20 +26,23 @@ use crate::{
|
||||
std::Args,
|
||||
};
|
||||
|
||||
use super::args::TyF64;
|
||||
|
||||
/// Extrudes by a given amount.
|
||||
pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
|
||||
let length = args.get_kw_arg("length")?;
|
||||
let length: TyF64 = args.get_kw_arg_typed("length", &RuntimeType::length(), exec_state)?;
|
||||
let symmetric = args.get_kw_arg_opt("symmetric")?;
|
||||
let bidirectional_length = args.get_kw_arg_opt("bidirectionalLength")?;
|
||||
let bidirectional_length: Option<TyF64> =
|
||||
args.get_kw_arg_opt_typed("bidirectionalLength", &RuntimeType::length(), exec_state)?;
|
||||
let tag_start = args.get_kw_arg_opt("tagStart")?;
|
||||
let tag_end = args.get_kw_arg_opt("tagEnd")?;
|
||||
|
||||
let result = inner_extrude(
|
||||
sketches,
|
||||
length,
|
||||
length.n,
|
||||
symmetric,
|
||||
bidirectional_length,
|
||||
bidirectional_length.map(|t| t.n),
|
||||
tag_start,
|
||||
tag_end,
|
||||
exec_state,
|
||||
|
@ -8,12 +8,11 @@ use kittycad_modeling_cmds as kcmc;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::DEFAULT_TOLERANCE;
|
||||
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
types::{PrimitiveType, RuntimeType},
|
||||
EdgeCut, ExecState, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, Solid, TagIdentifier,
|
||||
types::RuntimeType, EdgeCut, ExecState, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, Solid, TagIdentifier,
|
||||
},
|
||||
parsing::ast::types::TagNode,
|
||||
std::Args,
|
||||
@ -63,16 +62,16 @@ pub(super) fn validate_unique<T: Eq + std::hash::Hash>(tags: &[(T, SourceRange)]
|
||||
|
||||
/// Create fillets on tagged paths.
|
||||
pub async fn fillet(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solid = args.get_unlabeled_kw_arg_typed("solid", &RuntimeType::Primitive(PrimitiveType::Solid), exec_state)?;
|
||||
let radius = args.get_kw_arg("radius")?;
|
||||
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
||||
let solid = args.get_unlabeled_kw_arg_typed("solid", &RuntimeType::solid(), exec_state)?;
|
||||
let radius: TyF64 = args.get_kw_arg_typed("radius", &RuntimeType::length(), exec_state)?;
|
||||
let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::count(), exec_state)?;
|
||||
let tags = args.kw_arg_array_and_source::<EdgeReference>("tags")?;
|
||||
let tag = args.get_kw_arg_opt("tag")?;
|
||||
|
||||
// Run the function.
|
||||
validate_unique(&tags)?;
|
||||
let tags: Vec<EdgeReference> = tags.into_iter().map(|item| item.0).collect();
|
||||
let value = inner_fillet(solid, radius, tags, tolerance, tag, exec_state, args).await?;
|
||||
let value = inner_fillet(solid, radius, tags, tolerance.map(|t| t.n), tag, exec_state, args).await?;
|
||||
Ok(KclValue::Solid { value })
|
||||
}
|
||||
|
||||
@ -147,7 +146,7 @@ pub async fn fillet(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
}]
|
||||
async fn inner_fillet(
|
||||
solid: Box<Solid>,
|
||||
radius: f64,
|
||||
radius: TyF64,
|
||||
tags: Vec<EdgeReference>,
|
||||
tolerance: Option<f64>,
|
||||
tag: Option<TagNode>,
|
||||
@ -164,7 +163,7 @@ async fn inner_fillet(
|
||||
ModelingCmd::from(mcmd::Solid3dFilletEdge {
|
||||
edge_id,
|
||||
object_id: solid.id,
|
||||
radius: LengthUnit(radius),
|
||||
radius: LengthUnit(radius.n),
|
||||
tolerance: LengthUnit(tolerance.unwrap_or(DEFAULT_TOLERANCE)),
|
||||
cut_type: CutType::Fillet,
|
||||
}),
|
||||
@ -174,7 +173,7 @@ async fn inner_fillet(
|
||||
solid.edge_cuts.push(EdgeCut::Fillet {
|
||||
id,
|
||||
edge_id,
|
||||
radius,
|
||||
radius: radius.clone(),
|
||||
tag: Box::new(tag.clone()),
|
||||
});
|
||||
|
||||
|
@ -13,12 +13,14 @@ use crate::{
|
||||
std::{axis_or_reference::Axis3dOrEdgeReference, Args},
|
||||
};
|
||||
|
||||
use super::args::TyF64;
|
||||
|
||||
/// Create a helix.
|
||||
pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let angle_start = args.get_kw_arg("angleStart")?;
|
||||
let revolutions = args.get_kw_arg("revolutions")?;
|
||||
let angle_start: TyF64 = args.get_kw_arg_typed("angleStart", &RuntimeType::angle(), exec_state)?;
|
||||
let revolutions: TyF64 = args.get_kw_arg_typed("revolutions", &RuntimeType::count(), exec_state)?;
|
||||
let ccw = args.get_kw_arg_opt("ccw")?;
|
||||
let radius = args.get_kw_arg_opt("radius")?;
|
||||
let radius: Option<TyF64> = args.get_kw_arg_opt_typed("radius", &RuntimeType::length(), exec_state)?;
|
||||
let axis: Option<Axis3dOrEdgeReference> = args.get_kw_arg_opt_typed(
|
||||
"axis",
|
||||
&RuntimeType::Union(vec![
|
||||
@ -27,8 +29,8 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
]),
|
||||
exec_state,
|
||||
)?;
|
||||
let length = args.get_kw_arg_opt("length")?;
|
||||
let cylinder = args.get_kw_arg_opt("cylinder")?;
|
||||
let length: Option<TyF64> = args.get_kw_arg_opt_typed("length", &RuntimeType::length(), exec_state)?;
|
||||
let cylinder = args.get_kw_arg_opt_typed("cylinder", &RuntimeType::solid(), exec_state)?;
|
||||
|
||||
// Make sure we have a radius if we don't have a cylinder.
|
||||
if radius.is_none() && cylinder.is_none() {
|
||||
@ -79,12 +81,12 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
}
|
||||
|
||||
let value = inner_helix(
|
||||
revolutions,
|
||||
angle_start,
|
||||
revolutions.n,
|
||||
angle_start.n,
|
||||
ccw,
|
||||
radius,
|
||||
radius.map(|t| t.n),
|
||||
axis,
|
||||
length,
|
||||
length.map(|t| t.n),
|
||||
cylinder,
|
||||
exec_state,
|
||||
args,
|
||||
@ -154,14 +156,14 @@ async fn inner_helix(
|
||||
revolutions,
|
||||
start_angle: Angle::from_degrees(angle_start),
|
||||
axis: Point3d {
|
||||
x: direction[0],
|
||||
y: direction[1],
|
||||
z: direction[2],
|
||||
x: direction[0].n,
|
||||
y: direction[1].n,
|
||||
z: direction[2].n,
|
||||
},
|
||||
center: Point3d {
|
||||
x: LengthUnit(origin[0]),
|
||||
y: LengthUnit(origin[1]),
|
||||
z: LengthUnit(origin[2]),
|
||||
x: LengthUnit(origin[0].n),
|
||||
y: LengthUnit(origin[1].n),
|
||||
z: LengthUnit(origin[2].n),
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
@ -7,7 +7,7 @@ use kcl_derive_docs::stdlib;
|
||||
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, ModelingCmd};
|
||||
use kittycad_modeling_cmds as kcmc;
|
||||
|
||||
use super::DEFAULT_TOLERANCE;
|
||||
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{types::RuntimeType, ExecState, KclValue, Sketch, Solid},
|
||||
@ -30,7 +30,7 @@ pub async fn loft(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
// This can be set to override the automatically determined topological base curve, which is usually the first section encountered.
|
||||
let base_curve_index: Option<u32> = args.get_kw_arg_opt("baseCurveIndex")?;
|
||||
// Tolerance for the loft operation.
|
||||
let tolerance: Option<f64> = args.get_kw_arg_opt("tolerance")?;
|
||||
let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::count(), exec_state)?;
|
||||
let tag_start = args.get_kw_arg_opt("tagStart")?;
|
||||
let tag_end = args.get_kw_arg_opt("tagEnd")?;
|
||||
|
||||
@ -39,7 +39,7 @@ pub async fn loft(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
v_degree,
|
||||
bez_approximate_rational,
|
||||
base_curve_index,
|
||||
tolerance,
|
||||
tolerance.map(|t| t.n),
|
||||
tag_start,
|
||||
tag_end,
|
||||
exec_state,
|
||||
|
@ -3,11 +3,10 @@
|
||||
use anyhow::Result;
|
||||
use kcl_derive_docs::stdlib;
|
||||
|
||||
use super::args::FromArgs;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
types::{self, NumericType},
|
||||
types::{self, NumericType, RuntimeType},
|
||||
ExecState, KclValue,
|
||||
},
|
||||
std::args::{Args, TyF64},
|
||||
@ -17,8 +16,8 @@ use crate::{
|
||||
/// Compute the remainder after dividing `num` by `div`.
|
||||
/// If `num` is negative, the result will be too.
|
||||
pub async fn rem(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let n: TyF64 = args.get_unlabeled_kw_arg("number to divide")?;
|
||||
let d: TyF64 = args.get_kw_arg("divisor")?;
|
||||
let n: TyF64 = args.get_unlabeled_kw_arg_typed("number to divide", &RuntimeType::num_any(), exec_state)?;
|
||||
let d: TyF64 = args.get_kw_arg_typed("divisor", &RuntimeType::num_any(), exec_state)?;
|
||||
|
||||
let (n, d, ty) = NumericType::combine_div(n, d);
|
||||
if *types::CHECK_NUMERIC_TYPES && ty == NumericType::Unknown {
|
||||
@ -59,21 +58,21 @@ fn inner_rem(num: f64, divisor: f64) -> f64 {
|
||||
}
|
||||
|
||||
/// Compute the cosine of a number (in radians).
|
||||
pub async fn cos(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num: f64 = args.get_unlabeled_kw_arg("input")?;
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.cos())))
|
||||
pub async fn cos(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::radians(), exec_state)?;
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.n.cos())))
|
||||
}
|
||||
|
||||
/// Compute the sine of a number (in radians).
|
||||
pub async fn sin(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num: f64 = args.get_unlabeled_kw_arg("input")?;
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.sin())))
|
||||
pub async fn sin(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::radians(), exec_state)?;
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.n.sin())))
|
||||
}
|
||||
|
||||
/// Compute the tangent of a number (in radians).
|
||||
pub async fn tan(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num: f64 = args.get_unlabeled_kw_arg("input")?;
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.tan())))
|
||||
pub async fn tan(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::radians(), exec_state)?;
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.n.tan())))
|
||||
}
|
||||
|
||||
/// Return the value of `pi`. Archimedes’ constant (π).
|
||||
@ -105,11 +104,11 @@ fn inner_pi() -> Result<f64, KclError> {
|
||||
}
|
||||
|
||||
/// Compute the square root of a number.
|
||||
pub async fn sqrt(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num = args.get_number()?;
|
||||
let result = inner_sqrt(num)?;
|
||||
pub async fn sqrt(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num = args.get_number_with_type()?;
|
||||
let result = inner_sqrt(num.n)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::Unknown)))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
|
||||
/// Compute the square root of a number.
|
||||
@ -346,8 +345,8 @@ fn inner_max(args: Vec<f64>) -> f64 {
|
||||
}
|
||||
|
||||
/// Compute the number to a power.
|
||||
pub async fn pow(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let nums = args.get_number_array()?;
|
||||
pub async fn pow(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let nums = args.get_number_array_with_types()?;
|
||||
if nums.len() > 2 {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
message: format!("expected 2 arguments, got {}", nums.len()),
|
||||
@ -362,9 +361,9 @@ pub async fn pow(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
}));
|
||||
}
|
||||
|
||||
let result = inner_pow(nums[0], nums[1])?;
|
||||
let result = inner_pow(nums[0].n, nums[1].n)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::Unknown)))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
|
||||
/// Compute the number to a power.
|
||||
@ -391,8 +390,8 @@ fn inner_pow(num: f64, pow: f64) -> Result<f64, KclError> {
|
||||
|
||||
/// Compute the arccosine of a number (in radians).
|
||||
pub async fn acos(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num = args.get_number()?;
|
||||
let result = inner_acos(num)?;
|
||||
let num = args.get_number_with_type()?;
|
||||
let result = inner_acos(num.n)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
||||
}
|
||||
@ -422,8 +421,8 @@ fn inner_acos(num: f64) -> Result<f64, KclError> {
|
||||
|
||||
/// Compute the arcsine of a number (in radians).
|
||||
pub async fn asin(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num = args.get_number()?;
|
||||
let result = inner_asin(num)?;
|
||||
let num = args.get_number_with_type()?;
|
||||
let result = inner_asin(num.n)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
||||
}
|
||||
@ -452,8 +451,8 @@ fn inner_asin(num: f64) -> Result<f64, KclError> {
|
||||
|
||||
/// Compute the arctangent of a number (in radians).
|
||||
pub async fn atan(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num = args.get_number()?;
|
||||
let result = inner_atan(num)?;
|
||||
let num = args.get_number_with_type()?;
|
||||
let result = inner_atan(num.n)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
||||
}
|
||||
@ -481,8 +480,10 @@ fn inner_atan(num: f64) -> Result<f64, KclError> {
|
||||
}
|
||||
|
||||
/// Compute the four quadrant arctangent of Y and X (in radians).
|
||||
pub async fn atan2(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (y, x) = FromArgs::from_args(&args, 0)?;
|
||||
pub async fn atan2(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let y = args.get_kw_arg_typed("y", &RuntimeType::length(), exec_state)?;
|
||||
let x = args.get_kw_arg_typed("x", &RuntimeType::length(), exec_state)?;
|
||||
let (y, x, _) = NumericType::combine_eq(y, x);
|
||||
let result = inner_atan2(y, x)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
||||
@ -491,10 +492,10 @@ pub async fn atan2(_exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
/// Compute the four quadrant arctangent of Y and X (in radians).
|
||||
///
|
||||
/// ```no_run
|
||||
/// sketch001 = startSketchOn('XZ')
|
||||
/// sketch001 = startSketchOn(XZ)
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> angledLine(
|
||||
/// angle = toDegrees(atan2(1.25, 2)),
|
||||
/// angle = toDegrees(atan2(y = 1.25, x = 2)),
|
||||
/// length = 20,
|
||||
/// )
|
||||
/// |> yLine(endAbsolute = 0)
|
||||
@ -505,6 +506,12 @@ pub async fn atan2(_exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
#[stdlib {
|
||||
name = "atan2",
|
||||
tags = ["math"],
|
||||
keywords = true,
|
||||
unlabeled_first = false,
|
||||
args = {
|
||||
y = { docs = "Y"},
|
||||
x = { docs = "X"},
|
||||
}
|
||||
}]
|
||||
fn inner_atan2(y: f64, x: f64) -> Result<f64, KclError> {
|
||||
Ok(y.atan2(x))
|
||||
@ -515,8 +522,8 @@ fn inner_atan2(y: f64, x: f64) -> Result<f64, KclError> {
|
||||
/// The result might not be correctly rounded owing to implementation
|
||||
/// details; `log2()` can produce more accurate results for base 2,
|
||||
/// and `log10()` can produce more accurate results for base 10.
|
||||
pub async fn log(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let nums = args.get_number_array()?;
|
||||
pub async fn log(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let nums = args.get_number_array_with_types()?;
|
||||
if nums.len() > 2 {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
message: format!("expected 2 arguments, got {}", nums.len()),
|
||||
@ -530,9 +537,9 @@ pub async fn log(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
}
|
||||
let result = inner_log(nums[0], nums[1])?;
|
||||
let result = inner_log(nums[0].n, nums[1].n)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::Unknown)))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
|
||||
/// Compute the logarithm of the number with respect to an arbitrary base.
|
||||
@ -560,11 +567,11 @@ fn inner_log(num: f64, base: f64) -> Result<f64, KclError> {
|
||||
}
|
||||
|
||||
/// Compute the base 2 logarithm of the number.
|
||||
pub async fn log2(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num = args.get_number()?;
|
||||
let result = inner_log2(num)?;
|
||||
pub async fn log2(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num = args.get_number_with_type()?;
|
||||
let result = inner_log2(num.n)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::Unknown)))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
|
||||
/// Compute the base 2 logarithm of the number.
|
||||
@ -588,11 +595,11 @@ fn inner_log2(num: f64) -> Result<f64, KclError> {
|
||||
}
|
||||
|
||||
/// Compute the base 10 logarithm of the number.
|
||||
pub async fn log10(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num = args.get_number()?;
|
||||
let result = inner_log10(num)?;
|
||||
pub async fn log10(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num = args.get_number_with_type()?;
|
||||
let result = inner_log10(num.n)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::Unknown)))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
|
||||
/// Compute the base 10 logarithm of the number.
|
||||
@ -616,11 +623,11 @@ fn inner_log10(num: f64) -> Result<f64, KclError> {
|
||||
}
|
||||
|
||||
/// Compute the natural logarithm of the number.
|
||||
pub async fn ln(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num = args.get_number()?;
|
||||
let result = inner_ln(num)?;
|
||||
pub async fn ln(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num = args.get_number_with_type()?;
|
||||
let result = inner_ln(num.n)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::Unknown)))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
|
||||
/// Compute the natural logarithm of the number.
|
||||
@ -709,8 +716,8 @@ fn inner_tau() -> Result<f64, KclError> {
|
||||
|
||||
/// Converts a number from degrees to radians.
|
||||
pub async fn to_radians(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num = args.get_number()?;
|
||||
let result = inner_to_radians(num)?;
|
||||
let num = args.get_number_with_type()?;
|
||||
let result = inner_to_radians(num.n)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
||||
}
|
||||
@ -739,8 +746,8 @@ fn inner_to_radians(num: f64) -> Result<f64, KclError> {
|
||||
|
||||
/// Converts a number from radians to degrees.
|
||||
pub async fn to_degrees(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let num = args.get_number()?;
|
||||
let result = inner_to_degrees(num)?;
|
||||
let num = args.get_number_with_type()?;
|
||||
let result = inner_to_degrees(num.n)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
|
||||
}
|
||||
|
@ -61,13 +61,13 @@ async fn inner_mirror_2d(
|
||||
ModelingCmd::from(mcmd::EntityMirror {
|
||||
ids: starting_sketches.iter().map(|sketch| sketch.id).collect(),
|
||||
axis: Point3d {
|
||||
x: direction[0],
|
||||
y: direction[1],
|
||||
x: direction[0].n,
|
||||
y: direction[1].n,
|
||||
z: 0.0,
|
||||
},
|
||||
point: Point3d {
|
||||
x: LengthUnit(origin[0]),
|
||||
y: LengthUnit(origin[1]),
|
||||
x: LengthUnit(origin[0].n),
|
||||
y: LengthUnit(origin[1].n),
|
||||
z: LengthUnit(0.0),
|
||||
},
|
||||
}),
|
||||
|
@ -16,15 +16,18 @@ use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::args::Arg;
|
||||
use super::{
|
||||
args::Arg,
|
||||
utils::{untype_point, untype_point_3d},
|
||||
};
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
kcl_value::FunctionSource,
|
||||
types::{NumericType, RuntimeType},
|
||||
ExecState, Geometries, Geometry, KclObjectFields, KclValue, Point2d, Point3d, Sketch, Solid,
|
||||
ExecState, Geometries, Geometry, KclObjectFields, KclValue, Sketch, Solid,
|
||||
},
|
||||
std::Args,
|
||||
std::{args::TyF64, Args},
|
||||
ExecutorContext, SourceRange,
|
||||
};
|
||||
|
||||
@ -472,13 +475,14 @@ async fn make_transform<T: GeometryTrait>(
|
||||
|
||||
transforms
|
||||
.into_iter()
|
||||
.map(|obj| transform_from_obj_fields::<T>(obj, source_ranges.clone()))
|
||||
.map(|obj| transform_from_obj_fields::<T>(obj, source_ranges.clone(), exec_state))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn transform_from_obj_fields<T: GeometryTrait>(
|
||||
transform: KclObjectFields,
|
||||
source_ranges: Vec<SourceRange>,
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<Transform, KclError> {
|
||||
// Apply defaults to the transform.
|
||||
let replicate = match transform.get("replicate") {
|
||||
@ -494,13 +498,26 @@ fn transform_from_obj_fields<T: GeometryTrait>(
|
||||
};
|
||||
|
||||
let scale = match transform.get("scale") {
|
||||
Some(x) => T::array_to_point3d(x, source_ranges.clone())?,
|
||||
None => Point3d { x: 1.0, y: 1.0, z: 1.0 },
|
||||
Some(x) => untype_point_3d(T::array_to_point3d(x, source_ranges.clone(), exec_state)?)
|
||||
.0
|
||||
.into(),
|
||||
None => kcmc::shared::Point3d { x: 1.0, y: 1.0, z: 1.0 },
|
||||
};
|
||||
|
||||
let translate = match transform.get("translate") {
|
||||
Some(x) => T::array_to_point3d(x, source_ranges.clone())?,
|
||||
None => Point3d { x: 0.0, y: 0.0, z: 0.0 },
|
||||
Some(x) => {
|
||||
let (arr, _) = untype_point_3d(T::array_to_point3d(x, source_ranges.clone(), exec_state)?);
|
||||
kcmc::shared::Point3d::<LengthUnit> {
|
||||
x: LengthUnit(arr[0]),
|
||||
y: LengthUnit(arr[1]),
|
||||
z: LengthUnit(arr[2]),
|
||||
}
|
||||
}
|
||||
None => kcmc::shared::Point3d::<LengthUnit> {
|
||||
x: LengthUnit(0.0),
|
||||
y: LengthUnit(0.0),
|
||||
z: LengthUnit(0.0),
|
||||
},
|
||||
};
|
||||
|
||||
let mut rotation = Rotation::default();
|
||||
@ -513,7 +530,9 @@ fn transform_from_obj_fields<T: GeometryTrait>(
|
||||
}));
|
||||
};
|
||||
if let Some(axis) = rot.get("axis") {
|
||||
rotation.axis = T::array_to_point3d(axis, source_ranges.clone())?.into();
|
||||
rotation.axis = untype_point_3d(T::array_to_point3d(axis, source_ranges.clone(), exec_state)?)
|
||||
.0
|
||||
.into();
|
||||
}
|
||||
if let Some(angle) = rot.get("angle") {
|
||||
match angle {
|
||||
@ -533,7 +552,9 @@ fn transform_from_obj_fields<T: GeometryTrait>(
|
||||
KclValue::String { value: s, meta: _ } if s == "local" => OriginType::Local,
|
||||
KclValue::String { value: s, meta: _ } if s == "global" => OriginType::Global,
|
||||
other => {
|
||||
let origin = T::array_to_point3d(other, source_ranges.clone())?.into();
|
||||
let origin = untype_point_3d(T::array_to_point3d(other, source_ranges.clone(), exec_state)?)
|
||||
.0
|
||||
.into();
|
||||
OriginType::Custom { origin }
|
||||
}
|
||||
};
|
||||
@ -542,73 +563,50 @@ fn transform_from_obj_fields<T: GeometryTrait>(
|
||||
|
||||
Ok(Transform {
|
||||
replicate,
|
||||
scale: scale.into(),
|
||||
translate: translate.into(),
|
||||
scale,
|
||||
translate,
|
||||
rotation,
|
||||
})
|
||||
}
|
||||
|
||||
fn array_to_point3d(val: &KclValue, source_ranges: Vec<SourceRange>) -> Result<Point3d, KclError> {
|
||||
let KclValue::MixedArray { value: arr, meta } = val else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected an array of 3 numbers (i.e. a 3D point)".to_string(),
|
||||
source_ranges,
|
||||
}));
|
||||
};
|
||||
let len = arr.len();
|
||||
if len != 3 {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Expected an array of 3 numbers (i.e. a 3D point) but found {len} items"),
|
||||
source_ranges,
|
||||
}));
|
||||
};
|
||||
// Gets an f64 from a KCL value.
|
||||
let f = |k: &KclValue, component: char| {
|
||||
use super::args::FromKclValue;
|
||||
if let Some(value) = f64::from_kcl_val(k) {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("{component} component of this point was not a number"),
|
||||
source_ranges: meta.iter().map(|m| m.source_range).collect(),
|
||||
}))
|
||||
}
|
||||
};
|
||||
let x = f(&arr[0], 'x')?;
|
||||
let y = f(&arr[1], 'y')?;
|
||||
let z = f(&arr[2], 'z')?;
|
||||
Ok(Point3d { x, y, z })
|
||||
fn array_to_point3d(
|
||||
val: &KclValue,
|
||||
source_ranges: Vec<SourceRange>,
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<[TyF64; 3], KclError> {
|
||||
val.coerce(&RuntimeType::point3d(), exec_state)
|
||||
.map_err(|e| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"Expected an array of 3 numbers (i.e., a 3D point), found {}",
|
||||
e.found
|
||||
.map(|t| t.human_friendly_type())
|
||||
.unwrap_or_else(|| val.human_friendly_type().to_owned())
|
||||
),
|
||||
source_ranges,
|
||||
})
|
||||
})
|
||||
.map(|val| val.as_point3d().unwrap())
|
||||
}
|
||||
|
||||
fn array_to_point2d(val: &KclValue, source_ranges: Vec<SourceRange>) -> Result<Point2d, KclError> {
|
||||
let KclValue::MixedArray { value: arr, meta } = val else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected an array of 2 numbers (i.e. a 2D point)".to_string(),
|
||||
source_ranges,
|
||||
}));
|
||||
};
|
||||
let len = arr.len();
|
||||
if len != 2 {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Expected an array of 2 numbers (i.e. a 2D point) but found {len} items"),
|
||||
source_ranges,
|
||||
}));
|
||||
};
|
||||
// Gets an f64 from a KCL value.
|
||||
let f = |k: &KclValue, component: char| {
|
||||
use super::args::FromKclValue;
|
||||
if let Some(value) = f64::from_kcl_val(k) {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("{component} component of this point was not a number"),
|
||||
source_ranges: meta.iter().map(|m| m.source_range).collect(),
|
||||
}))
|
||||
}
|
||||
};
|
||||
let x = f(&arr[0], 'x')?;
|
||||
let y = f(&arr[1], 'y')?;
|
||||
Ok(Point2d { x, y })
|
||||
fn array_to_point2d(
|
||||
val: &KclValue,
|
||||
source_ranges: Vec<SourceRange>,
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<[TyF64; 2], KclError> {
|
||||
val.coerce(&RuntimeType::point2d(), exec_state)
|
||||
.map_err(|e| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"Expected an array of 2 numbers (i.e., a 2D point), found {}",
|
||||
e.found
|
||||
.map(|t| t.human_friendly_type())
|
||||
.unwrap_or_else(|| val.human_friendly_type().to_owned())
|
||||
),
|
||||
source_ranges,
|
||||
})
|
||||
})
|
||||
.map(|val| val.as_point2d().unwrap())
|
||||
}
|
||||
|
||||
trait GeometryTrait: Clone {
|
||||
@ -616,7 +614,11 @@ trait GeometryTrait: Clone {
|
||||
fn id(&self) -> Uuid;
|
||||
fn original_id(&self) -> Uuid;
|
||||
fn set_id(&mut self, id: Uuid);
|
||||
fn array_to_point3d(val: &KclValue, source_ranges: Vec<SourceRange>) -> Result<Point3d, KclError>;
|
||||
fn array_to_point3d(
|
||||
val: &KclValue,
|
||||
source_ranges: Vec<SourceRange>,
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<[TyF64; 3], KclError>;
|
||||
async fn flush_batch(args: &Args, exec_state: &mut ExecState, set: &Self::Set) -> Result<(), KclError>;
|
||||
}
|
||||
|
||||
@ -631,9 +633,14 @@ impl GeometryTrait for Sketch {
|
||||
fn original_id(&self) -> Uuid {
|
||||
self.original_id
|
||||
}
|
||||
fn array_to_point3d(val: &KclValue, source_ranges: Vec<SourceRange>) -> Result<Point3d, KclError> {
|
||||
let Point2d { x, y } = array_to_point2d(val, source_ranges)?;
|
||||
Ok(Point3d { x, y, z: 0.0 })
|
||||
fn array_to_point3d(
|
||||
val: &KclValue,
|
||||
source_ranges: Vec<SourceRange>,
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<[TyF64; 3], KclError> {
|
||||
let [x, y] = array_to_point2d(val, source_ranges, exec_state)?;
|
||||
let ty = x.ty.clone();
|
||||
Ok([x, y, TyF64::new(0.0, ty)])
|
||||
}
|
||||
|
||||
async fn flush_batch(_: &Args, _: &mut ExecState, _: &Self::Set) -> Result<(), KclError> {
|
||||
@ -655,8 +662,12 @@ impl GeometryTrait for Solid {
|
||||
self.sketch.original_id
|
||||
}
|
||||
|
||||
fn array_to_point3d(val: &KclValue, source_ranges: Vec<SourceRange>) -> Result<Point3d, KclError> {
|
||||
array_to_point3d(val, source_ranges)
|
||||
fn array_to_point3d(
|
||||
val: &KclValue,
|
||||
source_ranges: Vec<SourceRange>,
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<[TyF64; 3], KclError> {
|
||||
array_to_point3d(val, source_ranges, exec_state)
|
||||
}
|
||||
|
||||
async fn flush_batch(args: &Args, exec_state: &mut ExecState, solid_set: &Self::Set) -> Result<(), KclError> {
|
||||
@ -669,30 +680,35 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::execution::types::NumericType;
|
||||
|
||||
#[test]
|
||||
fn test_array_to_point3d() {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_array_to_point3d() {
|
||||
let mut exec_state = ExecState::new(&ExecutorContext::new_mock().await);
|
||||
let input = KclValue::MixedArray {
|
||||
value: vec![
|
||||
KclValue::Number {
|
||||
value: 1.1,
|
||||
meta: Default::default(),
|
||||
ty: NumericType::Unknown,
|
||||
ty: NumericType::mm(),
|
||||
},
|
||||
KclValue::Number {
|
||||
value: 2.2,
|
||||
meta: Default::default(),
|
||||
ty: NumericType::Unknown,
|
||||
ty: NumericType::mm(),
|
||||
},
|
||||
KclValue::Number {
|
||||
value: 3.3,
|
||||
meta: Default::default(),
|
||||
ty: NumericType::Unknown,
|
||||
ty: NumericType::mm(),
|
||||
},
|
||||
],
|
||||
meta: Default::default(),
|
||||
};
|
||||
let expected = Point3d { x: 1.1, y: 2.2, z: 3.3 };
|
||||
let actual = array_to_point3d(&input, Vec::new());
|
||||
let expected = [
|
||||
TyF64::new(1.1, NumericType::mm()),
|
||||
TyF64::new(2.2, NumericType::mm()),
|
||||
TyF64::new(3.3, NumericType::mm()),
|
||||
];
|
||||
let actual = array_to_point3d(&input, Vec::new(), &mut exec_state);
|
||||
assert_eq!(actual.unwrap(), expected);
|
||||
}
|
||||
}
|
||||
@ -701,10 +717,11 @@ mod tests {
|
||||
pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
|
||||
let instances: u32 = args.get_kw_arg("instances")?;
|
||||
let distance: f64 = args.get_kw_arg("distance")?;
|
||||
let axis: [f64; 2] = args.get_kw_arg("axis")?;
|
||||
let distance: TyF64 = args.get_kw_arg_typed("distance", &RuntimeType::length(), exec_state)?;
|
||||
let axis: [TyF64; 2] = args.get_kw_arg_typed("axis", &RuntimeType::point2d(), exec_state)?;
|
||||
let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
|
||||
|
||||
let axis = untype_point(axis).0;
|
||||
if axis == [0.0, 0.0] {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message:
|
||||
@ -714,7 +731,8 @@ pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result
|
||||
}));
|
||||
}
|
||||
|
||||
let sketches = inner_pattern_linear_2d(sketches, instances, distance, axis, use_original, exec_state, args).await?;
|
||||
let sketches =
|
||||
inner_pattern_linear_2d(sketches, instances, distance.n, axis, use_original, exec_state, args).await?;
|
||||
Ok(sketches.into())
|
||||
}
|
||||
|
||||
@ -780,10 +798,11 @@ async fn inner_pattern_linear_2d(
|
||||
pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solids = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?;
|
||||
let instances: u32 = args.get_kw_arg("instances")?;
|
||||
let distance: f64 = args.get_kw_arg("distance")?;
|
||||
let axis: [f64; 3] = args.get_kw_arg("axis")?;
|
||||
let distance: TyF64 = args.get_kw_arg_typed("distance", &RuntimeType::length(), exec_state)?;
|
||||
let axis: [TyF64; 3] = args.get_kw_arg_typed("axis", &RuntimeType::point3d(), exec_state)?;
|
||||
let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
|
||||
|
||||
let (axis, _) = untype_point_3d(axis);
|
||||
if axis == [0.0, 0.0, 0.0] {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message:
|
||||
@ -793,7 +812,7 @@ pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result
|
||||
}));
|
||||
}
|
||||
|
||||
let solids = inner_pattern_linear_3d(solids, instances, distance, axis, use_original, exec_state, args).await?;
|
||||
let solids = inner_pattern_linear_3d(solids, instances, distance.n, axis, use_original, exec_state, args).await?;
|
||||
Ok(solids.into())
|
||||
}
|
||||
|
||||
@ -828,11 +847,11 @@ pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result
|
||||
/// |> close(%)
|
||||
/// |> extrude(length = 65)
|
||||
///
|
||||
/// const thing1 = startSketchOn(case, END)
|
||||
/// const thing1 = startSketchOn(case, face = END)
|
||||
/// |> circle(center = [-size / 2, -size / 2], radius = 25)
|
||||
/// |> extrude(length = 50)
|
||||
///
|
||||
/// const thing2 = startSketchOn(case, END)
|
||||
/// const thing2 = startSketchOn(case, face = END)
|
||||
/// |> circle(center = [size / 2, -size / 2], radius = 25)
|
||||
/// |> extrude(length = 50)
|
||||
///
|
||||
@ -856,7 +875,7 @@ pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result
|
||||
/// |> close(%)
|
||||
/// |> extrude(length = 65)
|
||||
///
|
||||
/// const thing1 = startSketchOn(case, END)
|
||||
/// const thing1 = startSketchOn(case, face = END)
|
||||
/// |> circle(center =[-size / 2, -size / 2], radius = 25)
|
||||
/// |> extrude(length = 50)
|
||||
///
|
||||
@ -1025,16 +1044,16 @@ impl CircularPattern {
|
||||
pub async fn pattern_circular_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
|
||||
let instances: u32 = args.get_kw_arg("instances")?;
|
||||
let center: [f64; 2] = args.get_kw_arg("center")?;
|
||||
let arc_degrees: f64 = args.get_kw_arg("arcDegrees")?;
|
||||
let center: [TyF64; 2] = args.get_kw_arg_typed("center", &RuntimeType::point2d(), exec_state)?;
|
||||
let arc_degrees: TyF64 = args.get_kw_arg_typed("arcDegrees", &RuntimeType::angle(), exec_state)?;
|
||||
let rotate_duplicates: bool = args.get_kw_arg("rotateDuplicates")?;
|
||||
let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
|
||||
|
||||
let sketches = inner_pattern_circular_2d(
|
||||
sketches,
|
||||
instances,
|
||||
center,
|
||||
arc_degrees,
|
||||
untype_point(center).0,
|
||||
arc_degrees.n,
|
||||
rotate_duplicates,
|
||||
use_original,
|
||||
exec_state,
|
||||
@ -1134,11 +1153,11 @@ pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Resu
|
||||
// If instances is 1, this has no effect.
|
||||
let instances: u32 = args.get_kw_arg("instances")?;
|
||||
// The axis around which to make the pattern. This is a 3D vector.
|
||||
let axis: [f64; 3] = args.get_kw_arg("axis")?;
|
||||
let axis: [TyF64; 3] = args.get_kw_arg_typed("axis", &RuntimeType::point3d(), exec_state)?;
|
||||
// The center about which to make the pattern. This is a 3D vector.
|
||||
let center: [f64; 3] = args.get_kw_arg("center")?;
|
||||
let center: [TyF64; 3] = args.get_kw_arg_typed("center", &RuntimeType::point3d(), exec_state)?;
|
||||
// The arc angle (in degrees) to place the repetitions. Must be greater than 0.
|
||||
let arc_degrees: f64 = args.get_kw_arg("arcDegrees")?;
|
||||
let arc_degrees: TyF64 = args.get_kw_arg_typed("arcDegrees", &RuntimeType::angle(), exec_state)?;
|
||||
// Whether or not to rotate the duplicates as they are copied.
|
||||
let rotate_duplicates: bool = args.get_kw_arg("rotateDuplicates")?;
|
||||
// If the target being patterned is itself a pattern, then, should you use the original solid,
|
||||
@ -1148,9 +1167,9 @@ pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Resu
|
||||
let solids = inner_pattern_circular_3d(
|
||||
solids,
|
||||
instances,
|
||||
axis,
|
||||
center,
|
||||
arc_degrees,
|
||||
untype_point_3d(axis).0,
|
||||
untype_point_3d(center).0,
|
||||
arc_degrees.n,
|
||||
rotate_duplicates,
|
||||
use_original,
|
||||
exec_state,
|
||||
|
@ -4,18 +4,18 @@ use kcl_derive_docs::stdlib;
|
||||
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, shared::Color, ModelingCmd};
|
||||
use kittycad_modeling_cmds as kcmc;
|
||||
|
||||
use super::sketch::PlaneData;
|
||||
use super::{args::TyF64, sketch::PlaneData};
|
||||
use crate::{
|
||||
errors::KclError,
|
||||
execution::{ExecState, KclValue, Plane, PlaneType},
|
||||
execution::{types::RuntimeType, ExecState, KclValue, Plane, PlaneType},
|
||||
std::Args,
|
||||
};
|
||||
|
||||
/// Offset a plane by a distance along its normal.
|
||||
pub async fn offset_plane(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let std_plane = args.get_unlabeled_kw_arg("plane")?;
|
||||
let offset = args.get_kw_arg("offset")?;
|
||||
let plane = inner_offset_plane(std_plane, offset, exec_state).await?;
|
||||
let offset: TyF64 = args.get_kw_arg_typed("offset", &RuntimeType::length(), exec_state)?;
|
||||
let plane = inner_offset_plane(std_plane, offset.n, exec_state).await?;
|
||||
make_offset_plane_in_engine(&plane, exec_state, &args).await?;
|
||||
Ok(KclValue::Plane { value: Box::new(plane) })
|
||||
}
|
||||
@ -108,7 +108,7 @@ pub async fn offset_plane(exec_state: &mut ExecState, args: Args) -> Result<KclV
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
plane = { docs = "The plane (e.g. 'XY') which this new plane is created from." },
|
||||
plane = { docs = "The plane (e.g. XY) which this new plane is created from." },
|
||||
offset = { docs = "Distance from the standard plane this new plane will be created at." },
|
||||
}
|
||||
}]
|
||||
|
@ -9,7 +9,7 @@ use kcmc::{
|
||||
};
|
||||
use kittycad_modeling_cmds::{self as kcmc, shared::Point3d};
|
||||
|
||||
use super::DEFAULT_TOLERANCE;
|
||||
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
@ -31,22 +31,23 @@ pub async fn revolve(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
]),
|
||||
exec_state,
|
||||
)?;
|
||||
let angle = args.get_kw_arg_opt("angle")?;
|
||||
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
||||
let angle: Option<TyF64> = args.get_kw_arg_opt_typed("angle", &RuntimeType::angle(), exec_state)?;
|
||||
let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::count(), exec_state)?;
|
||||
let tag_start = args.get_kw_arg_opt("tagStart")?;
|
||||
let tag_end = args.get_kw_arg_opt("tagEnd")?;
|
||||
let symmetric = args.get_kw_arg_opt("symmetric")?;
|
||||
let bidirectional_angle = args.get_kw_arg_opt("bidirectionalAngle")?;
|
||||
let bidirectional_angle: Option<TyF64> =
|
||||
args.get_kw_arg_opt_typed("bidirectionalAngle", &RuntimeType::angle(), exec_state)?;
|
||||
|
||||
let value = inner_revolve(
|
||||
sketches,
|
||||
axis,
|
||||
angle,
|
||||
tolerance,
|
||||
angle.map(|t| t.n),
|
||||
tolerance.map(|t| t.n),
|
||||
tag_start,
|
||||
tag_end,
|
||||
symmetric,
|
||||
bidirectional_angle,
|
||||
bidirectional_angle.map(|t| t.n),
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
@ -139,13 +140,13 @@ async fn inner_revolve(
|
||||
angle,
|
||||
target: sketch.id.into(),
|
||||
axis: Point3d {
|
||||
x: direction[0],
|
||||
y: direction[1],
|
||||
x: direction[0].n,
|
||||
y: direction[1].n,
|
||||
z: 0.0,
|
||||
},
|
||||
origin: Point3d {
|
||||
x: LengthUnit(origin[0]),
|
||||
y: LengthUnit(origin[1]),
|
||||
x: LengthUnit(origin[0].n),
|
||||
y: LengthUnit(origin[1].n),
|
||||
z: LengthUnit(0.0),
|
||||
},
|
||||
tolerance: LengthUnit(tolerance.unwrap_or(DEFAULT_TOLERANCE)),
|
||||
|
@ -8,11 +8,13 @@ use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
types::{NumericType, PrimitiveType, RuntimeType},
|
||||
ExecState, KclValue, Point2d, Sketch, TagIdentifier,
|
||||
ExecState, KclValue, Sketch, TagIdentifier,
|
||||
},
|
||||
std::{args::TyF64, utils::between, Args},
|
||||
};
|
||||
|
||||
use super::utils::untype_point;
|
||||
|
||||
/// Returns the point at the end of the given segment.
|
||||
pub async fn segment_end(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let tag: TagIdentifier = args.get_unlabeled_kw_arg("tag")?;
|
||||
@ -562,17 +564,17 @@ async fn inner_tangent_to_end(tag: &TagIdentifier, exec_state: &mut ExecState, a
|
||||
})
|
||||
})?;
|
||||
|
||||
let from = Point2d::from(path.get_to());
|
||||
let from = untype_point(path.get_to()).0;
|
||||
|
||||
// Undocumented voodoo from get_tangential_arc_to_info
|
||||
let tangent_info = path.get_tangential_info();
|
||||
let tan_previous_point = tangent_info.tan_previous_point(from.into());
|
||||
let tan_previous_point = tangent_info.tan_previous_point(from);
|
||||
|
||||
// Calculate the end point from the angle and radius.
|
||||
// atan2 outputs radians.
|
||||
let previous_end_tangent = Angle::from_radians(f64::atan2(
|
||||
from.y - tan_previous_point[1],
|
||||
from.x - tan_previous_point[0],
|
||||
from[1] - tan_previous_point[1],
|
||||
from[0] - tan_previous_point[0],
|
||||
));
|
||||
|
||||
Ok(previous_end_tangent.to_degrees())
|
||||
@ -581,7 +583,7 @@ async fn inner_tangent_to_end(tag: &TagIdentifier, exec_state: &mut ExecState, a
|
||||
/// Returns the angle to match the given length for x.
|
||||
pub async fn angle_to_match_length_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (tag, to, sketch) = args.get_tag_to_number_sketch()?;
|
||||
let result = inner_angle_to_match_length_x(&tag, to, sketch, exec_state, args.clone())?;
|
||||
let result = inner_angle_to_match_length_x(&tag, to.n, sketch, exec_state, args.clone())?;
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
|
||||
}
|
||||
|
||||
@ -645,7 +647,7 @@ fn inner_angle_to_match_length_x(
|
||||
/// Returns the angle to match the given length for y.
|
||||
pub async fn angle_to_match_length_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (tag, to, sketch) = args.get_tag_to_number_sketch()?;
|
||||
let result = inner_angle_to_match_length_y(&tag, to, sketch, exec_state, args.clone())?;
|
||||
let result = inner_angle_to_match_length_y(&tag, to.n, sketch, exec_state, args.clone())?;
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
|
||||
}
|
||||
|
||||
|
@ -11,11 +11,11 @@ use kcmc::{
|
||||
use kittycad_modeling_cmds as kcmc;
|
||||
use kittycad_modeling_cmds::shared::PathSegment;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{BasePath, ExecState, GeoMeta, KclValue, Path, Sketch, SketchSurface},
|
||||
execution::{types::RuntimeType, BasePath, ExecState, GeoMeta, KclValue, Path, Sketch, SketchSurface},
|
||||
parsing::ast::types::TagNode,
|
||||
std::{
|
||||
sketch::NEW_TAG_KW,
|
||||
@ -24,6 +24,8 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use super::{args::TyF64, utils::untype_point};
|
||||
|
||||
/// A sketch surface or a sketch.
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
@ -36,11 +38,19 @@ pub enum SketchOrSurface {
|
||||
/// Sketch a circle.
|
||||
pub async fn circle(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch_or_surface = args.get_unlabeled_kw_arg("sketchOrSurface")?;
|
||||
let center = args.get_kw_arg("center")?;
|
||||
let radius = args.get_kw_arg("radius")?;
|
||||
let center = args.get_kw_arg_typed("center", &RuntimeType::point2d(), exec_state)?;
|
||||
let radius: TyF64 = args.get_kw_arg_typed("radius", &RuntimeType::length(), exec_state)?;
|
||||
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
||||
|
||||
let sketch = inner_circle(sketch_or_surface, center, radius, tag, exec_state, args).await?;
|
||||
let sketch = inner_circle(
|
||||
sketch_or_surface,
|
||||
untype_point(center).0,
|
||||
radius.n,
|
||||
tag,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(sketch),
|
||||
})
|
||||
@ -121,12 +131,21 @@ async fn inner_circle(
|
||||
/// Sketch a 3-point circle.
|
||||
pub async fn circle_three_point(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch_surface_or_group = args.get_unlabeled_kw_arg("sketch_surface_or_group")?;
|
||||
let p1 = args.get_kw_arg("p1")?;
|
||||
let p2 = args.get_kw_arg("p2")?;
|
||||
let p3 = args.get_kw_arg("p3")?;
|
||||
let p1 = args.get_kw_arg_typed("p1", &RuntimeType::point2d(), exec_state)?;
|
||||
let p2 = args.get_kw_arg_typed("p2", &RuntimeType::point2d(), exec_state)?;
|
||||
let p3 = args.get_kw_arg_typed("p3", &RuntimeType::point2d(), exec_state)?;
|
||||
let tag = args.get_kw_arg_opt("tag")?;
|
||||
|
||||
let sketch = inner_circle_three_point(sketch_surface_or_group, p1, p2, p3, tag, exec_state, args).await?;
|
||||
let sketch = inner_circle_three_point(
|
||||
sketch_surface_or_group,
|
||||
untype_point(p1).0,
|
||||
untype_point(p2).0,
|
||||
untype_point(p3).0,
|
||||
tag,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(sketch),
|
||||
})
|
||||
@ -165,7 +184,7 @@ async fn inner_circle_three_point(
|
||||
) -> Result<Sketch, KclError> {
|
||||
let center = calculate_circle_center(p1, p2, p3);
|
||||
// It can be the distance to any of the 3 points - they all lay on the circumference.
|
||||
let radius = distance(center.into(), p2.into());
|
||||
let radius = distance(center, p2);
|
||||
|
||||
let sketch_surface = match sketch_surface_or_group {
|
||||
SketchOrSurface::SketchSurface(surface) => surface,
|
||||
@ -231,7 +250,7 @@ async fn inner_circle_three_point(
|
||||
}
|
||||
|
||||
/// Type of the polygon
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Default)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema, Default)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum PolygonType {
|
||||
@ -241,16 +260,16 @@ pub enum PolygonType {
|
||||
}
|
||||
|
||||
/// Data for drawing a polygon
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PolygonData {
|
||||
/// The radius of the polygon
|
||||
pub radius: f64,
|
||||
pub radius: TyF64,
|
||||
/// The number of sides in the polygon
|
||||
pub num_sides: u64,
|
||||
/// The center point of the polygon
|
||||
pub center: [f64; 2],
|
||||
pub center: [TyF64; 2],
|
||||
/// The type of the polygon (inscribed or circumscribed)
|
||||
#[serde(skip)]
|
||||
pub polygon_type: PolygonType,
|
||||
@ -317,7 +336,7 @@ async fn inner_polygon(
|
||||
}));
|
||||
}
|
||||
|
||||
if data.radius <= 0.0 {
|
||||
if data.radius.n <= 0.0 {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
message: "Radius must be greater than 0".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
@ -332,8 +351,8 @@ async fn inner_polygon(
|
||||
let half_angle = std::f64::consts::PI / data.num_sides as f64;
|
||||
|
||||
let radius_to_vertices = match data.polygon_type {
|
||||
PolygonType::Inscribed => data.radius,
|
||||
PolygonType::Circumscribed => data.radius / half_angle.cos(),
|
||||
PolygonType::Inscribed => data.radius.n,
|
||||
PolygonType::Circumscribed => data.radius.n / half_angle.cos(),
|
||||
};
|
||||
|
||||
let angle_step = 2.0 * std::f64::consts::PI / data.num_sides as f64;
|
||||
@ -342,8 +361,8 @@ async fn inner_polygon(
|
||||
.map(|i| {
|
||||
let angle = angle_step * i as f64;
|
||||
[
|
||||
data.center[0] + radius_to_vertices * angle.cos(),
|
||||
data.center[1] + radius_to_vertices * angle.sin(),
|
||||
data.center[0].n + radius_to_vertices * angle.cos(),
|
||||
data.center[1].n + radius_to_vertices * angle.sin(),
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
|
@ -11,13 +11,15 @@ use crate::{
|
||||
std::{sketch::FaceTag, Args},
|
||||
};
|
||||
|
||||
use super::args::TyF64;
|
||||
|
||||
/// Create a shell.
|
||||
pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solids = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?;
|
||||
let thickness = args.get_kw_arg("thickness")?;
|
||||
let thickness: TyF64 = args.get_kw_arg_typed("thickness", &RuntimeType::count(), exec_state)?;
|
||||
let faces = args.get_kw_arg("faces")?;
|
||||
|
||||
let result = inner_shell(solids, thickness, faces, exec_state, args).await?;
|
||||
let result = inner_shell(solids, thickness.n, faces, exec_state, args).await?;
|
||||
Ok(result.into())
|
||||
}
|
||||
|
||||
@ -27,7 +29,7 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Remove the end face for the extrusion.
|
||||
/// firstSketch = startSketchOn('XY')
|
||||
/// firstSketch = startSketchOn(XY)
|
||||
/// |> startProfileAt([-12, 12], %)
|
||||
/// |> line(end = [24, 0])
|
||||
/// |> line(end = [0, -24])
|
||||
@ -45,7 +47,7 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Remove the start face for the extrusion.
|
||||
/// firstSketch = startSketchOn('-XZ')
|
||||
/// firstSketch = startSketchOn(-XZ)
|
||||
/// |> startProfileAt([-12, 12], %)
|
||||
/// |> line(end = [24, 0])
|
||||
/// |> line(end = [0, -24])
|
||||
@ -63,7 +65,7 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Remove a tagged face and the end face for the extrusion.
|
||||
/// firstSketch = startSketchOn('XY')
|
||||
/// firstSketch = startSketchOn(XY)
|
||||
/// |> startProfileAt([-12, 12], %)
|
||||
/// |> line(end = [24, 0])
|
||||
/// |> line(end = [0, -24])
|
||||
@ -81,7 +83,7 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Remove multiple faces at once.
|
||||
/// firstSketch = startSketchOn('XY')
|
||||
/// firstSketch = startSketchOn(XY)
|
||||
/// |> startProfileAt([-12, 12], %)
|
||||
/// |> line(end = [24, 0])
|
||||
/// |> line(end = [0, -24])
|
||||
@ -100,7 +102,7 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// ```no_run
|
||||
/// // Shell a sketch on face.
|
||||
/// size = 100
|
||||
/// case = startSketchOn('-XZ')
|
||||
/// case = startSketchOn(-XZ)
|
||||
/// |> startProfileAt([-size, -size], %)
|
||||
/// |> line(end = [2 * size, 0])
|
||||
/// |> line(end = [0, 2 * size])
|
||||
@ -108,11 +110,11 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// |> close()
|
||||
/// |> extrude(length = 65)
|
||||
///
|
||||
/// thing1 = startSketchOn(case, END)
|
||||
/// thing1 = startSketchOn(case, face = END)
|
||||
/// |> circle( center = [-size / 2, -size / 2], radius = 25 )
|
||||
/// |> extrude(length = 50)
|
||||
///
|
||||
/// thing2 = startSketchOn(case, END)
|
||||
/// thing2 = startSketchOn(case, face = END)
|
||||
/// |> circle( center = [size / 2, -size / 2], radius = 25 )
|
||||
/// |> extrude(length = 50)
|
||||
///
|
||||
@ -123,7 +125,7 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// ```no_run
|
||||
/// // Shell a sketch on face object on the end face.
|
||||
/// size = 100
|
||||
/// case = startSketchOn('XY')
|
||||
/// case = startSketchOn(XY)
|
||||
/// |> startProfileAt([-size, -size], %)
|
||||
/// |> line(end = [2 * size, 0])
|
||||
/// |> line(end = [0, 2 * size])
|
||||
@ -131,11 +133,11 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// |> close()
|
||||
/// |> extrude(length = 65)
|
||||
///
|
||||
/// thing1 = startSketchOn(case, END)
|
||||
/// thing1 = startSketchOn(case, face = END)
|
||||
/// |> circle( center = [-size / 2, -size / 2], radius = 25 )
|
||||
/// |> extrude(length = 50)
|
||||
///
|
||||
/// thing2 = startSketchOn(case, END)
|
||||
/// thing2 = startSketchOn(case, face = END)
|
||||
/// |> circle( center = [size / 2, -size / 2], radius = 25 )
|
||||
/// |> extrude(length = 50)
|
||||
///
|
||||
@ -148,7 +150,7 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// // the entire object.
|
||||
///
|
||||
/// size = 100
|
||||
/// case = startSketchOn('XY')
|
||||
/// case = startSketchOn(XY)
|
||||
/// |> startProfileAt([-size, -size], %)
|
||||
/// |> line(end = [2 * size, 0])
|
||||
/// |> line(end = [0, 2 * size])
|
||||
@ -156,11 +158,11 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// |> close()
|
||||
/// |> extrude(length = 65)
|
||||
///
|
||||
/// thing1 = startSketchOn(case, END)
|
||||
/// thing1 = startSketchOn(case, face = END)
|
||||
/// |> circle( center = [-size / 2, -size / 2], radius = 25 )
|
||||
/// |> extrude(length = 50)
|
||||
///
|
||||
/// thing2 = startSketchOn(case, END)
|
||||
/// thing2 = startSketchOn(case, face = END)
|
||||
/// |> circle( center = [size / 2, -size / 2], radius = 25)
|
||||
/// |> extrude(length = 50)
|
||||
///
|
||||
@ -245,9 +247,9 @@ async fn inner_shell(
|
||||
|
||||
/// Make the inside of a 3D object hollow.
|
||||
pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (thickness, solid): (f64, Box<Solid>) = args.get_data_and_solid(exec_state)?;
|
||||
let (thickness, solid) = args.get_data_and_solid(exec_state)?;
|
||||
|
||||
let value = inner_hollow(thickness, solid, exec_state, args).await?;
|
||||
let value = inner_hollow(thickness.n, solid, exec_state, args).await?;
|
||||
Ok(KclValue::Solid { value })
|
||||
}
|
||||
|
||||
@ -258,7 +260,7 @@ pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Hollow a basic sketch.
|
||||
/// firstSketch = startSketchOn('XY')
|
||||
/// firstSketch = startSketchOn(XY)
|
||||
/// |> startProfileAt([-12, 12], %)
|
||||
/// |> line(end = [24, 0])
|
||||
/// |> line(end = [0, -24])
|
||||
@ -270,7 +272,7 @@ pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Hollow a basic sketch.
|
||||
/// firstSketch = startSketchOn('-XZ')
|
||||
/// firstSketch = startSketchOn(-XZ)
|
||||
/// |> startProfileAt([-12, 12], %)
|
||||
/// |> line(end = [24, 0])
|
||||
/// |> line(end = [0, -24])
|
||||
@ -283,7 +285,7 @@ pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
/// ```no_run
|
||||
/// // Hollow a sketch on face object.
|
||||
/// size = 100
|
||||
/// case = startSketchOn('-XZ')
|
||||
/// case = startSketchOn(-XZ)
|
||||
/// |> startProfileAt([-size, -size], %)
|
||||
/// |> line(end = [2 * size, 0])
|
||||
/// |> line(end = [0, 2 * size])
|
||||
@ -291,11 +293,11 @@ pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
/// |> close()
|
||||
/// |> extrude(length = 65)
|
||||
///
|
||||
/// thing1 = startSketchOn(case, END)
|
||||
/// thing1 = startSketchOn(case, face = END)
|
||||
/// |> circle( center = [-size / 2, -size / 2], radius = 25 )
|
||||
/// |> extrude(length = 50)
|
||||
///
|
||||
/// thing2 = startSketchOn(case, END)
|
||||
/// thing2 = startSketchOn(case, face = END)
|
||||
/// |> circle( center = [size / 2, -size / 2], radius = 25 )
|
||||
/// |> extrude(length = 50)
|
||||
///
|
||||
|
@ -12,10 +12,11 @@ use parse_display::{Display, FromStr};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::utils::untype_point;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
types::{PrimitiveType, RuntimeType},
|
||||
types::{PrimitiveType, RuntimeType, UnitLen},
|
||||
Artifact, ArtifactId, BasePath, CodeRef, ExecState, Face, GeoMeta, KclValue, Path, Plane, Point2d, Point3d,
|
||||
Sketch, SketchSurface, Solid, StartSketchOnFace, StartSketchOnPlane, TagEngineInfo, TagIdentifier,
|
||||
},
|
||||
@ -99,15 +100,22 @@ pub async fn involute_circular(exec_state: &mut ExecState, args: Args) -> Result
|
||||
let sketch =
|
||||
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
|
||||
/*
|
||||
*/
|
||||
let start_radius = args.get_kw_arg("startRadius")?;
|
||||
let end_radius = args.get_kw_arg("endRadius")?;
|
||||
let angle = args.get_kw_arg("angle")?;
|
||||
let start_radius: TyF64 = args.get_kw_arg_typed("startRadius", &RuntimeType::length(), exec_state)?;
|
||||
let end_radius: TyF64 = args.get_kw_arg_typed("endRadius", &RuntimeType::length(), exec_state)?;
|
||||
let angle: TyF64 = args.get_kw_arg_typed("angle", &RuntimeType::angle(), exec_state)?;
|
||||
let reverse = args.get_kw_arg_opt("reverse")?;
|
||||
let tag = args.get_kw_arg_opt("tag")?;
|
||||
let new_sketch =
|
||||
inner_involute_circular(sketch, start_radius, end_radius, angle, reverse, tag, exec_state, args).await?;
|
||||
let new_sketch = inner_involute_circular(
|
||||
sketch,
|
||||
start_radius.n,
|
||||
end_radius.n,
|
||||
angle.n,
|
||||
reverse,
|
||||
tag,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
@ -129,7 +137,6 @@ fn involute_curve(radius: f64, angle: f64) -> (f64, f64) {
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> involuteCircular(startRadius = a, endRadius = b, angle = 60)
|
||||
/// |> involuteCircular(startRadius = a, endRadius = b, angle = 60, reverse = true)
|
||||
///
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "involuteCircular",
|
||||
@ -234,13 +241,20 @@ async fn inner_involute_circular(
|
||||
|
||||
/// Draw a line to a point.
|
||||
pub async fn line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch =
|
||||
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
let end = args.get_kw_arg_opt("end")?;
|
||||
let end_absolute = args.get_kw_arg_opt("endAbsolute")?;
|
||||
let sketch = args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::sketch(), exec_state)?;
|
||||
let end = args.get_kw_arg_opt_typed("end", &RuntimeType::point2d(), exec_state)?;
|
||||
let end_absolute = args.get_kw_arg_opt_typed("endAbsolute", &RuntimeType::point2d(), exec_state)?;
|
||||
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
||||
|
||||
let new_sketch = inner_line(sketch, end_absolute, end, tag, exec_state, args).await?;
|
||||
let new_sketch = inner_line(
|
||||
sketch,
|
||||
end_absolute.map(|p| untype_point(p).0),
|
||||
end.map(|p| untype_point(p).0),
|
||||
tag,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
@ -404,11 +418,19 @@ async fn straight_line(
|
||||
pub async fn x_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch =
|
||||
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
let length = args.get_kw_arg_opt("length")?;
|
||||
let end_absolute = args.get_kw_arg_opt("endAbsolute")?;
|
||||
let length: Option<TyF64> = args.get_kw_arg_opt_typed("length", &RuntimeType::length(), exec_state)?;
|
||||
let end_absolute: Option<TyF64> = args.get_kw_arg_opt_typed("endAbsolute", &RuntimeType::length(), exec_state)?;
|
||||
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
||||
|
||||
let new_sketch = inner_x_line(sketch, length, end_absolute, tag, exec_state, args).await?;
|
||||
let new_sketch = inner_x_line(
|
||||
sketch,
|
||||
length.map(|t| t.n),
|
||||
end_absolute.map(|t| t.n),
|
||||
tag,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
@ -473,11 +495,19 @@ async fn inner_x_line(
|
||||
pub async fn y_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch =
|
||||
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
let length = args.get_kw_arg_opt("length")?;
|
||||
let end_absolute = args.get_kw_arg_opt("endAbsolute")?;
|
||||
let length: Option<TyF64> = args.get_kw_arg_opt_typed("length", &RuntimeType::length(), exec_state)?;
|
||||
let end_absolute: Option<TyF64> = args.get_kw_arg_opt_typed("endAbsolute", &RuntimeType::length(), exec_state)?;
|
||||
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
||||
|
||||
let new_sketch = inner_y_line(sketch, length, end_absolute, tag, exec_state, args).await?;
|
||||
let new_sketch = inner_y_line(
|
||||
sketch,
|
||||
length.map(|t| t.n),
|
||||
end_absolute.map(|t| t.n),
|
||||
tag,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
@ -535,24 +565,25 @@ async fn inner_y_line(
|
||||
|
||||
/// Draw an angled line.
|
||||
pub async fn angled_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch =
|
||||
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
let angle = args.get_kw_arg("angle")?;
|
||||
let length = args.get_kw_arg_opt("length")?;
|
||||
let length_x = args.get_kw_arg_opt("lengthX")?;
|
||||
let length_y = args.get_kw_arg_opt("lengthY")?;
|
||||
let end_absolute_x = args.get_kw_arg_opt("endAbsoluteX")?;
|
||||
let end_absolute_y = args.get_kw_arg_opt("endAbsoluteY")?;
|
||||
let sketch = args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::sketch(), exec_state)?;
|
||||
let angle: TyF64 = args.get_kw_arg_typed("angle", &RuntimeType::degrees(), exec_state)?;
|
||||
let length: Option<TyF64> = args.get_kw_arg_opt_typed("length", &RuntimeType::length(), exec_state)?;
|
||||
let length_x: Option<TyF64> = args.get_kw_arg_opt_typed("lengthX", &RuntimeType::length(), exec_state)?;
|
||||
let length_y: Option<TyF64> = args.get_kw_arg_opt_typed("lengthY", &RuntimeType::length(), exec_state)?;
|
||||
let end_absolute_x: Option<TyF64> =
|
||||
args.get_kw_arg_opt_typed("endAbsoluteX", &RuntimeType::length(), exec_state)?;
|
||||
let end_absolute_y: Option<TyF64> =
|
||||
args.get_kw_arg_opt_typed("endAbsoluteY", &RuntimeType::length(), exec_state)?;
|
||||
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
||||
|
||||
let new_sketch = inner_angled_line(
|
||||
sketch,
|
||||
angle,
|
||||
length,
|
||||
length_x,
|
||||
length_y,
|
||||
end_absolute_x,
|
||||
end_absolute_y,
|
||||
angle.n,
|
||||
length.map(|t| t.n),
|
||||
length_x.map(|t| t.n),
|
||||
length_y.map(|t| t.n),
|
||||
end_absolute_x.map(|t| t.n),
|
||||
end_absolute_y.map(|t| t.n),
|
||||
tag,
|
||||
exec_state,
|
||||
args,
|
||||
@ -732,7 +763,7 @@ async fn inner_angled_line_of_x_length(
|
||||
|
||||
let to = get_y_component(Angle::from_degrees(angle_degrees), length);
|
||||
|
||||
let new_sketch = straight_line(StraightLineParams::relative(to.into(), sketch, tag), exec_state, args).await?;
|
||||
let new_sketch = straight_line(StraightLineParams::relative(to, sketch, tag), exec_state, args).await?;
|
||||
|
||||
Ok(new_sketch)
|
||||
}
|
||||
@ -798,7 +829,7 @@ async fn inner_angled_line_of_y_length(
|
||||
|
||||
let to = get_x_component(Angle::from_degrees(angle_degrees), length);
|
||||
|
||||
let new_sketch = straight_line(StraightLineParams::relative(to.into(), sketch, tag), exec_state, args).await?;
|
||||
let new_sketch = straight_line(StraightLineParams::relative(to, sketch, tag), exec_state, args).await?;
|
||||
|
||||
Ok(new_sketch)
|
||||
}
|
||||
@ -841,17 +872,17 @@ async fn inner_angled_line_to_y(
|
||||
}
|
||||
|
||||
/// Data for drawing an angled line that intersects with a given line.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
// TODO: make sure the docs on the args below are correct.
|
||||
pub struct AngledLineThatIntersectsData {
|
||||
/// The angle of the line.
|
||||
pub angle: f64,
|
||||
pub angle: TyF64,
|
||||
/// The tag of the line to intersect with.
|
||||
pub intersect_tag: TagIdentifier,
|
||||
/// The offset from the intersecting line.
|
||||
pub offset: Option<f64>,
|
||||
pub offset: Option<TyF64>,
|
||||
}
|
||||
|
||||
/// Draw an angled line that intersects with a given line.
|
||||
@ -903,13 +934,13 @@ pub async fn inner_angled_line_that_intersects(
|
||||
|
||||
let from = sketch.current_pen_position()?;
|
||||
let to = intersection_with_parallel_line(
|
||||
&[path.get_from().into(), path.get_to().into()],
|
||||
data.offset.unwrap_or_default(),
|
||||
data.angle,
|
||||
from,
|
||||
&[untype_point(path.get_from()).0, untype_point(path.get_to()).0],
|
||||
data.offset.map(|t| t.n).unwrap_or_default(),
|
||||
data.angle.n,
|
||||
from.into(),
|
||||
);
|
||||
|
||||
let new_sketch = straight_line(StraightLineParams::absolute(to.into(), sketch, tag), exec_state, args).await?;
|
||||
let new_sketch = straight_line(StraightLineParams::absolute(to, sketch, tag), exec_state, args).await?;
|
||||
Ok(new_sketch)
|
||||
}
|
||||
|
||||
@ -967,9 +998,14 @@ pub enum PlaneData {
|
||||
|
||||
/// Start a sketch on a specific plane or face.
|
||||
pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, tag) = args.get_sketch_data_and_optional_tag()?;
|
||||
let data = args.get_unlabeled_kw_arg_typed(
|
||||
"planeOrSolid",
|
||||
&RuntimeType::Union(vec![RuntimeType::solid(), RuntimeType::plane()]),
|
||||
exec_state,
|
||||
)?;
|
||||
let face = args.get_kw_arg_opt("face")?;
|
||||
|
||||
match inner_start_sketch_on(data, tag, exec_state, &args).await? {
|
||||
match inner_start_sketch_on(data, face, exec_state, &args).await? {
|
||||
SketchSurface::Plane(value) => Ok(KclValue::Plane { value }),
|
||||
SketchSurface::Face(value) => Ok(KclValue::Face { value }),
|
||||
}
|
||||
@ -1005,7 +1041,7 @@ pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 5)
|
||||
///
|
||||
/// exampleSketch002 = startSketchOn(example, END)
|
||||
/// exampleSketch002 = startSketchOn(example, face = END)
|
||||
/// |> startProfileAt([1, 1], %)
|
||||
/// |> line(end = [8, 0])
|
||||
/// |> line(end = [0, 8])
|
||||
@ -1014,7 +1050,7 @@ pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
///
|
||||
/// example002 = extrude(exampleSketch002, length = 5)
|
||||
///
|
||||
/// exampleSketch003 = startSketchOn(example002, END)
|
||||
/// exampleSketch003 = startSketchOn(example002, face = END)
|
||||
/// |> startProfileAt([2, 2], %)
|
||||
/// |> line(end = [6, 0])
|
||||
/// |> line(end = [0, 6])
|
||||
@ -1036,7 +1072,7 @@ pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 5, tagEnd = $end01)
|
||||
///
|
||||
/// exampleSketch002 = startSketchOn(example, end01)
|
||||
/// exampleSketch002 = startSketchOn(example, face = end01)
|
||||
/// |> startProfileAt([1, 1], %)
|
||||
/// |> line(end = [8, 0])
|
||||
/// |> line(end = [0, 8])
|
||||
@ -1045,7 +1081,7 @@ pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
///
|
||||
/// example002 = extrude(exampleSketch002, length = 5, tagEnd = $end02)
|
||||
///
|
||||
/// exampleSketch003 = startSketchOn(example002, end02)
|
||||
/// exampleSketch003 = startSketchOn(example002, face = end02)
|
||||
/// |> startProfileAt([2, 2], %)
|
||||
/// |> line(end = [6, 0])
|
||||
/// |> line(end = [0, 6])
|
||||
@ -1065,7 +1101,7 @@ pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 10)
|
||||
///
|
||||
/// exampleSketch002 = startSketchOn(example, sketchingFace)
|
||||
/// exampleSketch002 = startSketchOn(example, face = sketchingFace)
|
||||
/// |> startProfileAt([1, 1], %)
|
||||
/// |> line(end = [8, 0])
|
||||
/// |> line(end = [0, 8])
|
||||
@ -1074,7 +1110,7 @@ pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
///
|
||||
/// example002 = extrude(exampleSketch002, length = 10)
|
||||
///
|
||||
/// exampleSketch003 = startSketchOn(example002, sketchingFace002)
|
||||
/// exampleSketch003 = startSketchOn(example002, face = sketchingFace002)
|
||||
/// |> startProfileAt([-8, 12], %)
|
||||
/// |> line(end = [0, 6])
|
||||
/// |> line(end = [6, 0])
|
||||
@ -1098,7 +1134,7 @@ pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
///
|
||||
/// example = revolve(exampleSketch, axis = Y, angle = 180)
|
||||
///
|
||||
/// exampleSketch002 = startSketchOn(example, END)
|
||||
/// exampleSketch002 = startSketchOn(example, face = END)
|
||||
/// |> startProfileAt([4.5, -5], %)
|
||||
/// |> line(end = [0, 5])
|
||||
/// |> line(end = [5, 0])
|
||||
@ -1124,7 +1160,7 @@ pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
///
|
||||
/// example = revolve(exampleSketch, axis = Y, angle = 180, tagEnd = $end01)
|
||||
///
|
||||
/// exampleSketch002 = startSketchOn(example, end01)
|
||||
/// exampleSketch002 = startSketchOn(example, face = end01)
|
||||
/// |> startProfileAt([4.5, -5], %)
|
||||
/// |> line(end = [0, 5])
|
||||
/// |> line(end = [5, 0])
|
||||
@ -1136,13 +1172,11 @@ pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
///
|
||||
/// ```no_run
|
||||
/// a1 = startSketchOn({
|
||||
/// plane: {
|
||||
/// origin = { x = 0, y = 0, z = 0 },
|
||||
/// xAxis = { x = 1, y = 0, z = 0 },
|
||||
/// yAxis = { x = 0, y = 1, z = 0 },
|
||||
/// zAxis = { x = 0, y = 0, z = 1 }
|
||||
/// }
|
||||
/// })
|
||||
/// origin = { x = 0, y = 0, z = 0 },
|
||||
/// xAxis = { x = 1, y = 0, z = 0 },
|
||||
/// yAxis = { x = 0, y = 1, z = 0 },
|
||||
/// zAxis = { x = 0, y = 0, z = 1 }
|
||||
/// })
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = [100.0, 0])
|
||||
/// |> yLine(length = -100.0)
|
||||
@ -1154,14 +1188,20 @@ pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
#[stdlib {
|
||||
name = "startSketchOn",
|
||||
feature_tree_operation = true,
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
plane_or_solid = { docs = "The plane or solid to sketch on"},
|
||||
face = { docs = "Identify a face of a solid if a solid is specified as the input argument (`plane_or_solid`)"},
|
||||
}
|
||||
}]
|
||||
async fn inner_start_sketch_on(
|
||||
data: SketchData,
|
||||
tag: Option<FaceTag>,
|
||||
plane_or_solid: SketchData,
|
||||
face: Option<FaceTag>,
|
||||
exec_state: &mut ExecState,
|
||||
args: &Args,
|
||||
) -> Result<SketchSurface, KclError> {
|
||||
match data {
|
||||
match plane_or_solid {
|
||||
SketchData::PlaneOrientation(plane_data) => {
|
||||
let plane = make_sketch_plane_from_orientation(plane_data, exec_state, args).await?;
|
||||
Ok(SketchSurface::Plane(plane))
|
||||
@ -1183,7 +1223,7 @@ async fn inner_start_sketch_on(
|
||||
}
|
||||
}
|
||||
SketchData::Solid(solid) => {
|
||||
let Some(tag) = tag else {
|
||||
let Some(tag) = face else {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
message: "Expected a tag for the face to sketch on".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
@ -1242,9 +1282,9 @@ async fn make_sketch_plane_from_orientation(
|
||||
// TODO: ignoring the default planes here since we already created them, breaks the
|
||||
// front end for the feature tree which is stupid and we should fix it.
|
||||
let x_axis = match data {
|
||||
PlaneData::NegXY => Point3d::new(-1.0, 0.0, 0.0),
|
||||
PlaneData::NegXZ => Point3d::new(-1.0, 0.0, 0.0),
|
||||
PlaneData::NegYZ => Point3d::new(0.0, -1.0, 0.0),
|
||||
PlaneData::NegXY => Point3d::new(-1.0, 0.0, 0.0, UnitLen::Mm),
|
||||
PlaneData::NegXZ => Point3d::new(-1.0, 0.0, 0.0, UnitLen::Mm),
|
||||
PlaneData::NegYZ => Point3d::new(0.0, -1.0, 0.0, UnitLen::Mm),
|
||||
_ => plane.x_axis,
|
||||
};
|
||||
args.batch_modeling_cmd(
|
||||
@ -1286,10 +1326,9 @@ async fn make_sketch_plane_from_orientation(
|
||||
|
||||
/// Start a new profile at a given point.
|
||||
pub async fn start_profile_at(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (start, sketch_surface, tag): ([f64; 2], SketchSurface, Option<TagNode>) =
|
||||
args.get_data_and_sketch_surface()?;
|
||||
let (start, sketch_surface, tag) = args.get_data_and_sketch_surface()?;
|
||||
|
||||
let sketch = inner_start_profile_at(start, sketch_surface, tag, exec_state, args).await?;
|
||||
let sketch = inner_start_profile_at([start[0].n, start[1].n], sketch_surface, tag, exec_state, args).await?;
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(sketch),
|
||||
})
|
||||
@ -1566,7 +1605,7 @@ pub(crate) async fn inner_close(
|
||||
args: Args,
|
||||
) -> Result<Sketch, KclError> {
|
||||
let from = sketch.current_pen_position()?;
|
||||
let to: Point2d = sketch.start.from.into();
|
||||
let to: Point2d = sketch.start.get_from().into();
|
||||
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
@ -1597,7 +1636,7 @@ pub(crate) async fn inner_close(
|
||||
}
|
||||
|
||||
/// Data to draw an arc.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase", untagged)]
|
||||
pub enum ArcData {
|
||||
@ -1606,34 +1645,34 @@ pub enum ArcData {
|
||||
/// The start angle.
|
||||
#[serde(rename = "angleStart")]
|
||||
#[schemars(range(min = -360.0, max = 360.0))]
|
||||
angle_start: f64,
|
||||
angle_start: TyF64,
|
||||
/// The end angle.
|
||||
#[serde(rename = "angleEnd")]
|
||||
#[schemars(range(min = -360.0, max = 360.0))]
|
||||
angle_end: f64,
|
||||
angle_end: TyF64,
|
||||
/// The radius.
|
||||
radius: f64,
|
||||
radius: TyF64,
|
||||
},
|
||||
/// Center, to and radius with an optional tag.
|
||||
CenterToRadius {
|
||||
/// The center.
|
||||
center: [f64; 2],
|
||||
center: [TyF64; 2],
|
||||
/// The to point.
|
||||
to: [f64; 2],
|
||||
to: [TyF64; 2],
|
||||
/// The radius.
|
||||
radius: f64,
|
||||
radius: TyF64,
|
||||
},
|
||||
}
|
||||
|
||||
/// Data to draw a three point arc (arcTo).
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ArcToData {
|
||||
/// End point of the arc. A point in 3D space
|
||||
pub end: [f64; 2],
|
||||
pub end: [TyF64; 2],
|
||||
/// Interior point of the arc. A point in 3D space
|
||||
pub interior: [f64; 2],
|
||||
pub interior: [TyF64; 2],
|
||||
}
|
||||
|
||||
/// Draw an arc.
|
||||
@ -1687,14 +1726,26 @@ pub(crate) async fn inner_arc(
|
||||
angle_end,
|
||||
radius,
|
||||
} => {
|
||||
let a_start = Angle::from_degrees(*angle_start);
|
||||
let a_end = Angle::from_degrees(*angle_end);
|
||||
let (center, end) = arc_center_and_end(from, a_start, a_end, *radius);
|
||||
(center, a_start, a_end, *radius, end)
|
||||
let a_start = Angle::from_degrees(angle_start.n);
|
||||
let a_end = Angle::from_degrees(angle_end.n);
|
||||
let (center, end) = arc_center_and_end(from.into(), a_start, a_end, radius.n);
|
||||
(center, a_start, a_end, radius.n, end)
|
||||
}
|
||||
ArcData::CenterToRadius { center, to, radius } => {
|
||||
let (angle_start, angle_end) = arc_angles(from, to.into(), center.into(), *radius, args.source_range)?;
|
||||
(center.into(), angle_start, angle_end, *radius, to.into())
|
||||
let (angle_start, angle_end) = arc_angles(
|
||||
from.into(),
|
||||
untype_point(to.clone()).0,
|
||||
untype_point(center.clone()).0,
|
||||
radius.n,
|
||||
args.source_range,
|
||||
)?;
|
||||
(
|
||||
untype_point(center.clone()).0,
|
||||
angle_start,
|
||||
angle_end,
|
||||
radius.n,
|
||||
untype_point(to.clone()).0,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
@ -1726,7 +1777,7 @@ pub(crate) async fn inner_arc(
|
||||
let current_path = Path::Arc {
|
||||
base: BasePath {
|
||||
from: from.into(),
|
||||
to: end.into(),
|
||||
to: end,
|
||||
tag: tag.clone(),
|
||||
units: sketch.units,
|
||||
geo_meta: GeoMeta {
|
||||
@ -1734,7 +1785,7 @@ pub(crate) async fn inner_arc(
|
||||
metadata: args.source_range.into(),
|
||||
},
|
||||
},
|
||||
center: center.into(),
|
||||
center,
|
||||
radius,
|
||||
ccw,
|
||||
};
|
||||
@ -1795,13 +1846,13 @@ pub(crate) async fn inner_arc_to(
|
||||
path: sketch.id.into(),
|
||||
segment: PathSegment::ArcTo {
|
||||
end: kcmc::shared::Point3d {
|
||||
x: LengthUnit(data.end[0]),
|
||||
y: LengthUnit(data.end[1]),
|
||||
x: LengthUnit(data.end[0].n),
|
||||
y: LengthUnit(data.end[1].n),
|
||||
z: LengthUnit(0.0),
|
||||
},
|
||||
interior: kcmc::shared::Point3d {
|
||||
x: LengthUnit(data.interior[0]),
|
||||
y: LengthUnit(data.interior[1]),
|
||||
x: LengthUnit(data.interior[0].n),
|
||||
y: LengthUnit(data.interior[1].n),
|
||||
z: LengthUnit(0.0),
|
||||
},
|
||||
relative: false,
|
||||
@ -1812,12 +1863,12 @@ pub(crate) async fn inner_arc_to(
|
||||
|
||||
let start = [from.x, from.y];
|
||||
let interior = data.interior;
|
||||
let end = data.end;
|
||||
let end = data.end.clone();
|
||||
|
||||
let current_path = Path::ArcThreePoint {
|
||||
base: BasePath {
|
||||
from: from.into(),
|
||||
to: data.end,
|
||||
to: untype_point(data.end).0,
|
||||
tag: tag.clone(),
|
||||
units: sketch.units,
|
||||
geo_meta: GeoMeta {
|
||||
@ -1826,8 +1877,8 @@ pub(crate) async fn inner_arc_to(
|
||||
},
|
||||
},
|
||||
p1: start,
|
||||
p2: interior,
|
||||
p3: end,
|
||||
p2: untype_point(interior).0,
|
||||
p3: untype_point(end).0,
|
||||
};
|
||||
|
||||
let mut new_sketch = sketch.clone();
|
||||
@ -1844,13 +1895,23 @@ pub(crate) async fn inner_arc_to(
|
||||
pub async fn tangential_arc(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch =
|
||||
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
let end = args.get_kw_arg_opt("end")?;
|
||||
let end_absolute = args.get_kw_arg_opt("endAbsolute")?;
|
||||
let radius = args.get_kw_arg_opt("radius")?;
|
||||
let angle = args.get_kw_arg_opt("angle")?;
|
||||
let end = args.get_kw_arg_opt_typed("end", &RuntimeType::point2d(), exec_state)?;
|
||||
let end_absolute = args.get_kw_arg_opt_typed("endAbsolute", &RuntimeType::point2d(), exec_state)?;
|
||||
let radius = args.get_kw_arg_opt_typed("radius", &RuntimeType::length(), exec_state)?;
|
||||
let angle = args.get_kw_arg_opt_typed("angle", &RuntimeType::angle(), exec_state)?;
|
||||
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
||||
|
||||
let new_sketch = inner_tangential_arc(sketch, end_absolute, end, radius, angle, tag, exec_state, args).await?;
|
||||
let new_sketch = inner_tangential_arc(
|
||||
sketch,
|
||||
end_absolute.map(|p| untype_point(p).0),
|
||||
end.map(|p| untype_point(p).0),
|
||||
radius,
|
||||
angle,
|
||||
tag,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
@ -1928,8 +1989,8 @@ async fn inner_tangential_arc(
|
||||
sketch: Sketch,
|
||||
end_absolute: Option<[f64; 2]>,
|
||||
end: Option<[f64; 2]>,
|
||||
radius: Option<f64>,
|
||||
angle: Option<f64>,
|
||||
radius: Option<TyF64>,
|
||||
angle: Option<TyF64>,
|
||||
tag: Option<TagNode>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
@ -1962,16 +2023,16 @@ async fn inner_tangential_arc(
|
||||
}
|
||||
|
||||
/// Data to draw a tangential arc.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, JsonSchema, ts_rs::TS)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, JsonSchema, ts_rs::TS)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase", untagged)]
|
||||
pub enum TangentialArcData {
|
||||
RadiusAndOffset {
|
||||
/// Radius of the arc.
|
||||
/// Not to be confused with Raiders of the Lost Ark.
|
||||
radius: f64,
|
||||
radius: TyF64,
|
||||
/// Offset of the arc, in degrees.
|
||||
offset: f64,
|
||||
offset: TyF64,
|
||||
},
|
||||
}
|
||||
|
||||
@ -1998,7 +2059,7 @@ async fn inner_tangential_arc_radius_angle(
|
||||
let (center, to, ccw) = match data {
|
||||
TangentialArcData::RadiusAndOffset { radius, offset } => {
|
||||
// KCL stdlib types use degrees.
|
||||
let offset = Angle::from_degrees(offset);
|
||||
let offset = Angle::from_degrees(offset.n);
|
||||
|
||||
// Calculate the end point from the angle and radius.
|
||||
// atan2 outputs radians.
|
||||
@ -2020,26 +2081,26 @@ async fn inner_tangential_arc_radius_angle(
|
||||
// but the above logic *should* capture that behavior
|
||||
let start_angle = previous_end_tangent + tangent_to_arc_start_angle;
|
||||
let end_angle = start_angle + offset;
|
||||
let (center, to) = arc_center_and_end(from, start_angle, end_angle, radius);
|
||||
let (center, to) = arc_center_and_end(from.into(), start_angle, end_angle, radius.n);
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
id,
|
||||
ModelingCmd::from(mcmd::ExtendPath {
|
||||
path: sketch.id.into(),
|
||||
segment: PathSegment::TangentialArc {
|
||||
radius: LengthUnit(radius),
|
||||
radius: LengthUnit(radius.n),
|
||||
offset,
|
||||
},
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
(center, to.into(), ccw)
|
||||
(center, to, ccw)
|
||||
}
|
||||
};
|
||||
|
||||
let current_path = Path::TangentialArc {
|
||||
ccw,
|
||||
center: center.into(),
|
||||
center,
|
||||
base: BasePath {
|
||||
from: from.into(),
|
||||
to,
|
||||
@ -2147,16 +2208,16 @@ async fn inner_tangential_arc_to_point(
|
||||
}
|
||||
|
||||
/// Data to draw a bezier curve.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BezierData {
|
||||
/// The to point.
|
||||
pub to: [f64; 2],
|
||||
pub to: [TyF64; 2],
|
||||
/// The first control point.
|
||||
pub control1: [f64; 2],
|
||||
pub control1: [TyF64; 2],
|
||||
/// The second control point.
|
||||
pub control2: [f64; 2],
|
||||
pub control2: [TyF64; 2],
|
||||
}
|
||||
|
||||
/// Draw a bezier curve.
|
||||
@ -2200,8 +2261,8 @@ async fn inner_bezier_curve(
|
||||
let from = sketch.current_pen_position()?;
|
||||
|
||||
let relative = true;
|
||||
let delta = data.to;
|
||||
let to = [from.x + data.to[0], from.y + data.to[1]];
|
||||
let delta = data.to.clone();
|
||||
let to = [from.x + data.to[0].n, from.y + data.to[1].n];
|
||||
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
@ -2210,9 +2271,13 @@ async fn inner_bezier_curve(
|
||||
ModelingCmd::from(mcmd::ExtendPath {
|
||||
path: sketch.id.into(),
|
||||
segment: PathSegment::Bezier {
|
||||
control1: KPoint2d::from(data.control1).with_z(0.0).map(LengthUnit),
|
||||
control2: KPoint2d::from(data.control2).with_z(0.0).map(LengthUnit),
|
||||
end: KPoint2d::from(delta).with_z(0.0).map(LengthUnit),
|
||||
control1: KPoint2d::from(untype_point(data.control1).0)
|
||||
.with_z(0.0)
|
||||
.map(LengthUnit),
|
||||
control2: KPoint2d::from(untype_point(data.control2).0)
|
||||
.with_z(0.0)
|
||||
.map(LengthUnit),
|
||||
end: KPoint2d::from(untype_point(delta).0).with_z(0.0).map(LengthUnit),
|
||||
relative,
|
||||
},
|
||||
}),
|
||||
|
@ -7,7 +7,7 @@ use kittycad_modeling_cmds::{self as kcmc};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Serialize;
|
||||
|
||||
use super::DEFAULT_TOLERANCE;
|
||||
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
||||
use crate::{
|
||||
errors::KclError,
|
||||
execution::{types::RuntimeType, ExecState, Helix, KclValue, Sketch, Solid},
|
||||
@ -29,12 +29,19 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
|
||||
let path: SweepPath = args.get_kw_arg("path")?;
|
||||
let sectional = args.get_kw_arg_opt("sectional")?;
|
||||
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
||||
let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::count(), exec_state)?;
|
||||
let tag_start = args.get_kw_arg_opt("tagStart")?;
|
||||
let tag_end = args.get_kw_arg_opt("tagEnd")?;
|
||||
|
||||
let value = inner_sweep(
|
||||
sketches, path, sectional, tolerance, tag_start, tag_end, exec_state, args,
|
||||
sketches,
|
||||
path,
|
||||
sectional,
|
||||
tolerance.map(|t| t.n),
|
||||
tag_start,
|
||||
tag_end,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(value.into())
|
||||
|
@ -17,6 +17,8 @@ use crate::{
|
||||
std::Args,
|
||||
};
|
||||
|
||||
use super::args::TyF64;
|
||||
|
||||
/// Scale a solid or a sketch.
|
||||
pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let objects = args.get_unlabeled_kw_arg_typed(
|
||||
@ -28,9 +30,9 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
]),
|
||||
exec_state,
|
||||
)?;
|
||||
let scale_x = args.get_kw_arg_opt("x")?;
|
||||
let scale_y = args.get_kw_arg_opt("y")?;
|
||||
let scale_z = args.get_kw_arg_opt("z")?;
|
||||
let scale_x: Option<TyF64> = args.get_kw_arg_opt_typed("x", &RuntimeType::count(), exec_state)?;
|
||||
let scale_y: Option<TyF64> = args.get_kw_arg_opt_typed("y", &RuntimeType::count(), exec_state)?;
|
||||
let scale_z: Option<TyF64> = args.get_kw_arg_opt_typed("z", &RuntimeType::count(), exec_state)?;
|
||||
let global = args.get_kw_arg_opt("global")?;
|
||||
|
||||
// Ensure at least one scale value is provided.
|
||||
@ -41,7 +43,16 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
}));
|
||||
}
|
||||
|
||||
let objects = inner_scale(objects, scale_x, scale_y, scale_z, global, exec_state, args).await?;
|
||||
let objects = inner_scale(
|
||||
objects,
|
||||
scale_x.map(|t| t.n),
|
||||
scale_y.map(|t| t.n),
|
||||
scale_z.map(|t| t.n),
|
||||
global,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(objects.into())
|
||||
}
|
||||
|
||||
@ -200,9 +211,9 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
]),
|
||||
exec_state,
|
||||
)?;
|
||||
let translate_x = args.get_kw_arg_opt("x")?;
|
||||
let translate_y = args.get_kw_arg_opt("y")?;
|
||||
let translate_z = args.get_kw_arg_opt("z")?;
|
||||
let translate_x: Option<TyF64> = args.get_kw_arg_opt_typed("x", &RuntimeType::length(), exec_state)?;
|
||||
let translate_y: Option<TyF64> = args.get_kw_arg_opt_typed("y", &RuntimeType::length(), exec_state)?;
|
||||
let translate_z: Option<TyF64> = args.get_kw_arg_opt_typed("z", &RuntimeType::length(), exec_state)?;
|
||||
let global = args.get_kw_arg_opt("global")?;
|
||||
|
||||
// Ensure at least one translation value is provided.
|
||||
@ -213,7 +224,16 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
}));
|
||||
}
|
||||
|
||||
let objects = inner_translate(objects, translate_x, translate_y, translate_z, global, exec_state, args).await?;
|
||||
let objects = inner_translate(
|
||||
objects,
|
||||
translate_x.map(|t| t.n),
|
||||
translate_y.map(|t| t.n),
|
||||
translate_z.map(|t| t.n),
|
||||
global,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(objects.into())
|
||||
}
|
||||
|
||||
@ -429,11 +449,11 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
]),
|
||||
exec_state,
|
||||
)?;
|
||||
let roll = args.get_kw_arg_opt("roll")?;
|
||||
let pitch = args.get_kw_arg_opt("pitch")?;
|
||||
let yaw = args.get_kw_arg_opt("yaw")?;
|
||||
let axis = args.get_kw_arg_opt("axis")?;
|
||||
let angle = args.get_kw_arg_opt("angle")?;
|
||||
let roll: Option<TyF64> = args.get_kw_arg_opt_typed("roll", &RuntimeType::angle(), exec_state)?;
|
||||
let pitch: Option<TyF64> = args.get_kw_arg_opt_typed("pitch", &RuntimeType::angle(), exec_state)?;
|
||||
let yaw: Option<TyF64> = args.get_kw_arg_opt_typed("yaw", &RuntimeType::angle(), exec_state)?;
|
||||
let axis: Option<[TyF64; 3]> = args.get_kw_arg_opt_typed("axis", &RuntimeType::point3d(), exec_state)?;
|
||||
let angle: Option<TyF64> = args.get_kw_arg_opt_typed("angle", &RuntimeType::angle(), exec_state)?;
|
||||
let global = args.get_kw_arg_opt("global")?;
|
||||
|
||||
// Check if no rotation values are provided.
|
||||
@ -482,42 +502,53 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
}
|
||||
|
||||
// Validate the roll, pitch, and yaw values.
|
||||
if let Some(roll) = roll {
|
||||
if !(-360.0..=360.0).contains(&roll) {
|
||||
if let Some(roll) = &roll {
|
||||
if !(-360.0..=360.0).contains(&roll.n) {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Expected roll to be between -360 and 360, found `{}`", roll),
|
||||
message: format!("Expected roll to be between -360 and 360, found `{}`", roll.n),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
}
|
||||
}
|
||||
if let Some(pitch) = pitch {
|
||||
if !(-360.0..=360.0).contains(&pitch) {
|
||||
if let Some(pitch) = &pitch {
|
||||
if !(-360.0..=360.0).contains(&pitch.n) {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Expected pitch to be between -360 and 360, found `{}`", pitch),
|
||||
message: format!("Expected pitch to be between -360 and 360, found `{}`", pitch.n),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
}
|
||||
}
|
||||
if let Some(yaw) = yaw {
|
||||
if !(-360.0..=360.0).contains(&yaw) {
|
||||
if let Some(yaw) = &yaw {
|
||||
if !(-360.0..=360.0).contains(&yaw.n) {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Expected yaw to be between -360 and 360, found `{}`", yaw),
|
||||
message: format!("Expected yaw to be between -360 and 360, found `{}`", yaw.n),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the axis and angle values.
|
||||
if let Some(angle) = angle {
|
||||
if !(-360.0..=360.0).contains(&angle) {
|
||||
if let Some(angle) = &angle {
|
||||
if !(-360.0..=360.0).contains(&angle.n) {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Expected angle to be between -360 and 360, found `{}`", angle),
|
||||
message: format!("Expected angle to be between -360 and 360, found `{}`", angle.n),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
let objects = inner_rotate(objects, roll, pitch, yaw, axis, angle, global, exec_state, args).await?;
|
||||
let objects = inner_rotate(
|
||||
objects,
|
||||
roll.map(|t| t.n),
|
||||
pitch.map(|t| t.n),
|
||||
yaw.map(|t| t.n),
|
||||
axis.map(|p| [p[0].n, p[1].n, p[2].n]),
|
||||
angle.map(|t| t.n),
|
||||
global,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(objects.into())
|
||||
}
|
||||
|
||||
|
@ -11,8 +11,8 @@ use crate::{
|
||||
|
||||
/// Millimeters conversion factor for current files units.
|
||||
pub async fn from_mm(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let input = args.get_number()?;
|
||||
let result = inner_from_mm(input, exec_state)?;
|
||||
let input = args.get_number_with_type()?;
|
||||
let result = inner_from_mm(input.n, exec_state)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
@ -48,13 +48,14 @@ fn inner_from_mm(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
|
||||
UnitLen::M => measurements::Length::from_millimeters(input).as_meters(),
|
||||
UnitLen::Cm => measurements::Length::from_millimeters(input).as_centimeters(),
|
||||
UnitLen::Yards => measurements::Length::from_millimeters(input).as_yards(),
|
||||
UnitLen::Unknown => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Inches conversion factor for current files units.
|
||||
pub async fn from_inches(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let input = args.get_number()?;
|
||||
let result = inner_from_inches(input, exec_state)?;
|
||||
let input = args.get_number_with_type()?;
|
||||
let result = inner_from_inches(input.n, exec_state)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
@ -90,13 +91,14 @@ fn inner_from_inches(input: f64, exec_state: &ExecState) -> Result<f64, KclError
|
||||
UnitLen::M => Ok(measurements::Length::from_inches(input).as_meters()),
|
||||
UnitLen::Cm => Ok(measurements::Length::from_inches(input).as_centimeters()),
|
||||
UnitLen::Yards => Ok(measurements::Length::from_inches(input).as_yards()),
|
||||
UnitLen::Unknown => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Feet conversion factor for current files units.
|
||||
pub async fn from_ft(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let input = args.get_number()?;
|
||||
let result = inner_from_ft(input, exec_state)?;
|
||||
let input = args.get_number_with_type()?;
|
||||
let result = inner_from_ft(input.n, exec_state)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
@ -133,13 +135,14 @@ fn inner_from_ft(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
|
||||
UnitLen::M => Ok(measurements::Length::from_feet(input).as_meters()),
|
||||
UnitLen::Cm => Ok(measurements::Length::from_feet(input).as_centimeters()),
|
||||
UnitLen::Yards => Ok(measurements::Length::from_feet(input).as_yards()),
|
||||
UnitLen::Unknown => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Meters conversion factor for current files units.
|
||||
pub async fn from_m(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let input = args.get_number()?;
|
||||
let result = inner_from_m(input, exec_state)?;
|
||||
let input = args.get_number_with_type()?;
|
||||
let result = inner_from_m(input.n, exec_state)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
@ -176,13 +179,14 @@ fn inner_from_m(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
|
||||
UnitLen::M => Ok(input),
|
||||
UnitLen::Cm => Ok(measurements::Length::from_meters(input).as_centimeters()),
|
||||
UnitLen::Yards => Ok(measurements::Length::from_meters(input).as_yards()),
|
||||
UnitLen::Unknown => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Centimeters conversion factor for current files units.
|
||||
pub async fn from_cm(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let input = args.get_number()?;
|
||||
let result = inner_from_cm(input, exec_state)?;
|
||||
let input = args.get_number_with_type()?;
|
||||
let result = inner_from_cm(input.n, exec_state)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
@ -219,13 +223,14 @@ fn inner_from_cm(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
|
||||
UnitLen::M => Ok(measurements::Length::from_centimeters(input).as_meters()),
|
||||
UnitLen::Cm => Ok(input),
|
||||
UnitLen::Yards => Ok(measurements::Length::from_centimeters(input).as_yards()),
|
||||
UnitLen::Unknown => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Yards conversion factor for current files units.
|
||||
pub async fn from_yd(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let input = args.get_number()?;
|
||||
let result = inner_from_yd(input, exec_state)?;
|
||||
let input = args.get_number_with_type()?;
|
||||
let result = inner_from_yd(input.n, exec_state)?;
|
||||
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
|
||||
}
|
||||
@ -262,5 +267,6 @@ fn inner_from_yd(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
|
||||
UnitLen::M => Ok(measurements::Length::from_yards(input).as_meters()),
|
||||
UnitLen::Cm => Ok(measurements::Length::from_yards(input).as_centimeters()),
|
||||
UnitLen::Yards => Ok(input),
|
||||
UnitLen::Unknown => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,26 @@ use kittycad_modeling_cmds::shared::Angle;
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::Point2d,
|
||||
execution::{types::NumericType, Point2d},
|
||||
source_range::SourceRange,
|
||||
};
|
||||
|
||||
use super::args::TyF64;
|
||||
|
||||
pub fn untype_point(p: [TyF64; 2]) -> ([f64; 2], NumericType) {
|
||||
let (x, y, ty) = NumericType::combine_eq(p[0].clone(), p[1].clone());
|
||||
([x, y], ty)
|
||||
}
|
||||
|
||||
pub fn untype_point_3d(p: [TyF64; 3]) -> ([f64; 3], NumericType) {
|
||||
let (arr, ty) = NumericType::combine_eq_array(&[p[0].clone(), p[1].clone(), p[2].clone()]);
|
||||
let mut iter = arr.into_iter();
|
||||
([iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap()], ty)
|
||||
}
|
||||
|
||||
/// Get the distance between two points.
|
||||
pub fn distance(a: Point2d, b: Point2d) -> f64 {
|
||||
((b.x - a.x).powi(2) + (b.y - a.y).powi(2)).sqrt()
|
||||
pub fn distance(a: Coords2d, b: Coords2d) -> f64 {
|
||||
((b[0] - a[0]).powi(2) + (b[1] - a[1]).powi(2)).sqrt()
|
||||
}
|
||||
|
||||
/// Get the angle between these points
|
||||
@ -68,87 +81,54 @@ pub fn normalize_rad(angle: f64) -> f64 {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calculate_intersection_of_two_lines(line1: &[Point2d; 2], line2_angle: f64, line2_point: Point2d) -> Point2d {
|
||||
let line2_point_b = Point2d {
|
||||
x: line2_point.x + f64::cos(line2_angle.to_radians()) * 10.0,
|
||||
y: line2_point.y + f64::sin(line2_angle.to_radians()) * 10.0,
|
||||
};
|
||||
fn calculate_intersection_of_two_lines(line1: &[Coords2d; 2], line2_angle: f64, line2_point: Coords2d) -> Coords2d {
|
||||
let line2_point_b = [
|
||||
line2_point[0] + f64::cos(line2_angle.to_radians()) * 10.0,
|
||||
line2_point[1] + f64::sin(line2_angle.to_radians()) * 10.0,
|
||||
];
|
||||
intersect(line1[0], line1[1], line2_point, line2_point_b)
|
||||
}
|
||||
|
||||
pub fn intersect(p1: Point2d, p2: Point2d, p3: Point2d, p4: Point2d) -> Point2d {
|
||||
let slope = |p1: Point2d, p2: Point2d| (p1.y - p2.y) / (p1.x - p2.x);
|
||||
let constant = |p1: Point2d, p2: Point2d| p1.y - slope(p1, p2) * p1.x;
|
||||
let get_y = |for_x: f64, p1: Point2d, p2: Point2d| slope(p1, p2) * for_x + constant(p1, p2);
|
||||
fn intersect(p1: Coords2d, p2: Coords2d, p3: Coords2d, p4: Coords2d) -> Coords2d {
|
||||
let slope = |p1: Coords2d, p2: Coords2d| (p1[1] - p2[1]) / (p1[0] - p2[0]);
|
||||
let constant = |p1: Coords2d, p2: Coords2d| p1[1] - slope(p1, p2) * p1[0];
|
||||
let get_y = |for_x: f64, p1: Coords2d, p2: Coords2d| slope(p1, p2) * for_x + constant(p1, p2);
|
||||
|
||||
if p1.x == p2.x {
|
||||
return Point2d {
|
||||
x: p1.x,
|
||||
y: get_y(p1.x, p3, p4),
|
||||
};
|
||||
if p1[0] == p2[0] {
|
||||
return [p1[0], get_y(p1[0], p3, p4)];
|
||||
}
|
||||
if p3.x == p4.x {
|
||||
return Point2d {
|
||||
x: p3.x,
|
||||
y: get_y(p3.x, p1, p2),
|
||||
};
|
||||
if p3[0] == p4[0] {
|
||||
return [p3[0], get_y(p3[0], p1, p2)];
|
||||
}
|
||||
|
||||
let x = (constant(p3, p4) - constant(p1, p2)) / (slope(p1, p2) - slope(p3, p4));
|
||||
let y = get_y(x, p1, p2);
|
||||
Point2d { x, y }
|
||||
[x, y]
|
||||
}
|
||||
|
||||
pub fn intersection_with_parallel_line(
|
||||
line1: &[Point2d; 2],
|
||||
line1: &[Coords2d; 2],
|
||||
line1_offset: f64,
|
||||
line2_angle: f64,
|
||||
line2_point: Point2d,
|
||||
) -> Point2d {
|
||||
line2_point: Coords2d,
|
||||
) -> Coords2d {
|
||||
calculate_intersection_of_two_lines(&offset_line(line1_offset, line1[0], line1[1]), line2_angle, line2_point)
|
||||
}
|
||||
|
||||
fn offset_line(offset: f64, p1: Point2d, p2: Point2d) -> [Point2d; 2] {
|
||||
if p1.x == p2.x {
|
||||
let direction = (p1.y - p2.y).signum();
|
||||
return [
|
||||
Point2d {
|
||||
x: p1.x + offset * direction,
|
||||
y: p1.y,
|
||||
},
|
||||
Point2d {
|
||||
x: p2.x + offset * direction,
|
||||
y: p2.y,
|
||||
},
|
||||
];
|
||||
fn offset_line(offset: f64, p1: Coords2d, p2: Coords2d) -> [Coords2d; 2] {
|
||||
if p1[0] == p2[0] {
|
||||
let direction = (p1[1] - p2[1]).signum();
|
||||
return [[p1[0] + offset * direction, p1[1]], [p2[0] + offset * direction, p2[1]]];
|
||||
}
|
||||
if p1.y == p2.y {
|
||||
let direction = (p2.x - p1.x).signum();
|
||||
return [
|
||||
Point2d {
|
||||
x: p1.x,
|
||||
y: p1.y + offset * direction,
|
||||
},
|
||||
Point2d {
|
||||
x: p2.x,
|
||||
y: p2.y + offset * direction,
|
||||
},
|
||||
];
|
||||
if p1[1] == p2[1] {
|
||||
let direction = (p2[0] - p1[0]).signum();
|
||||
return [[p1[0], p1[1] + offset * direction], [p2[0], p2[1] + offset * direction]];
|
||||
}
|
||||
let x_offset = offset / f64::sin(f64::atan2(p1.y - p2.y, p1.x - p2.x));
|
||||
[
|
||||
Point2d {
|
||||
x: p1.x + x_offset,
|
||||
y: p1.y,
|
||||
},
|
||||
Point2d {
|
||||
x: p2.x + x_offset,
|
||||
y: p2.y,
|
||||
},
|
||||
]
|
||||
let x_offset = offset / f64::sin(f64::atan2(p1[1] - p2[1], p1[0] - p2[0]));
|
||||
[[p1[0] + x_offset, p1[1]], [p2[0] + x_offset, p2[1]]]
|
||||
}
|
||||
|
||||
pub fn get_y_component(angle: Angle, x: f64) -> Point2d {
|
||||
pub fn get_y_component(angle: Angle, x: f64) -> Coords2d {
|
||||
let normalised_angle = ((angle.to_degrees() % 360.0) + 360.0) % 360.0; // between 0 and 360
|
||||
let y = x * f64::tan(normalised_angle.to_radians());
|
||||
let sign = if normalised_angle > 90.0 && normalised_angle <= 270.0 {
|
||||
@ -156,10 +136,10 @@ pub fn get_y_component(angle: Angle, x: f64) -> Point2d {
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
Point2d { x, y }.scale(sign)
|
||||
[x * sign, y * sign]
|
||||
}
|
||||
|
||||
pub fn get_x_component(angle: Angle, y: f64) -> Point2d {
|
||||
pub fn get_x_component(angle: Angle, y: f64) -> Coords2d {
|
||||
let normalised_angle = ((angle.to_degrees() % 360.0) + 360.0) % 360.0; // between 0 and 360
|
||||
let x = y / f64::tan(normalised_angle.to_radians());
|
||||
let sign = if normalised_angle > 180.0 && normalised_angle <= 360.0 {
|
||||
@ -167,30 +147,30 @@ pub fn get_x_component(angle: Angle, y: f64) -> Point2d {
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
Point2d { x, y }.scale(sign)
|
||||
[x * sign, y * sign]
|
||||
}
|
||||
|
||||
pub fn arc_center_and_end(from: Point2d, start_angle: Angle, end_angle: Angle, radius: f64) -> (Point2d, Point2d) {
|
||||
pub fn arc_center_and_end(from: Coords2d, start_angle: Angle, end_angle: Angle, radius: f64) -> (Coords2d, Coords2d) {
|
||||
let start_angle = start_angle.to_radians();
|
||||
let end_angle = end_angle.to_radians();
|
||||
|
||||
let center = Point2d {
|
||||
x: -1.0 * (radius * start_angle.cos() - from.x),
|
||||
y: -1.0 * (radius * start_angle.sin() - from.y),
|
||||
};
|
||||
let center = [
|
||||
-1.0 * (radius * start_angle.cos() - from[0]),
|
||||
-1.0 * (radius * start_angle.sin() - from[1]),
|
||||
];
|
||||
|
||||
let end = Point2d {
|
||||
x: center.x + radius * end_angle.cos(),
|
||||
y: center.y + radius * end_angle.sin(),
|
||||
};
|
||||
let end = [
|
||||
center[0] + radius * end_angle.cos(),
|
||||
center[1] + radius * end_angle.sin(),
|
||||
];
|
||||
|
||||
(center, end)
|
||||
}
|
||||
|
||||
pub fn arc_angles(
|
||||
from: Point2d,
|
||||
to: Point2d,
|
||||
center: Point2d,
|
||||
from: Coords2d,
|
||||
to: Coords2d,
|
||||
center: Coords2d,
|
||||
radius: f64,
|
||||
source_range: SourceRange,
|
||||
) -> Result<(Angle, Angle), KclError> {
|
||||
@ -216,15 +196,15 @@ pub fn arc_angles(
|
||||
}));
|
||||
}
|
||||
|
||||
let start_angle = (from.y - center.y).atan2(from.x - center.x);
|
||||
let end_angle = (to.y - center.y).atan2(to.x - center.x);
|
||||
let start_angle = (from[1] - center[1]).atan2(from[0] - center[0]);
|
||||
let end_angle = (to[1] - center[1]).atan2(to[0] - center[0]);
|
||||
|
||||
Ok((Angle::from_radians(start_angle), Angle::from_radians(end_angle)))
|
||||
}
|
||||
|
||||
pub fn is_on_circumference(center: Point2d, point: Point2d, radius: f64) -> bool {
|
||||
let dx = point.x - center.x;
|
||||
let dy = point.y - center.y;
|
||||
fn is_on_circumference(center: Coords2d, point: Coords2d, radius: f64) -> bool {
|
||||
let dx = point[0] - center[0];
|
||||
let dy = point[1] - center[1];
|
||||
|
||||
let distance_squared = dx.powi(2) + dy.powi(2);
|
||||
|
||||
@ -268,12 +248,12 @@ pub fn calculate_circle_center(p1: [f64; 2], p2: [f64; 2], p3: [f64; 2]) -> [f64
|
||||
}
|
||||
|
||||
pub struct CircleParams {
|
||||
pub center: Point2d,
|
||||
pub center: Coords2d,
|
||||
pub radius: f64,
|
||||
}
|
||||
|
||||
pub fn calculate_circle_from_3_points(points: [Point2d; 3]) -> CircleParams {
|
||||
let center: Point2d = calculate_circle_center(points[0].into(), points[1].into(), points[2].into()).into();
|
||||
pub fn calculate_circle_from_3_points(points: [Coords2d; 3]) -> CircleParams {
|
||||
let center = calculate_circle_center(points[0], points[1], points[2]);
|
||||
CircleParams {
|
||||
center,
|
||||
radius: distance(center, points[1]),
|
||||
@ -312,27 +292,27 @@ mod tests {
|
||||
|
||||
for &(angle, expected_result) in EACH_QUAD.iter() {
|
||||
let res = get_y_component(Angle::from_degrees(angle as f64), 1.0);
|
||||
results.push([res.x.round() as i32, res.y.round() as i32]);
|
||||
results.push([res[0].round() as i32, res[1].round() as i32]);
|
||||
expected.push(expected_result);
|
||||
}
|
||||
|
||||
assert_eq!(results, expected);
|
||||
|
||||
let result = get_y_component(Angle::zero(), 1.0);
|
||||
assert_eq!(result.x as i32, 1);
|
||||
assert_eq!(result.y as i32, 0);
|
||||
assert_eq!(result[0] as i32, 1);
|
||||
assert_eq!(result[1] as i32, 0);
|
||||
|
||||
let result = get_y_component(Angle::from_degrees(90.0), 1.0);
|
||||
assert_eq!(result.x as i32, 1);
|
||||
assert!(result.y > 100000.0);
|
||||
assert_eq!(result[0] as i32, 1);
|
||||
assert!(result[1] > 100000.0);
|
||||
|
||||
let result = get_y_component(Angle::from_degrees(180.0), 1.0);
|
||||
assert_eq!(result.x as i32, -1);
|
||||
assert!((result.y - 0.0).abs() < f64::EPSILON);
|
||||
assert_eq!(result[0] as i32, -1);
|
||||
assert!((result[1] - 0.0).abs() < f64::EPSILON);
|
||||
|
||||
let result = get_y_component(Angle::from_degrees(270.0), 1.0);
|
||||
assert_eq!(result.x as i32, -1);
|
||||
assert!(result.y < -100000.0);
|
||||
assert_eq!(result[0] as i32, -1);
|
||||
assert!(result[1] < -100000.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -342,110 +322,71 @@ mod tests {
|
||||
|
||||
for &(angle, expected_result) in EACH_QUAD.iter() {
|
||||
let res = get_x_component(Angle::from_degrees(angle as f64), 1.0);
|
||||
results.push([res.x.round() as i32, res.y.round() as i32]);
|
||||
results.push([res[0].round() as i32, res[1].round() as i32]);
|
||||
expected.push(expected_result);
|
||||
}
|
||||
|
||||
assert_eq!(results, expected);
|
||||
|
||||
let result = get_x_component(Angle::zero(), 1.0);
|
||||
assert!(result.x > 100000.0);
|
||||
assert_eq!(result.y as i32, 1);
|
||||
assert!(result[0] > 100000.0);
|
||||
assert_eq!(result[1] as i32, 1);
|
||||
|
||||
let result = get_x_component(Angle::from_degrees(90.0), 1.0);
|
||||
assert!((result.x - 0.0).abs() < f64::EPSILON);
|
||||
assert_eq!(result.y as i32, 1);
|
||||
assert!((result[0] - 0.0).abs() < f64::EPSILON);
|
||||
assert_eq!(result[1] as i32, 1);
|
||||
|
||||
let result = get_x_component(Angle::from_degrees(180.0), 1.0);
|
||||
assert!(result.x < -100000.0);
|
||||
assert_eq!(result.y as i32, 1);
|
||||
assert!(result[0] < -100000.0);
|
||||
assert_eq!(result[1] as i32, 1);
|
||||
|
||||
let result = get_x_component(Angle::from_degrees(270.0), 1.0);
|
||||
assert!((result.x - 0.0).abs() < f64::EPSILON);
|
||||
assert_eq!(result.y as i32, -1);
|
||||
assert!((result[0] - 0.0).abs() < f64::EPSILON);
|
||||
assert_eq!(result[1] as i32, -1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arc_center_and_end() {
|
||||
let (center, end) = super::arc_center_and_end(
|
||||
super::Point2d { x: 0.0, y: 0.0 },
|
||||
Angle::zero(),
|
||||
Angle::from_degrees(90.0),
|
||||
1.0,
|
||||
);
|
||||
assert_eq!(center.x.round(), -1.0);
|
||||
assert_eq!(center.y, 0.0);
|
||||
assert_eq!(end.x.round(), -1.0);
|
||||
assert_eq!(end.y, 1.0);
|
||||
let (center, end) = super::arc_center_and_end([0.0, 0.0], Angle::zero(), Angle::from_degrees(90.0), 1.0);
|
||||
assert_eq!(center[0].round(), -1.0);
|
||||
assert_eq!(center[1], 0.0);
|
||||
assert_eq!(end[0].round(), -1.0);
|
||||
assert_eq!(end[1], 1.0);
|
||||
|
||||
let (center, end) = super::arc_center_and_end(
|
||||
super::Point2d { x: 0.0, y: 0.0 },
|
||||
Angle::zero(),
|
||||
Angle::from_degrees(180.0),
|
||||
1.0,
|
||||
);
|
||||
assert_eq!(center.x.round(), -1.0);
|
||||
assert_eq!(center.y, 0.0);
|
||||
assert_eq!(end.x.round(), -2.0);
|
||||
assert_eq!(end.y.round(), 0.0);
|
||||
let (center, end) = super::arc_center_and_end([0.0, 0.0], Angle::zero(), Angle::from_degrees(180.0), 1.0);
|
||||
assert_eq!(center[0].round(), -1.0);
|
||||
assert_eq!(center[1], 0.0);
|
||||
assert_eq!(end[0].round(), -2.0);
|
||||
assert_eq!(end[1].round(), 0.0);
|
||||
|
||||
let (center, end) = super::arc_center_and_end(
|
||||
super::Point2d { x: 0.0, y: 0.0 },
|
||||
Angle::zero(),
|
||||
Angle::from_degrees(180.0),
|
||||
10.0,
|
||||
);
|
||||
assert_eq!(center.x.round(), -10.0);
|
||||
assert_eq!(center.y, 0.0);
|
||||
assert_eq!(end.x.round(), -20.0);
|
||||
assert_eq!(end.y.round(), 0.0);
|
||||
let (center, end) = super::arc_center_and_end([0.0, 0.0], Angle::zero(), Angle::from_degrees(180.0), 10.0);
|
||||
assert_eq!(center[0].round(), -10.0);
|
||||
assert_eq!(center[1], 0.0);
|
||||
assert_eq!(end[0].round(), -20.0);
|
||||
assert_eq!(end[1].round(), 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arc_angles() {
|
||||
let (angle_start, angle_end) = super::arc_angles(
|
||||
super::Point2d { x: 0.0, y: 0.0 },
|
||||
super::Point2d { x: -1.0, y: 1.0 },
|
||||
super::Point2d { x: -1.0, y: 0.0 },
|
||||
1.0,
|
||||
SourceRange::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let (angle_start, angle_end) =
|
||||
super::arc_angles([0.0, 0.0], [-1.0, 1.0], [-1.0, 0.0], 1.0, SourceRange::default()).unwrap();
|
||||
assert_eq!(angle_start.to_degrees().round(), 0.0);
|
||||
assert_eq!(angle_end.to_degrees().round(), 90.0);
|
||||
|
||||
let (angle_start, angle_end) = super::arc_angles(
|
||||
super::Point2d { x: 0.0, y: 0.0 },
|
||||
super::Point2d { x: -2.0, y: 0.0 },
|
||||
super::Point2d { x: -1.0, y: 0.0 },
|
||||
1.0,
|
||||
SourceRange::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let (angle_start, angle_end) =
|
||||
super::arc_angles([0.0, 0.0], [-2.0, 0.0], [-1.0, 0.0], 1.0, SourceRange::default()).unwrap();
|
||||
assert_eq!(angle_start.to_degrees().round(), 0.0);
|
||||
assert_eq!(angle_end.to_degrees().round(), 180.0);
|
||||
|
||||
let (angle_start, angle_end) = super::arc_angles(
|
||||
super::Point2d { x: 0.0, y: 0.0 },
|
||||
super::Point2d { x: -20.0, y: 0.0 },
|
||||
super::Point2d { x: -10.0, y: 0.0 },
|
||||
10.0,
|
||||
SourceRange::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let (angle_start, angle_end) =
|
||||
super::arc_angles([0.0, 0.0], [-20.0, 0.0], [-10.0, 0.0], 10.0, SourceRange::default()).unwrap();
|
||||
assert_eq!(angle_start.to_degrees().round(), 0.0);
|
||||
assert_eq!(angle_end.to_degrees().round(), 180.0);
|
||||
|
||||
let result = super::arc_angles(
|
||||
super::Point2d { x: 0.0, y: 5.0 },
|
||||
super::Point2d { x: 5.0, y: 5.0 },
|
||||
super::Point2d { x: 10.0, y: -10.0 },
|
||||
10.0,
|
||||
SourceRange::default(),
|
||||
);
|
||||
let result = super::arc_angles([0.0, 5.0], [5.0, 5.0], [10.0, -10.0], 10.0, SourceRange::default());
|
||||
|
||||
if let Err(err) = result {
|
||||
assert!(err.to_string().contains("Point Point2d { x: 0.0, y: 5.0 } is not on the circumference of the circle with center Point2d { x: 10.0, y: -10.0 } and radius 10."));
|
||||
assert!(err.to_string().contains("Point [0.0, 5.0] is not on the circumference of the circle with center [10.0, -10.0] and radius 10."), "found: `{}`", err);
|
||||
} else {
|
||||
panic!("Expected error");
|
||||
}
|
||||
|
Reference in New Issue
Block a user