Compare commits

...

10 commits

Author SHA1 Message Date
Sander Hautvast
22c9e57995 fix test (updated svg def name) 2021-03-17 17:38:46 +01:00
Sander Hautvast
f9a3cbf24c console now resizable in 1 direction. updated vector dragging behavior. first part of matrix handling 2021-03-17 17:33:03 +01:00
Sander Hautvast
b412b11941 fixed test 2021-03-09 17:22:26 +01:00
Sander Hautvast
ca4fe5f490 updated help and a fix for vector notation 2021-03-09 17:05:47 +01:00
Sander Hautvast
d7ecddbad5 commas remain mandatory for separating values. Will do spaces later 2021-03-09 15:31:20 +01:00
Sander Hautvast
3651b7fe18 support for arrays and a shorthand for vector 2021-03-08 17:38:58 +01:00
Sander Hautvast
83bbc5edab fix for undefined, back to production mode 2021-03-05 19:43:38 +01:00
Sander Hautvast
30a74579f9 fix for undefined? 2021-03-05 19:35:10 +01:00
Sander Hautvast
720d3a647f fix for undefined? 2021-03-05 18:55:50 +01:00
Sander Hautvast
827d24742b added Math.E 2021-03-05 18:48:52 +01:00
8 changed files with 322 additions and 111 deletions

View file

