This commit is contained in:
polygon - 2022-09-17 00:30:20 +02:00
commit b94b95f7c3
10 changed files with 10451 additions and 0 deletions

3340
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

12
Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "julia"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bevy = "0.8.0"
bevy_better_exit = { git = "https://github.com/polygon/bevy_better_exit", branch = "main" }
csv = "1.1"
palette = "0.6"

6160
assets/path.csv Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
[[group(0), binding(0)]]
var input: texture_storage_2d<r32float, read>;
[[group(0), binding(1)]]
var output: texture_storage_2d<rgba8unorm, write>;
[[group(0), binding(2)]]
var mapping: texture_storage_1d<rgba8unorm, read>;
[[stage(compute), workgroup_size(8, 8, 1)]]
fn colormap([[builtin(global_invocation_id)]] invocation_id: vec3<u32>, [[builtin(num_workgroups)]] num_workgroups: vec3<u32>) {
let mapping_size = f32(textureDimensions(mapping));
let pos = vec2<i32>(i32(invocation_id.x), i32(invocation_id.y));
let val = textureLoad(input, pos).r;
let val_mapping = val * mapping_size / 1.05;
//let val_mapping = clamp(val_mapping, 0.0, f32(mapping_size));
let col = textureLoad(mapping, i32(val_mapping));
textureStore(output, pos, col);
}

47
assets/shaders/julia.wgsl Normal file
View File

@ -0,0 +1,47 @@
struct JuliaParams {
c: vec2<f32>,
w: f32,
h: f32,
view_center: vec2<f32>,
view_scale: f32,
view_aspect: f32,
iters: u32,
};
@group(0) @binding(0)
var texture: texture_storage_2d<r32float, read_write>;
@group(0) @binding(1)
var<uniform> params: JuliaParams;
@compute @workgroup_size(8, 8, 1)
fn julia(@builtin(global_invocation_id) invocation_id: vec3<u32>, @builtin(num_workgroups) num_workgroups: vec3<u32>) {
let uv = vec2<f32>(f32(invocation_id.x) / params.w, f32(invocation_id.y) / params.h);
var i: i32;
var z: vec2<f32> = vec2<f32>((params.view_aspect * (uv.x - 0.5)) / params.view_scale + params.view_center.x, (uv.y - 0.5) / params.view_scale + params.view_center.y);
var top: i32 = i32(params.iters) + 2;
for (i = 2; i < top; i = i + 1) {
var x: f32 = (z.x * z.x - z.y * z.y) + params.c.x;
var y: f32 = (2.0 * z.x * z.y) + params.c.y;
if (((x * x) + (y * y)) > 4.0) {
break;
}
z.x = x;
z.y = y;
}
var col: f32;
if (i == top) {
col = 1.0;
} else {
col = min(f32(i) / f32(top), 1.0);
}
let color = vec4<f32>(col, 0.0, 0.0, 0.0);
let location = vec2<i32>(i32(invocation_id.x), i32(invocation_id.y));
textureStore(texture, location, color);
}

56
flake.nix Normal file
View File

@ -0,0 +1,56 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs";
naersk.url = "github:nix-community/naersk";
rust-overlay.url = "github:oxalica/rust-overlay";
};
outputs = { self, naersk, nixpkgs, rust-overlay }:
let
system = "x86_64-linux";
overlays = [ (import rust-overlay) ];
pkgs = import nixpkgs {
inherit overlays system;
};
rust-bin = pkgs.rust-bin.rust-nightly;
naersk-lib = naersk.lib.${system};#.override {
#cargo = rust-bin;
#rust = rust-bin;
# };
build-deps = with pkgs; [
lld
clang
pkg-config
makeWrapper
];
runtime-deps = with pkgs; [
alsa-lib
udev
xorg.libX11
xorg.libXcursor
xorg.libXrandr
xorg.libXi
xorg.libxcb
libGL
vulkan-loader
vulkan-headers
];
in
{
packages.${system}.bevy_julia = naersk-lib.buildPackage {
pname = "bevy_julia";
root = ./.;
buildInputs = runtime-deps;
nativeBuildInputs = build-deps;
overrideMain = attrs: {
fixupPhase = ''
wrapProgram $out/bin/bevy_julia \
--prefix LD_LIBRARY_PATH : ${pkgs.lib.makeLibraryPath runtime-deps} \
--set CARGO_MANIFEST_DIR $out/share/bevy_julia
mkdir -p $out/share/bevy_julia
cp -a assets $out/share/bevy_julia'';
};
};
defaultPackage.${system} = self.packages.${system}.bevy_julia;
};
}

