plotpoint 0.1.9

A library to convert a point cloud into a (differentiable) image
Documentation
use catenary::Catenary;
use contourable::Contour;
use levenberg_marquardt::LevenbergMarquardt;
//todo: should come with catenary
use nalgebra::{Affine2, DVector, Point2};
use num_dual::DualDVec64;
use plotpoint::{image::Image, optim::PointProblem};

fn main() {
    //TODO: Err for jacobian/function eval = optimization stops. Find a way to handle this.
    // ex: dist + exp(p)=l,
    let contour = contourable::closed::circle::Circle {
        radius: 0.5,
        center: Point2::origin(),
    };
    let cat0 = Catenary::from_points_length(&contour.position(&2.5), &contour.position(&1.0), 1.4)
        .unwrap();
    println!("cat: {:?}, length: {}", cat0, cat0.length());
    let res = (20, 30);
    let chain_width = 0.05;
    let scale = res.0 as f64 / (2.0 * contour.radius);
    let n = (cat0.length() * scale) as u32;
    let n = n * 4;
    let cat_pts = cat0.divide(cat0.s_0, cat0.s_1, n);
    let translation = nalgebra::Translation2::new(res.1 as f64 / 2.0, res.0 as f64 / 2.0);
    let rotation = nalgebra::UnitComplex::identity();
    let pix_to_point = nalgebra::Similarity2::<f64>::from_parts(translation, rotation, scale);
    let mut m = pix_to_point.to_homogeneous();
    m[(1, 1)] *= -1.0;
    let pix_to_point = Affine2::from_matrix_unchecked(m);
    println!("{:?}", pix_to_point.to_homogeneous());
    let background = nalgebra::DMatrix::from_element(res.0, res.1, 1.0);
    let weight = cat0.length() / (n - 1) as f64 * scale.powf(2.0) * chain_width;
    println!("w:{:?}", weight);
    let point_weights = cat_pts.iter().map(|p| (*p, weight)).collect::<Vec<_>>();
    let img = Image::from_points(&point_weights, &pix_to_point, &background);
    let output = img.to_image();
    output.save("cat.png").unwrap();

    let param = DVector::from_column_slice(&[2.5 * 0.9, 1.0 * 1.10, cat0.length() * 1.2]); //contour_0, contour_1, cat_l //todo: wrap
    let table = catenary::grid::CatenaryTable::load(
        "/home/hubert/Documents/test/catenary/output/catenary_grid_t1000_d1000.bin", //TODO: get this automatically
                                                                                     // "/home/hub/catenary/output/catenary_grid_t1000_d1000.bin", //TODO: get this automatically
    )
    .unwrap();
    let pb = PointProblem::new(
        &param,
        img,
        |x| {
            let contour_0 = &x[0];
            let contour_1 = &x[1];
            let cat_l = &x[2];
            let p0 = contour.position(contour_0);
            let p1 = contour.position(contour_1);
            let cat = table.get_catenary::<DualDVec64>(&p0, &p1, cat_l)?;
            let n = 100;
            //todo: use this output type for the Fn output to avoid reallocation
            let pts = cat.divide(cat.s_0.clone(), cat.s_1.clone(), n);
            let w = cat.length() * scale.powf(2.0) * chain_width / (n - 1) as f64;
            let pts_w = pts
                .iter()
                .map(|p| (p.clone(), w.clone()))
                .collect::<Vec<_>>();
            Some(pts_w)
        },
        pix_to_point,
    );
    let solver = LevenbergMarquardt::new().with_ftol(1e-20).with_xtol(1e-20);
    save_result(
        "cat_opt_before.png",
        &contour,
        n,
        &pix_to_point,
        &background,
        scale,
        chain_width,
        &pb.params,
    );
    let (result, report) = solver.minimize(pb);
    // println!("{:?}", cat);
    println!("params: {:?}", result.params);
    println!("report: {:?}", report);
    save_result(
        "cat_opt_after.png",
        &contour,
        n,
        &pix_to_point,
        &background,
        scale,
        chain_width,
        &result.params,
    );
}

fn save_result(
    path: &str,
    contour: &contourable::closed::circle::Circle<f64>,
    n: u32,
    pix_to_point: &nalgebra::Transform<f64, nalgebra::TAffine, 2>,
    background: &nalgebra::Matrix<
        f64,
        nalgebra::Dyn,
        nalgebra::Dyn,
        nalgebra::VecStorage<f64, nalgebra::Dyn, nalgebra::Dyn>,
    >,
    scale: f64,
    chain_width: f64,
    params: &DVector<f64>,
) {
    let contour_0 = &params[0];
    let contour_1 = &params[1];
    let cat_l = &params[2];
    let p0 = contour.position(contour_0);
    let p1 = contour.position(contour_1);
    let cat = Catenary::from_points_length(&p0, &p1, *cat_l);
    if cat.is_none() {
        println!("cat is none for {:?}", path);
        return;
    }
    let cat = cat.unwrap();
    let cat_pts = cat.divide(cat.s_0, cat.s_1, n);
    let w = cat.length() * scale.powf(2.0) * chain_width / (n - 1) as f64;
    let pts_w = cat_pts.iter().map(|p| (*p, w)).collect::<Vec<_>>();
    let img = Image::from_points(&pts_w, pix_to_point, background);
    let output = img.to_image();
    output.save(path).unwrap();
}