more operations, fixes, improvements

This commit is contained in:
Tove 2026-02-09 17:57:28 +01:00
parent 4285b7a966
commit 3f711f0be5
10 changed files with 125 additions and 96 deletions

View file

@ -1,25 +0,0 @@
use std::f32::consts::*;
use crate::Canvas;
pub fn polar(x: f32, y: f32, meta: Canvas) -> (f32, f32) {
let x = x - 0.5;
let y = y - 0.5;
let r = (x * x + y * y).sqrt();
let mut phi = f32::atan2(y, x) + PI / 2.;
phi = phi.rem_euclid(TAU);
(r * 2. / corner_distance(meta), phi / TAU)
}
pub fn axial(r: f32, phi: f32, meta: Canvas) -> (f32, f32) {
let phi = phi * TAU - PI / 2.;
let r = r / 2. * corner_distance(meta);
let x = r * phi.cos();
let y = r * phi.sin();
(x + 0.5, y + 0.5)
}
fn corner_distance(meta: Canvas) -> f32 {
let corner_distance = (meta.width * meta.width + meta.height * meta.height).sqrt();
corner_distance / meta.width.max(meta.height)
}

View file

@ -2,8 +2,8 @@ use crate::{ImgSyn, pixel::Pixel};
#[derive(Clone, Copy, PartialEq)]
pub struct Canvas {
pub width: f32,
pub height: f32,
pub width: f64,
pub height: f64,
pub default_px: Pixel,
}
@ -13,7 +13,7 @@ pub struct PaintedCanvas {
}
impl Canvas {
pub fn new(width: f32, height: f32, default_px: Pixel) -> Self {
pub fn new(width: f64, height: f64, default_px: Pixel) -> Self {
Self {
width,
height,
@ -23,7 +23,7 @@ impl Canvas {
}
impl ImgSyn for Canvas {
fn getpx(&self, _meta: Canvas, _x: f32, _y: f32) -> Pixel {
fn getpx(&self, _meta: Canvas, _x: f64, _y: f64) -> Pixel {
self.default_px
}
}
@ -32,10 +32,10 @@ impl PaintedCanvas {
pub fn new(meta @ Canvas { width, height, .. }: Canvas, function: impl ImgSyn) -> Self {
Self {
array: (0..height as u32)
.map(|y| y as f32 / height)
.map(|y| y as f64 / height)
.map(|y| {
(0..width as u32)
.map(|x| x as f32 / width)
.map(|x| x as f64 / width)
.map(|x| function.getpx(meta, x, y))
.collect()
})
@ -52,7 +52,7 @@ impl PaintedCanvas {
}
impl ImgSyn for PaintedCanvas {
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> Pixel {
fn getpx(&self, meta: Canvas, x: f64, y: f64) -> Pixel {
// float coords -> int coords
let x = (x * meta.width).round() as usize;
let y = (y * meta.height).round() as usize;

View file

@ -1,8 +1,8 @@
pub mod assist;
mod canvas;
mod ops;
mod pixel;
mod pixfn;
pub mod polar_math;
pub use canvas::*;
pub use pixel::*;
@ -14,11 +14,11 @@ use crate::ops::{
};
pub trait ImgSynPixFn<T>: Clone {
fn run(&self, meta: Canvas, x: f32, y: f32, pixel: Pixel) -> T;
fn run(&self, meta: Canvas, x: f64, y: f64, pixel: Pixel) -> T;
}
pub trait ImgSyn: Clone {
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> Pixel;
fn getpx(&self, meta: Canvas, x: f64, y: f64) -> Pixel;
fn map<F: ImgSynPixFn<Pixel>>(self, f: F) -> ImgSynMap<Self, F> {
ImgSynMap(self, f)
}
@ -31,10 +31,10 @@ pub trait ImgSyn: Clone {
}
fn mapped_rect<ImgB: ImgSyn, F: FnOnce(ImgSynUnmapRect<Self>) -> ImgB>(
self,
begin_x: f32,
begin_y: f32,
size_x: f32,
size_y: f32,
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);
@ -42,10 +42,10 @@ pub trait ImgSyn: Clone {
}
fn add_image<ImgB: ImgSyn>(
self,
begin_x: f32,
begin_y: f32,
size_x: f32,
size_y: f32,
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);
@ -70,7 +70,7 @@ pub trait ImgSyn: Clone {
fn axial(self) -> ImgSynPolar<Self> {
ImgSynPolar(self)
}
fn stretched_by(self, x: f32, y: f32) -> ImgSynStretched<Self> {
fn stretched_by(self, x: f64, y: f64) -> ImgSynStretched<Self> {
ImgSynStretched(self, (x, y))
}
fn paint(self, meta: Canvas) -> PaintedCanvas {
@ -99,7 +99,7 @@ pub trait ImgSyn: Clone {
mod test {
use std::fs;
use crate::{Canvas, ImgSyn, PixFnFloat, Pixel};
use crate::{Canvas, ImgSyn, PixFnFloat, Pixel, polar_math::distance_from_circle};
#[test]
fn test() {
@ -108,7 +108,7 @@ mod test {
"test.png",
canvas
.polar()
.map(|_, y: f32, pix: Pixel| pix.with_r((y - 0.5).abs() * 2.))
.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)))
@ -118,7 +118,7 @@ mod test {
)
.area(PixFnFloat(|_, _, _, px| px.g > 0.8 && px.r < 0.3), |img| {
img.polar()
.map(PixFnFloat(|_, x, y, pix| pix.with_r(x).with_b(1. - y)))
.map(PixFnFloat(|_, x, _, pix| pix.with_r(x).with_b(0.0)))
.axial()
})
.mapped_rect(0.3, 0.3, 0.4, 0.4, |img| {
@ -145,30 +145,38 @@ mod test {
"graph.png",
canvas
.add_image(
0.1,
0.0,
0.07,
0.03,
0.9,
0.9,
Canvas::new(900., 900., Pixel::new_black()).map_area(
|x, y, _| x < 0.7 || y > 0.3,
|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 t = 0.004;
(0.1 - t..0.1 + t).contains(&x) || (0.9 - t..0.9 + t).contains(&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)
}
},
|_, _, px: Pixel| px.with_rgb(1.0, 0.0, 0.0),
)
.mapped_rect(0.05, 0.90, 0.05, 0.05, |img| {
img.polar()
.map_area(
|r: f32, phi, _| (r - 0.3).abs() < 0.04,
|_, _, px: Pixel| px.with_rgb_of(0.0),
)
.axial()
.stretched_by(1.0, 1.5)
.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),

View file

@ -13,7 +13,7 @@ where
C: ImgSynPixFn<bool>,
F: ImgSynPixFn<Pixel>,
{
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> crate::Pixel {
fn getpx(&self, meta: Canvas, x: f64, y: f64) -> crate::Pixel {
let px = self.0.getpx(meta, x, y);
if !self.1.run(meta, x, y, px) {
return px;
@ -35,7 +35,7 @@ where
ImgB: ImgSyn,
C: ImgSynPixFn<bool>,
{
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> crate::Pixel {
fn getpx(&self, meta: Canvas, x: f64, y: f64) -> crate::Pixel {
let px = self.0.getpx(meta, x, y);
if !self.2.run(meta, x, y, px) {
return px;
@ -48,7 +48,7 @@ where
pub struct ImgSynRect<Prev: ImgSyn, ImgB: ImgSyn>(
pub(crate) Prev,
pub(crate) ImgB,
pub(crate) (f32, f32, f32, f32),
pub(crate) (f64, f64, f64, f64),
);
impl<Prev, ImgB> ImgSyn for ImgSynRect<Prev, ImgB>
@ -56,7 +56,7 @@ where
Prev: ImgSyn,
ImgB: ImgSyn,
{
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> crate::Pixel {
fn getpx(&self, meta: Canvas, x: f64, y: f64) -> crate::Pixel {
let px = self.0.getpx(meta, x, y);
let (startx, starty, sizex, sizey) = self.2;
if x < startx || y < starty {
@ -74,13 +74,13 @@ where
}
#[derive(Clone, Copy)]
pub struct ImgSynUnmapRect<Prev: ImgSyn>(pub(crate) Prev, pub(crate) (f32, f32, f32, f32));
pub struct ImgSynUnmapRect<Prev: ImgSyn>(pub(crate) Prev, pub(crate) (f64, f64, f64, f64));
impl<Prev> ImgSyn for ImgSynUnmapRect<Prev>
where
Prev: ImgSyn,
{
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> crate::Pixel {
fn getpx(&self, meta: Canvas, x: f64, y: f64) -> crate::Pixel {
let (startx, starty, sizex, sizey) = self.1;
self.0.getpx(
Canvas::new(meta.width / sizex, meta.height / sizey, meta.default_px),

View file

@ -1,6 +1,6 @@
use crate::{
Canvas, ImgSyn,
assist::{axial, polar},
polar_math::{axial, polar},
};
#[derive(Clone, Copy)]
@ -10,8 +10,8 @@ impl<Prev> ImgSyn for ImgSynAxial<Prev>
where
Prev: ImgSyn,
{
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> crate::Pixel {
let (x, y) = axial(x, y, meta);
fn getpx(&self, meta: Canvas, x: f64, y: f64) -> crate::Pixel {
let (x, y) = axial(x, y);
self.0.getpx(meta, x, y)
}
}
@ -22,8 +22,8 @@ impl<Prev> ImgSyn for ImgSynPolar<Prev>
where
Prev: ImgSyn,
{
fn getpx(&self, meta: Canvas, x: f32, y: f32) -> crate::Pixel {
let (x, y) = polar(x, y, meta);
fn getpx(&self, meta: Canvas, x: f64, y: f64) -> crate::Pixel {
let (x, y) = polar(x, y);
self.0.getpx(meta, x, y)
}
}

View file

@ -4,7 +4,7 @@ use crate::{ImgSyn, ImgSynPixFn, Pixel};
pub struct ImgSynMap<Prev: ImgSyn, F: ImgSynPixFn<Pixel>>(pub(crate) Prev, pub(crate) F);
impl<Prev: ImgSyn, F: ImgSynPixFn<Pixel>> ImgSyn for ImgSynMap<Prev, F> {
fn getpx(&self, meta: crate::Canvas, x: f32, y: f32) -> crate::Pixel {
fn getpx(&self, meta: crate::Canvas, x: f64, y: f64) -> crate::Pixel {
self.1.run(meta, x, y, self.0.getpx(meta, x, y))
}
}

View file

@ -1,10 +1,10 @@
use crate::ImgSyn;
#[derive(Clone, Copy)]
pub struct ImgSynStretched<Prev: ImgSyn>(pub(crate) Prev, pub(crate) (f32, f32));
pub struct ImgSynStretched<Prev: ImgSyn>(pub(crate) Prev, pub(crate) (f64, f64));
impl<Prev: ImgSyn> ImgSyn for ImgSynStretched<Prev> {
fn getpx(&self, meta: crate::Canvas, x: f32, y: f32) -> crate::Pixel {
fn getpx(&self, meta: crate::Canvas, x: f64, y: f64) -> crate::Pixel {
self.0.getpx(
meta,
x / self.1.0 - 0.5 / self.1.0 + 0.5,

View file

@ -1,9 +1,9 @@
#[derive(Clone, Copy, PartialEq)]
pub struct Pixel {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
pub r: f64,
pub g: f64,
pub b: f64,
pub a: f64,
}
impl Pixel {
@ -33,26 +33,26 @@ impl Pixel {
}
}
pub fn of(r: f32, g: f32, b: f32, a: f32) -> Pixel {
pub fn of(r: f64, g: f64, b: f64, a: f64) -> Pixel {
Self { r, g, b, a }
}
pub fn with_r(self, x: f32) -> Pixel {
pub fn with_r(self, x: f64) -> Pixel {
Self { r: x, ..self }
}
pub fn with_g(self, x: f32) -> Pixel {
pub fn with_g(self, x: f64) -> Pixel {
Self { g: x, ..self }
}
pub fn with_b(self, x: f32) -> Pixel {
pub fn with_b(self, x: f64) -> Pixel {
Self { b: x, ..self }
}
pub fn with_a(self, x: f32) -> Pixel {
pub fn with_a(self, x: f64) -> Pixel {
Self { a: x, ..self }
}
pub fn with_rgb(self, r: f32, g: f32, b: f32) -> Pixel {
pub fn with_rgb(self, r: f64, g: f64, b: f64) -> Pixel {
Self { r, g, b, ..self }
}
pub fn with_rgb_of(self, x: f32) -> Pixel {
pub fn with_rgb_of(self, x: f64) -> Pixel {
Self {
r: x,
g: x,
@ -65,19 +65,19 @@ impl Pixel {
f(self)
}
pub fn map_r(mut self, f: impl FnOnce(f32) -> f32) -> Pixel {
pub fn map_r(mut self, f: impl FnOnce(f64) -> f64) -> Pixel {
self.r = f(self.r);
self
}
pub fn map_g(mut self, f: impl FnOnce(f32) -> f32) -> Pixel {
pub fn map_g(mut self, f: impl FnOnce(f64) -> f64) -> Pixel {
self.r = f(self.r);
self
}
pub fn map_b(mut self, f: impl FnOnce(f32) -> f32) -> Pixel {
pub fn map_b(mut self, f: impl FnOnce(f64) -> f64) -> Pixel {
self.r = f(self.r);
self
}
pub fn map_a(mut self, f: impl FnOnce(f32) -> f32) -> Pixel {
pub fn map_a(mut self, f: impl FnOnce(f64) -> f64) -> Pixel {
self.r = f(self.r);
self
}

View file

@ -6,7 +6,7 @@ impl<T: Clone + Copy, F> ImgSynPixFn<T> for PixFnInt<T, F>
where
F: (Fn(Canvas, u32, u32, Pixel) -> T) + Copy,
{
fn run(&self, meta: Canvas, x: f32, y: f32, pixel: Pixel) -> T {
fn run(&self, meta: Canvas, x: f64, y: f64, pixel: Pixel) -> T {
self.0(
meta,
(x * meta.width).round() as u32,
@ -17,21 +17,21 @@ where
}
#[derive(Clone, Copy)]
pub struct PixFnFloat<T: Clone + Copy, F: (Fn(Canvas, f32, f32, Pixel) -> T) + Copy>(pub F);
pub struct PixFnFloat<T: Clone + Copy, F: (Fn(Canvas, f64, f64, Pixel) -> T) + Copy>(pub F);
impl<T: Clone + Copy, F> ImgSynPixFn<T> for PixFnFloat<T, F>
where
F: (Fn(Canvas, f32, f32, Pixel) -> T) + Copy,
F: (Fn(Canvas, f64, f64, Pixel) -> T) + Copy,
{
fn run(&self, meta: Canvas, x: f32, y: f32, pixel: Pixel) -> T {
fn run(&self, meta: Canvas, x: f64, y: f64, pixel: Pixel) -> T {
self.0(meta, x, y, pixel)
}
}
impl<T: Clone + Copy, F> ImgSynPixFn<T> for F
where
F: (Fn(f32, f32, Pixel) -> T) + Copy,
F: (Fn(f64, f64, Pixel) -> T) + Copy,
{
fn run(&self, _meta: Canvas, x: f32, y: f32, px: Pixel) -> T {
fn run(&self, _meta: Canvas, x: f64, y: f64, px: Pixel) -> T {
self(x, y, px)
}
}

46
src/polar_math.rs Normal file
View file

@ -0,0 +1,46 @@
use std::f64::consts::*;
use crate::Canvas;
pub fn polar(x: f64, y: f64) -> (f64, f64) {
let x = x - 0.5;
let y = y - 0.5;
let r = (x * x + y * y).sqrt();
let mut phi = f64::atan2(y, x) + PI / 2.;
phi = phi.rem_euclid(TAU);
(r * 2. / SQRT_2, phi / TAU)
}
pub fn axial(r: f64, phi: f64) -> (f64, f64) {
let phi = phi * TAU - PI / 2.;
let r = r / 2. * SQRT_2;
let x = r * phi.cos();
let y = r * phi.sin();
(x + 0.5, y + 0.5)
}
/// rotates polar coordinates (phi @ 0..1), n=1.0 ~ 360°
pub fn polar_rotate(phi: f64, n: f64) -> f64 {
(phi + n).rem_euclid(1.0)
}
pub fn distance_from_circle(
x: f64,
y: f64,
circle_x: f64,
circle_y: f64,
radius: f64,
xstretch: f64,
ystretch: f64,
) -> f64 {
let xdiff = (x - circle_x) / xstretch;
let ydiff = (y - circle_y) / ystretch;
let r = (xdiff * xdiff + ydiff * ydiff).sqrt();
r - radius
}
pub fn relative_corner_distance(meta: Canvas) -> f64 {
let corner_distance = (meta.width * meta.width + meta.height * meta.height).sqrt();
corner_distance / meta.width.max(meta.height)
}