lichartee/lichartee.js
2023-12-30 22:59:50 +01:00

280 lines
5.6 KiB
JavaScript

class Manager
{
constructor(parent_element)
{
this.parent_element = parent_element;
this.list_canvas = [];
}
/**
* add a canvas
*
* @param int width Width of the added canvas
* @param int height Height of the added canvas
* @param int position Subjective position of the added canvas
*/
add_canvas(width = 100, height = 100, position = 0)
{
let element_canvas = document.createElement('canvas');
let text = document.createTextNode(
'Your browser doesn\'t seem to support canvas, or you ' +
'deactivated them. This is the canvas where the chart ' +
'should be displayed'
);
element_canvas.appendChild(text);
if (this.parent_element.childElementCount === 0)
{
this.parent_element.appendChild(element_canvas);
}
else
{
this.parent_element.children[position].before(
element_canvas
);
}
let ctx = element_canvas.getContext('2d');
ctx.canvas.width = width ;
ctx.canvas.height = height;
let figure = new Figure();
let canvas = new Canvas(ctx, figure);
this.list_canvas.splice(position, 0, canvas);
return canvas;
}
draw()
{
for (const index in this.list_canvas)
{
this.list_canvas[index].draw();
}
}
remove_canvas(position = 0)
{
document.removeChild(this.list_canvas[position].ctx.canvas);
this.list_canvas.splice(position, 1);
}
}
class Canvas
{
constructor(ctx, figure)
{
this.ctx = ctx ;
this.figure = figure;
}
draw()
{
if (this.figure instanceof Figure.constructor)
{
throw _('this canvas does not possess figure to draw')
}
let start_x = 0;
for (const index_axes in this.figure.list_axes)
{
let axes = this.figure.list_axes[index_axes];
let x_proportion = axes.width/this.figure.width;
let end_x = start_x + this.ctx.canvas.width * x_proportion;
this.ctx.beginPath();
this.ctx.strokeStyle = 'black';
this.ctx.setLineDash([]);
this.ctx.strokeRect(
start_x, 0 ,
end_x, this.ctx.canvas.height
);
const x_min = get_ext_array(
axes.lines.map(
(element) => element.x.reduce(
(a, b) => Math.min(a, b),
Infinity
)
) ,
Math.min,
1
);
const y_min = get_ext_array(
axes.lines.map(
(element) => element.y.reduce(
(a, b) => Math.min(a, b),
Infinity
)
) ,
Math.min,
1
);
const x_max = get_ext_array(
axes.lines.map(
(element) => element.x.reduce(
(a, b) => Math.max(a, b),
-Infinity
)
)
);
const y_max = get_ext_array(
axes.lines.map(
(element) => element.y.reduce(
(a, b) => Math.max(a, b),
-Infinity
)
)
);
for (const index_line in axes.lines)
{
axes.lines[index_line].draw(
this.ctx ,
[[start_x, 0],[end_x, this.ctx.canvas.height]],
[[x_min, y_min], [x_max, y_max]]
);
}
start_x = end_x;
}
}
}
class Figure
{
constructor()
{
this.width = 0 ;
this.height = 0 ;
this.list_axes = [];
}
add_axes(relative_width = 50, relative_height = 50, position = 0)
{
this.width += relative_width ;
this.height += relative_height;
let axes = new Axes(relative_width, relative_height);
this.list_axes.splice(position, 0, axes);
return axes;
}
remove_axes(position)
{
this.list_axes.splice(position, 1);
}
}
class Axes
{
constructor(
width = 100 ,
height = 100 ,
position = 0 ,
title = '' ,
)
{
this.width = width ;
this.height = height ;
this.position = position;
this.title = title ;
this.lines = [] ;
}
plot(x, y, linestyle = undefined, label = '')
{
if (linestyle === undefined)
{
linestyle = {
color: 'rgb(0,0,0)',
style: 'solid'
};
}
let line = new Line(x, y, linestyle, label);
this.lines.push(line);
}
}
class Line
{
constructor(
x ,
y ,
linestyle = undefined ,
label = ''
)
{
this.x = x ;
this.y = y ;
this.linestyle = linestyle ;
this.label = label ;
}
/**
* draw the line in the box coordinate
*
* @param ctx ctx Context of the canvas
* @param 2dlist<int> box Box where to draw the line
* @param 2dlist<float> view Scale of the box (link value <->
* coordinate
*/
draw(ctx, box, view)
{
const x_delta = view[1][0] - view[0][0];
const y_delta = view[1][1] - view[0][1];
const box_width = box[1][0] - box[0][0];
const box_height = box[1][1] - box[0][1];
let x_coordinates = this.x.map(
function (element, index, array)
{
return box[0][0] + box_width * (
element - view[0][0]
) / x_delta;
}
);
let y_coordinates = this.y.map(
function (element, index, array)
{
return box[0][1] + box_height - ( // starting top left
box_height * (
element - view[0][1]
) / y_delta
);
}
);
if (this.linestyle.style === 'solid')
{
ctx.setLineDash([]);
}
else if (this.linestyle.style === 'dashed')
{
ctx.setLineDash([15, 5]);
}
else if (this.linestyle.style === 'dotted')
{
ctx.setLineDash([2,2]);
}
else if (this.linestyle.style === 'dashdot')
{
ctx.setLineDash([15,2,2,2]);
}
else
{
throw _('the style of the line is not correctly defined');
}
ctx.strokeStyle = this.linestyle.color;
ctx.beginPath();
ctx.moveTo(
x_coordinates[0],
y_coordinates[0]
);
for (const index in x_coordinates)
{
ctx.lineTo(
x_coordinates[index],
y_coordinates[index]
);
}
ctx.stroke();
}
}
function get_ext_array(array, f = Math.max, sign = -1)
{
return array.reduce(
(a, b) => f(a, b),
sign * Infinity
);
}