merge integration
This commit is contained in:
commit
59819cf1af
|
|
@ -104,8 +104,6 @@ fn merge_planes_into_boxes(
|
||||||
let d2 = axis.dot(*p2);
|
let d2 = axis.dot(*p2);
|
||||||
let half = (d1 - d2).abs() / 2.0;
|
let half = (d1 - d2).abs() / 2.0;
|
||||||
if half > 1e-6 {
|
if half > 1e-6 {
|
||||||
let center_along_axis = (d1 + d2) / 2.0;
|
|
||||||
let _ = center_along_axis;
|
|
||||||
pairs.push((ia, ib, axis, half));
|
pairs.push((ia, ib, axis, half));
|
||||||
used[ia] = true;
|
used[ia] = true;
|
||||||
used[ib] = true;
|
used[ib] = true;
|
||||||
|
|
@ -236,18 +234,18 @@ fn align_box_axes(
|
||||||
Vec3::new(0.0, 0.0, 1.0),
|
Vec3::new(0.0, 0.0, 1.0),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Assign each detected axis to the closest canonical axis
|
|
||||||
let mut assignment = [0usize; 3];
|
let mut assignment = [0usize; 3];
|
||||||
let mut assigned = [false; 3];
|
let mut src_used = [false; 3];
|
||||||
|
let mut dst_used = [false; 3];
|
||||||
|
|
||||||
for pass in 0..3 {
|
for _ in 0..3 {
|
||||||
let mut best_dot = 0.0f64;
|
let mut best_dot = 0.0f64;
|
||||||
let mut best_src = 0;
|
let mut best_src = 0;
|
||||||
let mut best_dst = 0;
|
let mut best_dst = 0;
|
||||||
for src in 0..3 {
|
for src in 0..3 {
|
||||||
if assignment[src] != 0 && pass > 0 { continue; }
|
if src_used[src] { continue; }
|
||||||
for dst in 0..3 {
|
for dst in 0..3 {
|
||||||
if assigned[dst] { continue; }
|
if dst_used[dst] { continue; }
|
||||||
let d = axes[src].dot(canonical[dst]).abs();
|
let d = axes[src].dot(canonical[dst]).abs();
|
||||||
if d > best_dot {
|
if d > best_dot {
|
||||||
best_dot = d;
|
best_dot = d;
|
||||||
|
|
@ -257,8 +255,8 @@ fn align_box_axes(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assignment[best_src] = best_dst;
|
assignment[best_src] = best_dst;
|
||||||
assigned[best_dst] = true;
|
src_used[best_src] = true;
|
||||||
let _ = pass;
|
dst_used[best_dst] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ordered = [0.0; 3];
|
let mut ordered = [0.0; 3];
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,8 @@ impl TrigGraph {
|
||||||
TrigOp::Add(_, _) | TrigOp::Sub(_, _) | TrigOp::Neg(_)
|
TrigOp::Add(_, _) | TrigOp::Sub(_, _) | TrigOp::Neg(_)
|
||||||
| TrigOp::Abs(_) | TrigOp::Min(_, _) | TrigOp::Max(_, _)
|
| TrigOp::Abs(_) | TrigOp::Min(_, _) | TrigOp::Max(_, _)
|
||||||
| TrigOp::Clamp { .. } => cost.binary += 1,
|
| TrigOp::Clamp { .. } => cost.binary += 1,
|
||||||
_ => {}
|
TrigOp::InputX | TrigOp::InputY | TrigOp::InputZ
|
||||||
|
| TrigOp::Const(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cost
|
cost
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ir::{NodeId, TrigGraph, TrigOp};
|
use crate::ir::{NodeId, TrigGraph, TrigOp};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::HashSet;
|
||||||
|
|
||||||
/// Optimize a TrigGraph in-place.
|
/// Optimize a TrigGraph in-place.
|
||||||
///
|
///
|
||||||
|
|
@ -53,7 +53,7 @@ fn constant_fold(graph: &mut TrigGraph) {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => None,
|
TrigOp::InputX | TrigOp::InputY | TrigOp::InputZ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(c) = val {
|
if let Some(c) = val {
|
||||||
|
|
@ -96,28 +96,9 @@ fn fold2(values: &[Option<f64>], a: NodeId, b: NodeId, f: fn(f64, f64) -> f64) -
|
||||||
// for the cost model and leave the graph structure intact. The CORDIC
|
// for the cost model and leave the graph structure intact. The CORDIC
|
||||||
// compiler already recognizes shared-angle patterns.
|
// compiler already recognizes shared-angle patterns.
|
||||||
|
|
||||||
fn fuse_sincos_pairs(graph: &mut TrigGraph) {
|
// Sin/cos pair fusion: placeholder for future CORDIC fused-op rewriting.
|
||||||
let mut sin_of: HashMap<NodeId, NodeId> = HashMap::new();
|
// The CORDIC compiler already recognizes shared-angle patterns.
|
||||||
let mut cos_of: HashMap<NodeId, NodeId> = HashMap::new();
|
fn fuse_sincos_pairs(_graph: &mut TrigGraph) {}
|
||||||
|
|
||||||
for (i, op) in graph.nodes.iter().enumerate() {
|
|
||||||
match op {
|
|
||||||
TrigOp::Sin(a) => { sin_of.insert(*a, i as NodeId); }
|
|
||||||
TrigOp::Cos(a) => { cos_of.insert(*a, i as NodeId); }
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count fused pairs (both sin and cos of same angle exist)
|
|
||||||
let _fused: Vec<(NodeId, NodeId)> = sin_of.iter()
|
|
||||||
.filter_map(|(angle, sin_id)| {
|
|
||||||
cos_of.get(angle).map(|cos_id| (*sin_id, *cos_id))
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// The CORDIC compiler handles fusion; this pass is a no-op for now
|
|
||||||
// but validates that pairs exist. Future: rewrite to a fused op.
|
|
||||||
}
|
|
||||||
|
|
||||||
// === Pass 3: Dead node elimination ===
|
// === Pass 3: Dead node elimination ===
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,15 +108,9 @@ fn traverse_sequential(graph: &TrigGraph, bounds: &EvalBounds) -> EvalResult {
|
||||||
fn traverse_parallel_mesh(
|
fn traverse_parallel_mesh(
|
||||||
graph: &TrigGraph,
|
graph: &TrigGraph,
|
||||||
bounds: &EvalBounds,
|
bounds: &EvalBounds,
|
||||||
divisions: usize,
|
_divisions: usize,
|
||||||
overlap: f64,
|
_overlap: f64,
|
||||||
) -> EvalResult {
|
) -> EvalResult {
|
||||||
let center = [
|
|
||||||
(bounds.min[0] + bounds.max[0]) * 0.5,
|
|
||||||
(bounds.min[1] + bounds.max[1]) * 0.5,
|
|
||||||
(bounds.min[2] + bounds.max[2]) * 0.5,
|
|
||||||
];
|
|
||||||
|
|
||||||
let res = bounds.resolution;
|
let res = bounds.resolution;
|
||||||
let step = [
|
let step = [
|
||||||
(bounds.max[0] - bounds.min[0]) / (res - 1).max(1) as f64,
|
(bounds.max[0] - bounds.min[0]) / (res - 1).max(1) as f64,
|
||||||
|
|
@ -124,11 +118,6 @@ fn traverse_parallel_mesh(
|
||||||
(bounds.max[2] - bounds.min[2]) / (res - 1).max(1) as f64,
|
(bounds.max[2] - bounds.min[2]) / (res - 1).max(1) as f64,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Sector boundaries in spherical angles
|
|
||||||
let theta_step = PI / divisions as f64;
|
|
||||||
let phi_step = 2.0 * PI / divisions as f64;
|
|
||||||
let _overlap_angle = overlap * theta_step;
|
|
||||||
|
|
||||||
let mut values = Vec::with_capacity(res * res * res);
|
let mut values = Vec::with_capacity(res * res * res);
|
||||||
|
|
||||||
for iz in 0..res {
|
for iz in 0..res {
|
||||||
|
|
@ -138,39 +127,8 @@ fn traverse_parallel_mesh(
|
||||||
for ix in 0..res {
|
for ix in 0..res {
|
||||||
let x = bounds.min[0] + ix as f64 * step[0];
|
let x = bounds.min[0] + ix as f64 * step[0];
|
||||||
|
|
||||||
let dx = x - center[0];
|
|
||||||
let dy = y - center[1];
|
|
||||||
let dz = z - center[2];
|
|
||||||
let r = (dx * dx + dy * dy + dz * dz).sqrt();
|
|
||||||
|
|
||||||
let val = evaluate(graph, x, y, z);
|
let val = evaluate(graph, x, y, z);
|
||||||
|
// TODO: sector assignment + blend weights for actual parallel dispatch
|
||||||
if r < 1e-10 {
|
|
||||||
values.push(SpatialSample { position: [x, y, z], value: val });
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let theta = (dz / r).acos();
|
|
||||||
let phi = dy.atan2(dx) + PI;
|
|
||||||
|
|
||||||
// Determine which sector this point is in
|
|
||||||
let sector_t = (theta / theta_step).floor() as usize;
|
|
||||||
let sector_p = (phi / phi_step).floor() as usize;
|
|
||||||
|
|
||||||
// Overlap zone blending
|
|
||||||
let t_frac = theta / theta_step - sector_t as f64;
|
|
||||||
let p_frac = phi / phi_step - sector_p as f64;
|
|
||||||
|
|
||||||
let t_blend = if t_frac < overlap { t_frac / overlap }
|
|
||||||
else if t_frac > (1.0 - overlap) { (1.0 - t_frac) / overlap }
|
|
||||||
else { 1.0 };
|
|
||||||
let p_blend = if p_frac < overlap { p_frac / overlap }
|
|
||||||
else if p_frac > (1.0 - overlap) { (1.0 - p_frac) / overlap }
|
|
||||||
else { 1.0 };
|
|
||||||
|
|
||||||
let blend = t_blend.min(1.0) * p_blend.min(1.0);
|
|
||||||
let _ = (sector_t, sector_p, blend); // Sector info for parallel dispatch
|
|
||||||
|
|
||||||
values.push(SpatialSample { position: [x, y, z], value: val });
|
values.push(SpatialSample { position: [x, y, z], value: val });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -216,8 +174,6 @@ fn traverse_spherical(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Angular samples scale with r² (surface area of the shell)
|
|
||||||
let shell_area = 4.0 * PI * r * r;
|
|
||||||
let angular_samples = (base_angular_samples as f64 * t * t).max(6.0) as usize;
|
let angular_samples = (base_angular_samples as f64 * t * t).max(6.0) as usize;
|
||||||
|
|
||||||
// Fibonacci sphere sampling for uniform angular distribution
|
// Fibonacci sphere sampling for uniform angular distribution
|
||||||
|
|
@ -237,18 +193,9 @@ fn traverse_spherical(
|
||||||
volume_count += 1;
|
volume_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convergence check: RMS of volume vs surface area of current shell
|
|
||||||
let rms = (volume_sum_sq / volume_count as f64).sqrt();
|
let rms = (volume_sum_sq / volume_count as f64).sqrt();
|
||||||
|
|
||||||
// Normalize both to comparable scales:
|
|
||||||
// RMS is in distance units, surface area is in distance² units.
|
|
||||||
// Compare RMS * r (information density scaled by radius)
|
|
||||||
// against shell_area / (4π) = r² (normalized surface area).
|
|
||||||
// Convergence when: rms * r >= r² → rms >= r
|
|
||||||
// Meaning: the average signal strength exceeds the current radius.
|
|
||||||
// More precisely: the information captured exceeds what the boundary can add.
|
|
||||||
let volume_metric = rms * r;
|
let volume_metric = rms * r;
|
||||||
let surface_metric = shell_area / (4.0 * PI); // = r²
|
let surface_metric = r * r;
|
||||||
|
|
||||||
if volume_metric >= surface_metric && ri > radial_steps / 4 {
|
if volume_metric >= surface_metric && ri > radial_steps / 4 {
|
||||||
convergence_radius = r;
|
convergence_radius = r;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue