diff --git a/docs/kcl/legAngX.md b/docs/kcl/legAngX.md index d1c1f90df..67518ad94 100644 --- a/docs/kcl/legAngX.md +++ b/docs/kcl/legAngX.md @@ -24,8 +24,8 @@ legAngX( | Name | Type | Description | Required | |----------|------|-------------|----------| -| `hypotenuse` | [`number`](/docs/kcl/types/number) | | Yes | -| `leg` | [`number`](/docs/kcl/types/number) | | Yes | +| `hypotenuse` | [`number`](/docs/kcl/types/number) | The length of the triangle's hypotenuse | Yes | +| `leg` | [`number`](/docs/kcl/types/number) | The length of one of the triangle's legs (i.e. non-hypotenuse side) | Yes | ### Returns @@ -35,7 +35,7 @@ legAngX( ### Examples ```js -legAngX(5, 3) +legAngX(hypotenuse = 5, leg = 3) ``` diff --git a/docs/kcl/legAngY.md b/docs/kcl/legAngY.md index 3221ad6f8..26065520b 100644 --- a/docs/kcl/legAngY.md +++ b/docs/kcl/legAngY.md @@ -24,8 +24,8 @@ legAngY( | Name | Type | Description | Required | |----------|------|-------------|----------| -| `hypotenuse` | [`number`](/docs/kcl/types/number) | | Yes | -| `leg` | [`number`](/docs/kcl/types/number) | | Yes | +| `hypotenuse` | [`number`](/docs/kcl/types/number) | The length of the triangle's hypotenuse | Yes | +| `leg` | [`number`](/docs/kcl/types/number) | The length of one of the triangle's legs (i.e. non-hypotenuse side) | Yes | ### Returns @@ -35,7 +35,7 @@ legAngY( ### Examples ```js -legAngY(5, 3) +legAngY(hypotenuse = 5, leg = 3) ``` diff --git a/docs/kcl/legLen.md b/docs/kcl/legLen.md index c94330135..493a3c538 100644 --- a/docs/kcl/legLen.md +++ b/docs/kcl/legLen.md @@ -24,8 +24,8 @@ legLen( | Name | Type | Description | Required | |----------|------|-------------|----------| -| `hypotenuse` | [`number`](/docs/kcl/types/number) | | Yes | -| `leg` | [`number`](/docs/kcl/types/number) | | Yes | +| `hypotenuse` | [`number`](/docs/kcl/types/number) | The length of the triangle's hypotenuse | Yes | +| `leg` | [`number`](/docs/kcl/types/number) | The length of one of the triangle's legs (i.e. non-hypotenuse side) | Yes | ### Returns @@ -35,7 +35,7 @@ legLen( ### Examples ```js -legLen(5, 3) +legLen(hypotenuse = 5, leg = 3) ``` diff --git a/docs/kcl/std.json b/docs/kcl/std.json index ba84e33b6..b6dab05a1 100644 --- a/docs/kcl/std.json +++ b/docs/kcl/std.json @@ -133520,7 +133520,7 @@ "tags": [ "utilities" ], - "keywordArguments": false, + "keywordArguments": true, "args": [ { "name": "hypotenuse", @@ -133533,6 +133533,7 @@ }, "required": true, "includeInSnippet": true, + "description": "The length of the triangle's hypotenuse", "labelRequired": true }, { @@ -133546,6 +133547,7 @@ }, "required": true, "includeInSnippet": true, + "description": "The length of one of the triangle's legs (i.e. non-hypotenuse side)", "labelRequired": true } ], @@ -133565,7 +133567,7 @@ "unpublished": false, "deprecated": false, "examples": [ - "legAngX(5, 3)" + "legAngX(hypotenuse = 5, leg = 3)" ] }, { @@ -133575,7 +133577,7 @@ "tags": [ "utilities" ], - "keywordArguments": false, + "keywordArguments": true, "args": [ { "name": "hypotenuse", @@ -133588,6 +133590,7 @@ }, "required": true, "includeInSnippet": true, + "description": "The length of the triangle's hypotenuse", "labelRequired": true }, { @@ -133601,6 +133604,7 @@ }, "required": true, "includeInSnippet": true, + "description": "The length of one of the triangle's legs (i.e. non-hypotenuse side)", "labelRequired": true } ], @@ -133620,7 +133624,7 @@ "unpublished": false, "deprecated": false, "examples": [ - "legAngY(5, 3)" + "legAngY(hypotenuse = 5, leg = 3)" ] }, { @@ -133630,7 +133634,7 @@ "tags": [ "utilities" ], - "keywordArguments": false, + "keywordArguments": true, "args": [ { "name": "hypotenuse", @@ -133643,6 +133647,7 @@ }, "required": true, "includeInSnippet": true, + "description": "The length of the triangle's hypotenuse", "labelRequired": true }, { @@ -133656,6 +133661,7 @@ }, "required": true, "includeInSnippet": true, + "description": "The length of one of the triangle's legs (i.e. non-hypotenuse side)", "labelRequired": true } ], @@ -133675,7 +133681,7 @@ "unpublished": false, "deprecated": false, "examples": [ - "legLen(5, 3)" + "legLen(hypotenuse = 5, leg = 3)" ] }, { diff --git a/rust/kcl-lib/src/execution/mod.rs b/rust/kcl-lib/src/execution/mod.rs index 9e4a55c67..2a86db786 100644 --- a/rust/kcl-lib/src/execution/mod.rs +++ b/rust/kcl-lib/src/execution/mod.rs @@ -1277,7 +1277,7 @@ const part001 = startSketchOn(XY) |> line(end = [3, 4], tag = $seg01) |> line(end = [ min(segLen(seg01), myVar), - -legLen(segLen(seg01), myVar) + -legLen(hypotenuse = segLen(seg01), leg = myVar) ]) "#; @@ -1292,7 +1292,7 @@ const part001 = startSketchOn(XY) |> line(end = [3, 4], tag = $seg01) |> line(end = [ min(segLen(seg01), myVar), - legLen(segLen(seg01), myVar) + legLen(hypotenuse = segLen(seg01), leg = myVar) ]) "#; @@ -1684,7 +1684,7 @@ let shape = layer() |> patternTransform(instances = 10, transform = transform) #[tokio::test(flavor = "multi_thread")] async fn test_math_execute_with_functions() { - let ast = r#"const myVar = 2 + min(100, -1 + legLen(5, 3))"#; + let ast = r#"const myVar = 2 + min(100, -1 + legLen(hypotenuse = 5, leg = 3))"#; let result = parse_execute(ast).await.unwrap(); assert_eq!( 5.0, diff --git a/rust/kcl-lib/src/std/args.rs b/rust/kcl-lib/src/std/args.rs index 6a2edc0f7..df36cca37 100644 --- a/rust/kcl-lib/src/std/args.rs +++ b/rust/kcl-lib/src/std/args.rs @@ -615,22 +615,6 @@ impl Args { Ok(numbers) } - pub(crate) fn get_hypotenuse_leg(&self) -> Result<(f64, f64, NumericType), KclError> { - let numbers = self.get_number_array_with_types()?; - - if numbers.len() != 2 { - return Err(KclError::Type(KclErrorDetails { - message: format!("Expected a number array of length 2, found `{:?}`", numbers), - source_ranges: vec![self.source_range], - })); - } - - let mut numbers = numbers.into_iter(); - let a = numbers.next().unwrap(); - let b = numbers.next().unwrap(); - Ok(NumericType::combine_eq_coerce(a, b)) - } - pub(crate) fn get_sketches(&self, exec_state: &mut ExecState) -> Result<(Vec, Sketch), KclError> { let Some(arg0) = self.args.first() else { return Err(KclError::Semantic(KclErrorDetails { diff --git a/rust/kcl-lib/src/std/mod.rs b/rust/kcl-lib/src/std/mod.rs index 0ad5bb1d3..7a0c6018e 100644 --- a/rust/kcl-lib/src/std/mod.rs +++ b/rust/kcl-lib/src/std/mod.rs @@ -30,6 +30,7 @@ pub mod utils; use anyhow::Result; pub use args::Args; +use args::TyF64; use indexmap::IndexMap; use kcl_derive_docs::stdlib; use lazy_static::lazy_static; @@ -40,7 +41,10 @@ use serde::{Deserialize, Serialize}; use crate::{ docs::StdLibFn, errors::KclError, - execution::{types::PrimitiveType, ExecState, KclValue}, + execution::{ + types::{NumericType, PrimitiveType, RuntimeType, UnitAngle, UnitType}, + ExecState, KclValue, + }, parsing::ast::types::Name, }; @@ -287,8 +291,10 @@ pub enum FunctionKind { const DEFAULT_TOLERANCE: f64 = 0.0000001; /// Compute the length of the given leg. -pub async fn leg_length(_exec_state: &mut ExecState, args: Args) -> Result { - let (hypotenuse, leg, ty) = args.get_hypotenuse_leg()?; +pub async fn leg_length(exec_state: &mut ExecState, args: Args) -> Result { + let hypotenuse: TyF64 = args.get_kw_arg_typed("hypotenuse", &RuntimeType::length(), exec_state)?; + let leg: TyF64 = args.get_kw_arg_typed("leg", &RuntimeType::length(), exec_state)?; + let (hypotenuse, leg, ty) = NumericType::combine_eq_coerce(hypotenuse, leg); let result = inner_leg_length(hypotenuse, leg); Ok(KclValue::from_number_with_type(result, ty, vec![args.into()])) } @@ -296,10 +302,16 @@ pub async fn leg_length(_exec_state: &mut ExecState, args: Args) -> Result f64 { @@ -307,19 +319,31 @@ fn inner_leg_length(hypotenuse: f64, leg: f64) -> f64 { } /// Compute the angle of the given leg for x. -pub async fn leg_angle_x(_exec_state: &mut ExecState, args: Args) -> Result { - let (hypotenuse, leg, ty) = args.get_hypotenuse_leg()?; +pub async fn leg_angle_x(exec_state: &mut ExecState, args: Args) -> Result { + let hypotenuse: TyF64 = args.get_kw_arg_typed("hypotenuse", &RuntimeType::length(), exec_state)?; + let leg: TyF64 = args.get_kw_arg_typed("leg", &RuntimeType::length(), exec_state)?; + let (hypotenuse, leg, _ty) = NumericType::combine_eq_coerce(hypotenuse, leg); let result = inner_leg_angle_x(hypotenuse, leg); - Ok(KclValue::from_number_with_type(result, ty, vec![args.into()])) + Ok(KclValue::from_number_with_type( + result, + NumericType::Known(UnitType::Angle(UnitAngle::Degrees)), + vec![args.into()], + )) } /// Compute the angle of the given leg for x. /// /// ```no_run -/// legAngX(5, 3) +/// legAngX(hypotenuse = 5, leg = 3) /// ``` #[stdlib { name = "legAngX", + keywords = true, + unlabeled_first = false, + args = { + hypotenuse = { docs = "The length of the triangle's hypotenuse" }, + leg = { docs = "The length of one of the triangle's legs (i.e. non-hypotenuse side)" }, + }, tags = ["utilities"], }] fn inner_leg_angle_x(hypotenuse: f64, leg: f64) -> f64 { @@ -327,19 +351,31 @@ fn inner_leg_angle_x(hypotenuse: f64, leg: f64) -> f64 { } /// Compute the angle of the given leg for y. -pub async fn leg_angle_y(_exec_state: &mut ExecState, args: Args) -> Result { - let (hypotenuse, leg, ty) = args.get_hypotenuse_leg()?; +pub async fn leg_angle_y(exec_state: &mut ExecState, args: Args) -> Result { + let hypotenuse: TyF64 = args.get_kw_arg_typed("hypotenuse", &RuntimeType::length(), exec_state)?; + let leg: TyF64 = args.get_kw_arg_typed("leg", &RuntimeType::length(), exec_state)?; + let (hypotenuse, leg, _ty) = NumericType::combine_eq_coerce(hypotenuse, leg); let result = inner_leg_angle_y(hypotenuse, leg); - Ok(KclValue::from_number_with_type(result, ty, vec![args.into()])) + Ok(KclValue::from_number_with_type( + result, + NumericType::Known(UnitType::Angle(UnitAngle::Degrees)), + vec![args.into()], + )) } /// Compute the angle of the given leg for y. /// /// ```no_run -/// legAngY(5, 3) +/// legAngY(hypotenuse = 5, leg = 3) /// ``` #[stdlib { name = "legAngY", + keywords = true, + unlabeled_first = false, + args = { + hypotenuse = { docs = "The length of the triangle's hypotenuse" }, + leg = { docs = "The length of one of the triangle's legs (i.e. non-hypotenuse side)" }, + }, tags = ["utilities"], }] fn inner_leg_angle_y(hypotenuse: f64, leg: f64) -> f64 { diff --git a/src/lang/executor.test.ts b/src/lang/executor.test.ts index c4c6ee824..afa991b70 100644 --- a/src/lang/executor.test.ts +++ b/src/lang/executor.test.ts @@ -362,7 +362,7 @@ describe('testing math operators', () => { expect(mem['myVar']?.value).toBe(6) }) it('with nested callExpression', async () => { - const code = 'const myVar = 2 + min(100, legLen(5, 3))' + const code = 'myVar = 2 + min(100, legLen(hypotenuse = 5, leg = 3))' const mem = await exe(code) expect(mem['myVar']?.value).toBe(6) }) @@ -372,15 +372,15 @@ describe('testing math operators', () => { expect(mem['myVar']?.value).toBe(-3) }) it('with unaryExpression in callExpression', async () => { - const code = 'const myVar = min(-legLen(5, 4), 5)' - const code2 = 'const myVar = min(5 , -legLen(5, 4))' + const code = 'const myVar = min(-legLen(hypotenuse = 5, leg = 4), 5)' + const code2 = 'const myVar = min(5 , -legLen(hypotenuse = 5, leg = 4))' const mem = await exe(code) const mem2 = await exe(code2) expect(mem['myVar']?.value).toBe(-3) expect(mem['myVar']?.value).toBe(mem2['myVar']?.value) }) it('with unaryExpression in ArrayExpression', async () => { - const code = 'const myVar = [1,-legLen(5, 4)]' + const code = 'const myVar = [1,-legLen(hypotenuse = 5, leg = 4)]' const mem = await exe(code) expect(mem['myVar']?.value).toEqual([ { @@ -397,9 +397,9 @@ describe('testing math operators', () => { }) it('with unaryExpression in ArrayExpression in CallExpression, checking nothing funny happens when used in a sketch', async () => { const code = [ - 'const part001 = startSketchOn(XY)', + 'part001 = startSketchOn(XY)', ' |> startProfileAt([0, 0], %)', - '|> line(end = [-2.21, -legLen(5, min(3, 999))])', + '|> line(end = [-2.21, -legLen(hypotenuse = 5, leg = min(3, 999))])', ].join('\n') const mem = await exe(code) const sketch = sketchFromKclValue(mem['part001'], 'part001') @@ -415,7 +415,7 @@ describe('testing math operators', () => { ` |> line(end = [3, 4], tag = $seg01)`, ` |> line(end = [`, ` min(segLen(seg01), myVar),`, - ` -legLen(segLen(seg01), myVar)`, + ` -legLen(hypotenuse = segLen(seg01), leg = myVar)`, `])`, ``, ].join('\n') @@ -425,8 +425,8 @@ describe('testing math operators', () => { expect((sketch as Sketch).paths?.[1]?.from).toEqual([3, 4]) expect((sketch as Sketch).paths?.[1]?.to).toEqual([6, 0]) const removedUnaryExp = code.replace( - `-legLen(segLen(seg01), myVar)`, - `legLen(segLen(seg01), myVar)` + `-legLen(hypotenuse = segLen(seg01), leg = myVar)`, + `legLen(hypotenuse = segLen(seg01), leg = myVar)` ) const removedUnaryExpMem = await exe(removedUnaryExp) const removedUnaryExpMemSketch = sketchFromKclValue( @@ -438,7 +438,8 @@ describe('testing math operators', () => { expect((removedUnaryExpMemSketch as Sketch).paths?.[1]?.to).toEqual([6, 8]) }) it('with nested callExpression and binaryExpression', async () => { - const code = 'const myVar = 2 + min(100, -1 + legLen(5, 3))' + const code = + 'const myVar = 2 + min(100, -1 + legLen(hypotenuse = 5, leg = 3))' const mem = await exe(code) expect(mem['myVar']?.value).toBe(5) }) diff --git a/src/lang/recast.test.ts b/src/lang/recast.test.ts index 9abeb7712..d5b4b73e7 100644 --- a/src/lang/recast.test.ts +++ b/src/lang/recast.test.ts @@ -298,21 +298,21 @@ mySk1 = startSketchOn(XY) describe('testing call Expressions in BinaryExpressions and UnaryExpressions', () => { it('nested callExpression in binaryExpression', () => { - const code = 'myVar = 2 + min(100, legLen(5, 3))' + const code = 'myVar = 2 + min(100, legLen(hypotenuse = 5, leg = 3))' const { ast } = code2ast(code) const recasted = recast(ast) if (err(recasted)) throw recasted expect(recasted.trim()).toBe(code) }) it('nested callExpression in unaryExpression', () => { - const code = 'myVar = -min(100, legLen(5, 3))' + const code = 'myVar = -min(100, legLen(hypotenuse = 5, leg = 3))' const { ast } = code2ast(code) const recasted = recast(ast) if (err(recasted)) throw recasted expect(recasted.trim()).toBe(code) }) it('with unaryExpression in callExpression', () => { - const code = 'myVar = min(5, -legLen(5, 4))' + const code = 'myVar = min(5, -legLen(hypotenuse = 5, leg = 4))' const { ast } = code2ast(code) const recasted = recast(ast) if (err(recasted)) throw recasted @@ -322,7 +322,7 @@ describe('testing call Expressions in BinaryExpressions and UnaryExpressions', ( const code = [ 'part001 = startSketchOn(XY)', ' |> startProfileAt([0, 0])', - ' |> line(end = [-2.21, -legLen(5, min(3, 999))])', + ' |> line(end = [\n -2.21,\n -legLen(hypotenuse = 5, leg = min(3, 999))\n ])', ].join('\n') const { ast } = code2ast(code) const recasted = recast(ast) diff --git a/src/lang/std/sketchcombos.test.ts b/src/lang/std/sketchcombos.test.ts index e6241499e..a9bacba5b 100644 --- a/src/lang/std/sketchcombos.test.ts +++ b/src/lang/std/sketchcombos.test.ts @@ -308,11 +308,11 @@ part001 = startSketchOn(XY) |> angledLine(angle = myAng2, length = segLen(seg01)) // ln-angledLineToY-angle should become angledLine |> line(end = [ min(segLen(seg01), myVar), - legLen(segLen(seg01), myVar) + legLen(hypotenuse = segLen(seg01), leg = myVar) ]) // ln-should use legLen for y |> line(end = [ min(segLen(seg01), myVar), - -legLen(segLen(seg01), myVar) + -legLen(hypotenuse = segLen(seg01), leg = myVar) ]) // ln-legLen but negative |> angledLine(angle = -112, length = segLen(seg01)) // ln-should become angledLine |> angledLine(angle = myVar, length = segLen(seg01)) // ln-use segLen for second arg @@ -321,11 +321,11 @@ part001 = startSketchOn(XY) |> angledLine(angle = legAngX(segLen(seg01), myVar), lengthX = min(segLen(seg01), myVar)) // ln-should use legAngX to calculate angle |> angledLine(angle = 180 + legAngX(segLen(seg01), myVar), lengthX = min(segLen(seg01), myVar)) // ln-same as above but should have + 180 to match original quadrant |> line(end = [ - legLen(segLen(seg01), myVar), + legLen(hypotenuse = segLen(seg01), leg = myVar), min(segLen(seg01), myVar) ]) // ln-legLen again but yRelative |> line(end = [ - -legLen(segLen(seg01), myVar), + -legLen(hypotenuse = segLen(seg01), leg = myVar), min(segLen(seg01), myVar) ]) // ln-negative legLen yRelative |> angledLine(angle = 58, length = segLen(seg01)) // ln-angledLineOfYLength-free should become angledLine diff --git a/src/lang/std/sketchcombos.ts b/src/lang/std/sketchcombos.ts index 19ab3e07d..1d1f09ec9 100644 --- a/src/lang/std/sketchcombos.ts +++ b/src/lang/std/sketchcombos.ts @@ -453,7 +453,10 @@ const getMinAndSegLenVals = ( const segLenVal = createSegLen(referenceSegName) return [ createCallExpression('min', [segLenVal, varVal]), - createCallExpression('legLen', [segLenVal, varVal]), + createCallExpressionStdLibKw('legLen', null, [ + createLabeledArg('hypotenuse', segLenVal), + createLabeledArg('leg', varVal), + ]), ] }