src/main.rs

changeset 37
d7cd14b8ccc0
parent 33
556afeaa34bf
child 42
271fba635bce
--- a/src/main.rs	Thu Nov 07 16:52:05 2024 -0500
+++ b/src/main.rs	Wed Dec 04 23:19:46 2024 -0500
@@ -11,9 +11,11 @@
 mod manifold;
 mod fb;
 mod cube;
+mod cylinder;
 mod dist;
 mod zero;
 mod scaled;
+mod newton;
 
 use serde::{Serialize, Deserialize};
 use alg_tools::logger::Logger;
@@ -29,55 +31,61 @@
 
 use dist::{DistTo, DistToSquaredDiv2};
 use fb::{forward_backward, IterInfo, Desc, Prox};
-use manifold::EmbeddedManifoldPoint;
-use cube::*;
-use Face::*;
+use manifold::{EmbeddedManifoldPoint, ManifoldPoint, FacedManifoldPoint};
+use cube::OnCube;
+use cylinder::{CylCoords, Cylinder, CylinderConfig, OnCylinder, normalise_angle};
 #[allow(unused_imports)]
 use zero::ZeroFn;
 use scaled::Scaled;
 
+/// Location for saving results
+static PREFIX : &str = "res";
+
 /// Program entry point
 fn main() {
-    simple_cube_test().unwrap()
+    simple_cube_test(format!("{PREFIX}/cube").as_str()).unwrap();
+    simple_cylinder_test(format!("{PREFIX}/cylinder").as_str()).unwrap();
 }
 
 /// Helper structure for saving a point on a cube into a CSV file
 #[derive(Serialize,Deserialize,Debug)]
