Compare commits

...

2 Commits

Author SHA1 Message Date
98dae3aa9a WIP: Send engine the extruded face IDs 2024-12-13 17:43:20 -06:00
52dc8842f7 Format lego KCL 2024-12-13 14:06:17 -06:00
10 changed files with 143 additions and 105 deletions

View File

@ -1819,9 +1819,9 @@ dependencies = [
[[package]] [[package]]
name = "kittycad-modeling-cmds" name = "kittycad-modeling-cmds"
version = "0.2.79" version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a9cab4476455be70ea57643c31444068b056d091bd348cab6044c0d8ad7fcc" checksum = "e209a26f2ef14afbb25de6a4641bc899e8468a62ae30c6c40d3a2840c7a6ce76"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",

View File

@ -76,7 +76,7 @@ members = [
[workspace.dependencies] [workspace.dependencies]
http = "1" http = "1"
kittycad = { version = "0.3.28", default-features = false, features = ["js", "requests"] } kittycad = { version = "0.3.28", default-features = false, features = ["js", "requests"] }
kittycad-modeling-cmds = { version = "0.2.79", features = ["websocket"] } kittycad-modeling-cmds = { version = "0.2.82", features = ["websocket"] }
[workspace.lints.clippy] [workspace.lints.clippy]
assertions_on_result_states = "warn" assertions_on_result_states = "warn"

View File

@ -31,3 +31,5 @@ new-sim-test test_name render_to_png="true":
{{cita}} -p kcl-lib -- simulation_tests::{{test_name}}::unparse {{cita}} -p kcl-lib -- simulation_tests::{{test_name}}::unparse
TWENTY_TWENTY=overwrite {{cita}} -p kcl-lib -- tests::{{test_name}}::kcl_test_execute TWENTY_TWENTY=overwrite {{cita}} -p kcl-lib -- tests::{{test_name}}::kcl_test_execute
bench-lego:
cargo criterion -p kcl-lib --bench executor_benchmark_criterion -- lego

View File

@ -265,6 +265,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
}) })
.collect(); .collect();
dbg!(&requests);
let batched_requests = WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { let batched_requests = WebSocketRequest::ModelingCmdBatchReq(ModelingBatch {
requests, requests,
batch_id: uuid::Uuid::new_v4().into(), batch_id: uuid::Uuid::new_v4().into(),

View File

@ -8,7 +8,10 @@ use kcmc::{
each_cmd as mcmd, length_unit::LengthUnit, ok_response::OkModelingCmdResponse, output::ExtrusionFaceInfo, each_cmd as mcmd, length_unit::LengthUnit, ok_response::OkModelingCmdResponse, output::ExtrusionFaceInfo,
shared::ExtrusionFaceCapType, websocket::OkWebSocketResponseData, ModelingCmd, shared::ExtrusionFaceCapType, websocket::OkWebSocketResponseData, ModelingCmd,
}; };
use kittycad_modeling_cmds as kcmc; use kittycad_modeling_cmds::{
self as kcmc,
shared::{ExtrudedFaceInfo, SideFace},
};
use uuid::Uuid; use uuid::Uuid;
use crate::{ use crate::{
@ -108,12 +111,27 @@ async fn inner_extrude(
) )
.await?; .await?;
// Choose IDs for the new faces that will be created when extruding.
let _sketch_is_on_face = matches!(sketch.on, SketchSurface::Face(_));
let new_faces = ExtrudedFaceInfo {
bottom: Some(exec_state.id_generator.next_uuid()),
top: exec_state.id_generator.next_uuid(),
sides: sketch
.paths
.iter()
.map(|path| SideFace {
path_id: path.get_id(),
face_id: exec_state.id_generator.next_uuid(),
})
.collect(),
};
args.batch_modeling_cmd( args.batch_modeling_cmd(
id, id,
ModelingCmd::from(mcmd::Extrude { ModelingCmd::from(mcmd::Extrude {
target: sketch.id.into(), target: sketch.id.into(),
distance: LengthUnit(length), distance: LengthUnit(length),
faces: Default::default(), faces: Some(new_faces.clone()),
}), }),
) )
.await?; .await?;
@ -124,7 +142,7 @@ async fn inner_extrude(
ModelingCmd::SketchModeDisable(mcmd::SketchModeDisable {}), ModelingCmd::SketchModeDisable(mcmd::SketchModeDisable {}),
) )
.await?; .await?;
solids.push(do_post_extrude(sketch.clone(), length, exec_state, args.clone()).await?); solids.push(do_post_extrude(sketch.clone(), length, Some(new_faces), exec_state, args.clone()).await?);
} }
Ok(solids.into()) Ok(solids.into())
@ -133,6 +151,7 @@ async fn inner_extrude(
pub(crate) async fn do_post_extrude( pub(crate) async fn do_post_extrude(
sketch: Sketch, sketch: Sketch,
length: f64, length: f64,
new_faces: Option<ExtrudedFaceInfo>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
) -> Result<Box<Solid>, KclError> { ) -> Result<Box<Solid>, KclError> {
@ -160,60 +179,65 @@ pub(crate) async fn do_post_extrude(
sketch.id = face.solid.sketch.id; sketch.id = face.solid.sketch.id;
} }
let solid3d_info = args let face_infos = match new_faces {
.send_modeling_cmd( Some(new_faces) => new_faces.list_faces(),
exec_state.id_generator.next_uuid(), None => {
ModelingCmd::from(mcmd::Solid3dGetExtrusionFaceInfo { let solid3d_info = args
edge_id: any_edge_id, .send_modeling_cmd(
object_id: sketch.id, exec_state.id_generator.next_uuid(),
}), ModelingCmd::from(mcmd::Solid3dGetExtrusionFaceInfo {
) edge_id: any_edge_id,
.await?; object_id: sketch.id,
}),
)
.await?;
let face_infos = if let OkWebSocketResponseData::Modeling { let face_infos: Vec<ExtrusionFaceInfo> = if let OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::Solid3dGetExtrusionFaceInfo(data), modeling_response: OkModelingCmdResponse::Solid3dGetExtrusionFaceInfo(data),
} = solid3d_info } = solid3d_info
{ {
data.faces data.faces
} else {
vec![]
};
for (curve_id, face_id) in face_infos
.iter()
.filter(|face_info| face_info.cap == ExtrusionFaceCapType::None)
.filter_map(|face_info| {
if let (Some(curve_id), Some(face_id)) = (face_info.curve_id, face_info.face_id) {
Some((curve_id, face_id))
} else { } else {
None vec![]
} };
}) for (curve_id, face_id) in face_infos
{ .iter()
// Batch these commands, because the Rust code doesn't actually care about the outcome. .filter(|face_info| face_info.cap == ExtrusionFaceCapType::None)
// So, there's no need to await them. .filter_map(|face_info| {
// Instead, the Typescript codebases (which handles WebSocket sends when compiled via Wasm) if let (Some(curve_id), Some(face_id)) = (face_info.curve_id, face_info.face_id) {
// uses this to build the artifact graph, which the UI needs. Some((curve_id, face_id))
args.batch_modeling_cmd( } else {
exec_state.id_generator.next_uuid(), None
ModelingCmd::from(mcmd::Solid3dGetOppositeEdge { }
edge_id: curve_id, })
object_id: sketch.id, {
face_id, // Batch these commands, because the Rust code doesn't actually care about the outcome.
}), // So, there's no need to await them.
) // Instead, the Typescript codebases (which handles WebSocket sends when compiled via Wasm)
.await?; // uses this to build the artifact graph, which the UI needs.
args.batch_modeling_cmd(
exec_state.id_generator.next_uuid(),
ModelingCmd::from(mcmd::Solid3dGetOppositeEdge {
edge_id: curve_id,
object_id: sketch.id,
face_id,
}),
)
.await?;
args.batch_modeling_cmd( args.batch_modeling_cmd(
exec_state.id_generator.next_uuid(), exec_state.id_generator.next_uuid(),
ModelingCmd::from(mcmd::Solid3dGetNextAdjacentEdge { ModelingCmd::from(mcmd::Solid3dGetNextAdjacentEdge {
edge_id: curve_id, edge_id: curve_id,
object_id: sketch.id, object_id: sketch.id,
face_id, face_id,
}), }),
) )
.await?; .await?;
} }
face_infos
}
};
let Faces { let Faces {
sides: face_id_map, sides: face_id_map,

View File

@ -155,5 +155,5 @@ async fn inner_loft(
.await?; .await?;
// Using the first sketch as the base curve, idk we might want to change this later. // Using the first sketch as the base curve, idk we might want to change this later.
do_post_extrude(sketches[0].clone(), 0.0, exec_state, args).await do_post_extrude(sketches[0].clone(), 0.0, None, exec_state, args).await
} }

View File

@ -295,7 +295,7 @@ async fn inner_revolve(
} }
} }
do_post_extrude(sketch, 0.0, exec_state, args).await do_post_extrude(sketch, 0.0, None, exec_state, args).await
} }
#[cfg(test)] #[cfg(test)]

