diff --git a/crates/cord-gui/src/app.rs b/crates/cord-gui/src/app.rs index 0905eef..1fc917c 100644 --- a/crates/cord-gui/src/app.rs +++ b/crates/cord-gui/src/app.rs @@ -429,17 +429,17 @@ impl App { { match ext.to_lowercase().as_str() { "scad" => { self.mode = InputMode::Scad; return; } - "crd" | "obj" | "stl" | "3mf" => { self.mode = InputMode::Expr; return; } + "crd" | "zcd" | "obj" | "stl" | "3mf" => { self.mode = InputMode::Expr; return; } _ => {} } } let s = self.source.text(); let s = s.trim(); - if s.contains('{') || s.starts_with("module ") - || s.starts_with("for ") || s.starts_with("if ") + let has_scad_blocks = s.contains('{') && s.contains(';'); + let has_scad_keywords = s.starts_with("module ") || s.starts_with("difference") || s.starts_with("union") - || s.starts_with("intersection") - { + || s.starts_with("intersection"); + if has_scad_keywords || (has_scad_blocks && !s.starts_with("let ") && !s.starts_with("sch ") && !s.starts_with("//")) { self.mode = InputMode::Scad; } else { self.mode = InputMode::Expr; @@ -576,7 +576,13 @@ impl App { self.mode = InputMode::Expr; self.info = None; self.error = None; + self.scene_objects.clear(); + self.selected_object = None; + self.needs_cast = false; + self.needs_plot = false; self.md_items.clear(); + self.viewport.set_graph(&default_sphere_graph()); + self.viewport.set_bounds(2.0); self.status = Some("new file".into()); } @@ -2295,4 +2301,78 @@ mod tests { let graph = cord_expr::resolve_scene(scene); assert!(evaluate(&graph, 1.0, 2.0, 0.0) < 0.0); } + + fn sim_reparse(src: &str) -> cord_trig::TrigGraph { + use cord_expr::{classify_from, expr_to_sdf}; + let src = src.trim(); + let scene = parse_expr_scene(src).expect("parse failed"); + let mut graph = scene.graph; + let mut sdf_roots: Vec = Vec::new(); + + let render_objects: Vec<_> = if scene.cast_all { + scene.all_vars.clone() + } else if !scene.casts.is_empty() { + scene.casts.clone() + } else { + Vec::new() + }; + for &(_, id) in &render_objects { + sdf_roots.push(id); + } + + let mut plot_nodes: Vec = if scene.plot_all { + let mut nodes: Vec<_> = scene.all_vars.iter().map(|(_, id)| *id).collect(); + nodes.extend(&scene.bare_exprs); + nodes + } else { + Vec::new() + }; + plot_nodes.extend(&scene.plots); + for &node_id in &plot_nodes { + let info = classify_from(&graph, node_id); + let sdf = expr_to_sdf(&mut graph, node_id, info.dimensions, 0.05, 10.0); + sdf_roots.push(sdf); + } + + if sdf_roots.len() >= 2 { + let mut root = sdf_roots[0]; + for &node in &sdf_roots[1..] { + root = graph.push(cord_trig::TrigOp::Min(root, node)); + } + graph.set_output(root); + } else if sdf_roots.len() == 1 { + graph.set_output(sdf_roots[0]); + } else { + let empty = graph.push(cord_trig::TrigOp::Const(1e10)); + graph.set_output(empty); + } + + graph + } + + #[test] + fn open_file_produces_different_wgsl() { + let default_src = "let s = sphere(3)\ncast()"; + let shell_src = std::fs::read_to_string( + concat!(env!("CARGO_MANIFEST_DIR"), "/../../examples/shell.crd") + ).expect("can't read shell.crd"); + let bolt_src = std::fs::read_to_string( + concat!(env!("CARGO_MANIFEST_DIR"), "/../../examples/bolt.crd") + ).expect("can't read bolt.crd"); + + let default_graph = sim_reparse(default_src); + let shell_graph = sim_reparse(&shell_src); + let bolt_graph = sim_reparse(&bolt_src); + + let default_wgsl = cord_shader::generate_wgsl_from_trig(&default_graph); + let shell_wgsl = cord_shader::generate_wgsl_from_trig(&shell_graph); + let bolt_wgsl = cord_shader::generate_wgsl_from_trig(&bolt_graph); + + assert_ne!(default_wgsl, shell_wgsl, "shell.crd should produce different WGSL than default"); + assert_ne!(default_wgsl, bolt_wgsl, "bolt.crd should produce different WGSL than default"); + assert_ne!(shell_wgsl, bolt_wgsl, "shell and bolt should produce different WGSL"); + + assert!(shell_graph.nodes.len() > 10, "shell should have a nontrivial graph"); + assert!(bolt_graph.nodes.len() > 10, "bolt should have a nontrivial graph"); + } }