messing around with arc and bezier (#363)
updates fixes updates add another test updates updates updates updates updates updates add test for error; updates updates fixups updates updates fixes updates fixes updates fixes updates updates updates bump Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
1993
docs/kcl.json
1993
docs/kcl.json
File diff suppressed because it is too large
Load Diff
350
docs/kcl.md
350
docs/kcl.md
@ -33,6 +33,8 @@
|
|||||||
* [`angledLineThatIntersects`](#angledLineThatIntersects)
|
* [`angledLineThatIntersects`](#angledLineThatIntersects)
|
||||||
* [`startSketchAt`](#startSketchAt)
|
* [`startSketchAt`](#startSketchAt)
|
||||||
* [`close`](#close)
|
* [`close`](#close)
|
||||||
|
* [`arc`](#arc)
|
||||||
|
* [`bezierCurve`](#bezierCurve)
|
||||||
|
|
||||||
|
|
||||||
## Functions
|
## Functions
|
||||||
@ -3046,3 +3048,351 @@ close(sketch_group: SketchGroup) -> SketchGroup
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### arc
|
||||||
|
|
||||||
|
Draw an arc.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
arc(data: ArcData, sketch_group: SketchGroup) -> SketchGroup
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Arguments
|
||||||
|
|
||||||
|
* `data`: `ArcData` - Data to draw an arc.
|
||||||
|
```
|
||||||
|
{
|
||||||
|
// The end angle.
|
||||||
|
"angle_end": number,
|
||||||
|
// The start angle.
|
||||||
|
"angle_start": number,
|
||||||
|
// The radius.
|
||||||
|
"radius": number,
|
||||||
|
// The tag.
|
||||||
|
"tag": string,
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The end angle.
|
||||||
|
"angle_end": number,
|
||||||
|
// The start angle.
|
||||||
|
"angle_start": number,
|
||||||
|
// The radius.
|
||||||
|
"radius": number,
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The center.
|
||||||
|
"center": [number],
|
||||||
|
// The radius.
|
||||||
|
"radius": number,
|
||||||
|
// The tag.
|
||||||
|
"tag": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The center.
|
||||||
|
"center": [number],
|
||||||
|
// The radius.
|
||||||
|
"radius": number,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths.
|
||||||
|
```
|
||||||
|
{
|
||||||
|
// The id of the sketch group.
|
||||||
|
"id": uuid,
|
||||||
|
// The position of the sketch group.
|
||||||
|
"position": [number],
|
||||||
|
// The rotation of the sketch group.
|
||||||
|
"rotation": [number],
|
||||||
|
// The starting path.
|
||||||
|
"start": {
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
},
|
||||||
|
// The paths in the sketch group.
|
||||||
|
"value": [{
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
"type": string,
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
"type": string,
|
||||||
|
// The x coordinate.
|
||||||
|
"x": number,
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
"type": string,
|
||||||
|
// The x coordinate.
|
||||||
|
"x": number,
|
||||||
|
// The y coordinate.
|
||||||
|
"y": number,
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
"type": string,
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
* `SketchGroup` - A sketch group is a collection of paths.
|
||||||
|
```
|
||||||
|
{
|
||||||
|
// The id of the sketch group.
|
||||||
|
"id": uuid,
|
||||||
|
// The position of the sketch group.
|
||||||
|
"position": [number],
|
||||||
|
// The rotation of the sketch group.
|
||||||
|
"rotation": [number],
|
||||||
|
// The starting path.
|
||||||
|
"start": {
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
},
|
||||||
|
// The paths in the sketch group.
|
||||||
|
"value": [{
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
"type": string,
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
"type": string,
|
||||||
|
// The x coordinate.
|
||||||
|
"x": number,
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
"type": string,
|
||||||
|
// The x coordinate.
|
||||||
|
"x": number,
|
||||||
|
// The y coordinate.
|
||||||
|
"y": number,
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
"type": string,
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### bezierCurve
|
||||||
|
|
||||||
|
Draw a bezier curve.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
bezierCurve(data: BezierData, sketch_group: SketchGroup) -> SketchGroup
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Arguments
|
||||||
|
|
||||||
|
* `data`: `BezierData` - Data to draw a bezier curve.
|
||||||
|
```
|
||||||
|
{
|
||||||
|
// The first control point.
|
||||||
|
"control1": [number],
|
||||||
|
// The second control point.
|
||||||
|
"control2": [number],
|
||||||
|
// The tag.
|
||||||
|
"tag": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The first control point.
|
||||||
|
"control1": [number],
|
||||||
|
// The second control point.
|
||||||
|
"control2": [number],
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths.
|
||||||
|
```
|
||||||
|
{
|
||||||
|
// The id of the sketch group.
|
||||||
|
"id": uuid,
|
||||||
|
// The position of the sketch group.
|
||||||
|
"position": [number],
|
||||||
|
// The rotation of the sketch group.
|
||||||
|
"rotation": [number],
|
||||||
|
// The starting path.
|
||||||
|
"start": {
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
},
|
||||||
|
// The paths in the sketch group.
|
||||||
|
"value": [{
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
"type": string,
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
"type": string,
|
||||||
|
// The x coordinate.
|
||||||
|
"x": number,
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
"type": string,
|
||||||
|
// The x coordinate.
|
||||||
|
"x": number,
|
||||||
|
// The y coordinate.
|
||||||
|
"y": number,
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
"type": string,
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
* `SketchGroup` - A sketch group is a collection of paths.
|
||||||
|
```
|
||||||
|
{
|
||||||
|
// The id of the sketch group.
|
||||||
|
"id": uuid,
|
||||||
|
// The position of the sketch group.
|
||||||
|
"position": [number],
|
||||||
|
// The rotation of the sketch group.
|
||||||
|
"rotation": [number],
|
||||||
|
// The starting path.
|
||||||
|
"start": {
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
},
|
||||||
|
// The paths in the sketch group.
|
||||||
|
"value": [{
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
"type": string,
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
"type": string,
|
||||||
|
// The x coordinate.
|
||||||
|
"x": number,
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
"type": string,
|
||||||
|
// The x coordinate.
|
||||||
|
"x": number,
|
||||||
|
// The y coordinate.
|
||||||
|
"y": number,
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The from point.
|
||||||
|
"from": [number],
|
||||||
|
// The name of the path.
|
||||||
|
"name": string,
|
||||||
|
// The to point.
|
||||||
|
"to": [number],
|
||||||
|
"type": string,
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,13 +57,13 @@
|
|||||||
"build:both:local": "yarn build:wasm && vite build",
|
"build:both:local": "yarn build:wasm && vite build",
|
||||||
"test": "vitest --mode development",
|
"test": "vitest --mode development",
|
||||||
"test:nowatch": "vitest run --mode development",
|
"test:nowatch": "vitest run --mode development",
|
||||||
"test:rust": "(cd src/wasm-lib && cargo test && cargo clippy)",
|
"test:rust": "(cd src/wasm-lib && cargo test --all && cargo clippy --all --tests)",
|
||||||
"test:cov": "vitest run --coverage --mode development",
|
"test:cov": "vitest run --coverage --mode development",
|
||||||
"simpleserver:ci": "http-server ./public --cors -p 3000 &",
|
"simpleserver:ci": "http-server ./public --cors -p 3000 &",
|
||||||
"simpleserver": "http-server ./public --cors -p 3000",
|
"simpleserver": "http-server ./public --cors -p 3000",
|
||||||
"fmt": "prettier --write ./src",
|
"fmt": "prettier --write ./src",
|
||||||
"fmt-check": "prettier --check ./src",
|
"fmt-check": "prettier --check ./src",
|
||||||
"build:wasm": "yarn wasm-prep && (cd src/wasm-lib && wasm-pack build --target web --out-dir pkg && cargo test --all) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt && yarn remove-importmeta",
|
"build:wasm": "yarn wasm-prep && (cd src/wasm-lib && wasm-pack build --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt && yarn remove-importmeta",
|
||||||
"remove-importmeta": "sed -i 's/import.meta.url//g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url//g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
"remove-importmeta": "sed -i 's/import.meta.url//g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url//g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
||||||
"wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings",
|
"wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings",
|
||||||
"lint": "eslint --fix src",
|
"lint": "eslint --fix src",
|
||||||
|
2
src/wasm-lib/Cargo.lock
generated
2
src/wasm-lib/Cargo.lock
generated
@ -948,7 +948,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
version = "0.1.3"
|
version = "0.1.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bson",
|
"bson",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
description = "KittyCAD Language"
|
description = "KittyCAD Language"
|
||||||
version = "0.1.3"
|
version = "0.1.10"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
@ -33,6 +33,10 @@ reqwest = { version = "0.11.20", default-features = false }
|
|||||||
tokio = { version = "1.32.0", features = ["full"] }
|
tokio = { version = "1.32.0", features = ["full"] }
|
||||||
tokio-tungstenite = { version = "0.20.0", features = ["rustls-tls-native-roots"] }
|
tokio-tungstenite = { version = "0.20.0", features = ["rustls-tls-native-roots"] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["engine"]
|
||||||
|
engine = []
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
debug = true
|
debug = true
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use parse_display::{Display, FromStr};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Map;
|
use serde_json::Map;
|
||||||
@ -383,11 +384,22 @@ pub struct VariableDeclaration {
|
|||||||
pub start: usize,
|
pub start: usize,
|
||||||
pub end: usize,
|
pub end: usize,
|
||||||
pub declarations: Vec<VariableDeclarator>,
|
pub declarations: Vec<VariableDeclarator>,
|
||||||
pub kind: String, // Change to enum if there are specific values
|
pub kind: VariableKind, // Change to enum if there are specific values
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_value_meta!(VariableDeclaration);
|
impl_value_meta!(VariableDeclaration);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, FromStr, Display)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[display(style = "snake_case")]
|
||||||
|
pub enum VariableKind {
|
||||||
|
Let,
|
||||||
|
Const,
|
||||||
|
Fn,
|
||||||
|
Var,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
|
@ -44,6 +44,11 @@ impl StdLibFnArg {
|
|||||||
get_type_string_from_schema(&self.schema)
|
get_type_string_from_schema(&self.schema)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn get_autocomplete_string(&self) -> Result<String> {
|
||||||
|
get_autocomplete_string_from_schema(&self.schema)
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn description(&self) -> Option<String> {
|
pub fn description(&self) -> Option<String> {
|
||||||
get_description_string_from_schema(&self.schema)
|
get_description_string_from_schema(&self.schema)
|
||||||
@ -93,9 +98,24 @@ pub trait StdLibFn {
|
|||||||
deprecated: self.deprecated(),
|
deprecated: self.deprecated(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fn_signature(&self) -> String {
|
||||||
|
let mut signature = String::new();
|
||||||
|
signature.push_str(&format!("{}(", self.name()));
|
||||||
|
for (i, arg) in self.args().iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
signature.push_str(", ");
|
||||||
|
}
|
||||||
|
signature.push_str(&format!("{}: {}", arg.name, arg.type_));
|
||||||
|
}
|
||||||
|
signature.push_str(") -> ");
|
||||||
|
signature.push_str(&self.return_value().type_);
|
||||||
|
|
||||||
|
signature
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_description_string_from_schema(schema: &schemars::schema::Schema) -> Option<String> {
|
pub fn get_description_string_from_schema(schema: &schemars::schema::Schema) -> Option<String> {
|
||||||
if let schemars::schema::Schema::Object(o) = schema {
|
if let schemars::schema::Schema::Object(o) = schema {
|
||||||
if let Some(metadata) = &o.metadata {
|
if let Some(metadata) = &o.metadata {
|
||||||
if let Some(description) = &metadata.description {
|
if let Some(description) = &metadata.description {
|
||||||
@ -107,7 +127,7 @@ fn get_description_string_from_schema(schema: &schemars::schema::Schema) -> Opti
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_type_string_from_schema(schema: &schemars::schema::Schema) -> Result<(String, bool)> {
|
pub fn get_type_string_from_schema(schema: &schemars::schema::Schema) -> Result<(String, bool)> {
|
||||||
match schema {
|
match schema {
|
||||||
schemars::schema::Schema::Object(o) => {
|
schemars::schema::Schema::Object(o) => {
|
||||||
if let Some(format) = &o.format {
|
if let Some(format) = &o.format {
|
||||||
@ -187,3 +207,78 @@ fn get_type_string_from_schema(schema: &schemars::schema::Schema) -> Result<(Str
|
|||||||
schemars::schema::Schema::Bool(_) => Ok((Primitive::Bool.to_string(), false)),
|
schemars::schema::Schema::Bool(_) => Ok((Primitive::Bool.to_string(), false)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_autocomplete_string_from_schema(schema: &schemars::schema::Schema) -> Result<String> {
|
||||||
|
match schema {
|
||||||
|
schemars::schema::Schema::Object(o) => {
|
||||||
|
if let Some(format) = &o.format {
|
||||||
|
if format == "uuid" {
|
||||||
|
return Ok(Primitive::Uuid.to_string());
|
||||||
|
} else if format == "double" || format == "uint" {
|
||||||
|
return Ok(Primitive::Number.to_string());
|
||||||
|
} else {
|
||||||
|
anyhow::bail!("unknown format: {}", format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(obj_val) = &o.object {
|
||||||
|
let mut fn_docs = String::new();
|
||||||
|
fn_docs.push_str("{\n");
|
||||||
|
// Let's print out the object's properties.
|
||||||
|
for (prop_name, prop) in obj_val.properties.iter() {
|
||||||
|
if prop_name.starts_with('_') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(description) = get_description_string_from_schema(prop) {
|
||||||
|
fn_docs.push_str(&format!("\t// {}\n", description));
|
||||||
|
}
|
||||||
|
fn_docs.push_str(&format!(
|
||||||
|
"\t\"{}\": {},\n",
|
||||||
|
prop_name,
|
||||||
|
get_autocomplete_string_from_schema(prop)?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn_docs.push('}');
|
||||||
|
|
||||||
|
return Ok(fn_docs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(array_val) = &o.array {
|
||||||
|
if let Some(schemars::schema::SingleOrVec::Single(items)) = &array_val.items {
|
||||||
|
// Let's print out the object's properties.
|
||||||
|
return Ok(format!("[{}]", get_autocomplete_string_from_schema(items)?));
|
||||||
|
} else if let Some(items) = &array_val.contains {
|
||||||
|
return Ok(format!("[{}]", get_autocomplete_string_from_schema(items)?));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(subschemas) = &o.subschemas {
|
||||||
|
let mut fn_docs = String::new();
|
||||||
|
if let Some(items) = &subschemas.one_of {
|
||||||
|
if let Some(item) = items.iter().next() {
|
||||||
|
// Let's print out the object's properties.
|
||||||
|
fn_docs.push_str(&get_autocomplete_string_from_schema(item)?);
|
||||||
|
}
|
||||||
|
} else if let Some(items) = &subschemas.any_of {
|
||||||
|
if let Some(item) = items.iter().next() {
|
||||||
|
// Let's print out the object's properties.
|
||||||
|
fn_docs.push_str(&get_autocomplete_string_from_schema(item)?);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
anyhow::bail!("unknown subschemas: {:#?}", subschemas);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(fn_docs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(schemars::schema::SingleOrVec::Single(_string)) = &o.instance_type {
|
||||||
|
return Ok(Primitive::String.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
anyhow::bail!("unknown type: {:#?}", o)
|
||||||
|
}
|
||||||
|
schemars::schema::Schema::Bool(_) => Ok(Primitive::Bool.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,16 +4,20 @@ use wasm_bindgen::prelude::*;
|
|||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
|
#[cfg(feature = "engine")]
|
||||||
pub mod conn;
|
pub mod conn;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
|
#[cfg(feature = "engine")]
|
||||||
pub use conn::EngineConnection;
|
pub use conn::EngineConnection;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
|
#[cfg(feature = "engine")]
|
||||||
pub mod conn_wasm;
|
pub mod conn_wasm;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
|
#[cfg(feature = "engine")]
|
||||||
pub use conn_wasm::EngineConnection;
|
pub use conn_wasm::EngineConnection;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -21,6 +25,13 @@ pub mod conn_mock;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub use conn_mock::EngineConnection;
|
pub use conn_mock::EngineConnection;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "engine"))]
|
||||||
|
#[cfg(not(test))]
|
||||||
|
pub mod conn_mock;
|
||||||
|
#[cfg(not(feature = "engine"))]
|
||||||
|
#[cfg(not(test))]
|
||||||
|
pub use conn_mock::EngineConnection;
|
||||||
|
|
||||||
use crate::executor::SourceRange;
|
use crate::executor::SourceRange;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -33,6 +44,7 @@ pub struct EngineManager {
|
|||||||
impl EngineManager {
|
impl EngineManager {
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
|
#[cfg(feature = "engine")]
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
pub async fn new(manager: conn_wasm::EngineCommandManager) -> EngineManager {
|
pub async fn new(manager: conn_wasm::EngineCommandManager) -> EngineManager {
|
||||||
EngineManager {
|
EngineManager {
|
||||||
|
@ -298,12 +298,24 @@ impl From<[f64; 2]> for Point2d {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&[f64; 2]> for Point2d {
|
||||||
|
fn from(p: &[f64; 2]) -> Self {
|
||||||
|
Self { x: p[0], y: p[1] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Point2d> for [f64; 2] {
|
impl From<Point2d> for [f64; 2] {
|
||||||
fn from(p: Point2d) -> Self {
|
fn from(p: Point2d) -> Self {
|
||||||
[p.x, p.y]
|
[p.x, p.y]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Point2d> for kittycad::types::Point2D {
|
||||||
|
fn from(p: Point2d) -> Self {
|
||||||
|
Self { x: p.x, y: p.y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct Point3d {
|
pub struct Point3d {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
pub mod abstract_syntax_tree_types;
|
pub mod abstract_syntax_tree_types;
|
||||||
mod docs;
|
pub mod docs;
|
||||||
pub mod engine;
|
pub mod engine;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod executor;
|
pub mod executor;
|
||||||
|
@ -315,23 +315,25 @@ fn build_tree(
|
|||||||
})));
|
})));
|
||||||
return build_tree(&reverse_polish_notation_tokens[1..], new_stack);
|
return build_tree(&reverse_polish_notation_tokens[1..], new_stack);
|
||||||
} else if current_token.token_type == TokenType::Word {
|
} else if current_token.token_type == TokenType::Word {
|
||||||
if reverse_polish_notation_tokens[1].token_type == TokenType::Brace
|
if reverse_polish_notation_tokens.len() > 1 {
|
||||||
&& reverse_polish_notation_tokens[1].value == "("
|
if reverse_polish_notation_tokens[1].token_type == TokenType::Brace
|
||||||
{
|
&& reverse_polish_notation_tokens[1].value == "("
|
||||||
let closing_brace = find_closing_brace(reverse_polish_notation_tokens, 1, 0, "")?;
|
{
|
||||||
|
let closing_brace = find_closing_brace(reverse_polish_notation_tokens, 1, 0, "")?;
|
||||||
|
let mut new_stack = stack;
|
||||||
|
new_stack.push(MathExpression::CallExpression(Box::new(
|
||||||
|
make_call_expression(reverse_polish_notation_tokens, 0)?.expression,
|
||||||
|
)));
|
||||||
|
return build_tree(&reverse_polish_notation_tokens[closing_brace + 1..], new_stack);
|
||||||
|
}
|
||||||
let mut new_stack = stack;
|
let mut new_stack = stack;
|
||||||
new_stack.push(MathExpression::CallExpression(Box::new(
|
new_stack.push(MathExpression::Identifier(Box::new(Identifier {
|
||||||
make_call_expression(reverse_polish_notation_tokens, 0)?.expression,
|
name: current_token.value.clone(),
|
||||||
)));
|
start: current_token.start,
|
||||||
return build_tree(&reverse_polish_notation_tokens[closing_brace + 1..], new_stack);
|
end: current_token.end,
|
||||||
|
})));
|
||||||
|
return build_tree(&reverse_polish_notation_tokens[1..], new_stack);
|
||||||
}
|
}
|
||||||
let mut new_stack = stack;
|
|
||||||
new_stack.push(MathExpression::Identifier(Box::new(Identifier {
|
|
||||||
name: current_token.value.clone(),
|
|
||||||
start: current_token.start,
|
|
||||||
end: current_token.end,
|
|
||||||
})));
|
|
||||||
return build_tree(&reverse_polish_notation_tokens[1..], new_stack);
|
|
||||||
} else if current_token.token_type == TokenType::Brace && current_token.value == "(" {
|
} else if current_token.token_type == TokenType::Brace && current_token.value == "(" {
|
||||||
let mut new_stack = stack;
|
let mut new_stack = stack;
|
||||||
new_stack.push(MathExpression::ParenthesisToken(Box::new(ParenthesisToken {
|
new_stack.push(MathExpression::ParenthesisToken(Box::new(ParenthesisToken {
|
||||||
@ -424,6 +426,14 @@ fn build_tree(
|
|||||||
new_stack.push(expression);
|
new_stack.push(expression);
|
||||||
return build_tree(&reverse_polish_notation_tokens[1..], new_stack);
|
return build_tree(&reverse_polish_notation_tokens[1..], new_stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if stack.len() < 2 {
|
||||||
|
return Err(KclError::Syntax(KclErrorDetails {
|
||||||
|
source_ranges: vec![current_token.into()],
|
||||||
|
message: "unexpected end of expression".to_string(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
let left: (BinaryPart, usize) = match &stack[stack.len() - 2] {
|
let left: (BinaryPart, usize) = match &stack[stack.len() - 2] {
|
||||||
MathExpression::ExtendedBinaryExpression(bin_exp) => (
|
MathExpression::ExtendedBinaryExpression(bin_exp) => (
|
||||||
BinaryPart::BinaryExpression(Box::new(BinaryExpression {
|
BinaryPart::BinaryExpression(Box::new(BinaryExpression {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, str::FromStr};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
abstract_syntax_tree_types::{
|
abstract_syntax_tree_types::{
|
||||||
ArrayExpression, BinaryExpression, BinaryPart, BodyItem, CallExpression, ExpressionStatement,
|
ArrayExpression, BinaryExpression, BinaryPart, BodyItem, CallExpression, ExpressionStatement,
|
||||||
FunctionExpression, Identifier, Literal, LiteralIdentifier, MemberExpression, MemberObject, NoneCodeMeta,
|
FunctionExpression, Identifier, Literal, LiteralIdentifier, MemberExpression, MemberObject, NoneCodeMeta,
|
||||||
NoneCodeNode, ObjectExpression, ObjectKeyInfo, ObjectProperty, PipeExpression, PipeSubstitution, Program,
|
NoneCodeNode, ObjectExpression, ObjectKeyInfo, ObjectProperty, PipeExpression, PipeSubstitution, Program,
|
||||||
ReturnStatement, UnaryExpression, Value, VariableDeclaration, VariableDeclarator,
|
ReturnStatement, UnaryExpression, Value, VariableDeclaration, VariableDeclarator, VariableKind,
|
||||||
},
|
},
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
math_parser::parse_expression,
|
math_parser::parse_expression,
|
||||||
@ -145,7 +145,12 @@ pub fn find_closing_brace(
|
|||||||
search_opening_brace: &str,
|
search_opening_brace: &str,
|
||||||
) -> Result<usize, KclError> {
|
) -> Result<usize, KclError> {
|
||||||
let closing_brace_map: HashMap<&str, &str> = [("(", ")"), ("{", "}"), ("[", "]")].iter().cloned().collect();
|
let closing_brace_map: HashMap<&str, &str> = [("(", ")"), ("{", "}"), ("[", "]")].iter().cloned().collect();
|
||||||
let current_token = &tokens[index];
|
let Some(current_token) = tokens.get(index) else {
|
||||||
|
return Err(KclError::Syntax(KclErrorDetails {
|
||||||
|
source_ranges: vec![tokens.last().unwrap().into()],
|
||||||
|
message: "unexpected end".to_string(),
|
||||||
|
}));
|
||||||
|
};
|
||||||
let mut search_opening_brace = search_opening_brace;
|
let mut search_opening_brace = search_opening_brace;
|
||||||
let is_first_call = search_opening_brace.is_empty() && brace_count == 0;
|
let is_first_call = search_opening_brace.is_empty() && brace_count == 0;
|
||||||
if is_first_call {
|
if is_first_call {
|
||||||
@ -966,13 +971,12 @@ fn make_variable_declaration(tokens: &[Token], index: usize) -> Result<VariableD
|
|||||||
declaration: VariableDeclaration {
|
declaration: VariableDeclaration {
|
||||||
start: current_token.start,
|
start: current_token.start,
|
||||||
end: variable_declarators_result.declarations[variable_declarators_result.declarations.len() - 1].end,
|
end: variable_declarators_result.declarations[variable_declarators_result.declarations.len() - 1].end,
|
||||||
kind: if current_token.value == "const" {
|
kind: VariableKind::from_str(¤t_token.value).map_err(|_| {
|
||||||
"const".to_string()
|
KclError::Syntax(KclErrorDetails {
|
||||||
} else if current_token.value == "fn" {
|
source_ranges: vec![current_token.into()],
|
||||||
"fn".to_string()
|
message: "Unexpected token".to_string(),
|
||||||
} else {
|
})
|
||||||
"unkown".to_string()
|
})?,
|
||||||
},
|
|
||||||
declarations: variable_declarators_result.declarations,
|
declarations: variable_declarators_result.declarations,
|
||||||
},
|
},
|
||||||
last_index: variable_declarators_result.last_index,
|
last_index: variable_declarators_result.last_index,
|
||||||
@ -2479,7 +2483,7 @@ show(mySk1)"#;
|
|||||||
|> close(%)"#,
|
|> close(%)"#,
|
||||||
);
|
);
|
||||||
let result = make_variable_declaration(&tokens, 0).unwrap();
|
let result = make_variable_declaration(&tokens, 0).unwrap();
|
||||||
assert_eq!(result.declaration.kind, "const");
|
assert_eq!(result.declaration.kind.to_string(), "const");
|
||||||
assert_eq!(result.declaration.declarations.len(), 1);
|
assert_eq!(result.declaration.declarations.len(), 1);
|
||||||
assert_eq!(result.declaration.declarations[0].id.name, "yo");
|
assert_eq!(result.declaration.declarations[0].id.name, "yo");
|
||||||
let declaration = result.declaration.declarations[0].clone();
|
let declaration = result.declaration.declarations[0].clone();
|
||||||
|
@ -27,8 +27,7 @@ pub type FnMap = HashMap<String, StdFn>;
|
|||||||
pub type StdFn = fn(&mut Args) -> Result<MemoryItem, KclError>;
|
pub type StdFn = fn(&mut Args) -> Result<MemoryItem, KclError>;
|
||||||
|
|
||||||
pub struct StdLib {
|
pub struct StdLib {
|
||||||
#[allow(dead_code)]
|
pub internal_fn_names: Vec<Box<(dyn crate::docs::StdLibFn)>>,
|
||||||
internal_fn_names: Vec<Box<(dyn crate::docs::StdLibFn)>>,
|
|
||||||
|
|
||||||
pub fns: FnMap,
|
pub fns: FnMap,
|
||||||
}
|
}
|
||||||
@ -64,6 +63,8 @@ impl StdLib {
|
|||||||
Box::new(crate::std::sketch::AngledLineThatIntersects),
|
Box::new(crate::std::sketch::AngledLineThatIntersects),
|
||||||
Box::new(crate::std::sketch::StartSketchAt),
|
Box::new(crate::std::sketch::StartSketchAt),
|
||||||
Box::new(crate::std::sketch::Close),
|
Box::new(crate::std::sketch::Close),
|
||||||
|
Box::new(crate::std::sketch::Arc),
|
||||||
|
Box::new(crate::std::sketch::BezierCurve),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut fns = HashMap::new();
|
let mut fns = HashMap::new();
|
||||||
@ -536,15 +537,8 @@ mod tests {
|
|||||||
fn_docs.push_str(&format!("{}\n\n", internal_fn.description()));
|
fn_docs.push_str(&format!("{}\n\n", internal_fn.description()));
|
||||||
|
|
||||||
fn_docs.push_str("```\n");
|
fn_docs.push_str("```\n");
|
||||||
fn_docs.push_str(&format!("{}(", internal_fn.name()));
|
let signature = internal_fn.fn_signature();
|
||||||
for (i, arg) in internal_fn.args().iter().enumerate() {
|
fn_docs.push_str(&signature);
|
||||||
if i > 0 {
|
|
||||||
fn_docs.push_str(", ");
|
|
||||||
}
|
|
||||||
fn_docs.push_str(&format!("{}: {}", arg.name, arg.type_));
|
|
||||||
}
|
|
||||||
fn_docs.push_str(") -> ");
|
|
||||||
fn_docs.push_str(&internal_fn.return_value().type_);
|
|
||||||
fn_docs.push_str("\n```\n\n");
|
fn_docs.push_str("\n```\n\n");
|
||||||
|
|
||||||
fn_docs.push_str("#### Arguments\n\n");
|
fn_docs.push_str("#### Arguments\n\n");
|
||||||
|
@ -10,7 +10,7 @@ use crate::{
|
|||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::{BasePath, GeoMeta, MemoryItem, Path, Point2d, Position, Rotation, SketchGroup},
|
executor::{BasePath, GeoMeta, MemoryItem, Path, Point2d, Position, Rotation, SketchGroup},
|
||||||
std::{
|
std::{
|
||||||
utils::{get_x_component, get_y_component, intersection_with_parallel_line},
|
utils::{arc_angles, arc_center_and_end, get_x_component, get_y_component, intersection_with_parallel_line},
|
||||||
Args,
|
Args,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -43,7 +43,7 @@ pub fn line_to(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "lineTo",
|
name = "lineTo",
|
||||||
}]
|
}]
|
||||||
fn inner_line_to(data: LineToData, sketch_group: SketchGroup, args: &Args) -> Result<SketchGroup, KclError> {
|
fn inner_line_to(data: LineToData, sketch_group: SketchGroup, args: &mut Args) -> Result<SketchGroup, KclError> {
|
||||||
let from = sketch_group.get_coords_from_paths()?;
|
let from = sketch_group.get_coords_from_paths()?;
|
||||||
let to = match data {
|
let to = match data {
|
||||||
LineToData::PointWithTag { to, .. } => to,
|
LineToData::PointWithTag { to, .. } => to,
|
||||||
@ -51,6 +51,21 @@ fn inner_line_to(data: LineToData, sketch_group: SketchGroup, args: &Args) -> Re
|
|||||||
};
|
};
|
||||||
|
|
||||||
let id = uuid::Uuid::new_v4();
|
let id = uuid::Uuid::new_v4();
|
||||||
|
|
||||||
|
args.send_modeling_cmd(
|
||||||
|
id,
|
||||||
|
ModelingCmd::ExtendPath {
|
||||||
|
path: sketch_group.id,
|
||||||
|
segment: kittycad::types::PathSegment::Line {
|
||||||
|
end: Point3D {
|
||||||
|
x: to[0],
|
||||||
|
y: to[1],
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
let current_path = Path::ToPoint {
|
let current_path = Path::ToPoint {
|
||||||
base: BasePath {
|
base: BasePath {
|
||||||
from: from.into(),
|
from: from.into(),
|
||||||
@ -101,7 +116,7 @@ pub fn x_line_to(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "xLineTo",
|
name = "xLineTo",
|
||||||
}]
|
}]
|
||||||
fn inner_x_line_to(data: AxisLineToData, sketch_group: SketchGroup, args: &Args) -> Result<SketchGroup, KclError> {
|
fn inner_x_line_to(data: AxisLineToData, sketch_group: SketchGroup, args: &mut Args) -> Result<SketchGroup, KclError> {
|
||||||
let from = sketch_group.get_coords_from_paths()?;
|
let from = sketch_group.get_coords_from_paths()?;
|
||||||
|
|
||||||
let line_to_data = match data {
|
let line_to_data = match data {
|
||||||
@ -126,7 +141,7 @@ pub fn y_line_to(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "yLineTo",
|
name = "yLineTo",
|
||||||
}]
|
}]
|
||||||
fn inner_y_line_to(data: AxisLineToData, sketch_group: SketchGroup, args: &Args) -> Result<SketchGroup, KclError> {
|
fn inner_y_line_to(data: AxisLineToData, sketch_group: SketchGroup, args: &mut Args) -> Result<SketchGroup, KclError> {
|
||||||
let from = sketch_group.get_coords_from_paths()?;
|
let from = sketch_group.get_coords_from_paths()?;
|
||||||
|
|
||||||
let line_to_data = match data {
|
let line_to_data = match data {
|
||||||
@ -716,6 +731,248 @@ fn inner_close(sketch_group: SketchGroup, args: &mut Args) -> Result<SketchGroup
|
|||||||
Ok(new_sketch_group)
|
Ok(new_sketch_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Data to draw an arc.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[serde(rename_all = "camelCase", untagged)]
|
||||||
|
pub enum ArcData {
|
||||||
|
/// Angles and radius with a tag.
|
||||||
|
AnglesAndRadiusWithTag {
|
||||||
|
/// The start angle.
|
||||||
|
angle_start: f64,
|
||||||
|
/// The end angle.
|
||||||
|
angle_end: f64,
|
||||||
|
/// The radius.
|
||||||
|
radius: f64,
|
||||||
|
/// The tag.
|
||||||
|
tag: String,
|
||||||
|
},
|
||||||
|
/// Angles and radius.
|
||||||
|
AnglesAndRadius {
|
||||||
|
/// The start angle.
|
||||||
|
angle_start: f64,
|
||||||
|
/// The end angle.
|
||||||
|
angle_end: f64,
|
||||||
|
/// The radius.
|
||||||
|
radius: f64,
|
||||||
|
},
|
||||||
|
/// Center, to and radius with a tag.
|
||||||
|
CenterToRadiusWithTag {
|
||||||
|
/// The center.
|
||||||
|
center: [f64; 2],
|
||||||
|
/// The to point.
|
||||||
|
to: [f64; 2],
|
||||||
|
/// The radius.
|
||||||
|
radius: f64,
|
||||||
|
/// The tag.
|
||||||
|
tag: String,
|
||||||
|
},
|
||||||
|
/// Center, to and radius.
|
||||||
|
CenterToRadius {
|
||||||
|
/// The center.
|
||||||
|
center: [f64; 2],
|
||||||
|
/// The to point.
|
||||||
|
to: [f64; 2],
|
||||||
|
/// The radius.
|
||||||
|
radius: f64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw an arc.
|
||||||
|
pub fn arc(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
|
let (data, sketch_group): (ArcData, SketchGroup) = args.get_data_and_sketch_group()?;
|
||||||
|
|
||||||
|
let new_sketch_group = inner_arc(data, sketch_group, args)?;
|
||||||
|
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw an arc.
|
||||||
|
#[stdlib {
|
||||||
|
name = "arc",
|
||||||
|
}]
|
||||||
|
fn inner_arc(data: ArcData, sketch_group: SketchGroup, args: &mut Args) -> Result<SketchGroup, KclError> {
|
||||||
|
let from = sketch_group.get_coords_from_paths()?;
|
||||||
|
|
||||||
|
let (center, angle_start, angle_end, radius, end) = match &data {
|
||||||
|
ArcData::AnglesAndRadiusWithTag {
|
||||||
|
angle_start,
|
||||||
|
angle_end,
|
||||||
|
radius,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let (center, end) = arc_center_and_end(&from, *angle_start, *angle_end, *radius);
|
||||||
|
(center, *angle_start, *angle_end, *radius, end)
|
||||||
|
}
|
||||||
|
ArcData::AnglesAndRadius {
|
||||||
|
angle_start,
|
||||||
|
angle_end,
|
||||||
|
radius,
|
||||||
|
} => {
|
||||||
|
let (center, end) = arc_center_and_end(&from, *angle_start, *angle_end, *radius);
|
||||||
|
(center, *angle_start, *angle_end, *radius, end)
|
||||||
|
}
|
||||||
|
ArcData::CenterToRadiusWithTag { center, to, radius, .. } => {
|
||||||
|
let (angle_start, angle_end) = arc_angles(&from, ¢er.into(), &to.into(), *radius, args.source_range)?;
|
||||||
|
(center.into(), angle_start, angle_end, *radius, to.into())
|
||||||
|
}
|
||||||
|
ArcData::CenterToRadius { center, to, radius } => {
|
||||||
|
let (angle_start, angle_end) = arc_angles(&from, ¢er.into(), &to.into(), *radius, args.source_range)?;
|
||||||
|
(center.into(), angle_start, angle_end, *radius, to.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = uuid::Uuid::new_v4();
|
||||||
|
|
||||||
|
args.send_modeling_cmd(
|
||||||
|
id,
|
||||||
|
ModelingCmd::ExtendPath {
|
||||||
|
path: sketch_group.id,
|
||||||
|
segment: kittycad::types::PathSegment::Arc {
|
||||||
|
angle_start,
|
||||||
|
angle_end,
|
||||||
|
center: center.into(),
|
||||||
|
radius,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
// Move the path pen to the end of the arc.
|
||||||
|
// Since that is where we want to draw the next path.
|
||||||
|
// TODO: the engine should automatically move the pen to the end of the arc.
|
||||||
|
// This just seems inefficient.
|
||||||
|
args.send_modeling_cmd(
|
||||||
|
id,
|
||||||
|
ModelingCmd::MovePathPen {
|
||||||
|
path: sketch_group.id,
|
||||||
|
to: Point3D {
|
||||||
|
x: end.x,
|
||||||
|
y: end.y,
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let current_path = Path::ToPoint {
|
||||||
|
base: BasePath {
|
||||||
|
from: from.into(),
|
||||||
|
to: end.into(),
|
||||||
|
name: match data {
|
||||||
|
ArcData::AnglesAndRadiusWithTag { tag, .. } => tag.to_string(),
|
||||||
|
ArcData::AnglesAndRadius { .. } => "".to_string(),
|
||||||
|
ArcData::CenterToRadiusWithTag { tag, .. } => tag.to_string(),
|
||||||
|
ArcData::CenterToRadius { .. } => "".to_string(),
|
||||||
|
},
|
||||||
|
geo_meta: GeoMeta {
|
||||||
|
id,
|
||||||
|
metadata: args.source_range.into(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut new_sketch_group = sketch_group.clone();
|
||||||
|
new_sketch_group.value.push(current_path);
|
||||||
|
|
||||||
|
Ok(new_sketch_group)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Data to draw a bezier curve.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[serde(rename_all = "camelCase", untagged)]
|
||||||
|
pub enum BezierData {
|
||||||
|
/// Points with a tag.
|
||||||
|
PointsWithTag {
|
||||||
|
/// The to point.
|
||||||
|
to: [f64; 2],
|
||||||
|
/// The first control point.
|
||||||
|
control1: [f64; 2],
|
||||||
|
/// The second control point.
|
||||||
|
control2: [f64; 2],
|
||||||
|
/// The tag.
|
||||||
|
tag: String,
|
||||||
|
},
|
||||||
|
/// Points.
|
||||||
|
Points {
|
||||||
|
/// The to point.
|
||||||
|
to: [f64; 2],
|
||||||
|
/// The first control point.
|
||||||
|
control1: [f64; 2],
|
||||||
|
/// The second control point.
|
||||||
|
control2: [f64; 2],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw a bezier curve.
|
||||||
|
pub fn bezier_curve(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||||
|
let (data, sketch_group): (BezierData, SketchGroup) = args.get_data_and_sketch_group()?;
|
||||||
|
|
||||||
|
let new_sketch_group = inner_bezier_curve(data, sketch_group, args)?;
|
||||||
|
Ok(MemoryItem::SketchGroup(new_sketch_group))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw a bezier curve.
|
||||||
|
#[stdlib {
|
||||||
|
name = "bezierCurve",
|
||||||
|
}]
|
||||||
|
fn inner_bezier_curve(data: BezierData, sketch_group: SketchGroup, args: &mut Args) -> Result<SketchGroup, KclError> {
|
||||||
|
let from = sketch_group.get_coords_from_paths()?;
|
||||||
|
|
||||||
|
let (to, control1, control2) = match &data {
|
||||||
|
BezierData::PointsWithTag {
|
||||||
|
to, control1, control2, ..
|
||||||
|
} => (to, control1, control2),
|
||||||
|
BezierData::Points { to, control1, control2 } => (to, control1, control2),
|
||||||
|
};
|
||||||
|
|
||||||
|
let to = [from.x + to[0], from.y + to[1]];
|
||||||
|
|
||||||
|
let id = uuid::Uuid::new_v4();
|
||||||
|
|
||||||
|
args.send_modeling_cmd(
|
||||||
|
id,
|
||||||
|
ModelingCmd::ExtendPath {
|
||||||
|
path: sketch_group.id,
|
||||||
|
segment: kittycad::types::PathSegment::Bezier {
|
||||||
|
control1: Point3D {
|
||||||
|
x: from.x + control1[0],
|
||||||
|
y: from.y + control1[1],
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
|
control2: Point3D {
|
||||||
|
x: from.x + control2[0],
|
||||||
|
y: from.y + control2[1],
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
|
end: Point3D {
|
||||||
|
x: to[0],
|
||||||
|
y: to[1],
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let current_path = Path::ToPoint {
|
||||||
|
base: BasePath {
|
||||||
|
from: from.into(),
|
||||||
|
to,
|
||||||
|
name: if let BezierData::PointsWithTag { tag, .. } = data {
|
||||||
|
tag.to_string()
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
},
|
||||||
|
geo_meta: GeoMeta {
|
||||||
|
id,
|
||||||
|
metadata: args.source_range.into(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut new_sketch_group = sketch_group.clone();
|
||||||
|
new_sketch_group.value.push(current_path);
|
||||||
|
|
||||||
|
Ok(new_sketch_group)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
use crate::{
|
||||||
|
errors::{KclError, KclErrorDetails},
|
||||||
|
executor::{Point2d, SourceRange},
|
||||||
|
};
|
||||||
|
|
||||||
pub fn get_angle(a: &[f64; 2], b: &[f64; 2]) -> f64 {
|
pub fn get_angle(a: &[f64; 2], b: &[f64; 2]) -> f64 {
|
||||||
let x = b[0] - a[0];
|
let x = b[0] - a[0];
|
||||||
let y = b[1] - a[1];
|
let y = b[1] - a[1];
|
||||||
@ -160,12 +165,80 @@ pub fn get_x_component(angle_degree: f64, y_component: f64) -> [f64; 2] {
|
|||||||
[sign * x_component, sign * y_component]
|
[sign * x_component, sign * y_component]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn arc_center_and_end(from: &Point2d, start_angle_deg: f64, end_angle_deg: f64, radius: f64) -> (Point2d, Point2d) {
|
||||||
|
let start_angle = start_angle_deg * (std::f64::consts::PI / 180.0);
|
||||||
|
let end_angle = end_angle_deg * (std::f64::consts::PI / 180.0);
|
||||||
|
|
||||||
|
let center = Point2d {
|
||||||
|
x: -1.0 * (radius * start_angle.cos() - from.x),
|
||||||
|
y: -1.0 * (radius * start_angle.sin() - from.y),
|
||||||
|
};
|
||||||
|
|
||||||
|
let end = Point2d {
|
||||||
|
x: center.x + radius * end_angle.cos(),
|
||||||
|
y: center.y + radius * end_angle.sin(),
|
||||||
|
};
|
||||||
|
|
||||||
|
(center, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn arc_angles(
|
||||||
|
from: &Point2d,
|
||||||
|
to: &Point2d,
|
||||||
|
center: &Point2d,
|
||||||
|
radius: f64,
|
||||||
|
source_range: SourceRange,
|
||||||
|
) -> Result<(f64, f64), KclError> {
|
||||||
|
// First make sure that the points are on the circumference of the circle.
|
||||||
|
// If not, we'll return an error.
|
||||||
|
if !is_on_circumference(center, from, radius) {
|
||||||
|
return Err(KclError::Semantic(KclErrorDetails {
|
||||||
|
message: format!(
|
||||||
|
"Point {:?} is not on the circumference of the circle with center {:?} and radius {}.",
|
||||||
|
from, center, radius
|
||||||
|
),
|
||||||
|
source_ranges: vec![source_range],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_on_circumference(center, to, radius) {
|
||||||
|
return Err(KclError::Semantic(KclErrorDetails {
|
||||||
|
message: format!(
|
||||||
|
"Point {:?} is not on the circumference of the circle with center {:?} and radius {}.",
|
||||||
|
to, center, radius
|
||||||
|
),
|
||||||
|
source_ranges: vec![source_range],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
let start_angle = (from.y - center.y).atan2(from.x - center.x);
|
||||||
|
let end_angle = (to.y - center.y).atan2(to.x - center.x);
|
||||||
|
|
||||||
|
let start_angle_deg = start_angle * (180.0 / std::f64::consts::PI);
|
||||||
|
let end_angle_deg = end_angle * (180.0 / std::f64::consts::PI);
|
||||||
|
|
||||||
|
Ok((start_angle_deg, end_angle_deg))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_on_circumference(center: &Point2d, point: &Point2d, radius: f64) -> bool {
|
||||||
|
let dx = point.x - center.x;
|
||||||
|
let dy = point.y - center.y;
|
||||||
|
|
||||||
|
let distance_squared = dx.powi(2) + dy.powi(2);
|
||||||
|
|
||||||
|
// We'll check if the distance squared is approximately equal to radius squared.
|
||||||
|
// Due to potential floating point inaccuracies, we'll check if the difference
|
||||||
|
// is very small (e.g., 1e-9) rather than checking for strict equality.
|
||||||
|
(distance_squared - radius.powi(2)).abs() < 1e-9
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
// Here you can bring your functions into scope
|
// Here you can bring your functions into scope
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use super::{get_x_component, get_y_component};
|
use super::{get_x_component, get_y_component};
|
||||||
|
use crate::executor::SourceRange;
|
||||||
|
|
||||||
static EACH_QUAD: [(i32, [i32; 2]); 12] = [
|
static EACH_QUAD: [(i32, [i32; 2]); 12] = [
|
||||||
(-315, [1, 1]),
|
(-315, [1, 1]),
|
||||||
@ -241,4 +314,77 @@ mod tests {
|
|||||||
assert!((result[0] - 0.0).abs() < f64::EPSILON);
|
assert!((result[0] - 0.0).abs() < f64::EPSILON);
|
||||||
assert_eq!(result[1] as i32, -1);
|
assert_eq!(result[1] as i32, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_arc_center_and_end() {
|
||||||
|
let (center, end) = super::arc_center_and_end(&super::Point2d { x: 0.0, y: 0.0 }, 0.0, 90.0, 1.0);
|
||||||
|
assert_eq!(center.x.round(), -1.0);
|
||||||
|
assert_eq!(center.y, 0.0);
|
||||||
|
assert_eq!(end.x.round(), -1.0);
|
||||||
|
assert_eq!(end.y, 1.0);
|
||||||
|
|
||||||
|
let (center, end) = super::arc_center_and_end(&super::Point2d { x: 0.0, y: 0.0 }, 0.0, 180.0, 1.0);
|
||||||
|
assert_eq!(center.x.round(), -1.0);
|
||||||
|
assert_eq!(center.y, 0.0);
|
||||||
|
assert_eq!(end.x.round(), -2.0);
|
||||||
|
assert_eq!(end.y.round(), 0.0);
|
||||||
|
|
||||||
|
let (center, end) = super::arc_center_and_end(&super::Point2d { x: 0.0, y: 0.0 }, 0.0, 180.0, 10.0);
|
||||||
|
assert_eq!(center.x.round(), -10.0);
|
||||||
|
assert_eq!(center.y, 0.0);
|
||||||
|
assert_eq!(end.x.round(), -20.0);
|
||||||
|
assert_eq!(end.y.round(), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_arc_angles() {
|
||||||
|
let (angle_start, angle_end) = super::arc_angles(
|
||||||
|
&super::Point2d { x: 0.0, y: 0.0 },
|
||||||
|
&super::Point2d { x: -1.0, y: 1.0 },
|
||||||
|
&super::Point2d { x: -1.0, y: 0.0 },
|
||||||
|
1.0,
|
||||||
|
SourceRange(Default::default()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(angle_start.round(), 0.0);
|
||||||
|
assert_eq!(angle_end.round(), 90.0);
|
||||||
|
|
||||||
|
let (angle_start, angle_end) = super::arc_angles(
|
||||||
|
&super::Point2d { x: 0.0, y: 0.0 },
|
||||||
|
&super::Point2d { x: -2.0, y: 0.0 },
|
||||||
|
&super::Point2d { x: -1.0, y: 0.0 },
|
||||||
|
1.0,
|
||||||
|
SourceRange(Default::default()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(angle_start.round(), 0.0);
|
||||||
|
assert_eq!(angle_end.round(), 180.0);
|
||||||
|
|
||||||
|
let (angle_start, angle_end) = super::arc_angles(
|
||||||
|
&super::Point2d { x: 0.0, y: 0.0 },
|
||||||
|
&super::Point2d { x: -20.0, y: 0.0 },
|
||||||
|
&super::Point2d { x: -10.0, y: 0.0 },
|
||||||
|
10.0,
|
||||||
|
SourceRange(Default::default()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(angle_start.round(), 0.0);
|
||||||
|
assert_eq!(angle_end.round(), 180.0);
|
||||||
|
|
||||||
|
let result = super::arc_angles(
|
||||||
|
&super::Point2d { x: 0.0, y: 5.0 },
|
||||||
|
&super::Point2d { x: 5.0, y: 5.0 },
|
||||||
|
&super::Point2d { x: 10.0, y: -10.0 },
|
||||||
|
10.0,
|
||||||
|
SourceRange(Default::default()),
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Err(err) = result {
|
||||||
|
assert!(err.to_string().contains( "Point Point2d { x: 0.0, y: 5.0 } is not on the circumference of the circle with center Point2d { x: 10.0, y: -10.0 } and radius 10."));
|
||||||
|
} else {
|
||||||
|
panic!("Expected error");
|
||||||
|
}
|
||||||
|
assert_eq!(angle_start.round(), 0.0);
|
||||||
|
assert_eq!(angle_end.round(), 180.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user