the things are changing
This commit is contained in:
parent
b732e1ff3d
commit
866f979c0c
12 changed files with 328 additions and 131 deletions
63
Cargo.lock
generated
63
Cargo.lock
generated
|
@ -86,15 +86,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "approx"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278"
|
|
||||||
dependencies = [
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayref"
|
name = "arrayref"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
|
@ -245,16 +236,6 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cgmath"
|
|
||||||
version = "0.18.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317"
|
|
||||||
dependencies = [
|
|
||||||
"approx",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codespan-reporting"
|
name = "codespan-reporting"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
|
@ -291,12 +272,6 @@ dependencies = [
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "const_panic"
|
|
||||||
version = "0.2.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
|
@ -452,38 +427,6 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "encase"
|
|
||||||
version = "0.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8fce2eeef77fd4a293a54b62aa00ac9daebfbcda4bf8998c5a815635b004aa1c"
|
|
||||||
dependencies = [
|
|
||||||
"cgmath",
|
|
||||||
"const_panic",
|
|
||||||
"encase_derive",
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "encase_derive"
|
|
||||||
version = "0.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0e520cde08cbf4f7cc097f61573ec06ce467019803de8ae82fb2823fa1554a0e"
|
|
||||||
dependencies = [
|
|
||||||
"encase_derive_impl",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "encase_derive_impl"
|
|
||||||
version = "0.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3fe2568f851fd6144a45fa91cfed8fe5ca8fc0b56ba6797bfc1ed2771b90e37c"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.18",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "env_logger"
|
name = "env_logger"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
@ -818,8 +761,6 @@ checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
|
||||||
name = "libcxgraph"
|
name = "libcxgraph"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cgmath",
|
|
||||||
"encase",
|
|
||||||
"lalrpop",
|
"lalrpop",
|
||||||
"lalrpop-util",
|
"lalrpop-util",
|
||||||
"log",
|
"log",
|
||||||
|
@ -1258,9 +1199,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.59"
|
version = "1.0.60"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
|
checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use libcxgraph::{renderer::WgpuState, language::compile};
|
use libcxgraph::{renderer::WgpuState, language::compile};
|
||||||
use winit::{event_loop::EventLoop, window::Window, event::{Event, WindowEvent}};
|
use winit::{event_loop::EventLoop, window::Window, event::{Event, WindowEvent}};
|
||||||
|
|
||||||
|
@ -8,7 +10,7 @@ fn main() {
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
let src = "plot(z) = 27^z - 9^z - 3^z";
|
let src = "plot(z) = 27^z - 9^z - 3^z";
|
||||||
let wgsl = compile(src).unwrap();
|
let wgsl = compile(src, &HashMap::new()).unwrap();
|
||||||
println!("{wgsl}");
|
println!("{wgsl}");
|
||||||
|
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new();
|
||||||
|
@ -24,8 +26,9 @@ async fn run(event_loop: EventLoop<()>, window: Window, code: &str) {
|
||||||
|
|
||||||
state.load_shaders(code);
|
state.load_shaders(code);
|
||||||
|
|
||||||
state.set_bounds((-5.0, -5.0), (5.0, 5.0));
|
state.uniforms.bounds_min = (-5.0, -5.0).into();
|
||||||
state.set_shading_intensity(0.05);
|
state.uniforms.bounds_max = ( 5.0, 5.0).into();
|
||||||
|
state.uniforms.shading_intensity = 0.3;
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
control_flow.set_wait();
|
control_flow.set_wait();
|
||||||
|
|
|
@ -10,13 +10,64 @@
|
||||||
<canvas id="grid_canvas"></canvas>
|
<canvas id="grid_canvas"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="menu">
|
<div class="menu"><details open>
|
||||||
<input id="menu_checkbox" type="checkbox" checked>
|
<summary>Menu</summary>
|
||||||
<div class="menu-inner" id="menu_inner">
|
<div><input type="button" id="button_graph" value="Graph"></div>
|
||||||
<div><input type="button" id="button_graph" value="Graph"></div>
|
|
||||||
<textarea id="source_text">f(z) = z^2 + 3i plot(z) = 5z^2 + f(1/z) - 1</textarea>
|
<details>
|
||||||
</div>
|
<summary>Options</summary>
|
||||||
</div>
|
|
||||||
|
<div>
|
||||||
|
<div><label for="range_resolution">Resolution</label></div>
|
||||||
|
<input type="range" id="range_resolution" name="resolution" min="-2" max="2" step="1" value="0">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div><label for="range_shading">Shading intensity</label></div>
|
||||||
|
<input type="range" id="range_shading" min="0" max="1" step="0.01" value="0.3">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div><label for="range_contour">Contour intensity</label></div>
|
||||||
|
<input type="range" id="range_contour" min="0" max="1" step="0.01" value="0.0">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" class="decor" id="checkbox_decor_1" data-value="1">
|
||||||
|
<label for="checkbox_decor_1">Real contours</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" class="decor" id="checkbox_decor_2" data-value="2">
|
||||||
|
<label for="checkbox_decor_2">Imaginary contours</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" class="decor" id="checkbox_decor_4" data-value="4">
|
||||||
|
<label for="checkbox_decor_4">Argument contours</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" class="decor" id="checkbox_decor_8" data-value="8">
|
||||||
|
<label for="checkbox_decor_8">Magnitude contours</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div>Coloring</div>
|
||||||
|
<input type="radio" name="color_mode" id="radio_color_0" data-value="0" checked>
|
||||||
|
<label for="radio_color_0">Standard</label><br>
|
||||||
|
<input type="radio" name="color_mode" id="radio_color_1" data-value="1">
|
||||||
|
<label for="radio_color_1">Uniform</label><br>
|
||||||
|
<input type="radio" name="color_mode" id="radio_color_2" data-value="2">
|
||||||
|
<label for="radio_color_2">None</label>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<div id="div_error_msg" hidden></div>
|
||||||
|
<div><textarea id="source_text">f(z) = z^2 + 3i plot(z) = 5z^2 + f(1/z) - 1</textarea></div>
|
||||||
|
</div></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import('./index.js').then(m => window.module = m)
|
import('./index.js').then(m => window.module = m)
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
menu_checkbox.addEventListener("change", () => {
|
|
||||||
menu_inner.hidden = !menu_checkbox.checked;
|
|
||||||
});
|
|
||||||
|
|
||||||
import init, * as cxgraph from "./pkg/cxgraph_web.js";
|
import init, * as cxgraph from "./pkg/cxgraph_web.js";
|
||||||
await init();
|
await init();
|
||||||
|
|
||||||
|
@ -9,6 +5,7 @@ let graphView = {
|
||||||
xoff: 0,
|
xoff: 0,
|
||||||
yoff: 0,
|
yoff: 0,
|
||||||
scale: 3,
|
scale: 3,
|
||||||
|
res_mult: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
function redraw() {
|
function redraw() {
|
||||||
|
@ -31,7 +28,7 @@ function onViewChange() {
|
||||||
function onResize() {
|
function onResize() {
|
||||||
let width = window.innerWidth;
|
let width = window.innerWidth;
|
||||||
let height = window.innerHeight;
|
let height = window.innerHeight;
|
||||||
cxgraph.resize(width, height);
|
cxgraph.resize(width*graphView.res_mult, height*graphView.res_mult);
|
||||||
grid_canvas.width = width;
|
grid_canvas.width = width;
|
||||||
grid_canvas.height = height;
|
grid_canvas.height = height;
|
||||||
canvas.style.width = "100vw";
|
canvas.style.width = "100vw";
|
||||||
|
@ -72,8 +69,15 @@ function onMouseMove(e) {
|
||||||
|
|
||||||
function onGraph() {
|
function onGraph() {
|
||||||
let src = document.getElementById("source_text").value;
|
let src = document.getElementById("source_text").value;
|
||||||
cxgraph.load_shader(src);
|
try {
|
||||||
redraw();
|
cxgraph.load_shader(src);
|
||||||
|
div_error_msg.hidden = true;
|
||||||
|
redraw();
|
||||||
|
} catch(e) {
|
||||||
|
console.log(e);
|
||||||
|
div_error_msg.textContent = e.toString();
|
||||||
|
div_error_msg.hidden = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("resize", onResize);
|
window.addEventListener("resize", onResize);
|
||||||
|
@ -83,6 +87,50 @@ canvas.addEventListener("mouseup", onMouseUp);
|
||||||
canvas.addEventListener("mousemove", onMouseMove);
|
canvas.addEventListener("mousemove", onMouseMove);
|
||||||
button_graph.addEventListener("click", onGraph);
|
button_graph.addEventListener("click", onGraph);
|
||||||
|
|
||||||
|
let class_decor = document.getElementsByClassName("decor")
|
||||||
|
for(let e of class_decor) {
|
||||||
|
e.addEventListener("change", () => {
|
||||||
|
let decor = 0;
|
||||||
|
for(let elem of class_decor) {
|
||||||
|
if(elem.checked) {
|
||||||
|
decor += parseInt(elem.getAttribute("data-value"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cxgraph.set_decorations(decor);
|
||||||
|
redraw();
|
||||||
|
});
|
||||||
|
e.checked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name_color_mode = document.getElementsByName("color_mode");
|
||||||
|
for(let e of name_color_mode) {
|
||||||
|
e.addEventListener("change", () => {
|
||||||
|
let selected = document.querySelector("input[name=color_mode]:checked");
|
||||||
|
cxgraph.set_coloring(parseInt(selected.getAttribute("data-value")));
|
||||||
|
redraw();
|
||||||
|
});
|
||||||
|
e.checked = false;
|
||||||
|
}
|
||||||
|
name_color_mode[0].checked = true;
|
||||||
|
|
||||||
|
|
||||||
|
range_shading.addEventListener("change", () => {
|
||||||
|
let value = parseFloat(range_shading.value);
|
||||||
|
cxgraph.set_shading_intensity(value);
|
||||||
|
redraw();
|
||||||
|
});
|
||||||
|
|
||||||
|
range_contour.addEventListener("change", () => {
|
||||||
|
let value = parseFloat(range_contour.value);
|
||||||
|
cxgraph.set_contour_intensity(value);
|
||||||
|
redraw();
|
||||||
|
});
|
||||||
|
|
||||||
|
range_resolution.addEventListener("change", () => {
|
||||||
|
graphView.res_mult = Math.pow(2, parseFloat(range_resolution.value));
|
||||||
|
onResize();
|
||||||
|
});
|
||||||
|
|
||||||
onResize();
|
onResize();
|
||||||
onGraph();
|
onGraph();
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use libcxgraph::{renderer::WgpuState, language::compile};
|
use libcxgraph::{renderer::WgpuState, language::compile};
|
||||||
use winit::{window::WindowBuilder, event_loop::EventLoop, platform::web::WindowBuilderExtWebSys};
|
use winit::{window::WindowBuilder, event_loop::EventLoop, platform::web::WindowBuilderExtWebSys};
|
||||||
use wasm_bindgen::{prelude::*, JsValue};
|
use wasm_bindgen::{prelude::*, JsValue};
|
||||||
|
@ -41,14 +43,16 @@ pub async fn start() {
|
||||||
|
|
||||||
let size = window.inner_size();
|
let size = window.inner_size();
|
||||||
let mut state = WgpuState::new(&window, size.into()).await;
|
let mut state = WgpuState::new(&window, size.into()).await;
|
||||||
state.set_bounds((-5.0, -5.0), (5.0, 5.0));
|
state.uniforms.bounds_min = (-5.0, -5.0).into();
|
||||||
state.set_shading_intensity(0.01);
|
state.uniforms.bounds_max = ( 5.0, 5.0).into();
|
||||||
|
state.uniforms.shading_intensity = 0.3;
|
||||||
|
state.uniforms.contour_intensity = 0.0;
|
||||||
unsafe { WGPU_STATE = Some(state) };
|
unsafe { WGPU_STATE = Some(state) };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn load_shader(src: &str) -> Result<(), JsValue> {
|
pub fn load_shader(src: &str) -> Result<(), JsValue> {
|
||||||
let wgsl = compile(src).map_err(|e| e.to_string())?;
|
let wgsl = compile(src, &HashMap::new()).map_err(|e| e.to_string())?;
|
||||||
with_state(|state| state.load_shaders(&wgsl));
|
with_state(|state| state.load_shaders(&wgsl));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -65,10 +69,29 @@ pub fn resize(width: u32, height: u32) {
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn set_bounds(min_x: f32, min_y: f32, max_x: f32, max_y: f32) {
|
pub fn set_bounds(min_x: f32, min_y: f32, max_x: f32, max_y: f32) {
|
||||||
with_state(|state| state.set_bounds((min_x, min_y), (max_x, max_y)));
|
with_state(|state| {
|
||||||
|
state.uniforms.bounds_min = (min_x, min_y).into();
|
||||||
|
state.uniforms.bounds_max = (max_x, max_y).into();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn set_shading_intensity(value: f32) {
|
pub fn set_shading_intensity(value: f32) {
|
||||||
with_state(|state| state.set_shading_intensity(value));
|
with_state(|state| state.uniforms.shading_intensity = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn set_contour_intensity(value: f32) {
|
||||||
|
with_state(|state| state.uniforms.contour_intensity = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn set_coloring(value: u32) {
|
||||||
|
with_state(|state| state.uniforms.coloring = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn set_decorations(value: u32) {
|
||||||
|
with_state(|state| state.uniforms.decorations = value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
* {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
body, html {
|
body, html {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -39,8 +43,23 @@ canvas {
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#source_text {
|
#source_text {
|
||||||
|
min-width: max-content;
|
||||||
width: 300px;
|
width: 300px;
|
||||||
height: 500px;
|
height: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#div_error_msg {
|
||||||
|
color: #f9a;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.menu > details {
|
||||||
|
max-width: min-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
details > *:not(summary) {
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
padding-left: 8px;
|
||||||
|
border-left: 1px solid #fff;
|
||||||
|
}
|
||||||
|
|
|
@ -12,8 +12,6 @@ log = "0.4"
|
||||||
lalrpop-util = { version = "0.20.0", features = ["lexer", "unicode"] }
|
lalrpop-util = { version = "0.20.0", features = ["lexer", "unicode"] }
|
||||||
num-complex = "0.4"
|
num-complex = "0.4"
|
||||||
wgpu = "0.16"
|
wgpu = "0.16"
|
||||||
cgmath = "0.18"
|
|
||||||
encase = { version = "0.6", features = ["cgmath"] }
|
|
||||||
raw-window-handle = "0.5"
|
raw-window-handle = "0.5"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{collections::{HashSet, HashMap}, fmt};
|
use std::{collections::{HashSet, HashMap}, fmt};
|
||||||
|
|
||||||
use super::{ast::{Definition, Expression, ExpressionType, BinaryOp, UnaryOp}, builtins::{BUILTIN_CONSTS, BUILTIN_FUNCS}};
|
use super::{ast::{Definition, Expression, ExpressionType, BinaryOp, UnaryOp}, builtins::{BUILTIN_CONSTS, BUILTIN_FUNCS}, Variable};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CompileError(String);
|
pub struct CompileError(String);
|
||||||
|
@ -83,6 +83,7 @@ fn format_tmp(idx: usize) -> String { format!("tmp_{}", idx) }
|
||||||
|
|
||||||
pub struct Compiler<'w, 'i, W: fmt::Write> {
|
pub struct Compiler<'w, 'i, W: fmt::Write> {
|
||||||
buf: &'w mut W,
|
buf: &'w mut W,
|
||||||
|
vars: &'w HashMap<String, Variable>,
|
||||||
global_funcs: HashMap<&'i str, usize>,
|
global_funcs: HashMap<&'i str, usize>,
|
||||||
global_consts: HashSet<&'i str>,
|
global_consts: HashSet<&'i str>,
|
||||||
}
|
}
|
||||||
|
@ -109,9 +110,10 @@ impl<'i> LocalState<'i> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'w, 'i, W: fmt::Write> Compiler<'w, 'i, W> {
|
impl<'w, 'i, W: fmt::Write> Compiler<'w, 'i, W> {
|
||||||
pub fn new(buf: &'w mut W) -> Self {
|
pub fn new(buf: &'w mut W, vars: &'w HashMap<String, Variable>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
buf,
|
buf,
|
||||||
|
vars,
|
||||||
global_consts: HashSet::new(),
|
global_consts: HashSet::new(),
|
||||||
global_funcs: HashMap::new(),
|
global_funcs: HashMap::new(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use lalrpop_util::lalrpop_mod;
|
use lalrpop_util::lalrpop_mod;
|
||||||
|
|
||||||
use crate::language::token::Lexer;
|
use crate::language::token::Lexer;
|
||||||
|
@ -11,13 +13,18 @@ mod builtins;
|
||||||
|
|
||||||
lalrpop_mod!(pub syntax, "/language/syntax.rs");
|
lalrpop_mod!(pub syntax, "/language/syntax.rs");
|
||||||
|
|
||||||
pub fn compile(src: &str) -> Result<String, Box<dyn std::error::Error>> {
|
pub enum Variable {
|
||||||
|
Slider(usize),
|
||||||
|
Point(usize, usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(src: &str, vars: &HashMap<String, Variable>) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
let lexer = Lexer::new(src);
|
let lexer = Lexer::new(src);
|
||||||
let result = syntax::ProgramParser::new()
|
let result = syntax::ProgramParser::new()
|
||||||
.parse(src, lexer)
|
.parse(src, lexer)
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
let mut wgsl = String::new();
|
let mut wgsl = String::new();
|
||||||
let mut cmp = Compiler::new(&mut wgsl);
|
let mut cmp = Compiler::new(&mut wgsl, vars);
|
||||||
for defn in result {
|
for defn in result {
|
||||||
cmp.compile_defn(&defn)?;
|
cmp.compile_defn(&defn)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ Definition: Definition<'input> = {
|
||||||
// Expressions
|
// Expressions
|
||||||
|
|
||||||
Exprs: Vec<Expression<'input>> = {
|
Exprs: Vec<Expression<'input>> = {
|
||||||
<args:(<Expr> ",")*> <last:Expr?> => args.into_iter().chain(last).collect(),
|
<args:(<Expr> ",")*> <last:Expr> ","? => args.into_iter().chain(std::iter::once(last)).collect(),
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr: Expression<'input> = Store;
|
Expr: Expression<'input> = Store;
|
||||||
|
|
|
@ -3,10 +3,14 @@
|
||||||
////////////////
|
////////////////
|
||||||
|
|
||||||
struct Uniforms {
|
struct Uniforms {
|
||||||
|
variables: array<vec4f, 4>,
|
||||||
resolution: vec2u,
|
resolution: vec2u,
|
||||||
bounds_min: vec2f,
|
bounds_min: vec2f,
|
||||||
bounds_max: vec2f,
|
bounds_max: vec2f,
|
||||||
shading_intensity: f32,
|
shading_intensity: f32,
|
||||||
|
contour_intensity: f32,
|
||||||
|
decoration: u32,
|
||||||
|
coloring: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@group(0) @binding(1) var<uniform> uniforms: Uniforms;
|
@group(0) @binding(1) var<uniform> uniforms: Uniforms;
|
||||||
|
@ -19,12 +23,21 @@ fn remap(val: vec2f, a1: vec2f, b1: vec2f, a2: vec2f, b2: vec2f) -> vec2f {
|
||||||
return a2 + (b2 - a2) * ((val - a1) / (b1 - a1));
|
return a2 + (b2 - a2) * ((val - a1) / (b1 - a1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn correct_mod(x: f32, y: f32) -> f32 {
|
||||||
|
return ((x % y) + y) % y;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn correct_mod2(x: vec2f, y: vec2f) -> vec2f {
|
||||||
|
return ((x % y) + y) % y;
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////
|
/////////////////
|
||||||
// constants //
|
// constants //
|
||||||
/////////////////
|
/////////////////
|
||||||
|
|
||||||
const TAU = 6.283185307179586;
|
const TAU = 6.283185307179586;
|
||||||
const E = 2.718281828459045;
|
const E = 2.718281828459045;
|
||||||
|
const RECIP_SQRT2 = 0.7071067811865475;
|
||||||
|
|
||||||
const C_TAU = vec2f(TAU, 0.0);
|
const C_TAU = vec2f(TAU, 0.0);
|
||||||
const C_E = vec2f(E, 0.0);
|
const C_E = vec2f(E, 0.0);
|
||||||
|
@ -51,7 +64,7 @@ fn c_abs_sq(z: vec2f) -> vec2f {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn c_abs(z: vec2f) -> vec2f {
|
fn c_abs(z: vec2f) -> vec2f {
|
||||||
return vec2(sqrt(dot(z, z)), 0.0);
|
return vec2(length(z), 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn c_arg(z: vec2f) -> vec2f {
|
fn c_arg(z: vec2f) -> vec2f {
|
||||||
|
@ -202,28 +215,99 @@ fn hsv2rgb(c: vec3f) -> vec3f {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shademap(r: f32) -> f32 {
|
fn shademap(r: f32) -> f32 {
|
||||||
return r*inverseSqrt(r * r + 0.0625 * uniforms.shading_intensity);
|
let i = uniforms.shading_intensity * uniforms.shading_intensity * uniforms.shading_intensity;
|
||||||
|
return r*inverseSqrt(r * r + 0.0625 * i);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn colorfor(w: vec2f) -> vec3f {
|
fn coloring_standard(z: vec2f) -> vec3f {
|
||||||
let z = func_plot(w);
|
|
||||||
|
|
||||||
if z.x != z.x || z.y != z.y {
|
if z.x != z.x || z.y != z.y {
|
||||||
return vec3f(0.5, 0.5, 0.5);
|
return vec3f(0.5, 0.5, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
let r = sqrt(z.x*z.x + z.y*z.y);
|
let r = length(z);
|
||||||
let arg = atan2(z.y, z.x);
|
let arg = atan2(z.y, z.x);
|
||||||
let hsv = vec3f(arg / TAU + 1.0, shademap(1.0/r), shademap(r));
|
let hsv = vec3f(arg / TAU + 1.0, shademap(1.0/r), shademap(r));
|
||||||
return pow(hsv2rgb(hsv), vec3(2.0));
|
return hsv2rgb(hsv);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coloring_uniform(z: vec2f) -> vec3f {
|
||||||
|
if z.x != z.x || z.y != z.y {
|
||||||
|
return vec3f(0.5, 0.5, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg = atan2(z.y, z.x);
|
||||||
|
let mag = length(z);
|
||||||
|
|
||||||
|
let r = cos(arg - 0.0*TAU/3.0)*0.5 + 0.5;
|
||||||
|
let g = cos(arg - 1.0*TAU/3.0)*0.5 + 0.5;
|
||||||
|
let b = cos(arg - 2.0*TAU/3.0)*0.5 + 0.5;
|
||||||
|
let hue = vec3(r, g, b);
|
||||||
|
let s = 1.0 - shademap(1.0/mag);
|
||||||
|
let v = 1.0 - shademap(mag);
|
||||||
|
return hue * (1.0 - s - v) + s;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coloring_none(z: vec2f) -> vec3f {
|
||||||
|
return vec3f(0.5, 0.5, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decoration_contour_re(z: vec2f) -> f32 {
|
||||||
|
return correct_mod(floor(z.x), 2.0) * 2.0 - 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decoration_contour_im(z: vec2f) -> f32 {
|
||||||
|
return correct_mod(floor(z.y), 2.0) * 2.0 - 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decoration_contour_arg(z: vec2f) -> f32 {
|
||||||
|
let arg = atan2(z.y, z.x);
|
||||||
|
return round(correct_mod(arg + TAU, TAU/8.0) * 8.0/TAU) * 2.0 - 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decoration_contour_mag(z: vec2f) -> f32 {
|
||||||
|
let logmag = 0.5 * log2(z.x*z.x + z.y*z.y);
|
||||||
|
return round(correct_mod(0.5 * logmag, 1.0)) * 2.0 - 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn main(@builtin(position) in: vec4f) -> @location(0) vec4f {
|
fn main(@builtin(position) in: vec4f) -> @location(0) vec4f {
|
||||||
let pos = vec2(in.x, f32(uniforms.resolution.y) - in.y);
|
let pos = vec2(in.x, f32(uniforms.resolution.y) - in.y);
|
||||||
let z = remap(pos, vec2(0.0), vec2f(uniforms.resolution), uniforms.bounds_min, uniforms.bounds_max);
|
let w = remap(pos, vec2(0.0), vec2f(uniforms.resolution), uniforms.bounds_min, uniforms.bounds_max);
|
||||||
|
|
||||||
let col = colorfor(z);
|
let z = func_plot(w);
|
||||||
|
|
||||||
return vec4f(col, 1.0);
|
var col = vec3f();
|
||||||
|
switch uniforms.coloring {
|
||||||
|
case 0u, default: {
|
||||||
|
col = coloring_standard(z);
|
||||||
|
}
|
||||||
|
case 1u: {
|
||||||
|
col = coloring_uniform(z);
|
||||||
|
}
|
||||||
|
case 2u: {
|
||||||
|
col = coloring_none(z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var contours = 1.0;
|
||||||
|
|
||||||
|
if (uniforms.decoration & 0x01u) != 0u {
|
||||||
|
contours *= decoration_contour_re(z);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uniforms.decoration & 0x02u) != 0u {
|
||||||
|
contours *= decoration_contour_im(z);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uniforms.decoration & 0x04u) != 0u {
|
||||||
|
contours *= decoration_contour_arg(z);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uniforms.decoration & 0x08u) != 0u {
|
||||||
|
contours *= decoration_contour_mag(z);
|
||||||
|
}
|
||||||
|
|
||||||
|
let final_col = mix(col, vec3f(contours * 0.5 + 0.5), uniforms.contour_intensity);
|
||||||
|
|
||||||
|
return vec4f(pow(final_col, vec3(1.68)), 1.0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,45 @@
|
||||||
use std::num::NonZeroU64;
|
use std::{num::NonZeroU64, io::Cursor};
|
||||||
|
|
||||||
use encase::{ShaderType, ShaderSize, UniformBuffer};
|
use log::info;
|
||||||
use log::warn;
|
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
type Vec2u = cgmath::Vector2<u32>;
|
#[derive(Debug)]
|
||||||
type Vec2f = cgmath::Vector2<f32>;
|
#[repr(C)]
|
||||||
|
pub struct Uniforms {
|
||||||
#[derive(ShaderType)]
|
pub variables: [f32; 16],
|
||||||
struct Uniforms {
|
pub resolution: (u32, u32),
|
||||||
pub resolution: Vec2u,
|
pub bounds_min: (f32, f32),
|
||||||
pub bounds_min: Vec2f,
|
pub bounds_max: (f32, f32),
|
||||||
pub bounds_max: Vec2f,
|
|
||||||
pub shading_intensity: f32,
|
pub shading_intensity: f32,
|
||||||
|
pub contour_intensity: f32,
|
||||||
|
pub decorations: u32,
|
||||||
|
pub coloring: u32,
|
||||||
|
_padding: [u8; 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
const UNIFORM_SIZE: usize = std::mem::size_of::<Uniforms>();
|
||||||
|
|
||||||
|
impl Uniforms {
|
||||||
|
pub fn encode(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> {
|
||||||
|
for var in self.variables {
|
||||||
|
buf.write_all(&var.to_le_bytes())?;
|
||||||
|
}
|
||||||
|
buf.write_all(&self.resolution.0.to_le_bytes())?;
|
||||||
|
buf.write_all(&self.resolution.1.to_le_bytes())?;
|
||||||
|
buf.write_all(&self.bounds_min.0.to_le_bytes())?;
|
||||||
|
buf.write_all(&self.bounds_min.1.to_le_bytes())?;
|
||||||
|
buf.write_all(&self.bounds_max.0.to_le_bytes())?;
|
||||||
|
buf.write_all(&self.bounds_max.1.to_le_bytes())?;
|
||||||
|
buf.write_all(&self.shading_intensity.to_le_bytes())?;
|
||||||
|
buf.write_all(&self.contour_intensity.to_le_bytes())?;
|
||||||
|
buf.write_all(&self.decorations.to_le_bytes())?;
|
||||||
|
buf.write_all(&self.coloring.to_le_bytes())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WgpuState {
|
pub struct WgpuState {
|
||||||
uniforms: Uniforms,
|
pub uniforms: Uniforms,
|
||||||
surface: wgpu::Surface,
|
surface: wgpu::Surface,
|
||||||
device: wgpu::Device,
|
device: wgpu::Device,
|
||||||
config: wgpu::SurfaceConfiguration,
|
config: wgpu::SurfaceConfiguration,
|
||||||
|
@ -67,21 +90,21 @@ impl WgpuState {
|
||||||
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
contents: &[0; Uniforms::SHADER_SIZE.get() as usize],
|
contents: &[0; UNIFORM_SIZE],
|
||||||
});
|
});
|
||||||
|
|
||||||
let uniform_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
let uniform_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
entries: &[
|
entries: &[
|
||||||
wgpu::BindGroupLayoutEntry {
|
wgpu::BindGroupLayoutEntry {
|
||||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
binding: 1,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
ty: wgpu::BindingType::Buffer {
|
ty: wgpu::BindingType::Buffer {
|
||||||
ty: wgpu::BufferBindingType::Uniform,
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
has_dynamic_offset: false,
|
has_dynamic_offset: false,
|
||||||
min_binding_size: None,
|
min_binding_size: None,
|
||||||
},
|
},
|
||||||
count: None,
|
count: None,
|
||||||
binding: 1,
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
@ -95,7 +118,7 @@ impl WgpuState {
|
||||||
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
||||||
buffer: &uniform_buffer,
|
buffer: &uniform_buffer,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
size: Some(NonZeroU64::new(Uniforms::SHADER_SIZE.get()).unwrap()),
|
size: Some(NonZeroU64::new(UNIFORM_SIZE as u64).unwrap()),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -104,10 +127,15 @@ impl WgpuState {
|
||||||
// Done //
|
// Done //
|
||||||
|
|
||||||
let uniforms = Uniforms {
|
let uniforms = Uniforms {
|
||||||
|
variables: [0.0; 16],
|
||||||
resolution: size.into(),
|
resolution: size.into(),
|
||||||
bounds_min: [-0.0, -0.0].into(),
|
bounds_min: (-0.0, -0.0),
|
||||||
bounds_max: [ 0.0, 0.0].into(),
|
bounds_max: ( 0.0, 0.0),
|
||||||
shading_intensity: 0.0,
|
shading_intensity: 0.0,
|
||||||
|
contour_intensity: 0.0,
|
||||||
|
decorations: 0,
|
||||||
|
coloring: 0,
|
||||||
|
_padding: [0; 8],
|
||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -205,9 +233,11 @@ impl WgpuState {
|
||||||
rpass.draw(1..4, 0..1);
|
rpass.draw(1..4, 0..1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut uniform_buffer = UniformBuffer::new([0; Uniforms::SHADER_SIZE.get() as usize]);
|
info!("Redrawing");
|
||||||
uniform_buffer.write(&self.uniforms).unwrap();
|
info!("Uniforms: {:?}", self.uniforms);
|
||||||
self.queue.write_buffer(&self.uniform_buffer, 0, &uniform_buffer.into_inner());
|
let mut cursor = Cursor::new([0; UNIFORM_SIZE]);
|
||||||
|
self.uniforms.encode(&mut cursor).unwrap();
|
||||||
|
self.queue.write_buffer(&self.uniform_buffer, 0, &cursor.into_inner());
|
||||||
self.queue.submit(Some(encoder.finish()));
|
self.queue.submit(Some(encoder.finish()));
|
||||||
frame.present();
|
frame.present();
|
||||||
}
|
}
|
||||||
|
@ -219,13 +249,4 @@ impl WgpuState {
|
||||||
self.surface.configure(&self.device, &self.config);
|
self.surface.configure(&self.device, &self.config);
|
||||||
self.uniforms.resolution = size.into();
|
self.uniforms.resolution = size.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_bounds(&mut self, min: (f32, f32), max: (f32, f32)) {
|
|
||||||
self.uniforms.bounds_min = min.into();
|
|
||||||
self.uniforms.bounds_max = max.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_shading_intensity(&mut self, value: f32) {
|
|
||||||
self.uniforms.shading_intensity = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue