use camino::Utf8Path;
use pikru_compare::{extract_svg, run_c_pikchr};
const C_PIKCHR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/vendor/pikchr-c/pikchr");
const TEST32: &str = include_str!("../vendor/pikchr-c/tests/test32.pikchr");
fn extract_circles(svg_str: &str) -> Vec<(f64, f64, f64, String)> {
let mut circles = Vec::new();
let mut rest = svg_str;
while let Some(start) = rest.find("<circle") {
rest = &rest[start..];
let end = if let Some(self_close) = rest.find("/>") {
if let Some(explicit_close) = rest.find("</circle>") {
if self_close < explicit_close {
self_close + 2
} else {
explicit_close + 9
}
} else {
self_close + 2
}
} else if let Some(explicit_close) = rest.find("</circle>") {
explicit_close + 9
} else {
rest.len()
};
let circle_elem = &rest[..end];
let cx = extract_attr(circle_elem, "cx")
.and_then(|s| s.parse().ok())
.unwrap_or(0.0);
let cy = extract_attr(circle_elem, "cy")
.and_then(|s| s.parse().ok())
.unwrap_or(0.0);
let r = extract_attr(circle_elem, "r")
.and_then(|s| s.parse().ok())
.unwrap_or(0.0);
let fill = extract_style_prop(circle_elem, "fill").unwrap_or_else(|| "unknown".to_string());
circles.push((cx, cy, r, fill));
rest = &rest[end..];
}
circles
}
fn extract_attr(line: &str, attr: &str) -> Option<String> {
let pattern = format!("{}=\"", attr);
let start = line.find(&pattern)? + pattern.len();
let rest = &line[start..];
let end = rest.find('"')?;
Some(rest[..end].to_string())
}
fn extract_style_prop(line: &str, prop: &str) -> Option<String> {
let style_start = line.find("style=\"")?;
let style_content = &line[style_start + 7..];
let style_end = style_content.find('"')?;
let style = &style_content[..style_end];
let prop_pattern = format!("{}:", prop);
if let Some(idx) = style.find(&prop_pattern) {
let rest = &style[idx + prop_pattern.len()..];
let rest = rest.trim_start();
let end = rest.find(';').unwrap_or(rest.len());
return Some(rest[..end].trim().to_string());
}
if prop == "fill"
&& let Some(fill) = extract_attr(line, "fill")
{
return Some(fill);
}
None
}
fn extract_viewbox(svg_str: &str) -> Option<(f64, f64, f64, f64)> {
let start = svg_str.find("viewBox=\"")? + 9;
let rest = &svg_str[start..];
let end = rest.find('"')?;
let parts: Vec<f64> = rest[..end]
.split_whitespace()
.filter_map(|s| s.parse().ok())
.collect();
if parts.len() == 4 {
Some((parts[0], parts[1], parts[2], parts[3]))
} else {
None
}
}
#[test]
fn debug_test32_circles() {
let c_output = run_c_pikchr(Utf8Path::new(C_PIKCHR), TEST32);
let c_svg = extract_svg(&c_output).expect("C should produce SVG");
let rust_output = pikru::pikchr(TEST32).expect("Rust should produce SVG");
let rust_svg = extract_svg(&rust_output).expect("Rust should produce SVG");
let c_vb = extract_viewbox(c_svg);
let rust_vb = extract_viewbox(rust_svg);
println!("\n=== VIEWBOX COMPARISON ===");
println!("C: {:?}", c_vb);
println!("Rust: {:?}", rust_vb);
let c_circles = extract_circles(c_svg);
let rust_circles = extract_circles(rust_svg);
println!("\n=== CIRCLE COMPARISON ===");
println!(
"C has {} circles, Rust has {} circles",
c_circles.len(),
rust_circles.len()
);
println!("\n--- C Circles ---");
for (i, (cx, cy, r, fill)) in c_circles.iter().enumerate() {
println!(
" {}: cx={:.3} cy={:.3} r={:.3} fill={}",
i, cx, cy, r, fill
);
}
println!("\n--- Rust Circles ---");
for (i, (cx, cy, r, fill)) in rust_circles.iter().enumerate() {
println!(
" {}: cx={:.3} cy={:.3} r={:.3} fill={}",
i, cx, cy, r, fill
);
}
println!("\n=== DIFFERENCES ===");
let min_len = c_circles.len().min(rust_circles.len());
for i in 0..min_len {
let (c_cx, c_cy, c_r, c_fill) = &c_circles[i];
let (r_cx, r_cy, r_r, r_fill) = &rust_circles[i];
let cx_diff = (c_cx - r_cx).abs();
let cy_diff = (c_cy - r_cy).abs();
let r_diff = (c_r - r_r).abs();
if cx_diff > 0.01 || cy_diff > 0.01 || r_diff > 0.01 || c_fill != r_fill {
println!("Circle {}:", i);
if cx_diff > 0.01 {
println!(" cx: C={:.3} Rust={:.3} diff={:.3}", c_cx, r_cx, cx_diff);
}
if cy_diff > 0.01 {
println!(" cy: C={:.3} Rust={:.3} diff={:.3}", c_cy, r_cy, cy_diff);
}
if r_diff > 0.01 {
println!(" r: C={:.3} Rust={:.3} diff={:.3}", c_r, r_r, r_diff);
}
if c_fill != r_fill {
println!(" fill: C={} Rust={}", c_fill, r_fill);
}
}
}
}
#[test]
fn debug_test32_vertex_positions() {
let source = r#"
spline right then up then left then down ->
"#;
let result = pikru::pikchr(source).expect("should parse");
println!("\n=== Simple spline SVG ===");
println!("{}", result);
}
#[test]
fn debug_rad_150_percent() {
let source = r#"
dot rad 150%
dot same
dot same
dot same
"#;
let c_output = run_c_pikchr(Utf8Path::new(C_PIKCHR), source);
let c_svg = extract_svg(&c_output).expect("C should produce SVG");
let c_circles = extract_circles(c_svg);
let rust_output = pikru::pikchr(source).expect("should parse");
let rust_circles = extract_circles(&rust_output);
println!("\n=== rad 150% with same ===");
println!("\n--- C circles ---");
for (i, (cx, cy, r, _)) in c_circles.iter().enumerate() {
println!(" dot {}: cx={:.3} cy={:.3} r={:.6}", i, cx, cy, r);
}
println!("\n--- Rust circles ---");
for (i, (cx, cy, r, _)) in rust_circles.iter().enumerate() {
println!(" dot {}: cx={:.3} cy={:.3} r={:.6}", i, cx, cy, r);
}
if rust_circles.len() >= 2 {
let first_r = rust_circles[0].2;
for (i, (_, _, r, _)) in rust_circles.iter().enumerate().skip(1) {
assert!(
(first_r - r).abs() < 0.001,
"Dot {} radius {:.6} should equal first dot radius {:.6}",
i,
r,
first_r
);
}
}
}