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)
|
||||
* [`startSketchAt`](#startSketchAt)
|
||||
* [`close`](#close)
|
||||
* [`arc`](#arc)
|
||||
* [`bezierCurve`](#bezierCurve)
|
||||
|
||||
|
||||
## 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",
|
||||
"test": "vitest --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",
|
||||
"simpleserver:ci": "http-server ./public --cors -p 3000 &",
|
||||
"simpleserver": "http-server ./public --cors -p 3000",
|
||||
"fmt": "prettier --write ./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\"",
|
||||
"wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings",
|
||||
"lint": "eslint --fix src",
|
||||
|
2
src/wasm-lib/Cargo.lock
generated
2
src/wasm-lib/Cargo.lock
generated
@ -948,7 +948,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.1.3"
|
||||
version = "0.1.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bson",
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-lib"
|
||||
description = "KittyCAD Language"
|
||||
version = "0.1.3"
|
||||
version = "0.1.10"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
@ -33,6 +33,10 @@ reqwest = { version = "0.11.20", default-features = false }
|
||||
tokio = { version = "1.32.0", features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.20.0", features = ["rustls-tls-native-roots"] }
|
||||
|
||||
[features]
|
||||
default = ["engine"]
|
||||
engine = []
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
debug = true
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use parse_display::{Display, FromStr};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Map;
|
||||
@ -383,11 +384,22 @@ pub struct VariableDeclaration {
|
||||
pub start: usize,
|
||||
pub end: usize,
|
||||
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);
|
||||
|
||||
#[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)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
|
@ -44,6 +44,11 @@ impl StdLibFnArg {
|
||||
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)]
|
||||
pub fn description(&self) -> Option<String> {
|
||||
get_description_string_from_schema(&self.schema)
|
||||
@ -93,9 +98,24 @@ pub trait StdLibFn {
|
||||
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 Some(metadata) = &o.metadata {
|
||||
if let Some(description) = &metadata.description {
|
||||
@ -107,7 +127,7 @@ fn get_description_string_from_schema(schema: &schemars::schema::Schema) -> Opti
|
||||
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 {
|
||||
schemars::schema::Schema::Object(o) => {
|
||||
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)),
|
||||
}
|
||||
}
|
||||
|
||||
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(test))]
|
||||
#[cfg(feature = "engine")]
|
||||
pub mod conn;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(not(test))]
|
||||
#[cfg(feature = "engine")]
|
||||
pub use conn::EngineConnection;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(not(test))]
|
||||
#[cfg(feature = "engine")]
|
||||
pub mod conn_wasm;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(not(test))]
|
||||
#[cfg(feature = "engine")]
|
||||
pub use conn_wasm::EngineConnection;
|
||||
|
||||
#[cfg(test)]
|
||||
@ -21,6 +25,13 @@ pub mod conn_mock;
|
||||
#[cfg(test)]
|
||||
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;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -33,6 +44,7 @@ pub struct EngineManager {
|
||||
impl EngineManager {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(not(test))]
|
||||
#[cfg(feature = "engine")]
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub async fn new(manager: conn_wasm::EngineCommandManager) -> 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] {
|
||||
fn from(p: Point2d) -> Self {
|
||||
[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)]
|
||||
#[ts(export)]
|
||||
pub struct Point3d {
|
||||
|
@ -1,5 +1,5 @@
|
||||
pub mod abstract_syntax_tree_types;
|
||||
mod docs;
|
||||
pub mod docs;
|
||||
pub mod engine;
|
||||
pub mod errors;
|
||||
pub mod executor;
|
||||
|
@ -315,23 +315,25 @@ fn build_tree(
|
||||
})));
|
||||
return build_tree(&reverse_polish_notation_tokens[1..], new_stack);
|
||||
} else if current_token.token_type == TokenType::Word {
|
||||
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, "")?;
|
||||
if reverse_polish_notation_tokens.len() > 1 {
|
||||
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 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;
|
||||
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);
|
||||
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);
|
||||
}
|
||||
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 == "(" {
|
||||
let mut new_stack = stack;
|
||||
new_stack.push(MathExpression::ParenthesisToken(Box::new(ParenthesisToken {
|
||||
@ -424,6 +426,14 @@ fn build_tree(
|
||||
new_stack.push(expression);
|
||||
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] {
|
||||
MathExpression::ExtendedBinaryExpression(bin_exp) => (
|
||||
BinaryPart::BinaryExpression(Box::new(BinaryExpression {
|
||||
|
@ -1,11 +1,11 @@
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use crate::{
|
||||
abstract_syntax_tree_types::{
|
||||
ArrayExpression, BinaryExpression, BinaryPart, BodyItem, CallExpression, ExpressionStatement,
|
||||
FunctionExpression, Identifier, Literal, LiteralIdentifier, MemberExpression, MemberObject, NoneCodeMeta,
|
||||
NoneCodeNode, ObjectExpression, ObjectKeyInfo, ObjectProperty, PipeExpression, PipeSubstitution, Program,
|
||||
ReturnStatement, UnaryExpression, Value, VariableDeclaration, VariableDeclarator,
|
||||
ReturnStatement, UnaryExpression, Value, VariableDeclaration, VariableDeclarator, VariableKind,
|
||||
},
|
||||
errors::{KclError, KclErrorDetails},
|
||||
math_parser::parse_expression,
|
||||
@ -145,7 +145,12 @@ pub fn find_closing_brace(
|
||||
search_opening_brace: &str,
|
||||
) -> Result<usize, KclError> {
|
||||
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 is_first_call = search_opening_brace.is_empty() && brace_count == 0;
|
||||
if is_first_call {
|
||||
@ -966,13 +971,12 @@ fn make_variable_declaration(tokens: &[Token], index: usize) -> Result<VariableD
|
||||
declaration: VariableDeclaration {
|
||||
start: current_token.start,
|
||||
end: variable_declarators_result.declarations[variable_declarators_result.declarations.len() - 1].end,
|
||||
kind: if current_token.value == "const" {
|
||||
"const".to_string()
|
||||
} else if current_token.value == "fn" {
|
||||
"fn".to_string()
|
||||
} else {
|
||||
"unkown".to_string()
|
||||
},
|
||||
kind: VariableKind::from_str(¤t_token.value).map_err(|_| {
|
||||
KclError::Syntax(KclErrorDetails {
|
||||
source_ranges: vec![current_token.into()],
|
||||
message: "Unexpected token".to_string(),
|
||||
})
|
||||
})?,
|
||||
declarations: variable_declarators_result.declarations,
|
||||
},
|
||||
last_index: variable_declarators_result.last_index,
|
||||
@ -2479,7 +2483,7 @@ show(mySk1)"#;
|
||||
|> close(%)"#,
|
||||
);
|
||||
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[0].id.name, "yo");
|
||||
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 struct StdLib {
|
||||
#[allow(dead_code)]
|
||||
internal_fn_names: Vec<Box<(dyn crate::docs::StdLibFn)>>,
|
||||
pub internal_fn_names: Vec<Box<(dyn crate::docs::StdLibFn)>>,
|
||||
|
||||
pub fns: FnMap,
|
||||
}
|
||||
@ -64,6 +63,8 @@ impl StdLib {
|
||||
Box::new(crate::std::sketch::AngledLineThatIntersects),
|
||||
Box::new(crate::std::sketch::StartSketchAt),
|
||||
Box::new(crate::std::sketch::Close),
|
||||
Box::new(crate::std::sketch::Arc),
|
||||
Box::new(crate::std::sketch::BezierCurve),
|
||||
];
|
||||
|
||||
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("```\n");
|
||||
fn_docs.push_str(&format!("{}(", internal_fn.name()));
|
||||
for (i, arg) in internal_fn.args().iter().enumerate() {
|
||||
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_);
|
||||
let signature = internal_fn.fn_signature();
|
||||
fn_docs.push_str(&signature);
|
||||
fn_docs.push_str("\n```\n\n");
|
||||
|
||||
fn_docs.push_str("#### Arguments\n\n");
|
||||
|
@ -10,7 +10,7 @@ use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{BasePath, GeoMeta, MemoryItem, Path, Point2d, Position, Rotation, SketchGroup},
|
||||
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,
|
||||
},
|
||||
};
|
||||
@ -43,7 +43,7 @@ pub fn line_to(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||
#[stdlib {
|
||||
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 to = match data {
|
||||
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();
|
||||
|
||||
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 {
|
||||
base: BasePath {
|
||||
from: from.into(),
|
||||
@ -101,7 +116,7 @@ pub fn x_line_to(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||
#[stdlib {
|
||||
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 line_to_data = match data {
|
||||
@ -126,7 +141,7 @@ pub fn y_line_to(args: &mut Args) -> Result<MemoryItem, KclError> {
|
||||
#[stdlib {
|
||||
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 line_to_data = match data {
|
||||
@ -716,6 +731,248 @@ fn inner_close(sketch_group: SketchGroup, args: &mut Args) -> Result<SketchGroup
|
||||
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)]
|
||||
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 {
|
||||
let x = b[0] - a[0];
|
||||
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]
|
||||
}
|
||||
|
||||
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)]
|
||||
mod tests {
|
||||
// Here you can bring your functions into scope
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::{get_x_component, get_y_component};
|
||||
use crate::executor::SourceRange;
|
||||
|
||||
static EACH_QUAD: [(i32, [i32; 2]); 12] = [
|
||||
(-315, [1, 1]),
|
||||
@ -241,4 +314,77 @@ mod tests {
|
||||
assert!((result[0] - 0.0).abs() < f64::EPSILON);
|
||||
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