Skip to content

Commit 61ab327

Browse files
committed
Fix sketch on face
1 parent a00557d commit 61ab327

File tree

5 files changed

+118
-70
lines changed

5 files changed

+118
-70
lines changed

rust/kcl-lib/src/execution/exec_ast.rs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -158,15 +158,32 @@ impl ExecutorContext {
158158
exec_state.mod_local.sketch_mode,
159159
exec_state.mod_local.freedom_analysis,
160160
);
161-
#[cfg(feature = "artifact-graph")]
162-
{
163-
local_state
164-
.artifacts
165-
.scene_objects
166-
.clone_from(&exec_state.mod_local.artifacts.scene_objects);
167-
}
168-
if preserve_mem.normal() {
169-
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
161+
match preserve_mem {
162+
PreserveMem::Always => {
163+
#[cfg(feature = "artifact-graph")]
164+
{
165+
if !self.is_mock() {
166+
use crate::id::IncIdGenerator;
167+
exec_state
168+
.mod_local
169+
.artifacts
170+
.scene_objects
171+
.clone_from(&exec_state.global.root_module_artifacts.scene_objects);
172+
exec_state.mod_local.artifacts.object_id_generator =
173+
IncIdGenerator::new(exec_state.global.root_module_artifacts.scene_objects.len());
174+
}
175+
}
176+
}
177+
PreserveMem::Normal => {
178+
#[cfg(feature = "artifact-graph")]
179+
{
180+
local_state
181+
.artifacts
182+
.scene_objects
183+
.clone_from(&exec_state.mod_local.artifacts.scene_objects);
184+
}
185+
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
186+
}
170187
}
171188

172189
let no_prelude = self

rust/kcl-lib/src/execution/state.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,12 @@ impl ExecState {
361361
#[cfg(feature = "artifact-graph")]
362362
pub fn add_scene_object(&mut self, obj: Object, source_range: SourceRange) -> ObjectId {
363363
let id = obj.id;
364-
debug_assert!(id.0 == self.mod_local.artifacts.scene_objects.len());
364+
debug_assert!(
365+
id.0 == self.mod_local.artifacts.scene_objects.len(),
366+
"Adding scene object with ID {} but next ID is {}",
367+
id.0,
368+
self.mod_local.artifacts.scene_objects.len()
369+
);
365370
self.mod_local.artifacts.scene_objects.push(obj);
366371
self.mod_local.artifacts.source_range_to_object.insert(source_range, id);
367372
id
@@ -711,7 +716,10 @@ impl ModuleArtifactState {
711716
self.unprocessed_commands.extend(other.unprocessed_commands);
712717
self.commands.extend(other.commands);
713718
self.operations.extend(other.operations);
714-
self.scene_objects.extend(other.scene_objects);
719+
if other.scene_objects.len() > self.scene_objects.len() {
720+
self.scene_objects
721+
.extend(other.scene_objects[self.scene_objects.len()..].iter().cloned());
722+
}
715723
self.source_range_to_object.extend(other.source_range_to_object);
716724
self.var_solutions.extend(other.var_solutions);
717725
}

rust/kcl-lib/src/frontend.rs

Lines changed: 81 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,6 @@ impl SketchApi for FrontendState {
154154
async fn new_sketch(
155155
&mut self,
156156
ctx: &ExecutorContext,
157-
mock_ctx: &ExecutorContext,
158157
_project: ProjectId,
159158
_file: FileId,
160159
_version: Version,
@@ -214,62 +213,27 @@ impl SketchApi for FrontendState {
214213
});
215214
};
216215

217-
let sketch_source_range = new_program
218-
.ast
219-
.body
220-
.last()
221-
.map(SourceRange::from)
222-
.ok_or_else(|| Error {
223-
msg: "No AST body items after adding sketch".to_owned(),
224-
})?;
225-
226216
// Make sure to only set this if there are no errors.
227217
self.program = new_program.clone();
228218

229219
// We need to do an engine execute so that the plane object gets created
230220
// and is cached.
231-
{
232-
ctx.run_with_caching(new_program.clone()).await.map_err(|err| Error {
233-
msg: err.error.message().to_owned(),
234-
})?;
235-
}
236-
237-
let mut truncated_program = new_program;
238-
only_sketch_block(&mut truncated_program.ast, sketch_source_range, ChangeKind::None)?;
239-
240-
// Add one to reserve an ID for the plane.
241-
let sketch_id = ObjectId(self.scene_graph.objects.len() + 1);
221+
let outcome = ctx.run_with_caching(new_program.clone()).await.map_err(|err| Error {
222+
msg: err.error.message().to_owned(),
223+
})?;
224+
let freedom_analysis_ran = true;
242225

243-
// Execute.
244-
let outcome = mock_ctx
245-
.run_mock(
246-
&truncated_program,
247-
&MockConfig::new_sketch_mode(sketch_id).no_freedom_analysis(),
248-
)
249-
.await
250-
.map_err(|err| {
251-
// TODO: sketch-api: Yeah, this needs to change. We need to
252-
// return the full error.
253-
Error {
254-
msg: err.error.message().to_owned(),
255-
}
256-
})?;
226+
let outcome = self.update_state_after_exec(outcome, freedom_analysis_ran);
257227

258-
#[cfg(not(feature = "artifact-graph"))]
259-
let sketch_id = ObjectId(0);
260-
#[cfg(feature = "artifact-graph")]
261-
let sketch_id = outcome
262-
.source_range_to_object
263-
.get(&sketch_source_range)
264-
.copied()
265-
.ok_or_else(|| Error {
266-
msg: format!("Source range of sketch not found: {sketch_source_range:?}"),
267-
})?;
268-
let src_delta = SourceDelta { text: new_source };
228+
let Some(sketch_id) = self.scene_graph.objects.last().map(|object| object.id) else {
229+
return Err(Error {
230+
msg: "No objects in scene graph after adding sketch".to_owned(),
231+
});
232+
};
269233
// Store the object in the scene.
270234
self.scene_graph.sketch_mode = Some(sketch_id);
271-
// Uses .no_freedom_analysis() so freedom_analysis: false
272-
let outcome = self.update_state_after_exec(outcome, false);
235+
236+
let src_delta = SourceDelta { text: new_source };
273237
let scene_graph_delta = SceneGraphDelta {
274238
new_graph: self.scene_graph.clone(),
275239
invalidates_ids: false,
@@ -1404,8 +1368,9 @@ impl FrontendState {
14041368
msg: err.error.message().to_owned(),
14051369
}
14061370
})?;
1371+
let freedom_analysis_ran = true;
14071372

1408-
let outcome = self.update_state_after_exec(outcome);
1373+
let outcome = self.update_state_after_exec(outcome, freedom_analysis_ran);
14091374

14101375
let src_delta = SourceDelta { text: new_source };
14111376
let scene_graph_delta = SceneGraphDelta {
@@ -2732,6 +2697,15 @@ mod tests {
27322697
None
27332698
}
27342699

2700+
fn find_first_face_object(scene_graph: &SceneGraph) -> Option<&Object> {
2701+
for object in &scene_graph.objects {
2702+
if let ObjectKind::Face(_) = &object.kind {
2703+
return Some(object);
2704+
}
2705+
}
2706+
None
2707+
}
2708+
27352709
#[track_caller]
27362710
fn expect_sketch(object: &Object) -> &Sketch {
27372711
if let ObjectKind::Sketch(sketch) = &object.kind {
@@ -2756,7 +2730,7 @@ mod tests {
27562730
on: PlaneName::Xy.to_string(),
27572731
};
27582732
let (_src_delta, scene_delta, sketch_id) = frontend
2759-
.new_sketch(&ctx, &mock_ctx, ProjectId(0), FileId(0), version, sketch_args)
2733+
.new_sketch(&ctx, ProjectId(0), FileId(0), version, sketch_args)
27602734
.await
27612735
.unwrap();
27622736
assert_eq!(sketch_id, ObjectId(1));
@@ -2861,7 +2835,7 @@ sketch(on = XY) {
28612835
on: PlaneName::Xy.to_string(),
28622836
};
28632837
let (_src_delta, scene_delta, sketch_id) = frontend
2864-
.new_sketch(&ctx, &mock_ctx, ProjectId(0), FileId(0), version, sketch_args)
2838+
.new_sketch(&ctx, ProjectId(0), FileId(0), version, sketch_args)
28652839
.await
28662840
.unwrap();
28672841
assert_eq!(sketch_id, ObjectId(1));
@@ -2987,7 +2961,7 @@ sketch(on = XY) {
29872961
on: PlaneName::Xy.to_string(),
29882962
};
29892963
let (_src_delta, scene_delta, sketch_id) = frontend
2990-
.new_sketch(&ctx, &mock_ctx, ProjectId(0), FileId(0), version, sketch_args)
2964+
.new_sketch(&ctx, ProjectId(0), FileId(0), version, sketch_args)
29912965
.await
29922966
.unwrap();
29932967
assert_eq!(sketch_id, ObjectId(1));
@@ -3198,7 +3172,7 @@ s = sketch(on = XY) {
31983172
on: PlaneName::Xy.to_string(),
31993173
};
32003174
let (_src_delta, scene_delta, sketch_id) = frontend
3201-
.new_sketch(&ctx, &mock_ctx, ProjectId(0), FileId(0), version, sketch_args)
3175+
.new_sketch(&ctx, ProjectId(0), FileId(0), version, sketch_args)
32023176
.await
32033177
.unwrap();
32043178
assert_eq!(sketch_id, ObjectId(1));
@@ -4245,6 +4219,60 @@ sketch(on = XY) {
42454219
mock_ctx.close().await;
42464220
}
42474221

4222+
#[tokio::test(flavor = "multi_thread")]
4223+
async fn test_sketch_on_face_simple() {
4224+
let initial_source = "\
4225+
@settings(experimentalFeatures = allow)
4226+
4227+
len = 2mm
4228+
cube = startSketchOn(XY)
4229+
|> startProfile(at = [0, 0])
4230+
|> line(end = [len, 0], tag = $side)
4231+
|> line(end = [0, len])
4232+
|> line(end = [-len, 0])
4233+
|> line(end = [0, -len])
4234+
|> close()
4235+
|> extrude(length = len)
4236+
4237+
face = faceOf(cube, face = side)
4238+
";
4239+
4240+
let program = Program::parse(initial_source).unwrap().0.unwrap();
4241+
4242+
let mut frontend = FrontendState::new();
4243+
4244+
let ctx = ExecutorContext::new_with_default_client().await.unwrap();
4245+
let mock_ctx = ExecutorContext::new_mock(None).await;
4246+
let version = Version(0);
4247+
4248+
frontend.hack_set_program(&ctx, program).await.unwrap();
4249+
let face_object = find_first_face_object(&frontend.scene_graph).unwrap();
4250+
let face_id = face_object.id;
4251+
4252+
let sketch_args = SketchCtor { on: "face".to_owned() };
4253+
let (_src_delta, scene_delta, sketch_id) = frontend
4254+
.new_sketch(&ctx, ProjectId(0), FileId(0), version, sketch_args)
4255+
.await
4256+
.unwrap();
4257+
assert_eq!(sketch_id, ObjectId(2));
4258+
assert_eq!(scene_delta.new_objects, vec![ObjectId(2)]);
4259+
let sketch_object = &scene_delta.new_graph.objects[2];
4260+
assert_eq!(sketch_object.id, ObjectId(2));
4261+
assert_eq!(
4262+
sketch_object.kind,
4263+
ObjectKind::Sketch(Sketch {
4264+
args: SketchCtor { on: "face".to_owned() },
4265+
plane: face_id,
4266+
segments: vec![],
4267+
constraints: vec![],
4268+
})
4269+
);
4270+
assert_eq!(scene_delta.new_graph.objects.len(), 3);
4271+
4272+
ctx.close().await;
4273+
mock_ctx.close().await;
4274+
}
4275+
42484276
#[tokio::test(flavor = "multi_thread")]
42494277
async fn test_multiple_sketch_blocks() {
42504278
let initial_source = "\

rust/kcl-lib/src/frontend/sketch.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ pub trait SketchApi {
2222
async fn new_sketch(
2323
&mut self,
2424
ctx: &ExecutorContext,
25-
mock_ctx: &ExecutorContext,
2625
project: ProjectId,
2726
file: FileId,
2827
version: Version,

rust/kcl-wasm-lib/src/api.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,10 @@ impl Context {
157157
.create_executor_ctx(settings, None, false)
158158
.map_err(|e| format!("Could not create KCL executor context for new sketch. {TRUE_BUG} Details: {e}"))?;
159159

160-
let mock_ctx = self
161-
.create_executor_ctx(settings, None, true)
162-
.map_err(|e| format!("Could not create KCL executor context for new sketch. {TRUE_BUG} Details: {e}"))?;
163-
164160
let frontend = Arc::clone(&self.frontend);
165161
let mut guard = frontend.write().await;
166162
let result = guard
167-
.new_sketch(&ctx, &mock_ctx, project, file, version, args)
163+
.new_sketch(&ctx, project, file, version, args)
168164
.await
169165
.map_err(|e| format!("Failed to create new sketch: {:?}", e))?;
170166

0 commit comments

Comments
 (0)