Compare commits
4 Commits
kurt-add-s
...
v0.15.3
Author | SHA1 | Date | |
---|---|---|---|
47d40eb801 | |||
adc4b6148d | |||
27d0d4a28b | |||
fb609c19ef |
2
.gitignore
vendored
@ -54,3 +54,5 @@ e2e/playwright/export-snapshots/*embedded.gltf
|
||||
|
||||
## generated files
|
||||
src/**/*.typegen.ts
|
||||
|
||||
src/wasm-lib/grackle/stdlib_cube_partial.json
|
||||
|
@ -8,10 +8,6 @@ once fixed in engine will just start working here with no language changes.
|
||||
model for that sketch and its underlying 3D object.
|
||||
If you see a red line around your model, it means this is happening.
|
||||
|
||||
- **Patterns**: If you try and pass a pattern to `hole` currently only the first
|
||||
item in the pattern is being subtracted. This is an engine bug that is being
|
||||
worked on.
|
||||
|
||||
- **Import**: Right now you can import a file, even if that file has brep data
|
||||
you cannot edit it, after v1, the engine will account for this. You also cannot
|
||||
currently move or transform the imported objects at all, once we have assemblies
|
||||
|
@ -32506,14 +32506,14 @@
|
||||
"format": "double"
|
||||
},
|
||||
"axis": {
|
||||
"description": "The axis around which to make the pattern. This is a 3D vector.",
|
||||
"description": "The axis around which to make the pattern. This is a 2D vector.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"maxItems": 3,
|
||||
"minItems": 3
|
||||
"maxItems": 2,
|
||||
"minItems": 2
|
||||
},
|
||||
"center": {
|
||||
"description": "The center about which to make th pattern. This is a 3D vector.",
|
||||
@ -35128,14 +35128,14 @@
|
||||
],
|
||||
"properties": {
|
||||
"axis": {
|
||||
"description": "The axis of the pattern. This is a 3D vector.",
|
||||
"description": "The axis of the pattern. This is a 2D vector.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"maxItems": 3,
|
||||
"minItems": 3
|
||||
"maxItems": 2,
|
||||
"minItems": 2
|
||||
},
|
||||
"distance": {
|
||||
"description": "The distance between each repetition. This can also be referred to as spacing.",
|
||||
|
@ -6086,8 +6086,8 @@ patternCircular(data: CircularPatternData, geometry: Geometry) -> Geometries
|
||||
{
|
||||
// The arc angle (in degrees) to place the repetitions. Must be greater than 0.
|
||||
arcDegrees: number,
|
||||
// The axis around which to make the pattern. This is a 3D vector.
|
||||
axis: [number, number, number],
|
||||
// The axis around which to make the pattern. This is a 2D vector.
|
||||
axis: [number, number],
|
||||
// The center about which to make th pattern. This is a 3D vector.
|
||||
center: [number, number, number],
|
||||
// The number of repetitions. Must be greater than 0. This excludes the original entity. For example, if `repetitions` is 1, the original entity will be copied once.
|
||||
@ -6355,8 +6355,8 @@ patternLinear(data: LinearPatternData, geometry: Geometry) -> Geometries
|
||||
* `data`: `LinearPatternData` - Data for a linear pattern.
|
||||
```
|
||||
{
|
||||
// The axis of the pattern. This is a 3D vector.
|
||||
axis: [number, number, number],
|
||||
// The axis of the pattern. This is a 2D vector.
|
||||
axis: [number, number],
|
||||
// The distance between each repetition. This can also be referred to as spacing.
|
||||
distance: number,
|
||||
// The number of repetitions. Must be greater than 0. This excludes the original entity. For example, if `repetitions` is 1, the original entity will be copied once.
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "untitled-app",
|
||||
"version": "0.15.2",
|
||||
"version": "0.15.3",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.10.2",
|
||||
@ -10,7 +10,7 @@
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@headlessui/react": "^1.7.17",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@kittycad/lib": "^0.0.53",
|
||||
"@kittycad/lib": "^0.0.54",
|
||||
"@lezer/javascript": "^1.4.9",
|
||||
"@open-rpc/client-js": "^1.8.1",
|
||||
"@react-hook/resize-observer": "^1.2.6",
|
||||
|
@ -7,7 +7,7 @@
|
||||
},
|
||||
"package": {
|
||||
"productName": "zoo-modeling-app",
|
||||
"version": "0.15.2"
|
||||
"version": "0.15.3"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
|
@ -27,6 +27,8 @@ describe('UserSidebarMenu tests', () => {
|
||||
phone: '555-555-5555',
|
||||
first_name: 'Test',
|
||||
last_name: 'User',
|
||||
can_train_on_data: false,
|
||||
is_service_account: false,
|
||||
}
|
||||
|
||||
render(
|
||||
@ -57,6 +59,8 @@ describe('UserSidebarMenu tests', () => {
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
name: '',
|
||||
can_train_on_data: false,
|
||||
is_service_account: false,
|
||||
}
|
||||
|
||||
render(
|
||||
@ -84,6 +88,8 @@ describe('UserSidebarMenu tests', () => {
|
||||
first_name: 'Test',
|
||||
last_name: 'User',
|
||||
image: '',
|
||||
can_train_on_data: false,
|
||||
is_service_account: false,
|
||||
}
|
||||
|
||||
render(
|
||||
|
@ -20,6 +20,8 @@ const LOCAL_USER: Models['User_type'] = {
|
||||
phone: '555-555-5555',
|
||||
first_name: 'Test',
|
||||
last_name: 'User',
|
||||
can_train_on_data: false,
|
||||
is_service_account: false,
|
||||
}
|
||||
|
||||
export interface UserContext {
|
||||
|
47
src/wasm-lib/Cargo.lock
generated
@ -1460,13 +1460,17 @@ name = "grackle"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"kcl-lib",
|
||||
"kittycad",
|
||||
"kittycad-execution-plan",
|
||||
"kittycad-execution-plan-macros",
|
||||
"kittycad-execution-plan-traits",
|
||||
"kittycad-modeling-cmds",
|
||||
"kittycad-modeling-session",
|
||||
"pretty_assertions",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1912,7 +1916,7 @@ dependencies = [
|
||||
"itertools 0.12.1",
|
||||
"js-sys",
|
||||
"kittycad",
|
||||
"kittycad-execution-plan-macros 0.1.4 (git+https://github.com/KittyCAD/modeling-api?branch=main)",
|
||||
"kittycad-execution-plan-macros",
|
||||
"kittycad-execution-plan-traits",
|
||||
"lazy_static",
|
||||
"parse-display 0.9.0",
|
||||
@ -1986,7 +1990,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kittycad-execution-plan"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#08f05d91062380fe3a69f4baa1f1301532d31977"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9cb86ba54e4a60aa775fa2fd8af6f0ac9d05ebeb"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"insta",
|
||||
@ -2004,19 +2008,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad-execution-plan-macros"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d31b689c944d00aadda2ef83d8422a6efff97e1be5654a61f9d95496f0c19e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kittycad-execution-plan-macros"
|
||||
version = "0.1.4"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#632b75a0242400fa34373d7973b9149b0e08aa3f"
|
||||
version = "0.1.6"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9cb86ba54e4a60aa775fa2fd8af6f0ac9d05ebeb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2025,9 +2018,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad-execution-plan-traits"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3ec8efd57b59697eb140b63c0ffe7db44fdfe5a55f14e45513411eba2280ba5"
|
||||
version = "0.1.11"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9cb86ba54e4a60aa775fa2fd8af6f0ac9d05ebeb"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"thiserror",
|
||||
@ -2036,8 +2028,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad-modeling-cmds"
|
||||
version = "0.1.18"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#08f05d91062380fe3a69f4baa1f1301532d31977"
|
||||
version = "0.1.25"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9cb86ba54e4a60aa775fa2fd8af6f0ac9d05ebeb"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@ -2047,8 +2039,9 @@ dependencies = [
|
||||
"enum-iterator-derive",
|
||||
"euler",
|
||||
"http 0.2.9",
|
||||
"kittycad-execution-plan-macros 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"kittycad-execution-plan-macros",
|
||||
"kittycad-execution-plan-traits",
|
||||
"kittycad-modeling-cmds-macros",
|
||||
"kittycad-unit-conversion-derive",
|
||||
"measurements",
|
||||
"parse-display 0.8.2",
|
||||
@ -2061,10 +2054,20 @@ dependencies = [
|
||||
"webrtc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kittycad-modeling-cmds-macros"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9cb86ba54e4a60aa775fa2fd8af6f0ac9d05ebeb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kittycad-modeling-session"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#08f05d91062380fe3a69f4baa1f1301532d31977"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9cb86ba54e4a60aa775fa2fd8af6f0ac9d05ebeb"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"kittycad",
|
||||
|
@ -60,9 +60,10 @@ members = [
|
||||
[workspace.dependencies]
|
||||
kittycad = { version = "0.2.54", default-features = false, features = ["js", "requests"] }
|
||||
kittycad-execution-plan = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
||||
kittycad-execution-plan-traits = "0.1.10"
|
||||
kittycad-modeling-session = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
||||
kittycad-execution-plan-macros = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
||||
kittycad-execution-plan-traits = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
||||
kittycad-modeling-cmds = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
||||
kittycad-modeling-session = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
||||
|
||||
[[test]]
|
||||
name = "executor"
|
||||
@ -75,4 +76,7 @@ path = "tests/modify/main.rs"
|
||||
# Example: how to point modeling-api at a different repo (e.g. a branch or a local clone)
|
||||
#[patch."https://github.com/KittyCAD/modeling-api"]
|
||||
#kittycad-execution-plan = { path = "../../../modeling-api/execution-plan" }
|
||||
#kittycad-execution-plan-macros = { path = "../../../modeling-api/execution-plan-macros" }
|
||||
#kittycad-execution-plan-traits = { path = "../../../modeling-api/execution-plan-traits" }
|
||||
#kittycad-modeling-cmds = { path = "../../../modeling-api/modeling-cmds" }
|
||||
#kittycad-modeling-session = { path = "../../../modeling-api/modeling-session" }
|
||||
|
@ -7,11 +7,15 @@ description = "A new executor for KCL which compiles to Execution Plans"
|
||||
|
||||
[dependencies]
|
||||
kcl-lib = { path = "../kcl" }
|
||||
kittycad = { workspace = true }
|
||||
kittycad-execution-plan = { workspace = true }
|
||||
kittycad-execution-plan-traits = { workspace = true }
|
||||
kittycad-execution-plan-macros = { workspace = true }
|
||||
kittycad-modeling-cmds = { workspace = true }
|
||||
kittycad-modeling-session = { workspace = true }
|
||||
thiserror = "1.0.57"
|
||||
tokio = { version = "1.36.0", features = ["macros", "rt"] }
|
||||
uuid = "1.7"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1"
|
||||
|
@ -103,7 +103,7 @@ impl BindingScope {
|
||||
("add".into(), EpBinding::from(KclFunction::Add(native_functions::Add))),
|
||||
(
|
||||
"startSketchAt".into(),
|
||||
EpBinding::from(KclFunction::StartSketchAt(native_functions::StartSketchAt)),
|
||||
EpBinding::from(KclFunction::StartSketchAt(native_functions::sketch::StartSketchAt)),
|
||||
),
|
||||
]),
|
||||
parent: None,
|
||||
|
@ -45,6 +45,12 @@ pub enum CompileError {
|
||||
NoReturnStmt,
|
||||
#[error("You used the %, which means \"substitute this argument for the value to the left in this |> pipeline\". But there is no such value, because you're not calling a pipeline.")]
|
||||
NotInPipeline,
|
||||
#[error("The function '{fn_name}' expects a parameter of type {expected} but you supplied {actual}")]
|
||||
ArgWrongType {
|
||||
fn_name: &'static str,
|
||||
expected: &'static str,
|
||||
actual: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
@ -618,7 +618,7 @@ impl Eq for UserDefinedFunction {}
|
||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||
enum KclFunction {
|
||||
Id(native_functions::Id),
|
||||
StartSketchAt(native_functions::StartSketchAt),
|
||||
StartSketchAt(native_functions::sketch::StartSketchAt),
|
||||
Add(native_functions::Add),
|
||||
UserDefined(UserDefinedFunction),
|
||||
}
|
||||
|
@ -2,12 +2,13 @@
|
||||
//! This includes some of the stdlib, e.g. `startSketchAt`.
|
||||
//! But some other stdlib functions will be written in KCL.
|
||||
|
||||
use kcl_lib::std::sketch::PlaneData;
|
||||
use kittycad_execution_plan::{BinaryArithmetic, Destination, Instruction};
|
||||
use kittycad_execution_plan_traits::{Address, Value};
|
||||
use kittycad_execution_plan_traits::Address;
|
||||
|
||||
use crate::{CompileError, EpBinding, EvalPlan};
|
||||
|
||||
pub mod sketch;
|
||||
|
||||
/// The identity function. Always returns its first input.
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||
@ -41,34 +42,6 @@ impl Callable for Id {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||
pub struct StartSketchAt;
|
||||
|
||||
impl Callable for StartSketchAt {
|
||||
fn call(&self, next_addr: &mut Address, _args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
|
||||
let mut instructions = Vec::new();
|
||||
// Store the plane.
|
||||
let plane = PlaneData::XY.into_parts();
|
||||
instructions.push(Instruction::SetValue {
|
||||
address: next_addr.offset_by(plane.len()),
|
||||
value_parts: plane,
|
||||
});
|
||||
// TODO: Get the plane ID from global context.
|
||||
// TODO: Send this command:
|
||||
// ModelingCmd::SketchModeEnable {
|
||||
// animated: false,
|
||||
// ortho: false,
|
||||
// plane_id: plane.id,
|
||||
// // We pass in the normal for the plane here.
|
||||
// disable_camera_with_plane: Some(plane.z_axis.clone().into()),
|
||||
// },
|
||||
// TODO: Send ModelingCmd::StartPath at the given point.
|
||||
// TODO (maybe): Store the SketchGroup in KCEP memory.
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// A test function that adds two numbers.
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||
|
7
src/wasm-lib/grackle/src/native_functions/sketch.rs
Normal file
@ -0,0 +1,7 @@
|
||||
//! Native functions for sketching on the plane.
|
||||
|
||||
pub mod helpers;
|
||||
pub mod stdlib_functions;
|
||||
pub mod types;
|
||||
|
||||
pub use stdlib_functions::StartSketchAt;
|
82
src/wasm-lib/grackle/src/native_functions/sketch/helpers.rs
Normal file
@ -0,0 +1,82 @@
|
||||
use kittycad_execution_plan::{api_request::ApiRequest, Instruction};
|
||||
use kittycad_execution_plan_traits::{Address, InMemory};
|
||||
use kittycad_modeling_cmds::{id::ModelingCmdId, ModelingCmdEndpoint};
|
||||
|
||||
use crate::{binding_scope::EpBinding, error::CompileError};
|
||||
|
||||
/// Emit instructions for an API call with no parameters.
|
||||
pub fn no_arg_api_call(instrs: &mut Vec<Instruction>, endpoint: ModelingCmdEndpoint, cmd_id: ModelingCmdId) {
|
||||
instrs.push(Instruction::ApiRequest(ApiRequest {
|
||||
endpoint,
|
||||
store_response: None,
|
||||
arguments: vec![],
|
||||
cmd_id,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Emit instructions for an API call with the given parameters.
|
||||
/// The API parameters are stored in the EP memory stack.
|
||||
/// So, they have to be pushed onto the stack in the right order,
|
||||
/// i.e. the reverse order in which the API call's Rust struct defines the fields.
|
||||
pub fn stack_api_call<const N: usize>(
|
||||
instrs: &mut Vec<Instruction>,
|
||||
endpoint: ModelingCmdEndpoint,
|
||||
store_response: Option<Address>,
|
||||
cmd_id: ModelingCmdId,
|
||||
data: [Vec<kittycad_execution_plan_traits::Primitive>; N],
|
||||
) {
|
||||
let arguments = vec![InMemory::StackPop; data.len()];
|
||||
instrs.extend(data.map(|data| Instruction::StackPush { data }));
|
||||
instrs.push(Instruction::ApiRequest(ApiRequest {
|
||||
endpoint,
|
||||
store_response,
|
||||
arguments,
|
||||
cmd_id,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn single_binding(b: EpBinding, fn_name: &'static str, expected: &'static str) -> Result<Address, CompileError> {
|
||||
match b {
|
||||
EpBinding::Single(a) => Ok(a),
|
||||
EpBinding::Sequence { .. } => Err(CompileError::ArgWrongType {
|
||||
fn_name,
|
||||
expected,
|
||||
actual: "array".to_owned(),
|
||||
}),
|
||||
EpBinding::Map { .. } => Err(CompileError::ArgWrongType {
|
||||
fn_name,
|
||||
expected,
|
||||
actual: "object".to_owned(),
|
||||
}),
|
||||
EpBinding::Function(_) => Err(CompileError::ArgWrongType {
|
||||
fn_name,
|
||||
expected,
|
||||
actual: "function".to_owned(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sequence_binding(
|
||||
b: EpBinding,
|
||||
fn_name: &'static str,
|
||||
expected: &'static str,
|
||||
) -> Result<Vec<EpBinding>, CompileError> {
|
||||
match b {
|
||||
EpBinding::Sequence { elements, .. } => Ok(elements),
|
||||
EpBinding::Single(_) => Err(CompileError::ArgWrongType {
|
||||
fn_name,
|
||||
expected,
|
||||
actual: "single".to_owned(),
|
||||
}),
|
||||
EpBinding::Map { .. } => Err(CompileError::ArgWrongType {
|
||||
fn_name,
|
||||
expected,
|
||||
actual: "object".to_owned(),
|
||||
}),
|
||||
EpBinding::Function(_) => Err(CompileError::ArgWrongType {
|
||||
fn_name,
|
||||
expected,
|
||||
actual: "function".to_owned(),
|
||||
}),
|
||||
}
|
||||
}
|
@ -0,0 +1,167 @@
|
||||
use kittycad_execution_plan::{api_request::ApiRequest, Instruction};
|
||||
use kittycad_execution_plan_traits::{Address, InMemory, Value};
|
||||
use kittycad_modeling_cmds::{
|
||||
shared::{Point3d, Point4d},
|
||||
ModelingCmdEndpoint,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{binding_scope::EpBinding, error::CompileError, native_functions::Callable, EvalPlan};
|
||||
|
||||
use super::{
|
||||
helpers::{no_arg_api_call, sequence_binding, single_binding, stack_api_call},
|
||||
types::{Axes, BasePath, Plane, SketchGroup},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||
pub struct StartSketchAt;
|
||||
|
||||
impl Callable for StartSketchAt {
|
||||
fn call(&self, next_addr: &mut Address, args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
|
||||
let mut instructions = Vec::new();
|
||||
// First, before we send any API calls, let's validate the arguments to this function.
|
||||
let mut args_iter = args.into_iter();
|
||||
let Some(start) = args_iter.next() else {
|
||||
return Err(CompileError::NotEnoughArgs {
|
||||
fn_name: "startSketchAt".into(),
|
||||
required: 1,
|
||||
actual: 0,
|
||||
});
|
||||
};
|
||||
let start_point = {
|
||||
let expected = "2D point (array with length 2)";
|
||||
let fn_name = "startSketchAt";
|
||||
let elements = sequence_binding(start, "startSketchAt", "an array of length 2")?;
|
||||
if elements.len() != 2 {
|
||||
return Err(CompileError::ArgWrongType {
|
||||
fn_name,
|
||||
expected,
|
||||
actual: format!("array of length {}", elements.len()),
|
||||
});
|
||||
}
|
||||
// KCL stores points as an array.
|
||||
// KC API stores them as Rust objects laid flat out in memory.
|
||||
let start = next_addr.offset_by(2);
|
||||
let start_x = start;
|
||||
let start_y = start + 1;
|
||||
let start_z = start + 2;
|
||||
instructions.extend([
|
||||
Instruction::Copy {
|
||||
source: single_binding(elements[0].clone(), "startSketchAt (first parameter, elem 0)", "number")?,
|
||||
destination: start_x,
|
||||
},
|
||||
Instruction::Copy {
|
||||
source: single_binding(elements[1].clone(), "startSketchAt (first parameter, elem 1)", "number")?,
|
||||
destination: start_y,
|
||||
},
|
||||
Instruction::SetPrimitive {
|
||||
address: start_z,
|
||||
value: 0.0.into(),
|
||||
},
|
||||
]);
|
||||
start
|
||||
};
|
||||
let tag = match args_iter.next() {
|
||||
None => None,
|
||||
Some(b) => Some(single_binding(b, "startSketchAt", "a single string")?),
|
||||
};
|
||||
|
||||
// Define some constants:
|
||||
let axes = Axes {
|
||||
x: Point3d { x: 1.0, y: 0.0, z: 0.0 },
|
||||
y: Point3d { x: 0.0, y: 1.0, z: 0.0 },
|
||||
z: Point3d { x: 0.0, y: 0.0, z: 1.0 },
|
||||
};
|
||||
let origin = Point3d::default();
|
||||
|
||||
// Now the function can start.
|
||||
// First API call: make the plane.
|
||||
let plane_id = Uuid::new_v4();
|
||||
stack_api_call(
|
||||
&mut instructions,
|
||||
ModelingCmdEndpoint::MakePlane,
|
||||
None,
|
||||
plane_id.into(),
|
||||
[
|
||||
Some(true).into_parts(), // hide
|
||||
vec![false.into()], // clobber
|
||||
vec![60.0.into()], // size
|
||||
axes.y.into_parts(),
|
||||
axes.x.into_parts(),
|
||||
origin.into_parts(),
|
||||
],
|
||||
);
|
||||
|
||||
// Next, enter sketch mode.
|
||||
stack_api_call(
|
||||
&mut instructions,
|
||||
ModelingCmdEndpoint::SketchModeEnable,
|
||||
None,
|
||||
Uuid::new_v4().into(),
|
||||
[
|
||||
Some(axes.z).into_parts(),
|
||||
vec![false.into()], // animated
|
||||
vec![false.into()], // ortho mode
|
||||
vec![plane_id.into()],
|
||||
],
|
||||
);
|
||||
|
||||
// Then start a path
|
||||
let path_id = Uuid::new_v4();
|
||||
no_arg_api_call(&mut instructions, ModelingCmdEndpoint::StartPath, path_id.into());
|
||||
|
||||
// Move the path pen to the given point.
|
||||
instructions.push(Instruction::StackPush {
|
||||
data: vec![path_id.into()],
|
||||
});
|
||||
instructions.push(Instruction::ApiRequest(ApiRequest {
|
||||
endpoint: ModelingCmdEndpoint::MovePathPen,
|
||||
store_response: None,
|
||||
arguments: vec![InMemory::StackPop, InMemory::Address(start_point)],
|
||||
cmd_id: Uuid::new_v4().into(),
|
||||
}));
|
||||
|
||||
// Starting a sketch creates a sketch group.
|
||||
// Updating the sketch will update this sketch group later.
|
||||
let sketch_group = SketchGroup {
|
||||
id: path_id,
|
||||
position: origin,
|
||||
rotation: Point4d {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
w: 1.0,
|
||||
},
|
||||
// TODO: Must copy the existing data (from the arguments to this KCL function)
|
||||
// over these values after writing to memory.
|
||||
path_first: BasePath {
|
||||
from: Default::default(),
|
||||
to: Default::default(),
|
||||
name: Default::default(),
|
||||
},
|
||||
path_rest: Vec::new(),
|
||||
on: super::types::SketchSurface::Plane(Plane {
|
||||
id: plane_id,
|
||||
value: super::types::PlaneType::XY,
|
||||
origin,
|
||||
axes,
|
||||
}),
|
||||
axes,
|
||||
entity_id: Some(plane_id),
|
||||
};
|
||||
let sketch_group_primitives = sketch_group.clone().into_parts();
|
||||
|
||||
let sketch_group_addr = next_addr.offset_by(sketch_group_primitives.len());
|
||||
instructions.push(Instruction::SetValue {
|
||||
address: sketch_group_addr,
|
||||
value_parts: sketch_group_primitives,
|
||||
});
|
||||
instructions.extend(sketch_group.set_base_path(sketch_group_addr, start_point, tag));
|
||||
|
||||
Ok(EvalPlan {
|
||||
instructions,
|
||||
binding: EpBinding::Single(sketch_group_addr),
|
||||
})
|
||||
}
|
||||
}
|
133
src/wasm-lib/grackle/src/native_functions/sketch/types.rs
Normal file
@ -0,0 +1,133 @@
|
||||
use kittycad_execution_plan::Instruction;
|
||||
use kittycad_execution_plan_macros::ExecutionPlanValue;
|
||||
use kittycad_execution_plan_traits::{Address, Value};
|
||||
use kittycad_modeling_cmds::shared::{Point2d, Point3d, Point4d};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// A sketch group is a collection of paths.
|
||||
#[derive(Clone, ExecutionPlanValue)]
|
||||
pub struct SketchGroup {
|
||||
/// The id of the sketch group.
|
||||
pub id: Uuid,
|
||||
/// What the sketch is on (can be a plane or a face).
|
||||
pub on: SketchSurface,
|
||||
/// The position of the sketch group.
|
||||
pub position: Point3d,
|
||||
/// The rotation of the sketch group base plane.
|
||||
pub rotation: Point4d,
|
||||
/// The X, Y and Z axes of this sketch's base plane, in 3D space.
|
||||
pub axes: Axes,
|
||||
/// The plane id or face id of the sketch group.
|
||||
pub entity_id: Option<Uuid>,
|
||||
/// The base path.
|
||||
pub path_first: BasePath,
|
||||
/// Paths after the first path, if any.
|
||||
pub path_rest: Vec<Path>,
|
||||
}
|
||||
|
||||
impl SketchGroup {
|
||||
pub fn set_base_path(&self, sketch_group: Address, start_point: Address, tag: Option<Address>) -> Vec<Instruction> {
|
||||
let base_path_addr = sketch_group
|
||||
+ self.id.into_parts().len()
|
||||
+ self.on.into_parts().len()
|
||||
+ self.position.into_parts().len()
|
||||
+ self.rotation.into_parts().len()
|
||||
+ self.axes.into_parts().len()
|
||||
+ self.entity_id.into_parts().len()
|
||||
+ self.entity_id.into_parts().len();
|
||||
let mut out = vec![
|
||||
// Copy over the `from` field.
|
||||
Instruction::Copy {
|
||||
source: start_point,
|
||||
destination: base_path_addr,
|
||||
},
|
||||
// Copy over the `to` field.
|
||||
Instruction::Copy {
|
||||
source: start_point,
|
||||
destination: base_path_addr + self.path_first.from.into_parts().len(),
|
||||
},
|
||||
];
|
||||
if let Some(tag) = tag {
|
||||
// Copy over the `name` field.
|
||||
out.push(Instruction::Copy {
|
||||
source: tag,
|
||||
destination: base_path_addr
|
||||
+ self.path_first.from.into_parts().len()
|
||||
+ self.path_first.to.into_parts().len(),
|
||||
});
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
/// The X, Y and Z axes.
|
||||
#[derive(Clone, Copy, ExecutionPlanValue)]
|
||||
pub struct Axes {
|
||||
pub x: Point3d,
|
||||
pub y: Point3d,
|
||||
pub z: Point3d,
|
||||
}
|
||||
|
||||
#[derive(Clone, ExecutionPlanValue)]
|
||||
pub struct BasePath {
|
||||
pub from: Point2d<f64>,
|
||||
pub to: Point2d<f64>,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/// A path.
|
||||
#[derive(Clone, ExecutionPlanValue)]
|
||||
pub enum Path {
|
||||
/// A path that goes to a point.
|
||||
ToPoint { base: BasePath },
|
||||
/// A arc that is tangential to the last path segment that goes to a point
|
||||
TangentialArcTo {
|
||||
base: BasePath,
|
||||
/// the arc's center
|
||||
center: Point2d,
|
||||
/// arc's direction
|
||||
ccw: bool,
|
||||
},
|
||||
/// A path that is horizontal.
|
||||
Horizontal {
|
||||
base: BasePath,
|
||||
/// The x coordinate.
|
||||
x: f64,
|
||||
},
|
||||
/// An angled line to.
|
||||
AngledLineTo {
|
||||
base: BasePath,
|
||||
/// The x coordinate.
|
||||
x: Option<f64>,
|
||||
/// The y coordinate.
|
||||
y: Option<f64>,
|
||||
},
|
||||
/// A base path.
|
||||
Base { base: BasePath },
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, ExecutionPlanValue)]
|
||||
pub enum SketchSurface {
|
||||
Plane(Plane),
|
||||
}
|
||||
|
||||
/// A plane.
|
||||
#[derive(Clone, Copy, ExecutionPlanValue)]
|
||||
pub struct Plane {
|
||||
/// The id of the plane.
|
||||
pub id: Uuid,
|
||||
// The code for the plane either a string or custom.
|
||||
pub value: PlaneType,
|
||||
/// Origin of the plane.
|
||||
pub origin: Point3d,
|
||||
pub axes: Axes,
|
||||
}
|
||||
|
||||
/// Type for a plane.
|
||||
#[derive(Clone, Copy, ExecutionPlanValue)]
|
||||
pub enum PlaneType {
|
||||
XY,
|
||||
XZ,
|
||||
YZ,
|
||||
Custom,
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
|
||||
use ep::{Destination, UnaryArithmetic};
|
||||
use ept::{ListHeader, ObjectHeader};
|
||||
use kittycad_modeling_session::SessionBuilder;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
@ -1044,6 +1046,71 @@ fn store_object_with_array_property() {
|
||||
)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn stdlib_cube_partial() {
|
||||
let program = r#"
|
||||
let cube = startSketchAt([22.0, 33.0])
|
||||
"#;
|
||||
let (plan, _scope) = must_plan(program);
|
||||
std::fs::write("stdlib_cube_partial.json", serde_json::to_string_pretty(&plan).unwrap()).unwrap();
|
||||
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
|
||||
.ast()
|
||||
.unwrap();
|
||||
let mem = crate::execute(ast, Some(test_client().await)).await.unwrap();
|
||||
dbg!(mem);
|
||||
}
|
||||
|
||||
async fn test_client() -> Session {
|
||||
let kittycad_api_token = env::var("KITTYCAD_API_TOKEN").expect("You must set $KITTYCAD_API_TOKEN");
|
||||
let kittycad_api_client = kittycad::Client::new(kittycad_api_token);
|
||||
let session_builder = SessionBuilder {
|
||||
client: kittycad_api_client,
|
||||
fps: Some(10),
|
||||
unlocked_framerate: Some(false),
|
||||
video_res_height: Some(720),
|
||||
video_res_width: Some(1280),
|
||||
buffer_reqs: None,
|
||||
await_response_timeout: None,
|
||||
};
|
||||
match Session::start(session_builder).await {
|
||||
Err(e) => match e {
|
||||
kittycad::types::error::Error::InvalidRequest(s) => panic!("Request did not meet requirements {s}"),
|
||||
kittycad::types::error::Error::CommunicationError(e) => {
|
||||
panic!(" A server error either due to the data, or with the connection: {e}")
|
||||
}
|
||||
kittycad::types::error::Error::RequestError(e) => panic!("Could not build request: {e}"),
|
||||
kittycad::types::error::Error::SerdeError { error, status } => {
|
||||
panic!("Serde error (HTTP {status}): {error}")
|
||||
}
|
||||
kittycad::types::error::Error::InvalidResponsePayload { error, response } => {
|
||||
panic!("Invalid response payload. Error {error}, response {response:?}")
|
||||
}
|
||||
kittycad::types::error::Error::Server { body, status } => panic!("Server error (HTTP {status}): {body}"),
|
||||
kittycad::types::error::Error::UnexpectedResponse(resp) => {
|
||||
let status = resp.status();
|
||||
let url = resp.url().to_owned();
|
||||
match resp.text().await {
|
||||
Ok(body) => panic!(
|
||||
"Unexpected response from KittyCAD API.
|
||||
URL:{url}
|
||||
HTTP {status}
|
||||
---Body----
|
||||
{body}"
|
||||
),
|
||||
Err(e) => panic!(
|
||||
"Unexpected response from KittyCAD API.
|
||||
URL:{url}
|
||||
HTTP {status}
|
||||
---Body could not be read, the error is----
|
||||
{e}"
|
||||
),
|
||||
}
|
||||
}
|
||||
},
|
||||
Ok(x) => x,
|
||||
}
|
||||
}
|
||||
|
||||
#[ignore = "haven't done API calls or stdlib yet"]
|
||||
#[test]
|
||||
fn stdlib_api_calls() {
|
||||
|
@ -23,8 +23,8 @@ pub struct LinearPatternData {
|
||||
pub repetitions: usize,
|
||||
/// The distance between each repetition. This can also be referred to as spacing.
|
||||
pub distance: f64,
|
||||
/// The axis of the pattern. This is a 3D vector.
|
||||
pub axis: [f64; 3],
|
||||
/// The axis of the pattern. This is a 2D vector.
|
||||
pub axis: [f64; 2],
|
||||
}
|
||||
|
||||
/// Data for a circular pattern.
|
||||
@ -36,8 +36,8 @@ pub struct CircularPatternData {
|
||||
/// This excludes the original entity. For example, if `repetitions` is 1,
|
||||
/// the original entity will be copied once.
|
||||
pub repetitions: usize,
|
||||
/// The axis around which to make the pattern. This is a 3D vector.
|
||||
pub axis: [f64; 3],
|
||||
/// The axis around which to make the pattern. This is a 2D vector.
|
||||
pub axis: [f64; 2],
|
||||
/// The center about which to make th pattern. This is a 3D vector.
|
||||
pub center: [f64; 3],
|
||||
/// The arc angle (in degrees) to place the repetitions. Must be greater than 0.
|
||||
@ -50,7 +50,7 @@ pub struct CircularPatternData {
|
||||
pub async fn pattern_linear(args: Args) -> Result<MemoryItem, KclError> {
|
||||
let (data, geometry): (LinearPatternData, Geometry) = args.get_data_and_geometry()?;
|
||||
|
||||
if data.axis == [0.0, 0.0, 0.0] {
|
||||
if data.axis == [0.0, 0.0] {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message:
|
||||
"The axis of the linear pattern cannot be the zero vector. Otherwise they will just duplicate in place."
|
||||
@ -70,7 +70,7 @@ pub async fn pattern_linear(args: Args) -> Result<MemoryItem, KclError> {
|
||||
pub async fn pattern_circular(args: Args) -> Result<MemoryItem, KclError> {
|
||||
let (data, geometry): (CircularPatternData, Geometry) = args.get_data_and_geometry()?;
|
||||
|
||||
if data.axis == [0.0, 0.0, 0.0] {
|
||||
if data.axis == [0.0, 0.0] {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message:
|
||||
"The axis of the circular pattern cannot be the zero vector. Otherwise they will just duplicate in place."
|
||||
@ -97,7 +97,11 @@ async fn inner_pattern_linear(data: LinearPatternData, geometry: Geometry, args:
|
||||
.send_modeling_cmd(
|
||||
id,
|
||||
ModelingCmd::EntityLinearPattern {
|
||||
axis: data.axis.into(),
|
||||
axis: kittycad::types::Point3D {
|
||||
x: data.axis[0],
|
||||
y: data.axis[1],
|
||||
z: 0.0,
|
||||
},
|
||||
entity_id: geometry.id(),
|
||||
num_repetitions: data.repetitions as u32,
|
||||
spacing: data.distance,
|
||||
@ -154,7 +158,11 @@ async fn inner_pattern_circular(
|
||||
.send_modeling_cmd(
|
||||
id,
|
||||
ModelingCmd::EntityCircularPattern {
|
||||
axis: data.axis.into(),
|
||||
axis: kittycad::types::Point3D {
|
||||
x: data.axis[0],
|
||||
y: data.axis[1],
|
||||
z: 0.0,
|
||||
},
|
||||
entity_id: geometry.id(),
|
||||
center: data.center.into(),
|
||||
num_repetitions: data.repetitions as u32,
|
||||
|
@ -735,7 +735,7 @@ async fn serial_test_patterns_linear_basic() {
|
||||
}
|
||||
|
||||
const part = circle([0,0], 2)
|
||||
|> patternLinear({axis: [0,0,1], repetitions: 12, distance: 2}, %)
|
||||
|> patternLinear({axis: [0,1], repetitions: 12, distance: 2}, %)
|
||||
"#;
|
||||
|
||||
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
||||
@ -765,7 +765,7 @@ const part = startSketchOn('XY')
|
||||
|> line([0, -1], %)
|
||||
|> close(%)
|
||||
|> extrude(1, %)
|
||||
|> patternLinear({axis: [1, 0,1], repetitions: 3, distance: 6}, %)
|
||||
|> patternLinear({axis: [1, 0], repetitions: 3, distance: 6}, %)
|
||||
"#;
|
||||
|
||||
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
||||
@ -789,7 +789,7 @@ async fn serial_test_patterns_linear_basic_negative_distance() {
|
||||
}
|
||||
|
||||
const part = circle([0,0], 2)
|
||||
|> patternLinear({axis: [0,0,1], repetitions: 12, distance: -2}, %)
|
||||
|> patternLinear({axis: [0,1], repetitions: 12, distance: -2}, %)
|
||||
"#;
|
||||
|
||||
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
||||
@ -817,7 +817,7 @@ async fn serial_test_patterns_linear_basic_negative_axis() {
|
||||
}
|
||||
|
||||
const part = circle([0,0], 2)
|
||||
|> patternLinear({axis: [0,0,-1], repetitions: 12, distance: 2}, %)
|
||||
|> patternLinear({axis: [0,-1], repetitions: 12, distance: 2}, %)
|
||||
"#;
|
||||
|
||||
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
||||
@ -845,7 +845,7 @@ async fn serial_test_patterns_linear_basic_holes() {
|
||||
}
|
||||
|
||||
const circles = circle([5, 5], 1)
|
||||
|> patternLinear({axis: [1,1,0], repetitions: 12, distance: 3}, %)
|
||||
|> patternLinear({axis: [1,1], repetitions: 12, distance: 3}, %)
|
||||
|
||||
const rectangle = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
@ -878,7 +878,7 @@ async fn serial_test_patterns_circular_basic_2d() {
|
||||
}
|
||||
|
||||
const part = circle([0,0], 2)
|
||||
|> patternCircular({axis: [0,0,1], center: [20, 20, 20], repetitions: 12, arcDegrees: 210, rotateDuplicates: true}, %)
|
||||
|> patternCircular({axis: [0,1], center: [20, 20, 20], repetitions: 12, arcDegrees: 210, rotateDuplicates: true}, %)
|
||||
"#;
|
||||
|
||||
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
||||
@ -908,7 +908,7 @@ const part = startSketchOn('XY')
|
||||
|> line([0, -1], %)
|
||||
|> close(%)
|
||||
|> extrude(1, %)
|
||||
|> patternCircular({axis: [0,1,0], center: [-20, -20, -20], repetitions: 40, arcDegrees: 360, rotateDuplicates: false}, %)
|
||||
|> patternCircular({axis: [0,1], center: [-20, -20, -20], repetitions: 40, arcDegrees: 360, rotateDuplicates: false}, %)
|
||||
"#;
|
||||
|
||||
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
||||
@ -938,7 +938,7 @@ const part = startSketchOn('XY')
|
||||
|> line([0, -1], %)
|
||||
|> close(%)
|
||||
|> extrude(1, %)
|
||||
|> patternCircular({axis: [1,1,-1], center: [10, 0, 10], repetitions: 10, arcDegrees: 360, rotateDuplicates: true}, %)
|
||||
|> patternCircular({axis: [1,1], center: [10, 0, 10], repetitions: 10, arcDegrees: 360, rotateDuplicates: true}, %)
|
||||
"#;
|
||||
|
||||
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
||||
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 116 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 96 KiB |
@ -1801,10 +1801,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60"
|
||||
integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==
|
||||
|
||||
"@kittycad/lib@^0.0.53":
|
||||
version "0.0.53"
|
||||
resolved "https://registry.yarnpkg.com/@kittycad/lib/-/lib-0.0.53.tgz#32f10f63428c5f3bb6a435507dbfa72c1e7ba10d"
|
||||
integrity sha512-a0WTVVGKE+J7I1bERn8pcr8cC5/X14dFi78Y7wAsu8ok/SuHVTPoPHVCZ8bUmcWzY1iWpLC/HCIIHigXjkF3ZA==
|
||||
"@kittycad/lib@^0.0.54":
|
||||
version "0.0.54"
|
||||
resolved "https://registry.yarnpkg.com/@kittycad/lib/-/lib-0.0.54.tgz#6744977a2048152a425809d690e986054213ceab"
|
||||
integrity sha512-4fsQLo0+TDn65p4uAUa46/TpWvN55MCu5Yd5hriyF7Xt9PCrdvDsgBisn79Y5dPkh6lq5TMy16T+a1yKcdh/kg==
|
||||
dependencies:
|
||||
node-fetch "3.3.2"
|
||||
openapi-types "^12.0.0"
|
||||
|