210
src/colormap.rs Normal file
View File

@ -0,0 +1,210 @@
use bevy::{
core_pipeline::core_2d::graph::node::MAIN_PASS,
prelude::*,
render::{
render_asset::RenderAssets,
render_graph::{self, RenderGraph},
render_resource::*,
renderer::{RenderContext, RenderDevice},
RenderApp, RenderStage,
},
};
pub struct ColormapPlugin {
prev_node: &'static str,
}
impl ColormapPlugin {
pub fn with_previous(prev_node: &'static str) -> Self {
Self { prev_node }
}
}
impl Plugin for ColormapPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<ColormapInputImage>()
.init_resource::<ColormapOutputImage>()
.init_resource::<ColormapMappingImage>();
let render_app = app.sub_app_mut(RenderApp);
render_app
.init_resource::<ColormapPipeline>()
.add_system_to_stage(RenderStage::Extract, extract_colormap)
.add_system_to_stage(RenderStage::Queue, queue_bind_group);
let mut render_graph = render_app.world.get_resource_mut::<RenderGraph>().unwrap();
render_graph.add_node("colormap", ColormapDispatch);
render_graph.add_node_edge("colormap", MAIN_PASS).unwrap();
render_graph
.add_node_edge(self.prev_node, "colormap")
.unwrap();
}
}
#[derive(Default)]
pub struct ColormapInputImage(pub Handle<Image>);
#[derive(Default)]
pub struct ColormapOutputImage(pub Handle<Image>);
#[derive(Default)]
pub struct ColormapMappingImage(pub Handle<Image>);
struct ColormapBindGroup(BindGroup);
struct ColormapSize(Vec2);
struct ColormapPipeline {
pipeline: ComputePipeline,
bind_group_layout: BindGroupLayout,
}
struct ColormapDispatch;
impl FromWorld for ColormapPipeline {
fn from_world(world: &mut World) -> Self {
let render_device = world.get_resource::<RenderDevice>().unwrap();
let shader_source = include_str!("../assets/shaders/colormap.wgsl");
let shader = render_device.create_shader_module(ShaderModuleDescriptor {
label: Some("colormap_shader"),
source: ShaderSource::Wgsl(shader_source.into()),
});
let texture_bind_group_layout =
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("colormap_bind_group_layout"),
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::COMPUTE,
ty: BindingType::StorageTexture {
access: StorageTextureAccess::ReadOnly,
format: TextureFormat::R32Float,
view_dimension: TextureViewDimension::D2,
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::COMPUTE,
ty: BindingType::StorageTexture {
access: StorageTextureAccess::WriteOnly,
format: TextureFormat::Rgba8Unorm,
view_dimension: TextureViewDimension::D2,
},
count: None,
},
BindGroupLayoutEntry {
binding: 2,
visibility: ShaderStages::COMPUTE,
ty: BindingType::StorageTexture {
access: StorageTextureAccess::ReadOnly,
format: TextureFormat::Rgba8Unorm,
view_dimension: TextureViewDimension::D1,
},
count: None,
},
],
});
let pipeline_layout = render_device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: Some("colormap_pipline_layout"),
bind_group_layouts: &[&texture_bind_group_layout],
push_constant_ranges: &[],
});
let pipeline = render_device.create_compute_pipeline(&RawComputePipelineDescriptor {
label: Some("colormap_pipeline"),
layout: Some(&pipeline_layout),
module: &shader,
entry_point: "colormap",
});
ColormapPipeline {
pipeline,
bind_group_layout: texture_bind_group_layout,
}
}
}
impl render_graph::Node for ColormapDispatch {
fn update(&mut self, _world: &mut World) {}
fn run(
&self,
_graph: &mut render_graph::RenderGraphContext,
render_context: &mut RenderContext,
world: &World,
) -> Result<(), render_graph::NodeRunError> {
let pipeline = world.get_resource::<ColormapPipeline>().unwrap();
if let Some(texture_bind_group) = world.get_resource::<ColormapBindGroup>() {
let size = &world.get_resource::<ColormapSize>().unwrap();
let mut pass = render_context
.command_encoder
.begin_compute_pass(&ComputePassDescriptor::default());
pass.set_pipeline(&pipeline.pipeline);
pass.set_bind_group(0, &texture_bind_group.0, &[]);
pass.dispatch_workgroups(
(size.0.x / 8.0).ceil() as u32,
(size.0.y / 8.0).ceil() as u32,
1,
);
}
Ok(())
}
}
fn extract_colormap(
mut commands: Commands,
input: Res<ColormapInputImage>,
output: Res<ColormapOutputImage>,
mapping: Res<ColormapMappingImage>,
) {
commands.insert_resource(ColormapInputImage(input.0.clone()));
commands.insert_resource(ColormapOutputImage(output.0.clone()));
commands.insert_resource(ColormapMappingImage(mapping.0.clone()));
}
fn queue_bind_group(
mut commands: Commands,
pipeline: Res<ColormapPipeline>,
gpu_images: Res<RenderAssets<Image>>,
input: Res<ColormapInputImage>,
output: Res<ColormapOutputImage>,
mapping: Res<ColormapMappingImage>,
render_device: Res<RenderDevice>,
) {
if let (Some(input), Some(output), Some(mapping)) = (
gpu_images.get(&input.0),
gpu_images.get(&output.0),
gpu_images.get(&mapping.0),
) {
let ix = input.size.x.round() as i32;
let iy = input.size.y.round() as i32;
let ox = output.size.x.round() as i32;
let oy = output.size.y.round() as i32;
if (ix == ox) && (iy == oy) {
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
label: Some("colormap_bind_group"),
layout: &pipeline.bind_group_layout,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(&input.texture_view),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::TextureView(&output.texture_view),
},
BindGroupEntry {
binding: 2,
resource: BindingResource::TextureView(&mapping.texture_view),
},
],
});
commands.insert_resource(ColormapBindGroup(bind_group));
commands.insert_resource(ColormapSize(input.size));
}
}
}

