Initial kcl to core codegen (C++) util (#2999)

* wip on this

* wip on kcl-to-core tool

* Update conn_mock_core.rs

* stopping point

* wip

* use enum for is_mock for added versatility

* get at least circular patterns working to finish POC

* fmt

* fmt part 2

* Update Cargo.lock

* quick fix for tests

* quick fix

* more fixes

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* get more cases of this working

* Update src/wasm-lib/kcl-to-core/src/conn_mock_core.rs

Co-authored-by: Jonathan Tran <jonnytran@gmail.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* kcl core gen plane code by default

* fmt

* fix build?

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest)

* Fix clippy errors

* Remove unneeded clippy allow

* post merge build fix attempt

* Add missing indexmap dependency

* Fix to use IndexMap instead of HashMap

* Migrate to kittycad-modeling-cmds

* fix build

* fmt

* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)

* Revert "A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)"

This reverts commit 991cdde15e.

* Revert "A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest)"

This reverts commit a4ae03c740.

* Revert "A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest)"

This reverts commit 9a082e7c9f.

* Regenerate derive_docs

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest)

* Revert "A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest)"

This reverts commit a8bdb35627.

* Fix e2e test failing by masking state indicator

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest)

* Confirm snapshot change

* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)

* Confirm snapshot change

* Fix build from last merge with main

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
Co-authored-by: Frank Noirot <frank@zoo.dev>
This commit is contained in:
Mike Farrell
2024-10-01 12:45:01 -07:00
committed by GitHub
parent a284a270b7
commit 88cd27425e
30 changed files with 704 additions and 39 deletions

View File

@ -70,12 +70,55 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstream"
version = "0.6.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]] [[package]]
name = "anstyle" name = "anstyle"
version = "1.0.8" version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
[[package]]
name = "anstyle-parse"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
dependencies = [
"anstyle",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.89" version = "1.0.89"
@ -405,8 +448,12 @@ version = "4.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b"
dependencies = [ dependencies = [
"anstream",
"anstyle", "anstyle",
"clap_lex", "clap_lex",
"strsim",
"unicase",
"unicode-width",
] ]
[[package]] [[package]]
@ -427,6 +474,12 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]]
name = "colorchoice"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]] [[package]]
name = "colored" name = "colored"
version = "2.1.0" version = "2.1.0"
@ -1433,6 +1486,12 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.10.5" version = "0.10.5"
@ -1563,6 +1622,20 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "kcl-to-core"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"indexmap 2.5.0",
"kcl-lib",
"kittycad",
"kittycad-modeling-cmds",
"tokio",
"uuid",
]
[[package]] [[package]]
name = "kittycad" name = "kittycad"
version = "0.3.23" version = "0.3.23"
@ -1575,6 +1648,7 @@ dependencies = [
"bigdecimal", "bigdecimal",
"bytes", "bytes",
"chrono", "chrono",
"clap",
"data-encoding", "data-encoding",
"format_serde_error", "format_serde_error",
"futures", "futures",
@ -3348,6 +3422,7 @@ dependencies = [
"signal-hook-registry", "signal-hook-registry",
"socket2", "socket2",
"tokio-macros", "tokio-macros",
"tracing",
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
@ -3737,6 +3812,12 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.10.0" version = "1.10.0"

View File

@ -66,6 +66,7 @@ members = [
"kcl", "kcl",
"kcl-macros", "kcl-macros",
"kcl-test-server", "kcl-test-server",
"kcl-to-core",
] ]
[workspace.dependencies] [workspace.dependencies]

View File

@ -758,7 +758,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();

View File

@ -14,7 +14,7 @@ mod test_examples_someFn {
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();
} }

View File

@ -14,7 +14,7 @@ mod test_examples_someFn {
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();
} }

View File

@ -14,7 +14,7 @@ mod test_examples_show {
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();
} }
@ -47,7 +47,7 @@ mod test_examples_show {
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();
} }

View File

@ -14,7 +14,7 @@ mod test_examples_show {
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();
} }

View File

@ -15,7 +15,7 @@ mod test_examples_my_func {
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();
} }
@ -48,7 +48,7 @@ mod test_examples_my_func {
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();
} }

View File

