2024-12-05 14:27:51 -06:00
|
|
|
use std::{any::type_name, collections::HashMap, num::NonZeroU32};
|
2024-07-19 20:30:13 -05:00
|
|
|
|
2024-08-21 12:12:56 -07:00
|
|
|
use anyhow::Result;
|
2025-03-13 21:59:39 -07:00
|
|
|
use kcmc::{
|
|
|
|
websocket::{ModelingCmdReq, OkWebSocketResponseData},
|
|
|
|
ModelingCmd,
|
|
|
|
};
|
2024-09-18 17:04:04 -05:00
|
|
|
use kittycad_modeling_cmds as kcmc;
|
2025-02-21 12:36:21 +13:00
|
|
|
use schemars::JsonSchema;
|
|
|
|
use serde::{Deserialize, Serialize};
|
2024-07-29 13:18:55 -07:00
|
|
|
|
2024-07-16 07:45:43 -05:00
|
|
|
use crate::{
|
|
|
|
errors::{KclError, KclErrorDetails},
|
2024-12-07 07:16:04 +13:00
|
|
|
execution::{
|
2025-03-17 17:57:26 +13:00
|
|
|
kcl_value::{ArrayLen, FunctionSource, NumericType, RuntimeType},
|
|
|
|
ExecState, ExecutorContext, ExtrudeSurface, Helix, KclObjectFields, KclValue, Metadata, PrimitiveType, Sketch,
|
|
|
|
SketchSurface, Solid, TagIdentifier,
|
2024-07-16 07:45:43 -05:00
|
|
|
},
|
2024-12-05 17:56:49 +13:00
|
|
|
parsing::ast::types::TagNode,
|
2024-12-03 16:39:51 +13:00
|
|
|
source_range::SourceRange,
|
2025-03-13 21:59:39 -07:00
|
|
|
std::{
|
|
|
|
shapes::{PolygonType, SketchOrSurface},
|
|
|
|
sketch::FaceTag,
|
|
|
|
sweep::SweepPath,
|
|
|
|
},
|
2024-12-03 16:39:51 +13:00
|
|
|
ModuleId,
|
2024-07-16 07:45:43 -05:00
|
|
|
};
|
|
|
|
|
2024-11-21 13:10:03 -05:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct Arg {
|
|
|
|
/// The evaluated argument.
|
|
|
|
pub value: KclValue,
|
|
|
|
/// The source range of the unevaluated argument.
|
|
|
|
pub source_range: SourceRange,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Arg {
|
|
|
|
pub fn new(value: KclValue, source_range: SourceRange) -> Self {
|
|
|
|
Self { value, source_range }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn synthetic(value: KclValue) -> Self {
|
|
|
|
Self {
|
|
|
|
value,
|
|
|
|
source_range: SourceRange::synthetic(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn source_ranges(&self) -> Vec<SourceRange> {
|
|
|
|
vec![self.source_range]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 19:11:31 -06:00
|
|
|
#[derive(Debug, Clone, Default)]
|
|
|
|
pub struct KwArgs {
|
|
|
|
/// Unlabeled keyword args. Currently only the first arg can be unlabeled.
|
|
|
|
pub unlabeled: Option<Arg>,
|
|
|
|
/// Labeled args.
|
|
|
|
pub labeled: HashMap<String, Arg>,
|
|
|
|
}
|
|
|
|
|
2024-12-09 22:11:16 -06:00
|
|
|
impl KwArgs {
|
|
|
|
/// How many arguments are there?
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.labeled.len() + if self.unlabeled.is_some() { 1 } else { 0 }
|
|
|
|
}
|
2025-01-16 11:10:36 -05:00
|
|
|
/// Are there no arguments?
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.labeled.len() == 0 && self.unlabeled.is_none()
|
|
|
|
}
|
2024-12-09 22:11:16 -06:00
|
|
|
}
|
|
|
|
|
2025-02-21 12:36:21 +13:00
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
|
|
|
#[ts(export)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct TyF64 {
|
|
|
|
pub n: f64,
|
|
|
|
pub ty: NumericType,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TyF64 {
|
|
|
|
pub fn new(n: f64, ty: NumericType) -> Self {
|
|
|
|
Self { n, ty }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn count(n: f64) -> Self {
|
|
|
|
Self {
|
|
|
|
n,
|
|
|
|
ty: NumericType::count(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn map(mut self, n: f64) -> Self {
|
|
|
|
self.n = n;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-16 07:45:43 -05:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct Args {
|
2024-12-05 14:27:51 -06:00
|
|
|
/// Positional args.
|
2024-11-21 13:10:03 -05:00
|
|
|
pub args: Vec<Arg>,
|
2024-12-16 21:01:23 -06:00
|
|
|
/// Keyword arguments
|
2024-12-06 19:11:31 -06:00
|
|
|
pub kw_args: KwArgs,
|
2024-07-16 07:45:43 -05:00
|
|
|
pub source_range: SourceRange,
|
|
|
|
pub ctx: ExecutorContext,
|
2024-12-16 21:01:23 -06:00
|
|
|
/// If this call happens inside a pipe (|>) expression, this holds the LHS of that |>.
|
|
|
|
/// Otherwise it's None.
|
|
|
|
pipe_value: Option<Arg>,
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Args {
|
2024-12-16 21:01:23 -06:00
|
|
|
pub fn new(args: Vec<Arg>, source_range: SourceRange, ctx: ExecutorContext, pipe_value: Option<Arg>) -> Self {
|
2024-07-16 07:45:43 -05:00
|
|
|
Self {
|
|
|
|
args,
|
2024-12-05 14:27:51 -06:00
|
|
|
kw_args: Default::default(),
|
|
|
|
source_range,
|
|
|
|
ctx,
|
2024-12-16 21:01:23 -06:00
|
|
|
pipe_value,
|
2024-12-05 14:27:51 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Collect the given keyword arguments.
|
2024-12-16 21:01:23 -06:00
|
|
|
pub fn new_kw(kw_args: KwArgs, source_range: SourceRange, ctx: ExecutorContext, pipe_value: Option<Arg>) -> Self {
|
2024-12-05 14:27:51 -06:00
|
|
|
Self {
|
|
|
|
args: Default::default(),
|
|
|
|
kw_args,
|
2024-07-16 07:45:43 -05:00
|
|
|
source_range,
|
|
|
|
ctx,
|
2024-12-16 21:01:23 -06:00
|
|
|
pipe_value,
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-05 14:27:51 -06:00
|
|
|
/// Get a keyword argument. If not set, returns None.
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
pub(crate) fn get_kw_arg_opt<'a, T>(&'a self, label: &str) -> Result<Option<T>, KclError>
|
2024-12-05 14:27:51 -06:00
|
|
|
where
|
|
|
|
T: FromKclValue<'a>,
|
|
|
|
{
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
let Some(arg) = self.kw_args.labeled.get(label) else {
|
|
|
|
return Ok(None);
|
|
|
|
};
|
|
|
|
|
|
|
|
T::from_kcl_val(&arg.value).map(Some).ok_or_else(|| {
|
|
|
|
KclError::Type(KclErrorDetails {
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
message: format!(
|
|
|
|
"The optional arg {label} was given, but it was the wrong type. It should be type {} but it was {}",
|
|
|
|
type_name::<T>(),
|
|
|
|
arg.value.human_friendly_type(),
|
|
|
|
),
|
|
|
|
})
|
|
|
|
})
|
2024-12-05 14:27:51 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a keyword argument. If not set, returns Err.
|
|
|
|
pub(crate) fn get_kw_arg<'a, T>(&'a self, label: &str) -> Result<T, KclError>
|
|
|
|
where
|
|
|
|
T: FromKclValue<'a>,
|
|
|
|
{
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
self.get_kw_arg_opt(label)?.ok_or_else(|| {
|
2024-12-05 14:27:51 -06:00
|
|
|
KclError::Semantic(KclErrorDetails {
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
message: format!("This function requires a keyword argument '{label}'"),
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2025-03-12 11:24:27 -05:00
|
|
|
/// Get a labelled keyword arg, check it's an array, and return all items in the array
|
|
|
|
/// plus their source range.
|
|
|
|
pub(crate) fn kw_arg_array_and_source<'a, T>(&'a self, label: &str) -> Result<Vec<(T, SourceRange)>, KclError>
|
|
|
|
where
|
|
|
|
T: FromKclValue<'a>,
|
|
|
|
{
|
|
|
|
let Some(arg) = self.kw_args.labeled.get(label) else {
|
|
|
|
let err = KclError::Semantic(KclErrorDetails {
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
message: format!("This function requires a keyword argument '{label}'"),
|
|
|
|
});
|
|
|
|
return Err(err);
|
|
|
|
};
|
|
|
|
let Some(array) = arg.value.as_array() else {
|
|
|
|
let err = KclError::Semantic(KclErrorDetails {
|
|
|
|
source_ranges: vec![arg.source_range],
|
|
|
|
message: format!(
|
|
|
|
"Expected an array of {} but found {}",
|
|
|
|
type_name::<T>(),
|
|
|
|
arg.value.human_friendly_type()
|
|
|
|
),
|
|
|
|
});
|
|
|
|
return Err(err);
|
|
|
|
};
|
|
|
|
array
|
|
|
|
.iter()
|
|
|
|
.map(|item| {
|
|
|
|
let source = SourceRange::from(item);
|
|
|
|
let val = FromKclValue::from_kcl_val(item).ok_or_else(|| {
|
|
|
|
KclError::Semantic(KclErrorDetails {
|
|
|
|
source_ranges: arg.source_ranges(),
|
|
|
|
message: format!(
|
|
|
|
"Expected a {} but found {}",
|
|
|
|
type_name::<T>(),
|
|
|
|
arg.value.human_friendly_type()
|
|
|
|
),
|
|
|
|
})
|
|
|
|
})?;
|
|
|
|
Ok((val, source))
|
|
|
|
})
|
|
|
|
.collect::<Result<Vec<_>, _>>()
|
|
|
|
}
|
|
|
|
|
2025-02-21 10:24:12 -05:00
|
|
|
/// Get the unlabeled keyword argument. If not set, returns None.
|
|
|
|
pub(crate) fn unlabeled_kw_arg_unconverted(&self) -> Option<&Arg> {
|
|
|
|
self.kw_args
|
|
|
|
.unlabeled
|
|
|
|
.as_ref()
|
|
|
|
.or(self.args.first())
|
|
|
|
.or(self.pipe_value.as_ref())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the unlabeled keyword argument. If not set, returns Err. If it
|
|
|
|
/// can't be converted to the given type, returns Err.
|
2024-12-05 14:27:51 -06:00
|
|
|
pub(crate) fn get_unlabeled_kw_arg<'a, T>(&'a self, label: &str) -> Result<T, KclError>
|
|
|
|
where
|
|
|
|
T: FromKclValue<'a>,
|
|
|
|
{
|
2024-12-13 13:07:52 -06:00
|
|
|
let arg = self
|
2025-02-21 10:24:12 -05:00
|
|
|
.unlabeled_kw_arg_unconverted()
|
2024-12-13 13:07:52 -06:00
|
|
|
.ok_or(KclError::Semantic(KclErrorDetails {
|
2024-12-05 14:27:51 -06:00
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
message: format!("This function requires a value for the special unlabeled first parameter, '{label}'"),
|
2024-12-13 13:07:52 -06:00
|
|
|
}))?;
|
|
|
|
|
2024-12-05 14:27:51 -06:00
|
|
|
T::from_kcl_val(&arg.value).ok_or_else(|| {
|
2025-03-10 22:53:16 -05:00
|
|
|
let expected_type_name = tynm::type_name::<T>();
|
|
|
|
let actual_type_name = arg.value.human_friendly_type();
|
2025-03-17 17:57:26 +13:00
|
|
|
let message = format!("This function expected the input argument to be of type {expected_type_name} but it's actually of type {actual_type_name}");
|
|
|
|
KclError::Semantic(KclErrorDetails {
|
|
|
|
source_ranges: arg.source_ranges(),
|
|
|
|
message,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the unlabeled keyword argument. If not set, returns Err. If it
|
|
|
|
/// can't be converted to the given type, returns Err.
|
|
|
|
pub(crate) fn get_unlabeled_kw_arg_typed<T>(
|
|
|
|
&self,
|
|
|
|
label: &str,
|
|
|
|
ty: &RuntimeType,
|
|
|
|
exec_state: &mut ExecState,
|
|
|
|
) -> Result<T, KclError>
|
|
|
|
where
|
|
|
|
T: for<'a> FromKclValue<'a>,
|
|
|
|
{
|
|
|
|
let arg = self
|
|
|
|
.unlabeled_kw_arg_unconverted()
|
|
|
|
.ok_or(KclError::Semantic(KclErrorDetails {
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
message: format!("This function requires a value for the special unlabeled first parameter, '{label}'"),
|
|
|
|
}))?;
|
|
|
|
|
|
|
|
let arg = arg.value.coerce(ty, exec_state).ok_or_else(|| {
|
|
|
|
let actual_type_name = arg.value.human_friendly_type();
|
|
|
|
let msg_base = format!(
|
|
|
|
"This function expected the input argument to be {} but it's actually of type {actual_type_name}",
|
|
|
|
ty.human_friendly_type(),
|
|
|
|
);
|
|
|
|
let suggestion = match (ty, actual_type_name) {
|
|
|
|
(RuntimeType::Primitive(PrimitiveType::Solid), "Sketch")
|
|
|
|
| (RuntimeType::Array(PrimitiveType::Solid, _), "Sketch") => Some(
|
2025-03-10 22:53:16 -05:00
|
|
|
"You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`",
|
|
|
|
),
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
let message = match suggestion {
|
|
|
|
None => msg_base,
|
|
|
|
Some(sugg) => format!("{msg_base}. {sugg}"),
|
|
|
|
};
|
2024-12-05 14:27:51 -06:00
|
|
|
KclError::Semantic(KclErrorDetails {
|
|
|
|
source_ranges: arg.source_ranges(),
|
2025-03-10 22:53:16 -05:00
|
|
|
message,
|
2024-12-05 14:27:51 -06:00
|
|
|
})
|
2025-03-17 17:57:26 +13:00
|
|
|
})?;
|
|
|
|
|
|
|
|
// TODO unnecessary cloning
|
|
|
|
Ok(T::from_kcl_val(&arg).unwrap())
|
2024-12-05 14:27:51 -06:00
|
|
|
}
|
|
|
|
|
2024-07-16 07:45:43 -05:00
|
|
|
// Add a modeling command to the batch but don't fire it right away.
|
2024-07-28 00:30:04 -07:00
|
|
|
pub(crate) async fn batch_modeling_cmd(
|
2024-07-16 07:45:43 -05:00
|
|
|
&self,
|
|
|
|
id: uuid::Uuid,
|
2024-09-18 17:04:04 -05:00
|
|
|
cmd: ModelingCmd,
|
2024-07-16 07:45:43 -05:00
|
|
|
) -> Result<(), crate::errors::KclError> {
|
|
|
|
self.ctx.engine.batch_modeling_cmd(id, self.source_range, &cmd).await
|
|
|
|
}
|
|
|
|
|
2025-03-13 21:59:39 -07:00
|
|
|
// Add multiple modeling commands to the batch but don't fire them right away.
|
|
|
|
pub(crate) async fn batch_modeling_cmds(&self, cmds: &[ModelingCmdReq]) -> Result<(), crate::errors::KclError> {
|
|
|
|
self.ctx.engine.batch_modeling_cmds(self.source_range, cmds).await
|
|
|
|
}
|
|
|
|
|
2024-07-16 07:45:43 -05:00
|
|
|
// Add a modeling command to the batch that gets executed at the end of the file.
|
|
|
|
// This is good for something like fillet or chamfer where the engine would
|
|
|
|
// eat the path id if we executed it right away.
|
2024-09-18 17:04:04 -05:00
|
|
|
pub(crate) async fn batch_end_cmd(&self, id: uuid::Uuid, cmd: ModelingCmd) -> Result<(), crate::errors::KclError> {
|
2024-07-16 07:45:43 -05:00
|
|
|
self.ctx.engine.batch_end_cmd(id, self.source_range, &cmd).await
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Send the modeling cmd and wait for the response.
|
2024-07-28 00:30:04 -07:00
|
|
|
pub(crate) async fn send_modeling_cmd(
|
2024-07-16 07:45:43 -05:00
|
|
|
&self,
|
|
|
|
id: uuid::Uuid,
|
2024-09-18 17:04:04 -05:00
|
|
|
cmd: ModelingCmd,
|
2024-07-16 07:45:43 -05:00
|
|
|
) -> Result<OkWebSocketResponseData, KclError> {
|
2025-01-08 20:02:30 -05:00
|
|
|
self.ctx.engine.send_modeling_cmd(id, self.source_range, &cmd).await
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
2024-09-16 15:10:33 -04:00
|
|
|
fn get_tag_info_from_memory<'a, 'e>(
|
2024-07-27 22:56:46 -07:00
|
|
|
&'a self,
|
2024-09-16 15:10:33 -04:00
|
|
|
exec_state: &'e mut ExecState,
|
2024-07-27 22:56:46 -07:00
|
|
|
tag: &'a TagIdentifier,
|
2024-12-07 07:16:04 +13:00
|
|
|
) -> Result<&'e crate::execution::TagEngineInfo, KclError> {
|
2025-03-17 12:28:51 +13:00
|
|
|
if let (epoch, KclValue::TagIdentifier(t)) =
|
|
|
|
exec_state.stack().get_from_call_stack(&tag.value, self.source_range)?
|
|
|
|
{
|
|
|
|
let info = t.get_info(epoch).ok_or_else(|| {
|
2024-07-27 22:56:46 -07:00
|
|
|
KclError::Type(KclErrorDetails {
|
|
|
|
message: format!("Tag `{}` does not have engine info", tag.value),
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
})
|
2025-03-17 12:28:51 +13:00
|
|
|
})?;
|
|
|
|
Ok(info)
|
2024-07-27 22:56:46 -07:00
|
|
|
} else {
|
|
|
|
Err(KclError::Type(KclErrorDetails {
|
|
|
|
message: format!("Tag `{}` does not exist", tag.value),
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-16 15:10:33 -04:00
|
|
|
pub(crate) fn get_tag_engine_info<'a, 'e>(
|
2024-07-27 22:56:46 -07:00
|
|
|
&'a self,
|
2024-09-16 15:10:33 -04:00
|
|
|
exec_state: &'e mut ExecState,
|
2024-07-27 22:56:46 -07:00
|
|
|
tag: &'a TagIdentifier,
|
2024-12-07 07:16:04 +13:00
|
|
|
) -> Result<&'a crate::execution::TagEngineInfo, KclError>
|
2024-09-16 15:10:33 -04:00
|
|
|
where
|
|
|
|
'e: 'a,
|
|
|
|
{
|
2025-03-17 12:28:51 +13:00
|
|
|
if let Some(info) = tag.get_cur_info() {
|
2024-07-27 22:56:46 -07:00
|
|
|
return Ok(info);
|
|
|
|
}
|
|
|
|
|
2024-09-16 15:10:33 -04:00
|
|
|
self.get_tag_info_from_memory(exec_state, tag)
|
2024-07-27 22:56:46 -07:00
|
|
|
}
|
|
|
|
|
2024-09-16 15:10:33 -04:00
|
|
|
fn get_tag_engine_info_check_surface<'a, 'e>(
|
2024-07-27 22:56:46 -07:00
|
|
|
&'a self,
|
2024-09-16 15:10:33 -04:00
|
|
|
exec_state: &'e mut ExecState,
|
2024-07-27 22:56:46 -07:00
|
|
|
tag: &'a TagIdentifier,
|
2024-12-07 07:16:04 +13:00
|
|
|
) -> Result<&'a crate::execution::TagEngineInfo, KclError>
|
2024-09-16 15:10:33 -04:00
|
|
|
where
|
|
|
|
'e: 'a,
|
|
|
|
{
|
2025-03-17 12:28:51 +13:00
|
|
|
if let Some(info) = tag.get_cur_info() {
|
2024-10-28 20:20:45 -04:00
|
|
|
if info.surface.is_some() {
|
2024-07-27 22:56:46 -07:00
|
|
|
return Ok(info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-16 15:10:33 -04:00
|
|
|
self.get_tag_info_from_memory(exec_state, tag)
|
2024-07-27 22:56:46 -07:00
|
|
|
}
|
|
|
|
|
2024-09-27 15:44:44 -07:00
|
|
|
/// Flush just the fillets and chamfers for this specific SolidSet.
|
2024-07-28 00:30:04 -07:00
|
|
|
#[allow(clippy::vec_box)]
|
2025-03-17 17:57:26 +13:00
|
|
|
pub(crate) async fn flush_batch_for_solids(
|
2024-07-16 07:45:43 -05:00
|
|
|
&self,
|
2024-09-16 15:10:33 -04:00
|
|
|
exec_state: &mut ExecState,
|
2025-03-19 12:28:56 -07:00
|
|
|
solids: &[Solid],
|
2024-07-16 07:45:43 -05:00
|
|
|
) -> Result<(), KclError> {
|
2024-09-27 15:44:44 -07:00
|
|
|
// Make sure we don't traverse sketches more than once.
|
|
|
|
let mut traversed_sketches = Vec::new();
|
2024-07-16 07:45:43 -05:00
|
|
|
|
2024-09-27 15:44:44 -07:00
|
|
|
// Collect all the fillet/chamfer ids for the solids.
|
2024-07-16 07:45:43 -05:00
|
|
|
let mut ids = Vec::new();
|
2024-09-27 15:44:44 -07:00
|
|
|
for solid in solids {
|
|
|
|
// We need to traverse the solids that share the same sketch.
|
|
|
|
let sketch_id = solid.sketch.id;
|
|
|
|
if !traversed_sketches.contains(&sketch_id) {
|
|
|
|
// Find all the solids on the same shared sketch.
|
2024-07-16 07:45:43 -05:00
|
|
|
ids.extend(
|
2024-09-16 15:10:33 -04:00
|
|
|
exec_state
|
2025-03-05 12:03:32 +13:00
|
|
|
.stack()
|
2025-02-12 10:22:56 +13:00
|
|
|
.walk_call_stack()
|
|
|
|
.filter(|v| matches!(v, KclValue::Solid { value } if value.sketch.id == sketch_id))
|
|
|
|
.flat_map(|v| match v {
|
|
|
|
KclValue::Solid { value } => value.get_all_edge_cut_ids(),
|
|
|
|
_ => unreachable!(),
|
|
|
|
}),
|
2024-07-29 23:22:52 -04:00
|
|
|
);
|
2024-09-27 15:44:44 -07:00
|
|
|
traversed_sketches.push(sketch_id);
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
2024-09-27 15:44:44 -07:00
|
|
|
ids.extend(solid.get_all_edge_cut_ids());
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// We can return early if there are no fillets or chamfers.
|
|
|
|
if ids.is_empty() {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
// We want to move these fillets and chamfers from batch_end to batch so they get executed
|
|
|
|
// before what ever we call next.
|
|
|
|
for id in ids {
|
|
|
|
// Pop it off the batch_end and add it to the batch.
|
2025-02-18 13:50:13 -08:00
|
|
|
let Some(item) = self.ctx.engine.batch_end().write().await.shift_remove(&id) else {
|
2024-07-16 07:45:43 -05:00
|
|
|
// It might be in the batch already.
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
// Add it to the batch.
|
2025-02-18 13:50:13 -08:00
|
|
|
self.ctx.engine.batch().write().await.push(item);
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Run flush.
|
|
|
|
// Yes, we do need to actually flush the batch here, or references will fail later.
|
|
|
|
self.ctx.engine.flush_batch(false, SourceRange::default()).await?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
pub(crate) fn make_user_val_from_point(&self, p: [f64; 2]) -> Result<KclValue, KclError> {
|
|
|
|
let meta = Metadata {
|
|
|
|
source_range: self.source_range,
|
|
|
|
};
|
|
|
|
let x = KclValue::Number {
|
|
|
|
value: p[0],
|
|
|
|
meta: vec![meta],
|
2025-02-14 13:03:23 +13:00
|
|
|
ty: NumericType::Unknown,
|
2024-11-14 17:27:19 -06:00
|
|
|
};
|
|
|
|
let y = KclValue::Number {
|
|
|
|
value: p[1],
|
|
|
|
meta: vec![meta],
|
2025-02-14 13:03:23 +13:00
|
|
|
ty: NumericType::Unknown,
|
2024-11-14 17:27:19 -06:00
|
|
|
};
|
2025-03-08 04:04:57 +13:00
|
|
|
Ok(KclValue::MixedArray {
|
2024-11-14 17:27:19 -06:00
|
|
|
value: vec![x, y],
|
|
|
|
meta: vec![meta],
|
2024-11-05 14:10:35 -06:00
|
|
|
})
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
pub(crate) fn make_user_val_from_f64(&self, f: f64) -> KclValue {
|
|
|
|
KclValue::from_number(
|
|
|
|
f,
|
|
|
|
vec![Metadata {
|
|
|
|
source_range: self.source_range,
|
|
|
|
}],
|
|
|
|
)
|
2024-07-26 15:14:51 -04:00
|
|
|
}
|
|
|
|
|
2025-02-21 12:36:21 +13:00
|
|
|
pub(crate) fn make_user_val_from_f64_with_type(&self, f: TyF64) -> KclValue {
|
2025-02-14 13:03:23 +13:00
|
|
|
KclValue::from_number_with_type(
|
2025-02-21 12:36:21 +13:00
|
|
|
f.n,
|
|
|
|
f.ty,
|
2025-02-14 13:03:23 +13:00
|
|
|
vec![Metadata {
|
|
|
|
source_range: self.source_range,
|
|
|
|
}],
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2025-02-21 12:36:21 +13:00
|
|
|
pub(crate) fn make_user_val_from_f64_array(&self, f: Vec<f64>, ty: &NumericType) -> Result<KclValue, KclError> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let array = f
|
|
|
|
.into_iter()
|
|
|
|
.map(|n| KclValue::Number {
|
|
|
|
value: n,
|
|
|
|
meta: vec![Metadata {
|
|
|
|
source_range: self.source_range,
|
|
|
|
}],
|
2025-02-20 10:12:37 +13:00
|
|
|
ty: ty.clone(),
|
2024-11-14 17:27:19 -06:00
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
2025-03-08 04:04:57 +13:00
|
|
|
Ok(KclValue::MixedArray {
|
2024-11-14 17:27:19 -06:00
|
|
|
value: array,
|
|
|
|
meta: vec![Metadata {
|
|
|
|
source_range: self.source_range,
|
|
|
|
}],
|
|
|
|
})
|
2024-07-28 22:45:40 -07:00
|
|
|
}
|
|
|
|
|
2024-07-28 00:30:04 -07:00
|
|
|
pub(crate) fn get_number(&self) -> Result<f64, KclError> {
|
2024-07-19 20:30:13 -05:00
|
|
|
FromArgs::from_args(self, 0)
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
2025-02-21 12:36:21 +13:00
|
|
|
pub(crate) fn get_number_with_type(&self) -> Result<TyF64, KclError> {
|
2025-02-14 13:03:23 +13:00
|
|
|
FromArgs::from_args(self, 0)
|
|
|
|
}
|
|
|
|
|
2024-07-28 00:30:04 -07:00
|
|
|
pub(crate) fn get_number_array(&self) -> Result<Vec<f64>, KclError> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let numbers = self
|
|
|
|
.args
|
|
|
|
.iter()
|
|
|
|
.map(|arg| {
|
2024-11-21 13:10:03 -05:00
|
|
|
let Some(num) = f64::from_kcl_val(&arg.value) else {
|
2024-11-14 17:27:19 -06:00
|
|
|
return Err(KclError::Semantic(KclErrorDetails {
|
2024-11-21 13:10:03 -05:00
|
|
|
source_ranges: arg.source_ranges(),
|
|
|
|
message: format!("Expected a number but found {}", arg.value.human_friendly_type()),
|
2024-11-14 17:27:19 -06:00
|
|
|
}));
|
|
|
|
};
|
|
|
|
Ok(num)
|
|
|
|
})
|
|
|
|
.collect::<Result<_, _>>()?;
|
2024-07-16 07:45:43 -05:00
|
|
|
Ok(numbers)
|
|
|
|
}
|
|
|
|
|
2025-02-21 12:36:21 +13:00
|
|
|
pub(crate) fn get_number_array_with_types(&self) -> Result<Vec<TyF64>, KclError> {
|
2025-02-14 13:03:23 +13:00
|
|
|
let numbers = self
|
|
|
|
.args
|
|
|
|
.iter()
|
|
|
|
.map(|arg| {
|
2025-02-21 12:36:21 +13:00
|
|
|
let Some(num) = <TyF64>::from_kcl_val(&arg.value) else {
|
2025-02-14 13:03:23 +13:00
|
|
|
return Err(KclError::Semantic(KclErrorDetails {
|
|
|
|
source_ranges: arg.source_ranges(),
|
|
|
|
message: format!("Expected a number but found {}", arg.value.human_friendly_type()),
|
|
|
|
}));
|
|
|
|
};
|
|
|
|
Ok(num)
|
|
|
|
})
|
|
|
|
.collect::<Result<_, _>>()?;
|
|
|
|
Ok(numbers)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn get_hypotenuse_leg(&self) -> Result<(f64, f64, NumericType), KclError> {
|
|
|
|
let numbers = self.get_number_array_with_types()?;
|
2024-07-16 07:45:43 -05:00
|
|
|
|
|
|
|
if numbers.len() != 2 {
|
|
|
|
return Err(KclError::Type(KclErrorDetails {
|
|
|
|
message: format!("Expected a number array of length 2, found `{:?}`", numbers),
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2025-02-14 13:03:23 +13:00
|
|
|
let mut numbers = numbers.into_iter();
|
2025-02-21 12:36:21 +13:00
|
|
|
let a = numbers.next().unwrap();
|
|
|
|
let b = numbers.next().unwrap();
|
|
|
|
let ty = a.ty.combine_eq(&b.ty);
|
|
|
|
Ok((a.n, b.n, ty))
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
2025-03-17 17:57:26 +13:00
|
|
|
pub(crate) fn get_sketches(&self, exec_state: &mut ExecState) -> Result<(Vec<Sketch>, Sketch), KclError> {
|
|
|
|
let sarg = self.args[0]
|
|
|
|
.value
|
|
|
|
.coerce(&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::None), exec_state)
|
|
|
|
.ok_or(KclError::Type(KclErrorDetails {
|
|
|
|
message: format!(
|
|
|
|
"Expected an array of sketches, found {}",
|
|
|
|
self.args[0].value.human_friendly_type()
|
|
|
|
),
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
}))?;
|
|
|
|
let sketches = match sarg {
|
|
|
|
KclValue::HomArray { value, .. } => value.iter().map(|v| v.as_sketch().unwrap().clone()).collect(),
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
let sarg = self.args[1]
|
|
|
|
.value
|
|
|
|
.coerce(&RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)
|
|
|
|
.ok_or(KclError::Type(KclErrorDetails {
|
|
|
|
message: format!("Expected a sketch, found {}", self.args[1].value.human_friendly_type()),
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
}))?;
|
|
|
|
let sketch = match sarg {
|
|
|
|
KclValue::Sketch { value } => *value,
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok((sketches, sketch))
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
2025-03-17 17:57:26 +13:00
|
|
|
pub(crate) fn get_sketch(&self, exec_state: &mut ExecState) -> Result<Sketch, KclError> {
|
|
|
|
let sarg = self.args[0]
|
|
|
|
.value
|
|
|
|
.coerce(&RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)
|
|
|
|
.ok_or(KclError::Type(KclErrorDetails {
|
|
|
|
message: format!("Expected a sketch, found {}", self.args[0].value.human_friendly_type()),
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
}))?;
|
|
|
|
match sarg {
|
|
|
|
KclValue::Sketch { value } => Ok(*value),
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
2024-07-28 00:30:04 -07:00
|
|
|
pub(crate) fn get_data<'a, T>(&'a self) -> Result<T, KclError>
|
2024-07-19 20:30:13 -05:00
|
|
|
where
|
|
|
|
T: FromArgs<'a> + serde::de::DeserializeOwned,
|
|
|
|
{
|
|
|
|
FromArgs::from_args(self, 0)
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
2024-07-28 00:30:04 -07:00
|
|
|
pub(crate) fn get_import_data(&self) -> Result<(String, Option<crate::std::import::ImportFormat>), KclError> {
|
2024-07-19 20:30:13 -05:00
|
|
|
FromArgs::from_args(self, 0)
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
2024-07-28 00:30:04 -07:00
|
|
|
pub(crate) fn get_data_and_optional_tag<'a, T>(&'a self) -> Result<(T, Option<FaceTag>), KclError>
|
2024-07-19 20:30:13 -05:00
|
|
|
where
|
2024-08-12 16:53:24 -05:00
|
|
|
T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
|
2024-07-19 20:30:13 -05:00
|
|
|
{
|
|
|
|
FromArgs::from_args(self, 0)
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
2025-03-17 17:57:26 +13:00
|
|
|
pub(crate) fn get_data_and_sketches<'a, T>(
|
|
|
|
&'a self,
|
|
|
|
exec_state: &mut ExecState,
|
|
|
|
) -> Result<(T, Vec<Sketch>), KclError>
|
2024-07-19 20:30:13 -05:00
|
|
|
where
|
|
|
|
T: serde::de::DeserializeOwned + FromArgs<'a>,
|
|
|
|
{
|
2025-03-17 17:57:26 +13:00
|
|
|
let data: T = FromArgs::from_args(self, 0)?;
|
|
|
|
let sarg = self.args[1]
|
|
|
|
.value
|
|
|
|
.coerce(&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::None), exec_state)
|
|
|
|
.ok_or(KclError::Type(KclErrorDetails {
|
|
|
|
message: format!(
|
|
|
|
"Expected an array of sketches for second argument, found {}",
|
|
|
|
self.args[1].value.human_friendly_type()
|
|
|
|
),
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
}))?;
|
|
|
|
let sketches = match sarg {
|
|
|
|
KclValue::HomArray { value, .. } => value.iter().map(|v| v.as_sketch().unwrap().clone()).collect(),
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
Ok((data, sketches))
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
2025-03-17 17:57:26 +13:00
|
|
|
pub(crate) fn get_data_and_sketch_and_tag<'a, T>(
|
|
|
|
&'a self,
|
|
|
|
exec_state: &mut ExecState,
|
|
|
|
) -> Result<(T, Sketch, Option<TagNode>), KclError>
|
2024-07-19 20:30:13 -05:00
|
|
|
where
|
2024-08-12 16:53:24 -05:00
|
|
|
T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
|
2024-07-19 20:30:13 -05:00
|
|
|
{
|
2025-03-17 17:57:26 +13:00
|
|
|
let data: T = FromArgs::from_args(self, 0)?;
|
|
|
|
let sarg = self.args[1]
|
|
|
|
.value
|
|
|
|
.coerce(&RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)
|
|
|
|
.ok_or(KclError::Type(KclErrorDetails {
|
|
|
|
message: format!(
|
|
|
|
"Expected a sketch for second argument, found {}",
|
|
|
|
self.args[1].value.human_friendly_type()
|
|
|
|
),
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
}))?;
|
|
|
|
let sketch = match sarg {
|
|
|
|
KclValue::Sketch { value } => *value,
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
let tag: Option<TagNode> = FromArgs::from_args(self, 2)?;
|
|
|
|
Ok((data, sketch, tag))
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
2024-10-30 16:52:17 -04:00
|
|
|
pub(crate) fn get_data_and_sketch_surface<'a, T>(&'a self) -> Result<(T, SketchSurface, Option<TagNode>), KclError>
|
2024-07-19 20:30:13 -05:00
|
|
|
where
|
2024-08-12 16:53:24 -05:00
|
|
|
T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
|
2024-07-19 20:30:13 -05:00
|
|
|
{
|
|
|
|
FromArgs::from_args(self, 0)
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
2025-03-17 17:57:26 +13:00
|
|
|
pub(crate) fn get_data_and_solid<'a, T>(&'a self, exec_state: &mut ExecState) -> Result<(T, Box<Solid>), KclError>
|
2024-07-19 20:30:13 -05:00
|
|
|
where
|
2024-08-12 16:53:24 -05:00
|
|
|
T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
|
2024-07-19 20:30:13 -05:00
|
|
|
{
|
2025-03-17 17:57:26 +13:00
|
|
|
let data: T = FromArgs::from_args(self, 0)?;
|
|
|
|
let sarg = self.args[1]
|
|
|
|
.value
|
|
|
|
.coerce(&RuntimeType::Primitive(PrimitiveType::Solid), exec_state)
|
|
|
|
.ok_or(KclError::Type(KclErrorDetails {
|
|
|
|
message: format!(
|
|
|
|
"Expected a solid for second argument, found {}",
|
|
|
|
self.args[1].value.human_friendly_type()
|
|
|
|
),
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
}))?;
|
|
|
|
let solid = match sarg {
|
|
|
|
KclValue::Solid { value } => value,
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
Ok((data, solid))
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
2024-09-27 15:44:44 -07:00
|
|
|
pub(crate) fn get_tag_to_number_sketch(&self) -> Result<(TagIdentifier, f64, Sketch), KclError> {
|
2024-07-19 20:30:13 -05:00
|
|
|
FromArgs::from_args(self, 0)
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
|
|
|
|
2024-07-28 00:30:04 -07:00
|
|
|
pub(crate) async fn get_adjacent_face_to_tag(
|
2024-07-16 07:45:43 -05:00
|
|
|
&self,
|
2024-09-16 15:10:33 -04:00
|
|
|
exec_state: &mut ExecState,
|
2024-07-16 07:45:43 -05:00
|
|
|
tag: &TagIdentifier,
|
|
|
|
must_be_planar: bool,
|
|
|
|
) -> Result<uuid::Uuid, KclError> {
|
|
|
|
if tag.value.is_empty() {
|
|
|
|
return Err(KclError::Type(KclErrorDetails {
|
|
|
|
message: "Expected a non-empty tag for the face".to_string(),
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2024-09-16 15:10:33 -04:00
|
|
|
let engine_info = self.get_tag_engine_info_check_surface(exec_state, tag)?;
|
2024-07-27 22:56:46 -07:00
|
|
|
|
2024-10-28 20:20:45 -04:00
|
|
|
let surface = engine_info.surface.as_ref().ok_or_else(|| {
|
2024-07-27 22:56:46 -07:00
|
|
|
KclError::Type(KclErrorDetails {
|
|
|
|
message: format!("Tag `{}` does not have a surface", tag.value),
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
})
|
|
|
|
})?;
|
|
|
|
|
|
|
|
if let Some(face_from_surface) = match surface {
|
|
|
|
ExtrudeSurface::ExtrudePlane(extrude_plane) => {
|
|
|
|
if let Some(plane_tag) = &extrude_plane.tag {
|
|
|
|
if plane_tag.name == tag.value {
|
|
|
|
Some(Ok(extrude_plane.face_id))
|
2024-07-16 07:45:43 -05:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2024-07-27 22:56:46 -07:00
|
|
|
} else {
|
|
|
|
None
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
2024-07-27 22:56:46 -07:00
|
|
|
}
|
|
|
|
// The must be planar check must be called before the arc check.
|
|
|
|
ExtrudeSurface::ExtrudeArc(_) if must_be_planar => Some(Err(KclError::Type(KclErrorDetails {
|
|
|
|
message: format!("Tag `{}` is a non-planar surface", tag.value),
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
}))),
|
|
|
|
ExtrudeSurface::ExtrudeArc(extrude_arc) => {
|
|
|
|
if let Some(arc_tag) = &extrude_arc.tag {
|
|
|
|
if arc_tag.name == tag.value {
|
|
|
|
Some(Ok(extrude_arc.face_id))
|
2024-07-16 07:45:43 -05:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2024-07-27 22:56:46 -07:00
|
|
|
} else {
|
|
|
|
None
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
2024-07-27 22:56:46 -07:00
|
|
|
}
|
2024-07-28 00:30:04 -07:00
|
|
|
ExtrudeSurface::Chamfer(chamfer) => {
|
|
|
|
if let Some(chamfer_tag) = &chamfer.tag {
|
|
|
|
if chamfer_tag.name == tag.value {
|
|
|
|
Some(Ok(chamfer.face_id))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2024-07-16 07:45:43 -05:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2024-07-28 00:30:04 -07:00
|
|
|
// The must be planar check must be called before the fillet check.
|
|
|
|
ExtrudeSurface::Fillet(_) if must_be_planar => Some(Err(KclError::Type(KclErrorDetails {
|
|
|
|
message: format!("Tag `{}` is a non-planar surface", tag.value),
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
}))),
|
|
|
|
ExtrudeSurface::Fillet(fillet) => {
|
|
|
|
if let Some(fillet_tag) = &fillet.tag {
|
|
|
|
if fillet_tag.name == tag.value {
|
|
|
|
Some(Ok(fillet.face_id))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} {
|
|
|
|
return face_from_surface;
|
|
|
|
}
|
2024-07-16 07:45:43 -05:00
|
|
|
|
|
|
|
// If we still haven't found the face, return an error.
|
|
|
|
Err(KclError::Type(KclErrorDetails {
|
|
|
|
message: format!("Expected a face with the tag `{}`", tag.value),
|
|
|
|
source_ranges: vec![self.source_range],
|
|
|
|
}))
|
|
|
|
}
|
2024-10-28 20:52:51 -04:00
|
|
|
|
|
|
|
pub(crate) fn get_polygon_args(
|
|
|
|
&self,
|
|
|
|
) -> Result<
|
|
|
|
(
|
|
|
|
crate::std::shapes::PolygonData,
|
|
|
|
crate::std::shapes::SketchOrSurface,
|
2024-10-30 16:52:17 -04:00
|
|
|
Option<TagNode>,
|
2024-10-28 20:52:51 -04:00
|
|
|
),
|
|
|
|
KclError,
|
|
|
|
> {
|
|
|
|
FromArgs::from_args(self, 0)
|
|
|
|
}
|
2024-07-16 07:45:43 -05:00
|
|
|
}
|
2024-07-19 20:30:13 -05:00
|
|
|
|
|
|
|
/// Types which impl this trait can be read out of the `Args` passed into a KCL function.
|
|
|
|
pub trait FromArgs<'a>: Sized {
|
|
|
|
/// Get this type from the args passed into a KCL function, at the given index in the argument list.
|
|
|
|
fn from_args(args: &'a Args, index: usize) -> Result<Self, KclError>;
|
|
|
|
}
|
|
|
|
|
2024-08-12 16:53:24 -05:00
|
|
|
/// Types which impl this trait can be extracted from a `KclValue`.
|
|
|
|
pub trait FromKclValue<'a>: Sized {
|
|
|
|
/// Try to convert a KclValue into this type.
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self>;
|
2024-07-19 20:30:13 -05:00
|
|
|
}
|
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
impl<'a> FromArgs<'a> for Vec<KclValue> {
|
|
|
|
fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
|
|
|
|
let Some(arg) = args.args.get(i) else {
|
|
|
|
return Err(KclError::Semantic(KclErrorDetails {
|
|
|
|
message: format!("Expected an argument at index {i}"),
|
|
|
|
source_ranges: vec![args.source_range],
|
|
|
|
}));
|
|
|
|
};
|
2025-03-08 04:04:57 +13:00
|
|
|
let KclValue::MixedArray { value: array, meta: _ } = &arg.value else {
|
2024-11-21 13:10:03 -05:00
|
|
|
let message = format!("Expected an array but found {}", arg.value.human_friendly_type());
|
2024-11-14 17:27:19 -06:00
|
|
|
return Err(KclError::Type(KclErrorDetails {
|
2024-11-21 13:10:03 -05:00
|
|
|
source_ranges: arg.source_ranges(),
|
2024-11-14 17:27:19 -06:00
|
|
|
message,
|
|
|
|
}));
|
|
|
|
};
|
|
|
|
Ok(array.to_owned())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-19 20:30:13 -05:00
|
|
|
impl<'a, T> FromArgs<'a> for T
|
|
|
|
where
|
2024-08-12 16:53:24 -05:00
|
|
|
T: FromKclValue<'a> + Sized,
|
2024-07-19 20:30:13 -05:00
|
|
|
{
|
|
|
|
fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
|
|
|
|
let Some(arg) = args.args.get(i) else {
|
|
|
|
return Err(KclError::Semantic(KclErrorDetails {
|
|
|
|
message: format!("Expected an argument at index {i}"),
|
|
|
|
source_ranges: vec![args.source_range],
|
|
|
|
}));
|
|
|
|
};
|
2024-11-21 13:10:03 -05:00
|
|
|
let Some(val) = T::from_kcl_val(&arg.value) else {
|
2024-07-19 20:30:13 -05:00
|
|
|
return Err(KclError::Semantic(KclErrorDetails {
|
|
|
|
message: format!(
|
2024-08-13 16:25:09 -04:00
|
|
|
"Argument at index {i} was supposed to be type {} but found {}",
|
2024-07-27 17:59:41 -07:00
|
|
|
type_name::<T>(),
|
2025-01-07 19:10:53 -08:00
|
|
|
arg.value.human_friendly_type(),
|
2024-07-19 20:30:13 -05:00
|
|
|
),
|
2024-11-21 13:10:03 -05:00
|
|
|
source_ranges: arg.source_ranges(),
|
2024-07-19 20:30:13 -05:00
|
|
|
}));
|
|
|
|
};
|
|
|
|
Ok(val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, T> FromArgs<'a> for Option<T>
|
|
|
|
where
|
2024-08-12 16:53:24 -05:00
|
|
|
T: FromKclValue<'a> + Sized,
|
2024-07-19 20:30:13 -05:00
|
|
|
{
|
|
|
|
fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
|
|
|
|
let Some(arg) = args.args.get(i) else { return Ok(None) };
|
2024-12-05 17:56:49 +13:00
|
|
|
if crate::parsing::ast::types::KclNone::from_kcl_val(&arg.value).is_some() {
|
2024-08-30 13:44:20 -05:00
|
|
|
return Ok(None);
|
|
|
|
}
|
2024-11-21 13:10:03 -05:00
|
|
|
let Some(val) = T::from_kcl_val(&arg.value) else {
|
2024-07-19 20:30:13 -05:00
|
|
|
return Err(KclError::Semantic(KclErrorDetails {
|
|
|
|
message: format!(
|
2024-11-14 17:27:19 -06:00
|
|
|
"Argument at index {i} was supposed to be type Option<{}> but found {}",
|
2024-08-13 16:25:09 -04:00
|
|
|
type_name::<T>(),
|
2024-11-21 13:10:03 -05:00
|
|
|
arg.value.human_friendly_type()
|
2024-07-19 20:30:13 -05:00
|
|
|
),
|
2024-11-21 13:10:03 -05:00
|
|
|
source_ranges: arg.source_ranges(),
|
2024-07-19 20:30:13 -05:00
|
|
|
}));
|
|
|
|
};
|
|
|
|
Ok(Some(val))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, A, B> FromArgs<'a> for (A, B)
|
|
|
|
where
|
|
|
|
A: FromArgs<'a>,
|
|
|
|
B: FromArgs<'a>,
|
|
|
|
{
|
|
|
|
fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
|
|
|
|
let a = A::from_args(args, i)?;
|
|
|
|
let b = B::from_args(args, i + 1)?;
|
|
|
|
Ok((a, b))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, A, B, C> FromArgs<'a> for (A, B, C)
|
|
|
|
where
|
|
|
|
A: FromArgs<'a>,
|
|
|
|
B: FromArgs<'a>,
|
|
|
|
C: FromArgs<'a>,
|
|
|
|
{
|
|
|
|
fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
|
|
|
|
let a = A::from_args(args, i)?;
|
|
|
|
let b = B::from_args(args, i + 1)?;
|
|
|
|
let c = C::from_args(args, i + 2)?;
|
|
|
|
Ok((a, b, c))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<'a, A, B, C, D> FromArgs<'a> for (A, B, C, D)
|
|
|
|
where
|
|
|
|
A: FromArgs<'a>,
|
|
|
|
B: FromArgs<'a>,
|
|
|
|
C: FromArgs<'a>,
|
|
|
|
D: FromArgs<'a>,
|
|
|
|
{
|
|
|
|
fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
|
|
|
|
let a = A::from_args(args, i)?;
|
|
|
|
let b = B::from_args(args, i + 1)?;
|
|
|
|
let c = C::from_args(args, i + 2)?;
|
|
|
|
let d = D::from_args(args, i + 3)?;
|
|
|
|
Ok((a, b, c, d))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
impl<'a> FromKclValue<'a> for [f64; 2] {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2025-03-08 04:04:57 +13:00
|
|
|
let KclValue::MixedArray { value, meta: _ } = arg else {
|
2024-11-14 17:27:19 -06:00
|
|
|
return None;
|
|
|
|
};
|
|
|
|
if value.len() != 2 {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let v0 = value.first()?;
|
|
|
|
let v1 = value.get(1)?;
|
|
|
|
let array = [v0.as_f64()?, v1.as_f64()?];
|
|
|
|
Some(array)
|
2024-09-26 16:00:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
impl<'a> FromKclValue<'a> for [usize; 3] {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2025-03-08 04:04:57 +13:00
|
|
|
let KclValue::MixedArray { value, meta: _ } = arg else {
|
2024-11-14 17:27:19 -06:00
|
|
|
return None;
|
|
|
|
};
|
|
|
|
if value.len() != 3 {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let v0 = value.first()?;
|
|
|
|
let v1 = value.get(1)?;
|
|
|
|
let v2 = value.get(2)?;
|
|
|
|
let array = [v0.as_usize()?, v1.as_usize()?, v2.as_usize()?];
|
|
|
|
Some(array)
|
2024-10-01 08:50:23 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
impl<'a> FromKclValue<'a> for [f64; 3] {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2025-03-08 04:04:57 +13:00
|
|
|
let KclValue::MixedArray { value, meta: _ } = arg else {
|
2024-11-14 17:27:19 -06:00
|
|
|
return None;
|
|
|
|
};
|
|
|
|
if value.len() != 3 {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let v0 = value.first()?;
|
|
|
|
let v1 = value.get(1)?;
|
|
|
|
let v2 = value.get(2)?;
|
|
|
|
let array = [v0.as_f64()?, v1.as_f64()?, v2.as_f64()?];
|
|
|
|
Some(array)
|
2024-10-01 08:50:23 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-30 16:52:17 -04:00
|
|
|
impl<'a> FromKclValue<'a> for TagNode {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-07-19 20:30:13 -05:00
|
|
|
arg.get_tag_declarator().ok()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-12 16:53:24 -05:00
|
|
|
impl<'a> FromKclValue<'a> for TagIdentifier {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-07-19 20:30:13 -05:00
|
|
|
arg.get_tag_identifier().ok()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-01 08:50:23 -05:00
|
|
|
impl<'a> FromKclValue<'a> for KclValue {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-10-01 08:50:23 -05:00
|
|
|
Some(arg.clone())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-16 11:19:00 -06:00
|
|
|
macro_rules! let_field_of {
|
|
|
|
// Optional field
|
|
|
|
($obj:ident, $field:ident?) => {
|
|
|
|
let $field = $obj.get(stringify!($field)).and_then(FromKclValue::from_kcl_val);
|
2024-11-14 17:27:19 -06:00
|
|
|
};
|
KCL: Patterns of patterns can use the original sketch/solid as target (#5284)
Right now, if you model something like this box with a button:
<img width="413" alt="Screenshot 2025-02-06 at 3 08 03 PM" src="https://github.com/user-attachments/assets/04818a70-7cf3-4ee3-b8c5-df5959ac10db" />
Let's say you want to pattern the button, and repeat it a second time. If you try, you'll actually pattern the entire model (box + button).
<img width="486" alt="Screenshot 2025-02-06 at 3 08 52 PM" src="https://github.com/user-attachments/assets/09fc28d9-5d80-4ab3-b4dc-b8de2945fcba" />
Why? Because right now, when you sketch on a face (like the button was), both the box and the button share the same ID. All extrusions from a solid will share the same ID, because they all refer to the same composite solid.
This is helpful in some ways -- arguably the solid _is_ just one big complex shape now -- but it's not helpful in other ways. What if I want to only pattern the button? Luckily there's an original ID for the button part, which is still stored. So we just need a way to tell the pattern stdlib functions whether to use the target's main ID or its original ID. This PR adds a new optional bool, `useOriginal`, to patterns. It's false by default, to keep backwards-compatibility (make sure that old KCL code doesn't change).
This PR is based on https://github.com/KittyCAD/modeling-app/pull/3914. It's based on work Serena and I are doing to fix a bug (engine does not allow patterning a 3D solid which was sketched on a face of another solid). @gserena01 our test program is now:
```
w = 400
case = startSketchOn('XY')
|> startProfileAt([-w, -w], %)
|> line(endAbsolute = [-w, w])
|> line(endAbsolute = [w, -w])
|> line(endAbsolute = [-w, -w])
|> close()
|> extrude(length = 200)
bump1 = startSketchOn(case, 'end')
|> circle({ center = [-50, -50], radius = 40 }, %)
|> extrude(length = 20)
// We pass in "bump1" here since we want to pattern just this object on the face.
useOriginal = true
target = bump1
transform = {
axis = [1, 0, 0],
instances = 3,
distance = -100
}
patternLinear3d(transform, target, useOriginal)
```
If you change the `useOriginal = true` to `false` you can see the difference.
2025-02-06 17:46:47 -06:00
|
|
|
// Optional field but with a different string used as the key
|
|
|
|
($obj:ident, $field:ident? $key:literal) => {
|
|
|
|
let $field = $obj.get($key).and_then(FromKclValue::from_kcl_val);
|
|
|
|
};
|
2024-11-16 11:19:00 -06:00
|
|
|
// Mandatory field, but with a different string used as the key.
|
|
|
|
($obj:ident, $field:ident $key:literal) => {
|
|
|
|
let $field = $obj.get($key).and_then(FromKclValue::from_kcl_val)?;
|
2024-11-14 17:27:19 -06:00
|
|
|
};
|
2024-11-16 11:19:00 -06:00
|
|
|
// Mandatory field, optionally with a type annotation
|
|
|
|
($obj:ident, $field:ident $(, $annotation:ty)?) => {
|
|
|
|
let $field $(: $annotation)? = $obj.get(stringify!($field)).and_then(FromKclValue::from_kcl_val)?;
|
2024-11-14 17:27:19 -06:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for crate::std::import::ImportFormat {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let obj = arg.as_object()?;
|
2024-11-20 09:53:37 -05:00
|
|
|
let_field_of!(obj, typ "format");
|
2024-11-14 17:27:19 -06:00
|
|
|
match typ {
|
|
|
|
"fbx" => Some(Self::Fbx {}),
|
|
|
|
"gltf" => Some(Self::Gltf {}),
|
|
|
|
"sldprt" => Some(Self::Sldprt {}),
|
|
|
|
"step" => Some(Self::Step {}),
|
|
|
|
"stl" => {
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, coords?);
|
|
|
|
let_field_of!(obj, units);
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self::Stl { coords, units })
|
2024-07-19 20:30:13 -05:00
|
|
|
}
|
2024-11-14 17:27:19 -06:00
|
|
|
"obj" => {
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, coords?);
|
|
|
|
let_field_of!(obj, units);
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self::Obj { coords, units })
|
|
|
|
}
|
|
|
|
"ply" => {
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, coords?);
|
|
|
|
let_field_of!(obj, units);
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self::Ply { coords, units })
|
|
|
|
}
|
|
|
|
_ => None,
|
2024-07-19 20:30:13 -05:00
|
|
|
}
|
2024-11-14 17:27:19 -06:00
|
|
|
}
|
2024-07-19 20:30:13 -05:00
|
|
|
}
|
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
impl<'a> FromKclValue<'a> for super::sketch::AngledLineThatIntersectsData {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, angle);
|
|
|
|
let_field_of!(obj, intersect_tag "intersectTag");
|
|
|
|
let_field_of!(obj, offset?);
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self {
|
|
|
|
angle,
|
|
|
|
intersect_tag,
|
|
|
|
offset,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for super::shapes::PolygonData {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, radius);
|
|
|
|
let_field_of!(obj, num_sides "numSides");
|
|
|
|
let_field_of!(obj, center);
|
|
|
|
let_field_of!(obj, inscribed);
|
2024-11-14 17:27:19 -06:00
|
|
|
let polygon_type = if inscribed {
|
|
|
|
PolygonType::Inscribed
|
|
|
|
} else {
|
|
|
|
PolygonType::Circumscribed
|
|
|
|
};
|
|
|
|
Some(Self {
|
|
|
|
radius,
|
|
|
|
num_sides,
|
|
|
|
center,
|
|
|
|
polygon_type,
|
|
|
|
inscribed,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for crate::std::polar::PolarCoordsData {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, angle);
|
|
|
|
let_field_of!(obj, length);
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self { angle, length })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-07 07:16:04 +13:00
|
|
|
impl<'a> FromKclValue<'a> for crate::execution::Plane {
|
2024-11-18 16:25:25 -05:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2025-01-22 09:42:09 +13:00
|
|
|
arg.as_plane().cloned()
|
2024-11-18 16:25:25 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-07 07:16:04 +13:00
|
|
|
impl<'a> FromKclValue<'a> for crate::execution::PlaneType {
|
2024-11-18 16:25:25 -05:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
|
|
|
let plane_type = match arg.as_str()? {
|
|
|
|
"XY" | "xy" => Self::XY,
|
|
|
|
"XZ" | "xz" => Self::XZ,
|
|
|
|
"YZ" | "yz" => Self::YZ,
|
|
|
|
"Custom" => Self::Custom,
|
|
|
|
_ => return None,
|
|
|
|
};
|
|
|
|
Some(plane_type)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::units::UnitLength {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let s = arg.as_str()?;
|
|
|
|
s.parse().ok()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::System {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, forward);
|
|
|
|
let_field_of!(obj, up);
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self { forward, up })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::AxisDirectionPair {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, axis);
|
|
|
|
let_field_of!(obj, direction);
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self { axis, direction })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::Axis {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let s = arg.as_str()?;
|
|
|
|
match s {
|
|
|
|
"y" => Some(Self::Y),
|
|
|
|
"z" => Some(Self::Z),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for PolygonType {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let s = arg.as_str()?;
|
|
|
|
match s {
|
|
|
|
"inscribed" => Some(Self::Inscribed),
|
|
|
|
_ => Some(Self::Circumscribed),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for kittycad_modeling_cmds::coord::Direction {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let s = arg.as_str()?;
|
|
|
|
match s {
|
|
|
|
"positive" => Some(Self::Positive),
|
|
|
|
"negative" => Some(Self::Negative),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for super::sketch::BezierData {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, to);
|
|
|
|
let_field_of!(obj, control1);
|
|
|
|
let_field_of!(obj, control2);
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self { to, control1, control2 })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-07 19:10:53 -08:00
|
|
|
impl<'a> FromKclValue<'a> for super::helix::HelixRevolutionsData {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, revolutions);
|
|
|
|
let_field_of!(obj, length?);
|
|
|
|
let_field_of!(obj, ccw?);
|
2024-11-14 17:27:19 -06:00
|
|
|
let ccw = ccw.unwrap_or_default();
|
2025-01-07 19:10:53 -08:00
|
|
|
let angle_start = obj.get("angleStart")?.as_f64()?;
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self {
|
|
|
|
revolutions,
|
|
|
|
angle_start,
|
|
|
|
ccw,
|
|
|
|
length,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for FaceTag {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let case1 = || match arg.as_str() {
|
|
|
|
Some("start" | "START") => Some(Self::StartOrEnd(super::sketch::StartOrEnd::Start)),
|
|
|
|
Some("end" | "END") => Some(Self::StartOrEnd(super::sketch::StartOrEnd::End)),
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
let case2 = || {
|
2024-11-16 11:19:00 -06:00
|
|
|
let tag = TagIdentifier::from_kcl_val(arg)?;
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self::Tag(Box::new(tag)))
|
|
|
|
};
|
|
|
|
case1().or_else(case2)
|
2024-10-01 08:50:23 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
impl<'a> FromKclValue<'a> for super::sketch::AngledLineToData {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
// Deserialize from an {angle, to} object.
|
|
|
|
let case1 = || {
|
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, to);
|
|
|
|
let_field_of!(obj, angle);
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self { angle, to })
|
|
|
|
};
|
|
|
|
// Deserialize from an [angle, to] array.
|
|
|
|
let case2 = || {
|
|
|
|
let [angle, to] = arg.as_point2d()?;
|
|
|
|
Some(Self { angle, to })
|
|
|
|
};
|
|
|
|
case1().or_else(case2)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for super::sketch::ArcData {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, radius);
|
2024-11-14 17:27:19 -06:00
|
|
|
let case1 = || {
|
2025-01-07 19:10:53 -08:00
|
|
|
let angle_start = obj.get("angleStart")?.as_f64()?;
|
|
|
|
let angle_end = obj.get("angleEnd")?.as_f64()?;
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self::AnglesAndRadius {
|
|
|
|
angle_start,
|
|
|
|
angle_end,
|
|
|
|
radius,
|
|
|
|
})
|
|
|
|
};
|
|
|
|
let case2 = || {
|
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, to);
|
|
|
|
let_field_of!(obj, center);
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self::CenterToRadius { center, to, radius })
|
|
|
|
};
|
|
|
|
case1().or_else(case2)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-18 17:17:16 -05:00
|
|
|
impl<'a> FromKclValue<'a> for super::sketch::ArcToData {
|
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
|
|
|
let obj = arg.as_object()?;
|
|
|
|
let_field_of!(obj, end);
|
|
|
|
let_field_of!(obj, interior);
|
|
|
|
Some(Self { end, interior })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
impl<'a> FromKclValue<'a> for super::sketch::TangentialArcData {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, radius);
|
|
|
|
let_field_of!(obj, offset);
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self::RadiusAndOffset { radius, offset })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-07 07:16:04 +13:00
|
|
|
impl<'a> FromKclValue<'a> for crate::execution::Point3d {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
// Case 1: object with x/y/z fields
|
|
|
|
if let Some(obj) = arg.as_object() {
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, x);
|
|
|
|
let_field_of!(obj, y);
|
|
|
|
let_field_of!(obj, z);
|
2024-11-14 17:27:19 -06:00
|
|
|
return Some(Self { x, y, z });
|
|
|
|
}
|
|
|
|
// Case 2: Array of 3 numbers.
|
2024-11-16 11:19:00 -06:00
|
|
|
let [x, y, z]: [f64; 3] = FromKclValue::from_kcl_val(arg)?;
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self { x, y, z })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for super::sketch::PlaneData {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
// Case 0: actual plane
|
2025-01-22 09:42:09 +13:00
|
|
|
if let KclValue::Plane { value } = arg {
|
2024-11-14 17:27:19 -06:00
|
|
|
return Some(Self::Plane {
|
2025-02-27 15:58:58 +13:00
|
|
|
origin: value.origin,
|
|
|
|
x_axis: value.x_axis,
|
|
|
|
y_axis: value.y_axis,
|
|
|
|
z_axis: value.z_axis,
|
2024-11-14 17:27:19 -06:00
|
|
|
});
|
|
|
|
}
|
|
|
|
// Case 1: predefined plane
|
|
|
|
if let Some(s) = arg.as_str() {
|
|
|
|
return match s {
|
|
|
|
"XY" | "xy" => Some(Self::XY),
|
|
|
|
"-XY" | "-xy" => Some(Self::NegXY),
|
|
|
|
"XZ" | "xz" => Some(Self::XZ),
|
|
|
|
"-XZ" | "-xz" => Some(Self::NegXZ),
|
|
|
|
"YZ" | "yz" => Some(Self::YZ),
|
|
|
|
"-YZ" | "-yz" => Some(Self::NegYZ),
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
// Case 2: custom plane
|
|
|
|
let obj = arg.as_object()?;
|
2024-11-26 09:51:43 -06:00
|
|
|
let_field_of!(obj, plane, &KclObjectFields);
|
2025-02-27 15:58:58 +13:00
|
|
|
let origin = plane.get("origin").and_then(FromKclValue::from_kcl_val)?;
|
|
|
|
let x_axis = plane.get("xAxis").and_then(FromKclValue::from_kcl_val)?;
|
|
|
|
let y_axis = plane.get("yAxis").and_then(FromKclValue::from_kcl_val)?;
|
|
|
|
let z_axis = plane.get("zAxis").and_then(FromKclValue::from_kcl_val)?;
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self::Plane {
|
|
|
|
origin,
|
|
|
|
x_axis,
|
|
|
|
y_axis,
|
|
|
|
z_axis,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-07 07:16:04 +13:00
|
|
|
impl<'a> FromKclValue<'a> for crate::execution::ExtrudePlane {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, face_id "faceId");
|
|
|
|
let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
|
|
|
|
let_field_of!(obj, geo_meta "geoMeta");
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self { face_id, tag, geo_meta })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-07 07:16:04 +13:00
|
|
|
impl<'a> FromKclValue<'a> for crate::execution::ExtrudeArc {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, face_id "faceId");
|
|
|
|
let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
|
|
|
|
let_field_of!(obj, geo_meta "geoMeta");
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self { face_id, tag, geo_meta })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-07 07:16:04 +13:00
|
|
|
impl<'a> FromKclValue<'a> for crate::execution::GeoMeta {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, id);
|
|
|
|
let_field_of!(obj, source_range "sourceRange");
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self {
|
|
|
|
id,
|
|
|
|
metadata: Metadata { source_range },
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-07 07:16:04 +13:00
|
|
|
impl<'a> FromKclValue<'a> for crate::execution::ChamferSurface {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, face_id "faceId");
|
|
|
|
let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
|
|
|
|
let_field_of!(obj, geo_meta "geoMeta");
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self { face_id, tag, geo_meta })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-07 07:16:04 +13:00
|
|
|
impl<'a> FromKclValue<'a> for crate::execution::FilletSurface {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, face_id "faceId");
|
|
|
|
let tag = FromKclValue::from_kcl_val(obj.get("tag")?);
|
|
|
|
let_field_of!(obj, geo_meta "geoMeta");
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self { face_id, tag, geo_meta })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for ExtrudeSurface {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-12-07 07:16:04 +13:00
|
|
|
let case1 = crate::execution::ExtrudePlane::from_kcl_val;
|
|
|
|
let case2 = crate::execution::ExtrudeArc::from_kcl_val;
|
|
|
|
let case3 = crate::execution::ChamferSurface::from_kcl_val;
|
|
|
|
let case4 = crate::execution::FilletSurface::from_kcl_val;
|
2024-11-14 17:27:19 -06:00
|
|
|
case1(arg)
|
|
|
|
.map(Self::ExtrudePlane)
|
|
|
|
.or_else(|| case2(arg).map(Self::ExtrudeArc))
|
|
|
|
.or_else(|| case3(arg).map(Self::Chamfer))
|
|
|
|
.or_else(|| case4(arg).map(Self::Fillet))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-07 07:16:04 +13:00
|
|
|
impl<'a> FromKclValue<'a> for crate::execution::EdgeCut {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, typ "type");
|
|
|
|
let tag = Box::new(obj.get("tag").and_then(FromKclValue::from_kcl_val));
|
|
|
|
let_field_of!(obj, edge_id "edgeId");
|
|
|
|
let_field_of!(obj, id);
|
2024-11-14 17:27:19 -06:00
|
|
|
match typ {
|
|
|
|
"fillet" => {
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, radius);
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self::Fillet {
|
|
|
|
edge_id,
|
|
|
|
tag,
|
|
|
|
id,
|
|
|
|
radius,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
"chamfer" => {
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, length);
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self::Chamfer {
|
|
|
|
id,
|
|
|
|
length,
|
|
|
|
edge_id,
|
|
|
|
tag,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! impl_from_kcl_for_vec {
|
|
|
|
($typ:path) => {
|
|
|
|
impl<'a> FromKclValue<'a> for Vec<$typ> {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
arg.as_array()?
|
|
|
|
.iter()
|
2024-11-16 11:19:00 -06:00
|
|
|
.map(|value| FromKclValue::from_kcl_val(value))
|
2024-11-14 17:27:19 -06:00
|
|
|
.collect::<Option<_>>()
|
2024-07-19 20:30:13 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
impl_from_kcl_for_vec!(FaceTag);
|
2024-12-07 07:16:04 +13:00
|
|
|
impl_from_kcl_for_vec!(crate::execution::EdgeCut);
|
|
|
|
impl_from_kcl_for_vec!(crate::execution::Metadata);
|
2024-11-14 17:27:19 -06:00
|
|
|
impl_from_kcl_for_vec!(super::fillet::EdgeReference);
|
|
|
|
impl_from_kcl_for_vec!(ExtrudeSurface);
|
2024-07-19 20:30:13 -05:00
|
|
|
|
2024-12-03 16:39:51 +13:00
|
|
|
impl<'a> FromKclValue<'a> for SourceRange {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2025-03-08 04:04:57 +13:00
|
|
|
let KclValue::MixedArray { value, meta: _ } = arg else {
|
2024-12-03 16:39:51 +13:00
|
|
|
return None;
|
|
|
|
};
|
|
|
|
if value.len() != 3 {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let v0 = value.first()?;
|
|
|
|
let v1 = value.get(1)?;
|
|
|
|
let v2 = value.get(2)?;
|
|
|
|
Some(SourceRange::new(
|
|
|
|
v0.as_usize()?,
|
|
|
|
v1.as_usize()?,
|
|
|
|
ModuleId::from_usize(v2.as_usize()?),
|
|
|
|
))
|
2024-11-14 17:27:19 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-07 07:16:04 +13:00
|
|
|
impl<'a> FromKclValue<'a> for crate::execution::Metadata {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
|
|
|
FromKclValue::from_kcl_val(arg).map(|sr| Self { source_range: sr })
|
2024-11-14 17:27:19 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-07 07:16:04 +13:00
|
|
|
impl<'a> FromKclValue<'a> for crate::execution::Solid {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
arg.as_solid().cloned()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-18 16:36:48 -07:00
|
|
|
impl<'a> FromKclValue<'a> for crate::execution::SolidOrSketchOrImportedGeometry {
|
2025-03-11 18:23:21 -07:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
|
|
|
match arg {
|
2025-03-17 17:57:26 +13:00
|
|
|
KclValue::Solid { value } => Some(Self::SolidSet(vec![(**value).clone()])),
|
2025-03-18 16:36:48 -07:00
|
|
|
KclValue::Sketch { value } => Some(Self::SketchSet(vec![(**value).clone()])),
|
|
|
|
KclValue::HomArray { value, .. } => {
|
|
|
|
let mut solids = vec![];
|
|
|
|
let mut sketches = vec![];
|
|
|
|
for item in value {
|
|
|
|
match item {
|
|
|
|
KclValue::Solid { value } => solids.push((**value).clone()),
|
|
|
|
KclValue::Sketch { value } => sketches.push((**value).clone()),
|
|
|
|
_ => return None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !solids.is_empty() {
|
|
|
|
Some(Self::SolidSet(solids))
|
|
|
|
} else {
|
|
|
|
Some(Self::SketchSet(sketches))
|
|
|
|
}
|
|
|
|
}
|
2025-03-11 18:23:21 -07:00
|
|
|
KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
impl<'a> FromKclValue<'a> for super::sketch::SketchData {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-18 16:25:25 -05:00
|
|
|
// Order is critical since PlaneData is a subset of Plane.
|
2024-12-07 07:16:04 +13:00
|
|
|
let case1 = crate::execution::Plane::from_kcl_val;
|
2024-11-18 16:25:25 -05:00
|
|
|
let case2 = super::sketch::PlaneData::from_kcl_val;
|
2024-12-07 07:16:04 +13:00
|
|
|
let case3 = crate::execution::Solid::from_kcl_val;
|
2025-03-17 17:57:26 +13:00
|
|
|
let case4 = <Vec<Solid>>::from_kcl_val;
|
2024-11-14 17:27:19 -06:00
|
|
|
case1(arg)
|
2024-11-18 16:25:25 -05:00
|
|
|
.map(Box::new)
|
2024-11-14 17:27:19 -06:00
|
|
|
.map(Self::Plane)
|
2024-11-18 16:25:25 -05:00
|
|
|
.or_else(|| case2(arg).map(Self::PlaneOrientation))
|
|
|
|
.or_else(|| case3(arg).map(Box::new).map(Self::Solid))
|
2025-03-17 17:57:26 +13:00
|
|
|
.or_else(|| case4(arg).map(|v| Box::new(v[0].clone())).map(Self::Solid))
|
2024-11-14 17:27:19 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-07 19:10:53 -08:00
|
|
|
impl<'a> FromKclValue<'a> for super::axis_or_reference::AxisAndOrigin2d {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
// Case 1: predefined planes.
|
|
|
|
if let Some(s) = arg.as_str() {
|
|
|
|
return match s {
|
|
|
|
"X" | "x" => Some(Self::X),
|
|
|
|
"Y" | "y" => Some(Self::Y),
|
|
|
|
"-X" | "-x" => Some(Self::NegX),
|
|
|
|
"-Y" | "-y" => Some(Self::NegY),
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
// Case 2: custom planes.
|
|
|
|
let obj = arg.as_object()?;
|
2024-11-26 09:51:43 -06:00
|
|
|
let_field_of!(obj, custom, &KclObjectFields);
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(custom, origin);
|
|
|
|
let_field_of!(custom, axis);
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self::Custom { axis, origin })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-07 19:10:53 -08:00
|
|
|
impl<'a> FromKclValue<'a> for super::axis_or_reference::AxisAndOrigin3d {
|
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
|
|
|
// Case 1: predefined planes.
|
|
|
|
if let Some(s) = arg.as_str() {
|
|
|
|
return match s {
|
|
|
|
"X" | "x" => Some(Self::X),
|
|
|
|
"Y" | "y" => Some(Self::Y),
|
|
|
|
"Z" | "z" => Some(Self::Z),
|
|
|
|
"-X" | "-x" => Some(Self::NegX),
|
|
|
|
"-Y" | "-y" => Some(Self::NegY),
|
|
|
|
"-Z" | "-z" => Some(Self::NegZ),
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
// Case 2: custom planes.
|
|
|
|
let obj = arg.as_object()?;
|
|
|
|
let_field_of!(obj, custom, &KclObjectFields);
|
|
|
|
let_field_of!(custom, origin);
|
|
|
|
let_field_of!(custom, axis);
|
|
|
|
Some(Self::Custom { axis, origin })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
impl<'a> FromKclValue<'a> for super::fillet::EdgeReference {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let id = arg.as_uuid().map(Self::Uuid);
|
2024-11-16 11:19:00 -06:00
|
|
|
let tag = || TagIdentifier::from_kcl_val(arg).map(Box::new).map(Self::Tag);
|
2024-11-14 17:27:19 -06:00
|
|
|
id.or_else(tag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-07 19:10:53 -08:00
|
|
|
impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis2dOrEdgeReference {
|
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
|
|
|
let case1 = super::axis_or_reference::AxisAndOrigin2d::from_kcl_val;
|
|
|
|
let case2 = super::fillet::EdgeReference::from_kcl_val;
|
|
|
|
case1(arg).map(Self::Axis).or_else(|| case2(arg).map(Self::Edge))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis3dOrEdgeReference {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2025-01-07 19:10:53 -08:00
|
|
|
let case1 = super::axis_or_reference::AxisAndOrigin3d::from_kcl_val;
|
2024-11-16 11:19:00 -06:00
|
|
|
let case2 = super::fillet::EdgeReference::from_kcl_val;
|
2024-11-14 17:27:19 -06:00
|
|
|
case1(arg).map(Self::Axis).or_else(|| case2(arg).map(Self::Edge))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for super::mirror::Mirror2dData {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, axis);
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self { axis })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for super::sketch::AngledLineData {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let case1 = |arg: &KclValue| {
|
|
|
|
let obj = arg.as_object()?;
|
2024-11-16 11:19:00 -06:00
|
|
|
let_field_of!(obj, angle);
|
|
|
|
let_field_of!(obj, length);
|
2024-11-14 17:27:19 -06:00
|
|
|
Some(Self::AngleAndLengthNamed { angle, length })
|
|
|
|
};
|
|
|
|
let case2 = |arg: &KclValue| {
|
|
|
|
let array = arg.as_array()?;
|
|
|
|
let ang = array.first()?.as_f64()?;
|
|
|
|
let len = array.get(1)?.as_f64()?;
|
|
|
|
Some(Self::AngleAndLengthPair([ang, len]))
|
|
|
|
};
|
|
|
|
case1(arg).or_else(|| case2(arg))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for i64 {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-25 10:50:43 +13:00
|
|
|
match arg {
|
2025-02-14 13:03:23 +13:00
|
|
|
KclValue::Number { value, .. } => crate::try_f64_to_i64(*value),
|
2024-11-25 10:50:43 +13:00
|
|
|
_ => None,
|
|
|
|
}
|
2024-11-14 17:27:19 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-16 11:19:00 -06:00
|
|
|
impl<'a> FromKclValue<'a> for &'a str {
|
2024-11-26 09:51:43 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-16 11:19:00 -06:00
|
|
|
let KclValue::String { value, meta: _ } = arg else {
|
|
|
|
return None;
|
|
|
|
};
|
|
|
|
Some(value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-26 09:51:43 -06:00
|
|
|
impl<'a> FromKclValue<'a> for &'a KclObjectFields {
|
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-16 11:19:00 -06:00
|
|
|
let KclValue::Object { value, meta: _ } = arg else {
|
|
|
|
return None;
|
|
|
|
};
|
|
|
|
Some(value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for uuid::Uuid {
|
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
|
|
|
let KclValue::Uuid { value, meta: _ } = arg else {
|
|
|
|
return None;
|
|
|
|
};
|
|
|
|
Some(*value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
impl<'a> FromKclValue<'a> for u32 {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-25 10:50:43 +13:00
|
|
|
match arg {
|
2025-02-14 13:03:23 +13:00
|
|
|
KclValue::Number { value, .. } => crate::try_f64_to_u32(*value),
|
2024-11-25 10:50:43 +13:00
|
|
|
_ => None,
|
|
|
|
}
|
2024-11-14 17:27:19 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for NonZeroU32 {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
|
|
|
u32::from_kcl_val(arg).and_then(|x| x.try_into().ok())
|
2024-11-14 17:27:19 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for u64 {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-25 10:50:43 +13:00
|
|
|
match arg {
|
2025-02-14 13:03:23 +13:00
|
|
|
KclValue::Number { value, .. } => crate::try_f64_to_u64(*value),
|
2024-11-25 10:50:43 +13:00
|
|
|
_ => None,
|
|
|
|
}
|
2024-11-14 17:27:19 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<'a> FromKclValue<'a> for f64 {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
match arg {
|
2025-02-14 13:03:23 +13:00
|
|
|
KclValue::Number { value, .. } => Some(*value),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-02-21 12:36:21 +13:00
|
|
|
impl<'a> FromKclValue<'a> for TyF64 {
|
2025-02-14 13:03:23 +13:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
|
|
|
match arg {
|
2025-02-21 12:36:21 +13:00
|
|
|
KclValue::Number { value, ty, .. } => Some(TyF64::new(*value, ty.clone())),
|
2024-11-14 17:27:19 -06:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-03-17 17:57:26 +13:00
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
impl<'a> FromKclValue<'a> for Sketch {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let KclValue::Sketch { value } = arg else {
|
|
|
|
return None;
|
|
|
|
};
|
|
|
|
Some(value.as_ref().to_owned())
|
|
|
|
}
|
|
|
|
}
|
2025-01-07 19:10:53 -08:00
|
|
|
|
|
|
|
impl<'a> FromKclValue<'a> for Helix {
|
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2025-01-22 09:42:09 +13:00
|
|
|
let KclValue::Helix { value } = arg else {
|
2025-01-07 19:10:53 -08:00
|
|
|
return None;
|
|
|
|
};
|
|
|
|
Some(value.as_ref().to_owned())
|
|
|
|
}
|
|
|
|
}
|
2025-03-17 17:57:26 +13:00
|
|
|
|
2025-01-07 19:10:53 -08:00
|
|
|
impl<'a> FromKclValue<'a> for SweepPath {
|
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
|
|
|
let case1 = Sketch::from_kcl_val;
|
2025-03-17 17:57:26 +13:00
|
|
|
let case2 = <Vec<Sketch>>::from_kcl_val;
|
|
|
|
let case3 = Helix::from_kcl_val;
|
2025-01-07 19:10:53 -08:00
|
|
|
case1(arg)
|
|
|
|
.map(Self::Sketch)
|
2025-03-17 17:57:26 +13:00
|
|
|
.or_else(|| case2(arg).map(|arg0: Vec<Sketch>| Self::Sketch(arg0[0].clone())))
|
|
|
|
.or_else(|| case3(arg).map(|arg0: Helix| Self::Helix(Box::new(arg0))))
|
2025-01-07 19:10:53 -08:00
|
|
|
}
|
|
|
|
}
|
2024-11-14 17:27:19 -06:00
|
|
|
impl<'a> FromKclValue<'a> for String {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let KclValue::String { value, meta: _ } = arg else {
|
|
|
|
return None;
|
|
|
|
};
|
|
|
|
Some(value.to_owned())
|
|
|
|
}
|
|
|
|
}
|
2024-12-05 17:56:49 +13:00
|
|
|
impl<'a> FromKclValue<'a> for crate::parsing::ast::types::KclNone {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let KclValue::KclNone { value, meta: _ } = arg else {
|
|
|
|
return None;
|
|
|
|
};
|
|
|
|
Some(value.to_owned())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<'a> FromKclValue<'a> for bool {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-11-14 17:27:19 -06:00
|
|
|
let KclValue::Bool { value, meta: _ } = arg else {
|
|
|
|
return None;
|
|
|
|
};
|
|
|
|
Some(*value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-17 17:57:26 +13:00
|
|
|
impl<'a> FromKclValue<'a> for Box<Solid> {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2025-03-17 17:57:26 +13:00
|
|
|
let KclValue::Solid { value } = arg else {
|
|
|
|
return None;
|
|
|
|
};
|
|
|
|
Some(value.to_owned())
|
2024-07-19 20:30:13 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-17 17:57:26 +13:00
|
|
|
impl<'a> FromKclValue<'a> for Vec<Solid> {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2025-03-17 17:57:26 +13:00
|
|
|
let KclValue::HomArray { value, .. } = arg else {
|
2024-07-19 20:30:13 -05:00
|
|
|
return None;
|
|
|
|
};
|
2025-03-17 17:57:26 +13:00
|
|
|
value.iter().map(Solid::from_kcl_val).collect()
|
2024-07-19 20:30:13 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-17 17:57:26 +13:00
|
|
|
impl<'a> FromKclValue<'a> for Vec<Sketch> {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2025-03-17 17:57:26 +13:00
|
|
|
let KclValue::HomArray { value, .. } = arg else {
|
|
|
|
return None;
|
|
|
|
};
|
|
|
|
value.iter().map(Sketch::from_kcl_val).collect()
|
2024-07-19 20:30:13 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-17 17:57:26 +13:00
|
|
|
impl<'a> FromKclValue<'a> for &'a FunctionSource {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2025-03-17 17:57:26 +13:00
|
|
|
arg.get_function()
|
2024-07-19 20:30:13 -05:00
|
|
|
}
|
|
|
|
}
|
2024-11-14 17:27:19 -06:00
|
|
|
|
2024-09-27 15:44:44 -07:00
|
|
|
impl<'a> FromKclValue<'a> for SketchOrSurface {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-07-19 20:30:13 -05:00
|
|
|
match arg {
|
2024-11-14 17:27:19 -06:00
|
|
|
KclValue::Sketch { value: sg } => Some(Self::Sketch(sg.to_owned())),
|
2025-01-22 09:42:09 +13:00
|
|
|
KclValue::Plane { value } => Some(Self::SketchSurface(SketchSurface::Plane(value.clone()))),
|
|
|
|
KclValue::Face { value } => Some(Self::SketchSurface(SketchSurface::Face(value.clone()))),
|
2024-07-19 20:30:13 -05:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-08-12 16:53:24 -05:00
|
|
|
impl<'a> FromKclValue<'a> for SketchSurface {
|
2024-11-16 11:19:00 -06:00
|
|
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
2024-07-19 20:30:13 -05:00
|
|
|
match arg {
|
2025-01-22 09:42:09 +13:00
|
|
|
KclValue::Plane { value } => Some(Self::Plane(value.clone())),
|
|
|
|
KclValue::Face { value } => Some(Self::Face(value.clone())),
|
2024-07-19 20:30:13 -05:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-11-05 14:10:35 -06:00
|
|
|
|
2024-11-14 17:27:19 -06:00
|
|
|
impl From<Args> for Metadata {
|
|
|
|
fn from(value: Args) -> Self {
|
|
|
|
Self {
|
|
|
|
source_range: value.source_range,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Args> for Vec<Metadata> {
|
|
|
|
fn from(value: Args) -> Self {
|
|
|
|
vec![Metadata {
|
|
|
|
source_range: value.source_range,
|
|
|
|
}]
|
|
|
|
}
|
2024-11-05 14:10:35 -06:00
|
|
|
}
|