111
src/colorramp.rs Normal file
View File

@ -0,0 +1,111 @@
#[derive(Clone, Copy, Debug)]
pub struct Color {
r: f32,
g: f32,
b: f32,
a: f32,
}
impl std::ops::Mul<f32> for Color {
type Output = Self;
fn mul(self, rhs: f32) -> Self::Output {
Color {
r: self.r * rhs,
g: self.g * rhs,
b: self.b * rhs,
a: self.a * rhs,
}
}
}
impl std::ops::Add for Color {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Color {
r: self.r + rhs.r,
g: self.g + rhs.g,
b: self.b + rhs.b,
a: self.a + rhs.a,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct Tick {
position: f32,
color: Color,
}
#[derive(Debug)]
pub struct ColorRamp {
ticks: Vec<Tick>,
}
impl ColorRamp {
pub fn new() -> ColorRamp {
ColorRamp { ticks: vec![] }
}
pub fn add(&mut self, position: f32, r: f32, g: f32, b: f32, a: f32) {
self.ticks.push(Tick {
position,
color: Color { r, g, b, a },
});
self.ticks
.sort_by(|a, b| a.position.partial_cmp(&b.position).unwrap());
}
pub fn interpolate(&self, pos: f32) -> Option<Color> {
let mut span: Option<(&Tick, &Tick)> = None;
for i in 0..self.ticks.len() - 1 {
let t1 = &self.ticks[i];
let t2 = &self.ticks[i + 1];
if (pos >= t1.position) && (pos <= t2.position) {
span = Some((t1, t2));
break;
}
}
let (t1, t2) = span?;
let relpos = pos - t1.position;
let factor = relpos / (t2.position - t1.position);
Some(t1.color * (1.0 - factor) + t2.color * factor)
}
pub fn range(&self) -> Option<(f32, f32)> {
if self.ticks.len() < 2 {
None
} else {
Some((
self.ticks.first().unwrap().position,
self.ticks.last().unwrap().position,
))
}
}
pub fn build_texture_data(&self, width: usize, height: usize) -> Option<Vec<u8>> {
let (t0, t1) = self.range()?;
let range = t1 - t0;
let step = range / width as f32;
let mut result: Vec<u8> = vec![];
for p in 0..width - 1 {
let pos = t0 + p as f32 * step;
let col = self.interpolate(pos).unwrap();
result.push((col.r * 255.).round() as u8);
result.push((col.g * 255.).round() as u8);
result.push((col.b * 255.).round() as u8);
result.push((col.a * 255.).round() as u8);
}
let last = self.interpolate(t1).unwrap();
result.push((last.r * 255.).round() as u8);
result.push((last.g * 255.).round() as u8);
result.push((last.b * 255.).round() as u8);
result.push((last.a * 255.).round() as u8);
let mut repeated = result.clone();
for _ in 0..height - 1 {
repeated.append(&mut result.clone());
}
Some(repeated)
}
}

253
src/julia.rs Normal file
View File

@ -0,0 +1,253 @@
use bevy::{
core_pipeline::core_2d::graph::node::MAIN_PASS,
ecs::system::{lifetimeless::SRes, SystemParamItem},
prelude::*,
reflect::TypeUuid,
render::{
extract_component::ExtractComponentPlugin,
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
render_graph::{self, RenderGraph},
render_resource::encase::UniformBuffer,
render_resource::*,
renderer::{RenderContext, RenderDevice},
texture::GpuImage,
RenderApp, RenderStage,
},
};
pub struct JuliaPlugin;
impl Plugin for JuliaPlugin {
fn build(&self, app: &mut App) {
app.add_asset::<JuliaData>()
.add_plugin(ExtractComponentPlugin::<Handle<JuliaData>>::default())
.add_plugin(RenderAssetPlugin::<JuliaData>::default());
let render_app = app.sub_app_mut(RenderApp);
render_app
.init_resource::<JuliaPipeline>()
.add_system_to_stage(RenderStage::Extract, extract_julia)
.add_system_to_stage(RenderStage::Queue, queue_bind_group);
let mut render_graph = render_app.world.get_resource_mut::<RenderGraph>().unwrap();
render_graph.add_node("julia", JuliaDispatch);
render_graph.add_node_edge("julia", MAIN_PASS).unwrap();
}
}
#[derive(Clone, Default, TypeUuid)]
#[uuid = "fe4bd1fe-10b1-4762-8507-446740817c63"]
pub struct JuliaData {
pub c: Vec2,
pub view_center: Vec2,
pub view_scale: f32,
pub view_aspect: f32,
pub iters: u32,
pub image: Handle<Image>,
}
#[derive(Clone, Default, ShaderType)]
struct JuliaBuffer {
c: Vec2,
w: f32,
h: f32,
view_center: Vec2,
view_scale: f32,
view_aspect: f32,
iters: u32,
}
impl JuliaBuffer {
fn new(data: &JuliaData, image: &GpuImage) -> Self {
Self {
c: data.c,
w: image.size.x,
h: image.size.y,
view_center: data.view_center,
view_scale: data.view_scale,
view_aspect: data.view_aspect,
iters: data.iters,
}
}
}
struct JuliaSize {
w: u32,
h: u32,
}
pub struct GpuJuliaData {
params: Buffer,
}
impl RenderAsset for JuliaData {
type ExtractedAsset = JuliaData;
type PreparedAsset = GpuJuliaData;
type Param = (SRes<RenderDevice>, SRes<RenderAssets<Image>>);
fn extract_asset(&self) -> Self::ExtractedAsset {
self.clone()
}
fn prepare_asset(
data: Self::ExtractedAsset,
(render_device, images): &mut SystemParamItem<Self::Param>,
) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>> {
if let Some(image) = images.get(&data.image) {
let buffer_data = JuliaBuffer::new(&data, image);
let mut buffer = UniformBuffer::new(Vec::new());
buffer.write(&buffer_data).unwrap();
let params_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
label: Some("mandelbrot_material_uniform_fs_buffer"),
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
contents: buffer.as_ref(),
});
Ok(GpuJuliaData {
params: params_buffer,
})
} else {
Err(PrepareAssetError::RetryNextUpdate(data))
}
}
}
struct JuliaImage(pub Handle<Image>);
struct JuliaBindGroup(BindGroup);
fn extract_julia(
mut commands: Commands,
data: Res<Handle<JuliaData>>,
params: Res<Assets<JuliaData>>,
images: Res<Assets<Image>>,
) {
commands.insert_resource(data.clone());
let data = params.get(&data).unwrap();
let image = images.get(&data.image).unwrap();
let size = image.texture_descriptor.size;
commands.insert_resource(JuliaSize {
w: size.width,
h: size.height,
});
commands.insert_resource(JuliaImage(data.image.clone()));
}
fn queue_bind_group(
mut commands: Commands,
pipeline: Res<JuliaPipeline>,
gpu_images: Res<RenderAssets<Image>>,
params: Res<RenderAssets<JuliaData>>,
julia_image: Res<JuliaImage>,
data: Res<Handle<JuliaData>>,
render_device: Res<RenderDevice>,
) {
let view = &gpu_images[&julia_image.0];
if let Some(data_buffer) = params.get(&data) {
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
label: Some("julia_bind_group"),
layout: &pipeline.bind_group_layout,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(&view.texture_view),
},
BindGroupEntry {
binding: 1,
resource: data_buffer.params.as_entire_binding(),
},
],
});
commands.insert_resource(JuliaBindGroup(bind_group));
}
}
pub struct JuliaPipeline {
pipeline: ComputePipeline,
bind_group_layout: BindGroupLayout,
}
impl FromWorld for JuliaPipeline {
fn from_world(world: &mut World) -> Self {
let render_device = world.get_resource::<RenderDevice>().unwrap();
let shader_source = include_str!("../assets/shaders/julia.wgsl");
let shader = render_device.create_shader_module(ShaderModuleDescriptor {
label: Some("julia_shader"),
source: ShaderSource::Wgsl(shader_source.into()),
});
let texture_bind_group_layout =
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("julia_bind_group_layout"),
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::COMPUTE,
ty: BindingType::StorageTexture {
access: StorageTextureAccess::ReadWrite,
format: TextureFormat::R32Float,
view_dimension: TextureViewDimension::D2,
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::COMPUTE,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
});
let pipeline_layout = render_device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: Some("julia_pipline_layout"),
bind_group_layouts: &[&texture_bind_group_layout],
push_constant_ranges: &[],
});
let pipeline = render_device.create_compute_pipeline(&RawComputePipelineDescriptor {
label: Some("julia_pipeline"),
layout: Some(&pipeline_layout),
module: &shader,
entry_point: "julia",
});
JuliaPipeline {
pipeline,
bind_group_layout: texture_bind_group_layout,
}
}
}
struct JuliaDispatch;
impl render_graph::Node for JuliaDispatch {
fn update(&mut self, _world: &mut World) {}
fn run(
&self,
_graph: &mut render_graph::RenderGraphContext,
render_context: &mut RenderContext,
world: &World,
) -> Result<(), render_graph::NodeRunError> {
let pipeline = world.get_resource::<JuliaPipeline>().unwrap();
if let Some(texture_bind_group) = world.get_resource::<JuliaBindGroup>() {
let size = &world.get_resource::<JuliaSize>().unwrap();
let mut pass = render_context
.command_encoder
.begin_compute_pass(&ComputePassDescriptor::default());
pass.set_pipeline(&pipeline.pipeline);
pass.set_bind_group(0, &texture_bind_group.0, &[]);
pass.dispatch_workgroups((size.w + 7) / 8, (size.h + 7) / 8, 1);
}
Ok(())
}
}

