|
|
|
@@ -0,0 +1,106 @@
|
|
|
|
|
//! # Fractal flame
|
|
|
|
|
//!
|
|
|
|
|
//! The `flam3` sources include example parameters (`test.flam3`) to demonstrate the fractal flame algorithm,
|
|
|
|
|
//! which are copied here and used to generate an image that can be compared against the reference `flam3` renderer
|
|
|
|
|
|
|
|
|
|
use anyhow::Result;
|
|
|
|
|
use enkou_shaders::chaos_game::ChaosGame;
|
|
|
|
|
use enkou_shaders::transform::Transform;
|
|
|
|
|
use enkou_shaders::variation::{Variation, VariationKind};
|
|
|
|
|
use glam::{UVec2, UVec3, UVec4, Vec2, Vec2Swizzles, Vec4};
|
|
|
|
|
use rand::SeedableRng;
|
|
|
|
|
use rand_xoshiro::Xoshiro256StarStar;
|
|
|
|
|
use enkou_shaders::camera::Camera;
|
|
|
|
|
use enkou_shaders::image::BlendMode;
|
|
|
|
|
|
|
|
|
|
const ITERATIONS_DISCARD: usize = 100;
|
|
|
|
|
const IMAGE_DIMENSIONS: UVec2 = UVec2::new(640, 480);
|
|
|
|
|
const IMAGE_QUALITY: f32 = 10.0;
|
|
|
|
|
|
|
|
|
|
// Palette data copied from `flam3-palettes.xml`.
|
|
|
|
|
// Palettes are ASCII hexadecimal strings; two characters form a byte,
|
|
|
|
|
// and bytes are grouped in triplets to get a single RGB color.
|
|
|
|
|
const PALETTE: &'static str = "\
|
|
|
|
|
00dadebc00eee6c500eef2ce00eef2cf00e6eee100eaeed800f2f1eb00f2f5d8
|
|
|
|
|
00e6f2ce00deeac500d6dac600ced2bc00c2caa900becaa000ced6aa00dee2c5
|
|
|
|
|
00eaedce00eaf2c500dee2c500c2caaa00aebeaa00a5b29600a2a98d0096a284
|
|
|
|
|
008d8d7a0085897100858d710085856700797d6700797d670071795e00656d55
|
|
|
|
|
004d5d4200344025003040250030381c002c3c1c002c341c00242c1200242400
|
|
|
|
|
00242c0900283409003840120030401c0040502f0055694200657555006c7d5e
|
|
|
|
|
00748d710074898400748d8400788d840079897a0079857100757d670071795e
|
|
|
|
|
006c715e006d705e006c795e0068755e00697155006d7555006d755500697155
|
|
|
|
|
0065715500696d550064715e006870670068706700686c67006c6c5e0071715e
|
|
|
|
|
0079796700818571007d91710085927a0085927a007d9284007992840078928d
|
|
|
|
|
00788d8d00748d84007492840075927a006c85670064795e0059694b00aa5700
|
|
|
|
|
0038441c00303c1c002c3c1c003440250050614b005d6d5e0064715e0060715e
|
|
|
|
|
0060755e0068755e006c795e006c795e0071796700707967006c7d6700687967
|
|
|
|
|
006c7967006c75670071755e0071755e0075795e00757d5e00818d5e008d925e
|
|
|
|
|
008d9267009a9a71009aa27a009aa27a009aa17a00929a71008992670081855e
|
|
|
|
|
007d7d550069794b00616d4200444c250038441c0040512500454d2500716d42
|
|
|
|
|
00797d4b00817d5500797955006d755500697d55006c795e006579540068795e
|
|
|
|
|
00647967006479670068755e0064715e00646c5e00656d55004d584200344025
|
|
|
|
|
002c381c0020281c001c1409001818000004140000081000000c1800001c2809
|
|
|
|
|
00243012003c4425005d6555007579550085895e008991710096a271009aa27a
|
|
|
|
|
009eaa7a009eaa7a00aaae7100a6aa7a00a2aa7a00a1a57a00969e7a0085967a
|
|
|
|
|
0081927a0078927a0075927a00758d7a00708167007d7d670089896700929a71
|
|
|
|
|
009eaa7a00aab68400b2b68d00b6ba9700c2ca9700b2be8d00b2b68d00aab28d
|
|
|
|
|
00a2ae84009aa67a00929e7a00859a7a007d967a007d927a007d9284007d9284
|
|
|
|
|
0081968400859684008596840081928400859a8400859a84008d9a8400929684
|
|
|
|
|
009ea98400aeb28400aaba8400b2be8d00b6c2a000c6caa000c6ceaa00d6dab3
|
|
|
|
|
00dae2c500d2d6bc00bec2a000aab68d009ea67a00929a710089897100817d67
|
|
|
|
|
007d7d6700817867007d7d5e0079795e0079815e00817d6700817d6700818167
|
|
|
|
|
008189710085917a0089927a00969d7a00969e7a0092968400969a8d00929284
|
|
|
|
|
0089918400819284007d928d0078928d0074928d0078928d007896970081968d
|
|
|
|
|
0081968d00819a8d00859a8d00899e8d00899e8d008da2970095a297008da297
|
|
|
|
|
0096a68d009aa18d009ea984009ea67a00a2a571009ea671009aa67100959d71";
|
|
|
|
|
|
|
|
|
|
/// Generate an image
|
|
|
|
|
pub fn main() -> Result<()> {
|
|
|
|
|
let transforms: &[Transform] = &[];
|
|
|
|
|
|
|
|
|
|
let weights = [0.25; 4];
|
|
|
|
|
|
|
|
|
|
let variations = [Variation::new(
|
|
|
|
|
VariationKind::Spherical,
|
|
|
|
|
1.0,
|
|
|
|
|
[0.0; 4].into(),
|
|
|
|
|
)];
|
|
|
|
|
|
|
|
|
|
let camera = Camera::new(IMAGE_DIMENSIONS, Vec2::ZERO, 0.0, Vec2::ZERO, (IMAGE_DIMENSIONS / 2).as_vec2());
|
|
|
|
|
|
|
|
|
|
let palette_chars: String = PALETTE.chars().filter(|c| c.is_alphanumeric()).collect();
|
|
|
|
|
let palette_bytes: Vec<u8> = (0..palette_chars.len())
|
|
|
|
|
.step_by(2)
|
|
|
|
|
.map(|i| {
|
|
|
|
|
let s = &palette_chars[i..i + 2];
|
|
|
|
|
let b = u8::from_str_radix(s, 16).expect("illegal palette value");
|
|
|
|
|
b
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
let palette_colors: Vec<Vec4> = palette_bytes
|
|
|
|
|
.chunks(3)
|
|
|
|
|
.map(|chunk| [chunk[0] as f32, chunk[1] as f32, chunk[2] as f32, 255.0].into())
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
let mut rng = Xoshiro256StarStar::from_seed([4u8; 32]);
|
|
|
|
|
let chaos_game = ChaosGame::new(&mut rng, transforms, &weights, &variations);
|
|
|
|
|
|
|
|
|
|
let mut image_accum: Vec<Vec4> = Vec::new();
|
|
|
|
|
image_accum.resize(IMAGE_DIMENSIONS.element_product() as usize, Vec4::ZERO);
|
|
|
|
|
|
|
|
|
|
let iterations: usize = (IMAGE_DIMENSIONS.element_product() as f32 * IMAGE_QUALITY) as usize;
|
|
|
|
|
chaos_game.skip(ITERATIONS_DISCARD).take(iterations).for_each(|(ifs_point, ifs_color)| {
|
|
|
|
|
let pixel_point = camera.transform_point_to_image(ifs_point);
|
|
|
|
|
if pixel_point.is_none() {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let pixel_point = pixel_point.unwrap();
|
|
|
|
|
let pixel_index = pixel_point.y * IMAGE_DIMENSIONS.x + pixel_point.x;
|
|
|
|
|
|
|
|
|
|
let pixel_color = BlendMode::Linear.ifs_to_rgb(ifs_color, palette_colors.as_ref());
|
|
|
|
|
image_accum[pixel_index as usize] += pixel_color;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|