Compare commits

...

6 Commits

Author SHA1 Message Date
117e696430 Merge branch 'nrc-uom-cmds' into franknoirot/tweak-test-nrc-uom-cmds 2025-04-22 14:19:09 -04:00
c18a2223f3 Make useDemoCode use updateModelingState like everyone else 2025-04-22 13:18:22 -04:00
c5492a7937 Tweak test connection step on failing test 2025-04-22 09:52:01 -04:00
19a3c255c3 Convert all lengths to mm for engine calls
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-22 16:04:15 +12:00
f93979c648 Convert all lengths to mm for engine calls
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-22 12:15:28 +12:00
6288ab5f2f Turn on uom checks
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-22 11:12:20 +12:00
337 changed files with 35471 additions and 17185 deletions

View File

@ -4,8 +4,12 @@ excerpt: "Converts a number from centimeters to the current default unit."
layout: manual layout: manual
--- ---
**WARNING:** This function is deprecated.
Converts a number from centimeters to the current default unit. Converts a number from centimeters to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42cm`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in centimeters. No matter what units the current file uses, this function will always return a number equivalent to the input in centimeters.
For example, if the current file uses inches, `fromCm(1)` will return `0.393701`. If the current file uses millimeters, `fromCm(1)` will return `10`. If the current file uses centimeters, `fromCm(1)` will return `1`. For example, if the current file uses inches, `fromCm(1)` will return `0.393701`. If the current file uses millimeters, `fromCm(1)` will return `10`. If the current file uses centimeters, `fromCm(1)` will return `1`.

View File

@ -4,8 +4,12 @@ excerpt: "Converts a number from feet to the current default unit."
layout: manual layout: manual
--- ---
**WARNING:** This function is deprecated.
Converts a number from feet to the current default unit. Converts a number from feet to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42ft`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in feet. No matter what units the current file uses, this function will always return a number equivalent to the input in feet.
For example, if the current file uses inches, `fromFt(1)` will return `12`. If the current file uses millimeters, `fromFt(1)` will return `304.8`. If the current file uses feet, `fromFt(1)` will return `1`. For example, if the current file uses inches, `fromFt(1)` will return `12`. If the current file uses millimeters, `fromFt(1)` will return `304.8`. If the current file uses feet, `fromFt(1)` will return `1`.

View File

@ -4,8 +4,12 @@ excerpt: "Converts a number from inches to the current default unit."
layout: manual layout: manual
--- ---
**WARNING:** This function is deprecated.
Converts a number from inches to the current default unit. Converts a number from inches to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42inch`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in inches. No matter what units the current file uses, this function will always return a number equivalent to the input in inches.
For example, if the current file uses inches, `fromInches(1)` will return `1`. If the current file uses millimeters, `fromInches(1)` will return `25.4`. For example, if the current file uses inches, `fromInches(1)` will return `1`. If the current file uses millimeters, `fromInches(1)` will return `25.4`.

View File

@ -4,8 +4,12 @@ excerpt: "Converts a number from meters to the current default unit."
layout: manual layout: manual
--- ---
**WARNING:** This function is deprecated.
Converts a number from meters to the current default unit. Converts a number from meters to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42m`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in meters. No matter what units the current file uses, this function will always return a number equivalent to the input in meters.
For example, if the current file uses inches, `fromM(1)` will return `39.3701`. If the current file uses millimeters, `fromM(1)` will return `1000`. If the current file uses meters, `fromM(1)` will return `1`. For example, if the current file uses inches, `fromM(1)` will return `39.3701`. If the current file uses millimeters, `fromM(1)` will return `1000`. If the current file uses meters, `fromM(1)` will return `1`.

View File

@ -4,8 +4,12 @@ excerpt: "Converts a number from mm to the current default unit."
layout: manual layout: manual
--- ---
**WARNING:** This function is deprecated.
Converts a number from mm to the current default unit. Converts a number from mm to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42mm`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in millimeters. No matter what units the current file uses, this function will always return a number equivalent to the input in millimeters.
For example, if the current file uses inches, `fromMm(1)` will return `1/25.4`. If the current file uses millimeters, `fromMm(1)` will return `1`. For example, if the current file uses inches, `fromMm(1)` will return `1/25.4`. If the current file uses millimeters, `fromMm(1)` will return `1`.

View File

@ -4,8 +4,12 @@ excerpt: "Converts a number from yards to the current default unit."
layout: manual layout: manual
--- ---
**WARNING:** This function is deprecated.
Converts a number from yards to the current default unit. Converts a number from yards to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42yd`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in yards. No matter what units the current file uses, this function will always return a number equivalent to the input in yards.
For example, if the current file uses inches, `fromYd(1)` will return `36`. If the current file uses millimeters, `fromYd(1)` will return `914.4`. If the current file uses yards, `fromYd(1)` will return `1`. For example, if the current file uses inches, `fromYd(1)` will return `36`. If the current file uses millimeters, `fromYd(1)` will return `914.4`. If the current file uses yards, `fromYd(1)` will return `1`.

View File

