382 lines
15 KiB
Rust
382 lines
15 KiB
Rust
use std::f64;
|
|
use std::fs::File;
|
|
use std::io::prelude::*;
|
|
use std::char::from_u32;
|
|
|
|
use uuid::Uuid;
|
|
use rand::prelude::*;
|
|
use rand::distributions::{Normal, WeightedIndex};
|
|
|
|
use failure::Error;
|
|
// use failure::err_msg;
|
|
|
|
pub fn invader_write(id: Uuid) -> Result<Uuid, Error> {
|
|
let mut rng = thread_rng();
|
|
let mut svg = Vec::new();
|
|
|
|
// rounding ?
|
|
// filters ?
|
|
|
|
// distribution for lightness
|
|
// bellcurve around 75%
|
|
let l_dist = Normal::new(75.0, 10.0);
|
|
let s_dist = Normal::new(25.0, 10.0);
|
|
|
|
let mut colours = std::iter
|
|
::repeat_with(|| {
|
|
let h = rng.gen_range(0, 360);
|
|
let s = s_dist.sample(&mut rng) as usize;
|
|
let l = l_dist.sample(&mut rng) as usize;
|
|
format!("hsl({:}, {:}%, {:}%)", h, s, l)
|
|
})
|
|
.take(3)
|
|
.collect::<Vec<String>>();
|
|
|
|
colours.push("none".to_string());
|
|
|
|
// add up to 100 for %
|
|
let weights = [
|
|
5,
|
|
5,
|
|
65,
|
|
25, // the transparent colour
|
|
];
|
|
let colour_dist = WeightedIndex::new(&weights)?;
|
|
|
|
write!(&mut svg, "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' viewBox='-500 -500 1500 1500' width='375' height='375'><g>")?;
|
|
for i in 0..50 {
|
|
let x = (i % 5) * 50;
|
|
let y = (i / 5) * 50;
|
|
|
|
let colour = &colours[colour_dist.sample(&mut rng)];
|
|
|
|
write!(&mut svg, "<rect fill=\"{}\" x=\"{}\" y=\"{}\" width=\"50\" height=\"50\" />", colour, x, y)?;
|
|
write!(&mut svg, "<rect fill=\"{}\" x=\"{}\" y=\"{}\" width=\"50\" height=\"50\" />", colour, 450 - x, y)?;
|
|
}
|
|
|
|
write!(&mut svg, "</g></svg>")?;
|
|
|
|
let dest = format!("/var/lib/mnml/public/imgs/{}.svg", id);
|
|
// info!("molecule dest={:?}", dest);
|
|
|
|
let mut file = File::create(dest)?;
|
|
file.write_all(&svg)?;
|
|
|
|
Ok(id)
|
|
}
|
|
|
|
enum ConstructShapes {
|
|
Square,
|
|
Triangle,
|
|
Circle,
|
|
Line,
|
|
V,
|
|
Tri,
|
|
// Plus,
|
|
Blank,
|
|
}
|
|
|
|
// default ? shape
|
|
pub fn shapes_write(id: Uuid) -> Result<Uuid, Error> {
|
|
let mut rng = thread_rng();
|
|
let mut svg = Vec::new();
|
|
|
|
// distribution for lightness
|
|
// bellcurve around 75%
|
|
let l_dist = Normal::new(50.0, 10.0);
|
|
let s_dist = Normal::new(50.0, 20.0);
|
|
|
|
// 8 6 or 4 points in shape
|
|
// 1 head point
|
|
// n / 2 shapes with a colour each
|
|
// random size/radius props for each
|
|
|
|
// add up to 100 for %
|
|
let shapes = [
|
|
(ConstructShapes::Square, 10),
|
|
(ConstructShapes::Triangle, 10),
|
|
(ConstructShapes::Circle, 10),
|
|
(ConstructShapes::Line, 10),
|
|
(ConstructShapes::V, 10),
|
|
(ConstructShapes::Tri, 10),
|
|
// (ConstructShapes::Plus, 5),
|
|
(ConstructShapes::Blank, 1),
|
|
];
|
|
let shape_dist = WeightedIndex::new(shapes.iter().map(|v| v.1))?;
|
|
|
|
let n_shapes_items = [
|
|
(1, 1),
|
|
(2, 2),
|
|
(3, 5),
|
|
(4, 10),
|
|
(5, 10),
|
|
];
|
|
let num_dist = WeightedIndex::new(n_shapes_items.iter().map(|v| v.1))?;
|
|
let n_shapes = num_dist.sample(&mut rng) as usize;
|
|
|
|
write!(&mut svg, "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' viewBox='-250 -250 500 500' width='1000' height='1000'><g>")?;
|
|
for i in 0..n_shapes + 1 {
|
|
let h = rng.gen_range(0, 360);
|
|
let s = s_dist.sample(&mut rng) as usize;
|
|
let l = l_dist.sample(&mut rng) as usize;
|
|
let colour = format!("hsl({:}, {:}%, {:}%)", h, s, l);
|
|
|
|
let scalar = match i == n_shapes {
|
|
true => 0.0,
|
|
false => rng.gen_range(50.0, 200.0),
|
|
};
|
|
|
|
let rotation = rng.gen_range(0, 180);
|
|
let angle: f64 = i as f64 * (1.0 / n_shapes as f64) * f64::consts::PI;
|
|
let x_translate = angle.cos() * scalar;
|
|
let y_translate = angle.sin() * scalar;
|
|
|
|
match shapes[shape_dist.sample(&mut rng)].0 {
|
|
ConstructShapes::Square => {
|
|
let size = rng.gen_range(20.0, 50.0);
|
|
write!(&mut svg, "<rect fill=\"{fill}\" x=\"-{x}\" y=\"-{y}\" width=\"{width}\" height=\"{height}\" transform=\"translate({x_t}, {y_t}) rotate({rotation})\" />",
|
|
fill = colour, x = size / 2.0, y = size / 2.0, width = size, height = size, x_t = x_translate, y_t = y_translate, rotation = rotation)?;
|
|
if scalar == 0.0 && rng.gen_bool(0.5) {
|
|
continue;
|
|
}
|
|
write!(&mut svg, "<rect fill=\"{fill}\" x=\"-{x}\" y=\"-{y}\" width=\"{width}\" height=\"{height}\" transform=\"translate({x_t}, {y_t}) rotate({rotation})\" />",
|
|
fill = colour, x = size / 2.0, y = size / 2.0, width = size, height = size, x_t = -x_translate, y_t = -y_translate, rotation = rotation)?;
|
|
},
|
|
ConstructShapes::Triangle => {
|
|
let h = rng.gen_range(20.0, 50.0);
|
|
let b = rng.gen_range(20.0, 50.0);
|
|
write!(&mut svg, "<polygon fill=\"{fill}\" x=\"{x}\" y=\"{y}\" points=\"{x0} {y0}, {x1} {y1}, {x2} {y2}\" transform=\"translate({x_translate}, {y_translate}) rotate({rotation})\" />",
|
|
fill = colour, x = -b / 2.0, y = h / 2.0, x0 = -b / 2.0, y0 = -h / 2.0, x1 = 0, y1 = b / 2.0, x2 = b / 2.0, y2 = -h / 2.0, rotation = rotation, x_translate = x_translate, y_translate = y_translate)?;
|
|
if scalar == 0.0 && rng.gen_bool(0.5) {
|
|
continue;
|
|
}
|
|
write!(&mut svg, "<polygon fill=\"{fill}\" x=\"{x}\" y=\"{y}\" points=\"{x0} {y0}, {x1} {y1}, {x2} {y2}\" transform=\"translate({x_translate}, {y_translate}) rotate({rotation})\" />",
|
|
fill = colour, x = -b / 2.0, y = h / 2.0, x0 = -b / 2.0, y0 = -h / 2.0, x1 = 0, y1 = b / 2.0, x2 = b / 2.0, y2 = -h / 2.0, rotation = rotation + 180, x_translate = -x_translate, y_translate = -y_translate)?;
|
|
},
|
|
ConstructShapes::Circle => {
|
|
let r = rng.gen_range(10.0, 20.0);
|
|
write!(&mut svg, "<ellipse fill=\"{fill}\" cx=\"{x}\" cy=\"{y}\" rx=\"{r}\" ry=\"{r}\"/>",
|
|
fill = colour, r = r, x = x_translate, y = y_translate)?;
|
|
write!(&mut svg, "<ellipse fill=\"{fill}\" cx=\"{x}\" cy=\"{y}\" rx=\"{r}\" ry=\"{r}\"/>",
|
|
fill = colour, r = r, x = -x_translate, y = -y_translate)?;
|
|
},
|
|
ConstructShapes::Line => {
|
|
let width = rng.gen_range(2.0, 8.0);
|
|
let height = rng.gen_range(20.0, 50.0);
|
|
write!(&mut svg, "<rect fill=\"{fill}\" x=\"-{x}\" y=\"-{y}\" width=\"{width}\" height=\"{height}\" transform=\"translate({x_t}, {y_t}) rotate({rotation})\" />",
|
|
fill = colour, x = width / 2.0, y = height / 2.0, width = width, height = height, x_t = x_translate, y_t = y_translate, rotation = rotation)?;
|
|
if scalar == 0.0 && rng.gen_bool(0.5) {
|
|
continue;
|
|
}
|
|
write!(&mut svg, "<rect fill=\"{fill}\" x=\"-{x}\" y=\"-{y}\" width=\"{width}\" height=\"{height}\" transform=\"translate({x_t}, {y_t}) rotate({rotation})\" />",
|
|
fill = colour, x = width / 2.0, y = height / 2.0, width = width, height = height, x_t = -x_translate, y_t = -y_translate, rotation = rotation)?;
|
|
},
|
|
ConstructShapes::V => {
|
|
let h = rng.gen_range(20.0, 50.0);
|
|
let b = rng.gen_range(20.0, 50.0);
|
|
let width = rng.gen_range(2.0, 8.0);
|
|
|
|
write!(&mut svg, "<polyline fill=\"none\" stroke=\"{fill}\" stroke-width=\"{width}\" x=\"{x}\" y=\"{y}\" points=\"{x0} {y0}, {x1} {y1}, {x2} {y2}\" transform=\"translate({x_translate}, {y_translate}) rotate({rotation})\" />",
|
|
fill = colour, width = width, x = -b / 2.0, y = h / 2.0, x0 = -b / 2.0, y0 = -h / 2.0, x1 = 0, y1 = b / 2.0, x2 = b / 2.0, y2 = -h / 2.0, rotation = rotation, x_translate = x_translate, y_translate = y_translate)?;
|
|
if scalar == 0.0 && rng.gen_bool(0.5) {
|
|
continue;
|
|
}
|
|
write!(&mut svg, "<polyline fill=\"none\" stroke=\"{fill}\" stroke-width=\"{width}\" x=\"{x}\" y=\"{y}\" points=\"{x0} {y0}, {x1} {y1}, {x2} {y2}\" transform=\"translate({x_translate}, {y_translate}) rotate({rotation})\" />",
|
|
fill = colour, width = width, x = -b / 2.0, y = h / 2.0, x0 = -b / 2.0, y0 = -h / 2.0, x1 = 0, y1 = b / 2.0, x2 = b / 2.0, y2 = -h / 2.0, rotation = rotation + 180, x_translate = -x_translate, y_translate = -y_translate)?;
|
|
},
|
|
ConstructShapes::Tri => {
|
|
let width = rng.gen_range(2.0, 4.0);
|
|
let length = rng.gen_range(12.5, 25.0);
|
|
|
|
let x0 = (0.0 as f64).cos() * length;
|
|
let y0 = (0.0 as f64).sin() * length;
|
|
let x1 = ((f64::consts::PI * 2.0) / 3.0).cos() * length;
|
|
let y1 = ((f64::consts::PI * 2.0) / 3.0).sin() * length;
|
|
let x2 = ((f64::consts::PI * 4.0) / 3.0).cos() * length;
|
|
let y2 = ((f64::consts::PI * 4.0) / 3.0).sin() * length;
|
|
|
|
write!(&mut svg, "<path stroke=\"{fill}\" stroke-width=\"{width}\" d=\"M{x0} {y0}L 0 0 M{x1} {y1}L 0 0 M{x2} {y2}L 0 0 \" transform=\"translate({x_translate}, {y_translate}) rotate({rotation})\" />",
|
|
fill = colour, width = width, x0 = x0, y0 = y0, x1 = x1, y1 = y1, x2 = x2, y2 = y2, rotation = rotation, x_translate = x_translate, y_translate = y_translate)?;
|
|
if scalar == 0.0 && rng.gen_bool(0.5) {
|
|
continue;
|
|
}
|
|
write!(&mut svg, "<path stroke=\"{fill}\" stroke-width=\"{width}\" d=\"M{x0} {y0}L 0 0 M{x1} {y1}L 0 0 M{x2} {y2}L 0 0 \" transform=\"translate({x_translate}, {y_translate}) rotate({rotation})\" />",
|
|
fill = colour, width = width, x0 = x0, y0 = y0, x1 = x1, y1 = y1, x2 = x2, y2 = y2, rotation = rotation, x_translate = -x_translate, y_translate = -y_translate)?;
|
|
},
|
|
// ConstructShapes::Plus => { },
|
|
ConstructShapes::Blank => (),
|
|
}
|
|
}
|
|
|
|
write!(&mut svg, "</g></svg>")?;
|
|
|
|
let dest = format!("/var/lib/mnml/public/imgs/{}.svg", id);
|
|
// println!("/var/lib/mnml/public/imgs/{}.svg", id);
|
|
|
|
let mut file = File::create(dest)?;
|
|
file.write_all(&svg)?;
|
|
|
|
Ok(id)
|
|
}
|
|
|
|
|
|
fn _hieroglyph() -> String {
|
|
let mut rng = thread_rng();
|
|
|
|
let mut s = String::new();
|
|
for i in 0..4 {
|
|
s.push(from_u32(rng.gen_range(0x13000, 0x1342E)).unwrap());
|
|
if i == 1 {
|
|
// newline
|
|
s.push(from_u32(0x000a).unwrap());
|
|
}
|
|
}
|
|
|
|
// println!("{:}", s);
|
|
return s;
|
|
}
|
|
|
|
|
|
pub fn smile(id: Uuid) -> Result<Uuid, Error> {
|
|
let mut rng = thread_rng();
|
|
let mut svg = Vec::new();
|
|
|
|
// distribution for lightness
|
|
// bellcurve around 75%
|
|
let l_dist = Normal::new(50.0, 10.0);
|
|
let s_dist = Normal::new(50.0, 20.0);
|
|
|
|
// let stroke_width = rng.gen_range(1, 3);
|
|
let stroke_width = 3;
|
|
|
|
let h = rng.gen_range(0, 360);
|
|
let s = s_dist.sample(&mut rng) as usize;
|
|
let l = l_dist.sample(&mut rng) as usize;
|
|
let eye_colour = format!("hsl({:}, {:}%, {:}%)", h, s, l);
|
|
|
|
let h = rng.gen_range(0, 360);
|
|
let s = s_dist.sample(&mut rng) as usize;
|
|
let l = l_dist.sample(&mut rng) as usize;
|
|
let mouth_colour = format!("hsl({:}, {:}%, {:}%)", h, s, l);
|
|
|
|
// basic layout is 200x200 box w/ 100 padding
|
|
// 2:1 for each x,y
|
|
|
|
// left eye is at 0,0
|
|
// 50W 25H
|
|
let eyes_left = [
|
|
("M0,0 L25,25 L50,0", 1), // v
|
|
("M0,25 L25,0 L50,25", 1), // ^
|
|
("M0,0 L50,0", 1), // -
|
|
("M0,25 L50,25", 1), // _
|
|
("M0,0 L50,25 M50,0 L0,25", 1), // x
|
|
("M0,0 L50,12.5 L0,25", 1), // >
|
|
("M50,0 L0,12.5 L50,25", 1), // <
|
|
("M0,0 L0,25 L50,25", 1), // L
|
|
("M50,0 L50,25 L0,25", 1), // J
|
|
("M0,0 L50,0 M50,6.25 L50,12.5 M50,18.75 L50,25", 1), // ;
|
|
("M12.5,0 L37.5,0 L37.5,25 L12.5,25 L12.5,0", 1), // o
|
|
];
|
|
let eye_left_dist = WeightedIndex::new(eyes_left.iter().map(|v| v.1))?;
|
|
|
|
// right eye is 150,0
|
|
// 50W 25H
|
|
let eyes_right = [
|
|
("M150,0 L175,25 L200,0", 1), // v
|
|
("M150,25 L175,0 L200,25", 1), // ^
|
|
("M150,0 L200,0", 1), // -
|
|
("M150,25 L200,25", 1), // _
|
|
("M150,0 L200,25 M200,0 L150,25", 1), // x
|
|
("M150,0 L200,12.5 L150,25", 1), // >
|
|
("M200,0 L150,12.5 L200,25", 1), // <
|
|
("M150,0 L150,25 L200,25", 1), // L
|
|
("M200,0 L200,25 L150,25", 1), // J
|
|
("M150,0 L200,0 M150,6.25 L150,12.5 M150,18.75 L150,25", 1), // ;
|
|
("M162.5,0 L187.5,0 L187.5,25 L162.5,25 L162.5,0", 1), // o
|
|
];
|
|
let eye_right_dist= WeightedIndex::new(eyes_right.iter().map(|v| v.1))?;
|
|
|
|
// mouth is 50,75
|
|
// 100W 25H
|
|
let mouths = [
|
|
("M50,100 L150,100", 1), // _
|
|
("M50,75 L150,75 L125,100 L75,100 L50,75", 1), // D
|
|
("M50,75 L75,100 L100,75 L125,100 L150,75", 1), // w
|
|
("M50,75 L75,75 L75,87.5 M75,75 L125,75 L125,87.5 M125,75 L150,75", 1), // vamp
|
|
("M50,75 L150,75 M50,75 L50,87.5 M75,75 L75,87.5 M100,75 L100,87.5 M125,75 L125,87.5 M150,75 L150,87.5", 1), // mm
|
|
("M75,75 L125,75 L125,100 L75,100 L75,75", 1), // o
|
|
("M50,75 L150,100 M150,75 L50,100", 1), // x
|
|
("M50,75 L150,75 L150,100 L125,100 L125,75", 1), // p
|
|
("M50,75 L150,75 M50,75 L50,100 L75,100 L75,75", 1), // d
|
|
// ("M50,75 L50,100 L150,100 L150,75", 1), // u
|
|
("M50,100 L50,75 L150,75 L150,100", 1), // n
|
|
("M50,75 L50,100 L150,75 L150,100", 1), // Z
|
|
("M50,87.5 L100,75 L150,87.5 L100,100 L50,87.5", 1), // Z
|
|
];
|
|
let mouth_dist = WeightedIndex::new(mouths.iter().map(|v| v.1))?;
|
|
|
|
write!(&mut svg, "<svg xmlns='http://www.w3.org/2000/svg' version='1.1' viewBox='-50 -100 300 300' width='200' height='200'><g>")?;
|
|
|
|
let left_eye_path = eyes_left[eye_left_dist.sample(&mut rng)].0;
|
|
|
|
// left eye
|
|
write!(&mut svg,
|
|
"<path fill=\"none\" stroke=\"{:}\" stroke-width=\"{:}px\" d=\"{:}\" />",
|
|
eye_colour, stroke_width, left_eye_path)?;
|
|
|
|
let right_eye_path = eyes_right[eye_right_dist.sample(&mut rng)].0;
|
|
// right eye
|
|
write!(&mut svg,
|
|
"<path fill=\"none\" stroke=\"{:}\" stroke-width=\"{:}px\" d=\"{:}\" />",
|
|
eye_colour, stroke_width, right_eye_path)?;
|
|
|
|
let mouth_path = mouths[mouth_dist.sample(&mut rng)].0;
|
|
// mouth
|
|
write!(&mut svg,
|
|
"<path fill=\"none\" stroke=\"{:}\" stroke-width=\"{:}px\" d=\"{:}\" />",
|
|
mouth_colour, stroke_width, mouth_path)?;
|
|
|
|
write!(&mut svg, "</g></svg>")?;
|
|
|
|
// let dest = format!("/var/lib/mnml/face.svg");
|
|
let dest = format!("/var/lib/mnml/public/imgs/{}.svg", id);
|
|
|
|
let mut file = File::create(dest)?;
|
|
file.write_all(&svg)?;
|
|
|
|
Ok(id)
|
|
}
|
|
|
|
|
|
pub fn exists(id: Uuid) -> bool {
|
|
std::path::Path::new(&format!("/var/lib/mnml/public/imgs/{}.svg", id)).exists()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
// #[test]
|
|
// fn invader_img_test() {
|
|
// for i in 0..100 {
|
|
// invader_img_write(Uuid::new_v4()).unwrap();
|
|
// }
|
|
// }
|
|
|
|
// #[test]
|
|
// fn hieroglyph_test() {
|
|
// hieroglyph();
|
|
// }
|
|
|
|
// #[test]
|
|
// fn shapes_img_test() {
|
|
// for i in 0..100 {
|
|
// shapes_write(Uuid::new_v4()).unwrap();
|
|
// }
|
|
// }
|
|
|
|
#[test]
|
|
fn smile_test() {
|
|
smile(Uuid::new_v4()).unwrap();
|
|
}
|
|
}
|