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