Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ff19631ba | |||
| 9ea4261a84 | |||
| 81f23c1bd8 | |||
| 08ada94bd2 | |||
| 4005e14ab0 |
@@ -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
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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`
|
||||
|
||||
Reference in New Issue
Block a user