-struct CSVPoint {
+struct CSVPoint<Face> {
     face : Face,
     x : f64,
     y : f64,
     z : f64
 }
 
-impl From<&OnCube> for CSVPoint {
-    fn from(point : &OnCube) -> Self {
+impl<M> From<&M> for CSVPoint<M::Face>
+where M : EmbeddedManifoldPoint<EmbeddedCoords = Loc<f64, 3>> + FacedManifoldPoint {
+    fn from(point : &M) -> Self {
         let Loc([x,y,z]) = point.embedded_coords();
         let face = point.face();
         CSVPoint { face, x,  y, z }
     }
 }
 
-/// Helper structure for saving the log into a CSV file
+/// Helper structure for saving a point on a cylinder into a CSV file
 #[derive(Serialize,Deserialize,Debug)]
-struct CSVLog {
-    iter : usize,
-    value : f64,
-    // serde is junk
-    //#[serde(flatten)]
-    //point : CSVPoint
+struct CSVCylPoint<Face> {
     face : Face,
-    x : f64,
-    y : f64,
+    angle : f64,
+    r : f64,
     z : f64
 }
 
-
-/// Location for saving results
-static PREFIX : &str = "res";
+impl<'a> From<&'a crate::cylinder::OnCylinder<'a>> for CSVCylPoint<crate::cylinder::Face> {
+    fn from(point : &'a OnCylinder<'a>) -> Self {
+        let CylCoords {r, angle, z} = point.cyl_coords();
+        let face = point.face();
+        CSVCylPoint { face, r, angle, z }
+    }
+}
 
 /// A simple test on the cube
-fn simple_cube_test() -> DynError {
+fn simple_cube_test(dir : &str) -> DynError {
+    use crate::cube::Face;
+    use crate::cube::Face::*;
     
     let points = [
         OnCube::new(F1, Loc([0.5, 0.7])),
@@ -90,37 +98,34 @@
 
     let origin = OnCube::new(F4, Loc([0.5, 0.5]));
 
-    write_points(format!("{PREFIX}/data"), points.iter())?;
-    write_points(format!("{PREFIX}/origin"), std::iter::once(&origin))?;
+    std::fs::create_dir_all(dir)?;
+    write_csv(points.iter().map(CSVPoint::from), format!("{dir}/data.csv"))?;
+    write_csv(std::iter::once(&origin).map(CSVPoint::from), format!("{dir}/origin.csv"))?;
 
     let f = Sum::new(points.into_iter().map(DistToSquaredDiv2));
     //let g = ZeroFn::new();
     let g = Scaled::new(0.5, DistTo(origin));
     let τ = 0.1;
     
-    std::fs::create_dir_all(PREFIX)?;
     for face in Face::all() {
-        write_face_csv(format!("{PREFIX}/{face}"), face, 32, |x| f.apply(x) + g.apply(x))?;
+        write_cube_face_csv(format!("{dir}/{face}"), face, 32, |x| f.apply(x) + g.apply(x))?;
     }
-    write_face_imgs(128, |x| f.apply(x) + g.apply(x))?;
-
-    run_and_save("x1", &f, &g, OnCube::new(F3, Loc([0.1, 0.7])), τ)?;
-    run_and_save("x2", &f, &g, OnCube::new(F2, Loc([0.3, 0.1])), τ)?;
-    run_and_save("x3", &f, &g, OnCube::new(F6, Loc([0.6, 0.2])), τ)
-}
+    write_cube_face_imgs(dir, 128, |x| f.apply(x) + g.apply(x))?;
 
-/// Runs [forward_backward] and saves the results.
-pub fn run_and_save<F, G>(
-    name : &str,
-    f : &F,
-    g : &G,
-    x : OnCube,
-    τ : f64,
-) -> DynError
-where F : Desc<OnCube> +  Mapping<OnCube, Codomain = f64>,
-      G : Prox<OnCube> +  Mapping<OnCube, Codomain = f64> {
-    
-    let mut logger = Logger::new();
+    /// Helper structure for saving the log into a CSV file
+    #[derive(Serialize,Deserialize,Debug)]
+    struct CSVLog<Face> {
+        iter : usize,
+        value : f64,
+        // serde is junk
+        //#[serde(flatten)]
+        //point : CSVPoint
+        face : Face,
+        x : f64,
+        y : f64,
+        z : f64
+    }
+
     let logmap = |iter, IterInfo { value, point } : IterInfo<OnCube>| {
         let CSVPoint {x , y, z, face} = CSVPoint::from(&point);
         CSVLog {
@@ -128,6 +133,93 @@
             x, y, z, face
          }
     };
+
+    run_and_save(dir, "x1", &f, &g, OnCube::new(F3, Loc([0.1, 0.7])), τ, logmap)?;
+    run_and_save(dir, "x2", &f, &g, OnCube::new(F2, Loc([0.3, 0.1])), τ, logmap)?;
+    run_and_save(dir, "x3", &f, &g, OnCube::new(F6, Loc([0.6, 0.2])), τ, logmap)
+}
+
+
+/// A simple test on the cube
+fn simple_cylinder_test(dir : &str) -> DynError {
+    let π = f64::PI;
+
+    let cyl = Cylinder {
+        radius : 1.0,
+        height : 1.0,
+        config : CylinderConfig { newton_iters : 100 },
+        //Default::default(),
+    };
+    
+    let points = [
+        cyl.on_side(π/2.0, 0.4),
+        cyl.on_side(π*3.0/2.0, -0.1),
+        cyl.on_side(π, 0.2),
+        cyl.on_side(-π/5.0, -0.2),
+        cyl.on_side(π*2.0/3.0, -0.45),
+        cyl.on_top(π*3.0/4.0, 0.7),
+        cyl.on_bottom(π*5.0/4.0, 0.3),
+    ];
+
+    let origin = cyl.on_side(0.0, 0.0);
+
+    std::fs::create_dir_all(dir)?;
+    write_csv(points.iter().map(CSVCylPoint::from), format!("{dir}/data.csv"))?;
+    write_csv(std::iter::once(&origin).map(CSVCylPoint::from), format!("{dir}/origin.csv"))?;
+
+    let f = Sum::new(points.into_iter().map(DistToSquaredDiv2));
+    let g = Scaled::new(4.0, DistTo(origin));
+    let τ = 0.1;
+    
+    write_cylinder_faces_csv(dir, &cyl, 32, 32, 32, |x| f.apply(x) + g.apply(x))?;
+
+    /// Helper structure for saving the log into a CSV file
+    #[derive(Serialize,Deserialize,Debug)]
+    struct CSVLog<Face> {
+        iter : usize,
+        value : f64,
+        // serde is junk
+        //#[serde(flatten)]
+        //point : CSVCylPoint
+        face : Face,
+        angle : f64,
+        r : f64,
+        z : f64,
+    }
+
+    let logmap = |iter, IterInfo { value, point } : IterInfo<OnCylinder<'_>>| {
+        let CSVCylPoint {r, angle, z, face} = CSVCylPoint::from(&point);
+        CSVLog {
+            iter, value, //point : CSVPoint::from(&point)
+            r, angle, z, face
+         }
+    };
+
+    run_and_save(dir, "x1", &f, &g, cyl.on_top(0.0, 0.0), τ, logmap)?;
+    run_and_save(dir, "x2", &f, &g, cyl.on_side(-π*2.0/5.0, 0.0), τ, logmap)?;
+    run_and_save(dir, "x3", &f, &g,  cyl.on_bottom(-π*0.5, 0.8), τ, logmap)
+
+}
+
+
+/// Runs [forward_backward] and saves the results.
+pub fn run_and_save<F, G, M, I>(
+    dir : &str,
+    name : &str,
+    f : &F,
+    g : &G,
+    x : M,
+    τ : f64,
+    logmap : impl Fn(usize, IterInfo<M>) -> I
+) -> DynError
+where M : ManifoldPoint
+          + EmbeddedManifoldPoint<EmbeddedCoords = Loc<f64, 3>>
+          + FacedManifoldPoint,
+      F : Desc<M> +  Mapping<M, Codomain = f64>,
+      G : Prox<M> +  Mapping<M, Codomain = f64>,
+      I : Serialize {
+    
+    let mut logger = Logger::new();
     let iter = AlgIteratorOptions{
         max_iter : 20,
         verbose_iter : Verbose::Every(1),
@@ -138,14 +230,20 @@
     let x̂ = forward_backward(f, g, x, τ, iter);
     println!("result = {}\n{:?}", x̂.embedded_coords(), &x̂);
 
-    logger.write_csv(format!("{PREFIX}/{name}_log.csv"))?;
+    logger.write_csv(format!("{dir}/{name}_log.csv"))?;
 
     Ok(())
 }
 
 /// Writes the values of `f` on `face` of a [`OnCube`] into a PNG file
 /// with resolution `n × n`.
-fn write_face_imgs(n : usize, mut f : impl FnMut(&OnCube) -> f64) -> DynError {
+fn write_cube_face_imgs(
+    dir : &str,
+    n : usize,
+    mut f : impl FnMut(&OnCube) -> f64
+) -> DynError {
+    use crate::cube::Face;
+
     let grid = LinSpace {
         start : Loc([0.0, 0.0]),
         end : Loc([1.0, 1.0]),
@@ -175,7 +273,7 @@
                 *p = Rgb(rgb.map(|v| (v*(u8::RANGE_MAX as f64)) as u8))
             });
 
-        img.save_with_format(format!("{PREFIX}/{face}.png"), ImageFormat::Png)?;
+        img.save_with_format(format!("{dir}/{face}.png"), ImageFormat::Png)?;
     }
 
     Ok(())
@@ -183,7 +281,12 @@
 
 /// Writes the values of `f` on `face` of a [`OnCube`] into a CSV file
 /// with resolution `n × n`.
-fn write_face_csv(filename : String, face : Face, n : usize, mut f : impl FnMut(&OnCube) -> f64) -> DynError {
+fn write_cube_face_csv(
+    filename : String,
+    face : crate::cube::Face,
+    n : usize,
+    mut f : impl FnMut(&OnCube) -> f64
+) -> DynError {
 
     #[derive(Serialize)]
     struct CSVFace { u : f64, v : f64, value : f64 }
@@ -202,10 +305,70 @@
     Ok(())
 }
 
-/// Writes a list of points on a [`OnCube`] into a CSV file
-fn write_points<'a, I : Iterator<Item=&'a OnCube>>(filename : String, list : I) -> DynError {
+/// Writes the values of `f` on the faces of a `cylinder`.
+fn write_cylinder_faces_csv<'a>(
+    dir : &str,
+    cyl : &'a Cylinder,
+    n_height : usize,
+    n_radius : usize,
+    n_angle : usize,
+    mut f : impl FnMut(&OnCylinder<'a>) -> f64
+) -> DynError {
+    let π = f64::PI;
+
+    // Side front is [a, b] for the TikZ configuration.
+    let a = -π*5.0/6.0;
+    let b = π/6.0;
+
+    #[derive(Serialize)]
+    struct CSVFace { angle : f64, /*cos_angle : f64, sin_angle : f64,*/ v : f64, value : f64 }
+
+    let mkf = |angle, v, value| CSVFace {
+        angle,
+        //cos_angle : angle.cos(),
+        //sin_angle : angle.sin(),
+        v,
+        value
+    };
+
+    let side_half_grid = LinSpace {
+        start : Loc([0.0, cyl.bottom_z()]),
+        end : Loc([π, cyl.top_z()]),
+        count : [n_angle / 2, n_height]
+    };
 
-    write_csv(list.map(CSVPoint::from), format!("{filename}.csv"))?;
-    Ok(())
+    let side_grid = LinSpace {
+        start : Loc([0.0, cyl.bottom_z()]),
+        end : Loc([2.0*π, cyl.top_z()]),
+        count : [n_angle, n_height]
+    };
+    let cap_grid = LinSpace {
+        start : Loc([0.0, 0.0]),
+        end : Loc([2.0*π, cyl.radius]),
+        count : [n_angle, n_radius]
+    };
+
+    let side_front = side_grid.into_iter()
+        .map(|Loc([angle, v])| mkf(angle, v, f(&cyl.on_side(angle, v))));
+    write_csv(side_front, format!("{dir}/side.csv"))?;
+
+    let side_front = side_half_grid.into_iter()
+        .map(|Loc([angle, v])| (normalise_angle(angle + a), v))
+        .map(|(angle, v)| mkf(angle, v, f(&cyl.on_side(angle, v))));
+    write_csv(side_front, format!("{dir}/side_front.csv"))?;
+
+    let side_back = side_half_grid.into_iter()
+        .map(|Loc([angle, v])| (normalise_angle(angle + b), v))
+        .map(|(angle, v)| mkf(angle + π, v, f(&cyl.on_side(angle + π, v))));
+    write_csv(side_back, format!("{dir}/side_back.csv"))?;
+
+    let top = cap_grid.into_iter()
+        .map(|Loc([angle, v])| mkf(angle, v, f(&cyl.on_top(angle, v))));
+    write_csv(top, format!("{dir}/top.csv"))?;
+
+    let bottom = cap_grid.into_iter()
+        .map(|Loc([angle, v])| mkf(angle, v, f(&cyl.on_bottom(angle, v))));
+    write_csv(bottom, format!("{dir}/bottom.csv"))
+    
 }
 

mercurial