2024-03-13 17:16:57 -07:00
|
|
|
//! Standard library shapes.
|
|
|
|
|
|
|
|
use anyhow::Result;
|
2025-03-01 13:59:01 -08:00
|
|
|
use kcl_derive_docs::stdlib;
|
2024-09-27 15:44:44 -07:00
|
|
|
use kcmc::{
|
|
|
|
each_cmd as mcmd,
|
|
|
|
length_unit::LengthUnit,
|
|
|
|
shared::{Angle, Point2d as KPoint2d},
|
|
|
|
ModelingCmd,
|
|
|
|
};
|
2024-09-23 22:42:51 +10:00
|
|
|
use kittycad_modeling_cmds as kcmc;
|
|
|
|
use kittycad_modeling_cmds::shared::PathSegment;
|
2023-11-09 09:58:20 -06:00
|
|
|
use schemars::JsonSchema;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
|
|
|
use crate::{
|
2024-10-28 20:52:51 -04:00
|
|
|
errors::{KclError, KclErrorDetails},
|
2024-12-07 07:16:04 +13:00
|
|
|
execution::{BasePath, ExecState, GeoMeta, KclValue, Path, Sketch, SketchSurface},
|
2024-12-05 17:56:49 +13:00
|
|
|
parsing::ast::types::TagNode,
|
2025-01-04 12:18:29 -05:00
|
|
|
std::{
|
2025-02-28 17:40:01 -08:00
|
|
|
sketch::NEW_TAG_KW,
|
2025-01-04 12:18:29 -05:00
|
|
|
utils::{calculate_circle_center, distance},
|
|
|
|
Args,
|
|
|
|
},
|
2023-11-09 09:58:20 -06:00
|
|
|
};
|
|
|
|
|
2024-09-27 15:44:44 -07:00
|
|
|
/// A sketch surface or a sketch.
|
2024-03-13 17:16:57 -07:00
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
|
|
|
#[ts(export)]
|
|
|
|
#[serde(untagged)]
|
2024-09-27 15:44:44 -07:00
|
|
|
pub enum SketchOrSurface {
|
2024-03-13 17:16:57 -07:00
|
|
|
SketchSurface(SketchSurface),
|
2024-09-27 15:44:44 -07:00
|
|
|
Sketch(Box<Sketch>),
|
2023-11-09 09:58:20 -06:00
|
|
|
}
|
|
|
|
|
2024-03-13 17:16:57 -07:00
|
|
|
/// Sketch a circle.
|
2024-09-16 15:10:33 -04:00
|
|
|
pub async fn circle(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
2025-02-28 17:40:01 -08:00
|
|
|
let sketch_or_surface = args.get_unlabeled_kw_arg("sketchOrSurface")?;
|
|
|
|
let center = args.get_kw_arg("center")?;
|
|
|
|
let radius = args.get_kw_arg("radius")?;
|
|
|
|
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
2023-11-09 09:58:20 -06:00
|
|
|
|
2025-02-28 17:40:01 -08:00
|
|
|
let sketch = inner_circle(sketch_or_surface, center, radius, tag, exec_state, args).await?;
|
2024-11-14 17:27:19 -06:00
|
|
|
Ok(KclValue::Sketch {
|
|
|
|
value: Box::new(sketch),
|
|
|
|
})
|
2023-11-09 09:58:20 -06:00
|
|
|
}
|
|
|
|
|
2024-08-06 20:27:26 -04:00
|
|
|
/// Construct a 2-dimensional circle, of the specified radius, centered at
|
|
|
|
/// the provided (x, y) origin point.
|
2024-03-13 17:16:57 -07:00
|
|
|
///
|
|
|
|
/// ```no_run
|
2024-12-12 11:33:37 -05:00
|
|
|
/// exampleSketch = startSketchOn("-XZ")
|
2025-02-28 17:40:01 -08:00
|
|
|
/// |> circle( center = [0, 0], radius = 10 )
|
2024-03-13 17:16:57 -07:00
|
|
|
///
|
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
|
|
|
/// example = extrude(exampleSketch, length = 5)
|
2024-03-13 17:16:57 -07:00
|
|
|
/// ```
|
2024-05-22 09:15:38 -07:00
|
|
|
///
|
|
|
|
/// ```no_run
|
2024-12-12 11:33:37 -05:00
|
|
|
/// exampleSketch = startSketchOn("XZ")
|
2024-05-22 09:15:38 -07:00
|
|
|
/// |> startProfileAt([-15, 0], %)
|
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
|
|
|
/// |> line(end = [30, 0])
|
|
|
|
/// |> line(end = [0, 30])
|
|
|
|
/// |> line(end = [-30, 0])
|
|
|
|
/// |> close()
|
2025-02-28 17:40:01 -08:00
|
|
|
/// |> hole(circle( center = [0, 15], radius = 5), %)
|
2024-05-22 09:15:38 -07:00
|
|
|
///
|
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
|
|
|
/// example = extrude(exampleSketch, length = 5)
|
2024-08-13 14:14:23 -07:00
|
|
|
/// ```
|
2024-03-13 17:16:57 -07:00
|
|
|
#[stdlib {
|
|
|
|
name = "circle",
|
2025-02-28 17:40:01 -08:00
|
|
|
keywords = true,
|
|
|
|
unlabeled_first = true,
|
|
|
|
args = {
|
|
|
|
sketch_or_surface = {docs = "Plane or surface to sketch on."},
|
|
|
|
center = {docs = "The center of the circle."},
|
|
|
|
radius = {docs = "The radius of the circle."},
|
|
|
|
tag = { docs = "Create a new tag which refers to this circle"},
|
|
|
|
}
|
2024-03-13 17:16:57 -07:00
|
|
|
}]
|
|
|
|
async fn inner_circle(
|
2025-02-28 17:40:01 -08:00
|
|
|
sketch_or_surface: SketchOrSurface,
|
|
|
|
center: [f64; 2],
|
|
|
|
radius: f64,
|
2024-10-30 16:52:17 -04:00
|
|
|
tag: Option<TagNode>,
|
2024-09-16 15:10:33 -04:00
|
|
|
exec_state: &mut ExecState,
|
2024-03-13 17:16:57 -07:00
|
|
|
args: Args,
|
2024-09-27 15:44:44 -07:00
|
|
|
) -> Result<Sketch, KclError> {
|
2025-02-28 17:40:01 -08:00
|
|
|
let sketch_surface = match sketch_or_surface {
|
2024-09-27 15:44:44 -07:00
|
|
|
SketchOrSurface::SketchSurface(surface) => surface,
|
2025-02-28 17:40:01 -08:00
|
|
|
SketchOrSurface::Sketch(s) => s.on,
|
2024-03-13 17:16:57 -07:00
|
|
|
};
|
2025-02-20 10:12:37 +13:00
|
|
|
let units = sketch_surface.units();
|
2024-09-27 15:44:44 -07:00
|
|
|
let sketch = crate::std::sketch::inner_start_profile_at(
|
2025-02-28 17:40:01 -08:00
|
|
|
[center[0] + radius, center[1]],
|
2024-09-16 15:10:33 -04:00
|
|
|
sketch_surface,
|
|
|
|
None,
|
|
|
|
exec_state,
|
|
|
|
args.clone(),
|
|
|
|
)
|
|
|
|
.await?;
|
2024-03-13 17:16:57 -07:00
|
|
|
|
2025-02-28 17:40:01 -08:00
|
|
|
let from = [center[0] + radius, center[1]];
|
2024-09-23 22:42:51 +10:00
|
|
|
let angle_start = Angle::zero();
|
|
|
|
let angle_end = Angle::turn();
|
|
|
|
|
2024-12-17 09:38:32 +13:00
|
|
|
let id = exec_state.next_uuid();
|
2024-09-23 22:42:51 +10:00
|
|
|
|
|
|
|
args.batch_modeling_cmd(
|
|
|
|
id,
|
|
|
|
ModelingCmd::from(mcmd::ExtendPath {
|
2024-09-27 15:44:44 -07:00
|
|
|
path: sketch.id.into(),
|
2024-09-23 22:42:51 +10:00
|
|
|
segment: PathSegment::Arc {
|
|
|
|
start: angle_start,
|
|
|
|
end: angle_end,
|
2025-02-28 17:40:01 -08:00
|
|
|
center: KPoint2d::from(center).map(LengthUnit),
|
|
|
|
radius: radius.into(),
|
2024-09-23 22:42:51 +10:00
|
|
|
relative: false,
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
let current_path = Path::Circle {
|
|
|
|
base: BasePath {
|
2024-11-25 13:07:03 -05:00
|
|
|
from,
|
|
|
|
to: from,
|
2024-09-23 22:42:51 +10:00
|
|
|
tag: tag.clone(),
|
2025-02-20 10:12:37 +13:00
|
|
|
units,
|
2024-09-23 22:42:51 +10:00
|
|
|
geo_meta: GeoMeta {
|
|
|
|
id,
|
|
|
|
metadata: args.source_range.into(),
|
|
|
|
},
|
2024-03-13 17:16:57 -07:00
|
|
|
},
|
2025-02-28 17:40:01 -08:00
|
|
|
radius,
|
|
|
|
center,
|
2024-11-25 18:17:47 -05:00
|
|
|
ccw: angle_start < angle_end,
|
2024-09-23 22:42:51 +10:00
|
|
|
};
|
|
|
|
|
2024-09-27 15:44:44 -07:00
|
|
|
let mut new_sketch = sketch.clone();
|
2024-09-23 22:42:51 +10:00
|
|
|
if let Some(tag) = &tag {
|
2025-03-17 12:28:51 +13:00
|
|
|
new_sketch.add_tag(tag, ¤t_path, exec_state);
|
2024-09-23 22:42:51 +10:00
|
|
|
}
|
|
|
|
|
2024-10-23 12:42:54 -05:00
|
|
|
new_sketch.paths.push(current_path);
|
2024-09-23 22:42:51 +10:00
|
|
|
|
2024-09-27 15:44:44 -07:00
|
|
|
args.batch_modeling_cmd(id, ModelingCmd::from(mcmd::ClosePath { path_id: new_sketch.id }))
|
|
|
|
.await?;
|
2024-03-13 17:16:57 -07:00
|
|
|
|
2024-09-27 15:44:44 -07:00
|
|
|
Ok(new_sketch)
|
2023-11-09 09:58:20 -06:00
|
|
|
}
|
2024-10-28 20:52:51 -04:00
|
|
|
|
2025-01-04 12:18:29 -05:00
|
|
|
/// Sketch a 3-point circle.
|
|
|
|
pub async fn circle_three_point(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
|
|
|
let p1 = args.get_kw_arg("p1")?;
|
|
|
|
let p2 = args.get_kw_arg("p2")?;
|
|
|
|
let p3 = args.get_kw_arg("p3")?;
|
|
|
|
let sketch_surface_or_group = args.get_unlabeled_kw_arg("sketch_surface_or_group")?;
|
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 tag = args.get_kw_arg_opt("tag")?;
|
2025-01-04 12:18:29 -05:00
|
|
|
|
|
|
|
let sketch = inner_circle_three_point(p1, p2, p3, sketch_surface_or_group, tag, exec_state, args).await?;
|
|
|
|
Ok(KclValue::Sketch {
|
|
|
|
value: Box::new(sketch),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Construct a circle derived from 3 points.
|
|
|
|
///
|
|
|
|
/// ```no_run
|
|
|
|
/// exampleSketch = startSketchOn("XY")
|
|
|
|
/// |> circleThreePoint(p1 = [10,10], p2 = [20,8], p3 = [15,5])
|
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
|
|
|
/// |> extrude(length = 5)
|
2025-01-04 12:18:29 -05:00
|
|
|
/// ```
|
|
|
|
#[stdlib {
|
|
|
|
name = "circleThreePoint",
|
|
|
|
keywords = true,
|
|
|
|
unlabeled_first = true,
|
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
|
|
|
args = {
|
|
|
|
p1 = {docs = "1st point to derive the circle."},
|
|
|
|
p2 = {docs = "2nd point to derive the circle."},
|
|
|
|
p3 = {docs = "3rd point to derive the circle."},
|
|
|
|
sketch_surface_or_group = {docs = "Plane or surface to sketch on."},
|
|
|
|
tag = {docs = "Identifier for the circle to reference elsewhere."},
|
2025-01-04 12:18:29 -05:00
|
|
|
}
|
|
|
|
}]
|
2025-02-15 00:57:04 +11:00
|
|
|
|
|
|
|
// Similar to inner_circle, but needs to retain 3-point information in the
|
|
|
|
// path so it can be used for other features, otherwise it's lost.
|
2025-01-04 12:18:29 -05:00
|
|
|
async fn inner_circle_three_point(
|
|
|
|
p1: [f64; 2],
|
|
|
|
p2: [f64; 2],
|
|
|
|
p3: [f64; 2],
|
|
|
|
sketch_surface_or_group: SketchOrSurface,
|
|
|
|
tag: Option<TagNode>,
|
|
|
|
exec_state: &mut ExecState,
|
|
|
|
args: Args,
|
|
|
|
) -> Result<Sketch, KclError> {
|
|
|
|
let center = calculate_circle_center(p1, p2, p3);
|
2025-02-15 00:57:04 +11:00
|
|
|
// It can be the distance to any of the 3 points - they all lay on the circumference.
|
|
|
|
let radius = distance(center.into(), p2.into());
|
|
|
|
|
|
|
|
let sketch_surface = match sketch_surface_or_group {
|
|
|
|
SketchOrSurface::SketchSurface(surface) => surface,
|
|
|
|
SketchOrSurface::Sketch(group) => group.on,
|
|
|
|
};
|
|
|
|
let sketch = crate::std::sketch::inner_start_profile_at(
|
|
|
|
[center[0] + radius, center[1]],
|
|
|
|
sketch_surface,
|
|
|
|
None,
|
2025-01-04 12:18:29 -05:00
|
|
|
exec_state,
|
2025-02-15 00:57:04 +11:00
|
|
|
args.clone(),
|
2025-01-04 12:18:29 -05:00
|
|
|
)
|
2025-02-15 00:57:04 +11:00
|
|
|
.await?;
|
|
|
|
|
|
|
|
let from = [center[0] + radius, center[1]];
|
|
|
|
let angle_start = Angle::zero();
|
|
|
|
let angle_end = Angle::turn();
|
|
|
|
|
|
|
|
let id = exec_state.next_uuid();
|
|
|
|
|
|
|
|
args.batch_modeling_cmd(
|
|
|
|
id,
|
|
|
|
ModelingCmd::from(mcmd::ExtendPath {
|
|
|
|
path: sketch.id.into(),
|
|
|
|
segment: PathSegment::Arc {
|
|
|
|
start: angle_start,
|
|
|
|
end: angle_end,
|
|
|
|
center: KPoint2d::from(center).map(LengthUnit),
|
|
|
|
radius: radius.into(),
|
|
|
|
relative: false,
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
let current_path = Path::CircleThreePoint {
|
|
|
|
base: BasePath {
|
|
|
|
from,
|
|
|
|
to: from,
|
|
|
|
tag: tag.clone(),
|
2025-02-20 10:12:37 +13:00
|
|
|
units: sketch.units,
|
2025-02-15 00:57:04 +11:00
|
|
|
geo_meta: GeoMeta {
|
|
|
|
id,
|
|
|
|
metadata: args.source_range.into(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
p1,
|
|
|
|
p2,
|
|
|
|
p3,
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut new_sketch = sketch.clone();
|
|
|
|
if let Some(tag) = &tag {
|
2025-03-17 12:28:51 +13:00
|
|
|
new_sketch.add_tag(tag, ¤t_path, exec_state);
|
2025-02-15 00:57:04 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
new_sketch.paths.push(current_path);
|
|
|
|
|
|
|
|
args.batch_modeling_cmd(id, ModelingCmd::from(mcmd::ClosePath { path_id: new_sketch.id }))
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(new_sketch)
|
2025-01-04 12:18:29 -05:00
|
|
|
}
|
|
|
|
|
2024-10-28 20:52:51 -04:00
|
|
|
/// Type of the polygon
|
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Default)]
|
|
|
|
#[ts(export)]
|
|
|
|
#[serde(rename_all = "lowercase")]
|
|
|
|
pub enum PolygonType {
|
|
|
|
#[default]
|
|
|
|
Inscribed,
|
|
|
|
Circumscribed,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Data for drawing a polygon
|
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
|
|
|
#[ts(export)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct PolygonData {
|
|
|
|
/// The radius of the polygon
|
|
|
|
pub radius: f64,
|
|
|
|
/// The number of sides in the polygon
|
|
|
|
pub num_sides: u64,
|
|
|
|
/// The center point of the polygon
|
|
|
|
pub center: [f64; 2],
|
|
|
|
/// The type of the polygon (inscribed or circumscribed)
|
|
|
|
#[serde(skip)]
|
2024-11-14 17:27:19 -06:00
|
|
|
pub polygon_type: PolygonType,
|
2024-10-28 20:52:51 -04:00
|
|
|
/// Whether the polygon is inscribed (true) or circumscribed (false) about a circle with the specified radius
|
|
|
|
#[serde(default = "default_inscribed")]
|
|
|
|
pub inscribed: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_inscribed() -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a regular polygon with the specified number of sides and radius.
|
|
|
|
pub async fn polygon(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
2024-10-30 16:52:17 -04:00
|
|
|
let (data, sketch_surface_or_group, tag): (PolygonData, SketchOrSurface, Option<TagNode>) =
|
2024-10-28 20:52:51 -04:00
|
|
|
args.get_polygon_args()?;
|
|
|
|
|
|
|
|
let sketch = inner_polygon(data, sketch_surface_or_group, tag, exec_state, args).await?;
|
2024-11-14 17:27:19 -06:00
|
|
|
Ok(KclValue::Sketch {
|
|
|
|
value: Box::new(sketch),
|
|
|
|
})
|
2024-10-28 20:52:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a regular polygon with the specified number of sides that is either inscribed or circumscribed around a circle of the specified radius.
|
|
|
|
///
|
|
|
|
/// ```no_run
|
|
|
|
/// // Create a regular hexagon inscribed in a circle of radius 10
|
|
|
|
/// hex = startSketchOn('XY')
|
|
|
|
/// |> polygon({
|
2024-11-25 09:21:55 +13:00
|
|
|
/// radius = 10,
|
|
|
|
/// numSides = 6,
|
|
|
|
/// center = [0, 0],
|
|
|
|
/// inscribed = true,
|
2024-10-28 20:52:51 -04:00
|
|
|
/// }, %)
|
|
|
|
///
|
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
|
|
|
/// example = extrude(hex, length = 5)
|
2024-10-28 20:52:51 -04:00
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// ```no_run
|
|
|
|
/// // Create a square circumscribed around a circle of radius 5
|
|
|
|
/// square = startSketchOn('XY')
|
|
|
|
/// |> polygon({
|
2024-11-25 09:21:55 +13:00
|
|
|
/// radius = 5.0,
|
|
|
|
/// numSides = 4,
|
|
|
|
/// center = [10, 10],
|
|
|
|
/// inscribed = false,
|
2024-10-28 20:52:51 -04:00
|
|
|
/// }, %)
|
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
|
|
|
/// example = extrude(square, length = 5)
|
2024-10-28 20:52:51 -04:00
|
|
|
/// ```
|
|
|
|
#[stdlib {
|
|
|
|
name = "polygon",
|
|
|
|
}]
|
|
|
|
async fn inner_polygon(
|
|
|
|
data: PolygonData,
|
|
|
|
sketch_surface_or_group: SketchOrSurface,
|
2024-10-30 16:52:17 -04:00
|
|
|
tag: Option<TagNode>,
|
2024-10-28 20:52:51 -04:00
|
|
|
exec_state: &mut ExecState,
|
|
|
|
args: Args,
|
|
|
|
) -> Result<Sketch, KclError> {
|
|
|
|
if data.num_sides < 3 {
|
|
|
|
return Err(KclError::Type(KclErrorDetails {
|
|
|
|
message: "Polygon must have at least 3 sides".to_string(),
|
|
|
|
source_ranges: vec![args.source_range],
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
if data.radius <= 0.0 {
|
|
|
|
return Err(KclError::Type(KclErrorDetails {
|
|
|
|
message: "Radius must be greater than 0".to_string(),
|
|
|
|
source_ranges: vec![args.source_range],
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
let sketch_surface = match sketch_surface_or_group {
|
|
|
|
SketchOrSurface::SketchSurface(surface) => surface,
|
|
|
|
SketchOrSurface::Sketch(group) => group.on,
|
|
|
|
};
|
|
|
|
|
|
|
|
let half_angle = std::f64::consts::PI / data.num_sides as f64;
|
|
|
|
|
|
|
|
let radius_to_vertices = match data.polygon_type {
|
|
|
|
PolygonType::Inscribed => data.radius,
|
|
|
|
PolygonType::Circumscribed => data.radius / half_angle.cos(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let angle_step = 2.0 * std::f64::consts::PI / data.num_sides as f64;
|
|
|
|
|
|
|
|
let vertices: Vec<[f64; 2]> = (0..data.num_sides)
|
|
|
|
.map(|i| {
|
|
|
|
let angle = angle_step * i as f64;
|
|
|
|
[
|
|
|
|
data.center[0] + radius_to_vertices * angle.cos(),
|
|
|
|
data.center[1] + radius_to_vertices * angle.sin(),
|
|
|
|
]
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let mut sketch =
|
|
|
|
crate::std::sketch::inner_start_profile_at(vertices[0], sketch_surface, None, exec_state, args.clone()).await?;
|
|
|
|
|
|
|
|
// Draw all the lines with unique IDs and modified tags
|
|
|
|
for vertex in vertices.iter().skip(1) {
|
|
|
|
let from = sketch.current_pen_position()?;
|
2024-12-17 09:38:32 +13:00
|
|
|
let id = exec_state.next_uuid();
|
2024-10-28 20:52:51 -04:00
|
|
|
|
|
|
|
args.batch_modeling_cmd(
|
|
|
|
id,
|
|
|
|
ModelingCmd::from(mcmd::ExtendPath {
|
|
|
|
path: sketch.id.into(),
|
|
|
|
segment: PathSegment::Line {
|
|
|
|
end: KPoint2d::from(*vertex).with_z(0.0).map(LengthUnit),
|
|
|
|
relative: false,
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
let current_path = Path::ToPoint {
|
|
|
|
base: BasePath {
|
|
|
|
from: from.into(),
|
|
|
|
to: *vertex,
|
|
|
|
tag: tag.clone(),
|
2025-02-20 10:12:37 +13:00
|
|
|
units: sketch.units,
|
2024-10-28 20:52:51 -04:00
|
|
|
geo_meta: GeoMeta {
|
|
|
|
id,
|
|
|
|
metadata: args.source_range.into(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(tag) = &tag {
|
2025-03-17 12:28:51 +13:00
|
|
|
sketch.add_tag(tag, ¤t_path, exec_state);
|
2024-10-28 20:52:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
sketch.paths.push(current_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close the polygon by connecting back to the first vertex with a new ID
|
|
|
|
let from = sketch.current_pen_position()?;
|
2024-12-17 09:38:32 +13:00
|
|
|
let close_id = exec_state.next_uuid();
|
2024-10-28 20:52:51 -04:00
|
|
|
|
|
|
|
args.batch_modeling_cmd(
|
|
|
|
close_id,
|
|
|
|
ModelingCmd::from(mcmd::ExtendPath {
|
|
|
|
path: sketch.id.into(),
|
|
|
|
segment: PathSegment::Line {
|
|
|
|
end: KPoint2d::from(vertices[0]).with_z(0.0).map(LengthUnit),
|
|
|
|
relative: false,
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
let current_path = Path::ToPoint {
|
|
|
|
base: BasePath {
|
|
|
|
from: from.into(),
|
|
|
|
to: vertices[0],
|
|
|
|
tag: tag.clone(),
|
2025-02-20 10:12:37 +13:00
|
|
|
units: sketch.units,
|
2024-10-28 20:52:51 -04:00
|
|
|
geo_meta: GeoMeta {
|
|
|
|
id: close_id,
|
|
|
|
metadata: args.source_range.into(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(tag) = &tag {
|
2025-03-17 12:28:51 +13:00
|
|
|
sketch.add_tag(tag, ¤t_path, exec_state);
|
2024-10-28 20:52:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
sketch.paths.push(current_path);
|
|
|
|
|
|
|
|
args.batch_modeling_cmd(
|
2024-12-17 09:38:32 +13:00
|
|
|
exec_state.next_uuid(),
|
2024-10-28 20:52:51 -04:00
|
|
|
ModelingCmd::from(mcmd::ClosePath { path_id: sketch.id }),
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(sketch)
|
|
|
|
}
|