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:
parent
da5d9c7bdc
commit
bbbdacede0
3 changed files with 66 additions and 62 deletions
|
|
@ -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()
|
||||
|
|
@ -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}}`,
|
||||
};
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue