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:
Jonathan Tran
2025-04-14 05:58:19 -04:00
committed by GitHub
parent 7d7b153085
commit 160f55ede5
447 changed files with 60364 additions and 34465 deletions

View File

@ -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 {

View File

@ -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,

View File

@ -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)

View File

@ -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])

View File

@ -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)));