Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
FredericaBernkastel committed Jun 14, 2023
2 parents ef5a480 + 8ce2c14 commit ca478cb
Show file tree
Hide file tree
Showing 35 changed files with 2,253 additions and 1,779 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
/test
/gallery
/Cargo.lock
/out.png
40 changes: 25 additions & 15 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,65 +1,75 @@
[package]
name = "space-filling"
version = "0.3.1"
version = "0.4.0"
description = "Generalized 2D space filling"
readme = "readme.md"
authors = ["Frederica Bernkastel <bernkastel.frederica@protonmail.com>"]
repository = "https://github.com/FredericaBernkastel/space-filling"
documentation = "https://docs.rs/space-filling/"
categories = ["science"]
keywords = ["generative-art"]
license = "GPL-3.0-only"
edition = "2018"
license = "GPL-3.0"
edition = "2021"
exclude = ["LICENCE", "doc/*", "src/legacy/*"]

[lib]
name = "space_filling"
path = "src/lib.rs"

[dependencies]
error-chain = "0.12"
image = { version = "0.23", default_features = false, features = ["png", "jpeg"], optional = true }
rand = { version = "0.8", default-features = false, optional = true }
rand_pcg = { version = "0.3", optional = true }
anyhow = "1.0"
image = { version = "0.24", default_features = false, features = ["png", "jpeg"], optional = true }
rand = { version = "0.8", default-features = false }
rand_pcg = { version = "0.3" }
rayon = "1.5"
euclid = "0.22"
num-traits = "0.2"
itertools = "0.10"
humansize = "1.1"

[dev-dependencies]
regex = "1.4"
open = "1.4"
walkdir = "2.3"
lexical-sort = "0.3"
num-complex = "0.4"

[features]
default = []
drawing = ["image", "rand", "rand_pcg"]
drawing = ["image"]

[profile.release]
lto = true
opt-level = 3

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "doc"]

[[example]]
name = "fractal_distribution"
name = "01_fractal_distribution"
required-features = ["drawing"]
path = "examples/argmax2d/01_fractal_distribution.rs"

[[example]]
name = "random_distribution"
name = "02_random_distribution"
required-features = ["drawing"]
path = "examples/gd_adf/02_random_distribution.rs"

[[example]]
name = "embedded"
name = "03_embedded"
required-features = ["drawing"]
path = "examples/argmax2d/03_embedded.rs"

[[example]]
name = "polymorphic"
name = "04_polymorphic"
required-features = ["drawing"]
path = "examples/gd_adf/04_polymorphic.rs"

[[example]]
name = "image_dataset"
name = "05_image_dataset"
required-features = ["drawing"]
path = "examples/argmax2d/05_image_dataset.rs"

[[example]]
name = "06_custom_primitive"
required-features = ["drawing"]
path = "examples/gd_adf/06_custom_primitive.rs"
Binary file added doc/custom_primitive.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
71 changes: 39 additions & 32 deletions doc/eq2.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/polymorphic.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed doc/polymorphic.png
Binary file not shown.
45 changes: 45 additions & 0 deletions examples/argmax2d/01_fractal_distribution.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/// Generate a fractal-like pattern using discrete distance field representation.
/// Simple impeative code style is shown.

use {
space_filling::{
geometry::{Shape, Circle},
sdf::{self, SDF},
solver::Argmax2D,
drawing::Draw,
util
},
anyhow::Result,
image::{Luma, Pixel, RgbaImage}
};

fn fractal_distribution(representation: &mut Argmax2D, image: &mut RgbaImage) {
representation.insert_sdf(sdf::boundary_rect);

for _ in 0..1000 {
let global_max = representation.find_max();
let circle = Circle
.translate(global_max.point.to_vector())
.scale(global_max.distance / 4.0);
representation.insert_sdf_domain(
util::domain_empirical(global_max),
|v| circle.sdf(v)
);
circle
.texture(Luma([255u8]).to_rgba())
.draw(image);
}
}

// profile: 158ms, 1000 circles, Δ = 2^-10
fn main() -> Result<()> {
let path = "out.png";
let mut representation = Argmax2D::new(1024, 16)?;
let mut image = RgbaImage::new(2048, 2048);

fractal_distribution(&mut representation, &mut image);

image.save(path)?;
open::that(path)?;
Ok(())
}
85 changes: 85 additions & 0 deletions examples/argmax2d/03_embedded.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/// Unlike ADF, Argmax2D supports cheap sign inversion, thus it is easy to
/// embed one distribution inside another.