@ -15,7 +15,7 @@ mod test_examples_line_to {
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();
} }
@ -48,7 +48,7 @@ mod test_examples_line_to {
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();
} }

View File

@ -14,7 +14,7 @@ mod test_examples_min {
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();
} }
@ -47,7 +47,7 @@ mod test_examples_min {
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();
} }

View File

@ -14,7 +14,7 @@ mod test_examples_show {
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();
} }

View File

@ -14,7 +14,7 @@ mod test_examples_import {
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();
} }

View File

@ -14,7 +14,7 @@ mod test_examples_import {
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();
} }

View File

@ -14,7 +14,7 @@ mod test_examples_import {
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();
} }

View File

@ -14,7 +14,7 @@ mod test_examples_show {
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();
} }

View File

@ -14,7 +14,7 @@ mod test_examples_some_function {
fs: std::sync::Arc::new(crate::fs::FileManager::new()), fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}; };
ctx.run(&program, None).await.unwrap(); ctx.run(&program, None).await.unwrap();
} }

View File

@ -0,0 +1,23 @@
[package]
name = "kcl-to-core"
description = "Utility methods to convert kcl to engine core executable tests"
version = "0.1.0"
edition = "2021"
license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app"
[lib]
[[bin]]
name = "kcl-to-core"
path = "src/tool.rs"
[dependencies]
anyhow = "1"
async-trait = "0.1.81"
indexmap = "2.5.0"
kcl-lib = { path = "../kcl" }
kittycad = { workspace = true, features = ["clap"] }
kittycad-modeling-cmds = { workspace = true }
tokio = { version = "1.38", features = ["full", "time", "rt", "tracing"] }
uuid = { version = "1.9.1", features = ["v4", "js", "serde"] }

View File

