First commit
This commit is contained in:
commit
3176e4fdf6
8 changed files with 510 additions and 0 deletions
32
README.md
Normal file
32
README.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# lichartee
|
||||||
|
|
||||||
|
Clone of [matplotlib](https://matplotlib.org) in javascript
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
Download or clone this repo, then open [index.html](index.html) in your
|
||||||
|
favorite browser.
|
||||||
|
|
||||||
|
[test.js](test.js) present an example use of this library.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
WIP
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
- linarphy - *initial work*
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
We use [Semver](http://semver.org) for versioning.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensied under the Chocolate-Ware license - see the
|
||||||
|
[LICENSE](LICENSE) file for more information.
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
*v.0.1*
|
||||||
|
- creation of a basic layout
|
87
favicon.svg
Normal file
87
favicon.svg
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
<!-- Edited with neovim (Neovide for windows) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="500"
|
||||||
|
height="500"
|
||||||
|
version="2.0"
|
||||||
|
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<style>
|
||||||
|
path
|
||||||
|
{
|
||||||
|
fill: #000000;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark)
|
||||||
|
{
|
||||||
|
path
|
||||||
|
{
|
||||||
|
fill: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<title>
|
||||||
|
lichartee logo
|
||||||
|
</title>
|
||||||
|
<path
|
||||||
|
d="M 250,500 H 0 V 0 L 73.2233,73.2233 V 426.7767 c 0,0 60.78,72.95 179.20,72.19"
|
||||||
|
id="bar_left" />
|
||||||
|
<path
|
||||||
|
d="M 250,500 H 500 V 0 L 426.7767,73.2233 V 426.7767 c 0,0 -58.26,72.95 -177.94,72.19"
|
||||||
|
id="bar_right" />
|
||||||
|
<path
|
||||||
|
d="M 375,125 250,250 125,125 c -33.527221,33.35835 -52.451433,78.65491 -52.62,125.95 0,98.62149 79.94851,178.57 178.57,178.57 98.62149,0 178.57,-79.94851 178.57,-178.57 C 429.35143,203.65491 408.52722,158.35835 375,125 Z"
|
||||||
|
id="circle" />
|
||||||
|
<path
|
||||||
|
d="M 250,0 125,125 c 34.60338,-29.864158 79.34131,-45.322007 125,-43.19 46.91963,0.333871 86.51935,10.011293 125,43.19 z"
|
||||||
|
id="top" />
|
||||||
|
<metadata>
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:title>lichartee logo</dc:title>
|
||||||
|
<dc:date>2023/12/30</dc:date>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>linarphy</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
<dc:subject>
|
||||||
|
<rdf:Bag>
|
||||||
|
<rdf:li>logo</rdf:li>
|
||||||
|
<rdf:li>char</rdf:li>
|
||||||
|
<rdf:li>lichartee</rdf:li>
|
||||||
|
</rdf:Bag>
|
||||||
|
</dc:subject>
|
||||||
|
<dc:rights>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>ArtLibre</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:rights>
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://artlibre.org/licence/lal" />
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://artlibre.org/licence/lal">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
9
humans.txt
Normal file
9
humans.txt
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/* TEAM */
|
||||||
|
Dev: linarphy
|
||||||
|
Fediverse: @linarphy@linarphy.net
|
||||||
|
|
||||||
|
/* SITE */
|
||||||
|
Last update: 2023/12/30
|
||||||
|
Language: English
|
||||||
|
Doctype: HTML5
|
||||||
|
IDE: neovim
|
29
index.html
Normal file
29
index.html
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="description"
|
||||||
|
content="A small demo of this plotting library" />
|
||||||
|
<meta name="author" content="linarphy" />
|
||||||
|
<meta name="keywords" content="chart,plot,js,library" />
|
||||||
|
<meta name="generator" content="neovim" />
|
||||||
|
<link type="text/plain" rel="author" href="humans.txt" />
|
||||||
|
<link type="image/svg+xml" rel="icon" href="favicon.svg" />
|
||||||
|
<link rel="stylesheet" href="main.css" />
|
||||||
|
<title>Chart demo</title>
|
||||||
|
<script src="main.js"></script>
|
||||||
|
<!-- Added by include() -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
Multiple plot
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
<a href="https://git.linarphy.net/linarphy/lichartee" title="Source of this code">
|
||||||
|
code
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
281
lichartee.js
Normal file
281
lichartee.js
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
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
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.moveTo(
|
||||||
|
start_x,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
this.ctx.lineTo(
|
||||||
|
start_x ,
|
||||||
|
this.ctx.canvas.height
|
||||||
|
);
|
||||||
|
let end_x = start_x + this.ctx.canvas.width * x_proportion
|
||||||
|
this.ctx.lineTo(
|
||||||
|
end_x ,
|
||||||
|
this.ctx.canvas.height
|
||||||
|
);
|
||||||
|
this.ctx.lineTo(
|
||||||
|
end_x,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
this.ctx.lineTo(
|
||||||
|
start_x,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
this.ctx.strokeStyle = 'rgb(0,0,0)';
|
||||||
|
this.ctx.stroke();
|
||||||
|
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 = new Style();
|
||||||
|
}
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ctx.moveTo(
|
||||||
|
x_coordinates[0],
|
||||||
|
y_coordinates[0]
|
||||||
|
);
|
||||||
|
for (const index in x_coordinates)
|
||||||
|
{
|
||||||
|
ctx.lineTo(
|
||||||
|
x_coordinates[index],
|
||||||
|
y_coordinates[index]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ctx.strokeStyle = this.linestyle.color;
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Style
|
||||||
|
{
|
||||||
|
constructor(color = "rgb(0,0,0)", style = 'solid')
|
||||||
|
{
|
||||||
|
this.color = color;
|
||||||
|
this.style = style;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_ext_array(array, f = Math.max, sign = -1)
|
||||||
|
{
|
||||||
|
return array.reduce(
|
||||||
|
(a, b) => f(a, b),
|
||||||
|
sign * Infinity
|
||||||
|
);
|
||||||
|
}
|
1
main.css
Normal file
1
main.css
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
58
main.js
Normal file
58
main.js
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* GLOBAL VARIABLE!
|
||||||
|
* list of loaded scripts (only itself at start)
|
||||||
|
**/
|
||||||
|
var __scripts = [ 'main.js' ];
|
||||||
|
//var __lang = 'en';
|
||||||
|
|
||||||
|
/* i18n small function */
|
||||||
|
function _(key)
|
||||||
|
{
|
||||||
|
return key;
|
||||||
|
//return LANG[__lang][key] == undefined ? key : LANG[__lang][key];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* include other scripts to the html file */
|
||||||
|
async function include(src, script_list)
|
||||||
|
{
|
||||||
|
/* DOM operations */
|
||||||
|
let script = document.createElement('script');
|
||||||
|
script.setAttribute('src' , src );
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(script);
|
||||||
|
|
||||||
|
/* check if the element is added to the DOM */
|
||||||
|
let script_loaded = new Promise( (resolve, reject) => {
|
||||||
|
if (script !== undefined)
|
||||||
|
{
|
||||||
|
script.addEventListener('load', () => {
|
||||||
|
resolve(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reject(
|
||||||
|
_(
|
||||||
|
'an error occured when adding the script element' +
|
||||||
|
'for ${src}'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
script_list.push(src); // the script is considered as loaded
|
||||||
|
|
||||||
|
return script_loaded.then(
|
||||||
|
(value) => script_list,
|
||||||
|
(error) => {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function launch()
|
||||||
|
{
|
||||||
|
__scripts = await include('./lichartee.js', __scripts);
|
||||||
|
__scripts = await include('./test.js', __scripts);
|
||||||
|
}
|
||||||
|
|
||||||
|
launch()
|
13
test.js
Normal file
13
test.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
let manager = new Manager(
|
||||||
|
document.getElementsByTagName('main')[0]
|
||||||
|
);
|
||||||
|
manager.add_canvas(500, 500);
|
||||||
|
manager.add_canvas(500, 500).figure.add_axes().plot(
|
||||||
|
[0,1,2,3,4,5] ,
|
||||||
|
[0,1,4,9,16,25],
|
||||||
|
);
|
||||||
|
manager.list_canvas[0].figure.list_axes[0].plot(
|
||||||
|
[0,1,2,3,4,5],
|
||||||
|
[0,2,4,6,8,10]
|
||||||
|
);
|
||||||
|
manager.draw();
|
Loading…
Reference in a new issue