@ -55,12 +55,6 @@ layout: manual
* [`extrude`](kcl/extrude) * [`extrude`](kcl/extrude)
* [`fillet`](kcl/fillet) * [`fillet`](kcl/fillet)
* [`floor`](kcl/floor) * [`floor`](kcl/floor)
* [`fromCm`](kcl/fromCm)
* [`fromFt`](kcl/fromFt)
* [`fromInches`](kcl/fromInches)
* [`fromM`](kcl/fromM)
* [`fromMm`](kcl/fromMm)
* [`fromYd`](kcl/fromYd)
* [`getCommonEdge`](kcl/getCommonEdge) * [`getCommonEdge`](kcl/getCommonEdge)
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge) * [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
* [`getOppositeEdge`](kcl/getOppositeEdge) * [`getOppositeEdge`](kcl/getOppositeEdge)
@ -120,8 +114,14 @@ layout: manual
* [`sweep`](kcl/sweep) * [`sweep`](kcl/sweep)
* [`tangentToEnd`](kcl/tangentToEnd) * [`tangentToEnd`](kcl/tangentToEnd)
* [`tangentialArc`](kcl/tangentialArc) * [`tangentialArc`](kcl/tangentialArc)
* [`toDegrees`](kcl/toDegrees) * [`toCentimeters`](kcl/std-toCentimeters)
* [`toRadians`](kcl/toRadians) * [`toDegrees`](kcl/std-toDegrees)
* [`toFeet`](kcl/std-toFeet)
* [`toInches`](kcl/std-toInches)
* [`toMeters`](kcl/std-toMeters)
* [`toMillimeters`](kcl/std-toMillimeters)
* [`toRadians`](kcl/std-toRadians)
* [`toYards`](kcl/std-toYards)
* [`translate`](kcl/translate) * [`translate`](kcl/translate)
* [`union`](kcl/union) * [`union`](kcl/union)
* [`xLine`](kcl/xLine) * [`xLine`](kcl/xLine)

View File

@ -174,7 +174,7 @@ t = 0.005 // taper factor [0-1)
// Defines how to modify each layer of the vase. // Defines how to modify each layer of the vase.
// Each replica is shifted up the Z axis, and has a smoothly-varying radius // Each replica is shifted up the Z axis, and has a smoothly-varying radius
fn transform(replicaId) { fn transform(replicaId) {
scale = r * abs(1 - (t * replicaId)) * (5 + cos(replicaId / 8)) scale = r * abs(1 - (t * replicaId)) * (5 + cos(replicaId / 8: number(rad)))
return { return {
translate = [0, 0, replicaId * 10], translate = [0, 0, replicaId * 10],
scale = [scale, scale, 0] scale = [scale, scale, 0]

View File

@ -26,7 +26,7 @@ helix(
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `revolutions` | `number(_)` | Number of revolutions. | Yes | | `revolutions` | `number(_)` | Number of revolutions. | Yes |
| `angleStart` | `number(Angle)` | Start angle (in degrees). | Yes | | `angleStart` | `number(Angle)` | Start angle. | Yes |
| `ccw` | [`bool`](/docs/kcl/types/bool) | Is the helix rotation counter clockwise? The default is `false`. | No | | `ccw` | [`bool`](/docs/kcl/types/bool) | Is the helix rotation counter clockwise? The default is `false`. | No |
| `radius` | `number(Length)` | Radius of the helix. | No | | `radius` | `number(Length)` | Radius of the helix. | No |
| `axis` | `Axis3d | Edge` | Axis to use for the helix. | No | | `axis` | `Axis3d | Edge` | Axis to use for the helix. | No |

View File

@ -31,7 +31,7 @@ exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine( |> angledLine(
angle = 30, angle = 30,
length = 3 / cos(toRadians(30)), length = 3 / cos(30deg),
) )
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

View File

@ -11,7 +11,7 @@ cartesian (x/y/z grid) coordinates.
```js ```js
polar( polar(
angle: number(Angle), angle: number(rad),
length: number(Length), length: number(Length),
): Point2d ): Point2d
``` ```
@ -21,7 +21,7 @@ polar(
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `angle` | `number(Angle)` | | Yes | | `angle` | `number(rad)` | | Yes |
| `length` | `number(Length)` | | Yes | | `length` | `number(Length)` | | Yes |
### Returns ### Returns

View File

@ -31,7 +31,7 @@ exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine( |> angledLine(
angle = 50, angle = 50,
length = 15 / sin(toRadians(135)), length = 15 / sin(135deg),
) )
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

View File

@ -31,7 +31,7 @@ exampleSketch = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine( |> angledLine(
angle = 50, angle = 50,
length = 50 * tan(1/2), length = 50 * tan((1/2): number(rad)),
) )
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()

View File

@ -0,0 +1,27 @@
---
title: "std::toCentimeters"
excerpt: "Convert a number to centimeters from its current units."
layout: manual
---
Convert a number to centimeters from its current units.
```js
toCentimeters(@num: number(cm)): number(cm)
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | `number(cm)` | | Yes |
### Returns
`number(cm)`

44
docs/kcl/std-toDegrees.md Normal file

File diff suppressed because one or more lines are too long

27
docs/kcl/std-toFeet.md Normal file
View File

@ -0,0 +1,27 @@
---
title: "std::toFeet"
excerpt: "Convert a number to feet from its current units."
layout: manual
---
Convert a number to feet from its current units.
```js
toFeet(@num: number(ft)): number(ft)
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | `number(ft)` | | Yes |
### Returns
`number(ft)`

27
docs/kcl/std-toInches.md Normal file
View File

@ -0,0 +1,27 @@
---
title: "std::toInches"
excerpt: "Convert a number to inches from its current units."
layout: manual
---
Convert a number to inches from its current units.
```js
toInches(@num: number(in)): number(in)
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | `number(in)` | | Yes |
### Returns
`number(in)`

27
docs/kcl/std-toMeters.md Normal file
View File

@ -0,0 +1,27 @@
---
title: "std::toMeters"
excerpt: "Convert a number to meters from its current units."
layout: manual
---
Convert a number to meters from its current units.
```js
toMeters(@num: number(m)): number(m)
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | `number(m)` | | Yes |
### Returns
`number(m)`

View File

@ -0,0 +1,27 @@
---
title: "std::toMillimeters"
excerpt: "Convert a number to millimeters from its current units."
layout: manual
---
Convert a number to millimeters from its current units.
```js
toMillimeters(@num: number(mm)): number(mm)
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | `number(mm)` | | Yes |
### Returns
`number(mm)`

44
docs/kcl/std-toRadians.md Normal file

File diff suppressed because one or more lines are too long

27
docs/kcl/std-toYards.md Normal file
View File

@ -0,0 +1,27 @@
---
title: "std::toYards"
excerpt: "Converts a number to yards from its current units."
layout: manual
---
Converts a number to yards from its current units.
```js
toYards(@num: number(yd)): number(yd)
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `num` | `number(yd)` | | Yes |
### Returns
`number(yd)`

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -31,7 +31,6 @@ A sketch type.
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane's X axis be? | No | | `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane's X axis be? | No |
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane's Y axis be? | No | | `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane's Y axis be? | No |
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No | | `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
---- ----

View File

@ -63,7 +63,7 @@ test.describe('Onboarding tests', () => {
{ {
tag: '@electron', tag: '@electron',
}, },
async ({ page, tronApp }) => { async ({ page, tronApp, scene }) => {
if (!tronApp) { if (!tronApp) {
fail() fail()
} }
@ -72,7 +72,6 @@ test.describe('Onboarding tests', () => {
onboarding_status: '', onboarding_status: '',
}, },
}) })
const u = await getUtils(page)
const viewportSize = { width: 1200, height: 500 } const viewportSize = { width: 1200, height: 500 }
await page.setBodyDimensions(viewportSize) await page.setBodyDimensions(viewportSize)
@ -80,7 +79,7 @@ test.describe('Onboarding tests', () => {
await test.step(`Create a project and open to the onboarding`, async () => { await test.step(`Create a project and open to the onboarding`, async () => {
await createProject({ name: 'project-link', page }) await createProject({ name: 'project-link', page })
await test.step(`Ensure the engine connection works by testing the sketch button`, async () => { await test.step(`Ensure the engine connection works by testing the sketch button`, async () => {
await u.waitForPageLoad() await scene.connectionEstablished()
}) })
}) })

View File

@ -278,7 +278,7 @@ test.describe('Point-and-click assemblies tests', () => {
highlightedHeaderArg: 'x', highlightedHeaderArg: 'x',
commandName: 'Translate', commandName: 'Translate',
}) })
await page.keyboard.insertText('5') await page.keyboard.insertText('100')
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await page.keyboard.insertText('0.1') await page.keyboard.insertText('0.1')
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
@ -287,7 +287,7 @@ test.describe('Point-and-click assemblies tests', () => {
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'review', stage: 'review',
headerArguments: { headerArguments: {
X: '5', X: '100',
Y: '0.1', Y: '0.1',
Z: '0.2', Z: '0.2',
}, },
@ -299,7 +299,7 @@ test.describe('Point-and-click assemblies tests', () => {
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
` `
bracket bracket
|> translate(x = 5, y = 0.1, z = 0.2) |> translate(x = 100, y = 0.1, z = 0.2)
`, `,
{ shouldNormalise: true } { shouldNormalise: true }
) )
@ -348,7 +348,7 @@ test.describe('Point-and-click assemblies tests', () => {
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
` `
bracket bracket
|> translate(x = 5, y = 0.1, z = 0.2) |> translate(x = 100, y = 0.1, z = 0.2)
|> rotate(roll = 0.1, pitch = 0.2, yaw = 0.3) |> rotate(roll = 0.1, pitch = 0.2, yaw = 0.3)
`, `,
{ shouldNormalise: true } { shouldNormalise: true }

View File

@ -33,11 +33,11 @@ invas = map(angles, fn(a) {
// Map the involute curve // Map the involute curve
xs = map([0..cmo], fn(i) { xs = map([0..cmo], fn(i) {
return rs[i] * cos(invas[i]) return rs[i] * cos(invas[i]: number(rad))
}) })
ys = map([0..cmo], fn(i) { ys = map([0..cmo], fn(i) {
return rs[i] * sin(invas[i]) return rs[i] * sin(invas[i]: number(rad))
}) })
// Extrude the gear body // Extrude the gear body

View File

@ -29,7 +29,7 @@ sketch001 = startSketchOn(XZ)
) )
|> yLine(endAbsolute = -templateThickness, tag = $seg03) |> yLine(endAbsolute = -templateThickness, tag = $seg03)
|> xLine(length = templateThickness, tag = $seg07) |> xLine(length = templateThickness, tag = $seg07)
|> yLine(endAbsolute = (segEndY(seg01) + templateThickness) / 2 - templateThickness, tag = $seg02) |> yLine(endAbsolute = (segEndY(seg01) + templateThickness) / 2_ - templateThickness, tag = $seg02)
|> xLine(endAbsolute = segEndX(seg03) + minClampingDistance, tag = $seg06) |> xLine(endAbsolute = segEndX(seg03) + minClampingDistance, tag = $seg06)
|> yLine(length = templateThickness * 2, tag = $seg08) |> yLine(length = templateThickness * 2, tag = $seg08)
|> xLine(endAbsolute = segEndX(seg02) + 0, tag = $seg05) |> xLine(endAbsolute = segEndX(seg02) + 0, tag = $seg05)

View File

@ -5,7 +5,7 @@ const t = 0.005 // taper factor [0-1)
// Defines how to modify each layer of the vase. // Defines how to modify each layer of the vase.
// Each replica is shifted up the Z axis, and has a smoothly-varying radius // Each replica is shifted up the Z axis, and has a smoothly-varying radius
fn transform = (replicaId) => { fn transform = (replicaId) => {
let scale = r * abs(1 - (t * replicaId)) * (5 + cos(replicaId / 8)) let scale = r * abs(1 - (t * replicaId)) * (5 + cos((replicaId / 8): number(rad)))
return { return {
translate: [0, 0, replicaId * 10], translate: [0, 0, replicaId * 10],
scale: [scale, scale, 0], scale: [scale, scale, 0],

View File

@ -180,10 +180,6 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
) )
.await?; .await?;
// Reset to the default units. Modules assume the engine starts in the
// default state.
self.set_units(Default::default(), source_range, id_generator).await?;
// Flush the batch queue, so clear is run right away. // Flush the batch queue, so clear is run right away.
// Otherwise the hooks below won't work. // Otherwise the hooks below won't work.
self.flush_batch(false, source_range).await?; self.flush_batch(false, source_range).await?;
@ -298,23 +294,6 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
Ok(()) Ok(())
} }
async fn set_units(
&self,
units: crate::UnitLength,
source_range: SourceRange,
id_generator: &mut IdGenerator,
) -> Result<(), crate::errors::KclError> {
// Before we even start executing the program, set the units.
self.batch_modeling_cmd(
id_generator.next_uuid(),
source_range,
&ModelingCmd::from(mcmd::SetSceneUnits { unit: units.into() }),
)
.await?;
Ok(())
}
/// Re-run the command to apply the settings. /// Re-run the command to apply the settings.
async fn reapply_settings( async fn reapply_settings(
&self, &self,

View File

@ -3,11 +3,7 @@ use std::collections::HashMap;
use async_recursion::async_recursion; use async_recursion::async_recursion;
use indexmap::IndexMap; use indexmap::IndexMap;
use super::{ use super::{cad_op::Group, kcl_value::TypeDef, types::PrimitiveType};
cad_op::Group,
kcl_value::TypeDef,
types::{PrimitiveType, CHECK_NUMERIC_TYPES},
};
use crate::{ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
execution::{ execution::{
@ -64,14 +60,6 @@ impl ExecutorContext {
if exec_state.mod_local.settings.update_from_annotation(annotation)? { if exec_state.mod_local.settings.update_from_annotation(annotation)? {
exec_state.mod_local.explicit_length_units = true; exec_state.mod_local.explicit_length_units = true;
} }
let new_units = exec_state.length_unit();
self.engine
.set_units(
new_units.into(),
annotation.as_source_range(),
exec_state.id_generator(),
)
.await?;
} else { } else {
exec_state.err(CompilationError::err( exec_state.err(CompilationError::err(
annotation.as_source_range(), annotation.as_source_range(),
@ -873,7 +861,10 @@ impl Node<MemberExpression> {
source_ranges: vec![self.clone().into()], source_ranges: vec![self.clone().into()],
})) }))
} }
(KclValue::MixedArray { value: arr, meta: _ }, Property::UInt(index)) => { (
KclValue::MixedArray { value: arr, .. } | KclValue::HomArray { value: arr, .. },
Property::UInt(index),
) => {
let value_of_arr = arr.get(index); let value_of_arr = arr.get(index);
if let Some(value) = value_of_arr { if let Some(value) = value_of_arr {
Ok(value.to_owned()) Ok(value.to_owned())
@ -884,7 +875,7 @@ impl Node<MemberExpression> {
})) }))
} }
} }
(KclValue::MixedArray { .. }, p) => { (KclValue::MixedArray { .. } | KclValue::HomArray { .. }, p) => {
let t = p.type_name(); let t = p.type_name();
let article = article_for(t); let article = article_for(t);
Err(KclError::Semantic(KclErrorDetails { Err(KclError::Semantic(KclErrorDetails {
@ -1051,7 +1042,7 @@ impl Node<BinaryExpression> {
BinaryOperator::Pow => KclValue::Number { BinaryOperator::Pow => KclValue::Number {
value: left.n.powf(right.n), value: left.n.powf(right.n),
meta, meta,
ty: NumericType::Unknown, ty: exec_state.current_default_units(),
}, },
BinaryOperator::Neq => { BinaryOperator::Neq => {
let (l, r, ty) = NumericType::combine_eq(left, right); let (l, r, ty) = NumericType::combine_eq(left, right);
@ -1090,7 +1081,7 @@ impl Node<BinaryExpression> {
} }
fn warn_on_unknown(&self, ty: &NumericType, verb: &str, exec_state: &mut ExecState) { fn warn_on_unknown(&self, ty: &NumericType, verb: &str, exec_state: &mut ExecState) {
if *CHECK_NUMERIC_TYPES && ty == &NumericType::Unknown { if ty == &NumericType::Unknown {
// TODO suggest how to fix this // TODO suggest how to fix this
exec_state.warn(CompilationError::err( exec_state.warn(CompilationError::err(
self.as_source_range(), self.as_source_range(),
@ -1999,11 +1990,39 @@ fn assign_args_to_params(
for (index, param) in function_expression.params.iter().enumerate() { for (index, param) in function_expression.params.iter().enumerate() {
if let Some(arg) = args.get(index) { if let Some(arg) = args.get(index) {
// Argument was provided. // Argument was provided.
exec_state.mut_stack().add(
param.identifier.name.clone(), if let Some(ty) = &param.type_ {
arg.value.clone(), let value = arg
(&param.identifier).into(), .value
)?; .coerce(
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range).unwrap(),
exec_state,
)
.map_err(|e| {
let mut message = format!(
"Argument requires a value with type `{}`, but found {}",
ty.inner,
arg.value.human_friendly_type(),
);
if let Some(ty) = e.explicit_coercion {
// TODO if we have access to the AST for the argument we could choose which example to suggest.
message = format!("{message}\n\nYou may need to add information about the type of the argument, for example:\n using a numeric suffix: `42{ty}`\n or using type ascription: `foo(): number({ty})`");
}
KclError::Semantic(KclErrorDetails {
message,
source_ranges: vec![arg.source_range],
})
})?;
exec_state
.mut_stack()
.add(param.identifier.name.clone(), value, (&param.identifier).into())?;
} else {
exec_state.mut_stack().add(
param.identifier.name.clone(),
arg.value.clone(),
(&param.identifier).into(),
)?;
}
} else { } else {
// Argument was not provided. // Argument was not provided.
if let Some(ref default_val) = param.default_value { if let Some(ref default_val) = param.default_value {

View File

@ -10,14 +10,13 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
errors::KclError, errors::KclError,
execution::{types::NumericType, ArtifactId, ExecState, Metadata, TagEngineInfo, TagIdentifier, UnitLen}, execution::{
types::NumericType, ArtifactId, ExecState, ExecutorContext, Metadata, TagEngineInfo, TagIdentifier, UnitLen,
},
parsing::ast::types::{Node, NodeRef, TagDeclarator, TagNode}, parsing::ast::types::{Node, NodeRef, TagDeclarator, TagNode},
std::{args::TyF64, sketch::PlaneData}, std::{args::TyF64, sketch::PlaneData},
}; };
use super::ExecutorContext;
type Point2D = kcmc::shared::Point2d<f64>;
type Point3D = kcmc::shared::Point3d<f64>; type Point3D = kcmc::shared::Point3d<f64>;
/// A geometry. /// A geometry.
@ -265,7 +264,6 @@ pub struct Plane {
pub y_axis: Point3d, pub y_axis: Point3d,
/// The z-axis (normal). /// The z-axis (normal).
pub z_axis: Point3d, pub z_axis: Point3d,
pub units: UnitLen,
#[serde(skip)] #[serde(skip)]
pub meta: Vec<Metadata>, pub meta: Vec<Metadata>,
} }
@ -287,6 +285,8 @@ impl Plane {
x: 1.0, x: 1.0,
y: 0.0, y: 0.0,
z: 0.0, z: 0.0,
// TODO axes must be normalized, so maybe these should all be count
// rather than mm?
units: UnitLen::Mm, units: UnitLen::Mm,
}, },
y_axis: y_axis:
@ -483,7 +483,6 @@ impl Plane {
y_axis: Point3d::new(0.0, 1.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), z_axis: Point3d::new(0.0, 0.0, 1.0, UnitLen::Mm),
value: PlaneType::XY, value: PlaneType::XY,
units: exec_state.length_unit(),
meta: vec![], meta: vec![],
}, },
PlaneData::NegXY => Plane { PlaneData::NegXY => Plane {
@ -494,7 +493,6 @@ impl Plane {
y_axis: Point3d::new(0.0, 1.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), z_axis: Point3d::new(0.0, 0.0, -1.0, UnitLen::Mm),
value: PlaneType::XY, value: PlaneType::XY,
units: exec_state.length_unit(),
meta: vec![], meta: vec![],
}, },
PlaneData::XZ => Plane { PlaneData::XZ => Plane {
@ -505,7 +503,6 @@ impl Plane {
y_axis: Point3d::new(0.0, 0.0, 1.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), z_axis: Point3d::new(0.0, -1.0, 0.0, UnitLen::Mm),
value: PlaneType::XZ, value: PlaneType::XZ,
units: exec_state.length_unit(),
meta: vec![], meta: vec![],
}, },
PlaneData::NegXZ => Plane { PlaneData::NegXZ => Plane {
@ -516,7 +513,6 @@ impl Plane {
y_axis: Point3d::new(0.0, 0.0, 1.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), z_axis: Point3d::new(0.0, 1.0, 0.0, UnitLen::Mm),
value: PlaneType::XZ, value: PlaneType::XZ,
units: exec_state.length_unit(),
meta: vec![], meta: vec![],
}, },
PlaneData::YZ => Plane { PlaneData::YZ => Plane {
@ -527,7 +523,6 @@ impl Plane {
y_axis: Point3d::new(0.0, 0.0, 1.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), z_axis: Point3d::new(1.0, 0.0, 0.0, UnitLen::Mm),
value: PlaneType::YZ, value: PlaneType::YZ,
units: exec_state.length_unit(),
meta: vec![], meta: vec![],
}, },
PlaneData::NegYZ => Plane { PlaneData::NegYZ => Plane {
@ -538,7 +533,6 @@ impl Plane {
y_axis: Point3d::new(0.0, 0.0, 1.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), z_axis: Point3d::new(-1.0, 0.0, 0.0, UnitLen::Mm),
value: PlaneType::YZ, value: PlaneType::YZ,
units: exec_state.length_unit(),
meta: vec![], meta: vec![],
}, },
PlaneData::Plane { PlaneData::Plane {
@ -556,7 +550,6 @@ impl Plane {
y_axis, y_axis,
z_axis, z_axis,
value: PlaneType::Custom, value: PlaneType::Custom,
units: exec_state.length_unit(),
meta: vec![], meta: vec![],
} }
} }
@ -713,12 +706,6 @@ impl SketchSurface {
SketchSurface::Face(face) => face.z_axis, SketchSurface::Face(face) => face.z_axis,
} }
} }
pub(crate) fn units(&self) -> UnitLen {
match self {
SketchSurface::Plane(plane) => plane.units,
SketchSurface::Face(face) => face.units,
}
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -787,7 +774,8 @@ impl Sketch {
return Ok(Point2d::new(self.start.to[0], self.start.to[1], self.start.units)); return Ok(Point2d::new(self.start.to[0], self.start.to[1], self.start.units));
}; };
Ok(path.get_to().into()) let to = path.get_base().to;
Ok(Point2d::new(to[0], to[1], path.get_base().units))
} }
pub(crate) fn get_tangential_info_from_paths(&self) -> GetTangentialInfoFromPathsResult { pub(crate) fn get_tangential_info_from_paths(&self) -> GetTangentialInfoFromPathsResult {
@ -829,6 +817,10 @@ impl Solid {
pub(crate) fn get_all_edge_cut_ids(&self) -> impl Iterator<Item = uuid::Uuid> + '_ { pub(crate) fn get_all_edge_cut_ids(&self) -> impl Iterator<Item = uuid::Uuid> + '_ {
self.edge_cuts.iter().map(|foc| foc.id()) self.edge_cuts.iter().map(|foc| foc.id())
} }
pub(crate) fn height_in_mm(&self) -> f64 {
self.units.adjust_to(self.height, UnitLen::Mm).0
}
} }
/// A fillet or a chamfer. /// A fillet or a chamfer.
@ -889,28 +881,6 @@ pub struct Point2d {
pub units: UnitLen, pub units: UnitLen,
} }
impl From<[TyF64; 2]> for Point2d {
fn from(p: [TyF64; 2]) -> Self {
Self {
x: p[0].n,
y: p[1].n,
units: p[0].ty.expect_length(),
}
}
}
impl From<Point2d> for [f64; 2] {
fn from(p: Point2d) -> Self {
[p.x, p.y]
}
}
impl From<Point2d> for Point2D {
fn from(p: Point2d) -> Self {
Self { x: p.x, y: p.y }
}
}
impl Point2d { impl Point2d {
pub const ZERO: Self = Self { pub const ZERO: Self = Self {
x: 0.0, x: 0.0,
@ -921,6 +891,18 @@ impl Point2d {
pub fn new(x: f64, y: f64, units: UnitLen) -> Self { pub fn new(x: f64, y: f64, units: UnitLen) -> Self {
Self { x, y, units } Self { x, y, units }
} }
pub fn into_x(self) -> TyF64 {
TyF64::new(self.x, self.units.into())
}
pub fn into_y(self) -> TyF64 {
TyF64::new(self.y, self.units.into())
}
pub fn ignore_units(self) -> [f64; 2] {
[self.x, self.y]
}
} }
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy, ts_rs::TS, JsonSchema, Default)] #[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy, ts_rs::TS, JsonSchema, Default)]
@ -968,9 +950,9 @@ impl From<Point3d> for Point3D {
impl From<Point3d> for kittycad_modeling_cmds::shared::Point3d<LengthUnit> { impl From<Point3d> for kittycad_modeling_cmds::shared::Point3d<LengthUnit> {
fn from(p: Point3d) -> Self { fn from(p: Point3d) -> Self {
Self { Self {
x: LengthUnit(p.x), x: LengthUnit(p.units.adjust_to(p.x, UnitLen::Mm).0),
y: LengthUnit(p.y), y: LengthUnit(p.units.adjust_to(p.y, UnitLen::Mm).0),
z: LengthUnit(p.z), z: LengthUnit(p.units.adjust_to(p.z, UnitLen::Mm).0),
} }
} }
} }
@ -1318,9 +1300,9 @@ impl Path {
ccw: *ccw, ccw: *ccw,
}, },
Path::ArcThreePoint { p1, p2, p3, .. } => { Path::ArcThreePoint { p1, p2, p3, .. } => {
let circle_center = crate::std::utils::calculate_circle_from_3_points([*p1, *p2, *p3]); let circle = crate::std::utils::calculate_circle_from_3_points([*p1, *p2, *p3]);
GetTangentialInfoFromPathsResult::Arc { GetTangentialInfoFromPathsResult::Arc {
center: circle_center.center, center: circle.center,
ccw: crate::std::utils::is_points_ccw(&[*p1, *p2, *p3]) > 0, ccw: crate::std::utils::is_points_ccw(&[*p1, *p2, *p3]) > 0,
} }
} }
@ -1332,14 +1314,13 @@ impl Path {
radius: *radius, radius: *radius,
}, },
Path::CircleThreePoint { p1, p2, p3, .. } => { Path::CircleThreePoint { p1, p2, p3, .. } => {
let circle_center = crate::std::utils::calculate_circle_from_3_points([*p1, *p2, *p3]); let circle = 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[0], circle.center[1]];
let center_point = [circle_center.center[0], circle_center.center[1]];
GetTangentialInfoFromPathsResult::Circle { GetTangentialInfoFromPathsResult::Circle {
center: center_point, center: center_point,
// Note: a circle is always ccw regardless of the order of points // Note: a circle is always ccw regardless of the order of points
ccw: true, ccw: true,
radius, radius: circle.radius,
} }
} }
Path::ToPoint { .. } | Path::Horizontal { .. } | Path::AngledLineTo { .. } | Path::Base { .. } => { Path::ToPoint { .. } | Path::Horizontal { .. } | Path::AngledLineTo { .. } | Path::Base { .. } => {

View File

@ -18,16 +18,6 @@ use crate::{
CompilationError, SourceRange, CompilationError, SourceRange,
}; };
lazy_static::lazy_static! {
pub(crate) static ref CHECK_NUMERIC_TYPES: bool = {
let env_var = std::env::var("ZOO_NUM_TYS");
let Ok(env_var) = env_var else {
return false;
};
!env_var.is_empty()
};
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum RuntimeType { pub enum RuntimeType {
Primitive(PrimitiveType), Primitive(PrimitiveType),
@ -62,6 +52,10 @@ impl RuntimeType {
RuntimeType::Primitive(PrimitiveType::Solid) RuntimeType::Primitive(PrimitiveType::Solid)
} }
pub fn helix() -> Self {
RuntimeType::Primitive(PrimitiveType::Helix)
}
pub fn plane() -> Self { pub fn plane() -> Self {
RuntimeType::Primitive(PrimitiveType::Plane) RuntimeType::Primitive(PrimitiveType::Plane)
} }
@ -94,6 +88,10 @@ impl RuntimeType {
)))) ))))
} }
pub fn known_length(len: UnitLen) -> Self {
RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Length(len))))
}
pub fn angle() -> Self { pub fn angle() -> Self {
RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Angle( RuntimeType::Primitive(PrimitiveType::Number(NumericType::Known(UnitType::Angle(
UnitAngle::Unknown, UnitAngle::Unknown,
@ -370,6 +368,7 @@ impl fmt::Display for PrimitiveType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
PrimitiveType::Number(NumericType::Known(unit)) => write!(f, "number({unit})"), PrimitiveType::Number(NumericType::Known(unit)) => write!(f, "number({unit})"),
PrimitiveType::Number(NumericType::Unknown) => write!(f, "number(unknown units)"),
PrimitiveType::Number(_) => write!(f, "number"), PrimitiveType::Number(_) => write!(f, "number"),
PrimitiveType::String => write!(f, "string"), PrimitiveType::String => write!(f, "string"),
PrimitiveType::Boolean => write!(f, "bool"), PrimitiveType::Boolean => write!(f, "bool"),
@ -427,13 +426,61 @@ impl NumericType {
NumericType::Known(UnitType::Angle(UnitAngle::Degrees)) NumericType::Known(UnitType::Angle(UnitAngle::Degrees))
} }
/// Combine two types when we expect them to be equal. pub fn expect_default_length(&self) -> Self {
match self {
NumericType::Default { len, .. } => NumericType::Known(UnitType::Length(*len)),
_ => unreachable!(),
}
}
pub fn expect_default_angle(&self) -> Self {
match self {
NumericType::Default { angle, .. } => NumericType::Known(UnitType::Angle(*angle)),
_ => unreachable!(),
}
}
/// Combine two types when we expect them to be equal, erring on the side of less coercion. To be
/// precise, only adjusting one number or the other when they are of known types.
///
/// This combinator function is suitable for comparisons or arithmetic where uncertainty should
/// be handled by the user.
pub fn combine_eq(a: TyF64, b: TyF64) -> (f64, f64, NumericType) { pub fn combine_eq(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
use NumericType::*; use NumericType::*;
match (a.ty, b.ty) { match (a.ty, b.ty) {
(at, bt) if at == bt => (a.n, b.n, at), (at, bt) if at == bt => (a.n, b.n, at),
(at, Any) => (a.n, b.n, at), (at, Any) => (a.n, b.n, at),
(Any, bt) => (a.n, b.n, bt), (Any, bt) => (a.n, b.n, bt),
(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(UnitType::Count), Default { .. }) | (Default { .. }, Known(UnitType::Count)) => {
(a.n, b.n, Known(UnitType::Count))
}
(t @ Known(UnitType::Length(l1)), Default { len: l2, .. }) if l1 == l2 => (a.n, b.n, t),
(Default { len: l1, .. }, t @ Known(UnitType::Length(l2))) if l1 == l2 => (a.n, b.n, t),
(t @ Known(UnitType::Angle(a1)), Default { angle: a2, .. }) if a1 == a2 => (a.n, b.n, t),
(Default { angle: a1, .. }, t @ Known(UnitType::Angle(a2))) if a1 == a2 => (a.n, b.n, t),
_ => (a.n, b.n, Unknown),
}
}
/// Combine two types when we expect them to be equal, erring on the side of more coercion. Including adjusting when
/// we are certain about only one type.
///
/// This combinator function is suitable for situations where the user would almost certainly want the types to be
/// coerced together, for example two arguments to the same function or two numbers in an array being used as a point.
///
/// Prefer to use `combine_eq` if possible since using that prioritises correctness over ergonomics.
pub fn combine_eq_coerce(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
use NumericType::*;
match (a.ty, b.ty) {
(at, bt) if at == bt => (a.n, b.n, at),
(at, Any) => (a.n, b.n, at),
(Any, bt) => (a.n, b.n, bt),
(Default { .. }, Default { .. }) | (_, Unknown) | (Unknown, _) => (a.n, b.n, Unknown), (Default { .. }, Default { .. }) | (_, Unknown) | (Unknown, _) => (a.n, b.n, Unknown),
// Known types and compatible, but needs adjustment. // Known types and compatible, but needs adjustment.
@ -458,12 +505,9 @@ impl NumericType {
pub fn combine_eq_array(input: &[TyF64]) -> (Vec<f64>, NumericType) { pub fn combine_eq_array(input: &[TyF64]) -> (Vec<f64>, NumericType) {
use NumericType::*; use NumericType::*;
let result = input.iter().map(|t| t.n).collect();
let mut result = input.iter().map(|t| t.n).collect();
let mut ty = Any; let mut ty = Any;
// Invariant mismatch is true => ty is fully known
let mut mismatch = false;
for i in input { for i in input {
if i.ty == Any || ty == i.ty { if i.ty == Any || ty == i.ty {
continue; continue;
@ -475,58 +519,24 @@ impl NumericType {
} }
(_, Unknown) | (Default { .. }, Default { .. }) => return (result, Unknown), (_, Unknown) | (Default { .. }, Default { .. }) => return (result, Unknown),
// Known types and compatible, but needs adjustment.
(Known(UnitType::Length(_)), Known(UnitType::Length(_)))
| (Known(UnitType::Angle(_)), Known(UnitType::Angle(_))) => {
mismatch = true;
}
// Known but incompatible.
(Known(_), Known(_)) => return (result, Unknown),
// Known and unknown, no adjustment for counting numbers.
(Known(UnitType::Count), Default { .. }) | (Default { .. }, Known(UnitType::Count)) => { (Known(UnitType::Count), Default { .. }) | (Default { .. }, Known(UnitType::Count)) => {
ty = Known(UnitType::Count); ty = Known(UnitType::Count);
} }
(Known(UnitType::Length(l1)), Default { len: l2, .. }) => { (Known(UnitType::Length(l1)), Default { len: l2, .. }) if l1 == l2 => {}
mismatch |= l1 != l2; (Known(UnitType::Angle(a1)), Default { angle: a2, .. }) if a1 == a2 => {}
}
(Known(UnitType::Angle(a1)), Default { angle: a2, .. }) => {
mismatch |= a1 != a2;
}
(Default { len: l1, .. }, Known(UnitType::Length(l2))) => { (Default { len: l1, .. }, Known(UnitType::Length(l2))) if l1 == l2 => {
mismatch |= l1 != l2;
ty = Known(UnitType::Length(*l2)); ty = Known(UnitType::Length(*l2));
} }
(Default { angle: a1, .. }, Known(UnitType::Angle(a2))) => { (Default { angle: a1, .. }, Known(UnitType::Angle(a2))) if a1 == a2 => {
mismatch |= a1 != a2;
ty = Known(UnitType::Angle(*a2)); ty = Known(UnitType::Angle(*a2));
} }
(Unknown, _) | (_, Any) => unreachable!(), _ => return (result, Unknown),
} }
} }
if !mismatch {
return (result, ty);
}
result = result
.into_iter()
.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).0
}
(Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2)) | Default { angle: a2, .. }) => {
a2.adjust_to(n, *a1).0
}
_ => unreachable!(),
})
.collect();
(result, ty) (result, ty)
} }
@ -534,11 +544,11 @@ impl NumericType {
pub fn combine_mul(a: TyF64, b: TyF64) -> (f64, f64, NumericType) { pub fn combine_mul(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
use NumericType::*; use NumericType::*;
match (a.ty, b.ty) { match (a.ty, b.ty) {
(at @ Default { .. }, bt @ Default { .. }) if at != bt => (a.n, b.n, Unknown), (at @ Default { .. }, bt @ Default { .. }) if at == bt => (a.n, b.n, at),
(Default { .. }, Default { .. }) => (a.n, b.n, Unknown),
(Known(UnitType::Count), bt) => (a.n, b.n, bt), (Known(UnitType::Count), bt) => (a.n, b.n, bt),
(at, Known(UnitType::Count)) => (a.n, b.n, at), (at, Known(UnitType::Count)) => (a.n, b.n, at),
(Default { .. }, bt) => (a.n, b.n, bt), (at @ Known(_), Default { .. }) | (Default { .. }, at @ Known(_)) => (a.n, b.n, at),
(at, Default { .. }) => (a.n, b.n, at),
(Any, Any) => (a.n, b.n, Any), (Any, Any) => (a.n, b.n, Any),
_ => (a.n, b.n, Unknown), _ => (a.n, b.n, Unknown),
} }
@ -552,18 +562,7 @@ impl NumericType {
(at, bt) if at == bt => (a.n, b.n, Known(UnitType::Count)), (at, bt) if at == bt => (a.n, b.n, Known(UnitType::Count)),
(Default { .. }, Default { .. }) => (a.n, b.n, Unknown), (Default { .. }, Default { .. }) => (a.n, b.n, Unknown),
(at, Known(UnitType::Count) | Any) => (a.n, b.n, at), (at, Known(UnitType::Count) | Any) => (a.n, b.n, at),
(Known(UnitType::Length(l1)), Known(UnitType::Length(l2))) => { (at @ Known(_), Default { .. }) => (a.n, b.n, at),
(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).0, Known(UnitType::Count))
}
(Default { len: l1, .. }, Known(UnitType::Length(l2))) => {
(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).0, b.n, Known(UnitType::Count))
}
(Known(UnitType::Count), _) => (a.n, b.n, Known(UnitType::Count)), (Known(UnitType::Count), _) => (a.n, b.n, Known(UnitType::Count)),
_ => (a.n, b.n, Unknown), _ => (a.n, b.n, Unknown),
} }
@ -608,9 +607,18 @@ impl NumericType {
} }
} }
fn is_unknown(&self) -> bool {
matches!(
self,
NumericType::Unknown
| NumericType::Known(UnitType::Angle(UnitAngle::Unknown))
| NumericType::Known(UnitType::Length(UnitLen::Unknown))
)
}
fn example_ty(&self) -> Option<String> { fn example_ty(&self) -> Option<String> {
match self { match self {
Self::Known(t) => Some(t.to_string()), Self::Known(t) if !self.is_unknown() => Some(t.to_string()),
Self::Default { len, .. } => Some(len.to_string()), Self::Default { len, .. } => Some(len.to_string()),
_ => None, _ => None,
} }
@ -621,10 +629,6 @@ impl NumericType {
return Err(val.into()); return Err(val.into());
}; };
if !*CHECK_NUMERIC_TYPES {
return Ok(val.clone());
}
if ty.subtype(self) { if ty.subtype(self) {
return Ok(KclValue::Number { return Ok(KclValue::Number {
value: *value, value: *value,
@ -775,10 +779,10 @@ pub enum UnitLen {
} }
impl UnitLen { impl UnitLen {
fn adjust_to(self, value: f64, to: UnitLen) -> (f64, UnitLen) { pub fn adjust_to(self, value: f64, to: UnitLen) -> (f64, UnitLen) {
use UnitLen::*; use UnitLen::*;
if !*CHECK_NUMERIC_TYPES || self == to { if self == to {
return (value, to); return (value, to);
} }
@ -898,15 +902,11 @@ pub enum UnitAngle {
} }
impl UnitAngle { impl UnitAngle {
fn adjust_to(self, value: f64, to: UnitAngle) -> (f64, UnitAngle) { pub fn adjust_to(self, value: f64, to: UnitAngle) -> (f64, UnitAngle) {
use std::f64::consts::PI; use std::f64::consts::PI;
use UnitAngle::*; use UnitAngle::*;
if !*CHECK_NUMERIC_TYPES {
return (value, to);
}
if to == Unknown { if to == Unknown {
return (value, self); return (value, self);
} }
@ -1057,8 +1057,6 @@ impl KclValue {
y_axis, y_axis,
z_axis, z_axis,
value: super::PlaneType::Uninit, value: super::PlaneType::Uninit,
// TODO use length unit from origin
units: exec_state.length_unit(),
meta: meta.clone(), meta: meta.clone(),
}; };
@ -1952,10 +1950,6 @@ mod test {
assert_coerce_results(&unknown, &NumericType::Any.into(), &unknown, &mut exec_state); assert_coerce_results(&unknown, &NumericType::Any.into(), &unknown, &mut exec_state);
assert_coerce_results(&default, &NumericType::Any.into(), &default, &mut exec_state); assert_coerce_results(&default, &NumericType::Any.into(), &default, &mut exec_state);
if !*CHECK_NUMERIC_TYPES {
return;
}
assert_eq!( assert_eq!(
default default
.coerce( .coerce(
@ -2068,20 +2062,14 @@ u = min(3rad, 4in)
"#; "#;
let result = parse_execute(program).await.unwrap(); let result = parse_execute(program).await.unwrap();
if *CHECK_NUMERIC_TYPES { assert_eq!(result.exec_state.errors().len(), 5);
assert_eq!(result.exec_state.errors().len(), 3);
} else {
assert!(result.exec_state.errors().is_empty());
}
assert_value_and_type("a", &result, 9.0, NumericType::default()); assert_value_and_type("a", &result, 9.0, NumericType::default());
assert_value_and_type("b", &result, 3.0, NumericType::default()); assert_value_and_type("b", &result, 3.0, NumericType::default());
assert_value_and_type("c", &result, 13.0, NumericType::mm()); assert_value_and_type("c", &result, 13.0, NumericType::mm());
assert_value_and_type("d", &result, 13.0, NumericType::mm()); assert_value_and_type("d", &result, 13.0, NumericType::mm());
assert_value_and_type("e", &result, 13.0, NumericType::mm()); assert_value_and_type("e", &result, 13.0, NumericType::mm());
if *CHECK_NUMERIC_TYPES { assert_value_and_type("f", &result, 5.0, NumericType::mm());
assert_value_and_type("f", &result, 5.0, NumericType::mm());
}
assert_value_and_type("g", &result, 20.0, NumericType::default()); assert_value_and_type("g", &result, 20.0, NumericType::default());
assert_value_and_type("h", &result, 20.0, NumericType::mm()); assert_value_and_type("h", &result, 20.0, NumericType::mm());
@ -2091,16 +2079,14 @@ u = min(3rad, 4in)
assert_value_and_type("l", &result, 0.0, NumericType::default()); assert_value_and_type("l", &result, 0.0, NumericType::default());
assert_value_and_type("m", &result, 2.0, NumericType::count()); assert_value_and_type("m", &result, 2.0, NumericType::count());
if *CHECK_NUMERIC_TYPES { assert_value_and_type("n", &result, 5.0, NumericType::Unknown);
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("p", &result, 1.0, NumericType::count());
assert_value_and_type("q", &result, 2.0, NumericType::Known(UnitType::Length(UnitLen::Inches))); assert_value_and_type("q", &result, 2.0, NumericType::Known(UnitType::Length(UnitLen::Inches)));
assert_value_and_type("r", &result, 0.0, NumericType::default()); assert_value_and_type("r", &result, 0.0, NumericType::default());
assert_value_and_type("s", &result, -42.0, NumericType::mm()); assert_value_and_type("s", &result, -42.0, NumericType::mm());
assert_value_and_type("t", &result, 3.0, NumericType::Known(UnitType::Length(UnitLen::Inches))); assert_value_and_type("t", &result, 3.0, NumericType::Unknown);
assert_value_and_type("u", &result, 3.0, NumericType::Unknown); assert_value_and_type("u", &result, 3.0, NumericType::Unknown);
} }
@ -2114,7 +2100,24 @@ b = 180 / PI * a + 360
let result = parse_execute(program).await.unwrap(); let result = parse_execute(program).await.unwrap();
assert_value_and_type("a", &result, 1.0, NumericType::radians()); assert_value_and_type("a", &result, 1.0, NumericType::radians());
// TODO type is not ideal assert_value_and_type("b", &result, 417.0, NumericType::Unknown);
assert_value_and_type("b", &result, 417.0, NumericType::radians()); }
#[tokio::test(flavor = "multi_thread")]
async fn cos_coercions() {
let program = r#"
a = cos(toRadians(30))
b = 3 / a
c = cos(30deg)
d = cos(30)
"#;
let result = parse_execute(program).await.unwrap();
assert_eq!(result.exec_state.errors().len(), 1);
assert_value_and_type("a", &result, 1.0, NumericType::count());
assert_value_and_type("b", &result, 3.0, NumericType::default());
assert_value_and_type("c", &result, 1.0, NumericType::count());
assert_value_and_type("d", &result, 0.0, NumericType::count());
} }
} }

View File

@ -1,7 +1,7 @@
use sha2::{Digest as DigestTrait, Sha256}; use sha2::{Digest as DigestTrait, Sha256};
use crate::parsing::ast::types::{ use crate::parsing::ast::types::{
Annotation, ArrayExpression, ArrayRangeExpression, Ascription, BinaryExpression, BinaryPart, BodyItem, Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, BinaryExpression, BinaryPart, BodyItem,
CallExpression, CallExpressionKw, DefaultParamVal, ElseIf, Expr, ExpressionStatement, FunctionExpression, CallExpression, CallExpressionKw, DefaultParamVal, ElseIf, Expr, ExpressionStatement, FunctionExpression,
Identifier, IfExpression, ImportItem, ImportSelector, ImportStatement, ItemVisibility, KclNone, LabelledExpression, Identifier, IfExpression, ImportItem, ImportSelector, ImportStatement, ItemVisibility, KclNone, LabelledExpression,
Literal, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Name, ObjectExpression, ObjectProperty, Literal, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Name, ObjectExpression, ObjectProperty,
@ -464,7 +464,7 @@ impl LabelledExpression {
}); });
} }
impl Ascription { impl AscribedExpression {
compute_digest!(|slf, hasher| { compute_digest!(|slf, hasher| {
hasher.update(slf.expr.compute_digest()); hasher.update(slf.expr.compute_digest());
hasher.update(slf.ty.compute_digest()); hasher.update(slf.ty.compute_digest());

View File

@ -819,7 +819,7 @@ pub enum Expr {
UnaryExpression(BoxNode<UnaryExpression>), UnaryExpression(BoxNode<UnaryExpression>),
IfExpression(BoxNode<IfExpression>), IfExpression(BoxNode<IfExpression>),
LabelledExpression(BoxNode<LabelledExpression>), LabelledExpression(BoxNode<LabelledExpression>),
AscribedExpression(BoxNode<Ascription>), AscribedExpression(BoxNode<AscribedExpression>),
None(Node<KclNone>), None(Node<KclNone>),
} }
@ -1092,7 +1092,7 @@ impl LabelledExpression {
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)] #[ts(export)]
#[serde(tag = "type")] #[serde(tag = "type")]
pub struct Ascription { pub struct AscribedExpression {
pub expr: Expr, pub expr: Expr,
pub ty: Node<Type>, pub ty: Node<Type>,
@ -1101,12 +1101,12 @@ pub struct Ascription {
pub digest: Option<Digest>, pub digest: Option<Digest>,
} }
impl Ascription { impl AscribedExpression {
pub(crate) fn new(expr: Expr, ty: Node<Type>) -> Node<Ascription> { pub(crate) fn new(expr: Expr, ty: Node<Type>) -> Node<AscribedExpression> {
let start = expr.start(); let start = expr.start();
let end = ty.end; let end = ty.end;
let module_id = expr.module_id(); let module_id = expr.module_id();
Node::new(Ascription { expr, ty, digest: None }, start, end, module_id) Node::new(AscribedExpression { expr, ty, digest: None }, start, end, module_id)
} }
} }
@ -3080,7 +3080,7 @@ impl PipeExpression {
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)] #[ts(export)]
#[serde(tag = "type")] #[serde(tag = "p_type")]
pub enum PrimitiveType { pub enum PrimitiveType {
/// A string type. /// A string type.
String, String,

View File

@ -14,7 +14,7 @@ use winnow::{
}; };
use super::{ use super::{
ast::types::{Ascription, ImportPath, LabelledExpression}, ast::types::{AscribedExpression, ImportPath, LabelledExpression},
token::{NumericSuffix, RESERVED_WORDS}, token::{NumericSuffix, RESERVED_WORDS},
DeprecationKind, DeprecationKind,
}; };
@ -530,13 +530,6 @@ pub(crate) fn unsigned_number_literal(i: &mut TokenSlice) -> PResult<Node<Litera
CompilationError::fatal(token.as_source_range(), format!("Invalid float: {}", token.value)) CompilationError::fatal(token.as_source_range(), format!("Invalid float: {}", token.value))
})?; })?;
if token.numeric_suffix().is_some() {
ParseContext::warn(CompilationError::err(
(&token).into(),
"Unit of Measure suffixes are experimental and currently do nothing.",
));
}
Ok(( Ok((
LiteralValue::Number { LiteralValue::Number {
value, value,
@ -2015,9 +2008,7 @@ fn expression_but_not_pipe(i: &mut TokenSlice) -> PResult<Expr> {
let ty = opt((colon, opt(whitespace), argument_type)).parse_next(i)?; let ty = opt((colon, opt(whitespace), argument_type)).parse_next(i)?;
if let Some((_, _, ty)) = ty { if let Some((_, _, ty)) = ty {
ParseContext::warn(CompilationError::err((&ty).into(), "Type ascription is experimental.")); expr = Expr::AscribedExpression(Box::new(AscribedExpression::new(expr, ty)))
expr = Expr::AscribedExpression(Box::new(Ascription::new(expr, ty)))
} }
let label = opt(label).parse_next(i)?; let label = opt(label).parse_next(i)?;
match label { match label {
@ -2811,13 +2802,6 @@ fn primitive_type(i: &mut TokenSlice) -> PResult<Node<PrimitiveType>> {
let mut result = Node::new(PrimitiveType::Boolean, ident.start, ident.end, ident.module_id); let mut result = Node::new(PrimitiveType::Boolean, ident.start, ident.end, ident.module_id);
result.inner = PrimitiveType::primitive_from_str(&ident.name, suffix).unwrap_or(PrimitiveType::Named(ident)); result.inner = PrimitiveType::primitive_from_str(&ident.name, suffix).unwrap_or(PrimitiveType::Named(ident));
if suffix.is_some() {
ParseContext::warn(CompilationError::err(
result.as_source_range(),
"Unit of Measure types are experimental and currently do nothing.",
));
}
Ok(result) Ok(result)
} }
@ -4532,19 +4516,19 @@ export fn cos(num: number(rad)): number(_) {}"#;
fn fn_decl_uom_ty() { fn fn_decl_uom_ty() {
let some_program_string = r#"fn foo(x: number(mm)): number(_) { return 1 }"#; let some_program_string = r#"fn foo(x: number(mm)): number(_) { return 1 }"#;
let (_, errs) = assert_no_fatal(some_program_string); let (_, errs) = assert_no_fatal(some_program_string);
assert_eq!(errs.len(), 2); assert!(errs.is_empty(), "Expected no errors, found: {errs:?}");
} }
#[test] #[test]
fn error_underscore() { fn error_underscore() {
let (_, errs) = assert_no_fatal("_foo(_blah, _)"); let (_, errs) = assert_no_fatal("_foo(_blah, _)");
assert_eq!(errs.len(), 3, "found: {:#?}", errs); assert_eq!(errs.len(), 3, "found: {errs:#?}");
} }
#[test] #[test]
fn error_type_ascription() { fn error_type_ascription() {
let (_, errs) = assert_no_fatal("a + b: number"); let (_, errs) = assert_no_fatal("a + b: number");
assert_eq!(errs.len(), 1, "found: {:#?}", errs); assert!(errs.is_empty());
} }
#[test] #[test]

View File

@ -11,10 +11,7 @@ use serde::Serialize;
use crate::{ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
execution::{ execution::{types::RuntimeType, ExecState, KclValue, SolidOrImportedGeometry},
types::{NumericType, PrimitiveType, RuntimeType},
ExecState, KclValue, SolidOrImportedGeometry,
},
std::Args, std::Args,
}; };
@ -50,9 +47,8 @@ pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclVal
)?; )?;
let color: String = args.get_kw_arg("color")?; let color: String = args.get_kw_arg("color")?;
let count_ty = RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())); let metalness: Option<TyF64> = args.get_kw_arg_opt_typed("metalness", &RuntimeType::count(), exec_state)?;
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", &RuntimeType::count(), exec_state)?;
let roughness: Option<TyF64> = args.get_kw_arg_opt_typed("roughness", &count_ty, exec_state)?;
let data = AppearanceData { let data = AppearanceData {
color, color,
metalness, metalness,

View File

@ -14,7 +14,7 @@ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
execution::{ execution::{
kcl_value::FunctionSource, kcl_value::FunctionSource,
types::{NumericType, PrimitiveType, RuntimeType, UnitLen}, types::{NumericType, PrimitiveType, RuntimeType, UnitAngle, UnitLen, UnitType},
ExecState, ExecutorContext, ExtrudeSurface, Helix, KclObjectFields, KclValue, Metadata, Sketch, SketchSurface, ExecState, ExecutorContext, ExtrudeSurface, Helix, KclObjectFields, KclValue, Metadata, Sketch, SketchSurface,
Solid, TagIdentifier, Solid, TagIdentifier,
}, },
@ -88,6 +88,34 @@ impl TyF64 {
Self { n, ty } Self { n, ty }
} }
pub fn to_mm(&self) -> f64 {
self.to_length_units(UnitLen::Mm)
}
pub fn to_length_units(&self, units: UnitLen) -> f64 {
let len = match &self.ty {
NumericType::Default { len, .. } => *len,
NumericType::Known(UnitType::Length(len)) => *len,
t => unreachable!("expected length, found {t:?}"),
};
assert_ne!(len, UnitLen::Unknown);
len.adjust_to(self.n, units).0
}
pub fn to_degrees(&self) -> f64 {
let angle = match self.ty {
NumericType::Default { angle, .. } => angle,
NumericType::Known(UnitType::Angle(angle)) => angle,
_ => unreachable!(),
};
assert_ne!(angle, UnitAngle::Unknown);
angle.adjust_to(self.n, UnitAngle::Degrees).0
}
pub fn count(n: f64) -> Self { pub fn count(n: f64) -> Self {
Self { Self {
n, n,
@ -547,6 +575,29 @@ impl Args {
FromArgs::from_args(self, 0) FromArgs::from_args(self, 0)
} }
pub(crate) fn get_number_typed(&self, ty: &RuntimeType, exec_state: &mut ExecState) -> Result<f64, KclError> {
let Some(arg) = self.args.first() else {
return Err(KclError::Semantic(KclErrorDetails {
message: "Expected an argument".to_owned(),
source_ranges: vec![self.source_range],
}));
};
arg.value.coerce(ty, exec_state).map_err(|_| {
let actual_type_name = arg.value.human_friendly_type();
let message = format!(
"This function expected the input argument to be {} but it's actually of type {actual_type_name}",
ty.human_friendly_type(),
);
KclError::Semantic(KclErrorDetails {
source_ranges: arg.source_ranges(),
message,
})
})?;
Ok(TyF64::from_kcl_val(&arg.value).unwrap().n)
}
pub(crate) fn get_number_array_with_types(&self) -> Result<Vec<TyF64>, KclError> { pub(crate) fn get_number_array_with_types(&self) -> Result<Vec<TyF64>, KclError> {
let numbers = self let numbers = self
.args .args
@ -577,7 +628,7 @@ impl Args {
let mut numbers = numbers.into_iter(); let mut numbers = numbers.into_iter();
let a = numbers.next().unwrap(); let a = numbers.next().unwrap();
let b = numbers.next().unwrap(); let b = numbers.next().unwrap();
Ok(NumericType::combine_eq(a, b)) Ok(NumericType::combine_eq_coerce(a, b))
} }
pub(crate) fn get_sketches(&self, exec_state: &mut ExecState) -> Result<(Vec<Sketch>, Sketch), KclError> { pub(crate) fn get_sketches(&self, exec_state: &mut ExecState) -> Result<(Vec<Sketch>, Sketch), KclError> {
@ -657,8 +708,24 @@ impl Args {
FromArgs::from_args(self, 0) FromArgs::from_args(self, 0)
} }
pub(crate) fn get_data_and_solid(&self, exec_state: &mut ExecState) -> Result<(TyF64, Box<Solid>), KclError> { pub(crate) fn get_length_and_solid(&self, exec_state: &mut ExecState) -> Result<(TyF64, Box<Solid>), KclError> {
let data = FromArgs::from_args(self, 0)?; let Some(arg0) = self.args.first() else {
return Err(KclError::Semantic(KclErrorDetails {
message: "Expected a `number(Length)` for first argument".to_owned(),
source_ranges: vec![self.source_range],
}));
};
let val0 = arg0.value.coerce(&RuntimeType::length(), exec_state).map_err(|_| {
KclError::Type(KclErrorDetails {
message: format!(
"Expected a `number(Length)` for first argument, found {}",
arg0.value.human_friendly_type()
),
source_ranges: vec![self.source_range],
})
})?;
let data = TyF64::from_kcl_val(&val0).unwrap();
let Some(arg1) = self.args.get(1) else { let Some(arg1) = self.args.get(1) else {
return Err(KclError::Semantic(KclErrorDetails { return Err(KclError::Semantic(KclErrorDetails {
message: "Expected a solid for second argument".to_owned(), message: "Expected a solid for second argument".to_owned(),

View File

@ -137,7 +137,7 @@ async fn inner_chamfer(
ModelingCmd::from(mcmd::Solid3dFilletEdge { ModelingCmd::from(mcmd::Solid3dFilletEdge {
edge_id, edge_id,
object_id: solid.id, object_id: solid.id,
radius: LengthUnit(length.n), radius: LengthUnit(length.to_mm()),
tolerance: LengthUnit(DEFAULT_TOLERANCE), // We can let the user set this in the future. tolerance: LengthUnit(DEFAULT_TOLERANCE), // We can let the user set this in the future.
cut_type: CutType::Chamfer, cut_type: CutType::Chamfer,
}), }),

View File

@ -40,9 +40,9 @@ pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
let result = inner_extrude( let result = inner_extrude(
sketches, sketches,
length.n, length,
symmetric, symmetric,
bidirectional_length.map(|t| t.n), bidirectional_length,
tag_start, tag_start,
tag_end, tag_end,
exec_state, exec_state,
@ -164,9 +164,9 @@ pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
async fn inner_extrude( async fn inner_extrude(
sketches: Vec<Sketch>, sketches: Vec<Sketch>,
length: f64, length: TyF64,
symmetric: Option<bool>, symmetric: Option<bool>,
bidirectional_length: Option<f64>, bidirectional_length: Option<TyF64>,
tag_start: Option<TagNode>, tag_start: Option<TagNode>,
tag_end: Option<TagNode>, tag_end: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
@ -183,7 +183,7 @@ async fn inner_extrude(
})); }));
} }
let bidirection = bidirectional_length.map(LengthUnit); let bidirection = bidirectional_length.map(|l| LengthUnit(l.to_mm()));
let opposite = match (symmetric, bidirection) { let opposite = match (symmetric, bidirection) {
(Some(true), _) => Opposite::Symmetric, (Some(true), _) => Opposite::Symmetric,
@ -201,7 +201,7 @@ async fn inner_extrude(
cmd_id: id.into(), cmd_id: id.into(),
cmd: ModelingCmd::from(mcmd::Extrude { cmd: ModelingCmd::from(mcmd::Extrude {
target: sketch.id.into(), target: sketch.id.into(),
distance: LengthUnit(length), distance: LengthUnit(length.to_mm()),
faces: Default::default(), faces: Default::default(),
opposite: opposite.clone(), opposite: opposite.clone(),
}), }),
@ -213,7 +213,7 @@ async fn inner_extrude(
do_post_extrude( do_post_extrude(
sketch, sketch,
id.into(), id.into(),
length, length.clone(),
false, false,
&NamedCapTags { &NamedCapTags {
start: tag_start.as_ref(), start: tag_start.as_ref(),
@ -238,7 +238,7 @@ pub(crate) struct NamedCapTags<'a> {
pub(crate) async fn do_post_extrude<'a>( pub(crate) async fn do_post_extrude<'a>(
sketch: &Sketch, sketch: &Sketch,
solid_id: ArtifactId, solid_id: ArtifactId,
length: f64, length: TyF64,
sectional: bool, sectional: bool,
named_cap_tags: &'a NamedCapTags<'a>, named_cap_tags: &'a NamedCapTags<'a>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
@ -470,8 +470,8 @@ pub(crate) async fn do_post_extrude<'a>(
value: new_value, value: new_value,
meta: sketch.meta.clone(), meta: sketch.meta.clone(),
units: sketch.units, units: sketch.units,
height: length.to_length_units(sketch.units),
sketch, sketch,
height: length,
start_cap_id, start_cap_id,
end_cap_id, end_cap_id,
edge_cuts: vec![], edge_cuts: vec![],

View File

@ -64,14 +64,14 @@ pub(super) fn validate_unique<T: Eq + std::hash::Hash>(tags: &[(T, SourceRange)]
pub async fn fillet(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn fillet(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let solid = args.get_unlabeled_kw_arg_typed("solid", &RuntimeType::solid(), exec_state)?; 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 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 tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::length(), exec_state)?;
let tags = args.kw_arg_array_and_source::<EdgeReference>("tags")?; let tags = args.kw_arg_array_and_source::<EdgeReference>("tags")?;
let tag = args.get_kw_arg_opt("tag")?; let tag = args.get_kw_arg_opt("tag")?;
// Run the function. // Run the function.
validate_unique(&tags)?; validate_unique(&tags)?;
let tags: Vec<EdgeReference> = tags.into_iter().map(|item| item.0).collect(); let tags: Vec<EdgeReference> = tags.into_iter().map(|item| item.0).collect();
let value = inner_fillet(solid, radius, tags, tolerance.map(|t| t.n), tag, exec_state, args).await?; let value = inner_fillet(solid, radius, tags, tolerance, tag, exec_state, args).await?;
Ok(KclValue::Solid { value }) Ok(KclValue::Solid { value })
} }
@ -148,7 +148,7 @@ async fn inner_fillet(
solid: Box<Solid>, solid: Box<Solid>,
radius: TyF64, radius: TyF64,
tags: Vec<EdgeReference>, tags: Vec<EdgeReference>,
tolerance: Option<f64>, tolerance: Option<TyF64>,
tag: Option<TagNode>, tag: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
@ -163,8 +163,8 @@ async fn inner_fillet(
ModelingCmd::from(mcmd::Solid3dFilletEdge { ModelingCmd::from(mcmd::Solid3dFilletEdge {
edge_id, edge_id,
object_id: solid.id, object_id: solid.id,
radius: LengthUnit(radius.n), radius: LengthUnit(radius.to_mm()),
tolerance: LengthUnit(tolerance.unwrap_or(DEFAULT_TOLERANCE)), tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE)),
cut_type: CutType::Fillet, cut_type: CutType::Fillet,
}), }),
) )

View File

@ -17,7 +17,7 @@ use super::args::TyF64;
/// Create a helix. /// Create a helix.
pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let angle_start: TyF64 = args.get_kw_arg_typed("angleStart", &RuntimeType::angle(), exec_state)?; let angle_start: TyF64 = args.get_kw_arg_typed("angleStart", &RuntimeType::degrees(), exec_state)?;
let revolutions: TyF64 = args.get_kw_arg_typed("revolutions", &RuntimeType::count(), exec_state)?; let revolutions: TyF64 = args.get_kw_arg_typed("revolutions", &RuntimeType::count(), exec_state)?;
let ccw = args.get_kw_arg_opt("ccw")?; let ccw = args.get_kw_arg_opt("ccw")?;
let radius: Option<TyF64> = args.get_kw_arg_opt_typed("radius", &RuntimeType::length(), exec_state)?; let radius: Option<TyF64> = args.get_kw_arg_opt_typed("radius", &RuntimeType::length(), exec_state)?;
@ -84,9 +84,9 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
revolutions.n, revolutions.n,
angle_start.n, angle_start.n,
ccw, ccw,
radius.map(|t| t.n), radius,
axis, axis,
length.map(|t| t.n), length,
cylinder, cylinder,
exec_state, exec_state,
args, args,
@ -100,9 +100,9 @@ async fn inner_helix(
revolutions: f64, revolutions: f64,
angle_start: f64, angle_start: f64,
ccw: Option<bool>, ccw: Option<bool>,
radius: Option<f64>, radius: Option<TyF64>,
axis: Option<Axis3dOrEdgeReference>, axis: Option<Axis3dOrEdgeReference>,
length: Option<f64>, length: Option<TyF64>,
cylinder: Option<Solid>, cylinder: Option<Solid>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
@ -130,7 +130,7 @@ async fn inner_helix(
ModelingCmd::from(mcmd::EntityMakeHelix { ModelingCmd::from(mcmd::EntityMakeHelix {
cylinder_id: cylinder.id, cylinder_id: cylinder.id,
is_clockwise: !helix_result.ccw, is_clockwise: !helix_result.ccw,
length: LengthUnit(length.unwrap_or(cylinder.height)), length: LengthUnit(length.as_ref().map(|t| t.to_mm()).unwrap_or(cylinder.height_in_mm())),
revolutions, revolutions,
start_angle: Angle::from_degrees(angle_start), start_angle: Angle::from_degrees(angle_start),
}), }),
@ -150,20 +150,20 @@ async fn inner_helix(
args.batch_modeling_cmd( args.batch_modeling_cmd(
id, id,
ModelingCmd::from(mcmd::EntityMakeHelixFromParams { ModelingCmd::from(mcmd::EntityMakeHelixFromParams {
radius: LengthUnit(radius), radius: LengthUnit(radius.to_mm()),
is_clockwise: !helix_result.ccw, is_clockwise: !helix_result.ccw,
length: LengthUnit(length), length: LengthUnit(length.to_mm()),
revolutions, revolutions,
start_angle: Angle::from_degrees(angle_start), start_angle: Angle::from_degrees(angle_start),
axis: Point3d { axis: Point3d {
x: direction[0].n, x: direction[0].to_mm(),
y: direction[1].n, y: direction[1].to_mm(),
z: direction[2].n, z: direction[2].to_mm(),
}, },
center: Point3d { center: Point3d {
x: LengthUnit(origin[0].n), x: LengthUnit(origin[0].to_mm()),
y: LengthUnit(origin[1].n), y: LengthUnit(origin[1].to_mm()),
z: LengthUnit(origin[2].n), z: LengthUnit(origin[2].to_mm()),
}, },
}), }),
) )
@ -175,9 +175,9 @@ async fn inner_helix(
args.batch_modeling_cmd( args.batch_modeling_cmd(
id, id,
ModelingCmd::from(mcmd::EntityMakeHelixFromEdge { ModelingCmd::from(mcmd::EntityMakeHelixFromEdge {
radius: LengthUnit(radius), radius: LengthUnit(radius.to_mm()),
is_clockwise: !helix_result.ccw, is_clockwise: !helix_result.ccw,
length: length.map(LengthUnit), length: length.map(|t| LengthUnit(t.to_mm())),
revolutions, revolutions,
start_angle: Angle::from_degrees(angle_start), start_angle: Angle::from_degrees(angle_start),
edge_id, edge_id,

View File

@ -10,7 +10,10 @@ use kittycad_modeling_cmds as kcmc;
use super::{args::TyF64, DEFAULT_TOLERANCE}; use super::{args::TyF64, DEFAULT_TOLERANCE};
use crate::{ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
execution::{types::RuntimeType, ExecState, KclValue, Sketch, Solid}, execution::{
types::{NumericType, RuntimeType},
ExecState, KclValue, Sketch, Solid,
},
parsing::ast::types::TagNode, parsing::ast::types::TagNode,
std::{extrude::do_post_extrude, Args}, std::{extrude::do_post_extrude, Args},
}; };
@ -30,7 +33,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. // 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")?; let base_curve_index: Option<u32> = args.get_kw_arg_opt("baseCurveIndex")?;
// Tolerance for the loft operation. // Tolerance for the loft operation.
let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::count(), exec_state)?; let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::length(), exec_state)?;
let tag_start = args.get_kw_arg_opt("tagStart")?; let tag_start = args.get_kw_arg_opt("tagStart")?;
let tag_end = args.get_kw_arg_opt("tagEnd")?; let tag_end = args.get_kw_arg_opt("tagEnd")?;
@ -39,7 +42,7 @@ pub async fn loft(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
v_degree, v_degree,
bez_approximate_rational, bez_approximate_rational,
base_curve_index, base_curve_index,
tolerance.map(|t| t.n), tolerance,
tag_start, tag_start,
tag_end, tag_end,
exec_state, exec_state,
@ -136,7 +139,7 @@ async fn inner_loft(
v_degree: NonZeroU32, v_degree: NonZeroU32,
bez_approximate_rational: bool, bez_approximate_rational: bool,
base_curve_index: Option<u32>, base_curve_index: Option<u32>,
tolerance: Option<f64>, tolerance: Option<TyF64>,
tag_start: Option<TagNode>, tag_start: Option<TagNode>,
tag_end: Option<TagNode>, tag_end: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
@ -160,7 +163,7 @@ async fn inner_loft(
section_ids: sketches.iter().map(|group| group.id).collect(), section_ids: sketches.iter().map(|group| group.id).collect(),
base_curve_index, base_curve_index,
bez_approximate_rational, bez_approximate_rational,
tolerance: LengthUnit(tolerance.unwrap_or(DEFAULT_TOLERANCE)), tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE)),
v_degree, v_degree,
}), }),
) )
@ -174,7 +177,7 @@ async fn inner_loft(
do_post_extrude( do_post_extrude(
&sketch, &sketch,
id.into(), id.into(),
0.0, TyF64::new(0.0, NumericType::mm()),
false, false,
&super::extrude::NamedCapTags { &super::extrude::NamedCapTags {
start: tag_start.as_ref(), start: tag_start.as_ref(),

View File

@ -6,7 +6,7 @@ use kcl_derive_docs::stdlib;
use crate::{ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
execution::{ execution::{
types::{self, NumericType, RuntimeType}, types::{NumericType, RuntimeType, UnitAngle, UnitType},
ExecState, KclValue, ExecState, KclValue,
}, },
std::args::{Args, TyF64}, std::args::{Args, TyF64},
@ -20,11 +20,10 @@ pub async fn rem(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kcl
let d: TyF64 = args.get_kw_arg_typed("divisor", &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); let (n, d, ty) = NumericType::combine_div(n, d);
if *types::CHECK_NUMERIC_TYPES && ty == NumericType::Unknown { if ty == NumericType::Unknown {
// TODO suggest how to fix this
exec_state.warn(CompilationError::err( exec_state.warn(CompilationError::err(
args.source_range, args.source_range,
"Remainder of numbers which have unknown or incompatible units.", "Calling `rem` on numbers which have unknown or incompatible units.\n\nYou may need to add information about the type of the argument, for example:\n using a numeric suffix: `42{ty}`\n or using type ascription: `foo(): number({ty})`"
)); ));
} }
let remainder = inner_rem(n, d); let remainder = inner_rem(n, d);
@ -59,20 +58,63 @@ fn inner_rem(num: f64, divisor: f64) -> f64 {
/// Compute the cosine of a number (in radians). /// Compute the cosine of a number (in radians).
pub async fn cos(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { 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)?; let num: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::angle(), exec_state)?;
Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.n.cos()))) let num = match num.ty {
NumericType::Default {
angle: UnitAngle::Degrees,
..
} => {
exec_state.warn(CompilationError::err(
args.source_range,
"`cos` requires its input in radians, but the input is assumed to be in degrees. You can use a numeric suffix (e.g., `0rad`) or type ascription (e.g., `(1/2): number(rad)`) to show the number is in radians, or `toRadians` to convert from degrees to radians",
));
num.n
}
NumericType::Known(UnitType::Angle(UnitAngle::Degrees)) => num.n.to_radians(),
_ => num.n,
};
Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.cos())))
} }
/// Compute the sine of a number (in radians). /// Compute the sine of a number (in radians).
pub async fn sin(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { 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)?; let num: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::angle(), exec_state)?;
Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.n.sin()))) let num = match num.ty {
NumericType::Default {
angle: UnitAngle::Degrees,
..
} => {
exec_state.warn(CompilationError::err(
args.source_range,
"`sin` requires its input in radians, but the input is assumed to be in degrees. You can use a numeric suffix (e.g., `0rad`) or type ascription (e.g., `(1/2): number(rad)`) to show the number is in radians, or `toRadians` to convert from degrees to radians",
));
num.n
}
NumericType::Known(UnitType::Angle(UnitAngle::Degrees)) => num.n.to_radians(),
_ => num.n,
};
Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.sin())))
} }
/// Compute the tangent of a number (in radians). /// Compute the tangent of a number (in radians).
pub async fn tan(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { 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)?; let num: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::angle(), exec_state)?;
Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.n.tan()))) let num = match num.ty {
NumericType::Default {
angle: UnitAngle::Degrees,
..
} => {
exec_state.warn(CompilationError::err(
args.source_range,
"`tan` requires its input in radians, but the input is assumed to be in degrees. You can use a numeric suffix (e.g., `0rad`) or type ascription (e.g., `(1/2): number(rad)`) to show the number is in radians, or `toRadians` to convert from degrees to radians",
));
num.n
}
NumericType::Known(UnitType::Angle(UnitAngle::Degrees)) => num.n.to_radians(),
_ => num.n,
};
Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.tan())))
} }
/// Return the value of `pi`. Archimedes constant (π). /// Return the value of `pi`. Archimedes constant (π).
@ -258,11 +300,10 @@ fn inner_ceil(num: f64) -> Result<f64, KclError> {
pub async fn min(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn min(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let nums = args.get_number_array_with_types()?; let nums = args.get_number_array_with_types()?;
let (nums, ty) = NumericType::combine_eq_array(&nums); let (nums, ty) = NumericType::combine_eq_array(&nums);
if *types::CHECK_NUMERIC_TYPES && ty == NumericType::Unknown { if ty == NumericType::Unknown {
// TODO suggest how to fix this
exec_state.warn(CompilationError::err( exec_state.warn(CompilationError::err(
args.source_range, args.source_range,
"Calling `min` on numbers which have unknown or incompatible units.", "Calling `min` on numbers which have unknown or incompatible units.\n\nYou may need to add information about the type of the argument, for example:\n using a numeric suffix: `42{ty}`\n or using type ascription: `foo(): number({ty})`",
)); ));
} }
let result = inner_min(nums); let result = inner_min(nums);
@ -303,11 +344,10 @@ fn inner_min(args: Vec<f64>) -> f64 {
pub async fn max(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn max(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let nums = args.get_number_array_with_types()?; let nums = args.get_number_array_with_types()?;
let (nums, ty) = NumericType::combine_eq_array(&nums); let (nums, ty) = NumericType::combine_eq_array(&nums);
if *types::CHECK_NUMERIC_TYPES && ty == NumericType::Unknown { if ty == NumericType::Unknown {
// TODO suggest how to fix this
exec_state.warn(CompilationError::err( exec_state.warn(CompilationError::err(
args.source_range, args.source_range,
"Calling `max` on numbers which have unknown or incompatible units.", "Calling `max` on numbers which have unknown or incompatible units.\n\nYou may need to add information about the type of the argument, for example:\n using a numeric suffix: `42{ty}`\n or using type ascription: `foo(): number({ty})`",
)); ));
} }
let result = inner_max(nums); let result = inner_max(nums);
@ -389,8 +429,20 @@ fn inner_pow(num: f64, pow: f64) -> Result<f64, KclError> {
} }
/// Compute the arccosine of a number (in radians). /// Compute the arccosine of a number (in radians).
pub async fn acos(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn acos(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number_with_type()?; let num = args.get_number_with_type()?;
if matches!(
num.ty,
NumericType::Default {
angle: UnitAngle::Degrees,
..
}
) {
exec_state.warn(CompilationError::err(
args.source_range,
"`acos` requires its input in radians, but the input is assumed to be in degrees. You can use a numeric suffix (e.g., `0rad`) or type ascription (e.g., `(1/2): number(rad)`) to show the number is in radians, or `toRadians` to convert from degrees to radians",
));
}
let result = inner_acos(num.n)?; let result = inner_acos(num.n)?;
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians()))) Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
@ -420,8 +472,20 @@ fn inner_acos(num: f64) -> Result<f64, KclError> {
} }
/// Compute the arcsine of a number (in radians). /// Compute the arcsine of a number (in radians).
pub async fn asin(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn asin(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number_with_type()?; let num = args.get_number_with_type()?;
if matches!(
num.ty,
NumericType::Default {
angle: UnitAngle::Degrees,
..
}
) {
exec_state.warn(CompilationError::err(
args.source_range,
"`asin` requires its input in radians, but the input is assumed to be in degrees. You can use a numeric suffix (e.g., `0rad`) or type ascription (e.g., `(1/2): number(rad)`) to show the number is in radians, or `toRadians` to convert from degrees to radians",
));
}
let result = inner_asin(num.n)?; let result = inner_asin(num.n)?;
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians()))) Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
@ -450,8 +514,20 @@ fn inner_asin(num: f64) -> Result<f64, KclError> {
} }
/// Compute the arctangent of a number (in radians). /// Compute the arctangent of a number (in radians).
pub async fn atan(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn atan(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number_with_type()?; let num = args.get_number_with_type()?;
if matches!(
num.ty,
NumericType::Default {
angle: UnitAngle::Degrees,
..
}
) {
exec_state.warn(CompilationError::err(
args.source_range,
"`atan` requires its input in radians, but the input is assumed to be in degrees. You can use a numeric suffix (e.g., `0rad`) or type ascription (e.g., `(1/2): number(rad)`) to show the number is in radians, or `toRadians` to convert from degrees to radians",
));
}
let result = inner_atan(num.n)?; let result = inner_atan(num.n)?;
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians()))) Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
@ -483,7 +559,7 @@ fn inner_atan(num: f64) -> Result<f64, KclError> {
pub async fn atan2(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { 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 y = args.get_kw_arg_typed("y", &RuntimeType::length(), exec_state)?;
let x = args.get_kw_arg_typed("x", &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 (y, x, _) = NumericType::combine_eq_coerce(y, x);
let result = inner_atan2(y, x)?; let result = inner_atan2(y, x)?;
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians()))) Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
@ -714,66 +790,6 @@ fn inner_tau() -> Result<f64, KclError> {
Ok(std::f64::consts::TAU) Ok(std::f64::consts::TAU)
} }
/// 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_with_type()?;
let result = inner_to_radians(num.n)?;
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
}
/// Converts a number from degrees to radians.
///
/// ```no_run
/// exampleSketch = startSketchOn("XZ")
/// |> startProfileAt([0, 0], %)
/// |> angledLine(
/// angle = 50,
/// length = 70 * cos(toRadians(45)),
/// )
/// |> yLine(endAbsolute = 0)
/// |> close()
///
/// example = extrude(exampleSketch, length = 5)
/// ```
#[stdlib {
name = "toRadians",
tags = ["math"],
}]
fn inner_to_radians(num: f64) -> Result<f64, KclError> {
Ok(num.to_radians())
}
/// 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_with_type()?;
let result = inner_to_degrees(num.n)?;
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
}
/// Converts a number from radians to degrees.
///
/// ```no_run
/// exampleSketch = startSketchOn("XZ")
/// |> startProfileAt([0, 0], %)
/// |> angledLine(
/// angle = 50,
/// length = 70 * cos(toDegrees(pi()/4)),
/// )
/// |> yLine(endAbsolute = 0)
/// |> close()
///
/// example = extrude(exampleSketch, length = 5)
/// ```
#[stdlib {
name = "toDegrees",
tags = ["math"],
}]
fn inner_to_degrees(num: f64) -> Result<f64, KclError> {
Ok(num.to_degrees())
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;

View File

@ -61,13 +61,13 @@ async fn inner_mirror_2d(
ModelingCmd::from(mcmd::EntityMirror { ModelingCmd::from(mcmd::EntityMirror {
ids: starting_sketches.iter().map(|sketch| sketch.id).collect(), ids: starting_sketches.iter().map(|sketch| sketch.id).collect(),
axis: Point3d { axis: Point3d {
x: direction[0].n, x: direction[0].to_mm(),
y: direction[1].n, y: direction[1].to_mm(),
z: 0.0, z: 0.0,
}, },
point: Point3d { point: Point3d {
x: LengthUnit(origin[0].n), x: LengthUnit(origin[0].to_mm()),
y: LengthUnit(origin[1].n), y: LengthUnit(origin[1].to_mm()),
z: LengthUnit(0.0), z: LengthUnit(0.0),
}, },
}), }),

View File

@ -128,8 +128,6 @@ lazy_static! {
Box::new(crate::std::math::Log2), Box::new(crate::std::math::Log2),
Box::new(crate::std::math::Log10), Box::new(crate::std::math::Log10),
Box::new(crate::std::math::Ln), Box::new(crate::std::math::Ln),
Box::new(crate::std::math::ToDegrees),
Box::new(crate::std::math::ToRadians),
Box::new(crate::std::units::FromMm), Box::new(crate::std::units::FromMm),
Box::new(crate::std::units::FromInches), Box::new(crate::std::units::FromInches),
Box::new(crate::std::units::FromFt), Box::new(crate::std::units::FromFt),

View File

@ -13,12 +13,12 @@ use kittycad_modeling_cmds::{
shared::{Angle, OriginType, Rotation}, shared::{Angle, OriginType, Rotation},
}; };
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::Serialize;
use uuid::Uuid; use uuid::Uuid;
use super::{ use super::{
args::Arg, args::Arg,
utils::{untype_point, untype_point_3d}, utils::{point_3d_to_mm, point_to_mm},
}; };
use crate::{ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
@ -34,7 +34,7 @@ use crate::{
const MUST_HAVE_ONE_INSTANCE: &str = "There must be at least 1 instance of your geometry"; const MUST_HAVE_ONE_INSTANCE: &str = "There must be at least 1 instance of your geometry";
/// Data for a linear pattern on a 3D model. /// Data for a linear pattern on a 3D model.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)] #[ts(export)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct LinearPattern3dData { pub struct LinearPattern3dData {
@ -44,9 +44,9 @@ pub struct LinearPattern3dData {
/// If instances is 1, this has no effect. /// If instances is 1, this has no effect.
pub instances: u32, pub instances: u32,
/// The distance between each repetition. This can also be referred to as spacing. /// The distance between each repetition. This can also be referred to as spacing.
pub distance: f64, pub distance: TyF64,
/// The axis of the pattern. /// The axis of the pattern.
pub axis: [f64; 3], pub axis: [TyF64; 3],
} }
/// Repeat some 3D solid, changing each repetition slightly. /// Repeat some 3D solid, changing each repetition slightly.
@ -219,7 +219,7 @@ pub async fn pattern_transform_2d(exec_state: &mut ExecState, args: Args) -> Res
/// // Defines how to modify each layer of the vase. /// // Defines how to modify each layer of the vase.
/// // Each replica is shifted up the Z axis, and has a smoothly-varying radius /// // Each replica is shifted up the Z axis, and has a smoothly-varying radius
/// fn transform(replicaId) { /// fn transform(replicaId) {
/// scale = r * abs(1 - (t * replicaId)) * (5 + cos(replicaId / 8)) /// scale = r * abs(1 - (t * replicaId)) * (5 + cos((replicaId / 8): number(rad)))
/// return { /// return {
/// translate = [0, 0, replicaId * 10], /// translate = [0, 0, replicaId * 10],
/// scale = [scale, scale, 0], /// scale = [scale, scale, 0],
@ -498,15 +498,13 @@ fn transform_from_obj_fields<T: GeometryTrait>(
}; };
let scale = match transform.get("scale") { let scale = match transform.get("scale") {
Some(x) => untype_point_3d(T::array_to_point3d(x, source_ranges.clone(), exec_state)?) Some(x) => point_3d_to_mm(T::array_to_point3d(x, source_ranges.clone(), exec_state)?).into(),
.0
.into(),
None => kcmc::shared::Point3d { x: 1.0, y: 1.0, z: 1.0 }, None => kcmc::shared::Point3d { x: 1.0, y: 1.0, z: 1.0 },
}; };
let translate = match transform.get("translate") { let translate = match transform.get("translate") {
Some(x) => { Some(x) => {
let (arr, _) = untype_point_3d(T::array_to_point3d(x, source_ranges.clone(), exec_state)?); let arr = point_3d_to_mm(T::array_to_point3d(x, source_ranges.clone(), exec_state)?);
kcmc::shared::Point3d::<LengthUnit> { kcmc::shared::Point3d::<LengthUnit> {
x: LengthUnit(arr[0]), x: LengthUnit(arr[0]),
y: LengthUnit(arr[1]), y: LengthUnit(arr[1]),
@ -530,9 +528,7 @@ fn transform_from_obj_fields<T: GeometryTrait>(
})); }));
}; };
if let Some(axis) = rot.get("axis") { if let Some(axis) = rot.get("axis") {
rotation.axis = untype_point_3d(T::array_to_point3d(axis, source_ranges.clone(), exec_state)?) rotation.axis = point_3d_to_mm(T::array_to_point3d(axis, source_ranges.clone(), exec_state)?).into();
.0
.into();
} }
if let Some(angle) = rot.get("angle") { if let Some(angle) = rot.get("angle") {
match angle { match angle {
@ -552,9 +548,7 @@ fn transform_from_obj_fields<T: GeometryTrait>(
KclValue::String { value: s, meta: _ } if s == "local" => OriginType::Local, KclValue::String { value: s, meta: _ } if s == "local" => OriginType::Local,
KclValue::String { value: s, meta: _ } if s == "global" => OriginType::Global, KclValue::String { value: s, meta: _ } if s == "global" => OriginType::Global,
other => { other => {
let origin = untype_point_3d(T::array_to_point3d(other, source_ranges.clone(), exec_state)?) let origin = point_3d_to_mm(T::array_to_point3d(other, source_ranges.clone(), exec_state)?).into();
.0
.into();
OriginType::Custom { origin } OriginType::Custom { origin }
} }
}; };
@ -721,8 +715,7 @@ pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result
let axis: [TyF64; 2] = args.get_kw_arg_typed("axis", &RuntimeType::point2d(), 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 use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
let axis = untype_point(axis).0; if axis[0].n == 0.0 && axis[1].n == 0.0 {
if axis == [0.0, 0.0] {
return Err(KclError::Semantic(KclErrorDetails { return Err(KclError::Semantic(KclErrorDetails {
message: message:
"The axis of the linear pattern cannot be the zero vector. Otherwise they will just duplicate in place." "The axis of the linear pattern cannot be the zero vector. Otherwise they will just duplicate in place."
@ -731,8 +724,7 @@ pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result
})); }));
} }
let sketches = let sketches = inner_pattern_linear_2d(sketches, instances, distance, axis, use_original, exec_state, args).await?;
inner_pattern_linear_2d(sketches, instances, distance.n, axis, use_original, exec_state, args).await?;
Ok(sketches.into()) Ok(sketches.into())
} }
@ -765,18 +757,18 @@ pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result
async fn inner_pattern_linear_2d( async fn inner_pattern_linear_2d(
sketches: Vec<Sketch>, sketches: Vec<Sketch>,
instances: u32, instances: u32,
distance: f64, distance: TyF64,
axis: [f64; 2], axis: [TyF64; 2],
use_original: Option<bool>, use_original: Option<bool>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
) -> Result<Vec<Sketch>, KclError> { ) -> Result<Vec<Sketch>, KclError> {
let [x, y] = axis; let [x, y] = point_to_mm(axis);
let axis_len = f64::sqrt(x * x + y * y); let axis_len = f64::sqrt(x * x + y * y);
let normalized_axis = kcmc::shared::Point2d::from([x / axis_len, y / axis_len]); let normalized_axis = kcmc::shared::Point2d::from([x / axis_len, y / axis_len]);
let transforms: Vec<_> = (1..instances) let transforms: Vec<_> = (1..instances)
.map(|i| { .map(|i| {
let d = distance * (i as f64); let d = distance.to_mm() * (i as f64);
let translate = (normalized_axis * d).with_z(0.0).map(LengthUnit); let translate = (normalized_axis * d).with_z(0.0).map(LengthUnit);
vec![Transform { vec![Transform {
translate, translate,
@ -802,8 +794,7 @@ pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result
let axis: [TyF64; 3] = args.get_kw_arg_typed("axis", &RuntimeType::point3d(), 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 use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
let (axis, _) = untype_point_3d(axis); if axis[0].n == 0.0 && axis[1].n == 0.0 && axis[2].n == 0.0 {
if axis == [0.0, 0.0, 0.0] {
return Err(KclError::Semantic(KclErrorDetails { return Err(KclError::Semantic(KclErrorDetails {
message: message:
"The axis of the linear pattern cannot be the zero vector. Otherwise they will just duplicate in place." "The axis of the linear pattern cannot be the zero vector. Otherwise they will just duplicate in place."
@ -812,7 +803,7 @@ pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result
})); }));
} }
let solids = inner_pattern_linear_3d(solids, instances, distance.n, axis, use_original, exec_state, args).await?; let solids = inner_pattern_linear_3d(solids, instances, distance, axis, use_original, exec_state, args).await?;
Ok(solids.into()) Ok(solids.into())
} }
@ -903,18 +894,18 @@ pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result
async fn inner_pattern_linear_3d( async fn inner_pattern_linear_3d(
solids: Vec<Solid>, solids: Vec<Solid>,
instances: u32, instances: u32,
distance: f64, distance: TyF64,
axis: [f64; 3], axis: [TyF64; 3],
use_original: Option<bool>, use_original: Option<bool>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
) -> Result<Vec<Solid>, KclError> { ) -> Result<Vec<Solid>, KclError> {
let [x, y, z] = axis; let [x, y, z] = point_3d_to_mm(axis);
let axis_len = f64::sqrt(x * x + y * y + z * z); let axis_len = f64::sqrt(x * x + y * y + z * z);
let normalized_axis = kcmc::shared::Point3d::from([x / axis_len, y / axis_len, z / axis_len]); let normalized_axis = kcmc::shared::Point3d::from([x / axis_len, y / axis_len, z / axis_len]);
let transforms: Vec<_> = (1..instances) let transforms: Vec<_> = (1..instances)
.map(|i| { .map(|i| {
let d = distance * (i as f64); let d = distance.to_mm() * (i as f64);
let translate = (normalized_axis * d).map(LengthUnit); let translate = (normalized_axis * d).map(LengthUnit);
vec![Transform { vec![Transform {
translate, translate,
@ -926,7 +917,7 @@ async fn inner_pattern_linear_3d(
} }
/// Data for a circular pattern on a 2D sketch. /// Data for a circular pattern on a 2D sketch.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)] #[ts(export)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct CircularPattern2dData { struct CircularPattern2dData {
@ -936,7 +927,7 @@ struct CircularPattern2dData {
/// If instances is 1, this has no effect. /// If instances is 1, this has no effect.
pub instances: u32, pub instances: u32,
/// The center about which to make the pattern. This is a 2D vector. /// The center about which to make the pattern. This is a 2D vector.
pub center: [f64; 2], pub center: [TyF64; 2],
/// The arc angle (in degrees) to place the repetitions. Must be greater than 0. /// The arc angle (in degrees) to place the repetitions. Must be greater than 0.
pub arc_degrees: f64, pub arc_degrees: f64,
/// Whether or not to rotate the duplicates as they are copied. /// Whether or not to rotate the duplicates as they are copied.
@ -948,7 +939,7 @@ struct CircularPattern2dData {
} }
/// Data for a circular pattern on a 3D model. /// Data for a circular pattern on a 3D model.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)] #[ts(export)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CircularPattern3dData { pub struct CircularPattern3dData {
@ -958,9 +949,10 @@ pub struct CircularPattern3dData {
/// If instances is 1, this has no effect. /// If instances is 1, this has no effect.
pub instances: u32, pub instances: u32,
/// The axis around which to make the pattern. This is a 3D vector. /// The axis around which to make the pattern. This is a 3D vector.
// Only the direction should matter, not the magnitude so don't adjust units to avoid normalisation issues.
pub axis: [f64; 3], pub axis: [f64; 3],
/// The center about which to make the pattern. This is a 3D vector. /// The center about which to make the pattern. This is a 3D vector.
pub center: [f64; 3], pub center: [TyF64; 3],
/// The arc angle (in degrees) to place the repetitions. Must be greater than 0. /// The arc angle (in degrees) to place the repetitions. Must be greater than 0.
pub arc_degrees: f64, pub arc_degrees: f64,
/// Whether or not to rotate the duplicates as they are copied. /// Whether or not to rotate the duplicates as they are copied.
@ -971,6 +963,7 @@ pub struct CircularPattern3dData {
pub use_original: Option<bool>, pub use_original: Option<bool>,
} }
#[allow(clippy::large_enum_variant)]
enum CircularPattern { enum CircularPattern {
ThreeD(CircularPattern3dData), ThreeD(CircularPattern3dData),
TwoD(CircularPattern2dData), TwoD(CircularPattern2dData),
@ -999,14 +992,14 @@ impl CircularPattern {
pub fn axis(&self) -> [f64; 3] { pub fn axis(&self) -> [f64; 3] {
match self { match self {
CircularPattern::TwoD(_lp) => [0.0, 0.0, 0.0], CircularPattern::TwoD(_lp) => [0.0, 0.0, 0.0],
CircularPattern::ThreeD(lp) => lp.axis, CircularPattern::ThreeD(lp) => [lp.axis[0], lp.axis[1], lp.axis[2]],
} }
} }
pub fn center(&self) -> [f64; 3] { pub fn center_mm(&self) -> [f64; 3] {
match self { match self {
CircularPattern::TwoD(lp) => [lp.center[0], lp.center[1], 0.0], CircularPattern::TwoD(lp) => [lp.center[0].to_mm(), lp.center[1].to_mm(), 0.0],
CircularPattern::ThreeD(lp) => lp.center, CircularPattern::ThreeD(lp) => [lp.center[0].to_mm(), lp.center[1].to_mm(), lp.center[2].to_mm()],
} }
} }
@ -1045,14 +1038,14 @@ pub async fn pattern_circular_2d(exec_state: &mut ExecState, args: Args) -> Resu
let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?; let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
let instances: u32 = args.get_kw_arg("instances")?; let instances: u32 = args.get_kw_arg("instances")?;
let center: [TyF64; 2] = args.get_kw_arg_typed("center", &RuntimeType::point2d(), exec_state)?; 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 arc_degrees: TyF64 = args.get_kw_arg_typed("arcDegrees", &RuntimeType::degrees(), exec_state)?;
let rotate_duplicates: bool = args.get_kw_arg("rotateDuplicates")?; let rotate_duplicates: bool = args.get_kw_arg("rotateDuplicates")?;
let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?; let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
let sketches = inner_pattern_circular_2d( let sketches = inner_pattern_circular_2d(
sketches, sketches,
instances, instances,
untype_point(center).0, center,
arc_degrees.n, arc_degrees.n,
rotate_duplicates, rotate_duplicates,
use_original, use_original,
@ -1101,7 +1094,7 @@ pub async fn pattern_circular_2d(exec_state: &mut ExecState, args: Args) -> Resu
async fn inner_pattern_circular_2d( async fn inner_pattern_circular_2d(
sketch_set: Vec<Sketch>, sketch_set: Vec<Sketch>,
instances: u32, instances: u32,
center: [f64; 2], center: [TyF64; 2],
arc_degrees: f64, arc_degrees: f64,
rotate_duplicates: bool, rotate_duplicates: bool,
use_original: Option<bool>, use_original: Option<bool>,
@ -1151,13 +1144,13 @@ pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Resu
// This includes the original entity. For example, if instances is 2, // This includes the original entity. For example, if instances is 2,
// there will be two copies -- the original, and one new copy. // there will be two copies -- the original, and one new copy.
// If instances is 1, this has no effect. // If instances is 1, this has no effect.
let instances: u32 = args.get_kw_arg("instances")?; let instances: u32 = args.get_kw_arg_typed("instances", &RuntimeType::count(), exec_state)?;
// The axis around which to make the pattern. This is a 3D vector. // The axis around which to make the pattern. This is a 3D vector.
let axis: [TyF64; 3] = args.get_kw_arg_typed("axis", &RuntimeType::point3d(), exec_state)?; 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. // The center about which to make the pattern. This is a 3D vector.
let center: [TyF64; 3] = args.get_kw_arg_typed("center", &RuntimeType::point3d(), exec_state)?; 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. // The arc angle (in degrees) to place the repetitions. Must be greater than 0.
let arc_degrees: TyF64 = args.get_kw_arg_typed("arcDegrees", &RuntimeType::angle(), exec_state)?; let arc_degrees: TyF64 = args.get_kw_arg_typed("arcDegrees", &RuntimeType::degrees(), exec_state)?;
// Whether or not to rotate the duplicates as they are copied. // Whether or not to rotate the duplicates as they are copied.
let rotate_duplicates: bool = args.get_kw_arg("rotateDuplicates")?; 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, // If the target being patterned is itself a pattern, then, should you use the original solid,
@ -1167,8 +1160,8 @@ pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Resu
let solids = inner_pattern_circular_3d( let solids = inner_pattern_circular_3d(
solids, solids,
instances, instances,
untype_point_3d(axis).0, [axis[0].n, axis[1].n, axis[2].n],
untype_point_3d(center).0, center,
arc_degrees.n, arc_degrees.n,
rotate_duplicates, rotate_duplicates,
use_original, use_original,
@ -1217,7 +1210,7 @@ async fn inner_pattern_circular_3d(
solids: Vec<Solid>, solids: Vec<Solid>,
instances: u32, instances: u32,
axis: [f64; 3], axis: [f64; 3],
center: [f64; 3], center: [TyF64; 3],
arc_degrees: f64, arc_degrees: f64,
rotate_duplicates: bool, rotate_duplicates: bool,
use_original: Option<bool>, use_original: Option<bool>,
@ -1286,7 +1279,7 @@ async fn pattern_circular(
} }
}; };
let center = data.center(); let center = data.center_mm();
let resp = args let resp = args
.send_modeling_cmd( .send_modeling_cmd(
id, id,

View File

@ -15,8 +15,7 @@ use crate::{
pub async fn offset_plane(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn offset_plane(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let std_plane = args.get_unlabeled_kw_arg("plane")?; let std_plane = args.get_unlabeled_kw_arg("plane")?;
let offset: TyF64 = args.get_kw_arg_typed("offset", &RuntimeType::length(), exec_state)?; 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?; let plane = inner_offset_plane(std_plane, offset, exec_state, &args).await?;
make_offset_plane_in_engine(&plane, exec_state, &args).await?;
Ok(KclValue::Plane { value: Box::new(plane) }) Ok(KclValue::Plane { value: Box::new(plane) })
} }
@ -112,13 +111,19 @@ pub async fn offset_plane(exec_state: &mut ExecState, args: Args) -> Result<KclV
offset = { docs = "Distance from the standard plane this new plane will be created at." }, offset = { docs = "Distance from the standard plane this new plane will be created at." },
} }
}] }]
async fn inner_offset_plane(plane: PlaneData, offset: f64, exec_state: &mut ExecState) -> Result<Plane, KclError> { async fn inner_offset_plane(
plane: PlaneData,
offset: TyF64,
exec_state: &mut ExecState,
args: &Args,
) -> Result<Plane, KclError> {
let mut plane = Plane::from_plane_data(plane, exec_state); let mut plane = Plane::from_plane_data(plane, exec_state);
// Though offset planes might be derived from standard planes, they are not // Though offset planes might be derived from standard planes, they are not
// standard planes themselves. // standard planes themselves.
plane.value = PlaneType::Custom; plane.value = PlaneType::Custom;
plane.origin += plane.z_axis * offset; plane.origin += plane.z_axis * offset.to_length_units(plane.origin.units);
make_offset_plane_in_engine(&plane, exec_state, args).await?;
Ok(plane) Ok(plane)
} }

View File

@ -13,7 +13,7 @@ use super::{args::TyF64, DEFAULT_TOLERANCE};
use crate::{ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
execution::{ execution::{
types::{PrimitiveType, RuntimeType}, types::{NumericType, PrimitiveType, RuntimeType},
ExecState, KclValue, Sketch, Solid, ExecState, KclValue, Sketch, Solid,
}, },
parsing::ast::types::TagNode, parsing::ast::types::TagNode,
@ -31,8 +31,8 @@ pub async fn revolve(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
]), ]),
exec_state, exec_state,
)?; )?;
let angle: Option<TyF64> = args.get_kw_arg_opt_typed("angle", &RuntimeType::angle(), exec_state)?; let angle: Option<TyF64> = args.get_kw_arg_opt_typed("angle", &RuntimeType::degrees(), exec_state)?;
let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::count(), exec_state)?; let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::length(), exec_state)?;
let tag_start = args.get_kw_arg_opt("tagStart")?; let tag_start = args.get_kw_arg_opt("tagStart")?;
let tag_end = args.get_kw_arg_opt("tagEnd")?; let tag_end = args.get_kw_arg_opt("tagEnd")?;
let symmetric = args.get_kw_arg_opt("symmetric")?; let symmetric = args.get_kw_arg_opt("symmetric")?;
@ -43,7 +43,7 @@ pub async fn revolve(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
sketches, sketches,
axis, axis,
angle.map(|t| t.n), angle.map(|t| t.n),
tolerance.map(|t| t.n), tolerance,
tag_start, tag_start,
tag_end, tag_end,
symmetric, symmetric,
@ -60,7 +60,7 @@ async fn inner_revolve(
sketches: Vec<Sketch>, sketches: Vec<Sketch>,
axis: Axis2dOrEdgeReference, axis: Axis2dOrEdgeReference,
angle: Option<f64>, angle: Option<f64>,
tolerance: Option<f64>, tolerance: Option<TyF64>,
tag_start: Option<TagNode>, tag_start: Option<TagNode>,
tag_end: Option<TagNode>, tag_end: Option<TagNode>,
symmetric: Option<bool>, symmetric: Option<bool>,
@ -140,16 +140,16 @@ async fn inner_revolve(
angle, angle,
target: sketch.id.into(), target: sketch.id.into(),
axis: Point3d { axis: Point3d {
x: direction[0].n, x: direction[0].to_mm(),
y: direction[1].n, y: direction[1].to_mm(),
z: 0.0, z: 0.0,
}, },
origin: Point3d { origin: Point3d {
x: LengthUnit(origin[0].n), x: LengthUnit(origin[0].to_mm()),
y: LengthUnit(origin[1].n), y: LengthUnit(origin[1].to_mm()),
z: LengthUnit(0.0), z: LengthUnit(0.0),
}, },
tolerance: LengthUnit(tolerance.unwrap_or(DEFAULT_TOLERANCE)), tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE)),
axis_is_2d: true, axis_is_2d: true,
opposite: opposite.clone(), opposite: opposite.clone(),
}), }),
@ -164,7 +164,7 @@ async fn inner_revolve(
angle, angle,
target: sketch.id.into(), target: sketch.id.into(),
edge_id, edge_id,
tolerance: LengthUnit(tolerance.unwrap_or(DEFAULT_TOLERANCE)), tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE)),
opposite: opposite.clone(), opposite: opposite.clone(),
}), }),
) )
@ -176,7 +176,7 @@ async fn inner_revolve(
do_post_extrude( do_post_extrude(
sketch, sketch,
id.into(), id.into(),
0.0, TyF64::new(0.0, NumericType::mm()),
false, false,
&super::extrude::NamedCapTags { &super::extrude::NamedCapTags {
start: tag_start.as_ref(), start: tag_start.as_ref(),

View File

@ -464,7 +464,7 @@ fn inner_segment_angle(tag: &TagIdentifier, exec_state: &mut ExecState, args: Ar
}) })
})?; })?;
let result = between(path.get_from().into(), path.get_to().into()); let result = between(path.get_base().from, path.get_base().to);
Ok(result.to_degrees()) Ok(result.to_degrees())
} }
@ -584,7 +584,7 @@ async fn inner_tangent_to_end(tag: &TagIdentifier, exec_state: &mut ExecState, a
/// Returns the angle to match the given length for x. /// 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> { 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 (tag, to, sketch) = args.get_tag_to_number_sketch()?;
let result = inner_angle_to_match_length_x(&tag, to.n, sketch, exec_state, args.clone())?; let result = inner_angle_to_match_length_x(&tag, to, sketch, exec_state, args.clone())?;
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees()))) Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
} }
@ -607,7 +607,7 @@ pub async fn angle_to_match_length_x(exec_state: &mut ExecState, args: Args) ->
}] }]
fn inner_angle_to_match_length_x( fn inner_angle_to_match_length_x(
tag: &TagIdentifier, tag: &TagIdentifier,
to: f64, to: TyF64,
sketch: Sketch, sketch: Sketch,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
@ -633,8 +633,7 @@ fn inner_angle_to_match_length_x(
})? })?
.get_base(); .get_base();
// TODO assumption about the units of to let diff = (to.to_length_units(sketch.units) - last_line.to[0]).abs();
let diff = (to - last_line.to[0]).abs();
let angle_r = (diff / length).acos(); let angle_r = (diff / length).acos();
@ -648,7 +647,7 @@ fn inner_angle_to_match_length_x(
/// Returns the angle to match the given length for y. /// 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> { 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 (tag, to, sketch) = args.get_tag_to_number_sketch()?;
let result = inner_angle_to_match_length_y(&tag, to.n, sketch, exec_state, args.clone())?; let result = inner_angle_to_match_length_y(&tag, to, sketch, exec_state, args.clone())?;
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees()))) Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
} }
@ -672,7 +671,7 @@ pub async fn angle_to_match_length_y(exec_state: &mut ExecState, args: Args) ->
}] }]
fn inner_angle_to_match_length_y( fn inner_angle_to_match_length_y(
tag: &TagIdentifier, tag: &TagIdentifier,
to: f64, to: TyF64,
sketch: Sketch, sketch: Sketch,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
@ -698,8 +697,7 @@ fn inner_angle_to_match_length_y(
})? })?
.get_base(); .get_base();
// TODO assumption about the units of to let diff = (to.to_length_units(sketch.units) - last_line.to[1]).abs();
let diff = (to - last_line.to[1]).abs();
let angle_r = (diff / length).asin(); let angle_r = (diff / length).asin();