@ -0,0 +1,469 @@
use anyhow::Result;
use indexmap::IndexMap;
use kcl_lib::{errors::KclError, executor::DefaultPlanes};
use kittycad_modeling_cmds::{
self as kcmc,
id::ModelingCmdId,
ok_response::OkModelingCmdResponse,
shared::PathSegment::{self, *},
websocket::{ModelingBatch, ModelingCmdReq, OkWebSocketResponseData, WebSocketRequest, WebSocketResponse},
};
use std::{
collections::HashMap,
sync::{Arc, Mutex},
};
use tokio::sync::RwLock;
const CPP_PREFIX: &str = "const double scaleFactor = 100;\n";
const NEED_PLANES: bool = true;
#[derive(Debug, Clone)]
pub struct EngineConnection {
batch: Arc<Mutex<Vec<(WebSocketRequest, kcl_lib::executor::SourceRange)>>>,
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::executor::SourceRange)>>>,
core_test: Arc<Mutex<String>>,
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
}
impl EngineConnection {
pub async fn new(result: Arc<Mutex<String>>) -> Result<EngineConnection> {
if let Ok(mut code) = result.lock() {
code.push_str(CPP_PREFIX);
}
Ok(EngineConnection {
batch: Arc::new(Mutex::new(Vec::new())),
batch_end: Arc::new(Mutex::new(IndexMap::new())),
core_test: result,
default_planes: Default::default(),
})
}
fn handle_command(&self, cmd_id: &ModelingCmdId, cmd: &kcmc::ModelingCmd) -> (String, OkModelingCmdResponse) {
let cpp_id = id_to_cpp(cmd_id);
let cmd_id = format!("{}", cmd_id);
let mut this_response = OkModelingCmdResponse::Empty {};
let new_code = match cmd {
kcmc::ModelingCmd::ObjectVisible(kcmc::ObjectVisible { hidden, object_id }) => {
format!(r#"scene->getSceneObject(Utils::UUID("{object_id}"))->setHidden({hidden});"#)
}
kcmc::ModelingCmd::EnableSketchMode(kcmc::EnableSketchMode {
entity_id,
animated: _,
ortho: _,
adjust_camera: _,
planar_normal,
}) => {
if let Some(normal) = planar_normal {
format!(
r#"
if(!scene->enableSketchMode(Utils::UUID("{entity_id}"), glm::dvec3 {{ {}, {}, {}, }}, nullopt))
{{
Utils::Plane plane_{cpp_id}(glm::dvec3 {{ 0, 0, 0 }}, glm::dvec3 {{ 1, 0, 0 }}, glm::dvec3 {{ 0, 1, 0 }});
scene->enableSketchMode(plane_{cpp_id}, nullopt, nullopt, false);
}}
"#,
normal.x, normal.y, normal.z
)
} else {
"".into()
}
}
kcmc::ModelingCmd::SketchModeDisable(kcmc::SketchModeDisable {}) => "scene->disableSketchMode();".into(),
kcmc::ModelingCmd::MakePlane(kcmc::MakePlane {
origin,
x_axis,
y_axis,
size,
..
}) => {
let plane_id = format!("plane_{}", cpp_id);
format!(
r#"
auto {plane_id} = make_shared<Object>("plane", glm::dvec3 {{ 0, 0, 0 }});
{plane_id}->setUUID(Utils::UUID("{cmd_id}"));
{plane_id}->makePlane(glm::dvec3 {{ {}, {}, {} }} * scaleFactor, glm::dvec3 {{ {}, {}, {} }}, glm::dvec3 {{ {}, {}, {} }}, {}, false);
{plane_id}->setHidden();
scene->addSceneObject({plane_id});
"#,
origin.x.0,
origin.y.0,
origin.z.0,
x_axis.x,
x_axis.y,
x_axis.z,
y_axis.x,
y_axis.y,
y_axis.z,
size.0
)
}
kcmc::ModelingCmd::StartPath(kcmc::StartPath {}) => {
let sketch_id = format!("sketch_{}", cpp_id);
let path_id = format!("path_{}", cpp_id);
format!(
r#"
auto {sketch_id} = make_shared<Object>("sketch", glm::dvec3 {{ 0, 0, 0 }});
{sketch_id}->setUUID(Utils::UUID("{cmd_id}"));
{sketch_id}->makePath(true);
auto {path_id} = {sketch_id}->get<Model::Brep::Path>();
scene->addSceneObject({sketch_id});
"#
)
}
kcmc::ModelingCmd::MovePathPen(kcmc::MovePathPen { path, to }) => {
format!(
r#"
path_{}->moveTo(glm::dvec3 {{ {}, {}, 0.0 }} * scaleFactor);
"#,
id_to_cpp(path),
to.x.0,
to.y.0
)
}
kcmc::ModelingCmd::ExtendPath(kcmc::ExtendPath { path, segment }) => match segment {
Line { end, relative } => {
format!(
r#"
path_{}->lineTo(glm::dvec3 {{ {}, {}, 0.0 }} * scaleFactor, {{ {} }});
"#,
id_to_cpp(path),
end.x.0,
end.y.0,
relative
)
}
PathSegment::Arc {
center,
radius,
start,
end,
relative,
} => {
let start = start.value;
let end = end.value;
let radius = radius.0;
format!(
r#"
path_{}->addArc(glm::dvec2 {{ {}, {} }} * scaleFactor, {radius} * scaleFactor, {start}, {end}, {{ {} }});
"#,
id_to_cpp(path),
center.x.0,
center.y.0,
relative
)
}
PathSegment::TangentialArcTo {
angle_snap_increment: _,
to,
} => {
format!(
r#"
path_{}->tangentialArcTo(glm::dvec3 {{ {}, {}, {} }} * scaleFactor, nullopt, {{ true }});
"#,
id_to_cpp(path),
to.x.0,
to.y.0,
to.z.0,
)
}
_ => {
format!("//{:?}", cmd)
}
},
kcmc::ModelingCmd::ClosePath(kcmc::ClosePath { path_id }) => {
format!(
r#"
path_{}->close();
sketch_{}->toSolid2D();
"#,
uuid_to_cpp(path_id),
uuid_to_cpp(path_id)
)
}
kcmc::ModelingCmd::Extrude(kcmc::Extrude { distance, target }) => {
format!(
r#"
scene->getSceneObject(Utils::UUID("{target}"))->extrudeToSolid3D({} * scaleFactor, true);
"#,
distance.0
)
}
kcmc::ModelingCmd::Revolve(kcmc::Revolve {
angle,
axis,
axis_is_2d,
origin,
target,
tolerance,
}) => {
let ox = origin.x.0;
let oy = origin.y.0;
let oz = origin.z.0;
let ax = axis.x;
let ay = axis.y;
let az = axis.z;
let angle = angle.value;
let tolerance = tolerance.0;
format!(
r#"
scene->getSceneObject(Utils::UUID("{target}"))->revolveToSolid3D(nullopt, glm::dvec3 {{ {ox}, {oy}, {oz} }} * scaleFactor, glm::dvec3 {{ {ax}, {ay}, {az} }}, {axis_is_2d}, {angle}, {tolerance});
"#
)
}
kcmc::ModelingCmd::Solid2dAddHole(kcmc::Solid2dAddHole { hole_id, object_id }) => {
format!(
r#"scene->getSceneObject(Utils::UUID("{object_id}"))->get<Model::Brep::Solid2D>()->addHole(
make_shared<Model::Brep::Path>(*scene->getSceneObject(Utils::UUID("{hole_id}"))->get<Model::Brep::Solid2D>()->getPath())
);"#
)
}
kcmc::ModelingCmd::Solid3dGetExtrusionFaceInfo(kcmc::Solid3dGetExtrusionFaceInfo {
object_id,
edge_id,
}) => {
format!(
r#"
//face info get {} {}
"#,
object_id, edge_id
)
}
kcmc::ModelingCmd::EntityCircularPattern(kcmc::EntityCircularPattern {
entity_id,
axis,
center,
num_repetitions,
arc_degrees,
rotate_duplicates,
}) => {
let entity_ids = generate_repl_uuids(*num_repetitions as usize);
this_response = OkModelingCmdResponse::EntityCircularPattern(kcmc::output::EntityCircularPattern {
entity_ids: entity_ids.clone(),
});
let mut base_code: String = format!(
r#"
auto reps_{cpp_id} = scene->entityCircularPattern(Utils::UUID("{}"), {num_repetitions}, glm::dvec3 {{ {}, {}, {} }} * scaleFactor, glm::dvec3 {{ {}, {}, {} }} * scaleFactor, {arc_degrees}, {rotate_duplicates});
"#,
entity_id, axis.x, axis.y, axis.z, center.x.0, center.y.0, center.z.0
);
let repl_uuid_fix_code = codegen_cpp_repl_uuid_setters(&cpp_id, &entity_ids);
base_code.push_str(&repl_uuid_fix_code);
base_code
}
kcmc::ModelingCmd::EntityLinearPattern(kcmc::EntityLinearPattern {
entity_id: _,
axis: _,
num_repetitions: _,
spacing: _,
}) => {
// let num_transforms = transforms.len();
// let num_repetitions = transform.iter().map(|t| if t.replicate { 1 } else { 0 } ).sum();
// let mut base_code: String = format!(
// r#"
// std::vector<std::optional<Scene::Scene::LinearPatternTransform>> transforms_{cpp_id}({num_transforms});
// "#);
// for t in transform {
// translations_xyz.push(t.translate.x.to_millimeters(state.units));
// translations_xyz.push(t.translate.y.to_millimeters(state.units));
// translations_xyz.push(t.translate.z.to_millimeters(state.units));
// scale_xyz.push(t.scale.x);
// scale_xyz.push(t.scale.y);
// scale_xyz.push(t.scale.z);
// }
// let entity_ids = generate_repl_uuids(*num_repetitions as usize);
// this_response = OkModelingCmdResponse::EntityLinearPattern {
// data: kittycad::types::EntityLinearPattern {
// entity_ids: entity_ids.clone(),
// },
// };
// let mut base_code: String = format!(
// r#"
// auto reps_{cpp_id} = scene->entityCircularPattern(Utils::UUID("{}"), {num_repetitions}, glm::dvec3 {{ {}, {}, {} }} * scaleFactor, glm::dvec3 {{ {}, {}, {} }} * scaleFactor, {arc_degrees}, {rotate_duplicates});
// "#,
// entity_id, axis.x, axis.y, axis.z, center.x, center.y, center.z
// );
// let repl_uuid_fix_code = codegen_cpp_repl_uuid_setters(&cpp_id, &entity_ids);
// base_code.push_str(&repl_uuid_fix_code);
// base_code
format!("//{:?}", cmd)
}
_ => {
//helps us follow along with the currently unhandled engine commands
format!("//{:?}", cmd)
}
};
(new_code, this_response)
}
}
fn id_to_cpp(id: &ModelingCmdId) -> String {
uuid_to_cpp(&id.0)
}
fn uuid_to_cpp(id: &uuid::Uuid) -> String {
let str = format!("{}", id);
str::replace(&str, "-", "_")
}
fn generate_repl_uuids(count: usize) -> Vec<uuid::Uuid> {
let mut repl_ids: Vec<uuid::Uuid> = Vec::new();
repl_ids.resize_with(count, uuid::Uuid::new_v4);
repl_ids
}
fn codegen_cpp_repl_uuid_setters(reps_id: &str, entity_ids: &[uuid::Uuid]) -> String {
let mut codegen = String::new();
for (i, id) in entity_ids.iter().enumerate() {
let cpp_id = uuid_to_cpp(id);
let iter = format!(
r#"
//change object id -> {id}
auto repl_{cpp_id} = scene->getSceneObject(reps_{reps_id}[{i}]);
scene->removeSceneObject(repl_{cpp_id}->getUUID(), false);
repl_{cpp_id}->setUUID(Utils::UUID("{id}"));
scene->addSceneObject(repl_{cpp_id});
"#
);
codegen.push_str(&iter);
}
codegen
}
#[async_trait::async_trait]
impl kcl_lib::engine::EngineManager for EngineConnection {
fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, kcl_lib::executor::SourceRange)>>> {
self.batch.clone()
}
fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::executor::SourceRange)>>> {
self.batch_end.clone()
}
async fn default_planes(&self, source_range: kcl_lib::executor::SourceRange) -> Result<DefaultPlanes, KclError> {
if NEED_PLANES {
{
let opt = self.default_planes.read().await.as_ref().cloned();
if let Some(planes) = opt {
return Ok(planes);
}
} // drop the read lock
let new_planes = self.new_default_planes(source_range).await?;
*self.default_planes.write().await = Some(new_planes.clone());
Ok(new_planes)
} else {
Ok(DefaultPlanes::default())
}
}
async fn clear_scene_post_hook(&self, _source_range: kcl_lib::executor::SourceRange) -> Result<(), KclError> {
Ok(())
}
async fn inner_send_modeling_cmd(
&self,
id: uuid::Uuid,
_source_range: kcl_lib::executor::SourceRange,
cmd: WebSocketRequest,
_id_to_source_range: std::collections::HashMap<uuid::Uuid, kcl_lib::executor::SourceRange>,
) -> Result<WebSocketResponse, KclError> {
match cmd {
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch {
ref requests,
batch_id: _,
responses: _,
}) => {
let mut responses = HashMap::new();
for request in requests {
let (new_code, this_response);
if let Ok(mut test_code) = self.core_test.lock() {
(new_code, this_response) = self.handle_command(&request.cmd_id, &request.cmd);
if !new_code.is_empty() {
let new_code = new_code
.trim()
.split(' ')
.filter(|s| !s.is_empty())
.collect::<Vec<_>>()
.join(" ")
+ "\n";
//println!("{new_code}");
test_code.push_str(&new_code);
}
} else {
this_response = OkModelingCmdResponse::Empty {};
}
responses.insert(
request.cmd_id,
kcmc::websocket::BatchResponse::Success {
response: this_response,
},
);
}
Ok(WebSocketResponse::Success(kcmc::websocket::SuccessWebSocketResponse {
success: true,
request_id: Some(id),
resp: OkWebSocketResponseData::ModelingBatch { responses },
}))
}
WebSocketRequest::ModelingCmdReq(ModelingCmdReq { cmd, cmd_id }) => {
//also handle unbatched requests inline
let (new_code, this_response);
if let Ok(mut test_code) = self.core_test.lock() {
(new_code, this_response) = self.handle_command(&cmd_id, &cmd);
if !new_code.is_empty() {
let new_code = new_code
.trim()
.split(' ')
.filter(|s| !s.is_empty())
.collect::<Vec<_>>()
.join(" ")
+ "\n";
//println!("{new_code}");
test_code.push_str(&new_code);
}
} else {
this_response = OkModelingCmdResponse::Empty {};
}
Ok(WebSocketResponse::Success(kcmc::websocket::SuccessWebSocketResponse {
success: true,
request_id: Some(id),
resp: OkWebSocketResponseData::Modeling {
modeling_response: this_response,
},
}))
}
_ => Ok(WebSocketResponse::Success(kcmc::websocket::SuccessWebSocketResponse {
success: true,
request_id: Some(id),
resp: OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::Empty {},
},
})),
}
}
}

