From 02ce35e24fefe08dc38011ff777a014a533d7f62 Mon Sep 17 00:00:00 2001 From: "A.Olokhtonov" Date: Sun, 21 Jul 2024 16:02:25 +0300 Subject: [PATCH] Proper map controls (pan + zoom) --- .gitignore | 1 + geometry.js | 20 ++++++++++++--- input.js | 67 ++++++++++++++++++++++++++++++++++++------------- math.js | 14 +++++++++++ render.js | 72 +++++++++++++++++++++++++++++++++++++++++++++-------- 5 files changed, 143 insertions(+), 31 deletions(-) create mode 100644 .gitignore create mode 100644 math.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..397b4a7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.log diff --git a/geometry.js b/geometry.js index 51cc80c..631eb7c 100644 --- a/geometry.js +++ b/geometry.js @@ -1,6 +1,10 @@ let colors = {}; function get_color(stage_name) { + if (stage_name in config.predefined_colors) { + return config.predefined_colors[stage_name]; + } + if (stage_name in colors) { return colors[stage_name]; } @@ -9,7 +13,7 @@ function get_color(stage_name) { const g = Math.floor(Math.random() * 155); const b = Math.floor(Math.random() * 155); - colors[stage_name] = { 'r': 100 + r, 'g': 100 + g, 'b': 100 + b }; + colors[stage_name] = [ 100 + r, 100 + g, 100 + b ]; return colors[stage_name]; } @@ -45,13 +49,23 @@ function generate(trace_id) { stage_cycles = instruction.retcyc - stage.c; } - const color = get_color(stage.name); + let [r, g, b] = get_color(stage.name); + + if (!instruction.retired) { + r = Math.max(50, r - 50); + g = Math.max(50, g - 50); + b = Math.max(50, b - 50); + } sizes.push(stage_cycles * config.w, 1 * config.h); positions.push(config.w * stage.c, config.h * y); - colors.push(color.r, color.g, color.b, 255); + colors.push(r, g, b, 255); result.count++; + + if (config.limit > 0 && result.count >= config.limit) { + break; + } } ++y; diff --git a/input.js b/input.js index fd4c47c..ad8dc36 100644 --- a/input.js +++ b/input.js @@ -1,7 +1,13 @@ function init_listeners() { - document.querySelector('.main').addEventListener('dragover', cancel); - document.querySelector('.main').addEventListener('drop', drop); - document.querySelector('.main').addEventListener('click', debug); + document.querySelector('#c').addEventListener('dragover', cancel); + document.querySelector('#c').addEventListener('drop', drop); + document.querySelector('#c').addEventListener('click', debug); + + document.querySelector('#c').addEventListener('mousedown', mousedown); + document.querySelector('#c').addEventListener('mousemove', mousemove); + document.querySelector('#c').addEventListener('mouseup', mouseup); + document.querySelector('#c').addEventListener('mouseleave', mouseup); + document.querySelector('#c').addEventListener('wheel', wheel); window.addEventListener('keydown', keydown); window.addEventListener('keyup', keyup); @@ -16,24 +22,49 @@ function cancel(e) { e.stopPropagation(); } -function keydown(e) { - if (e.code === 'ArrowLeft') { - offset.x += 10 / scale; - } else if (e.code === 'ArrowRight') { - offset.x -= 10 / scale; - } else if (e.code === 'ArrowDown') { - offset.y -= 10 / scale; - } else if (e.code === 'ArrowUp') { - offset.y += 10 / scale; - } else if (e.code === 'PageUp') { - scale *= 0.9; - } else if (e.code === 'PageDown') { - scale *= 1.1; +function mousedown(e) { + if (e.button === 1) { + moving = true; + return; + } +} + +function mousemove(e) { + let do_draw = false; + + if (moving) { + offset.x += e.movementX; + offset.y += e.movementY; + do_draw = true; + } + + if (do_draw) { + schedule_draw(); + } +} + +function mouseup(e) { + if (e.button === 1 && moving) { + moving = false; } +} + +function wheel(e) { + const screenp = {'x': window.devicePixelRatio * e.clientX, 'y': window.devicePixelRatio * e.clientY}; + const zooming_in = e.deltaY < 0; + const level = zooming_in ? zoom_level + 2 : zoom_level - 2; + const dz = (zoom_level > 0 ? config.zoom_delta : -config.zoom_delta); + + zoom_level = level; + zoom_target = Math.pow(1.0 + dz, Math.abs(zoom_level)) + zoom_screenp = screenp; schedule_draw(); } +function keydown(e) { +} + function keyup(e) { } @@ -59,7 +90,9 @@ function drop(e) { const upload_finished = () => { const text = fr.result; console.log('Finished. String length:', text.length); - parse(text); + if (parse(text)) { + schedule_draw(); + } }; const upload_progress = (e) => { diff --git a/math.js b/math.js new file mode 100644 index 0000000..5c873b6 --- /dev/null +++ b/math.js @@ -0,0 +1,14 @@ +function screen_to_canvas(p) { + // should be called with coordinates obtained from MouseEvent.clientX/clientY * window.devicePixelRatio + const xc = (p.x - offset.x) / zoom; + const yc = (p.y - offset.y) / zoom; + + return {'x': xc, 'y': yc}; +} + +function canvas_to_screen(state, p) { + const xs = (p.x * zoom + offset.x) / window.devicePixelRatio; + const ys = (p.y * zoom + offset.y) / window.devicePixelRatio; + + return {'x': xs, 'y': ys}; +} diff --git a/render.js b/render.js index 6427b58..ed2885d 100644 --- a/render.js +++ b/render.js @@ -5,13 +5,39 @@ let config = { bytes_per_quad: 20, w: 24, h: 24, + + predefined_colors: { + 'Np': [75, 62, 143], + 'F': [62, 123, 143], + 'Pd': [61, 142, 88], + 'Dc': [109, 143, 61], + 'Rn': [143, 102, 61], + 'Ds': [142, 61, 95], + 'Sc': [115, 61, 143], + 'Is': [61, 81, 143], + 'Rr': [61, 143, 129], + 'X': [68, 143, 61], + 'Rw': [142, 142, 61], + 'Cm': [61, 81, 143], + 'Mt': [142, 142, 61], + 'Ma': [143, 68, 61], + }, + + limit: -1, + zoom_delta: 0.05, }; let canvas = null; let gl = null; let gpu_timer_ext = null; let offset = { x: 0, y: 0 }; -let scale = 1; +let moving = false; +let zoom = 1; +let zoom_target = 1.0; +let zoom_level = 0; +let zoom_screenp = { 'x': 0, 'y': 0 }; +let last_frame_dt = 0; +let last_frame_ts = 0; const tquad_vs_src = `#version 300 es in vec2 a_pos; @@ -30,16 +56,16 @@ const tquad_vs_src = `#version 300 es if (vertex_index == 0) { // "top left" aka "p1" - corner = a_pos + vec2(1.0); + corner = a_pos; } else if (vertex_index == 1 || vertex_index == 5) { // "top right" aka "p2" - corner = a_pos + vec2(a_size.x - 1.0, 1.0); + corner = a_pos + vec2(a_size.x, 0); } else if (vertex_index == 2 || vertex_index == 4) { // "bottom left" aka "p3" - corner = a_pos + vec2(1.0, a_size.y - 1.0); + corner = a_pos + vec2(0, a_size.y); } else { // "bottom right" aka "p4" - corner = a_pos + a_size - vec2(1.0); + corner = a_pos + a_size; } vec2 screen02 = (corner.xy * vec2(u_scale) + u_translation) / u_res * 2.0; @@ -62,18 +88,21 @@ const tquad_fs_src = `#version 300 es } `; -function schedule_draw() { +function schedule_draw(animation = false) { if (!timers.raf) { - window.requestAnimationFrame(draw); + window.requestAnimationFrame((ts) => draw(ts, animation)); timers.raf = true; } } -function draw() { +function draw(ts, animation) { + const dt = ts - last_frame_ts; const cpu_before = performance.now(); const width = window.innerWidth; const height = window.innerHeight; + last_frame_ts = ts; + let query = null; if (gpu_timer_ext !== null) { @@ -82,7 +111,7 @@ function draw() { } gl.viewport(0, 0, canvas.width, canvas.height); - gl.clearColor(0, 0, 0, 1); + gl.clearColor(0.11, 0.11, 0.11, 1); gl.clear(gl.COLOR_BUFFER_BIT); const quads = generate('0'); @@ -99,7 +128,7 @@ function draw() { gl.uniform2f(program.locations['u_res'], canvas.width, canvas.height); gl.uniform2f(program.locations['u_translation'], offset.x, offset.y); - gl.uniform1f(program.locations['u_scale'], scale); + gl.uniform1f(program.locations['u_scale'], zoom); gl.enableVertexAttribArray(program.locations['a_pos']); gl.enableVertexAttribArray(program.locations['a_size']); @@ -150,10 +179,31 @@ function draw() { timers.raf = false; console.log('Last CPU Frametime: ' + Math.round((cpu_after - cpu_before) * 100) / 100 + 'ms'); + + if (zoom_target != zoom) { + update_canvas_zoom(zoom, zoom_target, animation ? dt : last_frame_dt); + schedule_draw(true); + } + + last_frame_dt = dt; } +function update_canvas_zoom(current, target, dt) { + const rate = Math.min(1.0, dt / 16.66 * 0.3); + + if (Math.abs(1.0 - current / target) > 0.01) { + zoom = current + (target - current) * rate; + } else { + zoom = target; + } + + // https://gist.github.com/aolo2/a373363419bd5a9283977ab9f8841f78 + const zc = zoom_screenp; + offset.x = zc.x - (zc.x - offset.x) * zoom / current; + offset.y = zc.y - (zc.y - offset.y) * zoom / current; +} -function init_webgl() { +function init_webgl() { canvas = document.querySelector('#c'); gl = canvas.getContext('webgl2');