View File

@ -98,5 +98,5 @@ async fn inner_sweep(
) )
.await?; .await?;
do_post_extrude(sketch, 0.0, exec_state, args).await do_post_extrude(sketch, 0.0, None, exec_state, args).await
} }

View File

@ -1,36 +1,36 @@
// 2x8 Lego Brick // 2x8 Lego Brick
// A standard Lego brick with 2 bumps wide and 8 bumps long. // A standard Lego brick with 2 bumps wide and 8 bumps long.
// Define constants // Define constants
const lbumps = 10 // number of bumps long lbumps = 10 // number of bumps long
const wbumps = {{N}} // number of bumps wide wbumps = {{N}} // number of bumps wide
const pitch = 8.0 pitch = 8.0
const clearance = 0.1 clearance = 0.1
const bumpDiam = 4.8 bumpDiam = 4.8
const bumpHeight = 1.8 bumpHeight = 1.8
const height = 9.6 height = 9.6
const t = (pitch - (2 * clearance) - bumpDiam) / 2.0 t = (pitch - (2 * clearance) - bumpDiam) / 2.0
const totalLength = lbumps * pitch - (2.0 * clearance) totalLength = lbumps * pitch - (2.0 * clearance)
const totalWidth = wbumps * pitch - (2.0 * clearance) totalWidth = wbumps * pitch - (2.0 * clearance)
// Create the plane for the pegs. This is a hack so that the pegs can be patterned along the face of the lego base. // Create the plane for the pegs. This is a hack so that the pegs can be patterned along the face of the lego base.
const pegFace = { pegFace = {
plane: { plane = {
origin: { x: 0, y: 0, z: height }, origin = { x = 0, y = 0, z = height },
xAxis: { x: 1, y: 0, z: 0 }, xAxis = { x = 1, y = 0, z = 0 },
yAxis: { x: 0, y: 1, z: 0 }, yAxis = { x = 0, y = 1, z = 0 },
zAxis: { x: 0, y: 0, z: 1 } zAxis = { x = 0, y = 0, z = 1 }
} }
} }
// Create the plane for the tubes underneath the lego. This is a hack so that the tubes can be patterned underneath the lego. // Create the plane for the tubes underneath the lego. This is a hack so that the tubes can be patterned underneath the lego.
const tubeFace = { tubeFace = {
plane: { plane = {
origin: { x: 0, y: 0, z: height - t }, origin = { x = 0, y = 0, z = height - t },
xAxis: { x: 1, y: 0, z: 0 }, xAxis = { x = 1, y = 0, z = 0 },
yAxis: { x: 0, y: 1, z: 0 }, yAxis = { x = 0, y = 1, z = 0 },
zAxis: { x: 0, y: 0, z: 1 } zAxis = { x = 0, y = 0, z = 1 }
} }
} }
// Make the base // Make the base
const s = startSketchOn('XY') s = startSketchOn('XY')
|> startProfileAt([-totalWidth / 2, -totalLength / 2], %) |> startProfileAt([-totalWidth / 2, -totalLength / 2], %)
|> line([totalWidth, 0], %) |> line([totalWidth, 0], %)
|> line([0, totalLength], %) |> line([0, totalLength], %)
@ -39,7 +39,7 @@ const s = startSketchOn('XY')
|> extrude(height, %) |> extrude(height, %)
// Sketch and extrude a rectangular shape to create the shell underneath the lego. This is a hack until we have a shell function. // Sketch and extrude a rectangular shape to create the shell underneath the lego. This is a hack until we have a shell function.
const shellExtrude = startSketchOn(s, "start") shellExtrude = startSketchOn(s, "start")
|> startProfileAt([ |> startProfileAt([
-(totalWidth / 2 - t), -(totalWidth / 2 - t),
-(totalLength / 2 - t) -(totalLength / 2 - t)
@ -50,32 +50,33 @@ const shellExtrude = startSketchOn(s, "start")
|> close(%) |> close(%)
|> extrude(-(height - t), %) |> extrude(-(height - t), %)
fn tr = (i) => { fn tr(i) {
let j = i + 1 j = i + 1
let x = (j/wbumps) * pitch x = j / wbumps * pitch
let y = (j % wbumps) * pitch y = j % wbumps * pitch
return { return { translate = [x, y, 0] }
translate: [x, y, 0],
}
} }
// Create the pegs on the top of the base // Create the pegs on the top of the base
const totalBumps = (wbumps * lbumps)-1 totalBumps = wbumps * lbumps - 1
const peg = startSketchOn(s, 'end') peg = startSketchOn(s, 'end')
|> circle({ center: [ |> circle({
-(pitch*(wbumps-1)/2), center = [
-(pitch*(lbumps-1)/2) -(pitch * (wbumps - 1) / 2),
], radius: bumpDiam / 2 }, %) -(pitch * (lbumps - 1) / 2)
|> patternLinear2d({ ],
axis: [1, 0], radius = bumpDiam / 2
instances: wbumps,
distance: pitch
}, %) }, %)
|> patternLinear2d({ |> patternLinear2d({
axis: [0, 1], axis = [1, 0],
instances: lbumps, instances = wbumps,
distance: pitch distance = pitch
}, %)
|> patternLinear2d({
axis = [0, 1],
instances = lbumps,
distance = pitch
}, %) }, %)
|> extrude(bumpHeight, %) |> extrude(bumpHeight, %)
// |> patternTransform(int(totalBumps-1), tr, %) // |> patternTransform(int(totalBumps-1), tr, %)

View File

@ -62,6 +62,16 @@ async fn kcl_test_execute_i_shape() {
assert_out("i_shape", &result); assert_out("i_shape", &result);
} }
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_execute_slow_lego() {
// This is some code from lee that starts a pipe expression with a variable.
let code = include_str!("inputs/slow_lego.kcl.tmpl");
let code = code.replace("{{N}}", "1");
let result = execute_and_snapshot(&code, UnitLength::Mm, None).await.unwrap();
assert_out("i_shape", &result);
}
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
#[ignore] // No longer a stack overflow problem, instead it causes an engine internal error. #[ignore] // No longer a stack overflow problem, instead it causes an engine internal error.
async fn kcl_test_execute_pipes_on_pipes() { async fn kcl_test_execute_pipes_on_pipes() {