diff --git a/crates/cord-cordic/src/compiler.rs b/crates/cord-cordic/src/compiler.rs index c036a15..179e9d6 100644 --- a/crates/cord-cordic/src/compiler.rs +++ b/crates/cord-cordic/src/compiler.rs @@ -47,7 +47,7 @@ impl CORDICProgram { /// counts are resolved from the CordicConfig if present, otherwise /// all ops use word_bits as their iteration count. pub fn compile(graph: &TrigGraph, config: &CompileConfig) -> Self { - let frac_bits = config.word_bits - 1; + let frac_bits = config.word_bits / 2; let to_fixed = |val: f64| -> i64 { (val * (1i64 << frac_bits) as f64).round() as i64 }; diff --git a/crates/cord-cordic/src/eval.rs b/crates/cord-cordic/src/eval.rs index 769a87b..19c46ef 100644 --- a/crates/cord-cordic/src/eval.rs +++ b/crates/cord-cordic/src/eval.rs @@ -15,7 +15,7 @@ pub struct CORDICEvaluator { impl CORDICEvaluator { pub fn new(word_bits: u8) -> Self { - let frac_bits = word_bits - 1; + let frac_bits = word_bits / 2; CORDICEvaluator { frac_bits, instr_table_idx: Vec::new(), @@ -26,7 +26,7 @@ impl CORDICEvaluator { /// Create an evaluator from a compiled program, inheriting per-op tables. pub fn from_program(program: &CORDICProgram) -> Self { CORDICEvaluator { - frac_bits: program.word_bits - 1, + frac_bits: program.word_bits / 2, instr_table_idx: program.instr_table_idx.clone(), tables: program.tables.clone(), } @@ -190,15 +190,17 @@ impl CORDICEvaluator { let (_, angle) = self.cordic_vectoring_with(one, vals[*a as usize], t); angle } - TrigOp::Sinh(a) => self.to_fixed(self.to_float(vals[*a as usize]).sinh()), - TrigOp::Cosh(a) => self.to_fixed(self.to_float(vals[*a as usize]).cosh()), - TrigOp::Tanh(a) => self.to_fixed(self.to_float(vals[*a as usize]).tanh()), + // Phasor-mode CORDIC implementation coming + TrigOp::Sinh(_) => unimplemented!("sinh: phasor CORDIC pending"), + TrigOp::Cosh(_) => unimplemented!("cosh: phasor CORDIC pending"), + TrigOp::Tanh(_) => unimplemented!("tanh: phasor CORDIC pending"), + TrigOp::Exp(_) => unimplemented!("exp: phasor CORDIC pending"), + TrigOp::Ln(_) => unimplemented!("ln: phasor CORDIC pending"), + TrigOp::Asinh(a) => self.to_fixed(self.to_float(vals[*a as usize]).asinh()), TrigOp::Acosh(a) => self.to_fixed(self.to_float(vals[*a as usize]).acosh()), TrigOp::Atanh(a) => self.to_fixed(self.to_float(vals[*a as usize]).atanh()), TrigOp::Sqrt(a) => self.fixed_sqrt(vals[*a as usize]), - TrigOp::Exp(a) => self.to_fixed(self.to_float(vals[*a as usize]).exp()), - TrigOp::Ln(a) => self.to_fixed(self.to_float(vals[*a as usize]).ln()), TrigOp::Hypot(a, b) => { let t = if use_per_instr { self.table_for(i) } else { &self.tables[0] }; diff --git a/crates/cord-decompile/src/reconstruct.rs b/crates/cord-decompile/src/reconstruct.rs index e89e479..22d4fe4 100644 --- a/crates/cord-decompile/src/reconstruct.rs +++ b/crates/cord-decompile/src/reconstruct.rs @@ -235,17 +235,17 @@ fn align_box_axes( ]; let mut assignment = [0usize; 3]; - let mut src_used = [false; 3]; - let mut dst_used = [false; 3]; + let mut src_assigned = [false; 3]; + let mut dst_assigned = [false; 3]; for _ in 0..3 { let mut best_dot = 0.0f64; let mut best_src = 0; let mut best_dst = 0; for src in 0..3 { - if src_used[src] { continue; } + if src_assigned[src] { continue; } for dst in 0..3 { - if dst_used[dst] { continue; } + if dst_assigned[dst] { continue; } let d = axes[src].dot(canonical[dst]).abs(); if d > best_dot { best_dot = d; @@ -255,8 +255,8 @@ fn align_box_axes( } } assignment[best_src] = best_dst; - src_used[best_src] = true; - dst_used[best_dst] = true; + src_assigned[best_src] = true; + dst_assigned[best_dst] = true; } let mut ordered = [0.0; 3]; diff --git a/crates/cord-expr/src/parser.rs b/crates/cord-expr/src/parser.rs index 9e62007..c3c77c8 100644 --- a/crates/cord-expr/src/parser.rs +++ b/crates/cord-expr/src/parser.rs @@ -384,7 +384,10 @@ impl<'a> ExprParser<'a> { return Ok(self.graph.push(TrigOp::Mul(sq, base))); } } - Ok(self.graph.push(TrigOp::Mul(base, exp))) + // base^exp = exp(exp * ln(base)) + let ln_base = self.graph.push(TrigOp::Ln(base)); + let product = self.graph.push(TrigOp::Mul(exp, ln_base)); + Ok(self.graph.push(TrigOp::Exp(product))) } else { Ok(base) } diff --git a/crates/cord-expr/src/token.rs b/crates/cord-expr/src/token.rs index fc6d6f8..e16f4b3 100644 --- a/crates/cord-expr/src/token.rs +++ b/crates/cord-expr/src/token.rs @@ -44,7 +44,7 @@ pub(crate) fn tokenize(input: &str) -> Result<(Vec, Vec), String> '/' => { chars.next(); match chars.peek() { - Some('/') | Some('=') => { + Some('/') => { while let Some(&c) = chars.peek() { chars.next(); if c == '\n' { line += 1; break; } diff --git a/crates/cord-sdf/src/lower.rs b/crates/cord-sdf/src/lower.rs index cfae932..13009cc 100644 --- a/crates/cord-sdf/src/lower.rs +++ b/crates/cord-sdf/src/lower.rs @@ -15,8 +15,24 @@ impl std::fmt::Display for LowerError { impl std::error::Error for LowerError {} -/// Variable environment for expression evaluation during lowering. -type Env = HashMap; +#[derive(Debug, Clone)] +enum Value { + Scalar(f64), + Array(Vec), +} + +type Env = HashMap; + +fn env_get_scalar(env: &Env, name: &str) -> Option { + match env.get(name)? { + Value::Scalar(v) => Some(*v), + _ => None, + } +} + +fn env_insert_scalar(env: &mut Env, name: String, val: f64) { + env.insert(name, Value::Scalar(val)); +} /// Lower a parsed SCAD program into an SDF tree. /// Multiple top-level statements become an implicit union. @@ -47,7 +63,7 @@ fn lower_statement(stmt: &Statement, env: &mut Env) -> Result, L Statement::BooleanOp(bop) => lower_boolean(bop, env).map(Some), Statement::Assignment(asgn) => { if let Some(val) = eval_expr_env(&asgn.value, env) { - env.insert(asgn.name.clone(), val); + env_insert_scalar(env, asgn.name.clone(), val); } Ok(None) } @@ -69,7 +85,7 @@ fn lower_for_loop(fl: &ForLoop, env: &mut Env) -> Result { let mut branches = Vec::with_capacity(values.len()); for val in values { let mut inner_env = env.clone(); - inner_env.insert(fl.var.clone(), val); + env_insert_scalar(&mut inner_env, fl.var.clone(), val); let nodes = lower_statements(&fl.body, &mut inner_env)?; match nodes.len() { 0 => {} @@ -172,7 +188,7 @@ fn eval_expr_env(expr: &Expr, env: &Env) -> Option { match expr { Expr::Number(n) => Some(*n), Expr::Bool(b) => Some(if *b { 1.0 } else { 0.0 }), - Expr::Ident(name) => env.get(name).copied(), + Expr::Ident(name) => env_get_scalar(env, name), Expr::UnaryOp { op: UnaryOp::Neg, operand } => eval_expr_env(operand, env).map(|n| -n), Expr::UnaryOp { op: UnaryOp::Not, operand } => { eval_expr_env(operand, env).map(|n| if n == 0.0 { 1.0 } else { 0.0 }) diff --git a/crates/cord-sdf/src/scad.rs b/crates/cord-sdf/src/scad.rs index 971304b..e00628f 100644 --- a/crates/cord-sdf/src/scad.rs +++ b/crates/cord-sdf/src/scad.rs @@ -126,9 +126,7 @@ fn emit_scad(node: &SdfNode, depth: usize, out: &mut String) { SdfNode::SmoothUnion { children, k } => { indent(depth, out); - let _ = writeln!(out, "// smooth union (k={})", fmt(*k)); - indent(depth, out); - let _ = writeln!(out, "union() {{"); + let _ = writeln!(out, "smooth_union(k={}) {{", fmt(*k)); for c in children { emit_scad(c, depth + 1, out); } diff --git a/src/main.rs b/src/main.rs index b03ca33..4d517f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -191,6 +191,7 @@ fn cmd_build(input: &std::path::Path, output: Option, word_bits: u8) -> } else { writer.write_source_scad(&source)?; } + writer.write_trig(&graph.to_bytes())?; writer.write_shader(&wgsl)?; writer.write_cordic(&cordic_bytes, word_bits)?; writer.write_config(&config_toml)?;