use deterministic ids in more places (#6064)
* dont redact the ids now that they are deterministic Signed-off-by: Jess Frazelle <github@jessfraz.com> pass arouund id generator more Signed-off-by: Jess Frazelle <github@jessfraz.com> change the anme space Signed-off-by: Jess Frazelle <github@jessfraz.com> updates and re-run Signed-off-by: Jess Frazelle <github@jessfraz.com> updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixups Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * cleanup old files Signed-off-by: Jess Frazelle <github@jessfraz.com> * cleanup old files Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
@ -153,8 +153,3 @@ harness = false
|
|||||||
name = "executor"
|
name = "executor"
|
||||||
path = "e2e/executor/main.rs"
|
path = "e2e/executor/main.rs"
|
||||||
required-features = ["engine"]
|
required-features = ["engine"]
|
||||||
|
|
||||||
[[test]]
|
|
||||||
name = "modify"
|
|
||||||
path = "e2e/modify/main.rs"
|
|
||||||
required-features = ["engine"]
|
|
||||||
|
@ -1,232 +0,0 @@
|
|||||||
use anyhow::Result;
|
|
||||||
use kcl_lib::{
|
|
||||||
exec::{KclValue, PlaneType},
|
|
||||||
modify_ast_for_sketch, ExecState, ExecutorContext, ModuleId, Program, SourceRange,
|
|
||||||
};
|
|
||||||
use kittycad_modeling_cmds::{each_cmd as mcmd, length_unit::LengthUnit, shared::Point3d, ModelingCmd};
|
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
|
|
||||||
/// Setup the engine and parse code for an ast.
|
|
||||||
async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, ModuleId, uuid::Uuid)> {
|
|
||||||
let program = Program::parse_no_errs(code)?;
|
|
||||||
let ctx = kcl_lib::ExecutorContext::new_with_default_client(Default::default()).await?;
|
|
||||||
let mut exec_state = ExecState::new(&ctx);
|
|
||||||
let result = ctx.run(&program, &mut exec_state).await?;
|
|
||||||
let outcome = exec_state.to_wasm_outcome(result.0).await;
|
|
||||||
|
|
||||||
// We need to get the sketch ID.
|
|
||||||
let KclValue::Sketch { value: sketch } = outcome.variables.get(name).unwrap() else {
|
|
||||||
anyhow::bail!("part001 not found in: {:?}", outcome.variables);
|
|
||||||
};
|
|
||||||
let sketch_id = sketch.id;
|
|
||||||
|
|
||||||
let plane_id = uuid::Uuid::new_v4();
|
|
||||||
ctx.engine
|
|
||||||
.send_modeling_cmd(
|
|
||||||
plane_id,
|
|
||||||
SourceRange::default(),
|
|
||||||
&ModelingCmd::from(mcmd::MakePlane {
|
|
||||||
clobber: false,
|
|
||||||
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?;
|
|
||||||
|
|
||||||
// Enter sketch mode.
|
|
||||||
// We can't get control points without being in sketch mode.
|
|
||||||
// You can however get path info without sketch mode.
|
|
||||||
ctx.engine
|
|
||||||
.send_modeling_cmd(
|
|
||||||
uuid::Uuid::new_v4(),
|
|
||||||
SourceRange::default(),
|
|
||||||
&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 }),
|
|
||||||
adjust_camera: false,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok((ctx, program, ModuleId::default(), sketch_id))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn kcl_test_modify_sketch_part001() {
|
|
||||||
let name = "part001";
|
|
||||||
let code = format!(
|
|
||||||
r#"{} = startSketchOn(XY)
|
|
||||||
|> startProfileAt([8.41, 5.78], %)
|
|
||||||
|> line(end = [7.37, -11])
|
|
||||||
|> line(end = [-8.69, -3.75])
|
|
||||||
|> line(end = [-5, 4.25])
|
|
||||||
"#,
|
|
||||||
name
|
|
||||||
);
|
|
||||||
|
|
||||||
let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap();
|
|
||||||
let mut new_program = program.clone();
|
|
||||||
let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id)
|
|
||||||
.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")]
|
|
||||||
async fn kcl_test_modify_sketch_part002() {
|
|
||||||
let name = "part002";
|
|
||||||
let code = format!(
|
|
||||||
r#"{} = startSketchOn(XY)
|
|
||||||
|> startProfileAt([8.41, 5.78], %)
|
|
||||||
|> line(end = [7.42, -8.62])
|
|
||||||
|> line(end = [-6.38, -3.51])
|
|
||||||
|> line(end = [-3.77, 3.56])
|
|
||||||
"#,
|
|
||||||
name
|
|
||||||
);
|
|
||||||
|
|
||||||
let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap();
|
|
||||||
let mut new_program = program.clone();
|
|
||||||
let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id)
|
|
||||||
.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.
|
|
||||||
async fn kcl_test_modify_close_sketch() {
|
|
||||||
let name = "part002";
|
|
||||||
let code = format!(
|
|
||||||
r#"{} = startSketchOn(XY)
|
|
||||||
|> startProfileAt([7.91, 3.89], %)
|
|
||||||
|> line(end = [7.42, -8.62])
|
|
||||||
|> line(end = [-6.38, -3.51])
|
|
||||||
|> line(end = [-3.77, 3.56])
|
|
||||||
|> close()
|
|
||||||
"#,
|
|
||||||
name
|
|
||||||
);
|
|
||||||
|
|
||||||
let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap();
|
|
||||||
let mut new_program = program.clone();
|
|
||||||
let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id)
|
|
||||||
.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")]
|
|
||||||
async fn kcl_test_modify_line_to_close_sketch() {
|
|
||||||
let name = "part002";
|
|
||||||
let code = format!(
|
|
||||||
r#"const {} = startSketchOn(XY)
|
|
||||||
|> startProfileAt([7.91, 3.89], %)
|
|
||||||
|> line(end = [7.42, -8.62])
|
|
||||||
|> line(end = [-6.38, -3.51])
|
|
||||||
|> line(end = [-3.77, 3.56])
|
|
||||||
|> line(endAbsolute = [7.91, 3.89])
|
|
||||||
"#,
|
|
||||||
name
|
|
||||||
);
|
|
||||||
|
|
||||||
let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap();
|
|
||||||
let mut new_program = program.clone();
|
|
||||||
let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Make sure the code is the same.
|
|
||||||
assert_eq!(
|
|
||||||
new_code,
|
|
||||||
format!(
|
|
||||||
r#"{} = startSketchOn(XY)
|
|
||||||
|> startProfileAt([7.91, 3.89], %)
|
|
||||||
|> line(end = [7.42, -8.62])
|
|
||||||
|> line(end = [-6.38, -3.51])
|
|
||||||
|> line(end = [-3.77, 3.56])
|
|
||||||
|> close()
|
|
||||||
"#,
|
|
||||||
name
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn kcl_test_modify_with_constraint() {
|
|
||||||
let name = "part002";
|
|
||||||
let code = format!(
|
|
||||||
r#"const thing = 12
|
|
||||||
const {} = startSketchOn(XY)
|
|
||||||
|> startProfileAt([7.91, 3.89], %)
|
|
||||||
|> line(end = [7.42, -8.62])
|
|
||||||
|> line(end = [-6.38, -3.51])
|
|
||||||
|> line(end = [-3.77, 3.56])
|
|
||||||
|> line(endAbsolute = [thing, 3.89])
|
|
||||||
"#,
|
|
||||||
name
|
|
||||||
);
|
|
||||||
|
|
||||||
let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap();
|
|
||||||
let mut new_program = program.clone();
|
|
||||||
let result = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id).await;
|
|
||||||
|
|
||||||
assert!(result.is_err());
|
|
||||||
assert_eq!(
|
|
||||||
result.unwrap_err().message(),
|
|
||||||
"Sketch part002 is constrained `partial` and cannot be modified",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn kcl_test_modify_line_should_close_sketch() {
|
|
||||||
let name = "part003";
|
|
||||||
let code = format!(
|
|
||||||
r#"const {} = startSketchOn(XY)
|
|
||||||
|> startProfileAt([13.69, 3.8], %)
|
|
||||||
|> line(end = [4.23, -11.79])
|
|
||||||
|> line(end = [-10.7, -1.16])
|
|
||||||
|> line(end = [-3.72, 8.69])
|
|
||||||
|> line(end = [10.19, 4.26])
|
|
||||||
"#,
|
|
||||||
name
|
|
||||||
);
|
|
||||||
|
|
||||||
let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap();
|
|
||||||
let mut new_program = program.clone();
|
|
||||||
let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Make sure the code is the same.
|
|
||||||
assert_eq!(
|
|
||||||
new_code,
|
|
||||||
format!(
|
|
||||||
r#"{} = startSketchOn(XY)
|
|
||||||
|> startProfileAt([13.69, 3.8], %)
|
|
||||||
|> line(end = [4.23, -11.79])
|
|
||||||
|> line(end = [-10.7, -1.16])
|
|
||||||
|> line(end = [-3.72, 8.69])
|
|
||||||
|> close()
|
|
||||||
"#,
|
|
||||||
name
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
@ -170,7 +170,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
self.clear_queues().await;
|
self.clear_queues().await;
|
||||||
|
|
||||||
self.batch_modeling_cmd(
|
self.batch_modeling_cmd(
|
||||||
uuid::Uuid::new_v4(),
|
id_generator.next_uuid(),
|
||||||
source_range,
|
source_range,
|
||||||
&ModelingCmd::SceneClearAll(mcmd::SceneClearAll::default()),
|
&ModelingCmd::SceneClearAll(mcmd::SceneClearAll::default()),
|
||||||
)
|
)
|
||||||
@ -195,9 +195,10 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
&self,
|
&self,
|
||||||
visible: bool,
|
visible: bool,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
|
id_generator: &mut IdGenerator,
|
||||||
) -> Result<(), crate::errors::KclError> {
|
) -> Result<(), crate::errors::KclError> {
|
||||||
self.batch_modeling_cmd(
|
self.batch_modeling_cmd(
|
||||||
uuid::Uuid::new_v4(),
|
id_generator.next_uuid(),
|
||||||
source_range,
|
source_range,
|
||||||
&ModelingCmd::from(mcmd::EdgeLinesVisible { hidden: !visible }),
|
&ModelingCmd::from(mcmd::EdgeLinesVisible { hidden: !visible }),
|
||||||
)
|
)
|
||||||
@ -231,10 +232,11 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
&self,
|
&self,
|
||||||
units: crate::UnitLength,
|
units: crate::UnitLength,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
|
id_generator: &mut IdGenerator,
|
||||||
) -> Result<(), crate::errors::KclError> {
|
) -> Result<(), crate::errors::KclError> {
|
||||||
// Before we even start executing the program, set the units.
|
// Before we even start executing the program, set the units.
|
||||||
self.batch_modeling_cmd(
|
self.batch_modeling_cmd(
|
||||||
uuid::Uuid::new_v4(),
|
id_generator.next_uuid(),
|
||||||
source_range,
|
source_range,
|
||||||
&ModelingCmd::from(mcmd::SetSceneUnits { unit: units.into() }),
|
&ModelingCmd::from(mcmd::SetSceneUnits { unit: units.into() }),
|
||||||
)
|
)
|
||||||
@ -248,15 +250,18 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
&self,
|
&self,
|
||||||
settings: &crate::ExecutorSettings,
|
settings: &crate::ExecutorSettings,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
|
id_generator: &mut IdGenerator,
|
||||||
) -> Result<(), crate::errors::KclError> {
|
) -> Result<(), crate::errors::KclError> {
|
||||||
// Set the edge visibility.
|
// Set the edge visibility.
|
||||||
self.set_edge_visibility(settings.highlight_edges, source_range).await?;
|
self.set_edge_visibility(settings.highlight_edges, source_range, id_generator)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Change the units.
|
// Change the units.
|
||||||
self.set_units(settings.units, source_range).await?;
|
self.set_units(settings.units, source_range, id_generator).await?;
|
||||||
|
|
||||||
// Send the command to show the grid.
|
// Send the command to show the grid.
|
||||||
self.modify_grid(!settings.show_grid, source_range).await?;
|
self.modify_grid(!settings.show_grid, source_range, id_generator)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// We do not have commands for changing ssao on the fly.
|
// We do not have commands for changing ssao on the fly.
|
||||||
|
|
||||||
@ -502,6 +507,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
y_axis: Point3d,
|
y_axis: Point3d,
|
||||||
color: Option<Color>,
|
color: Option<Color>,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
|
id_generator: &mut IdGenerator,
|
||||||
) -> Result<uuid::Uuid, KclError> {
|
) -> Result<uuid::Uuid, KclError> {
|
||||||
// Create new default planes.
|
// Create new default planes.
|
||||||
let default_size = 100.0;
|
let default_size = 100.0;
|
||||||
@ -524,7 +530,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
if let Some(color) = color {
|
if let Some(color) = color {
|
||||||
// Set the color.
|
// Set the color.
|
||||||
self.batch_modeling_cmd(
|
self.batch_modeling_cmd(
|
||||||
uuid::Uuid::new_v4(),
|
id_generator.next_uuid(),
|
||||||
source_range,
|
source_range,
|
||||||
&ModelingCmd::from(mcmd::PlaneSetColor { color, plane_id }),
|
&ModelingCmd::from(mcmd::PlaneSetColor { color, plane_id }),
|
||||||
)
|
)
|
||||||
@ -615,7 +621,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
for (name, plane_id, x_axis, y_axis, color) in plane_settings {
|
for (name, plane_id, x_axis, y_axis, color) in plane_settings {
|
||||||
planes.insert(
|
planes.insert(
|
||||||
name,
|
name,
|
||||||
self.make_default_plane(plane_id, x_axis, y_axis, color, source_range)
|
self.make_default_plane(plane_id, x_axis, y_axis, color, source_range, id_generator)
|
||||||
.await?,
|
.await?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -701,10 +707,15 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn modify_grid(&self, hidden: bool, source_range: SourceRange) -> Result<(), KclError> {
|
async fn modify_grid(
|
||||||
|
&self,
|
||||||
|
hidden: bool,
|
||||||
|
source_range: SourceRange,
|
||||||
|
id_generator: &mut IdGenerator,
|
||||||
|
) -> Result<(), KclError> {
|
||||||
// Hide/show the grid.
|
// Hide/show the grid.
|
||||||
self.batch_modeling_cmd(
|
self.batch_modeling_cmd(
|
||||||
uuid::Uuid::new_v4(),
|
id_generator.next_uuid(),
|
||||||
source_range,
|
source_range,
|
||||||
&ModelingCmd::from(mcmd::ObjectVisible {
|
&ModelingCmd::from(mcmd::ObjectVisible {
|
||||||
hidden,
|
hidden,
|
||||||
@ -715,7 +726,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
|
|
||||||
// Hide/show the grid scale text.
|
// Hide/show the grid scale text.
|
||||||
self.batch_modeling_cmd(
|
self.batch_modeling_cmd(
|
||||||
uuid::Uuid::new_v4(),
|
id_generator.next_uuid(),
|
||||||
source_range,
|
source_range,
|
||||||
&ModelingCmd::from(mcmd::ObjectVisible {
|
&ModelingCmd::from(mcmd::ObjectVisible {
|
||||||
hidden,
|
hidden,
|
||||||
|
@ -64,7 +64,11 @@ impl ExecutorContext {
|
|||||||
let new_units = exec_state.length_unit();
|
let new_units = exec_state.length_unit();
|
||||||
if !self.engine.execution_kind().await.is_isolated() {
|
if !self.engine.execution_kind().await.is_isolated() {
|
||||||
self.engine
|
self.engine
|
||||||
.set_units(new_units.into(), annotation.as_source_range())
|
.set_units(
|
||||||
|
new_units.into(),
|
||||||
|
annotation.as_source_range(),
|
||||||
|
exec_state.id_generator(),
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -143,7 +147,9 @@ impl ExecutorContext {
|
|||||||
// command and we'd need to flush the batch again.
|
// command and we'd need to flush the batch again.
|
||||||
// This avoids that.
|
// This avoids that.
|
||||||
if !exec_kind.is_isolated() && new_units != old_units && *path != ModulePath::Main {
|
if !exec_kind.is_isolated() && new_units != old_units && *path != ModulePath::Main {
|
||||||
self.engine.set_units(old_units.into(), Default::default()).await?;
|
self.engine
|
||||||
|
.set_units(old_units.into(), Default::default(), exec_state.id_generator())
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
self.engine.replace_execution_kind(original_execution).await;
|
self.engine.replace_execution_kind(original_execution).await;
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::execution::ModuleId;
|
use crate::execution::ModuleId;
|
||||||
|
|
||||||
const NAMESPACE_KCL: uuid::Uuid = uuid::uuid!("efcd6508-4ce6-4a09-8317-e6a6994a3cd7");
|
const NAMESPACE_KCL: uuid::Uuid = uuid::uuid!("8bda3118-75eb-58c7-a866-bef1dcb495e7");
|
||||||
|
|
||||||
/// A generator for ArtifactIds that can be stable across executions.
|
/// A generator for ArtifactIds that can be stable across executions.
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
#[derive(Debug, Clone, Default, PartialEq)]
|
||||||
|
@ -618,7 +618,7 @@ impl ExecutorContext {
|
|||||||
if reapply_settings
|
if reapply_settings
|
||||||
&& self
|
&& self
|
||||||
.engine
|
.engine
|
||||||
.reapply_settings(&self.settings, Default::default())
|
.reapply_settings(&self.settings, Default::default(), old_state.id_generator())
|
||||||
.await
|
.await
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
@ -636,7 +636,7 @@ impl ExecutorContext {
|
|||||||
CacheResult::NoAction(true) => {
|
CacheResult::NoAction(true) => {
|
||||||
if self
|
if self
|
||||||
.engine
|
.engine
|
||||||
.reapply_settings(&self.settings, Default::default())
|
.reapply_settings(&self.settings, Default::default(), old_state.id_generator())
|
||||||
.await
|
.await
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
@ -737,7 +737,7 @@ impl ExecutorContext {
|
|||||||
|
|
||||||
// Re-apply the settings, in case the cache was busted.
|
// Re-apply the settings, in case the cache was busted.
|
||||||
self.engine
|
self.engine
|
||||||
.reapply_settings(&self.settings, Default::default())
|
.reapply_settings(&self.settings, Default::default(), exec_state.id_generator())
|
||||||
.await
|
.await
|
||||||
.map_err(KclErrorWithOutputs::no_outputs)?;
|
.map_err(KclErrorWithOutputs::no_outputs)?;
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ pub use lsp::{
|
|||||||
kcl::{Backend as KclLspBackend, Server as KclLspServerSubCommand},
|
kcl::{Backend as KclLspBackend, Server as KclLspServerSubCommand},
|
||||||
};
|
};
|
||||||
pub use modules::ModuleId;
|
pub use modules::ModuleId;
|
||||||
pub use parsing::ast::{modify::modify_ast_for_sketch, types::FormatOptions};
|
pub use parsing::ast::types::FormatOptions;
|
||||||
pub use settings::types::{project::ProjectConfiguration, Configuration, UnitLength};
|
pub use settings::types::{project::ProjectConfiguration, Configuration, UnitLength};
|
||||||
pub use source_range::SourceRange;
|
pub use source_range::SourceRange;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
pub(crate) mod digest;
|
pub(crate) mod digest;
|
||||||
pub mod modify;
|
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -1,285 +0,0 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use kcmc::{
|
|
||||||
each_cmd as mcmd, ok_response::OkModelingCmdResponse, shared::PathCommand, websocket::OkWebSocketResponseData,
|
|
||||||
ModelingCmd,
|
|
||||||
};
|
|
||||||
use kittycad_modeling_cmds as kcmc;
|
|
||||||
|
|
||||||
use super::types::{CallExpressionKw, Identifier, LabeledArg, LiteralValue};
|
|
||||||
use crate::{
|
|
||||||
engine::EngineManager,
|
|
||||||
errors::{KclError, KclErrorDetails},
|
|
||||||
execution::Point2d,
|
|
||||||
parsing::ast::types::{
|
|
||||||
ArrayExpression, CallExpression, ConstraintLevel, FormatOptions, Literal, Node, PipeExpression,
|
|
||||||
PipeSubstitution, VariableDeclarator,
|
|
||||||
},
|
|
||||||
source_range::SourceRange,
|
|
||||||
ModuleId, Program,
|
|
||||||
};
|
|
||||||
|
|
||||||
type Point3d = kcmc::shared::Point3d<f64>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
/// The control point data for a curve or line.
|
|
||||||
struct ControlPointData {
|
|
||||||
/// The control points for the curve or line.
|
|
||||||
points: Vec<Point3d>,
|
|
||||||
/// The command that created this curve or line.
|
|
||||||
_command: PathCommand,
|
|
||||||
/// The id of the curve or line.
|
|
||||||
_id: uuid::Uuid,
|
|
||||||
}
|
|
||||||
|
|
||||||
const EPSILON: f64 = 0.015625; // or 2^-6
|
|
||||||
|
|
||||||
/// Update the AST to reflect the new state of the program after something like
|
|
||||||
/// a move or a new line.
|
|
||||||
pub async fn modify_ast_for_sketch(
|
|
||||||
engine: &Arc<Box<dyn EngineManager>>,
|
|
||||||
program: &mut Program,
|
|
||||||
module_id: ModuleId,
|
|
||||||
// The name of the sketch.
|
|
||||||
sketch_name: &str,
|
|
||||||
// The type of plane the sketch is on. `XY` or `XZ`, etc
|
|
||||||
plane: crate::execution::PlaneType,
|
|
||||||
// The ID of the parent sketch.
|
|
||||||
sketch_id: uuid::Uuid,
|
|
||||||
) -> Result<String, KclError> {
|
|
||||||
// First we need to check if this sketch is constrained (even partially).
|
|
||||||
// If it is, we cannot modify it.
|
|
||||||
|
|
||||||
// Get the information about the sketch.
|
|
||||||
if let Some(ast_sketch) = program.ast.get_variable(sketch_name) {
|
|
||||||
let constraint_level = match ast_sketch {
|
|
||||||
super::types::Definition::Variable(var) => var.get_constraint_level(),
|
|
||||||
super::types::Definition::Import(import) => import.get_constraint_level(),
|
|
||||||
super::types::Definition::Type(_) => ConstraintLevel::Ignore {
|
|
||||||
source_ranges: Vec::new(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
match &constraint_level {
|
|
||||||
ConstraintLevel::None { source_ranges: _ } => {}
|
|
||||||
ConstraintLevel::Ignore { source_ranges: _ } => {}
|
|
||||||
ConstraintLevel::Partial {
|
|
||||||
source_ranges: _,
|
|
||||||
levels,
|
|
||||||
} => {
|
|
||||||
return Err(KclError::Engine(KclErrorDetails {
|
|
||||||
message: format!(
|
|
||||||
"Sketch {} is constrained `{}` and cannot be modified",
|
|
||||||
sketch_name, constraint_level
|
|
||||||
),
|
|
||||||
source_ranges: levels.get_all_partial_or_full_source_ranges(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
ConstraintLevel::Full { source_ranges } => {
|
|
||||||
return Err(KclError::Engine(KclErrorDetails {
|
|
||||||
message: format!(
|
|
||||||
"Sketch {} is constrained `{}` and cannot be modified",
|
|
||||||
sketch_name, constraint_level
|
|
||||||
),
|
|
||||||
source_ranges: source_ranges.clone(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let's start by getting the path info.
|
|
||||||
|
|
||||||
// Let's get the path info.
|
|
||||||
let resp = engine
|
|
||||||
.send_modeling_cmd(
|
|
||||||
uuid::Uuid::new_v4(),
|
|
||||||
SourceRange::default(),
|
|
||||||
&ModelingCmd::PathGetInfo(mcmd::PathGetInfo { path_id: sketch_id }),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let OkWebSocketResponseData::Modeling {
|
|
||||||
modeling_response: OkModelingCmdResponse::PathGetInfo(path_info),
|
|
||||||
} = &resp
|
|
||||||
else {
|
|
||||||
return Err(KclError::Engine(KclErrorDetails {
|
|
||||||
message: format!("Get path info response was not as expected: {:?}", resp),
|
|
||||||
source_ranges: vec![SourceRange::default()],
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Now let's get the control points for all the segments.
|
|
||||||
// TODO: We should probably await all these at once so we aren't going one by one.
|
|
||||||
// But I guess this is fine for now.
|
|
||||||
// We absolutely have to preserve the order of the control points.
|
|
||||||
let mut control_points = Vec::new();
|
|
||||||
for segment in &path_info.segments {
|
|
||||||
if let Some(command_id) = &segment.command_id {
|
|
||||||
let cmd = ModelingCmd::from(mcmd::CurveGetControlPoints {
|
|
||||||
curve_id: (*command_id).into(),
|
|
||||||
});
|
|
||||||
let h = engine.send_modeling_cmd(uuid::Uuid::new_v4(), SourceRange::default(), &cmd);
|
|
||||||
|
|
||||||
let OkWebSocketResponseData::Modeling {
|
|
||||||
modeling_response: OkModelingCmdResponse::CurveGetControlPoints(data),
|
|
||||||
} = h.await?
|
|
||||||
else {
|
|
||||||
return Err(KclError::Engine(KclErrorDetails {
|
|
||||||
message: format!("Curve get control points response was not as expected: {:?}", resp),
|
|
||||||
source_ranges: vec![SourceRange::default()],
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
control_points.push(ControlPointData {
|
|
||||||
points: data.control_points.clone(),
|
|
||||||
_command: segment.command,
|
|
||||||
_id: (*command_id).into(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if control_points.is_empty() {
|
|
||||||
return Err(KclError::Engine(KclErrorDetails {
|
|
||||||
message: format!("No control points found for sketch {}", sketch_name),
|
|
||||||
source_ranges: vec![SourceRange::default()],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
let first_control_points = control_points.first().ok_or_else(|| {
|
|
||||||
KclError::Engine(KclErrorDetails {
|
|
||||||
message: format!("No control points found for sketch {}", sketch_name),
|
|
||||||
source_ranges: vec![SourceRange::default()],
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let mut additional_lines = Vec::new();
|
|
||||||
let mut last_point = first_control_points.points[1];
|
|
||||||
for control_point in control_points[1..].iter() {
|
|
||||||
additional_lines.push([
|
|
||||||
(control_point.points[1].x - last_point.x),
|
|
||||||
(control_point.points[1].y - last_point.y),
|
|
||||||
]);
|
|
||||||
last_point = Point3d {
|
|
||||||
x: control_point.points[1].x,
|
|
||||||
y: control_point.points[1].y,
|
|
||||||
z: control_point.points[1].z,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Okay now let's recalculate the sketch from the control points.
|
|
||||||
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),
|
|
||||||
};
|
|
||||||
let sketch = create_start_sketch_on(
|
|
||||||
sketch_name,
|
|
||||||
[first_control_points.points[0].x, first_control_points.points[0].y],
|
|
||||||
[start_sketch_at_end.x, start_sketch_at_end.y],
|
|
||||||
plane,
|
|
||||||
additional_lines,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Add the sketch back to the program.
|
|
||||||
program.ast.replace_variable(sketch_name, sketch);
|
|
||||||
|
|
||||||
let recasted = program.ast.recast(&FormatOptions::default(), 0);
|
|
||||||
|
|
||||||
// Re-parse the ast so we get the correct source ranges.
|
|
||||||
program.ast = crate::parsing::parse_str(&recasted, module_id).parse_errs_as_err()?;
|
|
||||||
|
|
||||||
Ok(recasted)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a pipe expression that starts a sketch at the given point and draws a line to the given point.
|
|
||||||
fn create_start_sketch_on(
|
|
||||||
name: &str,
|
|
||||||
start: [f64; 2],
|
|
||||||
end: [f64; 2],
|
|
||||||
plane: crate::execution::PlaneType,
|
|
||||||
additional_lines: Vec<[f64; 2]>,
|
|
||||||
) -> Result<Node<VariableDeclarator>, KclError> {
|
|
||||||
let start_sketch_on = CallExpression::new("startSketchOn", vec![Literal::new(plane.to_string().into()).into()])?;
|
|
||||||
let start_profile_at = CallExpression::new(
|
|
||||||
"startProfileAt",
|
|
||||||
vec![
|
|
||||||
ArrayExpression::new(vec![
|
|
||||||
Literal::new(LiteralValue::from_f64_no_uom(round_before_recast(start[0]))).into(),
|
|
||||||
Literal::new(LiteralValue::from_f64_no_uom(round_before_recast(start[1]))).into(),
|
|
||||||
])
|
|
||||||
.into(),
|
|
||||||
PipeSubstitution::new().into(),
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Keep track of where we are so we can close the sketch if we need to.
|
|
||||||
let mut current_position = Point2d {
|
|
||||||
x: start[0],
|
|
||||||
y: start[1],
|
|
||||||
};
|
|
||||||
current_position.x += end[0];
|
|
||||||
current_position.y += end[1];
|
|
||||||
|
|
||||||
let expr = ArrayExpression::new(vec![
|
|
||||||
Literal::new(LiteralValue::from_f64_no_uom(round_before_recast(end[0]))).into(),
|
|
||||||
Literal::new(LiteralValue::from_f64_no_uom(round_before_recast(end[1]))).into(),
|
|
||||||
])
|
|
||||||
.into();
|
|
||||||
let initial_line = CallExpressionKw::new(
|
|
||||||
"line",
|
|
||||||
None,
|
|
||||||
vec![LabeledArg {
|
|
||||||
label: Node::no_src(super::types::Identifier {
|
|
||||||
name: "end".to_owned(),
|
|
||||||
digest: None,
|
|
||||||
}),
|
|
||||||
arg: expr,
|
|
||||||
}],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut pipe_body = vec![start_sketch_on.into(), start_profile_at.into(), initial_line.into()];
|
|
||||||
|
|
||||||
for (index, line) in additional_lines.iter().enumerate() {
|
|
||||||
current_position.x += line[0];
|
|
||||||
current_position.y += line[1];
|
|
||||||
|
|
||||||
// If we are on the last line, check if we have to close the sketch.
|
|
||||||
if index == additional_lines.len() - 1 {
|
|
||||||
let diff_x = (current_position.x - start[0]).abs();
|
|
||||||
let diff_y = (current_position.y - start[1]).abs();
|
|
||||||
// Compare the end of the last line to the start of the first line.
|
|
||||||
// This is a bit more lenient if you look at the value of epsilon.
|
|
||||||
if diff_x <= EPSILON && diff_y <= EPSILON {
|
|
||||||
// We have to close the sketch.
|
|
||||||
let close = CallExpressionKw::new("close", None, vec![])?;
|
|
||||||
pipe_body.push(close.into());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: we should check if we should close the sketch.
|
|
||||||
let expr = ArrayExpression::new(vec![
|
|
||||||
Literal::new(LiteralValue::from_f64_no_uom(round_before_recast(line[0]))).into(),
|
|
||||||
Literal::new(LiteralValue::from_f64_no_uom(round_before_recast(line[1]))).into(),
|
|
||||||
])
|
|
||||||
.into();
|
|
||||||
let line = CallExpressionKw::new(
|
|
||||||
"line",
|
|
||||||
None,
|
|
||||||
vec![LabeledArg {
|
|
||||||
arg: expr,
|
|
||||||
label: Node::no_src(Identifier {
|
|
||||||
name: "end".to_owned(),
|
|
||||||
digest: None,
|
|
||||||
}),
|
|
||||||
}],
|
|
||||||
)?;
|
|
||||||
pipe_body.push(line.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(VariableDeclarator::new(name, PipeExpression::new(pipe_body).into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn round_before_recast(num: f64) -> f64 {
|
|
||||||
// We use 2 decimal places.
|
|
||||||
(num * 100.0).round() / 100.0
|
|
||||||
}
|
|
@ -66,7 +66,7 @@ pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclVal
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = inner_appearance(solids, data.color, data.metalness, data.roughness, args).await?;
|
let result = inner_appearance(solids, data.color, data.metalness, data.roughness, exec_state, args).await?;
|
||||||
Ok(result.into())
|
Ok(result.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,6 +287,7 @@ async fn inner_appearance(
|
|||||||
color: String,
|
color: String,
|
||||||
metalness: Option<f64>,
|
metalness: Option<f64>,
|
||||||
roughness: Option<f64>,
|
roughness: Option<f64>,
|
||||||
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Vec<Solid>, KclError> {
|
) -> Result<Vec<Solid>, KclError> {
|
||||||
for solid in &solids {
|
for solid in &solids {
|
||||||
@ -306,7 +307,7 @@ async fn inner_appearance(
|
|||||||
};
|
};
|
||||||
|
|
||||||
args.batch_modeling_cmd(
|
args.batch_modeling_cmd(
|
||||||
uuid::Uuid::new_v4(),
|
exec_state.next_uuid(),
|
||||||
ModelingCmd::from(mcmd::ObjectSetMaterialParamsPbr {
|
ModelingCmd::from(mcmd::ObjectSetMaterialParamsPbr {
|
||||||
object_id: solid.id,
|
object_id: solid.id,
|
||||||
color,
|
color,
|
||||||
|
Reference in New Issue
Block a user