2023-09-17 21:57:43 -07:00
|
|
|
use anyhow::Result;
|
|
|
|
use kcl_lib::{
|
2024-10-30 16:52:17 -04:00
|
|
|
ast::{
|
|
|
|
modify::modify_ast_for_sketch,
|
|
|
|
types::{Node, Program},
|
|
|
|
},
|
2024-10-09 19:38:40 -04:00
|
|
|
executor::{ExecutorContext, IdGenerator, KclValue, PlaneType, Sketch, SourceRange},
|
2023-09-17 21:57:43 -07:00
|
|
|
};
|
2024-09-19 14:06:29 -07:00
|
|
|
use kittycad_modeling_cmds::{each_cmd as mcmd, length_unit::LengthUnit, shared::Point3d, ModelingCmd};
|
2023-09-17 21:57:43 -07:00
|
|
|
use pretty_assertions::assert_eq;
|
|
|
|
|
|
|
|
/// Setup the engine and parse code for an ast.
|
2024-10-30 16:52:17 -04:00
|
|
|
async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Node<Program>, uuid::Uuid)> {
|
2024-04-15 17:18:32 -07:00
|
|
|
let tokens = kcl_lib::token::lexer(code)?;
|
2023-09-17 21:57:43 -07:00
|
|
|
let parser = kcl_lib::parser::Parser::new(tokens);
|
|
|
|
let program = parser.ast()?;
|
2024-10-25 20:06:32 -07:00
|
|
|
let ctx = kcl_lib::executor::ExecutorContext::new_with_default_client(Default::default()).await?;
|
2024-10-17 00:48:33 -04:00
|
|
|
let exec_state = ctx.run(&program, None, IdGenerator::default(), None).await?;
|
2023-09-17 21:57:43 -07:00
|
|
|
|
|
|
|
// We need to get the sketch ID.
|
2024-09-27 15:44:44 -07:00
|
|
|
// Get the sketch ID from memory.
|
2024-09-16 15:10:33 -04:00
|
|
|
let KclValue::UserVal(user_val) = exec_state.memory.get(name, SourceRange::default()).unwrap() else {
|
|
|
|
anyhow::bail!("part001 not found in memory: {:?}", exec_state.memory);
|
2023-09-17 21:57:43 -07:00
|
|
|
};
|
2024-09-27 15:44:44 -07:00
|
|
|
let Some((sketch, _meta)) = user_val.get::<Sketch>() else {
|
|
|
|
anyhow::bail!("part001 was not a Sketch");
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
};
|
2024-09-27 15:44:44 -07:00
|
|
|
let sketch_id = sketch.id;
|
2023-09-17 21:57:43 -07:00
|
|
|
|
|
|
|
let plane_id = uuid::Uuid::new_v4();
|
2023-10-05 14:27:48 -07:00
|
|
|
ctx.engine
|
2023-09-20 18:27:08 -07:00
|
|
|
.send_modeling_cmd(
|
|
|
|
plane_id,
|
|
|
|
SourceRange::default(),
|
2024-09-18 17:04:04 -05:00
|
|
|
ModelingCmd::from(mcmd::MakePlane {
|
2023-09-20 18:27:08 -07:00
|
|
|
clobber: false,
|
2024-09-18 17:04:04 -05:00
|
|
|
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 },
|
2023-10-05 19:54:31 -07:00
|
|
|
hide: Some(true),
|
2024-09-18 17:04:04 -05:00
|
|
|
}),
|
2023-09-20 18:27:08 -07:00
|
|
|
)
|
|
|
|
.await?;
|
2023-09-17 21:57:43 -07:00
|
|
|
|
|
|
|
// Enter sketch mode.
|
|
|
|
// We can't get control points without being in sketch mode.
|
|
|
|
// You can however get path info without sketch mode.
|
2023-10-05 14:27:48 -07:00
|
|
|
ctx.engine
|
2023-09-20 18:27:08 -07:00
|
|
|
.send_modeling_cmd(
|
|
|
|
uuid::Uuid::new_v4(),
|
|
|
|
SourceRange::default(),
|
2024-09-18 17:04:04 -05:00
|
|
|
ModelingCmd::from(mcmd::EnableSketchMode {
|
2023-09-20 18:27:08 -07:00
|
|
|
animated: false,
|
|
|
|
ortho: true,
|
2024-05-03 14:45:39 -07:00
|
|
|
entity_id: plane_id,
|
2024-09-18 17:04:04 -05:00
|
|
|
planar_normal: Some(Point3d { x: 0.0, y: 0.0, z: 1.0 }),
|
2024-05-03 14:45:39 -07:00
|
|
|
adjust_camera: false,
|
2024-09-18 17:04:04 -05:00
|
|
|
}),
|
2023-09-20 18:27:08 -07:00
|
|
|
)
|
|
|
|
.await?;
|
2023-09-17 21:57:43 -07:00
|
|
|
|
2023-10-05 14:27:48 -07:00
|
|
|
Ok((ctx, program, sketch_id))
|
2023-09-17 21:57:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
2024-08-12 13:06:30 -07:00
|
|
|
async fn kcl_test_modify_sketch_part001() {
|
2023-09-17 21:57:43 -07:00
|
|
|
let name = "part001";
|
|
|
|
let code = format!(
|
2024-10-02 14:19:40 -05:00
|
|
|
r#"{} = startSketchOn("XY")
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([8.41, 5.78], %)
|
2023-09-17 21:57:43 -07:00
|
|
|
|> line([7.37, -11.0], %)
|
|
|
|
|> line([-8.69, -3.75], %)
|
|
|
|
|> line([-5.0, 4.25], %)
|
|
|
|
"#,
|
|
|
|
name
|
|
|
|
);
|
|
|
|
|
2024-03-13 12:56:46 -07:00
|
|
|
let (ctx, program, sketch_id) = setup(&code, name).await.unwrap();
|
2023-09-17 21:57:43 -07:00
|
|
|
let mut new_program = program.clone();
|
2024-03-13 12:56:46 -07:00
|
|
|
let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id)
|
2023-09-17 21:57:43 -07:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Make sure the code is the same.
|
|
|
|
assert_eq!(code, new_code);
|
|
|
|
// Make sure the program is the same.
|
|
|
|
assert_eq!(new_program, program);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
2024-08-12 13:06:30 -07:00
|
|
|
async fn kcl_test_modify_sketch_part002() {
|
2023-09-17 21:57:43 -07:00
|
|
|
let name = "part002";
|
|
|
|
let code = format!(
|
2024-10-02 14:19:40 -05:00
|
|
|
r#"{} = startSketchOn("XY")
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([8.41, 5.78], %)
|
2023-09-17 21:57:43 -07:00
|
|
|
|> line([7.42, -8.62], %)
|
|
|
|
|> line([-6.38, -3.51], %)
|
|
|
|
|> line([-3.77, 3.56], %)
|
|
|
|
"#,
|
|
|
|
name
|
|
|
|
);
|
|
|
|
|
2024-03-13 12:56:46 -07:00
|
|
|
let (ctx, program, sketch_id) = setup(&code, name).await.unwrap();
|
2023-09-17 21:57:43 -07:00
|
|
|
let mut new_program = program.clone();
|
2024-03-13 12:56:46 -07:00
|
|
|
let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id)
|
2023-09-17 21:57:43 -07:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Make sure the code is the same.
|
|
|
|
assert_eq!(code, new_code);
|
|
|
|
// Make sure the program is the same.
|
|
|
|
assert_eq!(new_program, program);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
|
|
#[ignore] // until KittyCAD/engine#1434 is fixed.
|
2024-08-12 13:06:30 -07:00
|
|
|
async fn kcl_test_modify_close_sketch() {
|
2023-09-17 21:57:43 -07:00
|
|
|
let name = "part002";
|
|
|
|
let code = format!(
|
2024-10-02 14:19:40 -05:00
|
|
|
r#"{} = startSketchOn("XY")
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([7.91, 3.89], %)
|
2023-09-17 21:57:43 -07:00
|
|
|
|> line([7.42, -8.62], %)
|
|
|
|
|> line([-6.38, -3.51], %)
|
|
|
|
|> line([-3.77, 3.56], %)
|
|
|
|
|> close(%)
|
|
|
|
"#,
|
|
|
|
name
|
|
|
|
);
|
|
|
|
|
2024-03-13 12:56:46 -07:00
|
|
|
let (ctx, program, sketch_id) = setup(&code, name).await.unwrap();
|
2023-09-17 21:57:43 -07:00
|
|
|
let mut new_program = program.clone();
|
2024-03-13 12:56:46 -07:00
|
|
|
let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id)
|
2023-09-17 21:57:43 -07:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Make sure the code is the same.
|
|
|
|
assert_eq!(code, new_code);
|
|
|
|
// Make sure the program is the same.
|
|
|
|
assert_eq!(new_program, program);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
2024-08-12 13:06:30 -07:00
|
|
|
async fn kcl_test_modify_line_to_close_sketch() {
|
2023-09-17 21:57:43 -07:00
|
|
|
let name = "part002";
|
|
|
|
let code = format!(
|
2023-10-05 14:27:48 -07:00
|
|
|
r#"const {} = startSketchOn("XY")
|
|
|
|
|> startProfileAt([7.91, 3.89], %)
|
2023-09-17 21:57:43 -07:00
|
|
|
|> line([7.42, -8.62], %)
|
|
|
|
|> line([-6.38, -3.51], %)
|
|
|
|
|> line([-3.77, 3.56], %)
|
|
|
|
|> lineTo([7.91, 3.89], %)
|
|
|
|
"#,
|
|
|
|
name
|
|
|
|
);
|
|
|
|
|
2024-03-13 12:56:46 -07:00
|
|
|
let (ctx, program, sketch_id) = setup(&code, name).await.unwrap();
|
2023-09-17 21:57:43 -07:00
|
|
|
let mut new_program = program.clone();
|
2024-03-13 12:56:46 -07:00
|
|
|
let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id)
|
2023-09-17 21:57:43 -07:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Make sure the code is the same.
|
|
|
|
assert_eq!(
|
|
|
|
new_code,
|
|
|
|
format!(
|
2024-10-02 14:19:40 -05:00
|
|
|
r#"{} = startSketchOn("XY")
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([7.91, 3.89], %)
|
2023-09-17 21:57:43 -07:00
|
|
|
|> line([7.42, -8.62], %)
|
|
|
|
|> line([-6.38, -3.51], %)
|
|
|
|
|> line([-3.77, 3.56], %)
|
|
|
|
|> close(%)
|
|
|
|
"#,
|
|
|
|
name
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
2024-08-12 13:06:30 -07:00
|
|
|
async fn kcl_test_modify_with_constraint() {
|
2023-09-17 21:57:43 -07:00
|
|
|
let name = "part002";
|
|
|
|
let code = format!(
|
|
|
|
r#"const thing = 12
|
2023-10-05 14:27:48 -07:00
|
|
|
const {} = startSketchOn("XY")
|
|
|
|
|> startProfileAt([7.91, 3.89], %)
|
2023-09-17 21:57:43 -07:00
|
|
|
|> line([7.42, -8.62], %)
|
|
|
|
|> line([-6.38, -3.51], %)
|
|
|
|
|> line([-3.77, 3.56], %)
|
|
|
|
|> lineTo([thing, 3.89], %)
|
|
|
|
"#,
|
|
|
|
name
|
|
|
|
);
|
|
|
|
|
2024-03-13 12:56:46 -07:00
|
|
|
let (ctx, program, sketch_id) = setup(&code, name).await.unwrap();
|
2023-09-17 21:57:43 -07:00
|
|
|
let mut new_program = program.clone();
|
2024-03-13 12:56:46 -07:00
|
|
|
let result = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id).await;
|
2023-09-17 21:57:43 -07:00
|
|
|
|
|
|
|
assert!(result.is_err());
|
|
|
|
assert_eq!(
|
|
|
|
result.unwrap_err().to_string(),
|
2023-10-05 14:27:48 -07:00
|
|
|
r#"engine: KclErrorDetails { source_ranges: [SourceRange([188, 193])], message: "Sketch part002 is constrained `partial` and cannot be modified" }"#
|
2023-09-17 21:57:43 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
2024-08-12 13:06:30 -07:00
|
|
|
async fn kcl_test_modify_line_should_close_sketch() {
|
2023-09-17 21:57:43 -07:00
|
|
|
let name = "part003";
|
|
|
|
let code = format!(
|
2023-10-05 14:27:48 -07:00
|
|
|
r#"const {} = startSketchOn("XY")
|
|
|
|
|> startProfileAt([13.69, 3.8], %)
|
2023-09-17 21:57:43 -07:00
|
|
|
|> line([4.23, -11.79], %)
|
|
|
|
|> line([-10.7, -1.16], %)
|
|
|
|
|> line([-3.72, 8.69], %)
|
|
|
|
|> line([10.19, 4.26], %)
|
|
|
|
"#,
|
|
|
|
name
|
|
|
|
);
|
|
|
|
|
2024-03-13 12:56:46 -07:00
|
|
|
let (ctx, program, sketch_id) = setup(&code, name).await.unwrap();
|
2023-09-17 21:57:43 -07:00
|
|
|
let mut new_program = program.clone();
|
2024-03-13 12:56:46 -07:00
|
|
|
let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id)
|
2023-09-17 21:57:43 -07:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Make sure the code is the same.
|
|
|
|
assert_eq!(
|
|
|
|
new_code,
|
|
|
|
format!(
|
2024-10-02 14:19:40 -05:00
|
|
|
r#"{} = startSketchOn("XY")
|
2023-10-05 14:27:48 -07:00
|
|
|
|> startProfileAt([13.69, 3.8], %)
|
2023-09-17 21:57:43 -07:00
|
|
|
|> line([4.23, -11.79], %)
|
|
|
|
|> line([-10.7, -1.16], %)
|
|
|
|
|> line([-3.72, 8.69], %)
|
|
|
|
|> close(%)
|
|
|
|
"#,
|
|
|
|
name
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|