This repository has no description
1#![allow(unused)]
2
3use std::sync::Mutex;
4
5use once_cell::sync::Lazy;
6use rand::SeedableRng;
7use rand::rngs::SmallRng;
8use wasm_bindgen::prelude::wasm_bindgen;
9use wasm_bindgen::{JsValue, UnwrapThrowExt};
10
11use crate::{
12 Canvas, Color, ColorMapping, Fill, Filter, Layer, Object, Point,
13 SVGRenderable,
14};
15
16use super::LayerWeb;
17
18static WEB_CANVAS: Lazy<Mutex<Canvas>> =
19 Lazy::new(|| Mutex::new(Canvas::default_settings()));
20
21pub(super) static mut RNG: Lazy<SmallRng> =
22 Lazy::new(|| SmallRng::seed_from_u64(0xCAFE));
23
24pub(super) fn canvas() -> std::sync::MutexGuard<'static, Canvas> {
25 WEB_CANVAS.lock().unwrap()
26}
27
28// Can't bind Color.name directly, see https://github.com/rustwasm/wasm-bindgen/issues/1715
29#[wasm_bindgen]
30pub fn color_name(c: Color) -> String {
31 c.name()
32}
33
34#[wasm_bindgen]
35extern "C" {
36 #[wasm_bindgen(js_namespace = console)]
37 pub fn log(s: &str);
38}
39
40macro_rules! console_log {
41 ($($t:tt)*) => (crate::log(&format_args!($($t)*).to_string()))
42}
43
44#[wasm_bindgen]
45pub fn render_image(opacity: f32, color: Color) -> Result<(), JsValue> {
46 let mut canvas = Canvas::with_colors(ColorMapping {
47 black: "#ffffff".into(),
48 white: "#ffffff".into(),
49 red: "#cf0a2b".into(),
50 green: "#22e753".into(),
51 blue: "#2734e6".into(),
52 yellow: "#f8e21e".into(),
53 orange: "#f05811".into(),
54 purple: "#6a24ec".into(),
55 brown: "#a05634".into(),
56 pink: "#e92e76".into(),
57 gray: "#81a0a8".into(),
58 cyan: "#4fecec".into(),
59 });
60
61 *WEB_CANVAS.lock().unwrap() = canvas;
62 render_canvas_at(String::from("body"));
63
64 Ok(())
65}
66
67#[wasm_bindgen]
68pub fn map_to_midi_controller() {}
69
70#[wasm_bindgen]
71pub fn render_canvas_into(selector: String) {
72 append_new_div_inside(render_canvas(), selector)
73}
74
75#[wasm_bindgen]
76pub fn render_canvas_at(selector: String) {
77 replace_content_with(render_canvas(), selector);
78}
79
80#[wasm_bindgen]
81pub enum MidiEvent {
82 Note,
83 ControlChange,
84}
85
86#[wasm_bindgen]
87pub struct MidiEventData([u8; 3]);
88
89#[wasm_bindgen]
90pub struct MidiPitch(u8);
91
92#[wasm_bindgen]
93impl MidiPitch {
94 pub fn octave(&self) -> u8 {
95 self.0 / 12
96 }
97}
98
99pub struct Percentage(pub f32);
100
101impl From<u8> for Percentage {
102 fn from(value: u8) -> Self {
103 Self(value as f32 / 127.0)
104 }
105}
106
107pub enum MidiMessage {
108 NoteOn(MidiPitch, Percentage),
109 NoteOff(MidiPitch),
110 PedalOn,
111 PedalOff,
112 ControlChange(u8, Percentage),
113}
114
115impl From<(MidiEvent, MidiEventData)> for MidiMessage {
116 fn from(value: (MidiEvent, MidiEventData)) -> Self {
117 match value {
118 (MidiEvent::Note, MidiEventData([pitch, velocity, _])) => {
119 if velocity == 0 {
120 MidiMessage::NoteOff(MidiPitch(pitch))
121 } else {
122 MidiMessage::NoteOn(MidiPitch(pitch), velocity.into())
123 }
124 }
125 (MidiEvent::ControlChange, MidiEventData([64, value, _])) => {
126 if value == 0 {
127 MidiMessage::PedalOff
128 } else {
129 MidiMessage::PedalOn
130 }
131 }
132 (MidiEvent::ControlChange, MidiEventData([_, controller, value])) => {
133 MidiMessage::ControlChange(controller, value.into())
134 }
135 }
136 }
137}
138
139#[wasm_bindgen]
140pub fn render_canvas() -> String {
141 let can = canvas();
142 can.render_to_svg(
143 can.colormap.clone(),
144 can.cell_size,
145 can.object_sizes,
146 "web_root_canvas",
147 )
148 .unwrap_throw()
149 .to_string()
150}
151
152#[wasm_bindgen]
153pub fn set_palette(palette: ColorMapping) {
154 canvas().colormap = palette;
155}
156
157#[wasm_bindgen]
158pub fn get_layer(name: &str) -> Result<LayerWeb, JsValue> {
159 match canvas().layer(name) {
160 Ok(layer) => Ok(LayerWeb {
161 name: layer.name.clone(),
162 }),
163 Err(_) => Err(JsValue::from_str("Layer not found")),
164 }
165}
166
167#[wasm_bindgen]
168pub fn random_linelikes(name: &str) -> LayerWeb {
169 unsafe {
170 #[allow(static_mut_refs)]
171 let layer = canvas().random_linelikes(&mut RNG, name);
172 }
173 LayerWeb {
174 name: name.to_string(),
175 }
176}
177
178fn document() -> web_sys::Document {
179 let window = web_sys::window().expect_throw("no global `window` exists");
180 window
181 .document()
182 .expect_throw("should have a document on window")
183}
184
185fn query_selector(selector: String) -> web_sys::Element {
186 document()
187 .query_selector(&selector)
188 .expect_throw(&format!("selector '{}' not found", selector))
189 .expect_throw(
190 "could not get the element, but is was found (shouldn't happen)",
191 )
192}
193
194pub(super) fn append_new_div_inside(content: String, selector: String) {
195 let output = document().create_element("div").unwrap();
196 output.set_class_name("frame");
197 output.set_inner_html(&content);
198 query_selector(selector).append_child(&output).unwrap();
199}
200
201pub(super) fn replace_content_with(content: String, selector: String) {
202 query_selector(selector).set_inner_html(&content);
203}