Replace kittycad crate with kittycad-modeling-cmds (#3909)

lib.rs/kittycad-modeling-cmds is the source of our Modeling API. It gets included in our backend APIs, and those APIs generate OpenAPI specs which are read by `openapitor` which generates the lib.rs/kittycad crate. So basically, our modeling app is using the _generated code_ instead of the _handwritten code_.

This sucks -- if you add a new field to the modeling-api crate, you have to merge PRs to the engine, api-deux, and kittycad.rs before finally you can get the new field into the modeling-app. I was pretty embarrased when @mlfarrell asked how to get a new field into the modeling app and had to explain this whole bullshit cycle. Let's fix it.

Switching to use the kittycad-modeling-cmds (aka kcmc) crate directly should speed up our dev cycle.
This commit is contained in:
Adam Chalmers
2024-09-18 17:04:04 -05:00
committed by GitHub
parent 2978e80226
commit 5cc92f0162
23 changed files with 744 additions and 510 deletions

196
src/wasm-lib/Cargo.lock generated
View File

@ -85,13 +85,19 @@ dependencies = [
"backtrace",
]
[[package]]
name = "approx"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94"
[[package]]
name = "approx"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [
"num-traits",
"num-traits 0.2.18",
]
[[package]]
@ -201,7 +207,7 @@ dependencies = [
"libm",
"num-bigint",
"num-integer",
"num-traits",
"num-traits 0.2.18",
"serde",
]
@ -261,7 +267,7 @@ dependencies = [
"indexmap 2.2.5",
"js-sys",
"once_cell",
"rand",
"rand 0.8.5",
"serde",
"serde_bytes",
"serde_json",
@ -326,6 +332,18 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cgmath"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64a4b57c8f4e3a2e9ac07e0f6abc9c24b6fc9e1b54c3478cfb598f3d0023e51c"
dependencies = [
"approx 0.1.1",
"mint",
"num-traits 0.1.43",
"rand 0.4.6",
]
[[package]]
name = "chrono"
version = "0.4.38"
@ -335,7 +353,7 @@ dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"num-traits 0.2.18",
"serde",
"wasm-bindgen",
"windows-targets 0.52.4",
@ -496,7 +514,7 @@ dependencies = [
"futures",
"is-terminal",
"itertools 0.10.5",
"num-traits",
"num-traits 0.2.18",
"once_cell",
"oorandom",
"plotters",
@ -754,6 +772,26 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "enum-iterator"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c280b9e6b3ae19e152d8e31cf47f18389781e119d4013a2a2bb0180e5facc635"
dependencies = [
"enum-iterator-derive",
]
[[package]]
name = "enum-iterator-derive"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
]
[[package]]
name = "equivalent"
version = "1.0.1"
@ -770,6 +808,16 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "euler"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f19d11568a4a46aee488bdab3a2963e5e2c3cfd6091aa0abceaddcea82c0bc1"
dependencies = [
"approx 0.1.1",
"cgmath",
]
[[package]]
name = "expectorate"
version = "1.1.0"
@ -834,6 +882,12 @@ dependencies = [
"unicode-segmentation",
]
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "funty"
version = "2.0.0"
@ -1211,7 +1265,7 @@ checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10"
dependencies = [
"bytemuck",
"byteorder-lite",
"num-traits",
"num-traits 0.2.18",
"png",
]
@ -1348,7 +1402,7 @@ name = "kcl-lib"
version = "0.2.14"
dependencies = [
"anyhow",
"approx",
"approx 0.5.1",
"async-recursion",
"async-trait",
"base64 0.22.1",
@ -1372,6 +1426,7 @@ dependencies = [
"itertools 0.13.0",
"js-sys",
"kittycad",
"kittycad-modeling-cmds",
"lazy_static",
"measurements",
"mime_guess",
@ -1449,7 +1504,7 @@ dependencies = [
"mime_guess",
"parse-display",
"phonenumber",
"rand",
"rand 0.8.5",
"reqwest",
"reqwest-conditional-middleware",
"reqwest-middleware",
@ -1467,6 +1522,54 @@ dependencies = [
"uuid",
]
[[package]]
name = "kittycad-modeling-cmds"
version = "0.2.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee900033a5804ca2354f0760478e851a0ab04d32b38a9117d0bd4f87a8867110"
dependencies = [
"anyhow",
"chrono",
"data-encoding",
"enum-iterator",
"enum-iterator-derive",
"euler",
"http 0.2.12",
"kittycad-modeling-cmds-macros",
"kittycad-unit-conversion-derive",
"measurements",
"parse-display",
"parse-display-derive",
"schemars",
"serde",
"serde_bytes",
"serde_json",
"uuid",
]
[[package]]
name = "kittycad-modeling-cmds-macros"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0cdc505a33bfffb87c317435ec41ced8f73474217cf30db685e479bf289757e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.77",
]
[[package]]
name = "kittycad-unit-conversion-derive"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7001c46a92c1edce6722a3900539b198230980799035f02d92b4e7df3fc08738"
dependencies = [
"inflections",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
@ -1600,6 +1703,12 @@ dependencies = [
"simd-adler32",
]
[[package]]
name = "mint"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff"
[[package]]
name = "mio"
version = "1.0.1"
@ -1639,7 +1748,7 @@ checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
"num-traits 0.2.18",
]
[[package]]
@ -1654,7 +1763,16 @@ version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
"num-traits 0.2.18",
]
[[package]]
name = "num-traits"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
dependencies = [
"num-traits 0.2.18",
]
[[package]]
@ -1714,7 +1832,7 @@ dependencies = [
"lazy_static",
"percent-encoding",
"pin-project",
"rand",
"rand 0.8.5",
"thiserror",
]
@ -1873,7 +1991,7 @@ version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
dependencies = [
"num-traits",
"num-traits 0.2.18",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
@ -2056,6 +2174,19 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi",
]
[[package]]
name = "rand"
version = "0.8.5"
@ -2064,7 +2195,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_core 0.6.4",
]
[[package]]
@ -2074,9 +2205,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
"rand_core 0.6.4",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rand_core"
version = "0.6.4"
@ -2106,6 +2252,15 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
@ -2291,7 +2446,7 @@ checksum = "e09bbcb5003282bcb688f0bae741b278e9c7e8f378f561522c9806c58e075d9b"
dependencies = [
"anyhow",
"chrono",
"rand",
"rand 0.8.5",
]
[[package]]
@ -2410,9 +2565,9 @@ dependencies = [
[[package]]
name = "rustls-pki-types"
version = "1.4.0"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "868e20fada228fefaf6b652e00cc73623d54f8171e7352c18bb281571f2d92da"
checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
[[package]]
name = "rustls-webpki"
@ -2550,9 +2705,9 @@ dependencies = [
[[package]]
name = "serde_bytes"
version = "0.11.14"
version = "0.11.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734"
checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a"
dependencies = [
"serde",
]
@ -3306,7 +3461,7 @@ dependencies = [
"http 1.1.0",
"httparse",
"log",
"rand",
"rand 0.8.5",
"rustls 0.23.7",
"rustls-pki-types",
"sha1",
@ -3570,6 +3725,7 @@ dependencies = [
"js-sys",
"kcl-lib",
"kittycad",
"kittycad-modeling-cmds",
"pretty_assertions",
"reqwest",
"serde_json",

View File

@ -27,6 +27,7 @@ anyhow = "1"
hyper = { version = "0.14.29", features = ["server", "http1"] }
image = { version = "0.25.1", default-features = false, features = ["png"] }
kittycad = { workspace = true, default-features = true }
kittycad-modeling-cmds = { workspace = true }
pretty_assertions = "1.4.1"
reqwest = { version = "0.11.26", default-features = false }
tokio = { version = "1.40.0", features = ["rt-multi-thread", "macros", "time"] }
@ -72,6 +73,7 @@ members = [
http = "0.2.12"
kittycad = { version = "0.3.20", default-features = false, features = ["js", "requests"] }
kittycad-modeling-session = "0.1.4"
kittycad-modeling-cmds = { version = "0.2.59", features = ["websocket"] }
[[test]]
name = "executor"

View File

@ -28,6 +28,7 @@ gltf-json = "1.4.1"
http = { workspace = true }
image = { version = "0.25.1", default-features = false, features = ["png"] }
kittycad = { workspace = true }
kittycad-modeling-cmds = { workspace = true }
lazy_static = "1.5.0"
measurements = "0.11.0"
mime_guess = "2.0.5"

View File

@ -1,6 +1,11 @@
use std::sync::Arc;
use kittycad::types::{ModelingCmd, Point3D};
use kcmc::each_cmd as mcmd;
use kcmc::ok_response::OkModelingCmdResponse;
use kcmc::shared::PathCommand;
use kcmc::websocket::OkWebSocketResponseData;
use kcmc::ModelingCmd;
use kittycad_modeling_cmds as kcmc;
use crate::{
ast::types::{
@ -12,13 +17,15 @@ use crate::{
executor::{Point2d, SourceRange},
};
type Point3d = kcmc::shared::Point3d<f64>;
#[derive(Debug)]
/// The control point data for a curve or line.
pub struct ControlPointData {
/// The control points for the curve or line.
pub points: Vec<kittycad::types::Point3D>,
pub points: Vec<Point3d>,
/// The command that created this curve or line.
pub command: kittycad::types::PathCommand,
pub command: PathCommand,
/// The id of the curve or line.
pub id: uuid::Uuid,
}
@ -77,12 +84,12 @@ pub async fn modify_ast_for_sketch(
.send_modeling_cmd(
uuid::Uuid::new_v4(),
SourceRange::default(),
ModelingCmd::PathGetInfo { path_id: sketch_id },
ModelingCmd::PathGetInfo(mcmd::PathGetInfo { path_id: sketch_id }),
)
.await?;
let kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::PathGetInfo { data: path_info },
let OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::PathGetInfo(path_info),
} = &resp
else {
return Err(KclError::Engine(KclErrorDetails {
@ -101,11 +108,13 @@ pub async fn modify_ast_for_sketch(
let h = engine.send_modeling_cmd(
uuid::Uuid::new_v4(),
SourceRange::default(),
ModelingCmd::CurveGetControlPoints { curve_id: *command_id },
ModelingCmd::from(mcmd::CurveGetControlPoints {
curve_id: (*command_id).into(),
}),
);
let kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::CurveGetControlPoints { data },
let OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::CurveGetControlPoints(data),
} = h.await?
else {
return Err(KclError::Engine(KclErrorDetails {
@ -116,8 +125,8 @@ pub async fn modify_ast_for_sketch(
control_points.push(ControlPointData {
points: data.control_points.clone(),
command: segment.command.clone(),
id: *command_id,
command: segment.command,
id: (*command_id).into(),
});
}
}
@ -143,7 +152,7 @@ pub async fn modify_ast_for_sketch(
(control_point.points[1].x - last_point.x),
(control_point.points[1].y - last_point.y),
]);
last_point = Point3D {
last_point = Point3d {
x: control_point.points[1].x,
y: control_point.points[1].y,
z: control_point.points[1].z,
@ -151,7 +160,7 @@ pub async fn modify_ast_for_sketch(
}
// Okay now let's recalculate the sketch from the control points.
let start_sketch_at_end = Point3D {
let start_sketch_at_end = Point3d {
x: (first_control_points.points[1].x - first_control_points.points[0].x),
y: (first_control_points.points[1].y - first_control_points.points[0].y),
z: (first_control_points.points[1].z - first_control_points.points[0].z),

View File

@ -9,7 +9,12 @@ use std::{
use anyhow::{anyhow, Result};
use dashmap::DashMap;
use futures::{SinkExt, StreamExt};
use kittycad::types::{ModelingSessionData, WebSocketRequest, WebSocketResponse};
use kcmc::websocket::{
BatchResponse, FailureWebSocketResponse, ModelingCmdReq, ModelingSessionData, OkWebSocketResponseData,
SuccessWebSocketResponse, WebSocketRequest, WebSocketResponse,
};
use kcmc::ModelingCmd;
use kittycad_modeling_cmds as kcmc;
use tokio::sync::{mpsc, oneshot, RwLock};
use tokio_tungstenite::tungstenite::Message as WsMsg;
@ -118,10 +123,10 @@ impl EngineConnection {
async fn start_write_actor(mut tcp_write: WebSocketTcpWrite, mut engine_req_rx: mpsc::Receiver<ToEngineReq>) {
while let Some(req) = engine_req_rx.recv().await {
let ToEngineReq { req, request_sent } = req;
let res = if let kittycad::types::WebSocketRequest::ModelingCmdReq {
cmd: kittycad::types::ModelingCmd::ImportFiles { .. },
let res = if let WebSocketRequest::ModelingCmdReq(ModelingCmdReq {
cmd: ModelingCmd::ImportFiles { .. },
cmd_id: _,
} = &req
}) = &req
{
// Send it as binary.
Self::inner_send_to_engine_binary(req, &mut tcp_write).await
@ -196,43 +201,51 @@ impl EngineConnection {
match tcp_read.read().await {
Ok(ws_resp) => {
// If we got a batch response, add all the inner responses.
match &ws_resp.resp {
Some(kittycad::types::OkWebSocketResponseData::ModelingBatch { responses }) => {
let id = ws_resp.request_id();
match &ws_resp {
WebSocketResponse::Success(SuccessWebSocketResponse {
resp: OkWebSocketResponseData::ModelingBatch { responses },
..
}) => {
for (resp_id, batch_response) in responses {
let id: uuid::Uuid = resp_id.parse().unwrap();
if let Some(response) = &batch_response.response {
let id: uuid::Uuid = (*resp_id).into();
match batch_response {
BatchResponse::Success { response } => {
responses_clone.insert(
id,
kittycad::types::WebSocketResponse {
WebSocketResponse::Success(SuccessWebSocketResponse {
success: true,
request_id: Some(id),
resp: Some(kittycad::types::OkWebSocketResponseData::Modeling {
resp: OkWebSocketResponseData::Modeling {
modeling_response: response.clone(),
}),
errors: None,
success: Some(true),
},
}),
);
} else {
}
BatchResponse::Failure { errors } => {
responses_clone.insert(
id,
kittycad::types::WebSocketResponse {
WebSocketResponse::Failure(FailureWebSocketResponse {
success: false,
request_id: Some(id),
resp: None,
errors: batch_response.errors.clone(),
success: Some(false),
},
errors: errors.clone(),
}),
);
}
}
}
Some(kittycad::types::OkWebSocketResponseData::ModelingSessionData { session }) => {
}
WebSocketResponse::Success(SuccessWebSocketResponse {
resp: OkWebSocketResponseData::ModelingSessionData { session },
..
}) => {
let mut sd = session_data2.lock().unwrap();
sd.replace(session.clone());
}
_ => {}
}
if let Some(id) = ws_resp.request_id {
if let Some(id) = id {
responses_clone.insert(id, ws_resp.clone());
}
}
@ -299,7 +312,7 @@ impl EngineManager for EngineConnection {
&self,
id: uuid::Uuid,
source_range: crate::executor::SourceRange,
cmd: kittycad::types::WebSocketRequest,
cmd: WebSocketRequest,
_id_to_source_range: std::collections::HashMap<uuid::Uuid, crate::executor::SourceRange>,
) -> Result<WebSocketResponse, KclError> {
let (tx, rx) = oneshot::channel();

View File

@ -7,7 +7,12 @@ use std::{
};
use anyhow::Result;
use kittycad::types::{OkWebSocketResponseData, WebSocketRequest, WebSocketResponse};
use kcmc::ok_response::OkModelingCmdResponse;
use kcmc::websocket::{
BatchResponse, ModelingBatch, OkWebSocketResponseData, SuccessWebSocketResponse, WebSocketRequest,
WebSocketResponse,
};
use kittycad_modeling_cmds::{self as kcmc};
use crate::{errors::KclError, executor::DefaultPlanes};
@ -48,41 +53,38 @@ impl crate::engine::EngineManager for EngineConnection {
&self,
id: uuid::Uuid,
_source_range: crate::executor::SourceRange,
cmd: kittycad::types::WebSocketRequest,
cmd: WebSocketRequest,
_id_to_source_range: std::collections::HashMap<uuid::Uuid, crate::executor::SourceRange>,
) -> Result<WebSocketResponse, KclError> {
match cmd {
WebSocketRequest::ModelingCmdBatchReq {
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch {
ref requests,
batch_id: _,
responses: _,
} => {
}) => {
// Create the empty responses.
let mut responses = HashMap::new();
for request in requests {
responses.insert(
request.cmd_id.to_string(),
kittycad::types::BatchResponse {
response: Some(kittycad::types::OkModelingCmdResponse::Empty {}),
errors: None,
request.cmd_id,
BatchResponse::Success {
response: OkModelingCmdResponse::Empty {},
},
);
}
Ok(WebSocketResponse {
Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
request_id: Some(id),
resp: Some(OkWebSocketResponseData::ModelingBatch { responses }),
success: Some(true),
errors: None,
})
resp: OkWebSocketResponseData::ModelingBatch { responses },
success: true,
}))
}
_ => Ok(WebSocketResponse {
_ => Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
request_id: Some(id),
resp: Some(OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::Empty {},
}),
success: Some(true),
errors: None,
}),
resp: OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::Empty {},
},
success: true,
})),
}
}
}

View File

@ -6,7 +6,8 @@ use std::{
};
use anyhow::Result;
use kittycad::types::WebSocketRequest;
use kcmc::websocket::{WebSocketRequest, WebSocketResponse};
use kittycad_modeling_cmds as kcmc;
use wasm_bindgen::prelude::*;
use crate::{
@ -137,9 +138,9 @@ impl crate::engine::EngineManager for EngineConnection {
&self,
id: uuid::Uuid,
source_range: crate::executor::SourceRange,
cmd: kittycad::types::WebSocketRequest,
cmd: WebSocketRequest,
id_to_source_range: std::collections::HashMap<uuid::Uuid, crate::executor::SourceRange>,
) -> Result<kittycad::types::WebSocketResponse, KclError> {
) -> Result<WebSocketResponse, KclError> {
let source_range_str = serde_json::to_string(&source_range).map_err(|e| {
KclError::Engine(KclErrorDetails {
message: format!("Failed to serialize source range: {:?}", e),
@ -184,7 +185,7 @@ impl crate::engine::EngineManager for EngineConnection {
})
})?;
let ws_result: kittycad::types::WebSocketResponse = serde_json::from_str(&s).map_err(|e| {
let ws_result: WebSocketResponse = serde_json::from_str(&s).map_err(|e| {
KclError::Engine(KclErrorDetails {
message: format!("Failed to deserialize response from engine: {:?}", e),
source_ranges: vec![source_range],

View File

@ -13,11 +13,19 @@ use std::{
sync::{Arc, Mutex},
};
use kittycad::types::{
Color, ModelingCmd, ModelingCmdReq, ModelingSessionData, OkWebSocketResponseData, WebSocketRequest,
use kcmc::each_cmd as mcmd;
use kcmc::length_unit::LengthUnit;
use kcmc::ok_response::OkModelingCmdResponse;
use kcmc::shared::Color;
use kcmc::websocket::ModelingBatch;
use kcmc::websocket::{
BatchResponse, ModelingCmdReq, ModelingSessionData, OkWebSocketResponseData, WebSocketRequest, WebSocketResponse,
};
use kcmc::ModelingCmd;
use kittycad_modeling_cmds as kcmc;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::{
errors::{KclError, KclErrorDetails},
@ -33,12 +41,10 @@ lazy_static::lazy_static! {
#[async_trait::async_trait]
pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
/// Get the batch of commands to be sent to the engine.
fn batch(&self) -> Arc<Mutex<Vec<(kittycad::types::WebSocketRequest, crate::executor::SourceRange)>>>;
fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, crate::executor::SourceRange)>>>;
/// Get the batch of end commands to be sent to the engine.
fn batch_end(
&self,
) -> Arc<Mutex<HashMap<uuid::Uuid, (kittycad::types::WebSocketRequest, crate::executor::SourceRange)>>>;
fn batch_end(&self) -> Arc<Mutex<HashMap<uuid::Uuid, (WebSocketRequest, crate::executor::SourceRange)>>>;
/// Get the default planes.
async fn default_planes(
@ -58,15 +64,15 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
&self,
id: uuid::Uuid,
source_range: crate::executor::SourceRange,
cmd: kittycad::types::WebSocketRequest,
id_to_source_range: std::collections::HashMap<uuid::Uuid, crate::executor::SourceRange>,
) -> Result<kittycad::types::WebSocketResponse, crate::errors::KclError>;
cmd: WebSocketRequest,
id_to_source_range: HashMap<uuid::Uuid, crate::executor::SourceRange>,
) -> Result<kcmc::websocket::WebSocketResponse, crate::errors::KclError>;
async fn clear_scene(&self, source_range: crate::executor::SourceRange) -> Result<(), crate::errors::KclError> {
self.batch_modeling_cmd(
uuid::Uuid::new_v4(),
source_range,
&kittycad::types::ModelingCmd::SceneClearAll {},
&ModelingCmd::SceneClearAll(mcmd::SceneClearAll {}),
)
.await?;
@ -85,12 +91,12 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
&self,
id: uuid::Uuid,
source_range: crate::executor::SourceRange,
cmd: &kittycad::types::ModelingCmd,
cmd: &ModelingCmd,
) -> Result<(), crate::errors::KclError> {
let req = WebSocketRequest::ModelingCmdReq {
let req = WebSocketRequest::ModelingCmdReq(ModelingCmdReq {
cmd: cmd.clone(),
cmd_id: id,
};
cmd_id: id.into(),
});
// Add cmd to the batch.
self.batch().lock().unwrap().push((req, source_range));
@ -105,12 +111,12 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
&self,
id: uuid::Uuid,
source_range: crate::executor::SourceRange,
cmd: &kittycad::types::ModelingCmd,
cmd: &ModelingCmd,
) -> Result<(), crate::errors::KclError> {
let req = WebSocketRequest::ModelingCmdReq {
let req = WebSocketRequest::ModelingCmdReq(ModelingCmdReq {
cmd: cmd.clone(),
cmd_id: id,
};
cmd_id: id.into(),
});
// Add cmd to the batch end.
self.batch_end().lock().unwrap().insert(id, (req, source_range));
@ -124,8 +130,8 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
&self,
id: uuid::Uuid,
source_range: crate::executor::SourceRange,
cmd: kittycad::types::ModelingCmd,
) -> Result<kittycad::types::OkWebSocketResponseData, crate::errors::KclError> {
cmd: ModelingCmd,
) -> Result<OkWebSocketResponseData, crate::errors::KclError> {
self.batch_modeling_cmd(id, source_range, &cmd).await?;
// Flush the batch queue.
@ -139,7 +145,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
// We only do this at the very end of the file.
batch_end: bool,
source_range: crate::executor::SourceRange,
) -> Result<kittycad::types::OkWebSocketResponseData, crate::errors::KclError> {
) -> Result<OkWebSocketResponseData, crate::errors::KclError> {
let all_requests = if batch_end {
let mut requests = self.batch().lock().unwrap().clone();
requests.extend(self.batch_end().lock().unwrap().values().cloned());
@ -151,14 +157,14 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
// Return early if we have no commands to send.
if all_requests.is_empty() {
return Ok(OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::Empty {},
modeling_response: OkModelingCmdResponse::Empty {},
});
}
let requests: Vec<ModelingCmdReq> = all_requests
.iter()
.filter_map(|(val, _)| match val {
WebSocketRequest::ModelingCmdReq { cmd, cmd_id } => Some(kittycad::types::ModelingCmdReq {
WebSocketRequest::ModelingCmdReq(ModelingCmdReq { cmd, cmd_id }) => Some(ModelingCmdReq {
cmd: cmd.clone(),
cmd_id: *cmd_id,
}),
@ -166,11 +172,11 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
})
.collect();
let batched_requests = WebSocketRequest::ModelingCmdBatchReq {
let batched_requests = WebSocketRequest::ModelingCmdBatchReq(ModelingBatch {
requests,
batch_id: uuid::Uuid::new_v4(),
batch_id: uuid::Uuid::new_v4().into(),
responses: true,
};
});
let final_req = if all_requests.len() == 1 {
// We can unwrap here because we know the batch has only one element.
@ -181,11 +187,11 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
// Create the map of original command IDs to source range.
// This is for the wasm side, kurt needs it for selections.
let mut id_to_source_range = std::collections::HashMap::new();
let mut id_to_source_range = HashMap::new();
for (req, range) in all_requests.iter() {
match req {
WebSocketRequest::ModelingCmdReq { cmd: _, cmd_id } => {
id_to_source_range.insert(*cmd_id, *range);
WebSocketRequest::ModelingCmdReq(ModelingCmdReq { cmd: _, cmd_id }) => {
id_to_source_range.insert(Uuid::from(*cmd_id), *range);
}
_ => {
return Err(KclError::Engine(KclErrorDetails {
@ -204,21 +210,22 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
// We pop off the responses to cleanup our mappings.
match final_req {
WebSocketRequest::ModelingCmdBatchReq {
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch {
ref requests,
batch_id,
responses: _,
} => {
}) => {
// Get the last command ID.
let last_id = requests.last().unwrap().cmd_id;
let ws_resp = self
.inner_send_modeling_cmd(batch_id, source_range, final_req, id_to_source_range.clone())
.inner_send_modeling_cmd(batch_id.into(), source_range, final_req, id_to_source_range.clone())
.await?;
let response = self.parse_websocket_response(ws_resp, source_range)?;
// If we have a batch response, we want to return the specific id we care about.
if let kittycad::types::OkWebSocketResponseData::ModelingBatch { responses } = &response {
self.parse_batch_responses(last_id, id_to_source_range, responses.clone())
if let OkWebSocketResponseData::ModelingBatch { responses } = response {
let responses = responses.into_iter().map(|(k, v)| (Uuid::from(k), v)).collect();
self.parse_batch_responses(last_id.into(), id_to_source_range, responses)
} else {
// We should never get here.
Err(KclError::Engine(KclErrorDetails {
@ -227,7 +234,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
}))
}
}
WebSocketRequest::ModelingCmdReq { cmd: _, cmd_id } => {
WebSocketRequest::ModelingCmdReq(ModelingCmdReq { cmd: _, cmd_id }) => {
// You are probably wondering why we can't just return the source range we were
// passed with the function. Well this is actually really important.
// If this is the last command in the batch and there is only one and we've reached
@ -235,14 +242,14 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
// send default or the end of the file as it's source range not the origin of the
// request so we need the original request source range in case the engine returns
// an error.
let source_range = id_to_source_range.get(&cmd_id).cloned().ok_or_else(|| {
let source_range = id_to_source_range.get(cmd_id.as_ref()).cloned().ok_or_else(|| {
KclError::Engine(KclErrorDetails {
message: format!("Failed to get source range for command ID: {:?}", cmd_id),
source_ranges: vec![],
})
})?;
let ws_resp = self
.inner_send_modeling_cmd(cmd_id, source_range, final_req, id_to_source_range)
.inner_send_modeling_cmd(cmd_id.into(), source_range, final_req, id_to_source_range)
.await?;
self.parse_websocket_response(ws_resp, source_range)
}
@ -268,14 +275,14 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
self.batch_modeling_cmd(
plane_id,
source_range,
&ModelingCmd::MakePlane {
&ModelingCmd::from(mcmd::MakePlane {
clobber: false,
origin: default_origin,
size: default_size,
size: LengthUnit(default_size),
x_axis: x_axis.into(),
y_axis: y_axis.into(),
hide: Some(true),
},
}),
)
.await?;
@ -284,7 +291,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
self.batch_modeling_cmd(
uuid::Uuid::new_v4(),
source_range,
&ModelingCmd::PlaneSetColor { color, plane_id },
&ModelingCmd::from(mcmd::PlaneSetColor { color, plane_id }),
)
.await?;
}
@ -394,46 +401,47 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
fn parse_websocket_response(
&self,
response: kittycad::types::WebSocketResponse,
response: WebSocketResponse,
source_range: crate::executor::SourceRange,
) -> Result<kittycad::types::OkWebSocketResponseData, crate::errors::KclError> {
if let Some(data) = &response.resp {
Ok(data.clone())
} else if let Some(errors) = &response.errors {
) -> Result<OkWebSocketResponseData, crate::errors::KclError> {
match response {
WebSocketResponse::Success(success) => Ok(success.resp),
WebSocketResponse::Failure(fail) => {
let _request_id = fail.request_id;
Err(KclError::Engine(KclErrorDetails {
message: format!("Modeling command failed: {:?}", errors),
source_ranges: vec![source_range],
}))
} else {
// We should never get here.
Err(KclError::Engine(KclErrorDetails {
message: "Modeling command failed: no response or errors".to_string(),
message: format!("Modeling command failed: {:?}", fail.errors),
source_ranges: vec![source_range],
}))
}
}
}
fn parse_batch_responses(
&self,
// The last response we are looking for.
id: uuid::Uuid,
// The mapping of source ranges to command IDs.
id_to_source_range: std::collections::HashMap<uuid::Uuid, crate::executor::SourceRange>,
id_to_source_range: HashMap<uuid::Uuid, crate::executor::SourceRange>,
// The response from the engine.
responses: HashMap<String, kittycad::types::BatchResponse>,
) -> Result<kittycad::types::OkWebSocketResponseData, crate::errors::KclError> {
responses: HashMap<uuid::Uuid, BatchResponse>,
) -> Result<OkWebSocketResponseData, crate::errors::KclError> {
// Iterate over the responses and check for errors.
for (cmd_id, resp) in responses.iter() {
let cmd_id = uuid::Uuid::parse_str(cmd_id).map_err(|e| {
KclError::Engine(KclErrorDetails {
message: format!("Failed to parse command ID: {:?}", e),
source_ranges: vec![id_to_source_range[&id]],
})
})?;
if let Some(errors) = resp.errors.as_ref() {
match resp {
BatchResponse::Success { response } => {
if cmd_id == &id {
// This is the response we care about.
return Ok(OkWebSocketResponseData::Modeling {
modeling_response: response.clone(),
});
} else {
// Continue the loop if this is not the response we care about.
continue;
}
}
BatchResponse::Failure { errors } => {
// Get the source range for the command.
let source_range = id_to_source_range.get(&cmd_id).cloned().ok_or_else(|| {
let source_range = id_to_source_range.get(cmd_id).cloned().ok_or_else(|| {
KclError::Engine(KclErrorDetails {
message: format!("Failed to get source range for command ID: {:?}", cmd_id),
source_ranges: vec![],
@ -444,16 +452,6 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
source_ranges: vec![source_range],
}));
}
if let Some(response) = resp.response.as_ref() {
if cmd_id == id {
// This is the response we care about.
return Ok(kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: response.clone(),
});
} else {
// Continue the loop if this is not the response we care about.
continue;
}
}
}
@ -470,10 +468,10 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
self.batch_modeling_cmd(
uuid::Uuid::new_v4(),
Default::default(),
&ModelingCmd::ObjectVisible {
&ModelingCmd::from(mcmd::ObjectVisible {
hidden,
object_id: *GRID_OBJECT_ID,
},
}),
)
.await?;
@ -481,10 +479,10 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
self.batch_modeling_cmd(
uuid::Uuid::new_v4(),
Default::default(),
&ModelingCmd::ObjectVisible {
&ModelingCmd::from(mcmd::ObjectVisible {
hidden,
object_id: *GRID_SCALE_TEXT_OBJECT_ID,
},
}),
)
.await?;

View File

@ -4,13 +4,21 @@ use std::{collections::HashMap, sync::Arc};
use anyhow::Result;
use async_recursion::async_recursion;
use kittycad::types::ModelingSessionData;
use kcmc::each_cmd as mcmd;
use kcmc::ok_response::{output::TakeSnapshot, OkModelingCmdResponse};
use kcmc::websocket::{ModelingSessionData, OkWebSocketResponseData};
use kcmc::{ImageFormat, ModelingCmd};
use kittycad_modeling_cmds as kcmc;
use kittycad_modeling_cmds::length_unit::LengthUnit;
use parse_display::{Display, FromStr};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value as JValue;
use tower_lsp::lsp_types::{Position as LspPosition, Range as LspRange};
type Point2D = kcmc::shared::Point2d<f64>;
type Point3D = kcmc::shared::Point3d<f64>;
use crate::{
ast::types::{
human_friendly_type, BodyItem, Expr, ExpressionStatement, FunctionExpression, KclNone, Program,
@ -1284,7 +1292,7 @@ impl From<Point2d> for [f64; 2] {
}
}
impl From<Point2d> for kittycad::types::Point2D {
impl From<Point2d> for Point2D {
fn from(p: Point2d) -> Self {
Self { x: p.x, y: p.y }
}
@ -1315,11 +1323,20 @@ impl Point3d {
}
}
impl From<Point3d> for kittycad::types::Point3D {
impl From<Point3d> for Point3D {
fn from(p: Point3d) -> Self {
Self { x: p.x, y: p.y, z: p.z }
}
}
impl From<Point3d> for kittycad_modeling_cmds::shared::Point3d<LengthUnit> {
fn from(p: Point3d) -> Self {
Self {
x: LengthUnit(p.x),
y: LengthUnit(p.y),
z: LengthUnit(p.z),
}
}
}
/// Metadata.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
@ -1687,9 +1704,9 @@ impl ExecutorContext {
.batch_modeling_cmd(
uuid::Uuid::new_v4(),
SourceRange::default(),
&kittycad::types::ModelingCmd::EdgeLinesVisible {
&ModelingCmd::from(mcmd::EdgeLinesVisible {
hidden: !settings.highlight_edges,
},
}),
)
.await?;
@ -1774,9 +1791,16 @@ impl ExecutorContext {
.batch_modeling_cmd(
uuid::Uuid::new_v4(),
SourceRange::default(),
&kittycad::types::ModelingCmd::SetSceneUnits {
unit: self.settings.units.into(),
&ModelingCmd::from(mcmd::SetSceneUnits {
unit: match self.settings.units {
UnitLength::Cm => kcmc::units::UnitLength::Centimeters,
UnitLength::Ft => kcmc::units::UnitLength::Feet,
UnitLength::In => kcmc::units::UnitLength::Inches,
UnitLength::M => kcmc::units::UnitLength::Meters,
UnitLength::Mm => kcmc::units::UnitLength::Millimeters,
UnitLength::Yd => kcmc::units::UnitLength::Yards,
},
}),
)
.await?;
let memory = if let Some(memory) = memory {
@ -1927,7 +1951,7 @@ impl ExecutorContext {
}
/// Execute the program, then get a PNG screenshot.
pub async fn execute_and_prepare_snapshot(&self, program: &Program) -> Result<kittycad::types::TakeSnapshot> {
pub async fn execute_and_prepare_snapshot(&self, program: &Program) -> Result<TakeSnapshot> {
let _ = self.run(program, None).await?;
// Zoom to fit.
@ -1935,10 +1959,11 @@ impl ExecutorContext {
.send_modeling_cmd(
uuid::Uuid::new_v4(),
crate::executor::SourceRange::default(),
kittycad::types::ModelingCmd::ZoomToFit {
ModelingCmd::from(mcmd::ZoomToFit {
object_ids: Default::default(),
animated: false,
padding: 0.1,
},
}),
)
.await?;
@ -1948,19 +1973,19 @@ impl ExecutorContext {
.send_modeling_cmd(
uuid::Uuid::new_v4(),
crate::executor::SourceRange::default(),
kittycad::types::ModelingCmd::TakeSnapshot {
format: kittycad::types::ImageFormat::Png,
},
ModelingCmd::from(mcmd::TakeSnapshot {
format: ImageFormat::Png,
}),
)
.await?;
let kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
let OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::TakeSnapshot(contents),
} = resp
else {
anyhow::bail!("Unexpected response from engine: {:?}", resp);
};
Ok(data)
Ok(contents)
}
}

View File

@ -1,7 +1,9 @@
use std::any::type_name;
use anyhow::Result;
use kittycad::types::OkWebSocketResponseData;
use kcmc::websocket::OkWebSocketResponseData;
use kcmc::ModelingCmd;
use kittycad_modeling_cmds as kcmc;
use serde::de::DeserializeOwned;
use crate::{
@ -51,7 +53,7 @@ impl Args {
pub(crate) async fn batch_modeling_cmd(
&self,
id: uuid::Uuid,
cmd: kittycad::types::ModelingCmd,
cmd: ModelingCmd,
) -> Result<(), crate::errors::KclError> {
self.ctx.engine.batch_modeling_cmd(id, self.source_range, &cmd).await
}
@ -59,11 +61,7 @@ impl Args {
// Add a modeling command to the batch that gets executed at the end of the file.
// This is good for something like fillet or chamfer where the engine would
// eat the path id if we executed it right away.
pub(crate) async fn batch_end_cmd(
&self,
id: uuid::Uuid,
cmd: kittycad::types::ModelingCmd,
) -> Result<(), crate::errors::KclError> {
pub(crate) async fn batch_end_cmd(&self, id: uuid::Uuid, cmd: ModelingCmd) -> Result<(), crate::errors::KclError> {
self.ctx.engine.batch_end_cmd(id, self.source_range, &cmd).await
}
@ -71,7 +69,7 @@ impl Args {
pub(crate) async fn send_modeling_cmd(
&self,
id: uuid::Uuid,
cmd: kittycad::types::ModelingCmd,
cmd: ModelingCmd,
) -> Result<OkWebSocketResponseData, KclError> {
self.ctx.engine.send_modeling_cmd(id, self.source_range, cmd).await
}

View File

@ -2,7 +2,10 @@
use anyhow::Result;
use derive_docs::stdlib;
use kittycad::types::ModelingCmd;
use kcmc::each_cmd as mcmd;
use kcmc::length_unit::LengthUnit;
use kcmc::{shared::CutType, ModelingCmd};
use kittycad_modeling_cmds as kcmc;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@ -137,14 +140,14 @@ async fn inner_chamfer(
let id = uuid::Uuid::new_v4();
args.batch_end_cmd(
id,
ModelingCmd::Solid3DFilletEdge {
ModelingCmd::from(mcmd::Solid3dFilletEdge {
edge_id,
object_id: extrude_group.id,
radius: data.length,
tolerance: DEFAULT_TOLERANCE, // We can let the user set this in the future.
cut_type: Some(kittycad::types::CutType::Chamfer),
radius: LengthUnit(data.length),
tolerance: LengthUnit(DEFAULT_TOLERANCE), // We can let the user set this in the future.
cut_type: CutType::Chamfer,
face_id: Some(id),
},
}),
)
.await?;

View File

@ -4,7 +4,12 @@ use std::collections::HashMap;
use anyhow::Result;
use derive_docs::stdlib;
use kittycad::types::{ExtrusionFaceCapType, ExtrusionFaceInfo};
use kcmc::each_cmd as mcmd;
use kcmc::length_unit::LengthUnit;
use kcmc::ok_response::OkModelingCmdResponse;
use kcmc::websocket::OkWebSocketResponseData;
use kcmc::{output::ExtrusionFaceInfo, shared::ExtrusionFaceCapType, ModelingCmd};
use kittycad_modeling_cmds as kcmc;
use schemars::JsonSchema;
use uuid::Uuid;
@ -86,7 +91,7 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args
// We do this here in case extrude is called out of order.
args.batch_modeling_cmd(
uuid::Uuid::new_v4(),
kittycad::types::ModelingCmd::EnableSketchMode {
ModelingCmd::from(mcmd::EnableSketchMode {
animated: false,
ortho: false,
entity_id: sketch_group.on.id(),
@ -97,21 +102,24 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args
} else {
None
},
},
}),
)
.await?;
args.batch_modeling_cmd(
id,
kittycad::types::ModelingCmd::Extrude {
target: sketch_group.id,
distance: length,
},
ModelingCmd::from(mcmd::Extrude {
target: sketch_group.id.into(),
distance: LengthUnit(length),
}),
)
.await?;
// Disable the sketch mode.
args.batch_modeling_cmd(uuid::Uuid::new_v4(), kittycad::types::ModelingCmd::SketchModeDisable {})
args.batch_modeling_cmd(
uuid::Uuid::new_v4(),
ModelingCmd::SketchModeDisable(mcmd::SketchModeDisable {}),
)
.await?;
extrude_groups.push(do_post_extrude(sketch_group.clone(), length, args.clone()).await?);
}
@ -128,9 +136,9 @@ pub(crate) async fn do_post_extrude(
// See: https://github.com/KittyCAD/modeling-app/issues/806
args.batch_modeling_cmd(
uuid::Uuid::new_v4(),
kittycad::types::ModelingCmd::ObjectBringToFront {
ModelingCmd::from(mcmd::ObjectBringToFront {
object_id: sketch_group.id,
},
}),
)
.await?;
@ -166,15 +174,15 @@ pub(crate) async fn do_post_extrude(
let solid3d_info = args
.send_modeling_cmd(
uuid::Uuid::new_v4(),
kittycad::types::ModelingCmd::Solid3DGetExtrusionFaceInfo {
ModelingCmd::from(mcmd::Solid3dGetExtrusionFaceInfo {
edge_id,
object_id: sketch_group.id,
},
}),
)
.await?;
let face_infos = if let kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::Solid3DGetExtrusionFaceInfo { data },
let face_infos = if let OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::Solid3dGetExtrusionFaceInfo(data),
} = solid3d_info
{
data.faces
@ -199,21 +207,21 @@ pub(crate) async fn do_post_extrude(
// uses this to build the artifact graph, which the UI needs.
args.batch_modeling_cmd(
uuid::Uuid::new_v4(),
kittycad::types::ModelingCmd::Solid3DGetOppositeEdge {
ModelingCmd::from(mcmd::Solid3dGetOppositeEdge {
edge_id: curve_id,
object_id: sketch_group.id,
face_id,
},
}),
)
.await?;
args.batch_modeling_cmd(
uuid::Uuid::new_v4(),
kittycad::types::ModelingCmd::Solid3DGetPrevAdjacentEdge {
ModelingCmd::from(mcmd::Solid3dGetPrevAdjacentEdge {
edge_id: curve_id,
object_id: sketch_group.id,
face_id,
},
}),
)
.await?;
}

View File

@ -2,7 +2,12 @@
use anyhow::Result;
use derive_docs::stdlib;
use kittycad::types::ModelingCmd;
use kcmc::each_cmd as mcmd;
use kcmc::length_unit::LengthUnit;
use kcmc::ok_response::OkModelingCmdResponse;
use kcmc::websocket::OkWebSocketResponseData;
use kcmc::{shared::CutType, ModelingCmd};
use kittycad_modeling_cmds as kcmc;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
@ -139,14 +144,14 @@ async fn inner_fillet(
let id = uuid::Uuid::new_v4();
args.batch_end_cmd(
id,
ModelingCmd::Solid3DFilletEdge {
ModelingCmd::from(mcmd::Solid3dFilletEdge {
edge_id,
object_id: extrude_group.id,
radius: data.radius,
tolerance: data.tolerance.unwrap_or(default_tolerance(&args.ctx.settings.units)),
cut_type: Some(kittycad::types::CutType::Fillet),
radius: LengthUnit(data.radius),
tolerance: LengthUnit(data.tolerance.unwrap_or(default_tolerance(&args.ctx.settings.units))),
cut_type: CutType::Fillet,
face_id: None,
},
}),
)
.await?;
@ -231,19 +236,19 @@ async fn inner_get_opposite_edge(tag: TagIdentifier, exec_state: &mut ExecState,
let resp = args
.send_modeling_cmd(
uuid::Uuid::new_v4(),
ModelingCmd::Solid3DGetOppositeEdge {
ModelingCmd::from(mcmd::Solid3dGetOppositeEdge {
edge_id: tagged_path.id,
object_id: tagged_path.sketch_group,
face_id,
},
}),
)
.await?;
let kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::Solid3DGetOppositeEdge { data: opposite_edge },
let OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::Solid3dGetOppositeEdge(opposite_edge),
} = &resp
else {
return Err(KclError::Engine(KclErrorDetails {
message: format!("Solid3DGetOppositeEdge response was not as expected: {:?}", resp),
message: format!("mcmd::Solid3dGetOppositeEdge response was not as expected: {:?}", resp),
source_ranges: vec![args.source_range],
}));
};
@ -312,24 +317,27 @@ async fn inner_get_next_adjacent_edge(
let resp = args
.send_modeling_cmd(
uuid::Uuid::new_v4(),
ModelingCmd::Solid3DGetNextAdjacentEdge {
ModelingCmd::from(mcmd::Solid3dGetNextAdjacentEdge {
edge_id: tagged_path.id,
object_id: tagged_path.sketch_group,
face_id,
},
}),
)
.await?;
let kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::Solid3DGetNextAdjacentEdge { data: ajacent_edge },
let OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::Solid3dGetNextAdjacentEdge(adjacent_edge),
} = &resp
else {
return Err(KclError::Engine(KclErrorDetails {
message: format!("Solid3DGetNextAdjacentEdge response was not as expected: {:?}", resp),
message: format!(
"mcmd::Solid3dGetNextAdjacentEdge response was not as expected: {:?}",
resp
),
source_ranges: vec![args.source_range],
}));
};
ajacent_edge.edge.ok_or_else(|| {
adjacent_edge.edge.ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("No edge found next adjacent to tag: `{}`", tag.value),
source_ranges: vec![args.source_range],
@ -398,24 +406,27 @@ async fn inner_get_previous_adjacent_edge(
let resp = args
.send_modeling_cmd(
uuid::Uuid::new_v4(),
ModelingCmd::Solid3DGetPrevAdjacentEdge {
ModelingCmd::from(mcmd::Solid3dGetPrevAdjacentEdge {
edge_id: tagged_path.id,
object_id: tagged_path.sketch_group,
face_id,
},
}),
)
.await?;
let kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::Solid3DGetPrevAdjacentEdge { data: ajacent_edge },
let OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::Solid3dGetPrevAdjacentEdge(adjacent_edge),
} = &resp
else {
return Err(KclError::Engine(KclErrorDetails {
message: format!("Solid3DGetPrevAdjacentEdge response was not as expected: {:?}", resp),
message: format!(
"mcmd::Solid3dGetPrevAdjacentEdge response was not as expected: {:?}",
resp
),
source_ranges: vec![args.source_range],
}));
};
ajacent_edge.edge.ok_or_else(|| {
adjacent_edge.edge.ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("No edge found previous adjacent to tag: `{}`", tag.value),
source_ranges: vec![args.source_range],

View File

@ -2,7 +2,11 @@
use anyhow::Result;
use derive_docs::stdlib;
use kittycad::types::ModelingCmd;
use kcmc::each_cmd as mcmd;
use kcmc::length_unit::LengthUnit;
use kcmc::shared::Angle;
use kcmc::ModelingCmd;
use kittycad_modeling_cmds as kcmc;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@ -61,13 +65,13 @@ async fn inner_helix(
let id = uuid::Uuid::new_v4();
args.batch_modeling_cmd(
id,
ModelingCmd::EntityMakeHelix {
ModelingCmd::from(mcmd::EntityMakeHelix {
cylinder_id: extrude_group.id,
is_clockwise: !data.ccw,
length: data.length.unwrap_or(extrude_group.height),
length: LengthUnit(data.length.unwrap_or(extrude_group.height)),
revolutions: data.revolutions,
start_angle: kittycad::types::Angle::from_degrees(data.angle_start),
},
start_angle: Angle::from_degrees(data.angle_start),
}),
)
.await?;

View File

@ -4,7 +4,15 @@ use std::str::FromStr;
use anyhow::Result;
use derive_docs::stdlib;
use kittycad::types::ModelingCmd;
use kcmc::coord::{Axis, AxisDirectionPair, Direction, System};
use kcmc::each_cmd as mcmd;
use kcmc::format::InputFormat;
use kcmc::ok_response::OkModelingCmdResponse;
use kcmc::shared::FileImportFormat;
use kcmc::units::UnitLength;
use kcmc::websocket::OkWebSocketResponseData;
use kcmc::{ImportFile, ModelingCmd};
use kittycad_modeling_cmds as kcmc;
use schemars::JsonSchema;
use crate::{
@ -19,14 +27,14 @@ use crate::{
// * Forward: -Y
// * Up: +Z
// * Handedness: Right
const ZOO_COORD_SYSTEM: kittycad::types::System = kittycad::types::System {
forward: kittycad::types::AxisDirectionPair {
axis: kittycad::types::Axis::Y,
direction: kittycad::types::Direction::Negative,
const ZOO_COORD_SYSTEM: System = System {
forward: AxisDirectionPair {
axis: Axis::Y,
direction: Direction::Negative,
},
up: kittycad::types::AxisDirectionPair {
axis: kittycad::types::Axis::Z,
direction: kittycad::types::Direction::Positive,
up: AxisDirectionPair {
axis: Axis::Z,
direction: Direction::Positive,
},
};
@ -47,22 +55,22 @@ pub enum ImportFormat {
Obj {
/// Co-ordinate system of input data.
/// Defaults to the [KittyCAD co-ordinate system.
coords: Option<kittycad::types::System>,
coords: Option<System>,
/// The units of the input data. This is very important for correct scaling and when
/// calculating physics properties like mass, etc.
/// Defaults to millimeters.
units: kittycad::types::UnitLength,
units: UnitLength,
},
/// The PLY Polygon File Format.
#[serde(rename = "ply")]
Ply {
/// Co-ordinate system of input data.
/// Defaults to the [KittyCAD co-ordinate system.
coords: Option<kittycad::types::System>,
coords: Option<System>,
/// The units of the input data. This is very important for correct scaling and when
/// calculating physics properties like mass, etc.
/// Defaults to millimeters.
units: kittycad::types::UnitLength,
units: UnitLength,
},
/// SolidWorks part (SLDPRT) format.
#[serde(rename = "sldprt")]
@ -75,37 +83,37 @@ pub enum ImportFormat {
Stl {
/// Co-ordinate system of input data.
/// Defaults to the [KittyCAD co-ordinate system.
coords: Option<kittycad::types::System>,
coords: Option<System>,
/// The units of the input data. This is very important for correct scaling and when
/// calculating physics properties like mass, etc.
/// Defaults to millimeters.
units: kittycad::types::UnitLength,
units: UnitLength,
},
}
impl From<ImportFormat> for kittycad::types::InputFormat {
impl From<ImportFormat> for InputFormat {
fn from(format: ImportFormat) -> Self {
match format {
ImportFormat::Fbx {} => kittycad::types::InputFormat::Fbx {},
ImportFormat::Gltf {} => kittycad::types::InputFormat::Gltf {},
ImportFormat::Obj { coords, units } => kittycad::types::InputFormat::Obj {
ImportFormat::Fbx {} => InputFormat::Fbx(Default::default()),
ImportFormat::Gltf {} => InputFormat::Gltf(Default::default()),
ImportFormat::Obj { coords, units } => InputFormat::Obj(kcmc::format::obj::import::Options {
coords: coords.unwrap_or(ZOO_COORD_SYSTEM),
units,
},
ImportFormat::Ply { coords, units } => kittycad::types::InputFormat::Ply {
}),
ImportFormat::Ply { coords, units } => InputFormat::Ply(kcmc::format::ply::import::Options {
coords: coords.unwrap_or(ZOO_COORD_SYSTEM),
units,
},
ImportFormat::Sldprt {} => kittycad::types::InputFormat::Sldprt {
}),
ImportFormat::Sldprt {} => InputFormat::Sldprt(kcmc::format::sldprt::import::Options {
split_closed_faces: false,
},
ImportFormat::Step {} => kittycad::types::InputFormat::Step {
}),
ImportFormat::Step {} => InputFormat::Step(kcmc::format::step::import::Options {
split_closed_faces: false,
},
ImportFormat::Stl { coords, units } => kittycad::types::InputFormat::Stl {
}),
ImportFormat::Stl { coords, units } => InputFormat::Stl(kcmc::format::stl::import::Options {
coords: coords.unwrap_or(ZOO_COORD_SYSTEM),
units,
},
}),
}
}
}
@ -194,7 +202,7 @@ async fn inner_import(
// Get the format type from the extension of the file.
let format = if let Some(options) = options {
// Validate the given format with the extension format.
let format: kittycad::types::InputFormat = options.into();
let format: InputFormat = options.into();
validate_extension_format(ext_format, format.clone()).map_err(|e| {
KclError::Semantic(KclErrorDetails {
message: e.to_string(),
@ -224,14 +232,14 @@ async fn inner_import(
source_ranges: vec![args.source_range],
})
})?;
let mut import_files = vec![kittycad::types::ImportFile {
let mut import_files = vec![kcmc::ImportFile {
path: file_name.to_string(),
data: file_contents.clone(),
}];
// In the case of a gltf importing a bin file we need to handle that! and figure out where the
// file is relative to our current file.
if let kittycad::types::InputFormat::Gltf {} = format {
if let InputFormat::Gltf(..) = format {
// Check if the file is a binary gltf file, in that case we don't need to import the bin
// file.
if !file_contents.starts_with(b"glTF") {
@ -265,7 +273,7 @@ async fn inner_import(
})
})?;
import_files.push(kittycad::types::ImportFile {
import_files.push(ImportFile {
path: uri.to_string(),
data: bin_contents,
});
@ -287,15 +295,15 @@ async fn inner_import(
let resp = args
.send_modeling_cmd(
id,
ModelingCmd::ImportFiles {
ModelingCmd::from(mcmd::ImportFiles {
files: import_files.clone(),
format,
},
}),
)
.await?;
let kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::ImportFiles { data: imported_files },
let OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::ImportFiles(imported_files),
} = &resp
else {
return Err(KclError::Engine(KclErrorDetails {
@ -312,14 +320,14 @@ async fn inner_import(
}
/// Get the source format from the extension.
fn get_import_format_from_extension(ext: &str) -> Result<kittycad::types::InputFormat> {
let format = match kittycad::types::FileImportFormat::from_str(ext) {
fn get_import_format_from_extension(ext: &str) -> Result<InputFormat> {
let format = match FileImportFormat::from_str(ext) {
Ok(format) => format,
Err(_) => {
if ext == "stp" {
kittycad::types::FileImportFormat::Step
FileImportFormat::Step
} else if ext == "glb" {
kittycad::types::FileImportFormat::Gltf
FileImportFormat::Gltf
} else {
anyhow::bail!("unknown source format for file extension: {}. Try setting the `--src-format` flag explicitly or use a valid format.", ext)
}
@ -327,7 +335,7 @@ fn get_import_format_from_extension(ext: &str) -> Result<kittycad::types::InputF
};
// Make the default units millimeters.
let ul = kittycad::types::UnitLength::Mm;
let ul = UnitLength::Millimeters;
// Zoo co-ordinate system.
//
@ -335,44 +343,44 @@ fn get_import_format_from_extension(ext: &str) -> Result<kittycad::types::InputF
// * Up: +Z
// * Handedness: Right
match format {
kittycad::types::FileImportFormat::Step => Ok(kittycad::types::InputFormat::Step {
FileImportFormat::Step => Ok(InputFormat::Step(kcmc::format::step::import::Options {
split_closed_faces: false,
}),
kittycad::types::FileImportFormat::Stl => Ok(kittycad::types::InputFormat::Stl {
})),
FileImportFormat::Stl => Ok(InputFormat::Stl(kcmc::format::stl::import::Options {
coords: ZOO_COORD_SYSTEM,
units: ul,
}),
kittycad::types::FileImportFormat::Obj => Ok(kittycad::types::InputFormat::Obj {
})),
FileImportFormat::Obj => Ok(InputFormat::Obj(kcmc::format::obj::import::Options {
coords: ZOO_COORD_SYSTEM,
units: ul,
}),
kittycad::types::FileImportFormat::Gltf => Ok(kittycad::types::InputFormat::Gltf {}),
kittycad::types::FileImportFormat::Ply => Ok(kittycad::types::InputFormat::Ply {
})),
FileImportFormat::Gltf => Ok(InputFormat::Gltf(kcmc::format::gltf::import::Options {})),
FileImportFormat::Ply => Ok(InputFormat::Ply(kcmc::format::ply::import::Options {
coords: ZOO_COORD_SYSTEM,
units: ul,
}),
kittycad::types::FileImportFormat::Fbx => Ok(kittycad::types::InputFormat::Fbx {}),
kittycad::types::FileImportFormat::Sldprt => Ok(kittycad::types::InputFormat::Sldprt {
})),
FileImportFormat::Fbx => Ok(InputFormat::Fbx(kcmc::format::fbx::import::Options {})),
FileImportFormat::Sldprt => Ok(InputFormat::Sldprt(kcmc::format::sldprt::import::Options {
split_closed_faces: false,
}),
})),
}
}
fn validate_extension_format(ext: kittycad::types::InputFormat, given: kittycad::types::InputFormat) -> Result<()> {
if let kittycad::types::InputFormat::Stl { coords: _, units: _ } = ext {
if let kittycad::types::InputFormat::Stl { coords: _, units: _ } = given {
fn validate_extension_format(ext: InputFormat, given: InputFormat) -> Result<()> {
if let InputFormat::Stl(_) = ext {
if let InputFormat::Stl(_) = given {
return Ok(());
}
}
if let kittycad::types::InputFormat::Obj { coords: _, units: _ } = ext {
if let kittycad::types::InputFormat::Obj { coords: _, units: _ } = given {
if let InputFormat::Obj(_) = ext {
if let InputFormat::Obj(_) = given {
return Ok(());
}
}
if let kittycad::types::InputFormat::Ply { coords: _, units: _ } = ext {
if let kittycad::types::InputFormat::Ply { coords: _, units: _ } = given {
if let InputFormat::Ply(_) = ext {
if let InputFormat::Ply(_) = given {
return Ok(());
}
}
@ -388,14 +396,14 @@ fn validate_extension_format(ext: kittycad::types::InputFormat, given: kittycad:
)
}
fn get_name_of_format(type_: kittycad::types::InputFormat) -> String {
fn get_name_of_format(type_: InputFormat) -> &'static str {
match type_ {
kittycad::types::InputFormat::Fbx {} => "fbx".to_string(),
kittycad::types::InputFormat::Gltf {} => "gltf".to_string(),
kittycad::types::InputFormat::Obj { coords: _, units: _ } => "obj".to_string(),
kittycad::types::InputFormat::Ply { coords: _, units: _ } => "ply".to_string(),
kittycad::types::InputFormat::Sldprt { split_closed_faces: _ } => "sldprt".to_string(),
kittycad::types::InputFormat::Step { split_closed_faces: _ } => "step".to_string(),
kittycad::types::InputFormat::Stl { coords: _, units: _ } => "stl".to_string(),
InputFormat::Fbx(_) => "fbx",
InputFormat::Gltf(_) => "gltf",
InputFormat::Obj(_) => "obj",
InputFormat::Ply(_) => "ply",
InputFormat::Sldprt(_) => "sldprt",
InputFormat::Step(_) => "step",
InputFormat::Stl(_) => "stl",
}
}

View File

@ -2,7 +2,10 @@
use anyhow::Result;
use derive_docs::stdlib;
use kittycad::types::ModelingCmd;
use kcmc::each_cmd as mcmd;
use kcmc::length_unit::LengthUnit;
use kcmc::ModelingCmd;
use kittycad_modeling_cmds as kcmc;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@ -156,16 +159,15 @@ async fn inner_loft(
let id = uuid::Uuid::new_v4();
args.batch_modeling_cmd(
id,
ModelingCmd::Loft {
ModelingCmd::from(mcmd::Loft {
section_ids: sketch_groups.iter().map(|group| group.id).collect(),
base_curve_index: data.base_curve_index,
bez_approximate_rational: data.bez_approximate_rational.unwrap_or(false),
tolerance: data.tolerance.unwrap_or(default_tolerance(&args.ctx.settings.units)),
tolerance: LengthUnit(data.tolerance.unwrap_or(default_tolerance(&args.ctx.settings.units))),
v_degree: data
.v_degree
.unwrap_or_else(|| std::num::NonZeroU32::new(DEFAULT_V_DEGREE).unwrap())
.into(),
},
.unwrap_or_else(|| std::num::NonZeroU32::new(DEFAULT_V_DEGREE).unwrap()),
}),
)
.await?;

View File

@ -2,7 +2,13 @@
use anyhow::Result;
use derive_docs::stdlib;
use kittycad::types::ModelingCmd;
use kcmc::each_cmd as mcmd;
use kcmc::length_unit::LengthUnit;
use kcmc::ok_response::OkModelingCmdResponse;
use kcmc::shared::Transform;
use kcmc::websocket::OkWebSocketResponseData;
use kcmc::ModelingCmd;
use kittycad_modeling_cmds::{self as kcmc};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@ -163,7 +169,7 @@ async fn inner_pattern_transform<'a>(
async fn send_pattern_transform(
// This should be passed via reference, see
// https://github.com/KittyCAD/modeling-app/issues/2821
transform: Vec<kittycad::types::Transform>,
transform: Vec<Transform>,
extrude_group: &ExtrudeGroup,
args: &Args,
) -> Result<Vec<Box<ExtrudeGroup>>, KclError> {
@ -172,15 +178,15 @@ async fn send_pattern_transform(
let resp = args
.send_modeling_cmd(
id,
ModelingCmd::EntityLinearPatternTransform {
ModelingCmd::from(mcmd::EntityLinearPatternTransform {
entity_id: extrude_group.id,
transform,
},
}),
)
.await?;
let kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::EntityLinearPatternTransform { data: pattern_info },
let OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::EntityLinearPatternTransform(pattern_info),
} = &resp
else {
return Err(KclError::Engine(KclErrorDetails {
@ -203,7 +209,7 @@ async fn make_transform<'a>(
transform_function: &FunctionParam<'a>,
source_range: SourceRange,
exec_state: &mut ExecState,
) -> Result<kittycad::types::Transform, KclError> {
) -> Result<Transform, KclError> {
// Call the transform fn for this repetition.
let repetition_num = KclValue::UserVal(UserVal {
value: serde_json::Value::Number(i.into()),
@ -247,12 +253,12 @@ async fn make_transform<'a>(
Some(x) => array_to_point3d(x, source_ranges.clone())?,
None => Point3d { x: 0.0, y: 0.0, z: 0.0 },
};
let t = kittycad::types::Transform {
let t = Transform {
replicate,
scale: Some(scale.into()),
translate: Some(translate.into()),
scale: scale.into(),
translate: translate.into(),
// TODO: chalmers to pipe thru to kcl.
rotation: None,
rotation: Default::default(),
};
Ok(t)
}
@ -451,21 +457,17 @@ async fn pattern_linear(data: LinearPattern, geometry: Geometry, args: Args) ->
let resp = args
.send_modeling_cmd(
id,
ModelingCmd::EntityLinearPattern {
axis: kittycad::types::Point3D {
x: data.axis()[0],
y: data.axis()[1],
z: data.axis()[2],
},
ModelingCmd::from(mcmd::EntityLinearPattern {
axis: kcmc::shared::Point3d::from(data.axis()),
entity_id: geometry.id(),
num_repetitions: data.repetitions(),
spacing: data.distance(),
},
spacing: LengthUnit(data.distance()),
}),
)
.await?;
let kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::EntityLinearPattern { data: pattern_info },
let OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::EntityLinearPattern(pattern_info),
} = &resp
else {
return Err(KclError::Engine(KclErrorDetails {
@ -713,26 +715,27 @@ async fn inner_pattern_circular_3d(
async fn pattern_circular(data: CircularPattern, geometry: Geometry, args: Args) -> Result<Geometries, KclError> {
let id = uuid::Uuid::new_v4();
let center = data.center();
let resp = args
.send_modeling_cmd(
id,
ModelingCmd::EntityCircularPattern {
axis: kittycad::types::Point3D {
x: data.axis()[0],
y: data.axis()[1],
z: data.axis()[2],
},
ModelingCmd::from(mcmd::EntityCircularPattern {
axis: kcmc::shared::Point3d::from(data.axis()),
entity_id: geometry.id(),
center: data.center().into(),
center: kcmc::shared::Point3d {
x: LengthUnit(center[0]),
y: LengthUnit(center[1]),
z: LengthUnit(center[2]),
},
num_repetitions: data.repetitions(),
arc_degrees: data.arc_degrees(),
rotate_duplicates: data.rotate_duplicates(),
},
}),
)
.await?;
let kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::EntityCircularPattern { data: pattern_info },
let OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::EntityCircularPattern(pattern_info),
} = &resp
else {
return Err(KclError::Engine(KclErrorDetails {

View File

@ -2,7 +2,11 @@
use anyhow::Result;
use derive_docs::stdlib;
use kittycad::types::ModelingCmd;
use kcmc::each_cmd as mcmd;
use kcmc::length_unit::LengthUnit;
use kcmc::shared::Angle;
use kcmc::ModelingCmd;
use kittycad_modeling_cmds::{self as kcmc};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@ -74,7 +78,7 @@ pub enum RevolveAxisAndOrigin {
impl RevolveAxisAndOrigin {
/// Get the axis and origin.
pub fn axis_and_origin(&self) -> Result<(kittycad::types::Point3D, kittycad::types::Point3D), KclError> {
pub fn axis_and_origin(&self) -> Result<(kcmc::shared::Point3d<f64>, kcmc::shared::Point3d<LengthUnit>), KclError> {
let (axis, origin) = match self {
RevolveAxisAndOrigin::X => ([1.0, 0.0, 0.0], [0.0, 0.0, 0.0]),
RevolveAxisAndOrigin::Y => ([0.0, 1.0, 0.0], [0.0, 0.0, 0.0]),
@ -86,15 +90,15 @@ impl RevolveAxisAndOrigin {
};
Ok((
kittycad::types::Point3D {
kcmc::shared::Point3d {
x: axis[0],
y: axis[1],
z: axis[2],
},
kittycad::types::Point3D {
x: origin[0],
y: origin[1],
z: origin[2],
kcmc::shared::Point3d {
x: LengthUnit(origin[0]),
y: LengthUnit(origin[1]),
z: LengthUnit(origin[2]),
},
))
}
@ -263,7 +267,7 @@ async fn inner_revolve(
}
}
let angle = kittycad::types::Angle::from_degrees(data.angle.unwrap_or(360.0));
let angle = Angle::from_degrees(data.angle.unwrap_or(360.0));
let id = uuid::Uuid::new_v4();
match data.axis {
@ -271,14 +275,14 @@ async fn inner_revolve(
let (axis, origin) = axis.axis_and_origin()?;
args.batch_modeling_cmd(
id,
ModelingCmd::Revolve {
ModelingCmd::from(mcmd::Revolve {
angle,
target: sketch_group.id,
target: sketch_group.id.into(),
axis,
origin,
tolerance: data.tolerance.unwrap_or(default_tolerance(&args.ctx.settings.units)),
tolerance: LengthUnit(data.tolerance.unwrap_or(default_tolerance(&args.ctx.settings.units))),
axis_is_2d: true,
},
}),
)
.await?;
}
@ -289,12 +293,12 @@ async fn inner_revolve(
};
args.batch_modeling_cmd(
id,
ModelingCmd::RevolveAboutEdge {
ModelingCmd::from(mcmd::RevolveAboutEdge {
angle,
target: sketch_group.id,
target: sketch_group.id.into(),
edge_id,
tolerance: data.tolerance.unwrap_or(default_tolerance(&args.ctx.settings.units)),
},
tolerance: LengthUnit(data.tolerance.unwrap_or(default_tolerance(&args.ctx.settings.units))),
}),
)
.await?;
}

View File

@ -251,7 +251,7 @@ fn inner_segment_angle(tag: &TagIdentifier, exec_state: &mut ExecState, args: Ar
let result = between(path.from.into(), path.to.into());
Ok(result.degrees())
Ok(result.to_degrees())
}
/// Returns the angle to match the given length for x.

View File

@ -2,7 +2,10 @@
use anyhow::Result;
use derive_docs::stdlib;
use kittycad::types::ModelingCmd;
use kcmc::each_cmd as mcmd;
use kcmc::length_unit::LengthUnit;
use kcmc::ModelingCmd;
use kittycad_modeling_cmds as kcmc;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@ -230,12 +233,12 @@ async fn inner_shell(
args.batch_modeling_cmd(
uuid::Uuid::new_v4(),
ModelingCmd::Solid3DShellFace {
ModelingCmd::from(mcmd::Solid3dShellFace {
hollow: false,
face_ids,
object_id: extrude_groups[0].id,
shell_thickness: data.thickness,
},
shell_thickness: LengthUnit(data.thickness),
}),
)
.await?;
@ -316,12 +319,12 @@ async fn inner_hollow(
args.batch_modeling_cmd(
uuid::Uuid::new_v4(),
ModelingCmd::Solid3DShellFace {
ModelingCmd::from(mcmd::Solid3dShellFace {
hollow: true,
face_ids: Vec::new(), // This is empty because we want to hollow the entire object.
object_id: extrude_group.id,
shell_thickness: thickness,
},
shell_thickness: LengthUnit(thickness),
}),
)
.await?;

View File

@ -4,7 +4,13 @@ use std::collections::HashMap;
use anyhow::Result;
use derive_docs::stdlib;
use kittycad::types::{Angle, ModelingCmd, Point3D};
use kcmc::each_cmd as mcmd;
use kcmc::length_unit::LengthUnit;
use kcmc::shared::Angle;
use kcmc::shared::Point2d as KPoint2d; // Point2d is already defined in this pkg, to impl ts_rs traits.
use kcmc::ModelingCmd;
use kittycad_modeling_cmds as kcmc;
use kittycad_modeling_cmds::shared::PathSegment;
use parse_display::{Display, FromStr};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@ -124,17 +130,13 @@ async fn inner_line_to(
args.batch_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,
},
ModelingCmd::from(mcmd::ExtendPath {
path: sketch_group.id.into(),
segment: PathSegment::Line {
end: KPoint2d::from(to).with_z(0.0).map(LengthUnit),
relative: false,
},
},
}),
)
.await?;
@ -297,17 +299,13 @@ async fn inner_line(
args.batch_modeling_cmd(
id,
ModelingCmd::ExtendPath {
path: sketch_group.id,
segment: kittycad::types::PathSegment::Line {
end: Point3D {
x: delta[0],
y: delta[1],
z: 0.0,
},
ModelingCmd::from(mcmd::ExtendPath {
path: sketch_group.id.into(),
segment: PathSegment::Line {
end: KPoint2d::from(delta).with_z(0.0).map(LengthUnit),
relative: true,
},
},
}),
)
.await?;
@ -484,17 +482,13 @@ async fn inner_angled_line(
args.batch_modeling_cmd(
id,
ModelingCmd::ExtendPath {
path: sketch_group.id,
segment: kittycad::types::PathSegment::Line {
end: Point3D {
x: delta[0],
y: delta[1],
z: 0.0,
},
ModelingCmd::from(mcmd::ExtendPath {
path: sketch_group.id.into(),
segment: PathSegment::Line {
end: KPoint2d::from(delta).with_z(0.0).map(LengthUnit),
relative,
},
},
}),
)
.await?;
@ -1192,14 +1186,14 @@ async fn start_sketch_on_plane(data: PlaneData, args: &Args) -> Result<Box<Plane
let id = uuid::Uuid::new_v4();
args.batch_modeling_cmd(
id,
ModelingCmd::MakePlane {
ModelingCmd::from(mcmd::MakePlane {
clobber: false,
origin: (*origin).into(),
size: 60.0,
size: LengthUnit(60.0),
x_axis: (*x_axis).into(),
y_axis: (*y_axis).into(),
hide: Some(true),
},
}),
)
.await?;
@ -1275,7 +1269,7 @@ pub(crate) async fn inner_start_profile_at(
let id = uuid::Uuid::new_v4();
args.batch_modeling_cmd(
id,
ModelingCmd::EnableSketchMode {
ModelingCmd::from(mcmd::EnableSketchMode {
animated: false,
ortho: false,
entity_id: sketch_surface.id(),
@ -1286,24 +1280,21 @@ pub(crate) async fn inner_start_profile_at(
} else {
None
},
},
}),
)
.await?;
let id = uuid::Uuid::new_v4();
let path_id = uuid::Uuid::new_v4();
args.batch_modeling_cmd(path_id, ModelingCmd::StartPath {}).await?;
args.batch_modeling_cmd(path_id, ModelingCmd::from(mcmd::StartPath {}))
.await?;
args.batch_modeling_cmd(
id,
ModelingCmd::MovePathPen {
path: path_id,
to: Point3D {
x: to[0],
y: to[1],
z: 0.0,
},
},
ModelingCmd::from(mcmd::MovePathPen {
path: path_id.into(),
to: KPoint2d::from(to).with_z(0.0).map(LengthUnit),
}),
)
.await?;
@ -1466,16 +1457,19 @@ pub(crate) async fn inner_close(
args.batch_modeling_cmd(
id,
ModelingCmd::ClosePath {
ModelingCmd::from(mcmd::ClosePath {
path_id: sketch_group.id,
},
}),
)
.await?;
// If we are sketching on a plane we can close the sketch group now.
if let SketchSurface::Plane(_) = sketch_group.on {
// We were on a plane, disable the sketch mode.
args.batch_modeling_cmd(uuid::Uuid::new_v4(), kittycad::types::ModelingCmd::SketchModeDisable {})
args.batch_modeling_cmd(
uuid::Uuid::new_v4(),
ModelingCmd::SketchModeDisable(mcmd::SketchModeDisable {}),
)
.await?;
}
@ -1600,16 +1594,16 @@ pub(crate) async fn inner_arc(
args.batch_modeling_cmd(
id,
ModelingCmd::ExtendPath {
path: sketch_group.id,
segment: kittycad::types::PathSegment::Arc {
ModelingCmd::from(mcmd::ExtendPath {
path: sketch_group.id.into(),
segment: PathSegment::Arc {
start: angle_start,
end: angle_end,
center: center.into(),
radius,
center: KPoint2d::from(center).map(LengthUnit),
radius: LengthUnit(radius),
relative: false,
},
},
}),
)
.await?;
@ -1715,7 +1709,7 @@ async fn inner_tangential_arc(
));
// make sure the arc center is on the correct side to guarantee deterministic behavior
// note the engine automatically rejects an offset of zero, if we want to flag that at KCL too to avoid engine errors
let ccw = offset.degrees() > 0.0;
let ccw = offset.to_degrees() > 0.0;
let tangent_to_arc_start_angle = if ccw {
// CCW turn
Angle::from_degrees(-90.0)
@ -1731,10 +1725,13 @@ async fn inner_tangential_arc(
args.batch_modeling_cmd(
id,
ModelingCmd::ExtendPath {
path: sketch_group.id,
segment: kittycad::types::PathSegment::TangentialArc { radius, offset },
ModelingCmd::from(mcmd::ExtendPath {
path: sketch_group.id.into(),
segment: PathSegment::TangentialArc {
radius: LengthUnit(radius),
offset,
},
}),
)
.await?;
(center, to.into(), ccw)
@ -1766,17 +1763,13 @@ async fn inner_tangential_arc(
}
fn tan_arc_to(sketch_group: &SketchGroup, to: &[f64; 2]) -> ModelingCmd {
ModelingCmd::ExtendPath {
path: sketch_group.id,
segment: kittycad::types::PathSegment::TangentialArcTo {
ModelingCmd::from(mcmd::ExtendPath {
path: sketch_group.id.into(),
segment: PathSegment::TangentialArcTo {
angle_snap_increment: None,
to: Point3D {
x: to[0],
y: to[1],
z: 0.0,
to: KPoint2d::from(*to).with_z(0.0).map(LengthUnit),
},
},
}
})
}
/// Draw a tangential arc to a specific point.
@ -1936,7 +1929,7 @@ async fn inner_tangential_arc_to_relative(
metadata: args.source_range.into(),
},
},
center: dbg!(result.center),
center: result.center,
ccw: result.ccw > 0,
};
@ -2009,27 +2002,15 @@ async fn inner_bezier_curve(
args.batch_modeling_cmd(
id,
ModelingCmd::ExtendPath {
path: sketch_group.id,
segment: kittycad::types::PathSegment::Bezier {
control_1: Point3D {
x: data.control1[0],
y: data.control1[1],
z: 0.0,
},
control_2: Point3D {
x: data.control2[0],
y: data.control2[1],
z: 0.0,
},
end: Point3D {
x: delta[0],
y: delta[1],
z: 0.0,
},
ModelingCmd::from(mcmd::ExtendPath {
path: sketch_group.id.into(),
segment: PathSegment::Bezier {
control1: KPoint2d::from(data.control1).with_z(0.0).map(LengthUnit),
control2: KPoint2d::from(data.control2).with_z(0.0).map(LengthUnit),
end: KPoint2d::from(delta).with_z(0.0).map(LengthUnit),
relative,
},
},
}),
)
.await?;
@ -2106,10 +2087,10 @@ async fn inner_hole(
for hole_sketch_group in hole_sketch_groups {
args.batch_modeling_cmd(
uuid::Uuid::new_v4(),
ModelingCmd::Solid2DAddHole {
ModelingCmd::from(mcmd::Solid2dAddHole {
object_id: sketch_group.id,
hole_id: hole_sketch_group.id,
},
}),
)
.await?;
@ -2117,10 +2098,10 @@ async fn inner_hole(
// we also hide the source hole since its essentially "consumed" by this operation
args.batch_modeling_cmd(
uuid::Uuid::new_v4(),
ModelingCmd::ObjectVisible {
ModelingCmd::from(mcmd::ObjectVisible {
object_id: hole_sketch_group.id,
hidden: true,
},
}),
)
.await?;
}

View File

@ -1,6 +1,6 @@
use std::f64::consts::PI;
use kittycad::types::Angle;
use kittycad_modeling_cmds::shared::Angle;
use crate::{
errors::{KclError, KclErrorDetails},
@ -16,7 +16,7 @@ pub fn between(a: Point2d, b: Point2d) -> Angle {
/// Normalize the angle
pub fn normalize(angle: Angle) -> Angle {
let deg = angle.degrees();
let deg = angle.to_degrees();
let result = ((deg % 360.0) + 360.0) % 360.0;
Angle::from_degrees(if result > 180.0 { result - 360.0 } else { result })
}
@ -37,8 +37,8 @@ pub fn normalize(angle: Angle) -> Angle {
/// );
/// ```
pub fn delta(from_angle: Angle, to_angle: Angle) -> Angle {
let norm_from_angle = normalize_rad(from_angle.radians());
let norm_to_angle = normalize_rad(to_angle.radians());
let norm_from_angle = normalize_rad(from_angle.to_radians());
let norm_to_angle = normalize_rad(to_angle.to_radians());
let provisional = norm_to_angle - norm_from_angle;
if provisional > -PI && provisional <= PI {
@ -50,7 +50,7 @@ pub fn delta(from_angle: Angle, to_angle: Angle) -> Angle {
if provisional < -PI {
return Angle::from_radians(provisional + 2.0 * PI);
}
Angle::ZERO
Angle::default()
}
pub fn clockwise_sign(points: &[Point2d]) -> i32 {
@ -183,7 +183,7 @@ fn offset_line(offset: f64, p1: Point2d, p2: Point2d) -> [Point2d; 2] {
}
pub fn get_y_component(angle: Angle, x: f64) -> Point2d {
let normalised_angle = ((angle.degrees() % 360.0) + 360.0) % 360.0; // between 0 and 360
let normalised_angle = ((angle.to_degrees() % 360.0) + 360.0) % 360.0; // between 0 and 360
let y = x * f64::tan(normalised_angle.to_radians());
let sign = if normalised_angle > 90.0 && normalised_angle <= 270.0 {
-1.0
@ -194,7 +194,7 @@ pub fn get_y_component(angle: Angle, x: f64) -> Point2d {
}
pub fn get_x_component(angle: Angle, y: f64) -> Point2d {
let normalised_angle = ((angle.degrees() % 360.0) + 360.0) % 360.0; // between 0 and 360
let normalised_angle = ((angle.to_degrees() % 360.0) + 360.0) % 360.0; // between 0 and 360
let x = y / f64::tan(normalised_angle.to_radians());
let sign = if normalised_angle > 180.0 && normalised_angle <= 360.0 {
-1.0
@ -205,8 +205,8 @@ pub fn get_x_component(angle: Angle, y: f64) -> Point2d {
}
pub fn arc_center_and_end(from: Point2d, start_angle: Angle, end_angle: Angle, radius: f64) -> (Point2d, Point2d) {
let start_angle = start_angle.radians();
let end_angle = end_angle.radians();
let start_angle = start_angle.to_radians();
let end_angle = end_angle.to_radians();
let center = Point2d {
x: -1.0 * (radius * start_angle.cos() - from.x),
@ -304,7 +304,7 @@ mod tests {
assert_eq!(results, expected);
let result = get_y_component(Angle::ZERO, 1.0);
let result = get_y_component(Angle::zero(), 1.0);
assert_eq!(result.x as i32, 1);
assert_eq!(result.y as i32, 0);
@ -334,7 +334,7 @@ mod tests {
assert_eq!(results, expected);
let result = get_x_component(Angle::ZERO, 1.0);
let result = get_x_component(Angle::zero(), 1.0);
assert!(result.x > 100000.0);
assert_eq!(result.y as i32, 1);
@ -355,7 +355,7 @@ mod tests {
fn test_arc_center_and_end() {
let (center, end) = super::arc_center_and_end(
super::Point2d { x: 0.0, y: 0.0 },
Angle::ZERO,
Angle::zero(),
Angle::from_degrees(90.0),
1.0,
);
@ -366,7 +366,7 @@ mod tests {
let (center, end) = super::arc_center_and_end(
super::Point2d { x: 0.0, y: 0.0 },
Angle::ZERO,
Angle::zero(),
Angle::from_degrees(180.0),
1.0,
);
@ -377,7 +377,7 @@ mod tests {
let (center, end) = super::arc_center_and_end(
super::Point2d { x: 0.0, y: 0.0 },
Angle::ZERO,
Angle::zero(),
Angle::from_degrees(180.0),
10.0,
);
@ -397,8 +397,8 @@ mod tests {
SourceRange(Default::default()),
)
.unwrap();
assert_eq!(angle_start.degrees().round(), 0.0);
assert_eq!(angle_end.degrees().round(), 90.0);
assert_eq!(angle_start.to_degrees().round(), 0.0);
assert_eq!(angle_end.to_degrees().round(), 90.0);
let (angle_start, angle_end) = super::arc_angles(
super::Point2d { x: 0.0, y: 0.0 },
@ -408,8 +408,8 @@ mod tests {
SourceRange(Default::default()),
)
.unwrap();
assert_eq!(angle_start.degrees().round(), 0.0);
assert_eq!(angle_end.degrees().round(), 180.0);
assert_eq!(angle_start.to_degrees().round(), 0.0);
assert_eq!(angle_end.to_degrees().round(), 180.0);
let (angle_start, angle_end) = super::arc_angles(
super::Point2d { x: 0.0, y: 0.0 },
@ -419,8 +419,8 @@ mod tests {
SourceRange(Default::default()),
)
.unwrap();
assert_eq!(angle_start.degrees().round(), 0.0);
assert_eq!(angle_end.degrees().round(), 180.0);
assert_eq!(angle_start.to_degrees().round(), 0.0);
assert_eq!(angle_end.to_degrees().round(), 180.0);
let result = super::arc_angles(
super::Point2d { x: 0.0, y: 5.0 },
@ -435,8 +435,8 @@ mod tests {
} else {
panic!("Expected error");
}
assert_eq!(angle_start.degrees().round(), 0.0);
assert_eq!(angle_end.degrees().round(), 180.0);
assert_eq!(angle_start.to_degrees().round(), 0.0);
assert_eq!(angle_end.to_degrees().round(), 180.0);
}
}
@ -631,11 +631,11 @@ pub fn get_tangential_arc_to_info(input: TangentialArcInfoInput) -> TangentialAr
let arc_mid_angle = (arc_mid_point[1] - center[1]).atan2(arc_mid_point[0] - center[0]);
let start_to_mid_arc_length = radius
* delta(Angle::from_radians(start_angle), Angle::from_radians(arc_mid_angle))
.radians()
.to_radians()
.abs();
let mid_to_end_arc_length = radius
* delta(Angle::from_radians(arc_mid_angle), Angle::from_radians(end_angle))
.radians()
.to_radians()
.abs();
let arc_length = start_to_mid_arc_length + mid_to_end_arc_length;

View File

@ -3,7 +3,9 @@ use kcl_lib::{
ast::{modify::modify_ast_for_sketch, types::Program},
executor::{ExecutorContext, KclValue, PlaneType, SketchGroup, SourceRange},
};
use kittycad::types::{ModelingCmd, Point3D};
use kittycad_modeling_cmds::shared::Point3d;
use kittycad_modeling_cmds::ModelingCmd;
use kittycad_modeling_cmds::{each_cmd as mcmd, length_unit::LengthUnit};
use pretty_assertions::assert_eq;
/// Setup the engine and parse code for an ast.
@ -52,14 +54,14 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid
.send_modeling_cmd(
plane_id,
SourceRange::default(),
ModelingCmd::MakePlane {
ModelingCmd::from(mcmd::MakePlane {
clobber: false,
origin: Point3D { x: 0.0, y: 0.0, z: 0.0 },
size: 60.0,
x_axis: Point3D { x: 1.0, y: 0.0, z: 0.0 },
y_axis: Point3D { x: 0.0, y: 1.0, z: 0.0 },
origin: Point3d::default(),
size: LengthUnit(60.0),
x_axis: Point3d { x: 1.0, y: 0.0, z: 0.0 },
y_axis: Point3d { x: 0.0, y: 1.0, z: 0.0 },
hide: Some(true),
},
}),
)
.await?;
@ -70,13 +72,13 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid
.send_modeling_cmd(
uuid::Uuid::new_v4(),
SourceRange::default(),
ModelingCmd::EnableSketchMode {
ModelingCmd::from(mcmd::EnableSketchMode {
animated: false,
ortho: true,
entity_id: plane_id,
planar_normal: Some(Point3D { x: 0.0, y: 0.0, z: 1.0 }),
planar_normal: Some(Point3d { x: 0.0, y: 0.0, z: 1.0 }),
adjust_camera: false,
},
}),
)
.await?;