View File

@ -13,10 +13,16 @@ use kittycad_modeling_cmds::shared::PathSegment;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Serialize; use serde::Serialize;
use super::{args::TyF64, utils::untype_point}; use super::{
args::TyF64,
utils::{point_to_len_unit, point_to_mm, point_to_typed, untype_point, untyped_point_to_mm},
};
use crate::{ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
execution::{types::RuntimeType, BasePath, ExecState, GeoMeta, KclValue, Path, Sketch, SketchSurface}, execution::{
types::{RuntimeType, UnitLen},
BasePath, ExecState, GeoMeta, KclValue, Path, Sketch, SketchSurface,
},
parsing::ast::types::TagNode, parsing::ast::types::TagNode,
std::{ std::{
sketch::NEW_TAG_KW, sketch::NEW_TAG_KW,
@ -41,15 +47,7 @@ pub async fn circle(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
let radius: TyF64 = args.get_kw_arg_typed("radius", &RuntimeType::length(), 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 tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
let sketch = inner_circle( let sketch = inner_circle(sketch_or_surface, center, radius, tag, exec_state, args).await?;
sketch_or_surface,
untype_point(center).0,
radius.n,
tag,
exec_state,
args,
)
.await?;
Ok(KclValue::Sketch { Ok(KclValue::Sketch {
value: Box::new(sketch), value: Box::new(sketch),
}) })
@ -57,8 +55,8 @@ pub async fn circle(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
async fn inner_circle( async fn inner_circle(
sketch_or_surface: SketchOrSurface, sketch_or_surface: SketchOrSurface,
center: [f64; 2], center: [TyF64; 2],
radius: f64, radius: TyF64,
tag: Option<TagNode>, tag: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
@ -67,17 +65,15 @@ async fn inner_circle(
SketchOrSurface::SketchSurface(surface) => surface, SketchOrSurface::SketchSurface(surface) => surface,
SketchOrSurface::Sketch(s) => s.on, SketchOrSurface::Sketch(s) => s.on,
}; };
let units = sketch_surface.units(); let (center_u, ty) = untype_point(center.clone());
let sketch = crate::std::sketch::inner_start_profile_at( let units = ty.expect_length();
[center[0] + radius, center[1]],
sketch_surface, let from = [center_u[0] + radius.to_length_units(units), center_u[1]];
None, let from_t = [TyF64::new(from[0], ty.clone()), TyF64::new(from[1], ty)];
exec_state,
args.clone(), let sketch =
) crate::std::sketch::inner_start_profile_at(from_t, sketch_surface, None, exec_state, args.clone()).await?;
.await?;
let from = [center[0] + radius, center[1]];
let angle_start = Angle::zero(); let angle_start = Angle::zero();
let angle_end = Angle::turn(); let angle_end = Angle::turn();
@ -90,8 +86,8 @@ async fn inner_circle(
segment: PathSegment::Arc { segment: PathSegment::Arc {
start: angle_start, start: angle_start,
end: angle_end, end: angle_end,
center: KPoint2d::from(center).map(LengthUnit), center: KPoint2d::from(point_to_mm(center)).map(LengthUnit),
radius: radius.into(), radius: LengthUnit(radius.to_mm()),
relative: false, relative: false,
}, },
}), }),
@ -109,8 +105,8 @@ async fn inner_circle(
metadata: args.source_range.into(), metadata: args.source_range.into(),
}, },
}, },
radius, radius: radius.to_length_units(units),
center, center: center_u,
ccw: angle_start < angle_end, ccw: angle_start < angle_end,
}; };
@ -135,16 +131,7 @@ pub async fn circle_three_point(exec_state: &mut ExecState, args: Args) -> Resul
let p3 = args.get_kw_arg_typed("p3", &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 tag = args.get_kw_arg_opt("tag")?;
let sketch = inner_circle_three_point( let sketch = inner_circle_three_point(sketch_surface_or_group, p1, p2, p3, tag, exec_state, args).await?;
sketch_surface_or_group,
untype_point(p1).0,
untype_point(p2).0,
untype_point(p3).0,
tag,
exec_state,
args,
)
.await?;
Ok(KclValue::Sketch { Ok(KclValue::Sketch {
value: Box::new(sketch), value: Box::new(sketch),
}) })
@ -174,13 +161,20 @@ pub async fn circle_three_point(exec_state: &mut ExecState, args: Args) -> Resul
// path so it can be used for other features, otherwise it's lost. // path so it can be used for other features, otherwise it's lost.
async fn inner_circle_three_point( async fn inner_circle_three_point(
sketch_surface_or_group: SketchOrSurface, sketch_surface_or_group: SketchOrSurface,
p1: [f64; 2], p1: [TyF64; 2],
p2: [f64; 2], p2: [TyF64; 2],
p3: [f64; 2], p3: [TyF64; 2],
tag: Option<TagNode>, tag: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
) -> Result<Sketch, KclError> { ) -> Result<Sketch, KclError> {
let ty = p1[0].ty.clone();
let units = ty.expect_length();
let p1 = point_to_len_unit(p1, units);
let p2 = point_to_len_unit(p2, units);
let p3 = point_to_len_unit(p3, units);
let center = calculate_circle_center(p1, p2, p3); 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. // It can be the distance to any of the 3 points - they all lay on the circumference.
let radius = distance(center, p2); let radius = distance(center, p2);
@ -189,16 +183,15 @@ async fn inner_circle_three_point(
SketchOrSurface::SketchSurface(surface) => surface, SketchOrSurface::SketchSurface(surface) => surface,
SketchOrSurface::Sketch(group) => group.on, SketchOrSurface::Sketch(group) => group.on,
}; };
let sketch = crate::std::sketch::inner_start_profile_at(
[center[0] + radius, center[1]],
sketch_surface,
None,
exec_state,
args.clone(),
)
.await?;
let from = [center[0] + radius, center[1]]; let from = [
TyF64::new(center[0] + radius, ty.clone()),
TyF64::new(center[1], ty.clone()),
];
let sketch =
crate::std::sketch::inner_start_profile_at(from.clone(), sketch_surface, None, exec_state, args.clone())
.await?;
let angle_start = Angle::zero(); let angle_start = Angle::zero();
let angle_end = Angle::turn(); let angle_end = Angle::turn();
@ -211,8 +204,8 @@ async fn inner_circle_three_point(
segment: PathSegment::Arc { segment: PathSegment::Arc {
start: angle_start, start: angle_start,
end: angle_end, end: angle_end,
center: KPoint2d::from(center).map(LengthUnit), center: KPoint2d::from(untyped_point_to_mm(center, units)).map(LengthUnit),
radius: radius.into(), radius: units.adjust_to(radius, UnitLen::Mm).0.into(),
relative: false, relative: false,
}, },
}), }),
@ -221,10 +214,11 @@ async fn inner_circle_three_point(
let current_path = Path::CircleThreePoint { let current_path = Path::CircleThreePoint {
base: BasePath { base: BasePath {
from, // It's fine to untype here because we know `from` has units as its units.
to: from, from: untype_point(from.clone()).0,
to: untype_point(from).0,
tag: tag.clone(), tag: tag.clone(),
units: sketch.units, units,
geo_meta: GeoMeta { geo_meta: GeoMeta {
id, id,
metadata: args.source_range.into(), metadata: args.source_range.into(),
@ -270,7 +264,7 @@ pub async fn polygon(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
sketch_surface_or_group, sketch_surface_or_group,
radius, radius,
num_sides.n as u64, num_sides.n as u64,
untype_point(center).0, center,
inscribed, inscribed,
exec_state, exec_state,
args, args,
@ -324,7 +318,7 @@ async fn inner_polygon(
sketch_surface_or_group: SketchOrSurface, sketch_surface_or_group: SketchOrSurface,
radius: TyF64, radius: TyF64,
num_sides: u64, num_sides: u64,
center: [f64; 2], center: [TyF64; 2],
inscribed: Option<bool>, inscribed: Option<bool>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
@ -343,9 +337,9 @@ async fn inner_polygon(
})); }));
} }
let sketch_surface = match sketch_surface_or_group { let (sketch_surface, units) = match sketch_surface_or_group {
SketchOrSurface::SketchSurface(surface) => surface, SketchOrSurface::SketchSurface(surface) => (surface, radius.ty.expect_length()),
SketchOrSurface::Sketch(group) => group.on, SketchOrSurface::Sketch(group) => (group.on, group.units),
}; };
let half_angle = std::f64::consts::PI / num_sides as f64; let half_angle = std::f64::consts::PI / num_sides as f64;
@ -360,18 +354,26 @@ async fn inner_polygon(
let angle_step = std::f64::consts::TAU / num_sides as f64; let angle_step = std::f64::consts::TAU / num_sides as f64;
let center_u = point_to_len_unit(center, units);
let vertices: Vec<[f64; 2]> = (0..num_sides) let vertices: Vec<[f64; 2]> = (0..num_sides)
.map(|i| { .map(|i| {
let angle = angle_step * i as f64; let angle = angle_step * i as f64;
[ [
center[0] + radius_to_vertices * angle.cos(), center_u[0] + radius_to_vertices * angle.cos(),
center[1] + radius_to_vertices * angle.sin(), center_u[1] + radius_to_vertices * angle.sin(),
] ]
}) })
.collect(); .collect();
let mut sketch = let mut sketch = crate::std::sketch::inner_start_profile_at(
crate::std::sketch::inner_start_profile_at(vertices[0], sketch_surface, None, exec_state, args.clone()).await?; point_to_typed(vertices[0], units),
sketch_surface,
None,
exec_state,
args.clone(),
)
.await?;
// Draw all the lines with unique IDs and modified tags // Draw all the lines with unique IDs and modified tags
for vertex in vertices.iter().skip(1) { for vertex in vertices.iter().skip(1) {
@ -383,7 +385,9 @@ async fn inner_polygon(
ModelingCmd::from(mcmd::ExtendPath { ModelingCmd::from(mcmd::ExtendPath {
path: sketch.id.into(), path: sketch.id.into(),
segment: PathSegment::Line { segment: PathSegment::Line {
end: KPoint2d::from(*vertex).with_z(0.0).map(LengthUnit), end: KPoint2d::from(untyped_point_to_mm(*vertex, units))
.with_z(0.0)
.map(LengthUnit),
relative: false, relative: false,
}, },
}), }),
@ -392,7 +396,7 @@ async fn inner_polygon(
let current_path = Path::ToPoint { let current_path = Path::ToPoint {
base: BasePath { base: BasePath {
from: from.into(), from: from.ignore_units(),
to: *vertex, to: *vertex,
tag: None, tag: None,
units: sketch.units, units: sketch.units,
@ -415,7 +419,9 @@ async fn inner_polygon(
ModelingCmd::from(mcmd::ExtendPath { ModelingCmd::from(mcmd::ExtendPath {
path: sketch.id.into(), path: sketch.id.into(),
segment: PathSegment::Line { segment: PathSegment::Line {
end: KPoint2d::from(vertices[0]).with_z(0.0).map(LengthUnit), end: KPoint2d::from(untyped_point_to_mm(vertices[0], units))
.with_z(0.0)
.map(LengthUnit),
relative: false, relative: false,
}, },
}), }),
@ -424,7 +430,7 @@ async fn inner_polygon(
let current_path = Path::ToPoint { let current_path = Path::ToPoint {
base: BasePath { base: BasePath {
from: from.into(), from: from.ignore_units(),
to: vertices[0], to: vertices[0],
tag: None, tag: None,
units: sketch.units, units: sketch.units,

View File

@ -16,10 +16,10 @@ use super::args::TyF64;
/// Create a shell. /// Create a shell.
pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { 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 solids = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?;
let thickness: TyF64 = args.get_kw_arg_typed("thickness", &RuntimeType::count(), exec_state)?; let thickness: TyF64 = args.get_kw_arg_typed("thickness", &RuntimeType::length(), exec_state)?;
let faces = args.get_kw_arg("faces")?; let faces = args.get_kw_arg("faces")?;
let result = inner_shell(solids, thickness.n, faces, exec_state, args).await?; let result = inner_shell(solids, thickness, faces, exec_state, args).await?;
Ok(result.into()) Ok(result.into())
} }
@ -182,7 +182,7 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
}] }]
async fn inner_shell( async fn inner_shell(
solids: Vec<Solid>, solids: Vec<Solid>,
thickness: f64, thickness: TyF64,
faces: Vec<FaceTag>, faces: Vec<FaceTag>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
@ -237,7 +237,7 @@ async fn inner_shell(
hollow: false, hollow: false,
face_ids, face_ids,
object_id: solids[0].id, object_id: solids[0].id,
shell_thickness: LengthUnit(thickness), shell_thickness: LengthUnit(thickness.to_mm()),
}), }),
) )
.await?; .await?;
@ -247,9 +247,9 @@ async fn inner_shell(
/// Make the inside of a 3D object hollow. /// Make the inside of a 3D object hollow.
pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (thickness, solid) = args.get_data_and_solid(exec_state)?; let (thickness, solid) = args.get_length_and_solid(exec_state)?;
let value = inner_hollow(thickness.n, solid, exec_state, args).await?; let value = inner_hollow(thickness, solid, exec_state, args).await?;
Ok(KclValue::Solid { value }) Ok(KclValue::Solid { value })
} }
@ -308,7 +308,7 @@ pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
feature_tree_operation = true, feature_tree_operation = true,
}] }]
async fn inner_hollow( async fn inner_hollow(
thickness: f64, thickness: TyF64,
solid: Box<Solid>, solid: Box<Solid>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
@ -323,7 +323,7 @@ async fn inner_hollow(
hollow: true, hollow: true,
face_ids: Vec::new(), // This is empty because we want to hollow the entire object. face_ids: Vec::new(), // This is empty because we want to hollow the entire object.
object_id: solid.id, object_id: solid.id,
shell_thickness: LengthUnit(thickness), shell_thickness: LengthUnit(thickness.to_mm()),
}), }),
) )
.await?; .await?;

View File

@ -12,11 +12,11 @@ use parse_display::{Display, FromStr};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::utils::untype_point; use super::utils::{point_to_len_unit, point_to_mm, untype_point, untyped_point_to_mm};
use crate::{ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
execution::{ execution::{
types::{PrimitiveType, RuntimeType, UnitLen}, types::{NumericType, PrimitiveType, RuntimeType, UnitLen},
Artifact, ArtifactId, BasePath, CodeRef, ExecState, Face, GeoMeta, KclValue, Path, Plane, Point2d, Point3d, Artifact, ArtifactId, BasePath, CodeRef, ExecState, Face, GeoMeta, KclValue, Path, Plane, Point2d, Point3d,
Sketch, SketchSurface, Solid, StartSketchOnFace, StartSketchOnPlane, TagEngineInfo, TagIdentifier, Sketch, SketchSurface, Solid, StartSketchOnFace, StartSketchOnPlane, TagEngineInfo, TagIdentifier,
}, },
@ -200,7 +200,7 @@ async fn inner_involute_circular(
let current_path = Path::ToPoint { let current_path = Path::ToPoint {
base: BasePath { base: BasePath {
from: from.into(), from: from.ignore_units(),
to: [end.x, end.y], to: [end.x, end.y],
tag: tag.clone(), tag: tag.clone(),
units: sketch.units, units: sketch.units,
@ -226,15 +226,7 @@ pub async fn line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
let end_absolute = args.get_kw_arg_opt_typed("endAbsolute", &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 tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
let new_sketch = inner_line( let new_sketch = inner_line(sketch, end_absolute, end, tag, exec_state, args).await?;
sketch,
end_absolute.map(|p| untype_point(p).0),
end.map(|p| untype_point(p).0),
tag,
exec_state,
args,
)
.await?;
Ok(KclValue::Sketch { Ok(KclValue::Sketch {
value: Box::new(new_sketch), value: Box::new(new_sketch),
}) })
@ -277,8 +269,8 @@ pub async fn line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
}] }]
async fn inner_line( async fn inner_line(
sketch: Sketch, sketch: Sketch,
end_absolute: Option<[f64; 2]>, end_absolute: Option<[TyF64; 2]>,
end: Option<[f64; 2]>, end: Option<[TyF64; 2]>,
tag: Option<TagNode>, tag: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
@ -298,13 +290,13 @@ async fn inner_line(
struct StraightLineParams { struct StraightLineParams {
sketch: Sketch, sketch: Sketch,
end_absolute: Option<[f64; 2]>, end_absolute: Option<[TyF64; 2]>,
end: Option<[f64; 2]>, end: Option<[TyF64; 2]>,
tag: Option<TagNode>, tag: Option<TagNode>,
} }
impl StraightLineParams { impl StraightLineParams {
fn relative(p: [f64; 2], sketch: Sketch, tag: Option<TagNode>) -> Self { fn relative(p: [TyF64; 2], sketch: Sketch, tag: Option<TagNode>) -> Self {
Self { Self {
sketch, sketch,
tag, tag,
@ -312,7 +304,7 @@ impl StraightLineParams {
end_absolute: None, end_absolute: None,
} }
} }
fn absolute(p: [f64; 2], sketch: Sketch, tag: Option<TagNode>) -> Self { fn absolute(p: [TyF64; 2], sketch: Sketch, tag: Option<TagNode>) -> Self {
Self { Self {
sketch, sketch,
tag, tag,
@ -357,7 +349,7 @@ async fn straight_line(
ModelingCmd::from(mcmd::ExtendPath { ModelingCmd::from(mcmd::ExtendPath {
path: sketch.id.into(), path: sketch.id.into(),
segment: PathSegment::Line { segment: PathSegment::Line {
end: KPoint2d::from(point).with_z(0.0).map(LengthUnit), end: KPoint2d::from(point_to_mm(point.clone())).with_z(0.0).map(LengthUnit),
relative: !is_absolute, relative: !is_absolute,
}, },
}), }),
@ -365,15 +357,16 @@ async fn straight_line(
.await?; .await?;
let end = if is_absolute { let end = if is_absolute {
point point_to_len_unit(point, from.units)
} else { } else {
let from = sketch.current_pen_position()?; let from = sketch.current_pen_position()?;
let point = point_to_len_unit(point, from.units);
[from.x + point[0], from.y + point[1]] [from.x + point[0], from.y + point[1]]
}; };
let current_path = Path::ToPoint { let current_path = Path::ToPoint {
base: BasePath { base: BasePath {
from: from.into(), from: from.ignore_units(),
to: end, to: end,
tag: tag.clone(), tag: tag.clone(),
units: sketch.units, units: sketch.units,
@ -402,15 +395,7 @@ pub async fn x_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
let end_absolute: Option<TyF64> = args.get_kw_arg_opt_typed("endAbsolute", &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 tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
let new_sketch = inner_x_line( let new_sketch = inner_x_line(sketch, length, end_absolute, tag, exec_state, args).await?;
sketch,
length.map(|t| t.n),
end_absolute.map(|t| t.n),
tag,
exec_state,
args,
)
.await?;
Ok(KclValue::Sketch { Ok(KclValue::Sketch {
value: Box::new(new_sketch), value: Box::new(new_sketch),
}) })
@ -451,8 +436,8 @@ pub async fn x_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
}] }]
async fn inner_x_line( async fn inner_x_line(
sketch: Sketch, sketch: Sketch,
length: Option<f64>, length: Option<TyF64>,
end_absolute: Option<f64>, end_absolute: Option<TyF64>,
tag: Option<TagNode>, tag: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
@ -461,8 +446,8 @@ async fn inner_x_line(
straight_line( straight_line(
StraightLineParams { StraightLineParams {
sketch, sketch,
end_absolute: end_absolute.map(|x| [x, from.y]), end_absolute: end_absolute.map(|x| [x, from.into_y()]),
end: length.map(|x| [x, 0.0]), end: length.map(|x| [x, TyF64::new(0.0, NumericType::mm())]),
tag, tag,
}, },
exec_state, exec_state,
@ -479,15 +464,7 @@ pub async fn y_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
let end_absolute: Option<TyF64> = args.get_kw_arg_opt_typed("endAbsolute", &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 tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
let new_sketch = inner_y_line( let new_sketch = inner_y_line(sketch, length, end_absolute, tag, exec_state, args).await?;
sketch,
length.map(|t| t.n),
end_absolute.map(|t| t.n),
tag,
exec_state,
args,
)
.await?;
Ok(KclValue::Sketch { Ok(KclValue::Sketch {
value: Box::new(new_sketch), value: Box::new(new_sketch),
}) })
@ -523,8 +500,8 @@ pub async fn y_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
}] }]
async fn inner_y_line( async fn inner_y_line(
sketch: Sketch, sketch: Sketch,
length: Option<f64>, length: Option<TyF64>,
end_absolute: Option<f64>, end_absolute: Option<TyF64>,
tag: Option<TagNode>, tag: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
@ -533,8 +510,8 @@ async fn inner_y_line(
straight_line( straight_line(
StraightLineParams { StraightLineParams {
sketch, sketch,
end_absolute: end_absolute.map(|y| [from.x, y]), end_absolute: end_absolute.map(|y| [from.into_x(), y]),
end: length.map(|y| [0.0, y]), end: length.map(|y| [TyF64::new(0.0, NumericType::mm()), y]),
tag, tag,
}, },
exec_state, exec_state,
@ -559,11 +536,11 @@ pub async fn angled_line(exec_state: &mut ExecState, args: Args) -> Result<KclVa
let new_sketch = inner_angled_line( let new_sketch = inner_angled_line(
sketch, sketch,
angle.n, angle.n,
length.map(|t| t.n), length,
length_x.map(|t| t.n), length_x,
length_y.map(|t| t.n), length_y,
end_absolute_x.map(|t| t.n), end_absolute_x,
end_absolute_y.map(|t| t.n), end_absolute_y,
tag, tag,
exec_state, exec_state,
args, args,
@ -610,16 +587,16 @@ pub async fn angled_line(exec_state: &mut ExecState, args: Args) -> Result<KclVa
async fn inner_angled_line( async fn inner_angled_line(
sketch: Sketch, sketch: Sketch,
angle: f64, angle: f64,
length: Option<f64>, length: Option<TyF64>,
length_x: Option<f64>, length_x: Option<TyF64>,
length_y: Option<f64>, length_y: Option<TyF64>,
end_absolute_x: Option<f64>, end_absolute_x: Option<TyF64>,
end_absolute_y: Option<f64>, end_absolute_y: Option<TyF64>,
tag: Option<TagNode>, tag: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
) -> Result<Sketch, KclError> { ) -> Result<Sketch, KclError> {
let options_given = [length, length_x, length_y, end_absolute_x, end_absolute_y] let options_given = [&length, &length_x, &length_y, &end_absolute_x, &end_absolute_y]
.iter() .iter()
.filter(|x| x.is_some()) .filter(|x| x.is_some())
.count(); .count();
@ -667,12 +644,13 @@ async fn inner_angled_line(
async fn inner_angled_line_length( async fn inner_angled_line_length(
sketch: Sketch, sketch: Sketch,
angle_degrees: f64, angle_degrees: f64,
length: f64, length: TyF64,
tag: Option<TagNode>, tag: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
) -> Result<Sketch, KclError> { ) -> Result<Sketch, KclError> {
let from = sketch.current_pen_position()?; let from = sketch.current_pen_position()?;
let length = length.to_length_units(from.units);
//double check me on this one - mike //double check me on this one - mike
let delta: [f64; 2] = [ let delta: [f64; 2] = [
@ -690,7 +668,9 @@ async fn inner_angled_line_length(
ModelingCmd::from(mcmd::ExtendPath { ModelingCmd::from(mcmd::ExtendPath {
path: sketch.id.into(), path: sketch.id.into(),
segment: PathSegment::Line { segment: PathSegment::Line {
end: KPoint2d::from(delta).with_z(0.0).map(LengthUnit), end: KPoint2d::from(untyped_point_to_mm(delta, from.units))
.with_z(0.0)
.map(LengthUnit),
relative, relative,
}, },
}), }),
@ -699,7 +679,7 @@ async fn inner_angled_line_length(
let current_path = Path::ToPoint { let current_path = Path::ToPoint {
base: BasePath { base: BasePath {
from: from.into(), from: from.ignore_units(),
to, to,
tag: tag.clone(), tag: tag.clone(),
units: sketch.units, units: sketch.units,
@ -721,7 +701,7 @@ async fn inner_angled_line_length(
async fn inner_angled_line_of_x_length( async fn inner_angled_line_of_x_length(
angle_degrees: f64, angle_degrees: f64,
length: f64, length: TyF64,
sketch: Sketch, sketch: Sketch,
tag: Option<TagNode>, tag: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
@ -741,7 +721,8 @@ async fn inner_angled_line_of_x_length(
})); }));
} }
let to = get_y_component(Angle::from_degrees(angle_degrees), length); let to = get_y_component(Angle::from_degrees(angle_degrees), length.n);
let to = [TyF64::new(to[0], length.ty.clone()), TyF64::new(to[1], length.ty)];
let new_sketch = straight_line(StraightLineParams::relative(to, sketch, tag), exec_state, args).await?; let new_sketch = straight_line(StraightLineParams::relative(to, sketch, tag), exec_state, args).await?;
@ -750,7 +731,7 @@ async fn inner_angled_line_of_x_length(
async fn inner_angled_line_to_x( async fn inner_angled_line_to_x(
angle_degrees: f64, angle_degrees: f64,
x_to: f64, x_to: TyF64,
sketch: Sketch, sketch: Sketch,
tag: Option<TagNode>, tag: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
@ -772,12 +753,12 @@ async fn inner_angled_line_to_x(
})); }));
} }
let x_component = x_to - from.x; let x_component = x_to.to_length_units(from.units) - from.x;
let y_component = x_component * f64::tan(angle_degrees.to_radians()); let y_component = x_component * f64::tan(angle_degrees.to_radians());
let y_to = from.y + y_component; let y_to = from.y + y_component;
let new_sketch = straight_line( let new_sketch = straight_line(
StraightLineParams::absolute([x_to, y_to], sketch, tag), StraightLineParams::absolute([x_to, TyF64::new(y_to, from.units.into())], sketch, tag),
exec_state, exec_state,
args, args,
) )
@ -787,7 +768,7 @@ async fn inner_angled_line_to_x(
async fn inner_angled_line_of_y_length( async fn inner_angled_line_of_y_length(
angle_degrees: f64, angle_degrees: f64,
length: f64, length: TyF64,
sketch: Sketch, sketch: Sketch,
tag: Option<TagNode>, tag: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
@ -807,7 +788,8 @@ async fn inner_angled_line_of_y_length(
})); }));
} }
let to = get_x_component(Angle::from_degrees(angle_degrees), length); let to = get_x_component(Angle::from_degrees(angle_degrees), length.n);
let to = [TyF64::new(to[0], length.ty.clone()), TyF64::new(to[1], length.ty)];
let new_sketch = straight_line(StraightLineParams::relative(to, sketch, tag), exec_state, args).await?; let new_sketch = straight_line(StraightLineParams::relative(to, sketch, tag), exec_state, args).await?;
@ -816,7 +798,7 @@ async fn inner_angled_line_of_y_length(
async fn inner_angled_line_to_y( async fn inner_angled_line_to_y(
angle_degrees: f64, angle_degrees: f64,
y_to: f64, y_to: TyF64,
sketch: Sketch, sketch: Sketch,
tag: Option<TagNode>, tag: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
@ -838,12 +820,12 @@ async fn inner_angled_line_to_y(
})); }));
} }
let y_component = y_to - from.y; let y_component = y_to.to_length_units(from.units) - from.y;
let x_component = y_component / f64::tan(angle_degrees.to_radians()); let x_component = y_component / f64::tan(angle_degrees.to_radians());
let x_to = from.x + x_component; let x_to = from.x + x_component;
let new_sketch = straight_line( let new_sketch = straight_line(
StraightLineParams::absolute([x_to, y_to], sketch, tag), StraightLineParams::absolute([TyF64::new(x_to, from.units.into()), y_to], sketch, tag),
exec_state, exec_state,
args, args,
) )
@ -916,11 +898,18 @@ pub async fn inner_angled_line_that_intersects(
let from = sketch.current_pen_position()?; let from = sketch.current_pen_position()?;
let to = intersection_with_parallel_line( let to = intersection_with_parallel_line(
&[untype_point(path.get_from()).0, untype_point(path.get_to()).0], &[
offset.map(|t| t.n).unwrap_or_default(), point_to_len_unit(path.get_from(), from.units),
angle.n, point_to_len_unit(path.get_to(), from.units),
from.into(), ],
offset.map(|t| t.to_length_units(from.units)).unwrap_or_default(),
angle.to_degrees(),
from.ignore_units(),
); );
let to = [
TyF64::new(to[0], from.units.into()),
TyF64::new(to[1], from.units.into()),
];
straight_line(StraightLineParams::absolute(to, sketch, tag), exec_state, args).await straight_line(StraightLineParams::absolute(to, sketch, tag), exec_state, args).await
} }
@ -1309,7 +1298,7 @@ async fn make_sketch_plane_from_orientation(
pub async fn start_profile_at(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn start_profile_at(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (start, sketch_surface, tag) = args.get_data_and_sketch_surface()?; let (start, sketch_surface, tag) = args.get_data_and_sketch_surface()?;
let sketch = inner_start_profile_at([start[0].n, start[1].n], sketch_surface, tag, exec_state, args).await?; let sketch = inner_start_profile_at(start, sketch_surface, tag, exec_state, args).await?;
Ok(KclValue::Sketch { Ok(KclValue::Sketch {
value: Box::new(sketch), value: Box::new(sketch),
}) })
@ -1353,7 +1342,7 @@ pub async fn start_profile_at(exec_state: &mut ExecState, args: Args) -> Result<
name = "startProfileAt", name = "startProfileAt",
}] }]
pub(crate) async fn inner_start_profile_at( pub(crate) async fn inner_start_profile_at(
to: [f64; 2], to: [TyF64; 2],
sketch_surface: SketchSurface, sketch_surface: SketchSurface,
tag: Option<TagNode>, tag: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
@ -1409,7 +1398,7 @@ pub(crate) async fn inner_start_profile_at(
ModelingCmdReq { ModelingCmdReq {
cmd: ModelingCmd::from(mcmd::MovePathPen { cmd: ModelingCmd::from(mcmd::MovePathPen {
path: path_id.into(), path: path_id.into(),
to: KPoint2d::from(to).with_z(0.0).map(LengthUnit), to: KPoint2d::from(point_to_mm(to.clone())).with_z(0.0).map(LengthUnit),
}), }),
cmd_id: move_pen_id.into(), cmd_id: move_pen_id.into(),
}, },
@ -1420,11 +1409,12 @@ pub(crate) async fn inner_start_profile_at(
]) ])
.await?; .await?;
let (to, ty) = untype_point(to);
let current_path = BasePath { let current_path = BasePath {
from: to, from: to,
to, to,
tag: tag.clone(), tag: tag.clone(),
units: sketch_surface.units(), units: ty.expect_length(),
geo_meta: GeoMeta { geo_meta: GeoMeta {
id: move_pen_id, id: move_pen_id,
metadata: args.source_range.into(), metadata: args.source_range.into(),
@ -1437,7 +1427,7 @@ pub(crate) async fn inner_start_profile_at(
artifact_id: path_id.into(), artifact_id: path_id.into(),
on: sketch_surface.clone(), on: sketch_surface.clone(),
paths: vec![], paths: vec![],
units: sketch_surface.units(), units: ty.expect_length(),
mirror: Default::default(), mirror: Default::default(),
meta: vec![args.source_range.into()], meta: vec![args.source_range.into()],
tags: if let Some(tag) = &tag { tags: if let Some(tag) = &tag {
@ -1586,7 +1576,7 @@ pub(crate) async fn inner_close(
args: Args, args: Args,
) -> Result<Sketch, KclError> { ) -> Result<Sketch, KclError> {
let from = sketch.current_pen_position()?; let from = sketch.current_pen_position()?;
let to: Point2d = sketch.start.get_from().into(); let to = point_to_len_unit(sketch.start.get_from(), from.units);
let id = exec_state.next_uuid(); let id = exec_state.next_uuid();
@ -1595,8 +1585,8 @@ pub(crate) async fn inner_close(
let current_path = Path::ToPoint { let current_path = Path::ToPoint {
base: BasePath { base: BasePath {
from: from.into(), from: from.ignore_units(),
to: to.into(), to,
tag: tag.clone(), tag: tag.clone(),
units: sketch.units, units: sketch.units,
geo_meta: GeoMeta { geo_meta: GeoMeta {
@ -1708,7 +1698,6 @@ pub(crate) async fn inner_arc(
let from: Point2d = sketch.current_pen_position()?; let from: Point2d = sketch.current_pen_position()?;
let id = exec_state.next_uuid(); let id = exec_state.next_uuid();
// Relative case
match (angle_start, angle_end, radius, interior_absolute, end_absolute) { match (angle_start, angle_end, radius, interior_absolute, end_absolute) {
(Some(angle_start), Some(angle_end), Some(radius), None, None) => { (Some(angle_start), Some(angle_end), Some(radius), None, None) => {
relative_arc(&args, id, exec_state, sketch, from, angle_start, angle_end, radius, tag).await relative_arc(&args, id, exec_state, sketch, from, angle_start, angle_end, radius, tag).await
@ -1745,13 +1734,13 @@ pub async fn absolute_arc(
path: sketch.id.into(), path: sketch.id.into(),
segment: PathSegment::ArcTo { segment: PathSegment::ArcTo {
end: kcmc::shared::Point3d { end: kcmc::shared::Point3d {
x: LengthUnit(end_absolute[0].n), x: LengthUnit(end_absolute[0].to_mm()),
y: LengthUnit(end_absolute[1].n), y: LengthUnit(end_absolute[1].to_mm()),
z: LengthUnit(0.0), z: LengthUnit(0.0),
}, },
interior: kcmc::shared::Point3d { interior: kcmc::shared::Point3d {
x: LengthUnit(interior_absolute[0].n), x: LengthUnit(interior_absolute[0].to_mm()),
y: LengthUnit(interior_absolute[1].n), y: LengthUnit(interior_absolute[1].to_mm()),
z: LengthUnit(0.0), z: LengthUnit(0.0),
}, },
relative: false, relative: false,
@ -1761,13 +1750,12 @@ pub async fn absolute_arc(
.await?; .await?;
let start = [from.x, from.y]; let start = [from.x, from.y];
let end = end_absolute.clone(); let end = point_to_len_unit(end_absolute, from.units);
let untyped_end = untype_point(end);
let current_path = Path::ArcThreePoint { let current_path = Path::ArcThreePoint {
base: BasePath { base: BasePath {
from: from.into(), from: from.ignore_units(),
to: untyped_end.0, to: end,
tag: tag.clone(), tag: tag.clone(),
units: sketch.units, units: sketch.units,
geo_meta: GeoMeta { geo_meta: GeoMeta {
@ -1776,8 +1764,8 @@ pub async fn absolute_arc(
}, },
}, },
p1: start, p1: start,
p2: untype_point(interior_absolute).0, p2: point_to_len_unit(interior_absolute, from.units),
p3: untyped_end.0, p3: end,
}; };
let mut new_sketch = sketch.clone(); let mut new_sketch = sketch.clone();
@ -1802,16 +1790,17 @@ pub async fn relative_arc(
radius: TyF64, radius: TyF64,
tag: Option<TagNode>, tag: Option<TagNode>,
) -> Result<Sketch, KclError> { ) -> Result<Sketch, KclError> {
let a_start = Angle::from_degrees(angle_start.n); let a_start = Angle::from_degrees(angle_start.to_degrees());
let a_end = Angle::from_degrees(angle_end.n); let a_end = Angle::from_degrees(angle_end.to_degrees());
let (center, end) = arc_center_and_end(from.into(), a_start, a_end, radius.n); let radius = radius.to_length_units(from.units);
if angle_start == angle_end { let (center, end) = arc_center_and_end(from.ignore_units(), a_start, a_end, radius);
if a_start == a_end {
return Err(KclError::Type(KclErrorDetails { return Err(KclError::Type(KclErrorDetails {
message: "Arc start and end angles must be different".to_string(), message: "Arc start and end angles must be different".to_string(),
source_ranges: vec![args.source_range], source_ranges: vec![args.source_range],
})); }));
} }
let ccw = angle_start.n < angle_end.n; let ccw = a_start < a_end;
args.batch_modeling_cmd( args.batch_modeling_cmd(
id, id,
@ -1820,8 +1809,8 @@ pub async fn relative_arc(
segment: PathSegment::Arc { segment: PathSegment::Arc {
start: a_start, start: a_start,
end: a_end, end: a_end,
center: KPoint2d::from(center).map(LengthUnit), center: KPoint2d::from(untyped_point_to_mm(center, from.units)).map(LengthUnit),
radius: LengthUnit(radius.n), radius: LengthUnit(from.units.adjust_to(radius, UnitLen::Mm).0),
relative: false, relative: false,
}, },
}), }),
@ -1830,17 +1819,17 @@ pub async fn relative_arc(
let current_path = Path::Arc { let current_path = Path::Arc {
base: BasePath { base: BasePath {
from: from.into(), from: from.ignore_units(),
to: end, to: end,
tag: tag.clone(), tag: tag.clone(),
units: sketch.units, units: from.units,
geo_meta: GeoMeta { geo_meta: GeoMeta {
id, id,
metadata: args.source_range.into(), metadata: args.source_range.into(),
}, },
}, },
center, center,
radius: radius.n, radius,
ccw, ccw,
}; };
@ -1853,6 +1842,7 @@ pub async fn relative_arc(
Ok(new_sketch) Ok(new_sketch)
} }
/// Draw a tangential arc to a specific point. /// Draw a tangential arc to a specific point.
pub async fn tangential_arc(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn tangential_arc(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let sketch = let sketch =
@ -1863,17 +1853,7 @@ pub async fn tangential_arc(exec_state: &mut ExecState, args: Args) -> Result<Kc
let angle = args.get_kw_arg_opt_typed("angle", &RuntimeType::angle(), 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 tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
let new_sketch = inner_tangential_arc( let new_sketch = inner_tangential_arc(sketch, end_absolute, end, radius, angle, tag, exec_state, args).await?;
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 { Ok(KclValue::Sketch {
value: Box::new(new_sketch), value: Box::new(new_sketch),
}) })
@ -1949,8 +1929,8 @@ pub async fn tangential_arc(exec_state: &mut ExecState, args: Args) -> Result<Kc
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
async fn inner_tangential_arc( async fn inner_tangential_arc(
sketch: Sketch, sketch: Sketch,
end_absolute: Option<[f64; 2]>, end_absolute: Option<[TyF64; 2]>,
end: Option<[f64; 2]>, end: Option<[TyF64; 2]>,
radius: Option<TyF64>, radius: Option<TyF64>,
angle: Option<TyF64>, angle: Option<TyF64>,
tag: Option<TagNode>, tag: Option<TagNode>,
@ -2014,14 +1994,14 @@ async fn inner_tangential_arc_radius_angle(
let from: Point2d = sketch.current_pen_position()?; let from: Point2d = sketch.current_pen_position()?;
// next set of lines is some undocumented voodoo from get_tangential_arc_to_info // next set of lines is some undocumented voodoo from get_tangential_arc_to_info
let tangent_info = sketch.get_tangential_info_from_paths(); //this function desperately needs some documentation let tangent_info = sketch.get_tangential_info_from_paths(); //this function desperately needs some documentation
let tan_previous_point = tangent_info.tan_previous_point(from.into()); let tan_previous_point = tangent_info.tan_previous_point(from.ignore_units());
let id = exec_state.next_uuid(); let id = exec_state.next_uuid();
let (center, to, ccw) = match data { let (center, to, ccw) = match data {
TangentialArcData::RadiusAndOffset { radius, offset } => { TangentialArcData::RadiusAndOffset { radius, offset } => {
// KCL stdlib types use degrees. // KCL stdlib types use degrees.
let offset = Angle::from_degrees(offset.n); let offset = Angle::from_degrees(offset.to_degrees());
// Calculate the end point from the angle and radius. // Calculate the end point from the angle and radius.
// atan2 outputs radians. // atan2 outputs radians.
@ -2043,14 +2023,19 @@ async fn inner_tangential_arc_radius_angle(
// but the above logic *should* capture that behavior // but the above logic *should* capture that behavior
let start_angle = previous_end_tangent + tangent_to_arc_start_angle; let start_angle = previous_end_tangent + tangent_to_arc_start_angle;
let end_angle = start_angle + offset; let end_angle = start_angle + offset;
let (center, to) = arc_center_and_end(from.into(), start_angle, end_angle, radius.n); let (center, to) = arc_center_and_end(
from.ignore_units(),
start_angle,
end_angle,
radius.to_length_units(from.units),
);
args.batch_modeling_cmd( args.batch_modeling_cmd(
id, id,
ModelingCmd::from(mcmd::ExtendPath { ModelingCmd::from(mcmd::ExtendPath {
path: sketch.id.into(), path: sketch.id.into(),
segment: PathSegment::TangentialArc { segment: PathSegment::TangentialArc {
radius: LengthUnit(radius.n), radius: LengthUnit(radius.to_mm()),
offset, offset,
}, },
}), }),
@ -2064,7 +2049,7 @@ async fn inner_tangential_arc_radius_angle(
ccw, ccw,
center, center,
base: BasePath { base: BasePath {
from: from.into(), from: from.ignore_units(),
to, to,
tag: tag.clone(), tag: tag.clone(),
units: sketch.units, units: sketch.units,
@ -2085,19 +2070,22 @@ async fn inner_tangential_arc_radius_angle(
Ok(new_sketch) Ok(new_sketch)
} }
fn tan_arc_to(sketch: &Sketch, to: &[f64; 2]) -> ModelingCmd { // `to` must be in sketch.units
fn tan_arc_to(sketch: &Sketch, to: [f64; 2]) -> ModelingCmd {
ModelingCmd::from(mcmd::ExtendPath { ModelingCmd::from(mcmd::ExtendPath {
path: sketch.id.into(), path: sketch.id.into(),
segment: PathSegment::TangentialArcTo { segment: PathSegment::TangentialArcTo {
angle_snap_increment: None, angle_snap_increment: None,
to: KPoint2d::from(*to).with_z(0.0).map(LengthUnit), to: KPoint2d::from(untyped_point_to_mm(to, sketch.units))
.with_z(0.0)
.map(LengthUnit),
}, },
}) })
} }
async fn inner_tangential_arc_to_point( async fn inner_tangential_arc_to_point(
sketch: Sketch, sketch: Sketch,
point: [f64; 2], point: [TyF64; 2],
is_absolute: bool, is_absolute: bool,
tag: Option<TagNode>, tag: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
@ -2105,7 +2093,9 @@ async fn inner_tangential_arc_to_point(
) -> Result<Sketch, KclError> { ) -> Result<Sketch, KclError> {
let from: Point2d = sketch.current_pen_position()?; let from: Point2d = sketch.current_pen_position()?;
let tangent_info = sketch.get_tangential_info_from_paths(); let tangent_info = sketch.get_tangential_info_from_paths();
let tan_previous_point = tangent_info.tan_previous_point(from.into()); let tan_previous_point = tangent_info.tan_previous_point(from.ignore_units());
let point = point_to_len_unit(point, from.units);
let to = if is_absolute { let to = if is_absolute {
point point
@ -2115,7 +2105,7 @@ async fn inner_tangential_arc_to_point(
let [to_x, to_y] = to; let [to_x, to_y] = to;
let result = get_tangential_arc_to_info(TangentialArcInfoInput { let result = get_tangential_arc_to_info(TangentialArcInfoInput {
arc_start_point: [from.x, from.y], arc_start_point: [from.x, from.y],
arc_end_point: to, arc_end_point: [to_x, to_y],
tan_previous_point, tan_previous_point,
obtuse: true, obtuse: true,
}); });
@ -2142,11 +2132,11 @@ async fn inner_tangential_arc_to_point(
point point
}; };
let id = exec_state.next_uuid(); let id = exec_state.next_uuid();
args.batch_modeling_cmd(id, tan_arc_to(&sketch, &delta)).await?; args.batch_modeling_cmd(id, tan_arc_to(&sketch, delta)).await?;
let current_path = Path::TangentialArcTo { let current_path = Path::TangentialArcTo {
base: BasePath { base: BasePath {
from: from.into(), from: from.ignore_units(),
to, to,
tag: tag.clone(), tag: tag.clone(),
units: sketch.units, units: sketch.units,
@ -2227,7 +2217,10 @@ async fn inner_bezier_curve(
let relative = true; let relative = true;
let delta = end.clone(); let delta = end.clone();
let to = [from.x + end[0].n, from.y + end[1].n]; let to = [
from.x + end[0].to_length_units(from.units),
from.y + end[1].to_length_units(from.units),
];
let id = exec_state.next_uuid(); let id = exec_state.next_uuid();
@ -2236,9 +2229,9 @@ async fn inner_bezier_curve(
ModelingCmd::from(mcmd::ExtendPath { ModelingCmd::from(mcmd::ExtendPath {
path: sketch.id.into(), path: sketch.id.into(),
segment: PathSegment::Bezier { segment: PathSegment::Bezier {
control1: KPoint2d::from(untype_point(control1).0).with_z(0.0).map(LengthUnit), control1: KPoint2d::from(point_to_mm(control1)).with_z(0.0).map(LengthUnit),
control2: KPoint2d::from(untype_point(control2).0).with_z(0.0).map(LengthUnit), control2: KPoint2d::from(point_to_mm(control2)).with_z(0.0).map(LengthUnit),
end: KPoint2d::from(untype_point(delta).0).with_z(0.0).map(LengthUnit), end: KPoint2d::from(point_to_mm(delta)).with_z(0.0).map(LengthUnit),
relative, relative,
}, },
}), }),
@ -2247,7 +2240,7 @@ async fn inner_bezier_curve(
let current_path = Path::ToPoint { let current_path = Path::ToPoint {
base: BasePath { base: BasePath {
from: from.into(), from: from.ignore_units(),
to, to,
tag: tag.clone(), tag: tag.clone(),
units: sketch.units, units: sketch.units,

View File

@ -10,7 +10,10 @@ use serde::Serialize;
use super::{args::TyF64, DEFAULT_TOLERANCE}; use super::{args::TyF64, DEFAULT_TOLERANCE};
use crate::{ use crate::{
errors::KclError, errors::KclError,
execution::{types::RuntimeType, ExecState, Helix, KclValue, Sketch, Solid}, execution::{
types::{NumericType, RuntimeType},
ExecState, Helix, KclValue, Sketch, Solid,
},
parsing::ast::types::TagNode, parsing::ast::types::TagNode,
std::{extrude::do_post_extrude, Args}, std::{extrude::do_post_extrude, Args},
}; };
@ -27,21 +30,18 @@ pub enum SweepPath {
/// Extrude a sketch along a path. /// Extrude a sketch along a path.
pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?; let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
let path: SweepPath = args.get_kw_arg("path")?; let path: SweepPath = args.get_kw_arg_typed(
"path",
&RuntimeType::Union(vec![RuntimeType::sketch(), RuntimeType::helix()]),
exec_state,
)?;
let sectional = args.get_kw_arg_opt("sectional")?; let sectional = args.get_kw_arg_opt("sectional")?;
let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::count(), exec_state)?; let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::length(), exec_state)?;
let tag_start = args.get_kw_arg_opt("tagStart")?; let tag_start = args.get_kw_arg_opt("tagStart")?;
let tag_end = args.get_kw_arg_opt("tagEnd")?; let tag_end = args.get_kw_arg_opt("tagEnd")?;
let value = inner_sweep( let value = inner_sweep(
sketches, sketches, path, sectional, tolerance, tag_start, tag_end, exec_state, args,
path,
sectional,
tolerance.map(|t| t.n),
tag_start,
tag_end,
exec_state,
args,
) )
.await?; .await?;
Ok(value.into()) Ok(value.into())
@ -167,7 +167,7 @@ async fn inner_sweep(
sketches: Vec<Sketch>, sketches: Vec<Sketch>,
path: SweepPath, path: SweepPath,
sectional: Option<bool>, sectional: Option<bool>,
tolerance: Option<f64>, tolerance: Option<TyF64>,
tag_start: Option<TagNode>, tag_start: Option<TagNode>,
tag_end: Option<TagNode>, tag_end: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
@ -187,7 +187,7 @@ async fn inner_sweep(
target: sketch.id.into(), target: sketch.id.into(),
trajectory, trajectory,
sectional: sectional.unwrap_or(false), sectional: sectional.unwrap_or(false),
tolerance: LengthUnit(tolerance.unwrap_or(DEFAULT_TOLERANCE)), tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE)),
}), }),
) )
.await?; .await?;
@ -196,7 +196,7 @@ async fn inner_sweep(
do_post_extrude( do_post_extrude(
sketch, sketch,
id.into(), id.into(),
0.0, TyF64::new(0.0, NumericType::mm()),
sectional.unwrap_or(false), sectional.unwrap_or(false),
&super::extrude::NamedCapTags { &super::extrude::NamedCapTags {
start: tag_start.as_ref(), start: tag_start.as_ref(),

View File

@ -225,16 +225,7 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
})); }));
} }
let objects = inner_translate( let objects = inner_translate(objects, translate_x, translate_y, translate_z, global, exec_state, args).await?;
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()) Ok(objects.into())
} }
@ -397,9 +388,9 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
}] }]
async fn inner_translate( async fn inner_translate(
objects: SolidOrSketchOrImportedGeometry, objects: SolidOrSketchOrImportedGeometry,
x: Option<f64>, x: Option<TyF64>,
y: Option<f64>, y: Option<TyF64>,
z: Option<f64>, z: Option<TyF64>,
global: Option<bool>, global: Option<bool>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
@ -421,9 +412,9 @@ async fn inner_translate(
transforms: vec![shared::ComponentTransform { transforms: vec![shared::ComponentTransform {
translate: Some(shared::TransformBy::<Point3d<LengthUnit>> { translate: Some(shared::TransformBy::<Point3d<LengthUnit>> {
property: shared::Point3d { property: shared::Point3d {
x: LengthUnit(x.unwrap_or_default()), x: LengthUnit(x.as_ref().map(|t| t.to_mm()).unwrap_or_default()),
y: LengthUnit(y.unwrap_or_default()), y: LengthUnit(y.as_ref().map(|t| t.to_mm()).unwrap_or_default()),
z: LengthUnit(z.unwrap_or_default()), z: LengthUnit(z.as_ref().map(|t| t.to_mm()).unwrap_or_default()),
}, },
set: false, set: false,
is_local: !global.unwrap_or(false), is_local: !global.unwrap_or(false),
@ -451,11 +442,11 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
]), ]),
exec_state, exec_state,
)?; )?;
let roll: Option<TyF64> = args.get_kw_arg_opt_typed("roll", &RuntimeType::angle(), exec_state)?; let roll: Option<TyF64> = args.get_kw_arg_opt_typed("roll", &RuntimeType::degrees(), exec_state)?;
let pitch: Option<TyF64> = args.get_kw_arg_opt_typed("pitch", &RuntimeType::angle(), exec_state)?; let pitch: Option<TyF64> = args.get_kw_arg_opt_typed("pitch", &RuntimeType::degrees(), exec_state)?;
let yaw: Option<TyF64> = args.get_kw_arg_opt_typed("yaw", &RuntimeType::angle(), exec_state)?; let yaw: Option<TyF64> = args.get_kw_arg_opt_typed("yaw", &RuntimeType::degrees(), exec_state)?;
let axis: Option<[TyF64; 3]> = args.get_kw_arg_opt_typed("axis", &RuntimeType::point3d(), 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 angle: Option<TyF64> = args.get_kw_arg_opt_typed("angle", &RuntimeType::degrees(), exec_state)?;
let global = args.get_kw_arg_opt("global")?; let global = args.get_kw_arg_opt("global")?;
// Check if no rotation values are provided. // Check if no rotation values are provided.
@ -544,7 +535,9 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
roll.map(|t| t.n), roll.map(|t| t.n),
pitch.map(|t| t.n), pitch.map(|t| t.n),
yaw.map(|t| t.n), yaw.map(|t| t.n),
axis.map(|p| [p[0].n, p[1].n, p[2].n]), // Don't adjust axis units since the axis must be normalized and only the direction
// should be significant, not the magnitude.
axis.map(|a| [a[0].n, a[1].n, a[2].n]),
angle.map(|t| t.n), angle.map(|t| t.n),
global, global,
exec_state, exec_state,
@ -780,7 +773,7 @@ async fn inner_rotate(
for object_id in objects.ids(&args.ctx).await? { for object_id in objects.ids(&args.ctx).await? {
let id = exec_state.next_uuid(); let id = exec_state.next_uuid();
if let (Some(axis), Some(angle)) = (axis, angle) { if let (Some(axis), Some(angle)) = (&axis, angle) {
args.batch_modeling_cmd( args.batch_modeling_cmd(
id, id,
ModelingCmd::from(mcmd::SetObjectTransform { ModelingCmd::from(mcmd::SetObjectTransform {

View File

@ -5,20 +5,28 @@ use kcl_derive_docs::stdlib;
use crate::{ use crate::{
errors::KclError, errors::KclError,
execution::{types::UnitLen, ExecState, KclValue}, execution::{
types::{RuntimeType, UnitLen},
ExecState, KclValue,
},
std::{args::TyF64, Args}, std::{args::TyF64, Args},
}; };
/// Millimeters conversion factor for current files units. /// Millimeters conversion factor for current files units.
pub async fn from_mm(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn from_mm(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let input = args.get_number_with_type()?; let input = args.get_number_typed(&RuntimeType::known_length(UnitLen::Mm), exec_state)?;
let result = inner_from_mm(input.n, exec_state)?; let result = inner_from_mm(input, exec_state)?;
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units()))) Ok(args.make_user_val_from_f64_with_type(TyF64::new(
result,
exec_state.current_default_units().expect_default_length(),
)))
} }
/// Converts a number from mm to the current default unit. /// Converts a number from mm to the current default unit.
/// ///
/// *DEPRECATED* prefer using explicit numeric suffixes (e.g., `42mm`) or the `to...` conversion functions.
///
/// No matter what units the current file uses, this function will always return a number equivalent /// No matter what units the current file uses, this function will always return a number equivalent
/// to the input in millimeters. /// to the input in millimeters.
/// ///
@ -39,6 +47,7 @@ pub async fn from_mm(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
#[stdlib { #[stdlib {
name = "fromMm", name = "fromMm",
tags = ["units"], tags = ["units"],
deprecated = true,
}] }]
fn inner_from_mm(input: f64, exec_state: &ExecState) -> Result<f64, KclError> { fn inner_from_mm(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
Ok(match exec_state.length_unit() { Ok(match exec_state.length_unit() {
@ -54,14 +63,19 @@ fn inner_from_mm(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
/// Inches conversion factor for current files units. /// Inches conversion factor for current files units.
pub async fn from_inches(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn from_inches(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let input = args.get_number_with_type()?; let input = args.get_number_typed(&RuntimeType::known_length(UnitLen::Inches), exec_state)?;
let result = inner_from_inches(input.n, exec_state)?; let result = inner_from_inches(input, exec_state)?;
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units()))) Ok(args.make_user_val_from_f64_with_type(TyF64::new(
result,
exec_state.current_default_units().expect_default_length(),
)))
} }
/// Converts a number from inches to the current default unit. /// Converts a number from inches to the current default unit.
/// ///
/// *DEPRECATED* prefer using explicit numeric suffixes (e.g., `42inch`) or the `to...` conversion functions.
///
/// No matter what units the current file uses, this function will always return a number equivalent /// No matter what units the current file uses, this function will always return a number equivalent
/// to the input in inches. /// to the input in inches.
/// ///
@ -82,6 +96,7 @@ pub async fn from_inches(exec_state: &mut ExecState, args: Args) -> Result<KclVa
#[stdlib { #[stdlib {
name = "fromInches", name = "fromInches",
tags = ["units"], tags = ["units"],
deprecated = true,
}] }]
fn inner_from_inches(input: f64, exec_state: &ExecState) -> Result<f64, KclError> { fn inner_from_inches(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
match exec_state.length_unit() { match exec_state.length_unit() {
@ -97,14 +112,19 @@ fn inner_from_inches(input: f64, exec_state: &ExecState) -> Result<f64, KclError
/// Feet conversion factor for current files units. /// Feet conversion factor for current files units.
pub async fn from_ft(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn from_ft(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let input = args.get_number_with_type()?; let input = args.get_number_typed(&RuntimeType::known_length(UnitLen::Feet), exec_state)?;
let result = inner_from_ft(input.n, exec_state)?; let result = inner_from_ft(input, exec_state)?;
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units()))) Ok(args.make_user_val_from_f64_with_type(TyF64::new(
result,
exec_state.current_default_units().expect_default_length(),
)))
} }
/// Converts a number from feet to the current default unit. /// Converts a number from feet to the current default unit.
/// ///
/// *DEPRECATED* prefer using explicit numeric suffixes (e.g., `42ft`) or the `to...` conversion functions.
///
/// No matter what units the current file uses, this function will always return a number equivalent /// No matter what units the current file uses, this function will always return a number equivalent
/// to the input in feet. /// to the input in feet.
/// ///
@ -126,6 +146,7 @@ pub async fn from_ft(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
#[stdlib { #[stdlib {
name = "fromFt", name = "fromFt",
tags = ["units"], tags = ["units"],
deprecated = true,
}] }]
fn inner_from_ft(input: f64, exec_state: &ExecState) -> Result<f64, KclError> { fn inner_from_ft(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
match exec_state.length_unit() { match exec_state.length_unit() {
@ -141,14 +162,19 @@ fn inner_from_ft(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
/// Meters conversion factor for current files units. /// Meters conversion factor for current files units.
pub async fn from_m(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn from_m(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let input = args.get_number_with_type()?; let input = args.get_number_typed(&RuntimeType::known_length(UnitLen::M), exec_state)?;
let result = inner_from_m(input.n, exec_state)?; let result = inner_from_m(input, exec_state)?;
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units()))) Ok(args.make_user_val_from_f64_with_type(TyF64::new(
result,
exec_state.current_default_units().expect_default_length(),
)))
} }
/// Converts a number from meters to the current default unit. /// Converts a number from meters to the current default unit.
/// ///
/// *DEPRECATED* prefer using explicit numeric suffixes (e.g., `42m`) or the `to...` conversion functions.
///
/// No matter what units the current file uses, this function will always return a number equivalent /// No matter what units the current file uses, this function will always return a number equivalent
/// to the input in meters. /// to the input in meters.
/// ///
@ -170,6 +196,7 @@ pub async fn from_m(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
#[stdlib { #[stdlib {
name = "fromM", name = "fromM",
tags = ["units"], tags = ["units"],
deprecated = true,
}] }]
fn inner_from_m(input: f64, exec_state: &ExecState) -> Result<f64, KclError> { fn inner_from_m(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
match exec_state.length_unit() { match exec_state.length_unit() {
@ -185,14 +212,19 @@ fn inner_from_m(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
/// Centimeters conversion factor for current files units. /// Centimeters conversion factor for current files units.
pub async fn from_cm(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn from_cm(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let input = args.get_number_with_type()?; let input = args.get_number_typed(&RuntimeType::known_length(UnitLen::Cm), exec_state)?;
let result = inner_from_cm(input.n, exec_state)?; let result = inner_from_cm(input, exec_state)?;
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units()))) Ok(args.make_user_val_from_f64_with_type(TyF64::new(
result,
exec_state.current_default_units().expect_default_length(),
)))
} }
/// Converts a number from centimeters to the current default unit. /// Converts a number from centimeters to the current default unit.
/// ///
/// *DEPRECATED* prefer using explicit numeric suffixes (e.g., `42cm`) or the `to...` conversion functions.
///
/// No matter what units the current file uses, this function will always return a number equivalent /// No matter what units the current file uses, this function will always return a number equivalent
/// to the input in centimeters. /// to the input in centimeters.
/// ///
@ -214,6 +246,7 @@ pub async fn from_cm(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
#[stdlib { #[stdlib {
name = "fromCm", name = "fromCm",
tags = ["units"], tags = ["units"],
deprecated = true,
}] }]
fn inner_from_cm(input: f64, exec_state: &ExecState) -> Result<f64, KclError> { fn inner_from_cm(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
match exec_state.length_unit() { match exec_state.length_unit() {
@ -229,14 +262,19 @@ fn inner_from_cm(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
/// Yards conversion factor for current files units. /// Yards conversion factor for current files units.
pub async fn from_yd(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn from_yd(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let input = args.get_number_with_type()?; let input = args.get_number_typed(&RuntimeType::known_length(UnitLen::Yards), exec_state)?;
let result = inner_from_yd(input.n, exec_state)?; let result = inner_from_yd(input, exec_state)?;
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units()))) Ok(args.make_user_val_from_f64_with_type(TyF64::new(
result,
exec_state.current_default_units().expect_default_length(),
)))
} }
/// Converts a number from yards to the current default unit. /// Converts a number from yards to the current default unit.
/// ///
/// *DEPRECATED* prefer using explicit numeric suffixes (e.g., `42yd`) or the `to...` conversion functions.
///
/// No matter what units the current file uses, this function will always return a number equivalent /// No matter what units the current file uses, this function will always return a number equivalent
/// to the input in yards. /// to the input in yards.
/// ///
@ -258,6 +296,7 @@ pub async fn from_yd(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
#[stdlib { #[stdlib {
name = "fromYd", name = "fromYd",
tags = ["units"], tags = ["units"],
deprecated = true,
}] }]
fn inner_from_yd(input: f64, exec_state: &ExecState) -> Result<f64, KclError> { fn inner_from_yd(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
match exec_state.length_unit() { match exec_state.length_unit() {

View File

@ -2,39 +2,54 @@ use std::f64::consts::PI;
use kittycad_modeling_cmds::shared::Angle; use kittycad_modeling_cmds::shared::Angle;
use crate::{ use crate::execution::types::{NumericType, UnitLen};
errors::{KclError, KclErrorDetails},
execution::{types::NumericType, Point2d},
source_range::SourceRange,
};
use super::args::TyF64; use super::args::TyF64;
pub fn untype_point(p: [TyF64; 2]) -> ([f64; 2], NumericType) { pub(crate) fn untype_point(p: [TyF64; 2]) -> ([f64; 2], NumericType) {
let (x, y, ty) = NumericType::combine_eq(p[0].clone(), p[1].clone()); let (x, y, ty) = NumericType::combine_eq(p[0].clone(), p[1].clone());
([x, y], ty) ([x, y], ty)
} }
pub fn untype_point_3d(p: [TyF64; 3]) -> ([f64; 3], NumericType) { pub(crate) fn point_to_mm(p: [TyF64; 2]) -> [f64; 2] {
let (arr, ty) = NumericType::combine_eq_array(&[p[0].clone(), p[1].clone(), p[2].clone()]); [p[0].to_mm(), p[1].to_mm()]
let mut iter = arr.into_iter(); }
([iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap()], ty)
pub(crate) fn untyped_point_to_mm(p: [f64; 2], units: UnitLen) -> [f64; 2] {
assert_ne!(units, UnitLen::Unknown);
[
units.adjust_to(p[0], UnitLen::Mm).0,
units.adjust_to(p[1], UnitLen::Mm).0,
]
}
pub(crate) fn point_to_len_unit(p: [TyF64; 2], len: UnitLen) -> [f64; 2] {
[p[0].to_length_units(len), p[1].to_length_units(len)]
}
/// Precondition, `p` must be in `len` units (this function does no conversion).
pub(crate) fn point_to_typed(p: [f64; 2], len: UnitLen) -> [TyF64; 2] {
[TyF64::new(p[0], len.into()), TyF64::new(p[1], len.into())]
}
pub(crate) fn point_3d_to_mm(p: [TyF64; 3]) -> [f64; 3] {
[p[0].to_mm(), p[1].to_mm(), p[2].to_mm()]
} }
/// Get the distance between two points. /// Get the distance between two points.
pub fn distance(a: Coords2d, b: Coords2d) -> f64 { pub(crate) fn distance(a: Coords2d, b: Coords2d) -> f64 {
((b[0] - a[0]).powi(2) + (b[1] - a[1]).powi(2)).sqrt() ((b[0] - a[0]).powi(2) + (b[1] - a[1]).powi(2)).sqrt()
} }
/// Get the angle between these points /// Get the angle between these points
pub fn between(a: Point2d, b: Point2d) -> Angle { pub(crate) fn between(a: Coords2d, b: Coords2d) -> Angle {
let x = b.x - a.x; let x = b[0] - a[0];
let y = b.y - a.y; let y = b[1] - a[1];
normalize(Angle::from_radians(y.atan2(x))) normalize(Angle::from_radians(y.atan2(x)))
} }
/// Normalize the angle /// Normalize the angle
pub fn normalize(angle: Angle) -> Angle { pub(crate) fn normalize(angle: Angle) -> Angle {
let deg = angle.to_degrees(); let deg = angle.to_degrees();
let result = ((deg % 360.0) + 360.0) % 360.0; let result = ((deg % 360.0) + 360.0) % 360.0;
Angle::from_degrees(if result > 180.0 { result - 360.0 } else { result }) Angle::from_degrees(if result > 180.0 { result - 360.0 } else { result })
@ -55,7 +70,7 @@ pub fn normalize(angle: Angle) -> Angle {
/// Angle::from_radians(PI / 8.0) /// Angle::from_radians(PI / 8.0)
/// ); /// );
/// ``` /// ```
pub fn delta(from_angle: Angle, to_angle: Angle) -> Angle { pub(crate) fn delta(from_angle: Angle, to_angle: Angle) -> Angle {
let norm_from_angle = normalize_rad(from_angle.to_radians()); let norm_from_angle = normalize_rad(from_angle.to_radians());
let norm_to_angle = normalize_rad(to_angle.to_radians()); let norm_to_angle = normalize_rad(to_angle.to_radians());
let provisional = norm_to_angle - norm_from_angle; let provisional = norm_to_angle - norm_from_angle;
@ -72,7 +87,7 @@ pub fn delta(from_angle: Angle, to_angle: Angle) -> Angle {
Angle::default() Angle::default()
} }
pub fn normalize_rad(angle: f64) -> f64 { pub(crate) fn normalize_rad(angle: f64) -> f64 {
let draft = angle % (2.0 * PI); let draft = angle % (2.0 * PI);
if draft < 0.0 { if draft < 0.0 {
draft + 2.0 * PI draft + 2.0 * PI
@ -106,7 +121,7 @@ fn intersect(p1: Coords2d, p2: Coords2d, p3: Coords2d, p4: Coords2d) -> Coords2d
[x, y] [x, y]
} }
pub fn intersection_with_parallel_line( pub(crate) fn intersection_with_parallel_line(
line1: &[Coords2d; 2], line1: &[Coords2d; 2],
line1_offset: f64, line1_offset: f64,
line2_angle: f64, line2_angle: f64,
@ -128,7 +143,7 @@ fn offset_line(offset: f64, p1: Coords2d, p2: Coords2d) -> [Coords2d; 2] {
[[p1[0] + x_offset, p1[1]], [p2[0] + x_offset, p2[1]]] [[p1[0] + x_offset, p1[1]], [p2[0] + x_offset, p2[1]]]
} }
pub fn get_y_component(angle: Angle, x: f64) -> Coords2d { pub(crate) 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 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 y = x * f64::tan(normalised_angle.to_radians());
let sign = if normalised_angle > 90.0 && normalised_angle <= 270.0 { let sign = if normalised_angle > 90.0 && normalised_angle <= 270.0 {
@ -139,7 +154,7 @@ pub fn get_y_component(angle: Angle, x: f64) -> Coords2d {
[x * sign, y * sign] [x * sign, y * sign]
} }
pub fn get_x_component(angle: Angle, y: f64) -> Coords2d { pub(crate) 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 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 x = y / f64::tan(normalised_angle.to_radians());
let sign = if normalised_angle > 180.0 && normalised_angle <= 360.0 { let sign = if normalised_angle > 180.0 && normalised_angle <= 360.0 {
@ -150,7 +165,12 @@ pub fn get_x_component(angle: Angle, y: f64) -> Coords2d {
[x * sign, y * sign] [x * sign, y * sign]
} }
pub fn arc_center_and_end(from: Coords2d, start_angle: Angle, end_angle: Angle, radius: f64) -> (Coords2d, Coords2d) { pub(crate) 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 start_angle = start_angle.to_radians();
let end_angle = end_angle.to_radians(); let end_angle = end_angle.to_radians();
@ -167,56 +187,9 @@ pub fn arc_center_and_end(from: Coords2d, start_angle: Angle, end_angle: Angle,
(center, end) (center, end)
} }
pub fn arc_angles(
from: Coords2d,
to: Coords2d,
center: Coords2d,
radius: f64,
source_range: SourceRange,
) -> Result<(Angle, Angle), KclError> {
// First make sure that the points are on the circumference of the circle.
// If not, we'll return an error.
if !is_on_circumference(center, from, radius) {
return Err(KclError::Semantic(KclErrorDetails {
message: format!(
"Point {:?} is not on the circumference of the circle with center {:?} and radius {}.",
from, center, radius
),
source_ranges: vec![source_range],
}));
}
if !is_on_circumference(center, to, radius) {
return Err(KclError::Semantic(KclErrorDetails {
message: format!(
"Point {:?} is not on the circumference of the circle with center {:?} and radius {}.",
to, center, radius
),
source_ranges: vec![source_range],
}));
}
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)))
}
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);
// We'll check if the distance squared is approximately equal to radius squared.
// Due to potential floating point inaccuracies, we'll check if the difference
// is very small (e.g., 1e-9) rather than checking for strict equality.
(distance_squared - radius.powi(2)).abs() < 1e-9
}
// Calculate the center of 3 points using an algebraic method // Calculate the center of 3 points using an algebraic method
// Handles if 3 points lie on the same line (collinear) by returning the average of the points (could return None instead..) // Handles if 3 points lie on the same line (collinear) by returning the average of the points (could return None instead..)
pub fn calculate_circle_center(p1: [f64; 2], p2: [f64; 2], p3: [f64; 2]) -> [f64; 2] { pub(crate) fn calculate_circle_center(p1: [f64; 2], p2: [f64; 2], p3: [f64; 2]) -> [f64; 2] {
let (x1, y1) = (p1[0], p1[1]); let (x1, y1) = (p1[0], p1[1]);
let (x2, y2) = (p2[0], p2[1]); let (x2, y2) = (p2[0], p2[1]);
let (x3, y3) = (p3[0], p3[1]); let (x3, y3) = (p3[0], p3[1]);
@ -268,7 +241,6 @@ mod tests {
use std::f64::consts::TAU; use std::f64::consts::TAU;
use super::{calculate_circle_center, get_x_component, get_y_component, Angle}; use super::{calculate_circle_center, get_x_component, get_y_component, Angle};
use crate::SourceRange;
static EACH_QUAD: [(i32, [i32; 2]); 12] = [ static EACH_QUAD: [(i32, [i32; 2]); 12] = [
(-315, [1, 1]), (-315, [1, 1]),
@ -366,34 +338,6 @@ mod tests {
assert_eq!(end[1].round(), 0.0); assert_eq!(end[1].round(), 0.0);
} }
#[test]
fn test_arc_angles() {
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([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([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([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 [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");
}
assert_eq!(angle_start.to_degrees().round(), 0.0);
assert_eq!(angle_end.to_degrees().round(), 180.0);
}
#[test] #[test]
fn test_calculate_circle_center() { fn test_calculate_circle_center() {
const EPS: f64 = 1e-4; const EPS: f64 = 1e-4;
@ -464,7 +408,7 @@ mod tests {
} }
} }
pub type Coords2d = [f64; 2]; pub(crate) type Coords2d = [f64; 2];
pub fn is_points_ccw_wasm(points: &[f64]) -> i32 { pub fn is_points_ccw_wasm(points: &[f64]) -> i32 {
// CCW is positive as that the Math convention // CCW is positive as that the Math convention
@ -478,7 +422,7 @@ pub fn is_points_ccw_wasm(points: &[f64]) -> i32 {
sum.signum() as i32 sum.signum() as i32
} }
pub fn is_points_ccw(points: &[Coords2d]) -> i32 { pub(crate) fn is_points_ccw(points: &[Coords2d]) -> i32 {
let flattened_points: Vec<f64> = points.iter().flat_map(|&p| vec![p[0], p[1]]).collect(); let flattened_points: Vec<f64> = points.iter().flat_map(|&p| vec![p[0], p[1]]).collect();
is_points_ccw_wasm(&flattened_points) is_points_ccw_wasm(&flattened_points)
} }
@ -587,7 +531,6 @@ pub struct TangentialArcInfoInput {
} }
/// Structure to hold the output data from calculating tangential arc information. /// Structure to hold the output data from calculating tangential arc information.
#[allow(dead_code)]
pub struct TangentialArcInfoOutput { pub struct TangentialArcInfoOutput {
/// The center point of the arc. /// The center point of the arc.
pub center: Coords2d, pub center: Coords2d,
@ -851,7 +794,7 @@ mod get_tangential_arc_to_info_tests {
} }
} }
pub fn get_tangent_point_from_previous_arc( pub(crate) fn get_tangent_point_from_previous_arc(
last_arc_center: Coords2d, last_arc_center: Coords2d,
last_arc_ccw: bool, last_arc_ccw: bool,
last_arc_end: Coords2d, last_arc_end: Coords2d,

View File

@ -35,7 +35,7 @@ pub enum Node<'a> {
IfExpression(NodeRef<'a, types::IfExpression>), IfExpression(NodeRef<'a, types::IfExpression>),
ElseIf(&'a types::ElseIf), ElseIf(&'a types::ElseIf),
LabelledExpression(NodeRef<'a, types::LabelledExpression>), LabelledExpression(NodeRef<'a, types::LabelledExpression>),
Ascription(NodeRef<'a, types::Ascription>), AscribedExpression(NodeRef<'a, types::AscribedExpression>),
Parameter(&'a types::Parameter), Parameter(&'a types::Parameter),
@ -79,7 +79,7 @@ impl Node<'_> {
Node::ElseIf(n) => n.digest, Node::ElseIf(n) => n.digest,
Node::KclNone(n) => n.digest, Node::KclNone(n) => n.digest,
Node::LabelledExpression(n) => n.digest, Node::LabelledExpression(n) => n.digest,
Node::Ascription(n) => n.digest, Node::AscribedExpression(n) => n.digest,
} }
} }
@ -124,7 +124,7 @@ impl Node<'_> {
Node::ElseIf(n) => *n as *const _ as *const (), Node::ElseIf(n) => *n as *const _ as *const (),
Node::KclNone(n) => *n as *const _ as *const (), Node::KclNone(n) => *n as *const _ as *const (),
Node::LabelledExpression(n) => *n as *const _ as *const (), Node::LabelledExpression(n) => *n as *const _ as *const (),
Node::Ascription(n) => *n as *const _ as *const (), Node::AscribedExpression(n) => *n as *const _ as *const (),
} }
} }
} }
@ -167,7 +167,7 @@ impl TryFrom<&Node<'_>> for SourceRange {
Node::ObjectProperty(n) => SourceRange::from(*n), Node::ObjectProperty(n) => SourceRange::from(*n),
Node::IfExpression(n) => SourceRange::from(*n), Node::IfExpression(n) => SourceRange::from(*n),
Node::LabelledExpression(n) => SourceRange::from(*n), Node::LabelledExpression(n) => SourceRange::from(*n),
Node::Ascription(n) => SourceRange::from(*n), Node::AscribedExpression(n) => SourceRange::from(*n),
// This is broken too // This is broken too
Node::ElseIf(n) => SourceRange::new(n.cond.start(), n.cond.end(), n.cond.module_id()), Node::ElseIf(n) => SourceRange::new(n.cond.start(), n.cond.end(), n.cond.module_id()),
@ -296,7 +296,7 @@ impl_from_ref!(Node, Parameter);
impl_from!(Node, IfExpression); impl_from!(Node, IfExpression);
impl_from!(Node, ElseIf); impl_from!(Node, ElseIf);
impl_from!(Node, LabelledExpression); impl_from!(Node, LabelledExpression);
impl_from!(Node, Ascription); impl_from!(Node, AscribedExpression);
impl_from!(Node, KclNone); impl_from!(Node, KclNone);
#[cfg(test)] #[cfg(test)]

View File

@ -131,7 +131,7 @@ impl<'tree> Visitable<'tree> for Node<'tree> {
Node::LabelledExpression(e) => { Node::LabelledExpression(e) => {
vec![(&e.expr).into(), (&e.label).into()] vec![(&e.expr).into(), (&e.label).into()]
} }
Node::Ascription(e) => { Node::AscribedExpression(e) => {
vec![(&e.expr).into()] vec![(&e.expr).into()]
} }
Node::Name(n) => Some((&n.name).into()) Node::Name(n) => Some((&n.name).into())

View File

@ -54,7 +54,7 @@ export TAU = 6.28318530717958647692528676655900577_
/// |> startProfileAt([0, 0], %) /// |> startProfileAt([0, 0], %)
/// |> angledLine( /// |> angledLine(
/// angle = 30, /// angle = 30,
/// length = 3 / cos(toRadians(30)), /// length = 3 / cos(30deg),
/// ) /// )
/// |> yLine(endAbsolute = 0) /// |> yLine(endAbsolute = 0)
/// |> close() /// |> close()
@ -71,7 +71,7 @@ export fn cos(@num: number(Angle)): number(_) {}
/// |> startProfileAt([0, 0], %) /// |> startProfileAt([0, 0], %)
/// |> angledLine( /// |> angledLine(
/// angle = 50, /// angle = 50,
/// length = 15 / sin(toRadians(135)), /// length = 15 / sin(135deg),
/// ) /// )
/// |> yLine(endAbsolute = 0) /// |> yLine(endAbsolute = 0)
/// |> close() /// |> close()
@ -88,7 +88,7 @@ export fn sin(@num: number(Angle)): number(_) {}
/// |> startProfileAt([0, 0], %) /// |> startProfileAt([0, 0], %)
/// |> angledLine( /// |> angledLine(
/// angle = 50, /// angle = 50,
/// length = 50 * tan(1/2), /// length = 50 * tan((1/2): number(rad)),
/// ) /// )
/// |> yLine(endAbsolute = 0) /// |> yLine(endAbsolute = 0)
/// |> close() /// |> close()
@ -112,10 +112,8 @@ export fn tan(@num: number(Angle)): number(_) {}
/// ///
/// example = extrude(exampleSketch, length = 5) /// example = extrude(exampleSketch, length = 5)
/// ``` /// ```
export fn polar(angle: number(Angle), length: number(Length)): Point2d { export fn polar(angle: number(rad), length: number(Length)): Point2d {
// TODO could be done by implicit conversion when UoM coercions are activated. x = length * cos(angle)
rads = toRadians(angle) y = length * sin(angle)
x = length * cos(rads)
y = length * sin(rads)
return [x, y] return [x, y]
} }

View File

@ -128,7 +128,7 @@ export END = 'end'
export fn helix( export fn helix(
/// Number of revolutions. /// Number of revolutions.
revolutions: number(_), revolutions: number(_),
/// Start angle (in degrees). /// Start angle.
angleStart: number(Angle), angleStart: number(Angle),
/// Is the helix rotation counter clockwise? The default is `false`. /// Is the helix rotation counter clockwise? The default is `false`.
ccw?: bool, ccw?: bool,
@ -383,3 +383,69 @@ export fn revolve(
/// A named tag for the face at the end of the revolve. /// A named tag for the face at the end of the revolve.
tagEnd?: tag, tagEnd?: tag,
): Solid {} ): Solid {}
/// Convert a number to millimeters from its current units.
export fn toMillimeters(@num: number(mm)): number(mm) {
return num
}
/// Convert a number to centimeters from its current units.
export fn toCentimeters(@num: number(cm)): number(cm) {
return num
}
/// Convert a number to meters from its current units.
export fn toMeters(@num: number(m)): number(m) {
return num
}
/// Convert a number to inches from its current units.
export fn toInches(@num: number(in)): number(in) {
return num
}
/// Convert a number to feet from its current units.
export fn toFeet(@num: number(ft)): number(ft) {
return num
}
/// Converts a number to yards from its current units.
export fn toYards(@num: number(yd)): number(yd) {
return num
}
/// Converts a number to radians from its current units.
///
/// ```
/// exampleSketch = startSketchOn(XZ)
/// |> startProfileAt([0, 0], %)
/// |> angledLine(
/// angle = 50,
/// length = 70 * cos(toRadians(45)),
/// )
/// |> yLine(endAbsolute = 0)
/// |> close()
///
/// example = extrude(exampleSketch, length = 5)
/// ```
export fn toRadians(@num: number(rad)): number(rad) {
return num
}
/// Converts a number to degrees from its current units.
///
/// ```
/// exampleSketch = startSketchOn(XZ)
/// |> startProfileAt([0, 0], %)
/// |> angledLine(
/// angle = 50,
/// length = 70 * cos(toDegrees((PI/4): number(rad))),
/// )
/// |> yLine(endAbsolute = 0)
/// |> close()
///
/// example = extrude(exampleSketch, length = 5)
/// ```
export fn toDegrees(@num: number(deg)): number(deg) {
return num
}

View File

@ -28,37 +28,5 @@ description: Artifact commands add_lots.kcl
"object_id": "[uuid]", "object_id": "[uuid]",
"hidden": true "hidden": true
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -360,37 +360,5 @@ description: Artifact commands angled_line.kcl
"edge_id": "[uuid]", "edge_id": "[uuid]",
"face_id": "[uuid]" "face_id": "[uuid]"
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -220,9 +220,6 @@ description: Variables in memory after executing angled_line.kcl
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {

View File

@ -28,37 +28,5 @@ description: Artifact commands argument_error.kcl
"object_id": "[uuid]", "object_id": "[uuid]",
"hidden": true "hidden": true
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -28,37 +28,5 @@ description: Artifact commands array_elem_pop.kcl
"object_id": "[uuid]", "object_id": "[uuid]",
"hidden": true "hidden": true
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -28,37 +28,5 @@ description: Artifact commands array_elem_pop_empty_fail.kcl
"object_id": "[uuid]", "object_id": "[uuid]",
"hidden": true "hidden": true
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -28,37 +28,5 @@ description: Artifact commands array_elem_pop_fail.kcl
"object_id": "[uuid]", "object_id": "[uuid]",
"hidden": true "hidden": true
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -28,37 +28,5 @@ description: Artifact commands array_elem_push.kcl
"object_id": "[uuid]", "object_id": "[uuid]",
"hidden": true "hidden": true
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -28,37 +28,5 @@ description: Artifact commands array_elem_push_fail.kcl
"object_id": "[uuid]", "object_id": "[uuid]",
"hidden": true "hidden": true
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -28,37 +28,5 @@ description: Artifact commands array_index_oob.kcl
"object_id": "[uuid]", "object_id": "[uuid]",
"hidden": true "hidden": true
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -28,37 +28,5 @@ description: Artifact commands array_range_expr.kcl
"object_id": "[uuid]", "object_id": "[uuid]",
"hidden": true "hidden": true
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -28,37 +28,5 @@ description: Artifact commands array_range_negative_expr.kcl
"object_id": "[uuid]", "object_id": "[uuid]",
"hidden": true "hidden": true
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -520,37 +520,5 @@ description: Artifact commands artifact_graph_example_code1.kcl
"edge_id": "[uuid]", "edge_id": "[uuid]",
"face_id": "[uuid]" "face_id": "[uuid]"
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -199,9 +199,6 @@ description: Variables in memory after executing artifact_graph_example_code1.kc
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {
@ -599,9 +596,6 @@ description: Variables in memory after executing artifact_graph_example_code1.kc
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {
@ -861,9 +855,6 @@ description: Variables in memory after executing artifact_graph_example_code1.kc
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {
@ -1207,9 +1198,6 @@ description: Variables in memory after executing artifact_graph_example_code1.kc
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {

View File

@ -291,37 +291,5 @@ description: Artifact commands artifact_graph_example_code_no_3d.kcl
"angle_snap_increment": null "angle_snap_increment": null
} }
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -174,9 +174,6 @@ description: Variables in memory after executing artifact_graph_example_code_no_
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {
@ -328,9 +325,6 @@ description: Variables in memory after executing artifact_graph_example_code_no_
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {

View File

@ -214,37 +214,5 @@ description: Artifact commands artifact_graph_example_code_offset_planes.kcl
"object_id": "[uuid]", "object_id": "[uuid]",
"hidden": true "hidden": true
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -40,9 +40,6 @@ description: Variables in memory after executing artifact_graph_example_code_off
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
} }
}, },
@ -83,9 +80,6 @@ description: Variables in memory after executing artifact_graph_example_code_off
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
} }
}, },
@ -126,9 +120,6 @@ description: Variables in memory after executing artifact_graph_example_code_off
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
} }
}, },
@ -194,9 +185,6 @@ description: Variables in memory after executing artifact_graph_example_code_off
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {

View File

@ -881,37 +881,5 @@ description: Artifact commands artifact_graph_sketch_on_face_etc.kcl
"edge_id": "[uuid]", "edge_id": "[uuid]",
"face_id": "[uuid]" "face_id": "[uuid]"
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -161,9 +161,6 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {
@ -499,9 +496,6 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {
@ -1021,9 +1015,6 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {
@ -1721,9 +1712,6 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {
@ -2010,9 +1998,6 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {
@ -2314,9 +2299,6 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {
@ -2796,9 +2778,6 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {
@ -3462,9 +3441,6 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {

View File

@ -29,54 +29,6 @@ description: Artifact commands assembly_mixed_units_cubes.kcl
"hidden": true "hidden": true
} }
}, },
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "in"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "in"
}
},
{ {
"cmdId": "[uuid]", "cmdId": "[uuid]",
"range": [], "range": [],
@ -132,8 +84,8 @@ description: Artifact commands assembly_mixed_units_cubes.kcl
"type": "move_path_pen", "type": "move_path_pen",
"path": "[uuid]", "path": "[uuid]",
"to": { "to": {
"x": -10.0, "x": -254.0,
"y": -10.0, "y": -254.0,
"z": 0.0 "z": 0.0
} }
} }
@ -154,7 +106,7 @@ description: Artifact commands assembly_mixed_units_cubes.kcl
"segment": { "segment": {
"type": "line", "type": "line",
"end": { "end": {
"x": 5.0, "x": 127.0,
"y": 0.0, "y": 0.0,
"z": 0.0 "z": 0.0
}, },
@ -172,7 +124,7 @@ description: Artifact commands assembly_mixed_units_cubes.kcl
"type": "line", "type": "line",
"end": { "end": {
"x": 0.0, "x": 0.0,
"y": -5.0, "y": -127.0,
"z": 0.0 "z": 0.0
}, },
"relative": true "relative": true
@ -188,7 +140,7 @@ description: Artifact commands assembly_mixed_units_cubes.kcl
"segment": { "segment": {
"type": "line", "type": "line",
"end": { "end": {
"x": -5.0, "x": -127.0,
"y": 0.0, "y": 0.0,
"z": 0.0 "z": 0.0
}, },
@ -205,8 +157,8 @@ description: Artifact commands assembly_mixed_units_cubes.kcl
"segment": { "segment": {
"type": "line", "type": "line",
"end": { "end": {
"x": -10.0, "x": -254.0,
"y": -10.0, "y": -254.0,
"z": 0.0 "z": 0.0
}, },
"relative": false "relative": false
@ -243,7 +195,7 @@ description: Artifact commands assembly_mixed_units_cubes.kcl
"command": { "command": {
"type": "extrude", "type": "extrude",
"target": "[uuid]", "target": "[uuid]",
"distance": 5.0, "distance": 127.0,
"faces": null, "faces": null,
"opposite": "None" "opposite": "None"
} }
@ -352,14 +304,6 @@ description: Artifact commands assembly_mixed_units_cubes.kcl
"face_id": "[uuid]" "face_id": "[uuid]"
} }
}, },
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{ {
"cmdId": "[uuid]", "cmdId": "[uuid]",
"range": [], "range": [],

View File

@ -29,54 +29,6 @@ description: Artifact commands assembly_non_default_units.kcl
"hidden": true "hidden": true
} }
}, },
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "in"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "in"
}
},
{ {
"cmdId": "[uuid]", "cmdId": "[uuid]",
"range": [], "range": [],
@ -132,7 +84,7 @@ description: Artifact commands assembly_non_default_units.kcl
"type": "move_path_pen", "type": "move_path_pen",
"path": "[uuid]", "path": "[uuid]",
"to": { "to": {
"x": 1.0, "x": 25.4,
"y": 0.0, "y": 0.0,
"z": 0.0 "z": 0.0
} }
@ -157,7 +109,7 @@ description: Artifact commands assembly_non_default_units.kcl
"x": 0.0, "x": 0.0,
"y": 0.0 "y": 0.0
}, },
"radius": 1.0, "radius": 25.4,
"start": { "start": {
"unit": "degrees", "unit": "degrees",
"value": 0.0 "value": 0.0
@ -178,14 +130,6 @@ description: Artifact commands assembly_non_default_units.kcl
"path_id": "[uuid]" "path_id": "[uuid]"
} }
}, },
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "in"
}
},
{ {
"cmdId": "[uuid]", "cmdId": "[uuid]",
"range": [], "range": [],
@ -241,8 +185,8 @@ description: Artifact commands assembly_non_default_units.kcl
"type": "move_path_pen", "type": "move_path_pen",
"path": "[uuid]", "path": "[uuid]",
"to": { "to": {
"x": 1.0, "x": 25.4,
"y": 2.0, "y": 50.8,
"z": 0.0 "z": 0.0
} }
} }
@ -264,9 +208,9 @@ description: Artifact commands assembly_non_default_units.kcl
"type": "arc", "type": "arc",
"center": { "center": {
"x": 0.0, "x": 0.0,
"y": 2.0 "y": 50.8
}, },
"radius": 1.0, "radius": 25.4,
"start": { "start": {
"unit": "degrees", "unit": "degrees",
"value": 0.0 "value": 0.0
@ -286,13 +230,5 @@ description: Artifact commands assembly_non_default_units.kcl
"type": "close_path", "type": "close_path",
"path_id": "[uuid]" "path_id": "[uuid]"
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "in"
}
} }
] ]

View File

@ -28,37 +28,5 @@ description: Artifact commands bad_units_in_annotation.kcl
"object_id": "[uuid]", "object_id": "[uuid]",
"hidden": true "hidden": true
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -320,37 +320,5 @@ description: Artifact commands basic_fillet_cube_close_opposite.kcl
"tolerance": 0.0000001, "tolerance": 0.0000001,
"cut_type": "fillet" "cut_type": "fillet"
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -192,9 +192,6 @@ description: Variables in memory after executing basic_fillet_cube_close_opposit
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {

View File

@ -320,37 +320,5 @@ description: Artifact commands basic_fillet_cube_end.kcl
"tolerance": 0.0000001, "tolerance": 0.0000001,
"cut_type": "fillet" "cut_type": "fillet"
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -180,9 +180,6 @@ description: Variables in memory after executing basic_fillet_cube_end.kcl
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {

View File

@ -308,37 +308,5 @@ description: Artifact commands basic_fillet_cube_next_adjacent.kcl
"tolerance": 0.0000001, "tolerance": 0.0000001,
"cut_type": "fillet" "cut_type": "fillet"
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -204,9 +204,6 @@ description: Variables in memory after executing basic_fillet_cube_next_adjacent
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {

View File

@ -308,37 +308,5 @@ description: Artifact commands basic_fillet_cube_previous_adjacent.kcl
"tolerance": 0.0000001, "tolerance": 0.0000001,
"cut_type": "fillet" "cut_type": "fillet"
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -204,9 +204,6 @@ description: Variables in memory after executing basic_fillet_cube_previous_adja
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {

View File

@ -310,37 +310,5 @@ description: Artifact commands basic_fillet_cube_start.kcl
"tolerance": 0.0000001, "tolerance": 0.0000001,
"cut_type": "fillet" "cut_type": "fillet"
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -180,9 +180,6 @@ description: Variables in memory after executing basic_fillet_cube_start.kcl
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {

View File

@ -249,37 +249,5 @@ description: Artifact commands big_number_angle_to_match_length_x.kcl
"edge_id": "[uuid]", "edge_id": "[uuid]",
"face_id": "[uuid]" "face_id": "[uuid]"
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -142,9 +142,6 @@ description: Variables in memory after executing big_number_angle_to_match_lengt
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {

View File

@ -249,37 +249,5 @@ description: Artifact commands big_number_angle_to_match_length_y.kcl
"edge_id": "[uuid]", "edge_id": "[uuid]",
"face_id": "[uuid]" "face_id": "[uuid]"
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -142,9 +142,6 @@ description: Variables in memory after executing big_number_angle_to_match_lengt
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"units": {
"type": "Mm"
} }
}, },
"start": { "start": {

View File

@ -28,37 +28,5 @@ description: Artifact commands boolean_logical_and.kcl
"object_id": "[uuid]", "object_id": "[uuid]",
"hidden": true "hidden": true
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

Some files were not shown because too many files have changed in this diff Show More