mnml/server/src/img.rs
2019-09-19 15:26:06 +10:00

385 lines
15 KiB
Rust

use std::f64;
use std::fs::copy;
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 molecular_write(id: Uuid) -> Result<Uuid, Error> {
let mut rng = thread_rng();
for _i in 0..100 {
let mol: u32 = rng.gen_range(0, 10000);
let src = format!("/var/lib/mnml/data/molecules/{}.svg", mol);
let dest = format!("/var/lib/mnml/public/imgs/{}.svg", id);
debug!("molecule src={:?}", src);
debug!("molecule dest={:?}", dest);
if let Ok(_bytes) = copy(&src, &dest) {
info!("new molecule img generated src={:?} dest={:?}", src, dest);
return Ok(id);
}
}
return Err(err_msg("too many missing molecules. wrong directory?"))
}
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;
}
#[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();
// }
// }
}
// function createColor() {
// //saturation is the whole color spectrum
// var h = Math.floor(rand() * 360);
// //saturation goes from 40 to 100, it avoids greyish colors
// var s = ((rand() * 60) + 40) + '%';
// //lightness can be anything from 0 to 100, but probabilities are a bell curve around 75%
// var l = ((rand()+rand()+rand()+rand()) * 25) + '%';
// var color = 'hsl(' + h + ',' + s + ',' + l + ')';
// return color;
// }
// function createImageData(size) {
// var width = size; // Only support square icons for now
// var height = size;
// var dataWidth = Math.ceil(width / 2);
// var mirrorWidth = width - dataWidth;
// var data = [];
// for(var y = 0; y < height; y++) {
// var row = [];
// for(var x = 0; x < dataWidth; x++) {
// // this makes foreground and background color to have a 43% (1/2.3) probability
// // spot color has 13% chance
// row[x] = Math.floor(rand()*2.3);
// }
// var r = row.slice(0, mirrorWidth);
// r.reverse();
// row = row.concat(r);
// for(var i = 0; i < row.length; i++) {
// data.push(row[i]);
// }
// }
// return data;
// }
// function buildOpts(opts) {
// var newOpts = {};
// newOpts.seed = opts.seed || Math.floor((Math.random()*Math.pow(10,16))).toString(16);
// seedrand(newOpts.seed);
// newOpts.size = opts.size || 8;
// newOpts.scale = opts.scale || 4;
// newOpts.color = opts.color || createColor();
// newOpts.bgcolor = opts.bgcolor || createColor();
// newOpts.spotcolor = opts.spotcolor || createColor();
// return newOpts;
// }
// ],
// - _ => vec![
// - (ItemAction::RerollStamina, 1),
// - (ItemAction::RerollPhysDamage, 1),
// - (ItemAction::RerollSpellDamage, 1),
// - (ItemAction::RerollSpeed, 1),
// - (ItemAction::RerollArmour, 1),
// - (ItemAction::RerollSpellShield, 1),
// - (ItemAction::RerollEvasion, 1),
// - ],
// + // _ => vec![
// + // (ItemAction::RerollStamina, 1),
// + // (ItemAction::RerollPhysDamage, 1),
// + // (ItemAction::RerollSpellDamage, 1),
// + // (ItemAction::RerollSpeed, 1),
// + // (ItemAction::RerollArmour, 1),
// + // (ItemAction::RerollSpellShield, 1),
// + // (ItemAction::RerollEvasion, 1),
// + // ],
// }
// }
// -pub fn item_drop(tx: &mut Transaction, account_id: Uuid, mode: GameMode) -> Result<Item, Error> {
// +pub fn item_drop(tx: &mut Transaction, account_id: Uuid, mode: GameMode) -> Result<(), Error> {
// let mut rng = thread_rng();
// - let actions = mode_drops(mode);
// + let log_normal = LogNormal::new(1.0, 1.0);
// + let num_drops = log_normal.sample(&mut rng).floor() as usize;
// +
// + println!("{:?} drops", num_drops);
// - let dist = WeightedIndex::new(actions.iter().map(|item| item.1)).unwrap();
// - let kind = actions[dist.sample(&mut rng)].0;
// - let item = Item::new(kind, account_id);
// + for _i in 0..num_drops {
// + let actions = mode_drops(mode);
// - println!("{:?} dropped {:?}", account_id, item);
// + let dist = WeightedIndex::new(actions.iter().map(|item| item.1)).unwrap();
// + let kind = actions[dist.sample(&mut rng)].0;
// + let item = Item::new(kind, account_id);
// - return item_create(item, tx, account_id);
// + println!("{:?} dropped {:?}", account_id, item);
// + item_create(item, tx, account_id)?;V