243
src/main.rs Normal file
View File

@ -0,0 +1,243 @@
mod colormap;
mod colorramp;
mod julia;
use std::{ops::Add, path::Path};
use colormap::{ColormapInputImage, ColormapMappingImage, ColormapOutputImage, ColormapPlugin};
use colorramp::ColorRamp;
use julia::{JuliaData, JuliaPlugin};
use bevy::{
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
prelude::*,
render::render_resource::*,
window::{WindowDescriptor, WindowResized},
};
use csv;
use palette::{rgb::Rgba, FromColor, Pixel};
use std::fs::File;
use bevy_better_exit::{BetterExitPlugin, ExitEvent, ExitListener};
struct MovementPath(Vec<Vec2>);
fn main() {
App::new()
.insert_resource(ClearColor(Color::BLACK))
.insert_resource(WindowDescriptor {
..Default::default()
})
.add_plugins(DefaultPlugins)
.add_plugin(JuliaPlugin)
.add_plugin(ColormapPlugin::with_previous("julia"))
.add_plugin(LogDiagnosticsPlugin::default())
.add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_plugin(BetterExitPlugin::new(None))
.add_startup_system(setup)
.add_startup_system(load_path)
.add_system(modi)
.add_system(window_size)
.add_system(bevy_better_exit::exit_on_esc_system)
//.add_system(update_color)
.run();
}
fn setup(
mut commands: Commands,
mut images: ResMut<Assets<Image>>,
mut params: ResMut<Assets<JuliaData>>,
) {
let mut julia_image = Image::new_fill(
Extent3d {
width: 400,
height: 400,
depth_or_array_layers: 1,
},
TextureDimension::D2,
&(0.0 as f32).to_ne_bytes(),
TextureFormat::R32Float,
);
julia_image.texture_descriptor.usage =
TextureUsages::COPY_DST | TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING;
let julia_image = images.add(julia_image);
let mut mapped_image = Image::new_fill(
Extent3d {
width: 400,
height: 400,
depth_or_array_layers: 1,
},
TextureDimension::D2,
&[0, 0, 0, 255],
TextureFormat::Rgba8Unorm,
);
mapped_image.texture_descriptor.usage =
TextureUsages::COPY_DST | TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING;
let mapped_image = images.add(mapped_image);
let mut ramp = ColorRamp::new();
ramp.add(0.0, 0.0, 0.0, 0.0, 1.0);
ramp.add(0.04, 0.0, 0.0, 0.0, 1.0);
ramp.add(0.2, 0.4, 0.0, 0.0, 1.0);
ramp.add(0.5, 1.0, 0.4, 0.0, 1.0);
ramp.add(0.8, 1.0, 1.0, 0.0, 1.0);
ramp.add(1.0, 1.0, 1.0, 1.0, 1.0);
/*let mut ramp = ColorRamp::new();
ramp.add(0.00, 0.0, 0.0, 0.0, 1.0);
ramp.add(0.02, 0.0, 0.0, 0.0, 1.0);
ramp.add(0.05, 0.0, 0.0, 0.4, 1.0);
ramp.add(0.1, 0.0, 0.3, 1.0, 1.0);
ramp.add(0.15, 0.6, 0.6, 1.0, 1.0);
ramp.add(0.2, 1.0, 1.0, 0.0, 1.0);
ramp.add(0.3, 1.0, 0.4, 0.0, 1.0);
ramp.add(0.4, 0.6, 0.0, 0.0, 1.0);
ramp.add(1.0, 0.0, 0.0, 0.0, 1.0);*/
let data = ramp.build_texture_data(1024, 1).unwrap();
let mut mapping_image = Image::new_fill(
Extent3d {
width: 1024,
height: 1,
depth_or_array_layers: 1,
},
TextureDimension::D1,
&[128, 64, 0, 255],
TextureFormat::Rgba8Unorm,
);
mapping_image.data = data;
mapping_image.texture_descriptor.usage =
TextureUsages::COPY_DST | TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING;
let mapping_image = images.add(mapping_image);
let data = JuliaData {
c: Vec2::new(0.2, 0.3),
view_aspect: 1.0,
view_center: Vec2::new(0.0, 0.0),
view_scale: 0.5,
iters: 128,
image: julia_image.clone(),
};
let data = params.add(data);
commands.spawn_bundle(SpriteBundle {
texture: mapped_image.clone(),
..Default::default()
});
commands.insert_resource(ColormapInputImage(julia_image));
commands.insert_resource(ColormapOutputImage(mapped_image));
commands.insert_resource(ColormapMappingImage(mapping_image));
commands.spawn_bundle(Camera2dBundle::default());
commands.insert_resource(data);
}
fn load_path(mut commands: Commands) {
let mut reader = csv::ReaderBuilder::new()
.has_headers(false)
.from_reader(File::open("assets/path.csv").unwrap());
let mut points: Vec<Vec2> = Vec::new();
for r in reader.records() {
let record = r.unwrap();
let data = record.deserialize::<[f32; 2]>(None).unwrap();
let x = data[0] / 2822.22217 * 4. - 2.5;
let y = data[1] / 2822.22217 * 4. - 2.;
points.push(Vec2::new(x, y));
}
commands.insert_resource(MovementPath(points));
}
fn modi(
mut params: ResMut<Assets<JuliaData>>,
data: Res<Handle<JuliaData>>,
time: Res<Time>,
mut path: Res<MovementPath>,
) {
let frame = (time.seconds_since_startup() * 60.) as usize;
let frame_wrap = frame % (path.0.len());
let data = params.get_mut(&data).unwrap();
data.c = path.0[frame_wrap];
/*let av = am.ahead.process(ef.0);
println!("av: {}", av);
//data.c.x = (0.2 * av) * (0.7 * time.seconds_since_startup() as f32).cos() as f32;
//data.c.y = (0.2 * av) * (0.9 * time.seconds_since_startup() as f32).sin() as f32;
data.c.x = -1.0 + 0.3 * (time.seconds_since_startup() as f32).cos();
data.c.y = 0.3 * (time.seconds_since_startup() as f32).sin();*/
}
fn window_size(
mut size_event: EventReader<WindowResized>,
julia: Res<Handle<JuliaData>>,
output: Res<ColormapOutputImage>,
mut images: ResMut<Assets<Image>>,
mut params: ResMut<Assets<JuliaData>>,
) {
for wse in size_event.iter() {
let julia = params.get_mut(&julia).unwrap();
julia.view_aspect = wse.width / wse.height;
let julia_img = images.get_mut(&julia.image).unwrap();
julia_img.resize(Extent3d {
width: wse.width as u32,
height: wse.height as u32,
depth_or_array_layers: 1,
});
let output = images.get_mut(&output.0).unwrap();
output.resize(Extent3d {
width: wse.width as u32,
height: wse.height as u32,
depth_or_array_layers: 1,
});
}
}
/*fn update_color(
mut images: ResMut<Assets<Image>>,
colormap: Res<ColormapMappingImage>,
time: Res<Time>,
) {
let colormap = images.get_mut(&colormap.0).unwrap();
let av = am.ahead.process(ef.0) * 10.;
let sat = (av / 2.0).min(1.0);
let cols = vec![
palette::Hsla::from_components((0.0 as f32, sat, 0.5, 1.0)),
palette::Hsla::from_components((32.0, sat, 0.5, 1.0)),
palette::Hsla::from_components((64.0, sat, 0.5, 1.0)),
palette::Hsla::from_components((96.0, sat, 0.5, 1.0)),
palette::Hsla::from_components((128.0, sat, 0.5, 1.0)),
palette::Hsla::from_components((160.0, sat, 0.5, 1.0)),
palette::Hsla::from_components((192.0, sat, 0.5, 1.0)),
palette::Hsla::from_components((224.0, sat, 0.5, 1.0)),
palette::Hsla::from_components((256.0, sat, 0.5, 1.0)),
];
let grad = palette::gradient::Gradient::new(cols);
let mut data: Vec<_> = grad
.take(1024)
.map(|hsva: palette::Hsla| Rgba::from_color(hsva))
.collect();
let rotations = 750;
println!("av: {}", av);
let c = &mut data.drain(0..rotations).collect();
data.append(c);
let bytes = data
.iter()
.flat_map(|rgba| rgba.into_format().into_raw::<[u8; 4]>())
.collect();
colormap.data = bytes;
}
*/