// CC BY-NC-SA 4.0, gtrrebel, shikhin, zgrep var dr_canvas, p_canvas var dr_ctx, p_ctx const high_resolution = 1000, low_resolution = 200 const enhance_resolutions = [low_resolution, 400, 800, high_resolution, 1.5 * high_resolution] const enhance_text = ["enhance", "enhance more", "enhance further", "ENHANCE", "no more enhance"] var enhance_level = 0 var dr_resolution = high_resolution var enhance_button, enhance_enabled = false var dim = 2 var matrix_entries = [];//[-3, 0, 0, 0, 0, 0, 0, -1, //0, 0, -2, 0, 0, 0, 0, -1, //0, 0, 0, 0, 1, -2, 0, -2, //0, -1, 0, -1, 0, -2, 2, 2] var tangents = [] const tangent_style = "rgb(30,144,255)" const tangent_thickness = 3 const tangent_thinness = 3 const p_radius_const = 0.06 const p_scale = 3.0 const p_font = "40px serif" var p_radius, p_radius_sq var zoom_scale = 1 var dr_scale = zoom_scale * dim * p_scale var mouse_moving = false, touch_moving = false, moving_id = 0 var touch_identifier var moving_origin = [] var moving_epsilon = 0.04 function p_to_canv_coords(x, y) { return [p_canvas.width * (x + p_scale) / (2 * p_scale), p_canvas.height * (-y + p_scale) / (2 * p_scale)] } function p_coords(i, j) { return [p_scale * ((i * 2.0 / p_canvas.width) - 1.0), -p_scale * ((j * 2.0 / p_canvas.height) - 1.0)] } function dr_to_canv_coords(x, y) { return [dr_canvas.width * (x + dr_scale) / (2 * dr_scale), dr_canvas.height * (-y + dr_scale) / (2 * dr_scale)] } function init() { enhance_button = document.getElementById("enhance") dr_canvas = document.getElementById("drawing_canvas") dr_ctx = dr_canvas.getContext("2d") p_canvas = document.getElementById("picker_canvas") p_ctx = p_canvas.getContext("2d") p_canvas.style.width = p_canvas.style.height = "400px" p_canvas.width = p_canvas.height = 800 dr_canvas.style.width = dr_canvas.style.height = "1000px" dr_canvas.width = dr_canvas.height = dr_resolution Module.set_resolution(dr_resolution) p_radius = p_canvas.width * p_radius_const p_radius_sq = p_radius * p_radius p_canvas.addEventListener("mousedown", mouse_down) p_canvas.addEventListener("mousemove", mouse_move) p_canvas.addEventListener("mouseup", mouse_up) p_canvas.addEventListener("mouseout", mouse_up) p_canvas.addEventListener("touchstart", touch_start) p_canvas.addEventListener("touchmove", touch_move) p_canvas.addEventListener("touchend", touch_end) p_canvas.addEventListener("touchcancel", touch_end) dim_update() } // const known_letters = 'abcdefghijklmnopqrstuvwxyzαβγδεζηθικλμνξπρστυφχψωABCDEFGHIJKLMNOPQRSTUVWXYZΓΔΘΛΞΠΣΦΨΩςאבגדהזחטכלמנעפצקשת'; function matrix_letter(i) { return 'C_{' + Math.floor(i / dim) + ',' + (i % dim) + '}' } function p_draw() { p_ctx.clearRect(0, 0, p_canvas.width, p_canvas.height) // axes p_ctx.beginPath() p_ctx.moveTo(p_canvas.width / 2, 0) p_ctx.lineTo(p_canvas.width / 2, p_canvas.height) p_ctx.stroke() p_ctx.beginPath() p_ctx.moveTo(0, p_canvas.height / 2) p_ctx.lineTo(p_canvas.width, p_canvas.height / 2) p_ctx.stroke() for (let i = 0; i < 2 * dim * dim; i += 2) { const [x, y] = p_to_canv_coords(matrix_entries[i], matrix_entries[i + 1]) p_ctx.beginPath() p_ctx.arc(x, y, p_radius, 0, 2 * Math.PI, false) p_ctx.fillStyle = "rgba(149, 165, 166,0.9)" p_ctx.fill() p_ctx.fillStyle = "black" p_ctx.font = p_font p_ctx.fillText(matrix_letter(i / 2), x + 1.5 * p_radius, y) } // text input grid update_grid() } function set_enhance_level(level) { enhance_level = level enhance_button.textContent = enhance_text[level] dr_resolution = enhance_resolutions[level] dr_canvas.width = dr_canvas.height = dr_resolution Module.set_resolution(dr_resolution) } function enhance_disable(dim_switch = false) { if (dim_switch) { enhance_button.textContent = enhance_text[0] dr_resolution = high_resolution dr_canvas.width = dr_canvas.height = dr_resolution Module.set_resolution(dr_resolution) } if (!enhance_enabled) { return } enhance_button.setAttribute('disabled', true) enhance_enabled = false if (enhance_level > 0 && !dim_switch) { set_enhance_level(0) } dr_draw() } function enhance_enable() { if (dim < 5) { return } enhance_button.removeAttribute('disabled') enhance_enabled = true } function enhance() { if (enhance_level == enhance_resolutions.length - 1) { return } enhance_button.textContent = "enhancing..." setTimeout(function() { set_enhance_level(enhance_level + 1) dr_draw() enhance_button.textContent = enhance_text[enhance_level] }, 10); } function to_float_arr(arr) { const res = new Float64Array(arr.length) for (let i = 0; i < arr.length; i++) { res[i] = arr[i] } return res } function transfer_matrix_to_heap() { const typed_array = to_float_arr(matrix_entries) const heap_pointer = Module._malloc(typed_array.length * typed_array.BYTES_PER_ELEMENT) Module.HEAPF64.set(typed_array, heap_pointer >> 3) return heap_pointer } function dr_draw() { tangents = [] let heap_array try { heap_array = transfer_matrix_to_heap() Module.set_matrix(dim, heap_array) } finally { Module._free(heap_array) } let arr = new Uint8ClampedArray(Module.HEAPU8.buffer, Module.get_output_buffer(), dr_canvas.width * dr_canvas.height * 4) var imageData = dr_ctx.createImageData(dr_canvas.width, dr_canvas.height) imageData.data.set(arr) dr_ctx.clearRect(0, 0, dr_canvas.width, dr_canvas.height); dr_ctx.putImageData(imageData, 0, 0) const [or_x, or_y] = dr_to_canv_coords(0, 0) dr_ctx.strokeStyle = tangent_style dr_ctx.beginPath() if (dim >= 5 && enhance_level == 0) { dr_ctx.lineWidth = tangent_thinness } else { dr_ctx.lineWidth = tangent_thickness } if (dim < 5 || (dim >= 5 && enhance_level > 0)) { dr_ctx.fillStyle = tangent_style dr_ctx.arc(or_x, or_y, tangent_thickness, 0, 2 * Math.PI, false) dr_ctx.fill() } for (let i = 0; i < tangents.length; i++) { dr_ctx.moveTo(or_x, or_y) dr_ctx.lineTo(tangents[i][0], tangents[i][1]) } dr_ctx.stroke() } function add_tangent(a, b) { tangents.push(dr_to_canv_coords(a, b)) } function update_dr_scale() { dr_scale = zoom_scale * dim * p_scale } function update_zoom() { update_dr_scale() Module.set_zoom(zoom_scale) dr_draw() } function zoom_in_dr() { zoom_scale *= 0.8 update_zoom() } function zoom_out_dr() { zoom_scale /= 0.8 update_zoom() } function randomize_matrix() { matrix_entries = [] for (let i = 0; i < dim * dim; i++) { matrix_entries.push((6.0 * Math.random()) - 3.0) matrix_entries.push((6.0 * Math.random()) - 3.0) } p_draw() dr_draw() } async function copy_matrix() { matrix = "[" for (let i = 0; i < dim; i++) { matrix += "[" for (let j = 0; j < dim; j++) { matrix += matrix_entries[2 * (dim * i + j)].toFixed(3) matrix += "+" matrix += matrix_entries[2 * (dim * i + j) + 1].toFixed(3) matrix += "i" if (j < dim - 1) matrix += "," } matrix += "]" if (i < dim - 1) matrix += "\n" } matrix += "]\n" matrix += "base64 = " matrix += btoa(JSON.stringify(matrix_entries)) let copy_timeout = undefined; dom_copy = document.getElementById("copy_button") try { await navigator.clipboard.writeText(matrix); dom_copy.textContent = 'copied'; } catch (e) { dom_copy.textContent = 'failed to copy'; } finally { copy_timeout && clearTimeout(copy_timeout); copy_timeout = setTimeout(function() { dom_copy.textContent = 'copy'; copy_timeout = undefined; }, 1500); } } function enter_matrix() { var matrix = prompt('base64?') try { entries = JSON.parse(atob(matrix)) if (!Array.isArray(entries)) { throw new Error("Bad object") } if (entries.length != 2 * dim * dim) { throw new Error("Bad length") } for (let i = 0; i < 2 * dim * dim; i++) { if (isNaN(entries[i]) || -3 > entries[i] || entries[i] > 3) { throw new Error("Bad entries") } } matrix_entries = entries dr_draw() } catch(error) { } } function dim_update() { var old_dim = dim dim = Number(document.getElementById("dim").value) update_dr_scale() matrix_entries = matrix_entries.slice(0, 2 * dim * dim) let diff = 2 * dim * dim - matrix_entries.length for (let i = 0; i < diff; i += 2) { matrix_entries.push((6.0 * Math.random()) - 3.0) matrix_entries.push((6.0 * Math.random()) - 3.0) } let latex_matrix = []; for (let row = 0; row < dim; row++) { let current = []; latex_matrix.push(current); for (let col = 0; col < dim; col++) { current.push('{' + matrix_letter(row*dim + col) + '}'); } } latex_matrix = ',\\hspace{2em} A + iB = \\begin{bmatrix}' + latex_matrix.map(row => row.join('&')).join('\\\\') + '\\end{bmatrix} =\\ ' katex.render(latex_matrix, document.getElementById('matrix')) make_grid() if (old_dim < 5 && dim >= 5) { dr_resolution = low_resolution dr_canvas.width = dr_canvas.height = dr_resolution Module.set_resolution(dr_resolution) } if (old_dim >= 5 && dim < 5) { dr_resolution = high_resolution dr_canvas.width = dr_canvas.height = dr_resolution Module.set_resolution(dr_resolution) } enhance_disable(old_dim >= 5 && dim < 5) enhance_enable() p_draw() dr_draw() } var input_timers = {}; function input_update(e) { const input = e.srcElement; const row = input.dataset.row; const col = input.dataset.col; const i = row * dim * 2 + col * 2; try { let [real, imag] = input.value.split('+'); real = Number(real) imag = Number(imag.replace(/i$/, '')) if (isNaN(real) || -3 > real || real > 3) { throw new Error("Bad real part") } if (isNaN(imag) || -3 > imag || imag > 3) { throw new Error("Bad imaginary part") } matrix_entries[i] = real; matrix_entries[i+1] = imag; p_draw() dr_draw() } catch (error) { update_grid(); const old = input.style.borderColor; input.style.borderColor = 'red'; input.blur(); input_timers[i] = setTimeout(function() { input.style.borderColor = old; delete input_timers[i]; }, 1500); } } function make_grid() { for (const [i, timer] of Object.entries(input_timers)) { clearTimeout(timer); delete input_timers[i]; } let grid = document.createElement('table'); grid.id = 'matrix_table'; for (let row = 0; row < dim; row++) { let section = document.createElement('tr'); grid.appendChild(section); for (let col = 0; col < dim; col++) { let input = document.createElement('input'); input.type = 'text'; input.onchange = input_update; input.dataset.row = row; input.dataset.col = col; let td = document.createElement('td'); td.appendChild(input); section.appendChild(td); } } document.getElementById('matrix_table').replaceWith(grid); } function update_grid() { let grid = document.getElementById('matrix_table'); for (let row = 0; row < dim; row++) { let section = grid.children[row]; for (let col = 0; col < dim; col++) { let input = section.children[col].children[0]; let i = row*dim*2 + col*2; input.value = matrix_entries[i] + '+' + matrix_entries[i+1] + 'i'; } } } // mouse function mouse_event_get_coords(e) { rect = p_canvas.getBoundingClientRect(); x = e.clientX - rect.left; y = e.clientY - rect.top; return [2 * x, 2 * y] } function touch_event_get_coords(e) { rect = p_canvas.getBoundingClientRect(); for (let i = 0; i < e.changedTouches.length; i++) { if (e.changedTouches[i].identifier == touch_identifier) { x = e.changedTouches[i].clientX - rect.left; y = e.changedTouches[i].clientY - rect.top; return [2 * x, 2 * y] } } return false } function input_start(x, y) { for (let i = 0; i < 2 * dim * dim; i += 2) { const [point_x, point_y] = p_to_canv_coords(matrix_entries[i], matrix_entries[i + 1]) if (((x - point_x)*(x - point_x) + (y - point_y)*(y - point_y)) <= p_radius_sq) { moving_id = i moving_origin[0] = matrix_entries[i] moving_origin[1] = matrix_entries[i + 1] enhance_disable() return true } } return false } function touch_start(e) { if (touch_moving || mouse_moving) { return } rect = p_canvas.getBoundingClientRect() for (let i = 0; i < e.touches.length; i++) { x = e.touches[i].clientX - rect.left y = e.touches[i].clientY - rect.top touch_moving = input_start(2 * x, 2 * y) if (touch_moving) { touch_identifier = e.touches[i].identifier return } } } function mouse_down(e) { if (touch_moving) { return } const [x, y] = mouse_event_get_coords(e) mouse_moving = input_start(x, y) } function input_moving(x, y) { if (x < p_radius || x > (p_canvas.width - p_radius) || y < p_radius || y > (p_canvas.height - p_radius)) { return } [x, y] = p_coords(x, y) matrix_entries[moving_id] = x matrix_entries[moving_id + 1] = y p_draw() if (dim <= 2 || (Math.abs(matrix_entries[moving_id] - moving_origin[0]) + Math.abs(matrix_entries[moving_id + 1] - moving_origin[1])) > moving_epsilon) { moving_origin[0] = matrix_entries[moving_id] moving_origin[1] = matrix_entries[moving_id + 1] dr_draw() } } function touch_move(e) { if (touch_moving == false) { return } const ret = touch_event_get_coords(e) if (ret == false) { return } input_moving(ret[0], ret[1]) } function mouse_move(e) { if (mouse_moving == false) { return } const [x, y] = mouse_event_get_coords(e) input_moving(x, y) } function input_end(x, y) { if (x < p_radius) { x = p_radius } if (x > (p_canvas.width - p_radius)) { x = p_canvas.width - p_radius } if (y < p_radius) { y = p_radius } if (y > (p_canvas.height - p_radius)) { y = p_canvas.width - p_radius } [x, y] = p_coords(x, y) matrix_entries[moving_id] = x matrix_entries[moving_id + 1] = y enhance_enable() p_draw() dr_draw() } function touch_end(e) { if (touch_moving == true) { const ret = touch_event_get_coords(e) if (ret == false) { return } input_end(ret[0], ret[1]) touch_moving = false } } function mouse_up(e) { //for (let i = 0; i < 4; i += 1) { // document.getElementById("number_"+i).value = matrix_entries[i][0].toFixed(3) + // (matrix_entries[i][1] >= 0 ? "+" : "") + matrix_entries[i][1].toFixed(3) + "i" //} if (mouse_moving == true) { const [x, y] = mouse_event_get_coords(e) input_end(x, y) mouse_moving = false } }