use {
space_filling::{
geometry::{Shape, Circle, Scale, Translation},
sdf::{self, SDF},
solver::Argmax2D,
drawing::Draw,
util
},
euclid::Point2D,
anyhow::Result,
image::{Luma, Pixel, RgbaImage}
};

type AffineT<T, P> = Scale<Translation<T, P>, P>;

pub fn report_progress<'a, I>(iter: impl Iterator<Item = I>) -> impl Iterator<Item = I> {
iter.enumerate()
.map(move |(i, item)| {
if i % 1000 == 0 {
println!("#{i}");
};
item
})
}

pub fn embedded(representation: &mut Argmax2D) -> impl Iterator<Item = AffineT<Circle, f32>> + '_ {
use rand::prelude::*;
let mut rng = rand_pcg::Pcg64::seed_from_u64(1);

representation.insert_sdf(sdf::boundary_rect);

report_progress(0..100000)
.for_each(|_| {
let global_max = representation.find_max();
let circle = {
use std::f32::consts::PI;

let angle = rng.gen_range(-PI..=PI);
let r = (rng.gen_range(0f32..1.0).powf(1.0) * global_max.distance)
.min(1.0 / 4.0);
let delta = global_max.distance - r;
let offset = Point2D::from([angle.cos(), angle.sin()]) * delta;

Circle.translate(global_max.point - offset)
.scale(r)
};
representation.insert_sdf_domain(
util::domain_empirical(global_max),
|v| circle.sdf(v)
);
});


representation.invert();


report_progress(0..).map(|_| {
let global_max = representation.find_max();
let circle = Circle
.translate(global_max.point.to_vector())
.scale(global_max.distance / 3.0);

representation.insert_sdf_domain(
util::domain_empirical(global_max),
|v| circle.sdf(v)
);

circle
})
}

// profile: 119.2s, Δ = 2^-14
fn main() -> Result<()> {
let path = "out.png";
let mut image = RgbaImage::new(16384, 16384);
embedded(&mut Argmax2D::new(16384, 64)?)
.take(100000)
.for_each(|c| c.texture(Luma([255]).to_rgba()).draw(&mut image));
image.save(path)?;
open::that(path)?;
Ok(())
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
//! Generate a distribution, and use it to display an image dataset.

#![allow(dead_code)]
use {
space_filling::{
error::Result,
argmax2d::Argmax2D,
drawing::{self, DrawSync},
solver::Argmax2D,
drawing::{self, Draw},
geometry::{Shape, BoundingBox}
},
embedded::embedded,
anyhow::Result,
image::RgbaImage
};
#[path = "03_embedded.rs"]
mod embedded;

fn find_files(
Expand All @@ -23,19 +26,18 @@ fn find_files(
lexical_sort::lexical_cmp(&a, &b)
})
.into_iter()
.filter_map(std::result::Result::ok)
.filter_map(Result::ok)
.map(|file: DirEntry| file.path().to_owned())
.filter(move |file| filter(file.file_name().unwrap().to_string_lossy().as_ref()))
}

/// Generate a distribution, and use it to display an image dataset, up to 100'000 images.
fn main() -> Result<()> {
use rayon::prelude::*;

let image_folder = std::env::args().nth(1)
.map(|path| std::path::Path::new(&path).is_dir().then(|| path))
.flatten()
.expect("please provide a valid path in arguments");
.expect("Please provide a valid folder path in arguments");

let mut argmax = Argmax2D::new(16384, 64)?;
let shapes = embedded(&mut argmax);
Expand All @@ -51,12 +53,12 @@ fn main() -> Result<()> {
.filter_map(|(shape, file)| {
image::open(&file).map(|tex| {
println!("{:?} -> {:?}", shape.bounding_box(), file);
Box::new(shape.texture(tex)) as Box<dyn DrawSync<_>>
Box::new(shape.texture(tex)) as Box<dyn Draw<_, _> + Send + Sync>
}).map_err(|_| println!("unable to open {:?}", file)).ok()
})
.par_bridge();

drawing::draw_parallel_unsafe(&mut RgbaImage::new(16384, 16384), shapes)
drawing::draw_parallel(&mut RgbaImage::new(16384, 16384), shapes)
.save("out.png")?;
open::that("out.png")?;
Ok(())
Expand Down
92 changes: 0 additions & 92 deletions examples/embedded.rs

This file was deleted.

0 comments on commit ca478cb

Please sign in to comment.