5 Commits

Author SHA1 Message Date
bspeice 7ff19631ba Merge pull request 'Add clippy, fix up warnings' (#7) from clippy into main
CI / cargo fmt (push) Successful in 25s
CI / cargo test (push) Successful in 12m39s
CI / cargo test (GPU) (push) Successful in 16m24s
Reviewed-on: #7
2026-07-12 15:50:24 -04:00
bspeice 9ea4261a84 Add clippy, fix up warnings
CI / cargo fmt (push) Successful in 23s
CI / cargo test (push) Successful in 12m44s
CI / cargo test (GPU) (push) Successful in 16m32s
2026-07-12 14:46:06 -04:00
bspeice 81f23c1bd8 Merge pull request 'Implement post-transform coefficients' (#6) from post_transform into main
CI / cargo fmt (push) Successful in 27s
CI / cargo test (push) Successful in 12m43s
CI / cargo test (GPU) (push) Successful in 12m25s
Reviewed-on: #6
2026-07-12 13:43:22 -04:00
bspeice 08ada94bd2 Implement post-transform coefficients
CI / cargo fmt (push) Successful in 1m25s
CI / cargo test (push) Successful in 14m59s
CI / cargo test (GPU) (push) Successful in 16m50s
2026-07-12 10:05:19 -04:00
bspeice 4005e14ab0 Merge pull request 'Include examples as part of cargo check' (#5) from ci_all_targets into main
CI / cargo fmt (push) Successful in 22s
CI / cargo test (push) Successful in 13m7s
CI / cargo test (GPU) (push) Successful in 12m35s
Reviewed-on: #5
2026-07-03 17:48:20 -04:00
9 changed files with 92 additions and 37 deletions
+4 -1
View File
@@ -21,7 +21,10 @@ jobs:
steps:
- uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
components: clippy
- run: cargo check --all-targets
- run: cargo clippy --all-targets
- run: cargo test
test-gpu:
@@ -30,5 +33,5 @@ jobs:
steps:
- uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
- run: cargo install --git https://github.com/rust-gpu/rust-gpu cargo-gpu
- run: cargo install --git https://github.com/rust-gpu/rust-gpu cargo-gpu --rev 67f1ff2
- run: cargo gpu check --auto-install-rust-toolchain -p enkou-shaders
+1 -1
View File
@@ -24,7 +24,7 @@ bytemuck = { version = "1.25.0", features = ["derive"] }
glam = { version = "0.33.1", default-features = false, features = ["bytemuck", "scalar-math"] }
image = { version = "0.25.10", default-features = false, features = ["default-formats"]}
libm = "0.2.16"
rand = { version = "0.10.1", default-features = false }
rand = { version = "0.10.1", default-features = false }
rand_xoshiro = "0.8.1"
rspirv = "0.13.0"
tempfile = "3.27.0"
+3 -6
View File
@@ -19,15 +19,12 @@ mod test {
}
fn has_entry_point(execution_model: ExecutionModel, name: &str) -> bool {
for ref entry_point in shader().entry_points.iter() {
for entry_point in shader().entry_points.iter() {
let operands: Vec<Operand> = entry_point
.operands
.iter()
.filter(|op| match op {
Operand::ExecutionModel(_) | Operand::LiteralString(_) => true,
_ => false,
})
.map(|op| op.clone())
.filter(|op| matches!(op, Operand::ExecutionModel(_) | Operand::LiteralString(_)))
.cloned()
.collect();
assert_eq!(operands.len(), 2);
+18 -19
View File
@@ -17,21 +17,21 @@ const IMAGE_DIMENSION: UVec2 = uvec2(600, 600);
pub fn main() -> Result<()> {
let transforms = [
// F_0: (x / 2, y / 2)
Transform::new(
Affine2::from_coefficients(0.5, 0.0, 0.0, 0.0, 0.5, 0.0),
uvec2(0, 1),
),
// F_1: ((x + 1) / 2, y / 2)
Transform::new(
Affine2::from_coefficients(0.5, 0.0, 0.5, 0.0, 0.5, 0.0),
uvec2(0, 1),
),
// F_2: (x / 2, (y + 1) / 2)
Transform::new(
Affine2::from_coefficients(0.5, 0.0, 0.0, 0.0, 0.5, 0.5),
uvec2(0, 1),
),
{
// F_0: (x / 2, y / 2)
let coefficients = Affine2::from_coefficients(0.5, 0.0, 0.0, 0.0, 0.5, 0.0);
Transform::new(coefficients, Affine2::IDENTITY, uvec2(0, 1))
},
{
// F_1: ((x + 1) / 2, y / 2)
let coefficients = Affine2::from_coefficients(0.5, 0.0, 0.5, 0.0, 0.5, 0.0);
Transform::new(coefficients, Affine2::IDENTITY, uvec2(0, 1))
},
{
// F_2: (x / 2, (y + 1) / 2)
let coefficients = Affine2::from_coefficients(0.5, 0.0, 0.0, 0.0, 0.5, 0.5);
Transform::new(coefficients, Affine2::IDENTITY, uvec2(0, 1))
},
];
let weights = [1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0];
@@ -78,10 +78,9 @@ pub fn main() -> Result<()> {
image.save(temp.path()).context("Unable to save image")?;
let open_program: &str = cfg_select! {
unix => Some("xdg-open"),
_ => None,
}
.expect("No available program to open images");
unix => "xdg-open",
_ => panic!("No available program to open images")
};
Command::new(open_program)
.arg(temp.path())
+5 -5
View File
@@ -26,14 +26,14 @@ impl Camera {
///
/// * `dimensions` - Width and height of the output image (in pixels).
/// * `center` - Location of the origin in IFS coordinates. Positive `x` shifts the image
/// left, and positive `y` position shifts the image up.
/// left, and positive `y` position shifts the image up.
/// * `rotate` - Rotation angle (in radians) of IFS coordinates. Rotation is applied after the
/// `center` translation, so it is about the new origin.
/// `center` translation, so it is about the new origin.
/// * `zoom` - Zoom factor applied to IFS coordinates. IFS coordinates are scaled by
/// `pow(2, zoom)`, so a zoom factor of 0 is the identity.
/// `pow(2, zoom)`, so a zoom factor of 0 is the identity.
/// * `scale` - Pixels per unit of IFS coordinates. This parameter is usually chosen such
/// that the largest dimension will cover the range `[-2, 2]`, but values higher or lower
/// can be used as a secondary zoom.
/// that the largest dimension will cover the range `[-2, 2]`, but values higher or lower
/// can be used as a secondary zoom.
pub fn new(dimensions: UVec2, center: Vec2, rotate: f32, zoom: Vec2, scale: Vec2) -> Camera {
let ifs_center_transform = Affine2::from_translation(-center);
let zoom_transform = Affine2::from_scale(vec2(powf(2.0, zoom.x), powf(2.0, zoom.y)));
+2 -1
View File
@@ -1,6 +1,7 @@
//! # Enkou
#![no_std]
#![warn(missing_docs)]
#![deny(missing_docs)]
#![allow(clippy::needless_range_loop)] // SPIR-V backend has issues with iteration over items
pub mod camera;
pub mod chaos_game;
+1 -1
View File
@@ -24,7 +24,7 @@ pub(crate) fn xoshiro256starstar_from_seed(
// - `u64::from_le_bytes` has issues with `OpBitcast` in SPIR-V validation
for i in 0..rng_state_actual.len() {
for j in 0..size_of::<u64>() {
rng_state_actual[i] |= (rng_state[i * size_of::<u64>() + j] as u64) << j * 8;
rng_state_actual[i] |= (rng_state[i * size_of::<u64>() + j] as u64) << (j * 8);
}
}
+52 -3
View File
@@ -13,14 +13,16 @@ use rand::Rng;
#[repr(C)]
pub struct Transform {
coefficients: Affine2,
coefficients_post: Affine2,
variation_range: UVec2,
}
impl Transform {
/// Create a new transform from an affine transformation matrix
pub fn new(coefficients: Affine2, variation_range: UVec2) -> Self {
pub fn new(coefficients: Affine2, coefficients_post: Affine2, variation_range: UVec2) -> Self {
Transform {
coefficients,
coefficients_post,
variation_range,
}
}
@@ -39,10 +41,57 @@ impl Transform {
let variation_start = self.variation_range.x;
let variation_end = self.variation_range.y;
for variation_index in variation_start..variation_end {
let ref variation = variations[variation_index as usize];
let variation = &variations[variation_index as usize];
point_output += variation.transform_point(point, rng, &self.coefficients)
}
point_output
self.coefficients_post.transform_point2(point)
}
}
#[cfg(test)]
mod test {
use crate::rng::xoshiro256starstar_from_seed;
use crate::transform::Transform;
use crate::variation::{Variation, VariationKind};
use glam::{Affine2, uvec2, vec2};
#[test]
fn transform_scaling() {
let scale_coefficients = vec2(2.0, 0.5);
let transform = Transform::new(
Affine2::from_scale(scale_coefficients),
Affine2::IDENTITY,
uvec2(0, 1),
);
let mut rng = xoshiro256starstar_from_seed([0; 32]);
let variations = [Variation::IDENTITY];
let point = vec2(1.0, 1.0);
assert_eq!(
transform.transform_point(&mut rng, &variations, point),
scale_coefficients
);
}
#[test]
fn transform_scaling_post() {
let scale_coefficients = vec2(2.0, 0.5);
let transform_pdj = Transform::new(Affine2::IDENTITY, Affine2::IDENTITY, uvec2(0, 1));
let transform_pdj_post = Transform::new(
Affine2::IDENTITY,
Affine2::from_scale(scale_coefficients),
uvec2(0, 1),
);
let mut rng = xoshiro256starstar_from_seed([0; 32]);
let variations = [Variation::new(VariationKind::Pdj, 1.0, [0.0f32; 4].into())];
let point = vec2(1.0, 1.0);
let point_pdj = transform_pdj.transform_point(&mut rng, &variations, point);
let point_pdj_post = transform_pdj_post.transform_point(&mut rng, &variations, point);
assert_eq!(point_pdj * scale_coefficients, point_pdj_post);
}
}
+6
View File
@@ -20,6 +20,12 @@ use rand::{Rng, RngExt};
#[repr(C)]
pub struct VariationParams([f32; 4]);
impl From<[f32; 4]> for VariationParams {
fn from(v: [f32; 4]) -> Self {
VariationParams(v)
}
}
/// Enum for all supported variation types
///
/// ID numbers are chosen to match the variation identifier also used by `flam3`