tjsm/public/main.js

545 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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))
assert(Array.isArray(entries))
assert(entries.length == 2 * dim * dim)
for (let i = 0; i < 2 * dim * dim; i++) {
assert(!isNaN(entries[i]) && -3 < entries[i] && entries[i] < 3)
}
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$/, ''));
assert(!isNaN(real) && -3 < real && real < 3);
assert(!isNaN(imag) && -3 < imag && imag < 3);
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
}
}