finally got it to work: add a lazily updated vector that is automatically rerun when one of its elements is updated.

This commit is contained in:
Sander Hautvast 2021-02-18 18:52:46 +01:00
parent da5d9c7bdc
commit bbbdacede0
3 changed files with 66 additions and 62 deletions

View file

@ -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()

View file

@ -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}}`,
};
}

View file

@ -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();