From f9a3cbf24c117af4abb42d1ca3f26aacb88a43b2 Mon Sep 17 00:00:00 2001 From: Sander Hautvast Date: Wed, 17 Mar 2021 17:33:03 +0100 Subject: [PATCH] console now resizable in 1 direction. updated vector dragging behavior. first part of matrix handling --- README.md | 3 +- src/css/app.css | 36 ++++++-- src/index.html | 1 + src/js/functions.js | 38 +++++++- src/js/index.js | 41 +++++++-- src/js/svg_functions.js | 194 ++++++++++++++++++++++++++++------------ 6 files changed, 238 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index 0ede108..09d17d8 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,8 @@ The repl has the following syntax (It's work in progress, new capabilities will ```vector(-1 -1)``` != ```vector(-1-1)``` and the latter would mean ```vector(-2)``` which is not legal. * arrays: \[i0, i1, i2, ... in] creates an array. * \[i0, i1] is special: it is a shorthand for creating a 2-dimensional vector. I'll have to think of another way to create an array of length 2... - + +* ```id()`` adds a 2d identity matrix, which is also the two basis vectors. * properties * ```a = [1,2]``` * ```a.type``` diff --git a/src/css/app.css b/src/css/app.css index 6607311..7db77eb 100644 --- a/src/css/app.css +++ b/src/css/app.css @@ -4,11 +4,13 @@ body { overflow: hidden; } -.right{ + +.right { color: gray; position: absolute; right: 0; } + .background { position: absolute; left: 0; @@ -51,6 +53,15 @@ svg { z-index: 10; } +#slider { + position: absolute; + left: calc(50% - 1.5em); + top: -1em; + color: darkgray; + cursor: row-resize; + user-select: none; +} + .axis { stroke-width: 1.5; stroke: lightpink; @@ -62,12 +73,25 @@ svg { stroke-linecap: round; } -#prompt{ +.matrix { + stroke-width: 2.5; + stroke-linecap: round; +} + +.matrix.i { + stroke: green; +} + +.matrix.j { + stroke: red; +} + +#prompt { position: absolute; bottom: 0; } -.multiline{ +.multiline { border-top: 1px slategray solid; border-left: 1px slategray solid; border-right: 1px slategray solid; @@ -76,11 +100,11 @@ svg { border-top-right-radius: 3px; } -.single_line{ +.single_line { border: none transparent; } -#command_input{ +#command_input { background: #111; color: greenyellow; outline: none; @@ -88,7 +112,7 @@ svg { height: 1em; } -#command_history{ +#command_history { font-size: 12px; color: greenyellow; position: absolute; diff --git a/src/index.html b/src/index.html index 45995ca..dc6f982 100644 --- a/src/index.html +++ b/src/index.html @@ -7,6 +7,7 @@ https://github.com/shautvast/matrepl
+
___
diff --git a/src/js/functions.js b/src/js/functions.js index 3870467..1dd6034 100644 --- a/src/js/functions.js +++ b/src/js/functions.js @@ -11,6 +11,9 @@ export const functions = { return create_vector(args[0], args[1], args[2], args[3]); } }, + id: () => { + return create_2d_id_matrix() + }, hide: (args) => { return hide(args[0]); }, @@ -103,7 +106,8 @@ export const create_vector = function (x0, y0, x, y) { //rename to create_vector return show(this); }, equals: function (other) { - return (this.id === other.id || (this.x0 === other.x0 && this.y0 === other.y0 && this.x === other.x && this.y === other.y)); + return (this.id === other.id || + (this.type === other.type && this.x0 === other.x0 && this.y0 === other.y0 && this.x === other.x && this.y === other.y)); } }; return vector; @@ -171,4 +175,36 @@ export const logical_and = function (left, right) { export const logical_or = function (left, right) { return left || right; +} + +const create_2d_id_matrix = function () { + return { + data: [[1, 0], [0, 1]], + id: index_sequence++, + is_visual: true, + is_vector: false, // for type comparison + is_matrix: true, + type: 'matrix', // for showing type to user + is_new: true, // to determine view action + visible: true, + toString: function () { + return `matrix@${this.id}`; + }, + hide: function () { + return hide(this); + }, + label: function (text) { + return label(this, text); + }, + show: function () { + return show(this); + }, + equals: function (other) { + return (this.id === other.id || (this.type === other.type && this.data === other.data)); // TODO + }, + row: function (index) { + return this.data[index]; + } + } + } \ No newline at end of file diff --git a/src/js/index.js b/src/js/index.js index 171be55..28f8ce0 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -1,7 +1,7 @@ import '../css/app.css'; import {scan, token_types} from './scanner'; import {parse} from './parser'; -import {add_vector_arrow, add_vector_arrow_to_svg, update_vector_arrow} from "./svg_functions"; +import {add_matrix_view, add_vector_arrow, add_vector_arrow_to_svg, update_vector_arrow,} from "./svg_functions"; import { addition, create_vector, @@ -97,6 +97,9 @@ command_input_element.onkeypress = function handle_key_input(event) { command_input_element.onkeyup = function handle_key_input(event) { adjust_input_element_height(); + if (event.key === 'c' && event.ctrlKey) { + command_input_element.value = ''; + } if (event.key === 'ArrowUp' && !event.shiftKey) { if (command_history_index > -1) { command_input_element.value = command_history[command_history_index]; @@ -151,14 +154,10 @@ const handle_enter = function () { } if (value.is_visual) { - if (binding && bindings[binding].previous && bindings[binding].previous.is_visual) { - update_vector_arrow(bindings[binding].previous.id, value); - } else { - if (value.is_new) { - value.label_text = binding ? binding : ""; - value.is_new = false; - add_vector_arrow(value); - } + if (value.is_vector) { + create_or_update_vector(binding, value); + } else if (value.is_matrix) { + create_or_update_matrix(binding, value); } } else { if (binding && bindings[binding].previous && bindings[binding].previous.is_visual) { @@ -323,4 +322,28 @@ const resolve_arguments = function (argument_exprs) { } return value; }); +} + +function create_or_update_vector(binding, value) { + if (binding && bindings[binding].previous && bindings[binding].previous.is_visual) { + update_vector_arrow(bindings[binding].previous.id, value); + } else { + if (value.is_new) { + value.label_text = binding ? binding : ""; + value.is_new = false; + add_vector_arrow(value); + } + } +} + +function create_or_update_matrix(binding, value) { + if (binding && bindings[binding].previous && bindings[binding].previous.is_visual) { + // update_matrix_view(bindings[binding].previous.id, value); + } else { + if (value.is_new) { + value.label_text = binding ? binding : ""; + value.is_new = false; + add_matrix_view(value); + } + } } \ No newline at end of file diff --git a/src/js/svg_functions.js b/src/js/svg_functions.js index 75be431..fbbb133 100644 --- a/src/js/svg_functions.js +++ b/src/js/svg_functions.js @@ -1,15 +1,17 @@ import {update_visible_objects} from "./index"; let vectors_by_id = {}; +let matrices_by_id = {}; const SVG_NS = 'http://www.w3.org/2000/svg'; // program needs these to create svg elements let grid_size = 100; // this is the nr of pixels for the basis vector (1,0) (0,1) let half_grid_size = grid_size >> 1; // used to position the grid lines - +const slider = document.getElementById('slider'); +const console_element = document.getElementById('console'); let moving_vector; // user can move vector arrows. when moving, this refers to the arrow -let width = window.innerWidth, height = window.innerHeight; -let origin_x = Math.floor((width / grid_size) / 2) * grid_size + half_grid_size, - origin_y = Math.floor((height / grid_size) / 2) * grid_size + half_grid_size; - +let window_width = window.innerWidth, window_height = window.innerHeight; +let origin_x = Math.floor((window_width / grid_size) / 2) * grid_size + half_grid_size, + origin_y = Math.floor((window_height / grid_size) / 2) * grid_size + half_grid_size; +let slider_start_Y, slider_start_height; /** * Creates an svg element * @param element_type path,g, etc @@ -89,15 +91,16 @@ const create_line = function (x0, y0, x1, y1, css_class) { * @param x1 end_x * @param y1 end_y * @param css_class class attribute + * @param arrow_def SVG definition of the arrow head * @returns {SVGPathElement} */ -const create_arrow = function (id, x0, y0, x1, y1, css_class) { +const create_arrow = function (id, x0, y0, x1, y1, css_class, arrow_def) { let path = create_svg_element('path'); path.setAttribute('d', calculate_d(x0, y0, x1, y1)); path.id = id; path.setAttribute('class', css_class); - path.setAttribute('marker-end', 'url(#arrow)'); + path.setAttribute('marker-end', arrow_def); return path; } @@ -118,31 +121,27 @@ export const create_grid = function (css_class, bg_css_class) { group.setAttribute('id', 'grid'); const horizontal = create_svg_element('g'); horizontal.setAttribute('id', 'horizontal'); - for (let y = 0; y < height; y += grid_size) { - horizontal.appendChild(create_line(0, y + half_grid_size, width, y + half_grid_size, css_class)); - horizontal.appendChild(create_line(0, y, width, y, bg_css_class)); + for (let y = 0; y < window_height; y += grid_size) { + horizontal.appendChild(create_line(0, y + half_grid_size, window_width, y + half_grid_size, css_class)); + horizontal.appendChild(create_line(0, y, window_width, y, bg_css_class)); } group.appendChild(horizontal); const vertical = create_svg_element('g'); vertical.setAttribute('id', 'vertical'); - for (let x = 0; x < width; x += grid_size) { - vertical.appendChild(create_line(x + half_grid_size, 0, x + half_grid_size, height, css_class)); - vertical.appendChild(create_line(x, 0, x, height, bg_css_class)); + for (let x = 0; x < window_width; x += grid_size) { + vertical.appendChild(create_line(x + half_grid_size, 0, x + half_grid_size, window_height, css_class)); + vertical.appendChild(create_line(x, 0, x, window_height, bg_css_class)); } group.appendChild(vertical); return group; } -export const add_vector_arrow = function (vector) { - vectors_by_id[vector.id] = vector; - add_vector_arrow_to_svg(vector); -} -function create_label(vector) { +function create_label(vector, color) { let label = create_svg_element('text'); - label.setAttribute('x', (calc_screen_x(vector.x) + 5).toString()); - label.setAttribute('y', (calc_screen_y(vector.y) + 5).toString()); - label.setAttribute('fill', 'yellow'); + label.setAttribute('x', (calc_screen_x(vector.x) - 5).toString()); + label.setAttribute('y', (calc_screen_y(vector.y) + 15).toString()); + label.setAttribute('fill', color); label.setAttribute('id', 'l' + vector.id); let text_node = document.createTextNode(vector.label_text); label.appendChild(text_node); @@ -163,26 +162,49 @@ export const update_label_text = function (id, text) { } } - export const add_vector_arrow_to_svg = function (vector) { let vector_group = get_or_create_vector_group(); - let vector_arrow = create_arrow(vector.id, vector.x0, vector.y0, vector.x, vector.y, 'vector'); + let vector_arrow = create_arrow(vector.id, vector.x0, vector.y0, vector.x, vector.y, 'vector', 'url(#vector_arrow)'); vector_arrow.onmousedown = function start_moving_vector(event) { moving_vector = event.target; + + window.onmousemove = move; + + window.onmouseup = function () { + window.onmousemove = undefined; + window.onmouseup = undefined; + }; }; vector_group.appendChild(vector_arrow); - let label = create_label(vector); + let label = create_label(vector, 'yellow'); vector_group.appendChild(label); } +export const add_matrix_to_svg = function (matrix) { + let matricesGroup = get_or_create_matrices_group(); + let vector_arrow_i = create_arrow(matrix.id, 0, 0, matrix.row(0)[0], matrix.row(0)[1], 'matrix i', 'url(#matrix_i_arrow)'); + let vector_arrow_j = create_arrow(matrix.id, 0, 0, matrix.row(1)[0], matrix.row(1)[1], 'matrix j', 'url(#matrix_j_arrow)'); + matricesGroup.appendChild(vector_arrow_i); + matricesGroup.appendChild(vector_arrow_j); + + matricesGroup.appendChild(create_label({ + x: matrix.row(0)[0], + y: matrix.row(0)[1], + id: matrix.id + "i", + label_text: 'i' + }, 'green')); + matricesGroup.appendChild(create_label({ + x: matrix.row(1)[0], + y: matrix.row(1)[1], + id: matrix.id + "j", + label_text: 'j' + }, 'red')); +} + + /** * Draws all the vectors. - * - * vector { - * x0,y0 origin - * x,y coordinates - * } */ const draw_vectors = function () { const vector_group = get_or_create_vector_group(); @@ -196,6 +218,21 @@ const draw_vectors = function () { svg.appendChild(vector_group); } +/** + * Draws all the matrices. + */ +const draw_matrices = function () { + const matrices_group = get_or_create_matrices_group(); + + let matrices = Object.values(matrices_by_id); + for (let i = 0; i < matrices.length; i++) { + if (matrices[i].visible) { + add_matrix_to_svg(matrices[i]); + } + } + svg.appendChild(matrices_group); +} + const get_or_create_vector_group = function () { let vector_group = document.getElementById('vectors'); if (vector_group === null || vector_group === undefined) { @@ -207,6 +244,17 @@ const get_or_create_vector_group = function () { return vector_group; } +const get_or_create_matrices_group = function () { + let matrices_group = document.getElementById('matrices'); + if (matrices_group === null || matrices_group === undefined) { + matrices_group = create_svg_element("g"); + svg.appendChild(matrices_group); + matrices_group.id = 'matrices'; + } + + return matrices_group; +} + /** * Removes all vectors in the svg and calls draw_vectors to draw updated versions. */ @@ -214,6 +262,14 @@ const redraw_vectors = function () { remove_child(svg, 'vectors'); draw_vectors(); } + +/** + * Removes all vectors in the svg and calls draw_vectors to draw updated versions. + */ +const redraw_matrices = function () { + remove_child(svg, 'matrices'); + draw_matrices(); +} /** * removes the grid from the DOM and adds an updated one. */ @@ -244,15 +300,16 @@ export const update_vector_arrow = function (existing_id, new_vector) { export const redraw = function () { redraw_grid(); redraw_vectors(); + redraw_matrices(); } const create_axes = function () { let axes_group = create_svg_element('g'); axes_group.setAttribute('id', 'axes'); - let x = create_line(0, origin_y, width, origin_y, 'axis'); + let x = create_line(0, origin_y, window_width, origin_y, 'axis'); x.id = 'x-axis'; axes_group.appendChild(x); - let y = create_line(origin_x, 0, origin_x, height, 'axis'); + let y = create_line(origin_x, 0, origin_x, window_height, 'axis'); y.id = 'y-axis'; axes_group.appendChild(y); return axes_group; @@ -262,10 +319,10 @@ const create_axes = function () { * setup the arrow head for the vector * @returns {SVGDefsElement} */ -const create_defs = function () { +const create_defs = function (type, color) { let defs = create_svg_element('defs'); let marker = create_svg_element('marker'); - marker.id = 'arrow'; + marker.id = type; marker.setAttribute('orient', 'auto'); marker.setAttribute('viewBox', '0 0 10 10'); marker.setAttribute('markerWidth', '3'); @@ -275,30 +332,25 @@ const create_defs = function () { marker.setAttribute('refY', '5'); let polyline = create_svg_element('polyline'); polyline.setAttribute('points', '0,0 10,5 0,10 1,5'); - polyline.setAttribute('fill', 'yellow'); + polyline.setAttribute('fill', color); marker.appendChild(polyline); defs.appendChild(marker); return defs; } -/** - * The moving operation. Called by onmousemove on the svg ('canvas') - * @param event - */ -const move_vector = function (event) { - if (moving_vector) { - let current_x = event.offsetX; - let current_y = event.offsetY; - let vector = vectors_by_id[parseInt(moving_vector.id)]; - if (vector) { - vector.x = (current_x - origin_x) / grid_size; - vector.y = (origin_y - current_y) / grid_size; - moving_vector.setAttribute('d', create_d(origin_x, origin_y, current_x, current_y)); - update_label(moving_vector.id, moving_vector.id, current_x + 5, current_y + 5); - update_visible_objects(); - } +const move = function (event) { + let current_x = event.offsetX; + let current_y = event.offsetY; + let vector = vectors_by_id[parseInt(moving_vector.id)]; + if (vector) { + vector.x = (current_x - origin_x) / grid_size; + vector.y = (origin_y - current_y) / grid_size; + moving_vector.setAttribute('d', create_d(origin_x, origin_y, current_x, current_y)); + update_label(moving_vector.id, moving_vector.id, current_x + 5, current_y + 5); + update_visible_objects(); } } + /** * Creates the SVG * @returns {SVGElement} @@ -306,21 +358,32 @@ const move_vector = function (event) { const create_svg = function () { let svg = create_svg_element('svg'); - svg.onmousemove = move_vector; - svg.onmouseup = function stop_moving_vector() { + svg.onmousemove = move; + svg.onmouseup = function stop_moving() { moving_vector = undefined; }; - let defs = create_defs(); - svg.appendChild(defs); + svg.appendChild(create_defs('vector_arrow', 'yellow')); + svg.appendChild(create_defs('matrix_i_arrow', 'green')); + svg.appendChild(create_defs('matrix_j_arrow', 'red')); return svg; } +export const add_vector_arrow = function (vector) { + vectors_by_id[vector.id] = vector; + add_vector_arrow_to_svg(vector); +} + +export const add_matrix_view = function (matrix) { + matrices_by_id[matrix.id] = matrix; + add_matrix_to_svg(matrix); +} + document.body.onresize = function recalculate_window_dimensions() { - width = window.innerWidth; - height = window.innerHeight; - origin_x = Math.floor((width / grid_size) / 2) * grid_size + half_grid_size; - origin_y = Math.floor((height / grid_size) / 2) * grid_size + half_grid_size; + window_width = window.innerWidth; + window_height = window.innerHeight; + origin_x = Math.floor((window_width / grid_size) / 2) * grid_size + half_grid_size; + origin_y = Math.floor((window_height / grid_size) / 2) * grid_size + half_grid_size; redraw(); } @@ -329,3 +392,18 @@ document.body.appendChild(svg); svg.appendChild(create_grid('grid', 'bg-grid')); svg.appendChild(create_axes()); get_or_create_vector_group(); +get_or_create_matrices_group(); + +slider.onmousedown = function (event) { + slider_start_Y = event.clientY; + slider_start_height = parseInt(document.defaultView.getComputedStyle(console_element).height, 10); + + window.onmousemove = function (event) { + console_element.style.height = (slider_start_height - (event.clientY - slider_start_Y)) + 'px'; + } + + window.onmouseup = function () { + window.onmousemove = undefined; + window.onmouseup = undefined; + }; +};