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 box Box where to draw the line * @param 2dlist 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 ); }