diff --git a/rust/kcl-lib/src/execution/artifact.rs b/rust/kcl-lib/src/execution/artifact.rs index 7b24a94702a..cc3958e783a 100644 --- a/rust/kcl-lib/src/execution/artifact.rs +++ b/rust/kcl-lib/src/execution/artifact.rs @@ -12,7 +12,7 @@ use uuid::Uuid; use crate::{ KclError, NodePath, SourceRange, errors::KclErrorDetails, - execution::ArtifactId, + execution::{ArtifactId, id_generator::generate_engine_id}, parsing::ast::types::{Node, Program}, }; @@ -583,6 +583,9 @@ pub(super) fn build_artifact_graph( let item_count = initial_graph.item_count; let mut map = initial_graph.into_map(); + #[cfg(target_arch = "wasm32")] + web_sys::console::warn_1(&format!("initial_graph {map:#?}").into()); + let mut path_to_plane_id_map = FnvHashMap::default(); let mut current_plane_id = None; @@ -594,6 +597,7 @@ pub(super) fn build_artifact_graph( fill_in_node_paths(exec_artifact, ast, item_count); } + let mut pi: u32 = 0; for artifact_command in artifact_commands { if let ModelingCmd::EnableSketchMode(EnableSketchMode { entity_id, .. }) = artifact_command.command { current_plane_id = Some(entity_id); @@ -620,9 +624,26 @@ pub(super) fn build_artifact_graph( ast, item_count, exec_artifacts, + pi, )?; + match artifact_command.command { + ModelingCmd::ClosePath(_) => { + pi = 0; + } + ModelingCmd::ExtendPath(_) => { + pi += 1; + } + _ => {} + } + + //#[cfg(target_arch = "wasm32")] + //web_sys::console::warn_1(&format!("artifact_command {artifact_command:#?}").into()); + for artifact in artifact_updates { // Merge with existing artifacts. + //#[cfg(target_arch = "wasm32")] + //web_sys::console::warn_1(&format!("artifact to insert {artifact:#?}").into()); + merge_artifact_into_map(&mut map, artifact); } } @@ -741,6 +762,7 @@ fn artifacts_to_update( ast: &Node, cached_body_items: usize, exec_artifacts: &IndexMap, + pi: u32, ) -> Result, KclError> { let uuid = artifact_command.cmd_id; let response = responses.get(&uuid); @@ -881,8 +903,14 @@ fn artifacts_to_update( ), }); let mut return_arr = Vec::new(); + + let base = path_id.into(); + + let path_modifier = format!("path_{}", pi); + let curve_id = ArtifactId::new(generate_engine_id(base, &path_modifier)); + return_arr.push(Artifact::Segment(Segment { - id, + id: curve_id, path_id, surface_id: None, edge_ids: Vec::new(), @@ -893,7 +921,7 @@ fn artifacts_to_update( let path = artifacts.get(&path_id); if let Some(Artifact::Path(path)) = path { let mut new_path = path.clone(); - new_path.seg_ids = vec![id]; + new_path.seg_ids = vec![curve_id]; return_arr.push(Artifact::Path(new_path)); } if let Some(OkModelingCmdResponse::ClosePath(close_path)) = response { @@ -1050,28 +1078,51 @@ fn artifacts_to_update( } return Ok(return_arr); } - ModelingCmd::Solid3dGetExtrusionFaceInfo(_) => { + ModelingCmd::Solid3dGetExtrusionFaceInfo(kcmc::Solid3dGetExtrusionFaceInfo { object_id, .. }) => { let Some(OkModelingCmdResponse::Solid3dGetExtrusionFaceInfo(face_info)) = response else { return Ok(Vec::new()); }; + + #[cfg(target_arch = "wasm32")] + web_sys::console::warn_1(&format!("Solid3dGetExtrusionFaceInfo {face_info:#?}").into()); + let mut return_arr = Vec::new(); let mut last_path = None; + + let base = *object_id; + let mut pi: u32 = 0; + for face in &face_info.faces { if face.cap != ExtrusionFaceCapType::None { continue; } - let Some(curve_id) = face.curve_id.map(ArtifactId::new) else { - continue; - }; - let Some(face_id) = face.face_id.map(ArtifactId::new) else { - continue; - }; + // let Some(curve_id) = face.curve_id.map(ArtifactId::new) else { + // continue; + // }; + // let Some(_face_id) = face.face_id.map(ArtifactId::new) else { + // continue; + // }; + + #[cfg(target_arch = "wasm32")] + web_sys::console::warn_1(&format!("artifacts{artifacts:#?}").into()); + + let path_modifier = format!("path_{}", pi); + pi += 1; + let face_id = ArtifactId::new(generate_engine_id(base, &format!("{}_face", path_modifier))); + let curve_id = ArtifactId::new(generate_engine_id(base, &path_modifier)); // aka edge/segment id, eg.: "path_0" + let Some(Artifact::Segment(seg)) = artifacts.get(&curve_id) else { + #[cfg(target_arch = "wasm32")] + web_sys::console::warn_1(&format!("segment not found {curve_id:#?}, {artifacts:#?}").into()); + continue; }; let Some(Artifact::Path(path)) = artifacts.get(&seg.path_id) else { + #[cfg(target_arch = "wasm32")] + web_sys::console::warn_1(&format!("no path???").into()); continue; }; + last_path = Some(path); let Some(path_sweep_id) = path.sweep_id else { // If the path doesn't have a sweep ID, check if it's a @@ -1104,6 +1155,11 @@ fn artifacts_to_update( // TODO: If we didn't find it, it's probably a bug. .unwrap_or_default(); + #[cfg(target_arch = "wasm32")] + web_sys::console::warn_1( + &format!("add wall: face_id: {face_id:#?}, curve_id: {curve_id:#?}, index: {pi:#?}").into(), + ); + return_arr.push(Artifact::Wall(Wall { id: face_id, seg_id: curve_id, @@ -1129,9 +1185,13 @@ fn artifacts_to_update( ExtrusionFaceCapType::Bottom => CapSubType::Start, ExtrusionFaceCapType::None | ExtrusionFaceCapType::Both => continue, }; - let Some(face_id) = face.face_id.map(ArtifactId::new) else { - continue; - }; + let face_id = ArtifactId::new(generate_engine_id( + base, + match sub_type { + CapSubType::Start => "face_bottom", + CapSubType::End => "face_top", + }, + )); let Some(path_sweep_id) = path.sweep_id else { // If the path doesn't have a sweep ID, check if it's a // hole. @@ -1181,32 +1241,51 @@ fn artifacts_to_update( } return Ok(return_arr); } - ModelingCmd::Solid3dGetAdjacencyInfo(kcmc::Solid3dGetAdjacencyInfo { .. }) => { + ModelingCmd::Solid3dGetAdjacencyInfo(kcmc::Solid3dGetAdjacencyInfo { object_id, .. }) => { let Some(OkModelingCmdResponse::Solid3dGetAdjacencyInfo(info)) = response else { return Ok(Vec::new()); }; + #[cfg(target_arch = "wasm32")] + web_sys::console::warn_1(&format!("Solid3dGetAdjacencyInfo {info:#?}").into()); + + let base = *object_id; + let mut return_arr = Vec::new(); + let mut pi: u32 = 0; for (index, edge) in info.edges.iter().enumerate() { - let Some(original_info) = &edge.original_info else { - continue; - }; - let edge_id = ArtifactId::new(original_info.edge_id); + let path_modifier = format!("path_{}", pi); + let edge_id = ArtifactId::new(generate_engine_id(base, &path_modifier)); // aka edge/segment id, eg.: "path_0" + let face_id = ArtifactId::new(generate_engine_id(base, &format!("{}_face", path_modifier))); + let next_face_id = ArtifactId::new(generate_engine_id( + base, + &format!("{}_face", format!("path_{}", (pi + 1) % (info.edges.len() as u32))), + )); + pi += 1; + let Some(artifact) = artifacts.get(&edge_id) else { continue; }; + match artifact { Artifact::Segment(segment) => { let mut new_segment = segment.clone(); - new_segment.common_surface_ids = - original_info.faces.iter().map(|face| ArtifactId::new(*face)).collect(); + new_segment.common_surface_ids = vec![ + face_id, + ArtifactId::new(generate_engine_id(base, "face_bottom")), // cap start + ] + .into_iter() + .filter(|id| artifacts.contains_key(id)) + .collect(); + return_arr.push(Artifact::Segment(new_segment)); } - Artifact::SweepEdge(sweep_edge) => { - let mut new_sweep_edge = sweep_edge.clone(); - new_sweep_edge.common_surface_ids = - original_info.faces.iter().map(|face| ArtifactId::new(*face)).collect(); - return_arr.push(Artifact::SweepEdge(new_sweep_edge)); + Artifact::SweepEdge(_sweep_edge) => { + // TODO is this ever used? + // let mut new_sweep_edge = sweep_edge.clone(); + // new_sweep_edge.common_surface_ids = + // original_info.faces.iter().map(|face| ArtifactId::new(*face)).collect(); + // return_arr.push(Artifact::SweepEdge(new_sweep_edge)); } _ => {} }; @@ -1214,9 +1293,9 @@ fn artifacts_to_update( let Some(Artifact::Segment(segment)) = artifacts.get(&edge_id) else { continue; }; - let Some(surface_id) = segment.surface_id else { - continue; - }; + + let surface_id = ArtifactId::new(generate_engine_id(base, &format!("{}_face", path_modifier))); + let Some(Artifact::Wall(wall)) = artifacts.get(&surface_id) else { continue; }; @@ -1227,46 +1306,55 @@ fn artifacts_to_update( continue; }; - if let Some(opposite_info) = &edge.opposite_info { - return_arr.push(Artifact::SweepEdge(SweepEdge { - id: opposite_info.edge_id.into(), - sub_type: SweepEdgeSubType::Opposite, - seg_id: edge_id, - cmd_id: artifact_command.cmd_id, - index, - sweep_id: sweep.id, - common_surface_ids: opposite_info.faces.iter().map(|face| ArtifactId::new(*face)).collect(), - })); - let mut new_segment = segment.clone(); - new_segment.edge_ids = vec![opposite_info.edge_id.into()]; - return_arr.push(Artifact::Segment(new_segment)); - let mut new_sweep = sweep.clone(); - new_sweep.edge_ids = vec![opposite_info.edge_id.into()]; - return_arr.push(Artifact::Sweep(new_sweep)); - let mut new_wall = wall.clone(); - new_wall.edge_cut_edge_ids = vec![opposite_info.edge_id.into()]; - return_arr.push(Artifact::Wall(new_wall)); - } - if let Some(adjacent_info) = &edge.adjacent_info { - return_arr.push(Artifact::SweepEdge(SweepEdge { - id: adjacent_info.edge_id.into(), - sub_type: SweepEdgeSubType::Adjacent, - seg_id: edge_id, - cmd_id: artifact_command.cmd_id, - index, - sweep_id: sweep.id, - common_surface_ids: adjacent_info.faces.iter().map(|face| ArtifactId::new(*face)).collect(), - })); - let mut new_segment = segment.clone(); - new_segment.edge_ids = vec![adjacent_info.edge_id.into()]; - return_arr.push(Artifact::Segment(new_segment)); - let mut new_sweep = sweep.clone(); - new_sweep.edge_ids = vec![adjacent_info.edge_id.into()]; - return_arr.push(Artifact::Sweep(new_sweep)); - let mut new_wall = wall.clone(); - new_wall.edge_cut_edge_ids = vec![adjacent_info.edge_id.into()]; - return_arr.push(Artifact::Wall(new_wall)); - } + let opposite_edge_id = ArtifactId::new(generate_engine_id(base, &format!("{}_opp", path_modifier))); //opposite_info.edge_id.into(), + return_arr.push(Artifact::SweepEdge(SweepEdge { + id: opposite_edge_id, + sub_type: SweepEdgeSubType::Opposite, + seg_id: edge_id, + cmd_id: artifact_command.cmd_id, + index, + sweep_id: sweep.id, + common_surface_ids: vec![ + face_id, + ArtifactId::new(generate_engine_id(base, "face_top")), // cap end + ] + .into_iter() + .filter(|id| artifacts.contains_key(id)) + .collect(), + })); + let mut new_segment = segment.clone(); + new_segment.edge_ids = vec![opposite_edge_id]; + return_arr.push(Artifact::Segment(new_segment)); + let mut new_sweep = sweep.clone(); + new_sweep.edge_ids = vec![opposite_edge_id]; + return_arr.push(Artifact::Sweep(new_sweep)); + let mut new_wall = wall.clone(); + new_wall.edge_cut_edge_ids = vec![opposite_edge_id]; + return_arr.push(Artifact::Wall(new_wall)); + + let adjacent_edge_id = ArtifactId::new(generate_engine_id(base, &format!("{}_adj", path_modifier))); //adjacent_info.edge_id.into(), + + return_arr.push(Artifact::SweepEdge(SweepEdge { + id: adjacent_edge_id, + sub_type: SweepEdgeSubType::Adjacent, + seg_id: edge_id, + cmd_id: artifact_command.cmd_id, + index, + sweep_id: sweep.id, + common_surface_ids: vec![face_id, next_face_id] + .into_iter() + .filter(|id| artifacts.contains_key(id)) + .collect(), + })); + let mut new_segment = segment.clone(); + new_segment.edge_ids = vec![adjacent_edge_id]; + return_arr.push(Artifact::Segment(new_segment)); + let mut new_sweep = sweep.clone(); + new_sweep.edge_ids = vec![adjacent_edge_id]; + return_arr.push(Artifact::Sweep(new_sweep)); + let mut new_wall = wall.clone(); + new_wall.edge_cut_edge_ids = vec![adjacent_edge_id]; + return_arr.push(Artifact::Wall(new_wall)); } return Ok(return_arr); } diff --git a/rust/kcl-lib/src/execution/id_generator.rs b/rust/kcl-lib/src/execution/id_generator.rs index 4fb1d089008..934347386a1 100644 --- a/rust/kcl-lib/src/execution/id_generator.rs +++ b/rust/kcl-lib/src/execution/id_generator.rs @@ -1,8 +1,74 @@ //! A generator for ArtifactIds that can be stable across executions. -use crate::execution::ModuleId; +use crate::execution::{ArtifactId, ModuleId}; const NAMESPACE_KCL: uuid::Uuid = uuid::uuid!("8bda3118-75eb-58c7-a866-bef1dcb495e7"); +const ENGINE_NAMESPACE_KCL: uuid::Uuid = uuid::uuid!("22b85cda-1c8d-57c4-88b5-3fd71846f31e"); + +// Generate predictive ids similar to the engine to avoid having to wait for the engine's id generation. +// This has to match the implementation in the engine! +// This is also duplicated in the engine's tests/triangle.rs +pub fn generate_engine_id(base: uuid::Uuid, modifier: &str) -> uuid::Uuid { + let name = format!("{}_{}", base, modifier); + uuid::Uuid::new_v5(&ENGINE_NAMESPACE_KCL, name.as_bytes()) +} + +pub struct EngineIdGenerator { + base: uuid::Uuid, + path_index: u32, +} + +// impl EngineIdGenerator { +// pub fn new(base: uuid::Uuid) -> Self { +// Self { base, path_index: 0 } +// } + +// pub fn next_edge(&mut self){ +// self.path_index += 1; +// } + +// // aka edge/segment id +// pub fn get_curve_id(&self) -> ArtifactId { +// self.generate_path_id("") // "path_0" +// } + +// // aka wall_id +// pub fn get_face_id(&self) -> ArtifactId { +// self.generate_path_id("face") // "path_0_face" +// } + +// pub fn get_opposite_edge_id(&self) -> ArtifactId { +// self.generate_path_id("opp") // "path_0_opp" +// } + +// pub fn get_adjacent_edge_id(&self) -> ArtifactId { +// self.generate_path_id("adj") // "path_0_adj" +// } + +// pub fn get_start_cap_id(&self) -> ArtifactId { +// self.generate_id("face_bottom") // "path_0_face_bottom" +// } + +// pub fn get_end_cap_id(&self) -> ArtifactId { +// self.generate_id("face_top") // "path_0_face_bottom" +// } + +// fn generate_path_id(&self, suffix: &str) -> ArtifactId { +// let path_modifier = format!("path_{}", self.path_index); +// let modifier = if suffix.is_empty() { +// path_modifier +// } else { +// format!("{}_{}", path_modifier, suffix) +// }; +// self.generate_id(&modifier) +// } + +// fn generate_id(&self, modifier: &str) -> ArtifactId { +// let name = format!("{}_{}", self.base, modifier); +// let uuid = uuid::Uuid::new_v5(&ENGINE_NAMESPACE_KCL, name.as_bytes()); +// ArtifactId::new(uuid) +// } +// } /// A generator for ArtifactIds that can be stable across executions. #[derive(Debug, Clone, Default, PartialEq)] diff --git a/rust/kcl-lib/src/execution/mod.rs b/rust/kcl-lib/src/execution/mod.rs index 85677fbd935..2886c4cd055 100644 --- a/rust/kcl-lib/src/execution/mod.rs +++ b/rust/kcl-lib/src/execution/mod.rs @@ -11,7 +11,7 @@ pub use cache::{bust_cache, clear_mem_cache}; pub use cad_op::Group; pub use cad_op::Operation; pub use geometry::*; -pub use id_generator::IdGenerator; +pub use id_generator::{IdGenerator, generate_engine_id}; pub(crate) use import::PreImportedGeometry; use indexmap::IndexMap; pub use kcl_value::{KclObjectFields, KclValue}; diff --git a/rust/kcl-lib/src/lib.rs b/rust/kcl-lib/src/lib.rs index 2997351ccbc..433ec35ca67 100644 --- a/rust/kcl-lib/src/lib.rs +++ b/rust/kcl-lib/src/lib.rs @@ -5,17 +5,17 @@ #![recursion_limit = "1024"] #![allow(clippy::boxed_local)] -#[allow(unused_macros)] -macro_rules! println { - ($($rest:tt)*) => { - #[cfg(all(feature = "disable-println", not(test)))] - { - let _ = format!($($rest)*); - } - #[cfg(any(not(feature = "disable-println"), test))] - std::println!($($rest)*) - } -} +// #[allow(unused_macros)] +// macro_rules! println { +// ($($rest:tt)*) => { +// #[cfg(all(feature = "disable-println", not(test)))] +// { +// let _ = format!($($rest)*); +// } +// #[cfg(any(not(feature = "disable-println"), test))] +// std::println!($($rest)*) +// } +// } #[allow(unused_macros)] macro_rules! eprintln { diff --git a/rust/kcl-lib/src/std/extrude.rs b/rust/kcl-lib/src/std/extrude.rs index 9c8a53941a8..2dfe522a26b 100644 --- a/rust/kcl-lib/src/std/extrude.rs +++ b/rust/kcl-lib/src/std/extrude.rs @@ -23,6 +23,7 @@ use crate::{ errors::{KclError, KclErrorDetails}, execution::{ ArtifactId, ExecState, ExtrudeSurface, GeoMeta, KclValue, ModelingCmdMeta, Path, Sketch, SketchSurface, Solid, + generate_engine_id, types::{PrimitiveType, RuntimeType}, }, parsing::ast::types::TagNode, @@ -342,6 +343,8 @@ pub(crate) async fn do_post_extrude<'a>( ) .await?; + let base = sketch.id; + let any_edge_id = if let Some(edge_id) = sketch.mirror { edge_id } else if let Some(id) = edge_id { @@ -349,13 +352,18 @@ pub(crate) async fn do_post_extrude<'a>( } else { // The "get extrusion face info" API call requires *any* edge on the sketch being extruded. // So, let's just use the first one. - let Some(any_edge_id) = sketch.paths.first().map(|edge| edge.get_base().geo_meta.id) else { - return Err(KclError::new_type(KclErrorDetails::new( - "Expected a non-empty sketch".to_owned(), - vec![args.source_range], - ))); - }; + let path_modifier = format!("path_{}", 0); + let any_edge_id = generate_engine_id(base, &path_modifier); any_edge_id + + // Previous version: + // let Some(any_edge_id) = sketch.paths.first().map(|edge| edge.get_base().geo_meta.id) else { + // return Err(KclError::new_type(KclErrorDetails::new( + // "Expected a non-empty sketch".to_owned(), + // vec![args.source_range], + // ))); + // }; + // any_edge_id }; let mut sketch = sketch.clone(); @@ -410,7 +418,7 @@ pub(crate) async fn do_post_extrude<'a>( sides: face_id_map, start_cap_id, end_cap_id, - } = analyze_faces(exec_state, args, face_infos).await; + } = analyze_faces(args, face_infos, base).await; // Iterate over the sketch.value array and add face_id to GeoMeta let no_engine_commands = args.ctx.no_engine_commands().await; let mut new_value: Vec = Vec::with_capacity(sketch.paths.len() + sketch.inner_paths.len() + 2); @@ -509,28 +517,34 @@ struct Faces { start_cap_id: Option, } -async fn analyze_faces(exec_state: &mut ExecState, args: &Args, face_infos: Vec) -> Faces { +async fn analyze_faces(args: &Args, face_infos: Vec, base: Uuid) -> Faces { let mut faces = Faces { sides: HashMap::with_capacity(face_infos.len()), ..Default::default() }; if args.ctx.no_engine_commands().await { // Create fake IDs for start and end caps, to make extrudes mock-execute safe - faces.start_cap_id = Some(exec_state.next_uuid()); - faces.end_cap_id = Some(exec_state.next_uuid()); + faces.start_cap_id = Some(generate_engine_id(base, "face_bottom")); + faces.end_cap_id = Some(generate_engine_id(base, "face_top")); } + let mut pi: u32 = 0; for face_info in face_infos { match face_info.cap { - ExtrusionFaceCapType::Bottom => faces.start_cap_id = face_info.face_id, - ExtrusionFaceCapType::Top => faces.end_cap_id = face_info.face_id, + ExtrusionFaceCapType::Bottom => faces.start_cap_id = Some(generate_engine_id(base, "face_bottom")), //faces.start_cap_id = face_info.face_id, + ExtrusionFaceCapType::Top => faces.end_cap_id = Some(generate_engine_id(base, "face_top")), //faces.end_cap_id = face_info.face_id, ExtrusionFaceCapType::Both => { - faces.end_cap_id = face_info.face_id; - faces.start_cap_id = face_info.face_id; + faces.end_cap_id = Some(generate_engine_id(base, "face_top")); //face_info.face_id; + faces.start_cap_id = Some(generate_engine_id(base, "face_bottom")); //face_info.face_id; } ExtrusionFaceCapType::None => { - if let Some(curve_id) = face_info.curve_id { - faces.sides.insert(curve_id, face_info.face_id); - } + let path_modifier = format!("path_{}", pi); + pi += 1; + let face_id = generate_engine_id(base, &format!("{}_face", path_modifier)); + let curve_id = generate_engine_id(base, &path_modifier); + faces.sides.insert(curve_id, Some(face_id)); + // if let Some(curve_id) = face_info.curve_id { + // faces.sides.insert(curve_id, face_info.face_id); + // } } } } diff --git a/src/components/Explorer/FileExplorer.tsx b/src/components/Explorer/FileExplorer.tsx index 9a749071755..043100a8ac1 100644 --- a/src/components/Explorer/FileExplorer.tsx +++ b/src/components/Explorer/FileExplorer.tsx @@ -372,7 +372,9 @@ export const FileExplorerRowElement = ({ try { droppedData = JSON.parse(event.dataTransfer.getData('json')) if (!('name' in droppedData) || !('path' in droppedData)) { - throw new Error('malformed drop data: ' + JSON.stringify(droppedData)) + throw new Error( + 'malformed drop data: ' + JSON.stringify(droppedData) + ) } } catch (e) { console.error('invalid JSON in drop event', e) diff --git a/src/lang/langHelpers.ts b/src/lang/langHelpers.ts index e6e2ee90719..b5b26380280 100644 --- a/src/lang/langHelpers.ts +++ b/src/lang/langHelpers.ts @@ -73,6 +73,9 @@ export async function executeAst({ const execState = await rustContext.execute(ast, settings, path) await rustContext.waitForAllEngineCommands() + + console.log('execState', execState.artifactGraph, execState) + return { logs: [], errors: [],