diff --git a/Cargo.toml b/Cargo.toml index ff75c81..71f13c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,11 @@ version = "1.1.0" edition = "2021" [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib", "lib"] + +[[bin]] +name = "shapemaker" +path = "src/main.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/Justfile b/Justfile index a5d553d..299be89 100644 --- a/Justfile +++ b/Justfile @@ -1,5 +1,5 @@ build: - cargo build + cargo build --bin shapemaker cp target/debug/shapemaker . web: diff --git a/example/main.rs b/example/main.rs deleted file mode 100644 index dcb7a66..0000000 --- a/example/main.rs +++ /dev/null @@ -1,265 +0,0 @@ -mod cli; -pub use cli::{canvas_from_cli, cli_args}; -use shapemaker::{Anchor, Canvas, Color, Fill, Layer, Object, Region, Video}; - -fn main() { - run(cli_args()); -} - -pub fn run(args: cli::Args) { - let mut canvas = canvas_from_cli(&args); - - if args.cmd_image && !args.cmd_video { - canvas.layers.push(canvas.random_layer("root")); - canvas.set_background(Color::White); - let aspect_ratio = canvas.grid_size.0 as f32 / canvas.grid_size.1 as f32; - match Canvas::save_as_png( - &args.arg_file, - aspect_ratio, - args.flag_resolution.unwrap_or(1000), - canvas.render(&vec!["*"], true), - ) { - Ok(_) => println!("Image saved to {}", args.arg_file), - Err(e) => println!("Error saving image: {}", e), - } - return; - } - - let mut video = Video::::new(canvas); - video.duration_override = args.flag_duration.map(|seconds| seconds * 1000); - video.start_rendering_at = args.flag_start.unwrap_or_default() * 1000; - video.fps = args.flag_fps.unwrap_or(30); - video.audiofile = args.flag_audio.unwrap().into(); - video = video - .init(&|canvas: _, context: _| { - context.extra = State { - bass_pattern_at: Region::from_origin_and_size((6, 3), (3, 3)), - first_kick_happened: false, - }; - canvas.set_background(Color::Black); - - let mut kicks = Layer::new("anchor kick"); - - let fill = Some(Fill::Translucent(Color::White, 0.0)); - let circle_at = |x: usize, y: usize| Object::SmallCircle(Anchor(x as i32, y as i32)); - - let (end_x, end_y) = { - let (x, y) = canvas.world_region.end; - (x - 2, y - 2) - }; - kicks.add_object("top left", circle_at(1, 1), fill); - kicks.add_object("top right", circle_at(end_x, 1), fill); - kicks.add_object("bottom left", circle_at(1, end_y), fill); - kicks.add_object("bottom right", circle_at(end_x, end_y), fill); - canvas.add_or_replace_layer(kicks); - - let mut ch = Layer::new("ch"); - ch.add_object("0", Object::Dot(Anchor(0, 0)), None); - canvas.add_or_replace_layer(ch); - }) - .sync_audio_with(&args.flag_sync_with.unwrap()) - .on_note("anchor kick", &|canvas, ctx| { - // ctx.extra.bass_pattern_at = region_cycle(&canvas.world_region, None); - canvas - .layer("anchor kick") - .paint_all_objects(Fill::Translucent(Color::White, 1.0)); - - canvas.layer("anchor kick").flush(); - - ctx.later_ms(200, &fade_out_kick_circles) - }) - .on_note("bass", &|canvas, ctx| { - let mut new_layer = canvas.random_layer_within("bass", &ctx.extra.bass_pattern_at); - new_layer.paint_all_objects(Fill::Solid(Color::White)); - canvas.add_or_replace_layer(new_layer); - }) - .on_note("powerful clap hit, clap, perclap", &|canvas, ctx| { - let mut new_layer = - canvas.random_layer_within("claps", &ctx.extra.bass_pattern_at.translated(2, 0)); - new_layer.paint_all_objects(Fill::Solid(Color::Red)); - canvas.add_or_replace_layer(new_layer) - }) - .on_note( - "rimshot, glitchy percs, hitting percs, glitchy percs", - &|canvas, ctx| { - let mut new_layer = canvas - .random_layer_within("percs", &ctx.extra.bass_pattern_at.translated(2, 0)); - new_layer.paint_all_objects(Fill::Translucent(Color::Red, 0.5)); - canvas.add_or_replace_layer(new_layer); - }, - ) - .on_note("qanda", &|canvas, ctx| { - let mut new_layer = canvas.random_linelikes_within( - "qanda", - &ctx.extra.bass_pattern_at.translated(-1, -1).enlarged(1, 1), - ); - new_layer.paint_all_objects(Fill::Solid(Color::Orange)); - new_layer.object_sizes.default_line_width = canvas.object_sizes.default_line_width - * 4.0 - * ctx.stem("qanda").velocity_relative(); - canvas.add_or_replace_layer(new_layer) - }) - .on_note("brokenup", &|canvas, ctx| { - let mut new_layer = canvas - .random_linelikes_within("brokenup", &ctx.extra.bass_pattern_at.translated(0, -2)); - new_layer.paint_all_objects(Fill::Solid(Color::Yellow)); - new_layer.object_sizes.default_line_width = canvas.object_sizes.default_line_width - * 4.0 - * ctx.stem("brokenup").velocity_relative(); - canvas.add_or_replace_layer(new_layer); - }) - .on_note("goup", &|canvas, ctx| { - let mut new_layer = - canvas.random_linelikes_within("goup", &ctx.extra.bass_pattern_at.translated(0, 2)); - new_layer.paint_all_objects(Fill::Solid(Color::Green)); - new_layer.object_sizes.default_line_width = - canvas.object_sizes.default_line_width * 4.0 * ctx.stem("goup").velocity_relative(); - canvas.add_or_replace_layer(new_layer); - }) - .on_note("ch", &|canvas, ctx| { - let world = canvas.world_region.clone(); - - // keep only the last 2 dots - let dots_to_keep = canvas - .layer("ch") - .objects - .iter() - .sorted_by_key(|(name, _)| name.parse::().unwrap()) - .rev() - .take(2) - .map(|(name, _)| (name.clone())) - .collect::>(); - - let layer = canvas.layer("ch"); - layer.object_sizes.empty_shape_stroke_width = 2.0; - layer.objects.retain(|name, _| dots_to_keep.contains(name)); - - let object_name = format!("{}", ctx.ms); - layer.add_object( - &object_name, - Object::Dot(world.resized(-1, -1).random_coordinates_within().into()), - Some(Fill::Solid(Color::Cyan)), - ); - - canvas.put_layer_on_top("ch"); - canvas.layer("ch").flush(); - }) - .when_remaining(10, &|canvas, _| { - canvas.root().add_object( - "credits text", - Object::RawSVG(Box::new(svg::node::Text::new("by ewen-lbh"))), - None, - ); - }) - .command("remove", &|argumentsline, canvas, _| { - let args = argumentsline.splitn(3, ' ').collect::>(); - canvas.remove_object(args[0]); - }); - - if args.flag_preview { - video.preview_on(8888); - } else { - video.render_to(args.arg_file, args.flag_workers.unwrap_or(8), false); - } -} - -fn fade_out_kick_circles(canvas: &mut Canvas) { - canvas - .layer("anchor kick") - .paint_all_objects(Fill::Translucent(Color::White, 0.0)); - - canvas.layer("anchor kick").flush(); -} - -fn update_stem_position( - ctx: &mut shapemaker::Context, - canvas: &mut Canvas, - layer_name: &str, - offset: usize, -) { - let (dx, dy) = ctx.extra.bass_pattern_at - - region_cycle_with_offset( - &canvas.world_region, - Some(&ctx.extra.bass_pattern_at), - offset, - ); - match canvas.layer_safe(layer_name) { - Some(l) => l.move_all_objects(dx, dy), - _ => (), - } -} - -#[derive(Default)] -struct State { - first_kick_happened: bool, - bass_pattern_at: Region, -} - -fn color_cycle(current_color: Color) -> Color { - match current_color { - Color::Blue => Color::Cyan, - Color::Cyan => Color::Green, - Color::Green => Color::Yellow, - Color::Yellow => Color::Orange, - Color::Orange => Color::Red, - Color::Red => Color::Purple, - Color::Purple => Color::Pink, - Color::Pink => Color::White, - Color::White => Color::Blue, - _ => unreachable!(), - } -} - -fn region_cycle_with_offset(world: &Region, current: Option<&Region>, offset: usize) -> Region { - if offset == 0 { - return current.unwrap().clone(); - } - - if offset == 1 { - return region_cycle(world, current); - } - - region_cycle_with_offset(world, current, offset - 1) -} - -fn hat_region_cycle(world: &Region, current: &Region) -> (i32, i32) { - let (end_x, end_y) = { - let (x, y) = world.end; - (x - 2, y - 2) - }; - - match current.start { - // top row - (x, 1) if x < end_x => (1, 0), - // right column - (x, y) if x == end_x && y < end_y => (0, 1), - // bottom row - (x, y) if y == end_y && x > 1 => (-1, 0), - // left column - (1, y) if y > 1 => (0, -1), - _ => unreachable!(), - } -} - -fn region_cycle(world: &Region, current: Option<&Region>) -> Region { - let mut region = if let Some(current) = current { - current.clone() - } else { - Region::from_origin_and_size((1, 1), (2, 2)) - }; - - let size = (region.width(), region.height()); - // Move along x axis if possible - if region.end.0 + size.0 <= world.end.0 - 1 { - region.translate(size.0 as i32, 0) - } - // Else go to x=0 and move along y axis - else if region.end.1 + size.1 <= world.end.1 - 1 { - region = Region::new(2, region.end.1, size.0 + 2, region.end.1 + size.1) - } - // Else go to origin - else { - region = Region::from_origin_and_size((1, 1), size) - } - region -} diff --git a/src/canvas.rs b/src/canvas.rs index 6079f62..08a29f2 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -2,6 +2,7 @@ use core::panic; use std::{cmp, collections::HashMap, io::Write, ops::Range}; use chrono::DateTime; +use itertools::Itertools; use rand::Rng; use crate::{ @@ -156,7 +157,6 @@ impl Canvas { } pub fn add_or_replace_layer(&mut self, layer: Layer) { - console_log!("ijogjrjoigeojigjoijoigerjoi"); if let Some(existing_layer) = self.layer_safe(&layer.name) { existing_layer.replace(layer); } else { @@ -195,7 +195,6 @@ impl Canvas { } pub fn random_linelikes_within(&self, layer_name: &str, region: &Region) -> Layer { - console_log!("iurgrhierihA"); let mut objects: HashMap = HashMap::new(); let number_of_objects = rand::thread_rng().gen_range(self.objects_count_range.clone()); for i in 0..number_of_objects { @@ -249,7 +248,6 @@ impl Canvas { pub fn random_linelike_within(&self, region: &Region) -> Object { let start = self.random_anchor(region); - console_log!("start: {:?}", start); match rand::thread_rng().gen_range(1..=3) { 1 => Object::CurveInward( start, @@ -322,7 +320,6 @@ impl Canvas { } pub fn random_anchor(&self, region: &Region) -> Anchor { - console_log!("region: {:?}", region); if self.region_is_whole_grid(region) && rand::thread_rng().gen_bool(1.0 / (self.grid_size.0 * self.grid_size.1) as f64) { @@ -409,6 +406,17 @@ impl Canvas { self.cell_size * (self.grid_size.1 - 1) + 2 * self.canvas_outter_padding } + /// returns a list of all unique filters used throughout the canvas + /// used to only generate one definition per filter + /// + fn unique_filters(&self) -> Vec { + self.layers + .iter() + .flat_map(|layer| layer.objects.iter().flat_map(|(_, o)| o.2.clone())) + .unique() + .collect() + } + pub fn render(&mut self, layers: &Vec<&str>, render_background: bool) -> String { let background_color = self.background.unwrap_or_default(); let mut svg = svg::Document::new(); @@ -431,6 +439,9 @@ impl Canvas { svg = svg.add(layer.render(self.colormap.clone(), self.cell_size, layer.object_sizes)); } + for filter in self.unique_filters() { + svg = svg.add(filter.definition()) + } svg.set( "viewBox", diff --git a/src/cli.rs b/src/cli.rs index 5e47619..07de7fa 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,6 +1,6 @@ use docopt::Docopt; use serde::Deserialize; -use shapemaker::{Canvas, ColorMapping}; +use crate::{Canvas, ColorMapping}; const USAGE: &str = " ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ diff --git a/src/filter.rs b/src/filter.rs index fa7b080..4f1a50f 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -1,3 +1,5 @@ +use std::hash::Hash; + use wasm_bindgen::prelude::*; use crate::RenderCSS; @@ -17,13 +19,22 @@ pub struct Filter { pub parameter: f32, } +#[wasm_bindgen] impl Filter { - pub fn name(&self) -> &str { + pub fn name(&self) -> String { match self.kind { FilterType::Glow => "glow", FilterType::NaturalShadow => "natural-shadow-filter", FilterType::Saturation => "saturation", } + .to_owned() + } + + pub fn glow(intensity: f32) -> Self { + Self { + kind: FilterType::Glow, + parameter: intensity, + } } pub fn id(&self) -> String { @@ -33,7 +44,9 @@ impl Filter { self.parameter.to_string().replace(".", "_") ) } +} +impl Filter { pub fn definition(&self) -> svg::node::element::Filter { match self.kind { FilterType::Glow => { @@ -113,12 +126,14 @@ impl Filter { ) } } + .set("id", self.id()) + .set("filterUnit", "userSpaceOnUse") } } impl RenderCSS for Filter { fn render_fill_css(&self, _colormap: &crate::ColorMapping) -> String { - format!("filter: url(#{});", self.name()) + format!("filter: url(#{}); overflow: visible;", self.id()) } fn render_stroke_css(&self, colormap: &crate::ColorMapping) -> String { @@ -132,3 +147,11 @@ impl PartialEq for Filter { self.kind == other.kind && (self.parameter - other.parameter).abs() < f32::EPSILON } } + +impl Hash for Filter { + fn hash(&self, state: &mut H) { + self.id().hash(state) + } +} + +impl Eq for Filter {} diff --git a/src/lib.rs b/src/lib.rs index 9515cf8..5874000 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ mod color; +pub mod cli; mod objects; pub use color::*; pub use objects::*; @@ -9,7 +10,7 @@ pub use fill::*; mod region; pub use region::*; mod web; -pub use web::{log, main}; +pub use web::log; mod audio; pub use audio::*; mod sync; @@ -867,3 +868,5 @@ impl Video { spinner.end(&format!("Built video to {}", output_file)); } } + +fn main() {} diff --git a/src/main.rs b/src/main.rs index e69de29..3abb397 100644 --- a/src/main.rs +++ b/src/main.rs @@ -0,0 +1,264 @@ +use itertools::Itertools; +use shapemaker::{cli::{canvas_from_cli, cli_args}, *}; + +pub fn main() { + run(cli_args()); +} + +pub fn run(args: cli::Args) { + let mut canvas = canvas_from_cli(&args); + + if args.cmd_image && !args.cmd_video { + canvas.layers.push(canvas.random_layer("root")); + canvas.set_background(Color::White); + let aspect_ratio = canvas.grid_size.0 as f32 / canvas.grid_size.1 as f32; + match Canvas::save_as_png( + &args.arg_file, + aspect_ratio, + args.flag_resolution.unwrap_or(1000), + canvas.render(&vec!["*"], true), + ) { + Ok(_) => println!("Image saved to {}", args.arg_file), + Err(e) => println!("Error saving image: {}", e), + } + return; + } + + let mut video = Video::::new(canvas); + video.duration_override = args.flag_duration.map(|seconds| seconds * 1000); + video.start_rendering_at = args.flag_start.unwrap_or_default() * 1000; + video.fps = args.flag_fps.unwrap_or(30); + video.audiofile = args.flag_audio.unwrap().into(); + video = video + .init(&|canvas: _, context: _| { + context.extra = State { + bass_pattern_at: Region::from_origin_and_size((6, 3), (3, 3)), + first_kick_happened: false, + }; + canvas.set_background(Color::Black); + + let mut kicks = Layer::new("anchor kick"); + + let fill = Some(Fill::Translucent(Color::White, 0.0)); + let circle_at = |x: usize, y: usize| Object::SmallCircle(Anchor(x as i32, y as i32)); + + let (end_x, end_y) = { + let Point(x, y) = canvas.world_region.end; + (x - 2, y - 2) + }; + kicks.add_object("top left", circle_at(1, 1), fill); + kicks.add_object("top right", circle_at(end_x, 1), fill); + kicks.add_object("bottom left", circle_at(1, end_y), fill); + kicks.add_object("bottom right", circle_at(end_x, end_y), fill); + canvas.add_or_replace_layer(kicks); + + let mut ch = Layer::new("ch"); + ch.add_object("0", Object::Dot(Anchor(0, 0)), None); + canvas.add_or_replace_layer(ch); + }) + .sync_audio_with(&args.flag_sync_with.unwrap()) + .on_note("anchor kick", &|canvas, ctx| { + // ctx.extra.bass_pattern_at = region_cycle(&canvas.world_region, None); + canvas + .layer("anchor kick") + .paint_all_objects(Fill::Translucent(Color::White, 1.0)); + + canvas.layer("anchor kick").flush(); + + ctx.later_ms(200, &fade_out_kick_circles) + }) + .on_note("bass", &|canvas, ctx| { + let mut new_layer = canvas.random_layer_within("bass", &ctx.extra.bass_pattern_at); + new_layer.paint_all_objects(Fill::Solid(Color::White)); + canvas.add_or_replace_layer(new_layer); + }) + .on_note("powerful clap hit, clap, perclap", &|canvas, ctx| { + let mut new_layer = + canvas.random_layer_within("claps", &ctx.extra.bass_pattern_at.translated(2, 0)); + new_layer.paint_all_objects(Fill::Solid(Color::Red)); + canvas.add_or_replace_layer(new_layer) + }) + .on_note( + "rimshot, glitchy percs, hitting percs, glitchy percs", + &|canvas, ctx| { + let mut new_layer = canvas + .random_layer_within("percs", &ctx.extra.bass_pattern_at.translated(2, 0)); + new_layer.paint_all_objects(Fill::Translucent(Color::Red, 0.5)); + canvas.add_or_replace_layer(new_layer); + }, + ) + .on_note("qanda", &|canvas, ctx| { + let mut new_layer = canvas.random_linelikes_within( + "qanda", + &ctx.extra.bass_pattern_at.translated(-1, -1).enlarged(1, 1), + ); + new_layer.paint_all_objects(Fill::Solid(Color::Orange)); + new_layer.object_sizes.default_line_width = canvas.object_sizes.default_line_width + * 4.0 + * ctx.stem("qanda").velocity_relative(); + canvas.add_or_replace_layer(new_layer) + }) + .on_note("brokenup", &|canvas, ctx| { + let mut new_layer = canvas + .random_linelikes_within("brokenup", &ctx.extra.bass_pattern_at.translated(0, -2)); + new_layer.paint_all_objects(Fill::Solid(Color::Yellow)); + new_layer.object_sizes.default_line_width = canvas.object_sizes.default_line_width + * 4.0 + * ctx.stem("brokenup").velocity_relative(); + canvas.add_or_replace_layer(new_layer); + }) + .on_note("goup", &|canvas, ctx| { + let mut new_layer = + canvas.random_linelikes_within("goup", &ctx.extra.bass_pattern_at.translated(0, 2)); + new_layer.paint_all_objects(Fill::Solid(Color::Green)); + new_layer.object_sizes.default_line_width = + canvas.object_sizes.default_line_width * 4.0 * ctx.stem("goup").velocity_relative(); + canvas.add_or_replace_layer(new_layer); + }) + .on_note("ch", &|canvas, ctx| { + let world = canvas.world_region.clone(); + + // keep only the last 2 dots + let dots_to_keep = canvas + .layer("ch") + .objects + .iter() + .sorted_by_key(|(name, _)| name.parse::().unwrap()) + .rev() + .take(2) + .map(|(name, _)| (name.clone())) + .collect::>(); + + let layer = canvas.layer("ch"); + layer.object_sizes.empty_shape_stroke_width = 2.0; + layer.objects.retain(|name, _| dots_to_keep.contains(name)); + + let object_name = format!("{}", ctx.ms); + layer.add_object( + &object_name, + Object::Dot(world.resized(-1, -1).random_coordinates_within().into()), + Some(Fill::Solid(Color::Cyan)), + ); + + canvas.put_layer_on_top("ch"); + canvas.layer("ch").flush(); + }) + .when_remaining(10, &|canvas, _| { + canvas.root().add_object( + "credits text", + Object::RawSVG(Box::new(svg::node::Text::new("by ewen-lbh"))), + None, + ); + }) + .command("remove", &|argumentsline, canvas, _| { + let args = argumentsline.splitn(3, ' ').collect::>(); + canvas.remove_object(args[0]); + }); + + if args.flag_preview { + video.preview_on(8888); + } else { + video.render_to(args.arg_file, args.flag_workers.unwrap_or(8), false); + } +} + +fn fade_out_kick_circles(canvas: &mut Canvas) { + canvas + .layer("anchor kick") + .paint_all_objects(Fill::Translucent(Color::White, 0.0)); + + canvas.layer("anchor kick").flush(); +} + +fn update_stem_position( + ctx: &mut shapemaker::Context, + canvas: &mut Canvas, + layer_name: &str, + offset: usize, +) { + let (dx, dy) = ctx.extra.bass_pattern_at + - region_cycle_with_offset( + &canvas.world_region, + Some(&ctx.extra.bass_pattern_at), + offset, + ); + match canvas.layer_safe(layer_name) { + Some(l) => l.move_all_objects(dx, dy), + _ => (), + } +} + +#[derive(Default)] +struct State { + first_kick_happened: bool, + bass_pattern_at: Region, +} + +fn color_cycle(current_color: Color) -> Color { + match current_color { + Color::Blue => Color::Cyan, + Color::Cyan => Color::Green, + Color::Green => Color::Yellow, + Color::Yellow => Color::Orange, + Color::Orange => Color::Red, + Color::Red => Color::Purple, + Color::Purple => Color::Pink, + Color::Pink => Color::White, + Color::White => Color::Blue, + _ => unreachable!(), + } +} + +fn region_cycle_with_offset(world: &Region, current: Option<&Region>, offset: usize) -> Region { + if offset == 0 { + return current.unwrap().clone(); + } + + if offset == 1 { + return region_cycle(world, current); + } + + region_cycle_with_offset(world, current, offset - 1) +} + +fn hat_region_cycle(world: &Region, current: &Region) -> (i32, i32) { + let (end_x, end_y) = { + let Point(x, y) = world.end; + (x - 2, y - 2) + }; + + match current.start { + // top row + Point(x, 1) if x < end_x => (1, 0), + // right column + Point(x, y) if x == end_x && y < end_y => (0, 1), + // bottom row + Point(x, y) if y == end_y && x > 1 => (-1, 0), + // left column + Point(1, y) if y > 1 => (0, -1), + _ => unreachable!(), + } +} + +fn region_cycle(world: &Region, current: Option<&Region>) -> Region { + let mut region = if let Some(current) = current { + current.clone() + } else { + Region::from_origin_and_size((1, 1), (2, 2)) + }; + + let size = (region.width(), region.height()); + // Move along x axis if possible + if region.end.0 + size.0 <= world.end.0 - 1 { + region.translate(size.0 as i32, 0) + } + // Else go to x=0 and move along y axis + else if region.end.1 + size.1 <= world.end.1 - 1 { + region = Region::new(2, region.end.1, size.0 + 2, region.end.1 + size.1) + } + // Else go to origin + else { + region = Region::from_origin_and_size((1, 1), size) + } + region +} diff --git a/src/objects.rs b/src/objects.rs index 1f19827..2555404 100644 --- a/src/objects.rs +++ b/src/objects.rs @@ -174,7 +174,6 @@ impl Object { let mut css = String::new(); if !matches!(self, Object::RawSVG(..)) { - // group = group.set("style", fill.render_css(colormap, !self.fillable())); css = fill.render_css(colormap, !self.fillable()); } @@ -185,13 +184,7 @@ impl Object { .join(" ") .as_ref(); - group = group.set("data-object", id).set("style", css); - - for f in filter { - group = group.add(f.definition().set("id", f.id())) - } - - group + group.set("data-object", id).set("style", css) } fn render_raw_svg(&self) -> Box { diff --git a/src/web.rs b/src/web.rs index b6f931b..7e2f1e5 100644 --- a/src/web.rs +++ b/src/web.rs @@ -6,7 +6,8 @@ use wasm_bindgen::{closure::Closure, prelude::wasm_bindgen}; use wasm_bindgen::{JsValue, UnwrapThrowExt}; use crate::{ - layer, Anchor, Canvas, CenterAnchor, Color, ColorMapping, Fill, Filter, Layer, Object, + layer, Anchor, Canvas, CenterAnchor, Color, ColorMapping, Fill, Filter, FilterType, Layer, + Object, }; static WEB_CANVAS: Lazy> = Lazy::new(|| Mutex::new(Canvas::default_settings())); @@ -16,7 +17,7 @@ fn canvas() -> std::sync::MutexGuard<'static, Canvas> { } #[wasm_bindgen(start)] -pub fn main() -> Result<(), JsValue> { +pub fn js_init() -> Result<(), JsValue> { render_image(0.0, Color::Black)?; Ok(()) } @@ -59,10 +60,9 @@ pub fn render_image(opacity: f32, color: Color) -> Result<(), JsValue> { canvas.set_grid_size(4, 4); - console_log!("Amerika ya :D {}", "Hallo :D ".repeat(8)); - let mut layer = canvas.random_layer(&color.name()); layer.paint_all_objects(Fill::Translucent(color.into(), opacity)); + // layer.filter_all_objects(Filter::glow(3.0)); canvas.add_or_replace_layer(layer); let window = web_sys::window().expect("no global `window` exists"); @@ -77,6 +77,18 @@ pub fn render_image(opacity: f32, color: Color) -> Result<(), JsValue> { Ok(()) } +#[wasm_bindgen] +pub fn render_canvas_into(selector: String) -> () { + let svgstring = canvas().render(&vec!["*"], false); + append_new_div_inside(svgstring, selector) +} + +#[wasm_bindgen] +pub fn render_canvas_at(selector: String) -> () { + let svgstring = canvas().render(&vec!["*"], false); + replace_content_with(svgstring, selector) +} + #[wasm_bindgen] pub enum MidiEvent { Note, @@ -172,7 +184,6 @@ pub fn get_layer(name: &str) -> Result { #[wasm_bindgen] pub fn random_linelikes(name: &str) -> LayerWeb { - console_log!("Canvas is {:?}", canvas()); let layer = canvas().random_linelikes(name); canvas().add_or_replace_layer(layer); LayerWeb { @@ -180,6 +191,31 @@ pub fn random_linelikes(name: &str) -> LayerWeb { } } +fn document() -> web_sys::Document { + let window = web_sys::window().expect_throw("no global `window` exists"); + window + .document() + .expect_throw("should have a document on window") +} + +fn query_selector(selector: String) -> web_sys::Element { + document() + .query_selector(&selector) + .expect_throw(&format!("selector '{}' not found", selector)) + .expect_throw("could not get the element, but is was found (shouldn't happen)") +} + +fn append_new_div_inside(content: String, selector: String) -> () { + let output = document().create_element("div").unwrap(); + output.set_class_name("frame"); + output.set_inner_html(&content); + query_selector(selector).append_child(&output).unwrap(); +} + +fn replace_content_with(content: String, selector: String) -> () { + query_selector(selector).set_inner_html(&content); +} + #[wasm_bindgen(getter_with_clone)] pub struct LayerWeb { pub name: String, @@ -192,19 +228,11 @@ impl LayerWeb { } pub fn render_into(&self, selector: String) -> () { - let window = web_sys::window().expect_throw("no global `window` exists"); - let document = window - .document() - .expect_throw("should have a document on window"); - let element = document - .query_selector(&selector) - .expect_throw(&format!("selector '{}' not found", selector)) - .expect_throw("could not get the element, but is was found (shouldn't happen)"); - - let output = document.create_element("div").unwrap(); - output.set_class_name("frame"); - output.set_inner_html(&self.render()); - element.append_child(&output).unwrap(); + append_new_div_inside(self.render(), selector) + } + + pub fn render_at(self, selector: String) -> () { + replace_content_with(self.render(), selector) } pub fn paint_all(&self, color: Color, opacity: Option, filter: Filter) -> () { diff --git a/web/index.html b/web/index.html index f42ce81..2bd0750 100644 --- a/web/index.html +++ b/web/index.html @@ -4,9 +4,6 @@ Shapemaker Web - - -