use super::*;
#[test]
fn gdt_annotation_artifacts_get_node_paths() {
let code = r#"gdt::annotation(annotation = "NOTE", faces = [], edges = [])"#;
let ast = crate::parsing::parse_str(code, ModuleId::default()).unwrap();
let programs = crate::execution::ProgramLookup::new(ast, Default::default());
let source_range = SourceRange::new(0, code.len(), ModuleId::default());
let mut artifact = Artifact::GdtAnnotation(GdtAnnotationArtifact {
id: ArtifactId::new(Uuid::new_v4()),
code_ref: CodeRef::placeholder(source_range),
});
fill_in_node_paths(&mut artifact, &programs, 0, &FnvHashMap::default());
let Artifact::GdtAnnotation(annotation) = artifact else {
panic!("Expected GD&T annotation artifact");
};
assert_eq!(annotation.code_ref.range, source_range);
assert!(!annotation.code_ref.node_path.is_empty());
}
#[test]
fn entity_clone_remaps_sweep_ids() {
let source_id = ArtifactId::new(Uuid::new_v4());
let source_path_id = ArtifactId::new(Uuid::new_v4());
let source_surface_id = ArtifactId::new(Uuid::new_v4());
let source_edge_id = ArtifactId::new(Uuid::new_v4());
let source_trajectory_id = ArtifactId::new(Uuid::new_v4());
let cmd_id = Uuid::new_v4();
let cloned_path_id = ArtifactId::new(Uuid::new_v4());
let cloned_surface_id = ArtifactId::new(Uuid::new_v4());
let cloned_edge_id = ArtifactId::new(Uuid::new_v4());
let cloned_trajectory_id = ArtifactId::new(Uuid::new_v4());
let mut artifacts = IndexMap::new();
artifacts.insert(
source_id,
Artifact::Sweep(Sweep {
id: source_id,
sub_type: SweepSubType::Revolve,
path_id: source_path_id,
surface_ids: vec![source_surface_id],
edge_ids: vec![source_edge_id],
code_ref: CodeRef::placeholder(SourceRange::synthetic()),
trajectory_id: Some(source_trajectory_id),
method: kittycad_modeling_cmds::shared::ExtrudeMethod::New,
consumed: true,
pattern_ids: Vec::new(),
}),
);
let mut clone_id_map = FnvHashMap::default();
clone_id_map.insert(source_path_id, cloned_path_id);
clone_id_map.insert(source_surface_id, cloned_surface_id);
clone_id_map.insert(source_edge_id, cloned_edge_id);
clone_id_map.insert(source_trajectory_id, cloned_trajectory_id);
let mut entity_clone_id_maps = FnvHashMap::default();
entity_clone_id_maps.insert(cmd_id, clone_id_map);
let command = ModelingCmd::from(
kcmc::each_cmd::EntityClone::builder()
.entity_id(Uuid::from(source_id))
.build(),
);
let artifact_command = ArtifactCommand {
cmd_id,
range: SourceRange::synthetic(),
command,
};
let ast = crate::parsing::parse_str("", ModuleId::default()).unwrap();
let programs = crate::execution::ProgramLookup::new(ast, Default::default());
let updated = artifacts_to_update(
&artifacts,
&artifact_command,
&FnvHashMap::default(),
&entity_clone_id_maps,
&FnvHashMap::default(),
&programs,
0,
&IndexMap::default(),
&FnvHashMap::default(),
)
.unwrap();
assert_eq!(updated.len(), 1);
let Artifact::Sweep(clone_sweep) = &updated[0] else {
panic!("Expected EntityClone to preserve sweep type, got: {updated:?}");
};
assert_eq!(clone_sweep.id, ArtifactId::new(cmd_id));
assert_eq!(clone_sweep.sub_type, SweepSubType::Revolve);
assert_eq!(clone_sweep.path_id, cloned_path_id);
assert_eq!(clone_sweep.method, kittycad_modeling_cmds::shared::ExtrudeMethod::New);
assert_eq!(clone_sweep.surface_ids, vec![cloned_surface_id]);
assert_eq!(clone_sweep.edge_ids, vec![cloned_edge_id]);
assert_eq!(clone_sweep.trajectory_id, Some(cloned_trajectory_id));
assert!(!clone_sweep.consumed);
}
#[test]
fn entity_clone_remaps_path_ids() {
let source_id = ArtifactId::new(Uuid::new_v4());
let cmd_id = Uuid::new_v4();
let plane_id = ArtifactId::new(Uuid::new_v4());
let sketch_block_id = ArtifactId::new(Uuid::new_v4());
let source_seg_id = ArtifactId::new(Uuid::new_v4());
let source_sweep_id = ArtifactId::new(Uuid::new_v4());
let source_trajectory_sweep_id = ArtifactId::new(Uuid::new_v4());
let source_solid2d_id = ArtifactId::new(Uuid::new_v4());
let source_composite_solid_id = ArtifactId::new(Uuid::new_v4());
let source_origin_path_id = ArtifactId::new(Uuid::new_v4());
let source_inner_path_id = ArtifactId::new(Uuid::new_v4());
let source_outer_path_id = ArtifactId::new(Uuid::new_v4());
let cloned_seg_id = ArtifactId::new(Uuid::new_v4());
let cloned_sweep_id = ArtifactId::new(Uuid::new_v4());
let cloned_trajectory_sweep_id = ArtifactId::new(Uuid::new_v4());
let cloned_solid2d_id = ArtifactId::new(Uuid::new_v4());
let cloned_composite_solid_id = ArtifactId::new(Uuid::new_v4());
let cloned_origin_path_id = ArtifactId::new(Uuid::new_v4());
let cloned_inner_path_id = ArtifactId::new(Uuid::new_v4());
let cloned_outer_path_id = ArtifactId::new(Uuid::new_v4());
let mut artifacts = IndexMap::new();
artifacts.insert(
source_id,
Artifact::Path(Path {
id: source_id,
sub_type: PathSubType::Region,
plane_id,
sketch_block_id: Some(sketch_block_id),
seg_ids: vec![source_seg_id],
consumed: true,
sweep_id: Some(source_sweep_id),
trajectory_sweep_id: Some(source_trajectory_sweep_id),
solid2d_id: Some(source_solid2d_id),
code_ref: CodeRef::placeholder(SourceRange::synthetic()),
composite_solid_id: Some(source_composite_solid_id),
origin_path_id: Some(source_origin_path_id),
pattern_ids: Vec::new(),
inner_path_id: Some(source_inner_path_id),
outer_path_id: Some(source_outer_path_id),
}),
);
let mut clone_id_map = FnvHashMap::default();
clone_id_map.insert(source_seg_id, cloned_seg_id);
clone_id_map.insert(source_sweep_id, cloned_sweep_id);
clone_id_map.insert(source_trajectory_sweep_id, cloned_trajectory_sweep_id);
clone_id_map.insert(source_solid2d_id, cloned_solid2d_id);
clone_id_map.insert(source_composite_solid_id, cloned_composite_solid_id);
clone_id_map.insert(source_origin_path_id, cloned_origin_path_id);
clone_id_map.insert(source_inner_path_id, cloned_inner_path_id);
clone_id_map.insert(source_outer_path_id, cloned_outer_path_id);
let mut entity_clone_id_maps = FnvHashMap::default();
entity_clone_id_maps.insert(cmd_id, clone_id_map);
let command = ModelingCmd::from(
kcmc::each_cmd::EntityClone::builder()
.entity_id(Uuid::from(source_id))
.build(),
);
let artifact_command = ArtifactCommand {
cmd_id,
range: SourceRange::synthetic(),
command,
};
let ast = crate::parsing::parse_str("", ModuleId::default()).unwrap();
let programs = crate::execution::ProgramLookup::new(ast, Default::default());
let updated = artifacts_to_update(
&artifacts,
&artifact_command,
&FnvHashMap::default(),
&entity_clone_id_maps,
&FnvHashMap::default(),
&programs,
0,
&IndexMap::default(),
&FnvHashMap::default(),
)
.unwrap();
assert_eq!(updated.len(), 1);
let Artifact::Path(clone_path) = &updated[0] else {
panic!("Expected EntityClone to preserve path type, got: {updated:?}");
};
assert_eq!(clone_path.id, ArtifactId::new(cmd_id));
assert_eq!(clone_path.sub_type, PathSubType::Region);
assert_eq!(clone_path.plane_id, plane_id);
assert_eq!(clone_path.sketch_block_id, Some(sketch_block_id));
assert_eq!(clone_path.seg_ids, vec![cloned_seg_id]);
assert_eq!(clone_path.sweep_id, Some(cloned_sweep_id));
assert_eq!(clone_path.trajectory_sweep_id, Some(cloned_trajectory_sweep_id));
assert_eq!(clone_path.solid2d_id, Some(cloned_solid2d_id));
assert_eq!(clone_path.composite_solid_id, Some(cloned_composite_solid_id));
assert_eq!(clone_path.origin_path_id, Some(cloned_origin_path_id));
assert_eq!(clone_path.inner_path_id, Some(cloned_inner_path_id));
assert_eq!(clone_path.outer_path_id, Some(cloned_outer_path_id));
assert!(!clone_path.consumed);
}
#[test]
fn entity_clone_remaps_composite_solid_ids() {
let source_id = ArtifactId::new(Uuid::new_v4());
let cmd_id = Uuid::new_v4();
let source_solid_id = ArtifactId::new(Uuid::new_v4());
let source_tool_id = ArtifactId::new(Uuid::new_v4());
let source_parent_composite_id = ArtifactId::new(Uuid::new_v4());
let cloned_solid_id = ArtifactId::new(Uuid::new_v4());
let cloned_tool_id = ArtifactId::new(Uuid::new_v4());
let cloned_parent_composite_id = ArtifactId::new(Uuid::new_v4());
let mut artifacts = IndexMap::new();
artifacts.insert(
source_id,
Artifact::CompositeSolid(CompositeSolid {
id: source_id,
consumed: true,
sub_type: CompositeSolidSubType::Subtract,
output_index: Some(1),
solid_ids: vec![source_solid_id],
tool_ids: vec![source_tool_id],
code_ref: CodeRef::placeholder(SourceRange::synthetic()),
composite_solid_id: Some(source_parent_composite_id),
pattern_ids: Vec::new(),
}),
);
let mut clone_id_map = FnvHashMap::default();
clone_id_map.insert(source_solid_id, cloned_solid_id);
clone_id_map.insert(source_tool_id, cloned_tool_id);
clone_id_map.insert(source_parent_composite_id, cloned_parent_composite_id);
let mut entity_clone_id_maps = FnvHashMap::default();
entity_clone_id_maps.insert(cmd_id, clone_id_map);
let command = ModelingCmd::from(
kcmc::each_cmd::EntityClone::builder()
.entity_id(Uuid::from(source_id))
.build(),
);
let artifact_command = ArtifactCommand {
cmd_id,
range: SourceRange::synthetic(),
command,
};
let ast = crate::parsing::parse_str("", ModuleId::default()).unwrap();
let programs = crate::execution::ProgramLookup::new(ast, Default::default());
let updated = artifacts_to_update(
&artifacts,
&artifact_command,
&FnvHashMap::default(),
&entity_clone_id_maps,
&FnvHashMap::default(),
&programs,
0,
&IndexMap::default(),
&FnvHashMap::default(),
)
.unwrap();
assert_eq!(updated.len(), 1);
let Artifact::CompositeSolid(clone_solid) = &updated[0] else {
panic!("Expected EntityClone to preserve composite solid type, got: {updated:?}");
};
assert_eq!(clone_solid.id, ArtifactId::new(cmd_id));
assert_eq!(clone_solid.sub_type, CompositeSolidSubType::Subtract);
assert_eq!(clone_solid.output_index, Some(1));
assert_eq!(clone_solid.solid_ids, vec![cloned_solid_id]);
assert_eq!(clone_solid.tool_ids, vec![cloned_tool_id]);
assert_eq!(clone_solid.composite_solid_id, Some(cloned_parent_composite_id));
assert!(!clone_solid.consumed);
}
#[test]
fn entity_clone_does_not_preserve_unmapped_pattern_links() {
let source_id = ArtifactId::new(Uuid::new_v4());
let pattern_id = ArtifactId::new(Uuid::new_v4());
let cmd_id = Uuid::new_v4();
let mut artifacts = IndexMap::new();
artifacts.insert(
source_id,
Artifact::Path(Path {
id: source_id,
sub_type: PathSubType::Sketch,
plane_id: ArtifactId::new(Uuid::new_v4()),
sketch_block_id: None,
seg_ids: Vec::new(),
consumed: true,
sweep_id: None,
trajectory_sweep_id: None,
solid2d_id: None,
code_ref: CodeRef::placeholder(SourceRange::synthetic()),
composite_solid_id: None,
origin_path_id: None,
pattern_ids: vec![pattern_id],
inner_path_id: None,
outer_path_id: None,
}),
);
let command = ModelingCmd::from(
kcmc::each_cmd::EntityClone::builder()
.entity_id(Uuid::from(source_id))
.build(),
);
let artifact_command = ArtifactCommand {
cmd_id,
range: SourceRange::synthetic(),
command,
};
let ast = crate::parsing::parse_str("", ModuleId::default()).unwrap();
let programs = crate::execution::ProgramLookup::new(ast, Default::default());
let updated = artifacts_to_update(
&artifacts,
&artifact_command,
&FnvHashMap::default(),
&FnvHashMap::default(),
&FnvHashMap::default(),
&programs,
0,
&IndexMap::default(),
&FnvHashMap::default(),
)
.unwrap();
let Artifact::Path(clone_path) = &updated[0] else {
panic!("Expected EntityClone to preserve path type, got: {updated:?}");
};
assert!(clone_path.pattern_ids.is_empty());
}
#[test]
fn entity_clone_clones_mapped_child_artifacts() {
let source_path_id = ArtifactId::new(Uuid::new_v4());
let source_seg_id = ArtifactId::new(Uuid::new_v4());
let source_sweep_id = ArtifactId::new(Uuid::new_v4());
let source_wall_id = ArtifactId::new(Uuid::new_v4());
let source_plane_id = ArtifactId::new(Uuid::new_v4());
let cmd_id = Uuid::new_v4();
let cloned_path_id = ArtifactId::new(cmd_id);
let cloned_seg_id = ArtifactId::new(Uuid::new_v4());
let cloned_sweep_id = ArtifactId::new(Uuid::new_v4());
let cloned_wall_id = ArtifactId::new(Uuid::new_v4());
let mut artifacts = IndexMap::new();
artifacts.insert(
source_path_id,
Artifact::Path(Path {
id: source_path_id,
sub_type: PathSubType::Sketch,
plane_id: source_plane_id,
seg_ids: vec![source_seg_id],
consumed: true,
sweep_id: Some(source_sweep_id),
trajectory_sweep_id: None,
solid2d_id: None,
code_ref: CodeRef::placeholder(SourceRange::synthetic()),
composite_solid_id: None,
sketch_block_id: None,
origin_path_id: None,
inner_path_id: None,
outer_path_id: None,
pattern_ids: Vec::new(),
}),
);
artifacts.insert(
source_seg_id,
Artifact::Segment(Segment {
id: source_seg_id,
path_id: source_path_id,
original_seg_id: None,
surface_id: Some(source_wall_id),
edge_ids: Vec::new(),
edge_cut_id: None,
code_ref: CodeRef::placeholder(SourceRange::synthetic()),
common_surface_ids: Vec::new(),
}),
);
artifacts.insert(
source_sweep_id,
Artifact::Sweep(Sweep {
id: source_sweep_id,
sub_type: SweepSubType::Extrusion,
path_id: source_path_id,
surface_ids: vec![source_wall_id],
edge_ids: Vec::new(),
code_ref: CodeRef::placeholder(SourceRange::synthetic()),
trajectory_id: None,
method: kittycad_modeling_cmds::shared::ExtrudeMethod::Merge,
consumed: true,
pattern_ids: Vec::new(),
}),
);
artifacts.insert(
source_wall_id,
Artifact::Wall(Wall {
id: source_wall_id,
seg_id: source_seg_id,
edge_cut_edge_ids: Vec::new(),
sweep_id: source_sweep_id,
path_ids: vec![source_path_id],
face_code_ref: CodeRef::placeholder(SourceRange::synthetic()),
cmd_id: Uuid::new_v4(),
}),
);
let mut clone_id_map = FnvHashMap::default();
clone_id_map.insert(source_path_id, cloned_path_id);
clone_id_map.insert(source_seg_id, cloned_seg_id);
clone_id_map.insert(source_sweep_id, cloned_sweep_id);
clone_id_map.insert(source_wall_id, cloned_wall_id);
let mut entity_clone_id_maps = FnvHashMap::default();
entity_clone_id_maps.insert(cmd_id, clone_id_map);
let command = ModelingCmd::from(
kcmc::each_cmd::EntityClone::builder()
.entity_id(Uuid::from(source_path_id))
.build(),
);
let artifact_command = ArtifactCommand {
cmd_id,
range: SourceRange::synthetic(),
command,
};
let ast = crate::parsing::parse_str("", ModuleId::default()).unwrap();
let programs = crate::execution::ProgramLookup::new(ast, Default::default());
let updated = artifacts_to_update(
&artifacts,
&artifact_command,
&FnvHashMap::default(),
&entity_clone_id_maps,
&FnvHashMap::default(),
&programs,
0,
&IndexMap::default(),
&FnvHashMap::default(),
)
.unwrap();
let updated_map: IndexMap<ArtifactId, Artifact> =
IndexMap::from_iter(updated.into_iter().map(|artifact| (artifact.id(), artifact)));
let Artifact::Path(cloned_path) = updated_map.get(&cloned_path_id).expect("Expected cloned path artifact") else {
panic!("Expected cloned path artifact");
};
assert_eq!(cloned_path.seg_ids, vec![cloned_seg_id]);
assert_eq!(cloned_path.sweep_id, Some(cloned_sweep_id));
assert!(!cloned_path.consumed);
let Artifact::Segment(cloned_seg) = updated_map
.get(&cloned_seg_id)
.expect("Expected cloned segment artifact")
else {
panic!("Expected cloned segment artifact");
};
assert_eq!(cloned_seg.path_id, cloned_path_id);
assert_eq!(cloned_seg.surface_id, Some(cloned_wall_id));
let Artifact::Sweep(cloned_sweep) = updated_map
.get(&cloned_sweep_id)
.expect("Expected cloned sweep artifact")
else {
panic!("Expected cloned sweep artifact");
};
assert_eq!(cloned_sweep.path_id, cloned_path_id);
assert_eq!(cloned_sweep.surface_ids, vec![cloned_wall_id]);
assert!(cloned_sweep.consumed);
let Artifact::Wall(cloned_wall) = updated_map.get(&cloned_wall_id).expect("Expected cloned wall artifact") else {
panic!("Expected cloned wall artifact");
};
assert_eq!(cloned_wall.seg_id, cloned_seg_id);
assert_eq!(cloned_wall.sweep_id, cloned_sweep_id);
assert_eq!(cloned_wall.path_ids, vec![cloned_path_id]);
}
#[test]
fn build_entity_clone_id_maps_from_child_queries() {
let source_id = Uuid::new_v4();
let clone_cmd_id = Uuid::new_v4();
let old_children_cmd_id = Uuid::new_v4();
let new_children_cmd_id = Uuid::new_v4();
let old_child_a = Uuid::new_v4();
let old_child_b = Uuid::new_v4();
let new_child_a = Uuid::new_v4();
let new_child_b = Uuid::new_v4();
let artifact_commands = vec![
ArtifactCommand {
cmd_id: clone_cmd_id,
range: SourceRange::synthetic(),
command: ModelingCmd::from(kcmc::each_cmd::EntityClone::builder().entity_id(source_id).build()),
},
ArtifactCommand {
cmd_id: old_children_cmd_id,
range: SourceRange::synthetic(),
command: ModelingCmd::from(
kcmc::each_cmd::EntityGetAllChildUuids::builder()
.entity_id(source_id)
.build(),
),
},
ArtifactCommand {
cmd_id: new_children_cmd_id,
range: SourceRange::synthetic(),
command: ModelingCmd::from(
kcmc::each_cmd::EntityGetAllChildUuids::builder()
.entity_id(clone_cmd_id)
.build(),
),
},
];
let mut responses = FnvHashMap::default();
let old_children_response: kcmc::output::EntityGetAllChildUuids =
serde_json::from_value(serde_json::json!({ "entity_ids": [old_child_a, old_child_b] })).unwrap();
let new_children_response: kcmc::output::EntityGetAllChildUuids =
serde_json::from_value(serde_json::json!({ "entity_ids": [new_child_a, new_child_b] })).unwrap();
responses.insert(
old_children_cmd_id,
OkModelingCmdResponse::EntityGetAllChildUuids(old_children_response),
);
responses.insert(
new_children_cmd_id,
OkModelingCmdResponse::EntityGetAllChildUuids(new_children_response),
);
let clone_id_maps = build_entity_clone_id_maps(&artifact_commands, &responses);
let id_map = clone_id_maps
.get(&clone_cmd_id)
.expect("Expected clone ID map for clone command");
assert_eq!(
id_map.get(&ArtifactId::new(source_id)),
Some(&ArtifactId::new(clone_cmd_id))
);
assert_eq!(
id_map.get(&ArtifactId::new(old_child_a)),
Some(&ArtifactId::new(new_child_a))
);
assert_eq!(
id_map.get(&ArtifactId::new(old_child_b)),
Some(&ArtifactId::new(new_child_b))
);
}
#[test]
fn surface_blend_creates_blend_sweep_artifact() {
let path_one_id = ArtifactId::new(Uuid::new_v4());
let path_two_id = ArtifactId::new(Uuid::new_v4());
let source_surface_one_id = ArtifactId::new(Uuid::new_v4());
let source_surface_two_id = ArtifactId::new(Uuid::new_v4());
let source_code_ref = CodeRef::placeholder(SourceRange::synthetic());
let mut artifacts = IndexMap::new();
artifacts.insert(
source_surface_one_id,
Artifact::Sweep(Sweep {
id: source_surface_one_id,
sub_type: SweepSubType::Extrusion,
path_id: path_one_id,
surface_ids: Vec::new(),
edge_ids: Vec::new(),
code_ref: source_code_ref.clone(),
trajectory_id: None,
method: kittycad_modeling_cmds::shared::ExtrudeMethod::Merge,
consumed: false,
pattern_ids: Vec::new(),
}),
);
artifacts.insert(
source_surface_two_id,
Artifact::Sweep(Sweep {
id: source_surface_two_id,
sub_type: SweepSubType::Extrusion,
path_id: path_two_id,
surface_ids: Vec::new(),
edge_ids: Vec::new(),
code_ref: source_code_ref,
trajectory_id: None,
method: kittycad_modeling_cmds::shared::ExtrudeMethod::Merge,
consumed: false,
pattern_ids: Vec::new(),
}),
);
let cmd_id = Uuid::new_v4();
let command = ModelingCmd::from(
kcmc::each_cmd::SurfaceBlend::builder()
.surfaces(vec![
kcmc::shared::SurfaceEdgeReference::builder()
.object_id(Uuid::from(source_surface_one_id))
.edges(vec![
kcmc::shared::FractionOfEdge::builder()
.edge_id(Uuid::new_v4())
.lower_bound(0.0)
.upper_bound(1.0)
.build(),
])
.build(),
kcmc::shared::SurfaceEdgeReference::builder()
.object_id(Uuid::from(source_surface_two_id))
.edges(vec![
kcmc::shared::FractionOfEdge::builder()
.edge_id(Uuid::new_v4())
.lower_bound(0.0)
.upper_bound(1.0)
.build(),
])
.build(),
])
.build(),
);
let artifact_command = ArtifactCommand {
cmd_id,
range: SourceRange::synthetic(),
command,
};
let ast = crate::parsing::parse_str("", ModuleId::default()).unwrap();
let programs = crate::execution::ProgramLookup::new(ast, Default::default());
let updated = artifacts_to_update(
&artifacts,
&artifact_command,
&FnvHashMap::default(),
&FnvHashMap::default(),
&FnvHashMap::default(),
&programs,
0,
&IndexMap::default(),
&FnvHashMap::default(),
)
.unwrap();
assert_eq!(updated.len(), 1);
let Artifact::Sweep(blend_sweep) = &updated[0] else {
panic!("Expected SurfaceBlend to create a sweep artifact, got: {updated:?}");
};
assert_eq!(blend_sweep.id, ArtifactId::new(cmd_id));
assert_eq!(blend_sweep.sub_type, SweepSubType::Blend);
assert_eq!(blend_sweep.path_id, path_one_id);
assert_eq!(blend_sweep.trajectory_id, Some(path_two_id));
assert_eq!(blend_sweep.method, kittycad_modeling_cmds::shared::ExtrudeMethod::New);
assert!(!blend_sweep.consumed);
}
#[test]
fn create_region_creates_region_path_sub_type() {
let origin_path_id = ArtifactId::new(Uuid::new_v4());
let origin_plane_id = ArtifactId::new(Uuid::new_v4());
let source_code_ref = CodeRef::placeholder(SourceRange::synthetic());
let mut artifacts = IndexMap::new();
artifacts.insert(
origin_path_id,
Artifact::Path(Path {
id: origin_path_id,
sub_type: PathSubType::Sketch,
plane_id: origin_plane_id,
seg_ids: Vec::new(),
consumed: false,
sweep_id: None,
trajectory_sweep_id: None,
solid2d_id: None,
code_ref: source_code_ref,
composite_solid_id: None,
sketch_block_id: None,
origin_path_id: None,
inner_path_id: None,
outer_path_id: None,
pattern_ids: Vec::new(),
}),
);
let cmd_id = Uuid::new_v4();
let command = ModelingCmd::from(
kcmc::each_cmd::CreateRegion::builder()
.object_id(Uuid::from(origin_path_id))
.segment(Uuid::new_v4())
.intersection_segment(Uuid::new_v4())
.intersection_index(-1)
.curve_clockwise(false)
.build(),
);
let artifact_command = ArtifactCommand {
cmd_id,
range: SourceRange::synthetic(),
command,
};
let ast = crate::parsing::parse_str("", ModuleId::default()).unwrap();
let programs = crate::execution::ProgramLookup::new(ast, Default::default());
let updated = artifacts_to_update(
&artifacts,
&artifact_command,
&FnvHashMap::default(),
&FnvHashMap::default(),
&FnvHashMap::default(),
&programs,
0,
&IndexMap::default(),
&FnvHashMap::default(),
)
.unwrap();
assert_eq!(updated.len(), 1);
let Artifact::Path(region_path) = &updated[0] else {
panic!("Expected CreateRegion to create a path artifact, got: {updated:?}");
};
assert_eq!(region_path.id, ArtifactId::new(cmd_id));
assert_eq!(region_path.sub_type, PathSubType::Region);
assert_eq!(region_path.plane_id, origin_plane_id);
assert_eq!(region_path.sketch_block_id, None);
assert_eq!(region_path.origin_path_id, Some(origin_path_id));
}
#[test]
fn pattern_artifact_links_to_source_geometry() {
let path_id = ArtifactId::new(Uuid::new_v4());
let sweep_id = ArtifactId::new(Uuid::new_v4());
let pattern_id = ArtifactId::new(Uuid::new_v4());
let plane_id = ArtifactId::new(Uuid::new_v4());
let copy_id = Uuid::new_v4();
let copy_face_id = Uuid::new_v4();
let copy_edge_id = Uuid::new_v4();
let code_ref = CodeRef::placeholder(SourceRange::synthetic());
let face_edge_infos: Vec<kcmc::output::FaceEdgeInfo> = serde_json::from_value(serde_json::json!([
{
"object_id": copy_id,
"faces": [copy_face_id],
"edges": [copy_edge_id],
}
]))
.expect("valid face-edge info");
let mut artifacts = IndexMap::new();
artifacts.insert(
path_id,
Artifact::Path(Path {
id: path_id,
sub_type: PathSubType::Sketch,
plane_id,
seg_ids: Vec::new(),
consumed: true,
sweep_id: Some(sweep_id),
trajectory_sweep_id: None,
solid2d_id: None,
code_ref: code_ref.clone(),
composite_solid_id: None,
sketch_block_id: None,
origin_path_id: None,
inner_path_id: None,
outer_path_id: None,
pattern_ids: Vec::new(),
}),
);
artifacts.insert(
sweep_id,
Artifact::Sweep(Sweep {
id: sweep_id,
sub_type: SweepSubType::Extrusion,
path_id,
surface_ids: Vec::new(),
edge_ids: Vec::new(),
code_ref: code_ref.clone(),
trajectory_id: None,
method: kittycad_modeling_cmds::shared::ExtrudeMethod::Merge,
consumed: false,
pattern_ids: Vec::new(),
}),
);
let updated = pattern_artifact_updates(
&artifacts,
pattern_id,
PatternSubType::Circular,
path_id,
&face_edge_infos,
code_ref,
);
assert!(matches!(
updated.first(),
Some(Artifact::Pattern(Pattern {
id,
sub_type: PatternSubType::Circular,
source_id,
copy_ids,
copy_face_ids,
copy_edge_ids,
..
})) if *id == pattern_id
&& *source_id == path_id
&& copy_ids == &vec![ArtifactId::new(copy_id)]
&& copy_face_ids == &vec![ArtifactId::new(copy_face_id)]
&& copy_edge_ids == &vec![ArtifactId::new(copy_edge_id)]
));
assert!(updated.iter().any(|artifact| {
matches!(artifact, Artifact::Path(path) if path.id == path_id && path.pattern_ids == vec![pattern_id])
}));
assert!(updated.iter().any(|artifact| {
matches!(artifact, Artifact::Sweep(sweep) if sweep.id == sweep_id && sweep.pattern_ids == vec![pattern_id])
}));
}
#[test]
fn primitive_edge_does_not_replace_existing_segment_artifact() {
let shared_id = ArtifactId::new(Uuid::new_v4());
let path_id = ArtifactId::new(Uuid::new_v4());
let solid_id = ArtifactId::new(Uuid::new_v4());
let mut map = IndexMap::new();
map.insert(
shared_id,
Artifact::Segment(Segment {
id: shared_id,
path_id,
original_seg_id: None,
surface_id: None,
edge_ids: Vec::new(),
edge_cut_id: None,
code_ref: CodeRef::placeholder(SourceRange::synthetic()),
common_surface_ids: Vec::new(),
}),
);
merge_artifact_into_map(
&mut map,
Artifact::PrimitiveEdge(PrimitiveEdge {
id: shared_id,
solid_id,
code_ref: CodeRef::placeholder(SourceRange::synthetic()),
}),
);
assert!(matches!(map.get(&shared_id), Some(Artifact::Segment(_))));
}
#[test]
fn primitive_face_does_not_replace_existing_cap_artifact() {
let shared_id = ArtifactId::new(Uuid::new_v4());
let sweep_id = ArtifactId::new(Uuid::new_v4());
let solid_id = ArtifactId::new(Uuid::new_v4());
let mut map = IndexMap::new();
map.insert(
shared_id,
Artifact::Cap(Cap {
id: shared_id,
sub_type: CapSubType::End,
edge_cut_edge_ids: Vec::new(),
sweep_id,
path_ids: Vec::new(),
face_code_ref: CodeRef::placeholder(SourceRange::synthetic()),
cmd_id: Uuid::new_v4(),
}),
);
merge_artifact_into_map(
&mut map,
Artifact::PrimitiveFace(PrimitiveFace {
id: shared_id,
solid_id,
code_ref: CodeRef::placeholder(SourceRange::synthetic()),
}),
);
assert!(matches!(map.get(&shared_id), Some(Artifact::Cap(_))));
}
#[test]
fn mirror_3d_artifacts_include_mirrored_body_with_face_and_edge_ids() {
let path_id = ArtifactId::new(Uuid::new_v4());
let source_sweep_id = ArtifactId::new(Uuid::new_v4());
let mirrored_sweep_id = Uuid::new_v4();
let face_one_id = Uuid::new_v4();
let face_two_id = Uuid::new_v4();
let edge_id = Uuid::new_v4();
let code_ref = CodeRef::placeholder(SourceRange::synthetic());
let mut artifacts = IndexMap::new();
artifacts.insert(
path_id,
Artifact::Path(Path {
id: path_id,
sub_type: PathSubType::Region,
plane_id: ArtifactId::new(Uuid::new_v4()),
seg_ids: Vec::new(),
consumed: true,
sweep_id: Some(source_sweep_id),
trajectory_sweep_id: None,
solid2d_id: None,
code_ref: CodeRef::placeholder(SourceRange::synthetic()),
composite_solid_id: None,
sketch_block_id: None,
origin_path_id: None,
inner_path_id: None,
outer_path_id: None,
pattern_ids: Vec::new(),
}),
);
artifacts.insert(
source_sweep_id,
Artifact::Sweep(Sweep {
id: source_sweep_id,
sub_type: SweepSubType::Extrusion,
path_id,
surface_ids: Vec::new(),
edge_ids: Vec::new(),
code_ref,
trajectory_id: None,
method: kittycad_modeling_cmds::shared::ExtrudeMethod::Merge,
consumed: false,
pattern_ids: Vec::new(),
}),
);
let cmd_id = Uuid::new_v4();
let command = ModelingCmd::from(
kcmc::each_cmd::EntityMirrorAcross::builder()
.ids(vec![Uuid::from(path_id)])
.across(kittycad_modeling_cmds::shared::MirrorAcross::Plane { id: Uuid::new_v4() })
.build(),
);
let artifact_command = ArtifactCommand {
cmd_id,
range: SourceRange::synthetic(),
command,
};
let mirror_response: kcmc::output::EntityMirrorAcross = serde_json::from_value(serde_json::json!({
"entity_face_edge_ids": [
{
"object_id": mirrored_sweep_id,
"faces": [face_one_id, face_two_id],
"edges": [edge_id],
}
]
}))
.expect("valid mirror response");
let mut responses = FnvHashMap::default();
responses.insert(cmd_id, OkModelingCmdResponse::EntityMirrorAcross(mirror_response));
let ast = crate::parsing::parse_str("", ModuleId::default()).unwrap();
let programs = crate::execution::ProgramLookup::new(ast, Default::default());
let updated = artifacts_to_update(
&artifacts,
&artifact_command,
&responses,
&FnvHashMap::default(),
&FnvHashMap::default(),
&programs,
0,
&IndexMap::default(),
&FnvHashMap::default(),
)
.unwrap();
assert_eq!(updated.len(), 1);
assert!(updated.iter().any(|artifact| {
matches!(
artifact,
Artifact::Sweep(Sweep {
id,
surface_ids,
edge_ids,
..
}) if *id == ArtifactId::new(mirrored_sweep_id)
&& surface_ids == &vec![ArtifactId::new(face_one_id), ArtifactId::new(face_two_id)]
&& edge_ids == &vec![ArtifactId::new(edge_id)]
)
}));
}