View File

@ -0,0 +1,30 @@
use anyhow::Result;
use kcl_lib::executor::ExecutorContext;
use std::sync::{Arc, Mutex};
#[cfg(not(target_arch = "wasm32"))]
mod conn_mock_core;
///Converts the given kcl code to an engine test
pub async fn kcl_to_engine_core(code: &str) -> Result<String> {
let tokens = kcl_lib::token::lexer(code)?;
let parser = kcl_lib::parser::Parser::new(tokens);
let program = parser.ast()?;
let result = Arc::new(Mutex::new("".into()));
let ref_result = Arc::clone(&result);
let ctx = ExecutorContext {
engine: Arc::new(Box::new(
crate::conn_mock_core::EngineConnection::new(ref_result).await?,
)),
fs: Arc::new(kcl_lib::fs::FileManager::new()),
stdlib: Arc::new(kcl_lib::std::StdLib::new()),
settings: Default::default(),
context_type: kcl_lib::executor::ContextType::MockCustomForwarded,
};
let _memory = ctx.run(&program, None).await?;
let result = result.lock().expect("mutex lock").clone();
Ok(result)
}

View File

@ -0,0 +1,19 @@
use kcl_to_core::*;
use std::{env, fs};
#[tokio::main]
async fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: kcl-to-core path/to/file.kcl");
return;
}
let file_path = &args[1];
let kcl = fs::read_to_string(file_path).expect("read file");
let result = kcl_to_engine_core(&kcl).await.expect("kcl conversion");
println!("{}", result);
}

