230 lines
7.6 KiB
Rust
230 lines
7.6 KiB
Rust
mod canvas;
|
|
mod ops;
|
|
mod pixel;
|
|
mod pixfn;
|
|
pub mod polar_math;
|
|
|
|
pub use canvas::*;
|
|
pub use pixel::*;
|
|
pub use pixfn::*;
|
|
|
|
use crate::ops::{
|
|
ImgSynArea, ImgSynAxial, ImgSynMap, ImgSynMapArea, ImgSynPolar, ImgSynRect, ImgSynStretched,
|
|
ImgSynUnmapRect,
|
|
};
|
|
|
|
pub trait ImgSynPixFn<T>: Clone {
|
|
fn run(&self, meta: Canvas, x: f64, y: f64, pixel: Pixel) -> T;
|
|
}
|
|
|
|
pub trait ImgSyn: Clone {
|
|
fn getpx(&self, meta: Canvas, x: f64, y: f64) -> Pixel;
|
|
fn map<F: ImgSynPixFn<Pixel>>(self, f: F) -> ImgSynMap<Self, F> {
|
|
ImgSynMap(self, f)
|
|
}
|
|
fn area<C: ImgSynPixFn<bool>, ImgB: ImgSyn, F: FnOnce(Self) -> ImgB>(
|
|
self,
|
|
condition: C,
|
|
f: F,
|
|
) -> ImgSynArea<Self, ImgB, C> {
|
|
ImgSynArea(self.clone(), f(self), condition)
|
|
}
|
|
fn mapped_rect<ImgB: ImgSyn, F: FnOnce(ImgSynUnmapRect<Self>) -> ImgB>(
|
|
self,
|
|
begin_x: f64,
|
|
begin_y: f64,
|
|
size_x: f64,
|
|
size_y: f64,
|
|
f: F,
|
|
) -> ImgSynRect<Self, ImgB> {
|
|
let rect = (begin_x, begin_y, size_x, size_y);
|
|
ImgSynRect(self.clone(), f(ImgSynUnmapRect(self, rect)), rect)
|
|
}
|
|
fn add_image<ImgB: ImgSyn>(
|
|
self,
|
|
begin_x: f64,
|
|
begin_y: f64,
|
|
size_x: f64,
|
|
size_y: f64,
|
|
img: ImgB,
|
|
) -> ImgSynRect<Self, ImgB> {
|
|
let rect = (begin_x, begin_y, size_x, size_y);
|
|
ImgSynRect(self, img, rect)
|
|
}
|
|
fn map_area<C: ImgSynPixFn<bool>, F: ImgSynPixFn<Pixel>>(
|
|
self,
|
|
condition: C,
|
|
f: F,
|
|
) -> ImgSynMapArea<Self, C, F> {
|
|
ImgSynMapArea(self, condition, f)
|
|
}
|
|
/// Makes future operations work on polar coordinates when terminated by .axial()
|
|
///
|
|
/// **MUST** be used before a subsequent .axial()
|
|
fn polar(self) -> ImgSynAxial<Self> {
|
|
ImgSynAxial(self)
|
|
}
|
|
/// Makes future operations work on axial coordinates when .polar() was used previously
|
|
///
|
|
/// **MUST** be used after .polar()
|
|
fn axial(self) -> ImgSynPolar<Self> {
|
|
ImgSynPolar(self)
|
|
}
|
|
fn stretched_by(self, x: f64, y: f64) -> ImgSynStretched<Self> {
|
|
ImgSynStretched(self, (x, y))
|
|
}
|
|
fn paint(self, meta: Canvas) -> PaintedCanvas {
|
|
PaintedCanvas::new(meta, self)
|
|
}
|
|
fn rgba255(self, meta: Canvas) -> Vec<u8> {
|
|
let painted = self.paint(meta);
|
|
PaintedCanvas::rgba255(&painted)
|
|
}
|
|
#[cfg(feature = "png")]
|
|
fn png(self, meta: Canvas) -> Vec<u8> {
|
|
let painted = self.paint(meta);
|
|
let mut data = Vec::new();
|
|
{
|
|
let mut encoder = png::Encoder::new(&mut data, meta.width as u32, meta.height as u32);
|
|
encoder.set_color(png::ColorType::Rgba);
|
|
encoder.set_depth(png::BitDepth::Eight);
|
|
let mut writer = encoder.write_header().unwrap();
|
|
writer.write_image_data(&painted.rgba255(meta)).unwrap();
|
|
writer.finish().unwrap();
|
|
}
|
|
data
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use std::{fs, hint::black_box};
|
|
|
|
use crate::{Canvas, ImgSyn, PixFnFloat, Pixel, polar_math::distance_from_circle};
|
|
|
|
#[test]
|
|
fn test() {
|
|
let canvas = Canvas::new(1000., 1000., Pixel::new_black());
|
|
fs::write(
|
|
"test.png",
|
|
canvas
|
|
.polar()
|
|
.map(|_, y: f64, pix: Pixel| pix.with_r((y - 0.5).abs() * 2.))
|
|
.map(|_, y, pix: Pixel| pix.with_b(y))
|
|
.axial()
|
|
.map(PixFnFloat(|_, _, y, pix| pix.with_g(y)))
|
|
.map_area(
|
|
PixFnFloat(|_, x, y, _| x < 0.2 && y < 0.2),
|
|
PixFnFloat(|_, _, _, pix| pix.with_g(1.0)),
|
|
)
|
|
.area(PixFnFloat(|_, _, _, px| px.g > 0.8 && px.r < 0.3), |img| {
|
|
img.polar()
|
|
.map(PixFnFloat(|_, x, _, pix| pix.with_r(x).with_b(0.0)))
|
|
.axial()
|
|
})
|
|
.mapped_rect(0.3, 0.3, 0.4, 0.4, |img| {
|
|
img.map(PixFnFloat(|_, x, y, px| {
|
|
let thickness = 0.01;
|
|
if !(thickness..=1. - thickness).contains(&x)
|
|
|| !(thickness..=1. - thickness).contains(&y)
|
|
{
|
|
return Pixel::new_white();
|
|
}
|
|
px
|
|
}))
|
|
})
|
|
.paint(canvas)
|
|
.png(canvas),
|
|
)
|
|
.unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn graph() {
|
|
let canvas = Canvas::new(1000., 1000., Pixel::new_white());
|
|
fs::write(
|
|
"graph.png",
|
|
canvas
|
|
.add_image(
|
|
0.07,
|
|
0.03,
|
|
0.9,
|
|
0.9,
|
|
Canvas::new(900., 900., Pixel::new_black()).map_area(
|
|
|x, y, _| distance_from_circle(x, y, 1.0, 0.0, 0.5, 1.0, 1.0) > 0.0,
|
|
|x, y, px: Pixel| px.with_r(x).with_g(1. - y),
|
|
),
|
|
)
|
|
.map_area(
|
|
|x, y, _| {
|
|
let xc = 0.07;
|
|
let yc = 0.93;
|
|
let t = 0.003;
|
|
(xc - t..=xc + t).contains(&x) && y >= 0.03
|
|
|| (yc - t..=yc + t).contains(&y) && x <= 0.97
|
|
},
|
|
|x, y, px: Pixel| {
|
|
if (1. + x - y) % 0.03 <= 0.02 {
|
|
px.with_rgb(1.0, 0.0, 0.5)
|
|
} else {
|
|
px.with_rgb(1.0, 1.0, 0.0)
|
|
}
|
|
},
|
|
)
|
|
.mapped_rect(0.02, 0.93, 0.05, 0.05, |img| {
|
|
img.map_area(
|
|
|x: f64, y: f64, _| {
|
|
distance_from_circle(x, y, 0.6, 0.5, 0.2, 1.0, 1.4).abs() < 0.03
|
|
},
|
|
|_, _, px: Pixel| px.with_rgb_of(0.0),
|
|
)
|
|
})
|
|
.paint(canvas)
|
|
.png(canvas),
|
|
)
|
|
.unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn speed() {
|
|
let canvas = Canvas::new(10000., 10000., Pixel::new_white());
|
|
black_box(
|
|
canvas
|
|
.add_image(
|
|
0.07,
|
|
0.03,
|
|
0.9,
|
|
0.9,
|
|
Canvas::new(9000., 9000., Pixel::new_black()).map_area(
|
|
|x, y, _| distance_from_circle(x, y, 1.0, 0.0, 0.5, 1.0, 1.0) > 0.0,
|
|
|x, y, px: Pixel| px.with_r(x).with_g(1. - y),
|
|
),
|
|
)
|
|
.map_area(
|
|
|x, y, _| {
|
|
let xc = 0.07;
|
|
let yc = 0.93;
|
|
let t = 0.003;
|
|
(xc - t..=xc + t).contains(&x) && y >= 0.03
|
|
|| (yc - t..=yc + t).contains(&y) && x <= 0.97
|
|
},
|
|
|x, y, px: Pixel| {
|
|
if (1. + x - y) % 0.03 <= 0.02 {
|
|
px.with_rgb(1.0, 0.0, 0.5)
|
|
} else {
|
|
px.with_rgb(1.0, 1.0, 0.0)
|
|
}
|
|
},
|
|
)
|
|
.mapped_rect(0.02, 0.93, 0.05, 0.05, |img| {
|
|
img.map_area(
|
|
|x: f64, y: f64, _| {
|
|
distance_from_circle(x, y, 0.6, 0.5, 0.2, 1.0, 1.4).abs() < 0.03
|
|
},
|
|
|_, _, px: Pixel| px.with_rgb_of(0.0),
|
|
)
|
|
})
|
|
.paint(canvas),
|
|
);
|
|
}
|
|
}
|