Compare commits
10 commits
86d0853693
...
22c9e57995
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22c9e57995 | ||
|
|
f9a3cbf24c | ||
|
|
b412b11941 | ||
|
|
ca4fe5f490 | ||
|
|
d7ecddbad5 | ||
|
|
3651b7fe18 | ||
|
|
83bbc5edab | ||
|
|
30a74579f9 | ||
|
|
720d3a647f | ||
|
|
827d24742b |
8 changed files with 322 additions and 111 deletions
11
README.md
11
README.md
|
|
@ -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```
|
||||||
> > vector
|
> > vector
|
||||||
* ```a.x+1```
|
* ```a.x+1```
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
|
|
|
||||||
|
|
@ -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,6 +73,19 @@ svg {
|
||||||
stroke-linecap: round;
|
stroke-linecap: round;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.matrix {
|
||||||
|
stroke-width: 2.5;
|
||||||
|
stroke-linecap: round;
|
||||||
|
}
|
||||||
|
|
||||||
|
.matrix.i {
|
||||||
|
stroke: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.matrix.j {
|
||||||
|
stroke: red;
|
||||||
|
}
|
||||||
|
|
||||||
#prompt {
|
#prompt {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
@ -93,7 +117,7 @@ svg {
|
||||||
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;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
x0 and y0 default to 0 (the origin)
|
||||||
|
- [x,y] also draws a vector, but generally, [i,j,k...] defines an array
|
||||||
- remove(<identifier>|<ref>): removes an object,
|
- remove(<identifier>|<ref>): removes an object,
|
||||||
a ref is @n where n is the reference number asigned to the 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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,6 +138,7 @@ 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);
|
||||||
|
if (value !== undefined) {
|
||||||
let binding;
|
let binding;
|
||||||
if (value.is_binding) { // if it's declaration work with the initializer
|
if (value.is_binding) { // if it's declaration work with the initializer
|
||||||
binding = value.name; // but we also need the name of the bound variable
|
binding = value.name; // but we also need the name of the bound variable
|
||||||
|
|
@ -148,14 +154,10 @@ const handle_enter = function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
} else if (value.is_matrix) {
|
||||||
if (value.is_new) {
|
create_or_update_matrix(binding, value);
|
||||||
value.label_text = binding ? binding : "";
|
|
||||||
value.is_new = false;
|
|
||||||
add_vector_arrow(value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (binding && bindings[binding].previous && bindings[binding].previous.is_visual) {
|
if (binding && bindings[binding].previous && bindings[binding].previous.is_visual) {
|
||||||
|
|
@ -166,8 +168,11 @@ const handle_enter = function () {
|
||||||
if (value.description) {
|
if (value.description) {
|
||||||
value = value.description;
|
value = value.description;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} else {
|
||||||
value = e.message;
|
value = 'value is undefined';
|
||||||
|
}
|
||||||
|
} catch (exception) {
|
||||||
|
value = exception.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,18 +332,13 @@ 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')
|
|
||||||
* @param event
|
|
||||||
*/
|
|
||||||
const move_vector = function (event) {
|
|
||||||
if (moving_vector) {
|
|
||||||
let current_x = event.offsetX;
|
let current_x = event.offsetX;
|
||||||
let current_y = event.offsetY;
|
let current_y = event.offsetY;
|
||||||
let vector = vectors_by_id[parseInt(moving_vector.id)];
|
let vector = vectors_by_id[parseInt(moving_vector.id)];
|
||||||
|
|
@ -298,7 +350,7 @@ const move_vector = function (event) {
|
||||||
update_visible_objects();
|
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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue