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"```
|
* 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.
|
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
|
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,
|
automatically. Combined with vector dragging, this way you can get an intuition for vector addition.
|
||||||
you can get an intuition for vector addition.
|
Want to do the same for matrix multiplication and basis change.
|
||||||
|
|
||||||
|
|
||||||
**To run locally**
|
**To run locally**
|
||||||
* make sure you have node/npm
|
* 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)
|
a=vector(0,0,0.5,0.5)
|
||||||
b=vector(0,0,-1,1)
|
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()
|
* or type help()
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import {scan, token_types} from './scanner';
|
import {scan, token_types} from './scanner';
|
||||||
import {parse} from './parser';
|
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
|
* handles user input from the console div
|
||||||
|
|
@ -9,6 +9,7 @@ const state = {};
|
||||||
const command_input_element = document.getElementById('command_input');
|
const command_input_element = document.getElementById('command_input');
|
||||||
const command_history_element = document.getElementById('command_history');
|
const command_history_element = document.getElementById('command_history');
|
||||||
command_input_element.value = '';
|
command_input_element.value = '';
|
||||||
|
let current_object_index = 0;
|
||||||
let command_history = [''];
|
let command_history = [''];
|
||||||
let command_history_index = 0;
|
let command_history_index = 0;
|
||||||
|
|
||||||
|
|
@ -22,7 +23,10 @@ export const update_lazy_objects = function () {
|
||||||
if (existing_value) {
|
if (existing_value) {
|
||||||
update_vector_arrow(existing_value.object.id, value.object);
|
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;
|
let description = state[object.binding].description;
|
||||||
if (!description) {
|
if (!description) {
|
||||||
description = state[object.binding];
|
description = state[object.binding];
|
||||||
|
|
@ -75,6 +79,16 @@ command_input_element.onkeyup = function handle_key_input(event) {
|
||||||
let result;
|
let result;
|
||||||
try {
|
try {
|
||||||
result = visit_expression(statement);
|
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) {
|
if (result.description) {
|
||||||
result = result.description;
|
result = result.description;
|
||||||
}
|
}
|
||||||
|
|
@ -93,20 +107,16 @@ export let visit_expression = function (expr) {
|
||||||
switch (expr.type) {
|
switch (expr.type) {
|
||||||
case 'declaration': {
|
case 'declaration': {
|
||||||
let value = visit_expression(expr.initializer);
|
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;
|
value.binding = expr.var_name.value;
|
||||||
state[expr.var_name.value] = value;
|
if (value.binding in state){
|
||||||
let description = state[expr.var_name.value].description;
|
value.previous = state[value.binding].object;
|
||||||
if (!description) {
|
|
||||||
description = state[expr.var_name.value]; //questionable. use toString instead of message?
|
|
||||||
}
|
}
|
||||||
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':
|
case 'group':
|
||||||
return visit_expression(expr.expression);
|
return visit_expression(expr.expression);
|
||||||
|
|
@ -196,7 +206,7 @@ const method_call = function (object_wrapper, method_or_property) {
|
||||||
|
|
||||||
const functions = {
|
const functions = {
|
||||||
help: () => help(),
|
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) => {
|
remove: (args) => {
|
||||||
if (Object.prototype.hasOwnProperty.call(args[0], ['binding'])) {
|
if (Object.prototype.hasOwnProperty.call(args[0], ['binding'])) {
|
||||||
delete state[args[0].binding];
|
delete state[args[0].binding];
|
||||||
|
|
@ -233,3 +243,26 @@ const addition = function (left, right) {
|
||||||
}
|
}
|
||||||
return 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
|
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 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 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 moving_vector; // user can move vector arrows. when moving, this refers to the arrow
|
||||||
let width = window.innerWidth, height = window.innerHeight;
|
let width = window.innerWidth, height = window.innerHeight;
|
||||||
let origin_x = Math.floor((width / grid_size) / 2) * grid_size + half_grid_size,
|
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) {
|
export const update_vector_arrow = function (id, vector) {
|
||||||
let d = calculate_d(vector.x0, vector.y0, vector.x, vector.y);
|
let d = calculate_d(vector.x0, vector.y0, vector.x, vector.y);
|
||||||
document.getElementById(id).setAttribute('d', d);
|
let v = document.getElementById(id.toString());
|
||||||
}
|
v.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}}`,
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const remove_vector = function (vector_or_index) {
|
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].x = (current_x - origin_x) / grid_size;
|
||||||
vectors[moving_vector.id].y = (origin_y - current_y) / 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));
|
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();
|
const vector_group = create_vector_group();
|
||||||
|
|
||||||
for (let i = 0; i < vectors.length; i++) {
|
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);
|
svg.appendChild(vector_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
const add_vector_to_group = function (vector, vector_group) {
|
export const add_vector_to_group = function (vector) {
|
||||||
if (!vector_group) {
|
vectors.push(vector);
|
||||||
vector_group = document.getElementById('vectors');
|
vector.is_visible = true;
|
||||||
}
|
let vector_group = document.getElementById('vectors');
|
||||||
if (!vector_group) {
|
|
||||||
|
if (vector_group === null || vector_group === undefined) {
|
||||||
vector_group = create_vector_group();
|
vector_group = create_vector_group();
|
||||||
}
|
}
|
||||||
let vector_arrow = arrow(vector.id, vector.x0, vector.y0, vector.x, vector.y,
|
let vector_arrow = arrow(vector.id, vector.x0, vector.y0, vector.x, vector.y, 'vector');
|
||||||
'vector');
|
|
||||||
vector_arrow.onmousedown = function start_moving_vector(event) {
|
vector_arrow.onmousedown = function start_moving_vector(event) {
|
||||||
moving_vector = event.target;
|
moving_vector = event.target;
|
||||||
};
|
};
|
||||||
|
|
||||||
vector_group.appendChild(vector_arrow);
|
vector_group.appendChild(vector_arrow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -304,7 +277,6 @@ const create_svg = function () {
|
||||||
svg.onmousemove = move_vector;
|
svg.onmousemove = move_vector;
|
||||||
svg.onmouseup = function stop_moving_vector() {
|
svg.onmouseup = function stop_moving_vector() {
|
||||||
moving_vector = undefined;
|
moving_vector = undefined;
|
||||||
update_lazy_objects();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let defs = create_defs();
|
let defs = create_defs();
|
||||||
|
|
@ -322,6 +294,6 @@ document.body.onresize = function recalculate_window_dimensions() {
|
||||||
|
|
||||||
const svg = create_svg();
|
const svg = create_svg();
|
||||||
document.body.appendChild(svg);
|
document.body.appendChild(svg);
|
||||||
|
|
||||||
svg.appendChild(create_grid('grid', 'bg-grid'));
|
svg.appendChild(create_grid('grid', 'bg-grid'));
|
||||||
svg.appendChild(create_axes());
|
svg.appendChild(create_axes());
|
||||||
|
create_vector_group();
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue