From bbbdacede049a11e3224b8666857d66d5767b662 Mon Sep 17 00:00:00 2001 From: Sander Hautvast Date: Thu, 18 Feb 2021 18:52:46 +0100 Subject: [PATCH] finally got it to work: add a lazily updated vector that is automatically rerun when one of its elements is updated. --- README.md | 9 +++---- src/js/console.js | 63 ++++++++++++++++++++++++++++++++++++----------- src/js/index.js | 56 +++++++++++------------------------------ 3 files changed, 66 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 2f7b330..11a838a 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,8 @@ The repl has the following syntax (It's work in progress, new capabilities will * lazy evaluation. The difference between ```c = a+b``` and ```c = "a+b"``` is that the latter assigns to c a parsed expression that can always be evaluated later. When you apply lazy evaluation and later update ```a```, the value for c will be reevaluated - automatically. (NB automatic reevaluation is not yet implemented). Combined with vector dragging, - you can get an intuition for vector addition. - + automatically. Combined with vector dragging, this way you can get an intuition for vector addition. + Want to do the same for matrix multiplication and basis change. **To run locally** * make sure you have node/npm @@ -36,7 +35,7 @@ The repl has the following syntax (It's work in progress, new capabilities will ``` a=vector(0,0,0.5,0.5) b=vector(0,0,-1,1) - c=a+b + c="a+b" ``` -* and press enter +* and press enter. Then using the mouse pointer move a or b. (Labels will be added). * or type help() \ No newline at end of file diff --git a/src/js/console.js b/src/js/console.js index f0370ae..e8a23c8 100644 --- a/src/js/console.js +++ b/src/js/console.js @@ -1,6 +1,6 @@ import {scan, token_types} from './scanner'; import {parse} from './parser'; -import {add_vector, remove_vector, update_vector_arrow} from "./index"; +import {remove_vector, update_vector_arrow, add_vector_to_group} from "./index"; /** * handles user input from the console div @@ -9,6 +9,7 @@ const state = {}; const command_input_element = document.getElementById('command_input'); const command_history_element = document.getElementById('command_history'); command_input_element.value = ''; +let current_object_index = 0; let command_history = ['']; let command_history_index = 0; @@ -22,7 +23,10 @@ export const update_lazy_objects = function () { if (existing_value) { update_vector_arrow(existing_value.object.id, value.object); } - state[object.binding].object = value.object; + state[object.binding].object.x0 = value.object.x0; + state[object.binding].object.y0 = value.object.y0; + state[object.binding].object.x = value.object.x; + state[object.binding].object.y = value.object.y; let description = state[object.binding].description; if (!description) { description = state[object.binding]; @@ -75,6 +79,16 @@ command_input_element.onkeyup = function handle_key_input(event) { let result; try { result = visit_expression(statement); + let object_wrapper = result.value; + + if (object_wrapper.object.is_vector) { + if (object_wrapper.previous){ + update_vector_arrow(object_wrapper.previous.id, object_wrapper.object); + } else { + add_vector_to_group(result.value.object); + } + } + if (result.description) { result = result.description; } @@ -93,20 +107,16 @@ export let visit_expression = function (expr) { switch (expr.type) { case 'declaration': { let value = visit_expression(expr.initializer); - console.log(value); - let existing_value = state[expr.var_name.value]; - if (existing_value) { - if (existing_value.type === 'vector') { - remove_vector(existing_value.object); // remove from screen - } - } value.binding = expr.var_name.value; - state[expr.var_name.value] = value; - let description = state[expr.var_name.value].description; - if (!description) { - description = state[expr.var_name.value]; //questionable. use toString instead of message? + if (value.binding in state){ + value.previous = state[value.binding].object; } - return {description: expr.var_name.value + ':' + description}; + state[value.binding] = value; + let description = state[value.binding].description; + if (!description) { + description = state[value.binding]; //questionable. use toString instead of message? + } + return {description: expr.var_name.value + ':' + description, value: value}; } case 'group': return visit_expression(expr.expression); @@ -196,7 +206,7 @@ const method_call = function (object_wrapper, method_or_property) { const functions = { help: () => help(), - vector: (args) => add_vector({x0: args[0], y0: args[1], x: args[2], y: args[3]}), + vector: (args) => create_vector({x0: args[0], y0: args[1], x: args[2], y: args[3]}), remove: (args) => { if (Object.prototype.hasOwnProperty.call(args[0], ['binding'])) { delete state[args[0].binding]; @@ -233,3 +243,26 @@ const addition = function (left, right) { } return left + right; } + +export const create_vector = function (vector) { //rename to create_vector + vector.id = current_object_index++; + vector.add = (other) => create_vector({ + x0: vector.x0 + other.x0, + y0: vector.x0 + other.x0, + x: vector.x + other.x, + y: vector.y + other.y + }); + vector.multiply = (scalar) => create_vector({ + x0: vector.x0 * scalar, + y0: vector.y0 * scalar, + x: vector.x * scalar, + y: vector.y * scalar + }); + vector.is_vector = true; + vector.type = () => 'vector'; + return { //object_wrapper + type: 'vector', + object: vector, + description: `vector@${vector.id}{x0:${vector.x0},y0:${vector.y0} x:${vector.x},y:${vector.y}}`, + }; +} \ No newline at end of file diff --git a/src/js/index.js b/src/js/index.js index 9940750..cfc6157 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -8,7 +8,7 @@ import {update_lazy_objects} from "./console"; 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 -let vectors = []; // collection of added vectors +let vectors = []; // collection of added vectors // maybe move to console.js 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, @@ -135,37 +135,8 @@ const redraw_grid = function () { export const update_vector_arrow = function (id, vector) { let d = calculate_d(vector.x0, vector.y0, vector.x, vector.y); - document.getElementById(id).setAttribute('d', d); -} -/** - * Adds a vector to the set. - * @param vector - */ -export const add_vector = function (vector) { - vector.id = vectors.length; - vectors.push(vector); - add_vector_to_group(vector); - - vector.add = (other) => add_vector({ - x0: vector.x0 + other.x0, - y0: vector.x0 + other.x0, - x: vector.x + other.x, - y: vector.y + other.y - }); - vector.multiply = (scalar) => add_vector({ - x0: vector.x0 * scalar, - y0: vector.y0 * scalar, - x: vector.x * scalar, - y: vector.y * scalar - }); - vector.is_vector = true; - vector.type = () => 'vector'; - return { //object_wrapper - type: 'vector', - object: vector, - description: `vector@${vector.id}{x0:${vector.x0},y0:${vector.y0} x:${vector.x},y:${vector.y}}`, - }; - + let v = document.getElementById(id.toString()); + v.setAttribute('d', d); } export const remove_vector = function (vector_or_index) { @@ -201,6 +172,7 @@ const move_vector = function (event) { vectors[moving_vector.id].x = (current_x - origin_x) / grid_size; vectors[moving_vector.id].y = (origin_y - current_y) / grid_size; moving_vector.setAttribute('d', create_d(origin_x, origin_y, current_x, current_y)); + update_lazy_objects(); } } @@ -216,23 +188,24 @@ const draw_vectors = function () { const vector_group = create_vector_group(); for (let i = 0; i < vectors.length; i++) { - add_vector_to_group(vectors[i], vector_group); + add_vector_to_group(vectors[i]); } svg.appendChild(vector_group); } -const add_vector_to_group = function (vector, vector_group) { - if (!vector_group) { - vector_group = document.getElementById('vectors'); - } - if (!vector_group) { +export const add_vector_to_group = function (vector) { + vectors.push(vector); + vector.is_visible = true; + let vector_group = document.getElementById('vectors'); + + if (vector_group === null || vector_group === undefined) { vector_group = create_vector_group(); } - let vector_arrow = arrow(vector.id, vector.x0, vector.y0, vector.x, vector.y, - 'vector'); + let vector_arrow = arrow(vector.id, vector.x0, vector.y0, vector.x, vector.y, 'vector'); vector_arrow.onmousedown = function start_moving_vector(event) { moving_vector = event.target; }; + vector_group.appendChild(vector_arrow); } @@ -304,7 +277,6 @@ const create_svg = function () { svg.onmousemove = move_vector; svg.onmouseup = function stop_moving_vector() { moving_vector = undefined; - update_lazy_objects(); }; let defs = create_defs(); @@ -322,6 +294,6 @@ document.body.onresize = function recalculate_window_dimensions() { const svg = create_svg(); document.body.appendChild(svg); - svg.appendChild(create_grid('grid', 'bg-grid')); svg.appendChild(create_axes()); +create_vector_group();