@ -18,13 +18,16 @@ The repl has the following syntax (It's work in progress, new capabilities will
* and then ```@0``` * and then ```@0```
> > vector@0{x0:0, y0: 1, x:1, y:1} > > vector@0{x0:0, y0: 1, x:1, y:1}
* method calls: * method calls:
```a = vector(12, 1)``` ```a = vector(12, 1)``` works as well. The start is now the origin.
> > vector@0{x0:0, y0: 2, x:12, y:1} > > vector@0{x0:0, y0: 2, x:12, y:1}
* ```vector(1 2)``` works as well. The start is now the origin.
commas are not mandatory. I'm planning to add a more mathematical notation for vectors: ```[1 2]```
```vector(-1 -1)``` != ```vector(-1-1)``` and the latter would mean ```vector(-2)``` which is not legal.
* arrays: \[i<sub>0</sub>, i<sub>1</sub>, i<sub>2</sub>, ... i<sub>n</sub>] creates an array.
* \[i<sub>0</sub>, i<sub>1</sub>] 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 * properties
* ```a = vector(12, 1)``` * ```a = [1,2]```
* ```a.type``` * ```a.type```
> &gt; vector > &gt; vector
* ```a.x+1``` * ```a.x+1```

View file

@ -1,11 +1,12 @@
describe('draw a vector', () => { describe('draw a vector', () => {
it('adds the svg vector', () => { it('adds the svg vector', () => {
cy.visit('http://localhost:8080'); cy.visit('http://localhost:8080');
cy.get('#command_input').type("{enter}");
cy.get('#command_input').type("a = vector(0,0,0.5,0.5){enter}"); cy.get('#command_input').type("a = vector(0,0,0.5,0.5){enter}");
cy.get('#0').invoke('attr','d').should('eq','M550 350 L600 300'); cy.get('#0').invoke('attr','d').should('eq','M550 350 L600 300');
cy.get('#0').invoke('attr','class').should('eq','vector'); cy.get('#0').invoke('attr','class').should('eq','vector');
cy.get('#0').invoke('attr','marker-end').should('eq','url(#arrow)'); cy.get('#0').invoke('attr','marker-end').should('eq','url(#vector_arrow)');
cy.get('#command_input').type("b = vector(0,0,-1,1){enter}"); cy.get('#command_input').type("b = vector(0,0,-1,1){enter}");
cy.get('#1').invoke('attr','d').should('eq','M550 350 L450 250'); cy.get('#1').invoke('attr','d').should('eq','M550 350 L450 250');
@ -18,11 +19,12 @@ describe('draw a vector', () => {
describe('draw a lazy vector', () => { describe('draw a lazy vector', () => {
it('adds the svg vector', () => { it('adds the svg vector', () => {
cy.visit('http://localhost:8080'); cy.visit('http://localhost:8080');
cy.get('#command_input').type("{enter}");
cy.get('#command_input').type("a = vector(0,0,0.5,0.5){enter}"); cy.get('#command_input').type("a = [0.5,0.5]{enter}");
cy.get('#0').invoke('attr','d').should('eq','M550 350 L600 300'); cy.get('#0').invoke('attr','d').should('eq','M550 350 L600 300');
cy.get('#0').invoke('attr','class').should('eq','vector'); cy.get('#0').invoke('attr','class').should('eq','vector');
cy.get('#0').invoke('attr','marker-end').should('eq','url(#arrow)'); cy.get('#0').invoke('attr','marker-end').should('eq','url(#vector_arrow)');
cy.get('#command_input').type("b = vector(0,0,-1,1){enter}"); cy.get('#command_input').type("b = vector(0,0,-1,1){enter}");
cy.get('#1').invoke('attr','d').should('eq','M550 350 L450 250'); cy.get('#1').invoke('attr','d').should('eq','M550 350 L450 250');

View file

@ -4,11 +4,13 @@ body {
overflow: hidden; overflow: hidden;
} }
.right{
.right {
color: gray; color: gray;
position: absolute; position: absolute;
right: 0; right: 0;
} }
.background { .background {
position: absolute; position: absolute;
left: 0; left: 0;
@ -45,12 +47,21 @@ svg {
right: 10px; right: 10px;
bottom: 0; bottom: 0;
width: 40%; width: 40%;
height: 10em; height: 20em;
border: 2px solid darkgray; border: 2px solid darkgray;
border-radius: 10px; border-radius: 10px;
z-index: 10; z-index: 10;
} }
#slider {
position: absolute;
left: calc(50% - 1.5em);
top: -1em;
color: darkgray;
cursor: row-resize;
user-select: none;
}
.axis { .axis {
stroke-width: 1.5; stroke-width: 1.5;
stroke: lightpink; stroke: lightpink;
@ -62,12 +73,25 @@ svg {
stroke-linecap: round; stroke-linecap: round;
} }
#prompt{ .matrix {
stroke-width: 2.5;
stroke-linecap: round;
}
.matrix.i {
stroke: green;
}
.matrix.j {
stroke: red;
}
#prompt {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
} }
.multiline{ .multiline {
border-top: 1px slategray solid; border-top: 1px slategray solid;
border-left: 1px slategray solid; border-left: 1px slategray solid;
border-right: 1px slategray solid; border-right: 1px slategray solid;
@ -76,11 +100,11 @@ svg {
border-top-right-radius: 3px; border-top-right-radius: 3px;
} }
.single_line{ .single_line {
border: none transparent; border: none transparent;
} }
#command_input{ #command_input {
background: #111; background: #111;
color: greenyellow; color: greenyellow;
outline: none; outline: none;
@ -88,12 +112,12 @@ svg {
height: 1em; height: 1em;
} }
#command_history{ #command_history {
font-size: 12px; font-size: 12px;
color: greenyellow; color: greenyellow;
position: absolute; position: absolute;
bottom: 1.5em; bottom: 1.5em;
max-height: 9em; max-height: 20em;
width: 100%; width: 100%;
overflow-y: visible; overflow-y: visible;
overflow-x: hidden; overflow-x: hidden;

View file

@ -7,6 +7,7 @@
<body> <body>
<a class="right" href="https://github.com/shautvast/matrepl" target="_blank">https://github.com/shautvast/matrepl</a> <a class="right" href="https://github.com/shautvast/matrepl" target="_blank">https://github.com/shautvast/matrepl</a>
<div id="console"> <div id="console">
<div id="slider">___</div>
<div id="command_history"> <div id="command_history">
<div id="bottom"></div> <div id="bottom"></div>
</div> </div>

View file

@ -11,6 +11,9 @@ export const functions = {
return create_vector(args[0], args[1], args[2], args[3]); return create_vector(args[0], args[1], args[2], args[3]);
} }
}, },
id: () => {
return create_2d_id_matrix()
},
hide: (args) => { hide: (args) => {
return hide(args[0]); return hide(args[0]);
}, },
@ -59,13 +62,26 @@ export const show = function (vector) {
const help = function () { const help = function () {
return { return {
description: description:
`- vector(<x0>, <y0>, <x>, <y>): draws a vector from x0,y0 to x,y `- vector(x0, y0, x,y): draws a vector from x0,y0 to x,y
- remove(<identifier>|<ref>): removes an object, x0 and y0 default to 0 (the origin)
a ref is @n where n is the reference number asigned to the object` - [x,y] also draws a vector, but generally, [i,j,k...] defines an array
- remove(<identifier>|<ref>): removes an object,
a ref is @n where n is the reference number asigned to the object
- "..." is a lazy expression
Try the following:
a = [0.5, 0.5]
b = [-1,1]
c = "a+b"
a = a*2
=> when a is updated, c is too.
Now try dragging a vector using the mouse pointer and see what happens.
(NB dragging c won't work, because it is lazy)`
} }
} }
const create_vector = function (x0, y0, x, y) { //rename to create_vector export const create_vector = function (x0, y0, x, y) { //rename to create_vector
const vector = { const vector = {
id: index_sequence++, id: index_sequence++,
x0: x0, x0: x0,
@ -90,7 +106,8 @@ const create_vector = function (x0, y0, x, y) { //rename to create_vector
return show(this); return show(this);
}, },
equals: function (other) { 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; return vector;
@ -159,3 +176,35 @@ export const logical_and = function (left, right) {
export const logical_or = function (left, right) { export const logical_or = function (left, right) {
return 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];
}
}
}

View file

@ -1,9 +1,10 @@
import '../css/app.css'; import '../css/app.css';
import {scan, token_types} from './scanner'; import {scan, token_types} from './scanner';
import {parse} from './parser'; 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 { import {
addition, addition,
create_vector,
division, division,
functions, functions,
label, label,
@ -19,7 +20,7 @@ const bindings = {}; // binding -> {name:binding_name, evaluated: evaluated_l
const references = {}; // for retrieval of objects by reference (@n, where n is a number) const references = {}; // for retrieval of objects by reference (@n, where n is a number)
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 = 'help()';
let command_history = ['']; let command_history = [''];
let command_history_index = 0; let command_history_index = 0;
@ -27,7 +28,8 @@ const keywords = {
'true': true, 'true': true,
'false': false, 'false': false,
'pi': Math.PI, 'pi': Math.PI,
'PI': Math.PI 'PI': Math.PI,
'e': Math.E
} }
export const update_visible_objects = function () { export const update_visible_objects = function () {
@ -95,6 +97,9 @@ command_input_element.onkeypress = function handle_key_input(event) {
command_input_element.onkeyup = function handle_key_input(event) { command_input_element.onkeyup = function handle_key_input(event) {
adjust_input_element_height(); adjust_input_element_height();
if (event.key === 'c' && event.ctrlKey) {
command_input_element.value = '';
}
if (event.key === 'ArrowUp' && !event.shiftKey) { if (event.key === 'ArrowUp' && !event.shiftKey) {
if (command_history_index > -1) { if (command_history_index > -1) {
command_input_element.value = command_history[command_history_index]; command_input_element.value = command_history[command_history_index];
@ -133,41 +138,41 @@ const handle_enter = function () {
let tokens = scan(command); let tokens = scan(command);
let statement = parse(tokens); let statement = parse(tokens);
value = visit(statement); value = visit(statement);
let binding; if (value !== undefined) {
if (value.is_binding) { // if it's declaration work with the initializer let binding;
binding = value.name; // but we also need the name of the bound variable if (value.is_binding) { // if it's declaration work with the initializer
value = state[binding]; // lookup the value for the binding binding = value.name; // but we also need the name of the bound variable
} else if (Object.prototype.hasOwnProperty.call(value, ['id'])) { value = state[binding]; // lookup the value for the binding
references['@' + value.id] = value; } else if (Object.prototype.hasOwnProperty.call(value, ['id'])) {
} references['@' + value.id] = value;
while (value.lazy_expression) { }
value = value.get(); while (value.lazy_expression) {
} value = value.get();
if (binding) { }
bindings[binding].evaluated = value; // store evaluation result if (binding) {
} bindings[binding].evaluated = value; // store evaluation result
}
if (value.is_visual) { if (value.is_visual) {
if (binding && bindings[binding].previous && bindings[binding].previous.is_visual) { if (value.is_vector) {
update_vector_arrow(bindings[binding].previous.id, value); create_or_update_vector(binding, value);
} else if (value.is_matrix) {
create_or_update_matrix(binding, value);
}
} else { } else {
if (value.is_new) { if (binding && bindings[binding].previous && bindings[binding].previous.is_visual) {
value.label_text = binding ? binding : ""; label(bindings[binding].previous, '@' + bindings[binding].previous.id);
value.is_new = false;
add_vector_arrow(value);
} }
} }
} else { update_visible_objects();
if (binding && bindings[binding].previous && bindings[binding].previous.is_visual) { if (value.description) {
label(bindings[binding].previous, '@' + bindings[binding].previous.id); value = value.description;
} }
} else {
value = 'value is undefined';
} }
update_visible_objects(); } catch (exception) {
if (value.description) { value = exception.message;
value = value.description;
}
} catch (e) {
value = e.message;
} }
command_history_element.innerText += value.toString() + "\n"; command_history_element.innerText += value.toString() + "\n";
command_history.push(command); command_history.push(command);
@ -269,6 +274,15 @@ const visit = function (expr) {
case 'reference': { case 'reference': {
return references[expr.name]; return references[expr.name];
} }
case 'array': { //unsure this is what I want
let array = expr.elements.map(x => visit(x));
if (array.length === 2) {
return create_vector(0, 0, array[0], array[1]);
} else {
return array;
}
}
} }
} }
@ -309,3 +323,27 @@ const resolve_arguments = function (argument_exprs) {
return value; 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);
}
}
}

View file

@ -110,7 +110,6 @@ export const parse = function (tokens) {
return {type: 'call', name: callee, arguments: arguments_list}; return {type: 'call', name: callee, arguments: arguments_list};
} }
function primary() { function primary() {
if (match([token_types.NUMERIC, token_types.STRING])) { if (match([token_types.NUMERIC, token_types.STRING])) {
return {type: 'literal', value: previous_token().value, value_type: previous_token().type}; return {type: 'literal', value: previous_token().value, value_type: previous_token().type};
@ -147,6 +146,23 @@ export const parse = function (tokens) {
}; };
advance(); advance();
return result; return result;
} else if (match([token_types.LEFT_BRACKET])) {
let array = [];
if (!check(token_types.RIGHT_BRACKET, token_index)) {
let result;
do {
result = expression();
if (result) {
array.push(result);
} else {
throw {message: "Expect ']' after array elements."};
}
match([token_types.COMMA]);
} while (!match([token_types.RIGHT_BRACKET]));
}
return {type: 'array', elements: array};
} }
} }

View file

@ -1,15 +1,17 @@
import {update_visible_objects} from "./index"; import {update_visible_objects} from "./index";
let vectors_by_id = {}; let vectors_by_id = {};
let matrices_by_id = {};
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
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 moving_vector; // user can move vector arrows. when moving, this refers to the arrow
let width = window.innerWidth, height = window.innerHeight; let window_width = window.innerWidth, window_height = window.innerHeight;
let origin_x = Math.floor((width / grid_size) / 2) * grid_size + half_grid_size, let origin_x = Math.floor((window_width / grid_size) / 2) * grid_size + half_grid_size,
origin_y = Math.floor((height / 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 * Creates an svg element
* @param element_type path,g, etc * @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 x1 end_x
* @param y1 end_y * @param y1 end_y
* @param css_class class attribute * @param css_class class attribute
* @param arrow_def SVG definition of the arrow head
* @returns {SVGPathElement} * @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'); let path = create_svg_element('path');
path.setAttribute('d', calculate_d(x0, y0, x1, y1)); path.setAttribute('d', calculate_d(x0, y0, x1, y1));
path.id = id; path.id = id;
path.setAttribute('class', css_class); path.setAttribute('class', css_class);
path.setAttribute('marker-end', 'url(#arrow)'); path.setAttribute('marker-end', arrow_def);
return path; return path;
} }
@ -118,31 +121,27 @@ export const create_grid = function (css_class, bg_css_class) {
group.setAttribute('id', 'grid'); group.setAttribute('id', 'grid');
const horizontal = create_svg_element('g'); const horizontal = create_svg_element('g');
horizontal.setAttribute('id', 'horizontal'); horizontal.setAttribute('id', 'horizontal');
for (let y = 0; y < height; y += grid_size) { for (let y = 0; y < window_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 + half_grid_size, window_width, y + half_grid_size, css_class));
horizontal.appendChild(create_line(0, y, width, y, bg_css_class)); horizontal.appendChild(create_line(0, y, window_width, y, bg_css_class));
} }
group.appendChild(horizontal); group.appendChild(horizontal);
const vertical = create_svg_element('g'); const vertical = create_svg_element('g');
vertical.setAttribute('id', 'vertical'); vertical.setAttribute('id', 'vertical');
for (let x = 0; x < width; x += grid_size) { for (let x = 0; x < window_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 + half_grid_size, 0, x + half_grid_size, window_height, css_class));
vertical.appendChild(create_line(x, 0, x, height, bg_css_class)); vertical.appendChild(create_line(x, 0, x, window_height, bg_css_class));
} }
group.appendChild(vertical); group.appendChild(vertical);
return group; 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'); let label = create_svg_element('text');
label.setAttribute('x', (calc_screen_x(vector.x) + 5).toString()); label.setAttribute('x', (calc_screen_x(vector.x) - 5).toString());
label.setAttribute('y', (calc_screen_y(vector.y) + 5).toString()); label.setAttribute('y', (calc_screen_y(vector.y) + 15).toString());
label.setAttribute('fill', 'yellow'); label.setAttribute('fill', color);
label.setAttribute('id', 'l' + vector.id); label.setAttribute('id', 'l' + vector.id);
let text_node = document.createTextNode(vector.label_text); let text_node = document.createTextNode(vector.label_text);
label.appendChild(text_node); 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) { export const add_vector_arrow_to_svg = function (vector) {
let vector_group = get_or_create_vector_group(); 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) { vector_arrow.onmousedown = function start_moving_vector(event) {
moving_vector = event.target; moving_vector = event.target;
window.onmousemove = move;
window.onmouseup = function () {
window.onmousemove = undefined;
window.onmouseup = undefined;
};
}; };
vector_group.appendChild(vector_arrow); vector_group.appendChild(vector_arrow);
let label = create_label(vector); let label = create_label(vector, 'yellow');
vector_group.appendChild(label); 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. * Draws all the vectors.
*
* vector {
* x0,y0 origin
* x,y coordinates
* }
*/ */
const draw_vectors = function () { const draw_vectors = function () {
const vector_group = get_or_create_vector_group(); const vector_group = get_or_create_vector_group();
@ -196,6 +218,21 @@ const draw_vectors = function () {
svg.appendChild(vector_group); 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 () { const get_or_create_vector_group = function () {
let vector_group = document.getElementById('vectors'); let vector_group = document.getElementById('vectors');
if (vector_group === null || vector_group === undefined) { if (vector_group === null || vector_group === undefined) {
@ -207,6 +244,17 @@ const get_or_create_vector_group = function () {
return vector_group; 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. * 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'); remove_child(svg, 'vectors');
draw_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. * 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 () { export const redraw = function () {
redraw_grid(); redraw_grid();
redraw_vectors(); redraw_vectors();
redraw_matrices();
} }
const create_axes = function () { const create_axes = function () {
let axes_group = create_svg_element('g'); let axes_group = create_svg_element('g');
axes_group.setAttribute('id', 'axes'); 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'; x.id = 'x-axis';
axes_group.appendChild(x); 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'; y.id = 'y-axis';
axes_group.appendChild(y); axes_group.appendChild(y);
return axes_group; return axes_group;
@ -262,10 +319,10 @@ const create_axes = function () {
* setup the arrow head for the vector * setup the arrow head for the vector
* @returns {SVGDefsElement} * @returns {SVGDefsElement}
*/ */
const create_defs = function () { const create_defs = function (type, color) {
let defs = create_svg_element('defs'); let defs = create_svg_element('defs');
let marker = create_svg_element('marker'); let marker = create_svg_element('marker');
marker.id = 'arrow'; marker.id = type;
marker.setAttribute('orient', 'auto'); marker.setAttribute('orient', 'auto');
marker.setAttribute('viewBox', '0 0 10 10'); marker.setAttribute('viewBox', '0 0 10 10');
marker.setAttribute('markerWidth', '3'); marker.setAttribute('markerWidth', '3');
@ -275,30 +332,25 @@ const create_defs = function () {
marker.setAttribute('refY', '5'); marker.setAttribute('refY', '5');
let polyline = create_svg_element('polyline'); let polyline = create_svg_element('polyline');
polyline.setAttribute('points', '0,0 10,5 0,10 1,5'); polyline.setAttribute('points', '0,0 10,5 0,10 1,5');
polyline.setAttribute('fill', 'yellow'); polyline.setAttribute('fill', color);
marker.appendChild(polyline); marker.appendChild(polyline);
defs.appendChild(marker); defs.appendChild(marker);
return defs; return defs;
} }
/** const move = function (event) {
* The moving operation. Called by onmousemove on the svg ('canvas') let current_x = event.offsetX;
* @param event let current_y = event.offsetY;
*/ let vector = vectors_by_id[parseInt(moving_vector.id)];
const move_vector = function (event) { if (vector) {
if (moving_vector) { vector.x = (current_x - origin_x) / grid_size;
let current_x = event.offsetX; vector.y = (origin_y - current_y) / grid_size;
let current_y = event.offsetY; moving_vector.setAttribute('d', create_d(origin_x, origin_y, current_x, current_y));
let vector = vectors_by_id[parseInt(moving_vector.id)]; update_label(moving_vector.id, moving_vector.id, current_x + 5, current_y + 5);
if (vector) { update_visible_objects();
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 * Creates the SVG
* @returns {SVGElement} * @returns {SVGElement}
@ -306,21 +358,32 @@ const move_vector = function (event) {
const create_svg = function () { const create_svg = function () {
let svg = create_svg_element('svg'); let svg = create_svg_element('svg');
svg.onmousemove = move_vector; svg.onmousemove = move;
svg.onmouseup = function stop_moving_vector() { svg.onmouseup = function stop_moving() {
moving_vector = undefined; moving_vector = undefined;
}; };
let defs = create_defs(); svg.appendChild(create_defs('vector_arrow', 'yellow'));
svg.appendChild(defs); svg.appendChild(create_defs('matrix_i_arrow', 'green'));
svg.appendChild(create_defs('matrix_j_arrow', 'red'));
return svg; 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() { document.body.onresize = function recalculate_window_dimensions() {
width = window.innerWidth; window_width = window.innerWidth;
height = window.innerHeight; window_height = window.innerHeight;
origin_x = Math.floor((width / grid_size) / 2) * grid_size + half_grid_size; origin_x = Math.floor((window_width / grid_size) / 2) * grid_size + half_grid_size;
origin_y = Math.floor((height / grid_size) / 2) * grid_size + half_grid_size; origin_y = Math.floor((window_height / grid_size) / 2) * grid_size + half_grid_size;
redraw(); redraw();
} }
@ -329,3 +392,18 @@ 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());
get_or_create_vector_group(); 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;
};
};