View File

@ -0,0 +1,19 @@
use kcl_to_core::*;
#[tokio::test]
async fn kcl_to_core_test() {
let result = kcl_to_engine_core(
r#"
const part001 = startSketchOn('XY')
|> startProfileAt([11.19, 28.35], %)
|> line([28.67, -13.25], %, $here)
|> line([-4.12, -22.81], %)
|> line([-33.24, 14.55], %)
|> close(%)
|> extrude(5, %)
"#,
)
.await;
assert!(result.is_ok());
}

View File

@ -1649,6 +1649,22 @@ impl ExtrudeSurface {
} }
} }
/// The type of ExecutorContext being used
#[derive(PartialEq, Debug, Default, Clone)]
pub enum ContextType {
/// Live engine connection
#[default]
Live,
/// Completely mocked connection
/// Mock mode is only for the modeling app when they just want to mock engine calls and not
/// actually make them.
Mock,
/// Handled by some other interpreter/conversion system
MockCustomForwarded,
}
/// The executor context. /// The executor context.
/// Cloning will return another handle to the same engine connection/session, /// Cloning will return another handle to the same engine connection/session,
/// as this uses `Arc` under the hood. /// as this uses `Arc` under the hood.
@ -1658,9 +1674,7 @@ pub struct ExecutorContext {
pub fs: Arc<FileManager>, pub fs: Arc<FileManager>,
pub stdlib: Arc<StdLib>, pub stdlib: Arc<StdLib>,
pub settings: ExecutorSettings, pub settings: ExecutorSettings,
/// Mock mode is only for the modeling app when they just want to mock engine calls and not pub context_type: ContextType,
/// actually make them.
pub is_mock: bool,
} }
/// The executor settings. /// The executor settings.
@ -1770,10 +1784,14 @@ impl ExecutorContext {
fs: Arc::new(FileManager::new()), fs: Arc::new(FileManager::new()),
stdlib: Arc::new(StdLib::new()), stdlib: Arc::new(StdLib::new()),
settings, settings,
is_mock: false, context_type: ContextType::Live,
}) })
} }
pub fn is_mock(&self) -> bool {
self.context_type == ContextType::Mock || self.context_type == ContextType::MockCustomForwarded
}
/// For executing unit tests. /// For executing unit tests.
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub async fn new_for_unit_test(units: UnitLength, engine_addr: Option<String>) -> Result<Self> { pub async fn new_for_unit_test(units: UnitLength, engine_addr: Option<String>) -> Result<Self> {
@ -2155,7 +2173,7 @@ mod tests {
fs: Arc::new(crate::fs::FileManager::new()), fs: Arc::new(crate::fs::FileManager::new()),
stdlib: Arc::new(crate::std::StdLib::new()), stdlib: Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: ContextType::Mock,
}; };
let exec_state = ctx.run(&program, None).await?; let exec_state = ctx.run(&program, None).await?;

View File

@ -44,7 +44,7 @@ impl Args {
fs: Arc::new(crate::fs::FileManager::new()), fs: Arc::new(crate::fs::FileManager::new()),
stdlib: Arc::new(crate::std::StdLib::new()), stdlib: Arc::new(crate::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: crate::executor::ContextType::Mock,
}, },
}) })
} }

View File

@ -247,7 +247,7 @@ pub(crate) async fn do_post_extrude(sketch: Sketch, length: f64, args: Args) ->
Some(extrude_surface) Some(extrude_surface)
} }
} }
} else if args.ctx.is_mock { } else if args.ctx.is_mock() {
// Only pre-populate the extrude surface if we are in mock mode. // Only pre-populate the extrude surface if we are in mock mode.
let extrude_surface = ExtrudeSurface::ExtrudePlane(crate::executor::ExtrudePlane { let extrude_surface = ExtrudeSurface::ExtrudePlane(crate::executor::ExtrudePlane {
@ -296,7 +296,7 @@ fn analyze_faces(args: &Args, face_infos: Vec<ExtrusionFaceInfo>) -> Faces {
sides: HashMap::with_capacity(face_infos.len()), sides: HashMap::with_capacity(face_infos.len()),
..Default::default() ..Default::default()
}; };
if args.ctx.is_mock { if args.ctx.is_mock() {
// Create fake IDs for start and end caps, to make extrudes mock-execute safe // Create fake IDs for start and end caps, to make extrudes mock-execute safe
faces.start_cap_id = Some(Uuid::new_v4()); faces.start_cap_id = Some(Uuid::new_v4());
faces.end_cap_id = Some(Uuid::new_v4()); faces.end_cap_id = Some(Uuid::new_v4());

View File

@ -228,7 +228,7 @@ pub async fn get_opposite_edge(exec_state: &mut ExecState, args: Args) -> Result
name = "getOppositeEdge", name = "getOppositeEdge",
}] }]
async fn inner_get_opposite_edge(tag: TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<Uuid, KclError> { async fn inner_get_opposite_edge(tag: TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<Uuid, KclError> {
if args.ctx.is_mock { if args.ctx.is_mock() {
return Ok(Uuid::new_v4()); return Ok(Uuid::new_v4());
} }
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?; let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
@ -309,7 +309,7 @@ async fn inner_get_next_adjacent_edge(
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
) -> Result<Uuid, KclError> { ) -> Result<Uuid, KclError> {
if args.ctx.is_mock { if args.ctx.is_mock() {
return Ok(Uuid::new_v4()); return Ok(Uuid::new_v4());
} }
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?; let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
@ -398,7 +398,7 @@ async fn inner_get_previous_adjacent_edge(
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
) -> Result<Uuid, KclError> { ) -> Result<Uuid, KclError> {
if args.ctx.is_mock { if args.ctx.is_mock() {
return Ok(Uuid::new_v4()); return Ok(Uuid::new_v4());
} }
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?; let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;

View File

@ -284,7 +284,7 @@ async fn inner_import(
} }
} }
if args.ctx.is_mock { if args.ctx.is_mock() {
return Ok(ImportedGeometry { return Ok(ImportedGeometry {
id: uuid::Uuid::new_v4(), id: uuid::Uuid::new_v4(),
value: import_files.iter().map(|f| f.path.to_string()).collect(), value: import_files.iter().map(|f| f.path.to_string()).collect(),

View File

@ -112,7 +112,7 @@ async fn inner_mirror_2d(
SketchSet::Sketches(sketches) => sketches, SketchSet::Sketches(sketches) => sketches,
}; };
if args.ctx.is_mock { if args.ctx.is_mock() {
return Ok(starting_sketches); return Ok(starting_sketches);
} }

View File

@ -290,7 +290,7 @@ async fn inner_pattern_transform<'a>(
let starting_solids: Vec<Box<Solid>> = solid_set.into(); let starting_solids: Vec<Box<Solid>> = solid_set.into();
if args.ctx.is_mock { if args.ctx.context_type == crate::executor::ContextType::Mock {
return Ok(starting_solids); return Ok(starting_solids);
} }
@ -513,7 +513,7 @@ async fn inner_pattern_linear_2d(
) -> Result<Vec<Box<Sketch>>, KclError> { ) -> Result<Vec<Box<Sketch>>, KclError> {
let starting_sketches: Vec<Box<Sketch>> = sketch_set.into(); let starting_sketches: Vec<Box<Sketch>> = sketch_set.into();
if args.ctx.is_mock { if args.ctx.context_type == crate::executor::ContextType::Mock {
return Ok(starting_sketches); return Ok(starting_sketches);
} }
@ -591,7 +591,7 @@ async fn inner_pattern_linear_3d(
let starting_solids: Vec<Box<Solid>> = solid_set.into(); let starting_solids: Vec<Box<Solid>> = solid_set.into();
if args.ctx.is_mock { if args.ctx.context_type == crate::executor::ContextType::Mock {
return Ok(starting_solids); return Ok(starting_solids);
} }
@ -783,7 +783,7 @@ async fn inner_pattern_circular_2d(
) -> Result<Vec<Box<Sketch>>, KclError> { ) -> Result<Vec<Box<Sketch>>, KclError> {
let starting_sketches: Vec<Box<Sketch>> = sketch_set.into(); let starting_sketches: Vec<Box<Sketch>> = sketch_set.into();
if args.ctx.is_mock { if args.ctx.context_type == crate::executor::ContextType::Mock {
return Ok(starting_sketches); return Ok(starting_sketches);
} }
@ -852,7 +852,7 @@ async fn inner_pattern_circular_3d(
let starting_solids: Vec<Box<Solid>> = solid_set.into(); let starting_solids: Vec<Box<Solid>> = solid_set.into();
if args.ctx.is_mock { if args.ctx.context_type == crate::executor::ContextType::Mock {
return Ok(starting_solids); return Ok(starting_solids);
} }

View File

@ -42,6 +42,11 @@ pub async fn execute_wasm(
)) ))
}; };
let fs = Arc::new(kcl_lib::fs::FileManager::new(fs_manager)); let fs = Arc::new(kcl_lib::fs::FileManager::new(fs_manager));
let context_type = if is_mock {
kcl_lib::executor::ContextType::Mock
} else {
kcl_lib::executor::ContextType::Live
};
let ctx = kcl_lib::executor::ExecutorContext { let ctx = kcl_lib::executor::ExecutorContext {
engine, engine,
fs, fs,
@ -50,7 +55,7 @@ pub async fn execute_wasm(
units, units,
..Default::default() ..Default::default()
}, },
is_mock, context_type,
}; };
let exec_state = ctx.run(&program, Some(memory)).await.map_err(String::from)?; let exec_state = ctx.run(&program, Some(memory)).await.map_err(String::from)?;
@ -274,7 +279,7 @@ pub async fn kcl_lsp_run(
units, units,
..Default::default() ..Default::default()
}, },
is_mock: false, context_type: kcl_lib::executor::ContextType::Live,
}) })
} else { } else {
None None

View File

@ -38,7 +38,7 @@ async fn setup(program: &str) -> (ExecutorContext, Program) {
fs: std::sync::Arc::new(kcl_lib::fs::FileManager::new()), fs: std::sync::Arc::new(kcl_lib::fs::FileManager::new()),
stdlib: std::sync::Arc::new(kcl_lib::std::StdLib::new()), stdlib: std::sync::Arc::new(kcl_lib::std::StdLib::new()),
settings: Default::default(), settings: Default::default(),
is_mock: true, context_type: kcl_lib::executor::ContextType::Mock,
}; };
(ctx, program) (ctx, program)
} }