diff --git a/Cargo.lock b/Cargo.lock index a093c2c..2d76c04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1420,6 +1420,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hex" version = "0.4.3" @@ -3214,6 +3220,7 @@ dependencies = [ "flume", "futures", "glam", + "heck", "indexmap", "itertools 0.12.1", "puffin", diff --git a/Cargo.toml b/Cargo.toml index e2a9c92..93c1678 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,6 +78,7 @@ wasm-bindgen-futures = "0.4" wasm-bindgen = "0.2" web-sys = { version = "0.3", features = ["Clipboard"] } tracing-tree = "0.3" +heck = "0.5" [dependencies] violet-wgpu = { path = "violet-wgpu" } diff --git a/colors.js b/colors.js index 4373758..6ed27dc 100644 --- a/colors.js +++ b/colors.js @@ -1,111 +1,24 @@ -const colors = { - eerie_black: { - '50': '#d9d9d9', - '100': '#cccccc', - '200': '#b5b5b5', - '300': '#949494', - '400': '#6b6b6b', - '500': '#525252', - '600': '#404040', - '700': '#333333', - '800': '#292929', - '900': '#212121', - '950': '#1b1b1b', - }, - platinum: { - '50': '#f6f6f6', - '100': '#e5e4e2', - '200': '#d6d4d2', - '300': '#bcbab5', - '400': '#a19c96', - '500': '#8e8781', - '600': '#817a75', - '700': '#6c6662', - '800': '#5a5552', - '900': '#4a4744', - '950': '#272423', - }, - emerald: { - '50': '#effaf2', - '100': '#d9f2dd', - '200': '#b5e5c1', - '300': '#85d09b', - '400': '#57b777', - '500': '#309956', - '600': '#207b44', - '700': '#1a6238', - '800': '#174e2e', - '900': '#144028', - '950': '#0a2416', - }, - cyan: { - '50': '#f3faf9', - '100': '#d8efed', - '200': '#b0dfdb', - '300': '#80c8c4', - '400': '#56aba9', - '500': '#409999', - '600': '#2e7173', - '700': '#285b5d', - '800': '#24494b', - '900': '#213f40', - '950': '#0f2224', - }, - ultra_violet: { - '50': '#f1f2fc', - '100': '#e6e6f9', - '200': '#d1d1f4', - '300': '#b6b5ec', - '400': '#9f97e2', - '500': '#8d7dd7', - '600': '#7e63c8', - '700': '#6d53af', - '800': '#534185', - '900': '#4a3d72', - '950': '#2c2442', - }, - redwood: { - '50': '#fbf6f5', - '100': '#f8eae8', - '200': '#f2d9d6', - '300': '#e8beb9', - '400': '#da978f', - '500': '#c8756b', - '600': '#b35a4f', - '700': '#96493f', - '800': '#7d3f37', - '900': '#693933', - '950': '#381b17', - }, - lion: { - '50': '#f8f6ee', - '100': '#eee9d3', - '200': '#ded2aa', - '300': '#cbb479', - '400': '#bb9954', - '500': '#b38c49', - '600': '#946c3a', - '700': '#775131', - '800': '#64442f', - '900': '#573b2c', - '950': '#321f16', - }, -}; +colors = require("colors.json") console.log(`use palette::Srgba; use crate::srgba; `); +function kebabToSnakeCase(kebabCaseString) { + return kebabCaseString.replace(/-/g, '_').toUpperCase(); +} + for (var color_name in colors) { const tints = colors[color_name]; for (var tint in tints) { const color = tints[tint]; + let color_name = kebabToSnakeCase(color_name); // console.log(`tint: ${ tint }, name: ${ name }, value: ${ color } `) console.log( - `pub const ${color_name.toUpperCase()}_${tint.toUpperCase()}: Srgba = srgba!("${color}"); `, + `pub const ${color_name}_${tint.toUpperCase()}: Srgba = srgba!("${color}"); `, ); } - const tint_names = Object.keys(tints).map((tint) => ` ${color_name.toUpperCase()}_${tint.toUpperCase()},\n`).join(""); - console.log(`pub const ${color_name.toUpperCase()}_TINTS: [Srgba; ${Object.keys(tints).length}] = [\n${tint_names}];`) + const tint_names = Object.keys(tints).map((tint) => ` ${color_name}_${tint.toUpperCase()},\n`).join(""); + console.log(`pub const ${color_name}_TINTS: [Srgba; ${Object.keys(tints).length}] = [\n${tint_names}];`) } diff --git a/violet-demo/Cargo.toml b/violet-demo/Cargo.toml index 9c85582..34ccb1d 100644 --- a/violet-demo/Cargo.toml +++ b/violet-demo/Cargo.toml @@ -35,6 +35,7 @@ indexmap.workspace = true rfd.workspace = true anyhow.workspace = true flume.workspace = true +heck.workspace = true [package.metadata.wasm-pack.profile.profiling] wasm-opt = false diff --git a/violet-demo/src/lib.rs b/violet-demo/src/lib.rs index 8b21793..c8cc8d1 100644 --- a/violet-demo/src/lib.rs +++ b/violet-demo/src/lib.rs @@ -1,9 +1,10 @@ -use std::{collections::HashMap, sync::Arc}; +use std::sync::Arc; use anyhow::Context; use flume::Sender; use futures::{Future, Stream, StreamExt}; use glam::Vec2; +use heck::ToKebabCase; use indexmap::IndexMap; use itertools::Itertools; use rfd::AsyncFileDialog; @@ -75,6 +76,7 @@ pub fn run() { setup(); App::builder() + .with_title("Palette Editor") .with_renderer_config(RendererConfig { debug_mode: false }) .run(MainApp) .unwrap(); @@ -92,7 +94,7 @@ impl Widget for MainApp { Mutable::new(PaletteColor { color: Oklch::new(0.5, 0.27, (i as f32 * 60.0) % 360.0), falloff: DEFAULT_FALLOFF, - name: format!("color_{i}"), + name: format!("Color {i}"), }) }) .collect(), @@ -118,14 +120,15 @@ impl Widget for MainApp { fn tints(color: impl StateStream) -> impl Widget { puffin::profile_function!(); - row((1..=9) - .map(move |i| { + row(TINTS + .iter() + .map(move |&i| { let color = color.stream().map(move |v| { - let f = (i as f32) / 10.0; + let f = (i as f32) / 1000.0; let color = v.tint(f); Rectangle::new(ValueOrRef::value(color.into_color())) - .with_size(Unit::px2(80.0, 60.0)) + .with_size(Unit::px2(120.0, 80.0)) }); Stack::new(column(StreamWidget(color))) @@ -218,7 +221,7 @@ impl Widget for Palettes { v.push(Mutable::new(PaletteColor { color: Oklch::new(0.5, 0.27, (v.len() as f32 * 60.0) % 360.0), falloff: DEFAULT_FALLOFF, - name: format!("color_{}", v.len() + 1), + name: format!("Color {}", v.len() + 1), })); current_choice.set(Some(v.len() - 1)); }) @@ -239,7 +242,7 @@ struct Notification { kind: NotificationKind, } -enum NotificationKind { +pub enum NotificationKind { Info, Warning, Error, @@ -307,6 +310,17 @@ where } } +fn local_dir() -> std::path::PathBuf { + #[cfg(not(target_arch = "wasm32"))] + { + std::env::current_dir().unwrap() + } + #[cfg(target_arch = "wasm32")] + { + PathBuf::from(".") + } +} + fn description() -> impl Widget { let content = Mutable::new( r#"This is a palette editor. You can add, remove and select the colors in the list. Edit the color by selecting them and using the sliders or typing in the slider labels @@ -347,7 +361,7 @@ fn menu_bar( } } - let export = Button::label("Export Tints").on_press({ + let export = Button::label("Export Json").on_press({ to_owned![items, notify_tx]; move |frame, _| { let data = items @@ -355,24 +369,30 @@ fn menu_bar( .iter() .map(|item| { let item = item.lock_ref(); - let tints = (1..=9) - .map(|i| { - let color = item.tint(i as f32 / 10.0); + let tints = TINTS + .iter() + .map(|&i| { + let color = item.tint(i as f32 / 1000.0); ( - format!("{}", i * 100), + format!("{}", i), HexColor(Srgb::from_color(color).into_format()), ) }) .collect::>(); - (item.name.clone(), tints) + (item.name.to_kebab_case(), tints) }) .collect::>(); let json = serde_json::to_string_pretty(&data).unwrap(); let fut = async move { - let Some(file) = AsyncFileDialog::new().set_directory(".").save_file().await else { + let Some(file) = AsyncFileDialog::new() + .set_directory(local_dir()) + .set_file_name("colors.json") + .save_file() + .await + else { return anyhow::Ok(()); }; @@ -392,7 +412,12 @@ fn menu_bar( move |frame, _| { to_owned![items, notify_tx]; let fut = async move { - let Some(file) = AsyncFileDialog::new().set_directory(".").save_file().await else { + let Some(file) = AsyncFileDialog::new() + .set_directory(local_dir()) + .set_file_name("colors.save.json") + .save_file() + .await + else { return anyhow::Ok(()); }; @@ -416,7 +441,11 @@ fn menu_bar( move |frame, _| { to_owned![items, notify_tx]; let fut = async move { - let Some(file) = AsyncFileDialog::new().set_directory(".").pick_file().await else { + let Some(file) = AsyncFileDialog::new() + .set_directory(local_dir()) + .pick_file() + .await + else { return anyhow::Ok(()); }; @@ -597,27 +626,4 @@ impl<'de> Deserialize<'de> for HexColor { } } -#[derive(serde::Serialize, serde::Deserialize)] -pub struct TintsData { - #[serde(rename = "100")] - _100: HexColor, - #[serde(rename = "200")] - _200: HexColor, - #[serde(rename = "300")] - _300: HexColor, - #[serde(rename = "400")] - _400: HexColor, - #[serde(rename = "500")] - _500: HexColor, - #[serde(rename = "600")] - _600: HexColor, - #[serde(rename = "700")] - _700: HexColor, - #[serde(rename = "800")] - _800: HexColor, - #[serde(rename = "900")] - _900: HexColor, -} - -#[derive(serde::Serialize, serde::Deserialize)] -pub struct ColorPalettes(HashMap); +static TINTS: &[i32] = &[50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950]; diff --git a/violet-wgpu/src/app.rs b/violet-wgpu/src/app.rs index d2e9475..5ce6ad8 100644 --- a/violet-wgpu/src/app.rs +++ b/violet-wgpu/src/app.rs @@ -2,7 +2,7 @@ use futures::channel::oneshot; use std::sync::Arc; use web_time::{Duration, Instant}; -use flax::{components::name, Entity, Schedule, World}; +use flax::{components::name, entity_ids, Entity, Query, Schedule, World}; use glam::{vec2, Vec2}; use parking_lot::Mutex; use winit::{ @@ -61,15 +61,22 @@ impl Widget for Canvas { pub struct AppBuilder { renderer_config: RendererConfig, + title: String, } impl AppBuilder { pub fn new() -> Self { Self { renderer_config: Default::default(), + title: "Violet".to_string(), } } + pub fn with_title(mut self, title: impl Into) -> Self { + self.title = title.into(); + self + } + /// Set the renderer config pub fn with_renderer_config(mut self, renderer_config: RendererConfig) -> Self { self.renderer_config = renderer_config; @@ -86,7 +93,9 @@ impl AppBuilder { let event_loop = EventLoopBuilder::new().build()?; #[allow(unused_mut)] - let mut builder = WindowBuilder::new().with_inner_size(PhysicalSize::new(1920, 1080)); + let mut builder = WindowBuilder::new() + .with_inner_size(PhysicalSize::new(1920, 1080)) + .with_title(self.title); #[cfg(target_arch = "wasm32")] { @@ -175,8 +184,8 @@ impl AppBuilder { let start_time = Instant::now(); - #[cfg(not(target_arch = "wasm32"))] - let _puffin_server = setup_puffin(); + // #[cfg(not(target_arch = "wasm32"))] + // let _puffin_server = setup_puffin(); let mut instance = App { frame, @@ -203,11 +212,19 @@ impl AppBuilder { instance.update(); if !instance.is_minimized() { - let report = instance.stats.report(); - window.set_title(&format!( - "Violet - {:>4.1?} {:>4.1?} {:>4.1?}", - report.min_frame_time, report.average_frame_time, report.max_frame_time, - )); + let archetypes = instance.frame.world.archetype_info(); + let pruned = instance.frame.world.prune_archetypes(); + let entity_count = Query::new(entity_ids()) + .borrow(&instance.frame.world) + .iter() + .count(); + tracing::info!(archetype_count = archetypes.len(), entity_count, pruned); + // let report = instance.?stats.report(); + + // window.set_title(&format!( + // "Violet - {:>4.1?} {:>4.1?} {:>4.1?}", + // report.min_frame_time, report.average_frame_time, report.max_frame_time, + // )); } ctl.set_control_flow(ControlFlow::Poll); @@ -389,10 +406,10 @@ impl AppStats { } fn record_frame(&mut self, frame_time: Duration) { - self.frames.push(AppFrame { frame_time }); - if self.frames.len() > self.max_frames { + if self.frames.len() >= self.max_frames { self.frames.remove(0); } + self.frames.push(AppFrame { frame_time }); } fn report(&self) -> StatsReport {