├── README.md
├── LICENSE
├── jmat_test.js
├── jmat_demo.html
└── jmat_plot.js
/README.md:
--------------------------------------------------------------------------------
1 | # Jmat.js
2 |
3 | Complex special functions, numerical linear algebra and statistics in JavaScript
4 |
5 | See demo at:
6 | http://lodev.org/jmat/jmat_demo.html
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014, lvandeve
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 |
14 | * Neither the name of the {organization} nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/jmat_test.js:
--------------------------------------------------------------------------------
1 | /*
2 | Jmat.js
3 |
4 | Copyright (c) 2011-2014, Lode Vandevenne
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification,
8 | are permitted provided that the following conditions are met:
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 | 3. The name of the author may not be used to endorse or promote products
15 | derived from this software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | // Unit tests for Jmat.js
30 |
31 | // constructor
32 | Jmat.Test = function() {
33 | // empty, it's a namespace
34 | };
35 |
36 | // Works both for Complex or Matrix objects.
37 | Jmat.Test.expectNear = function(e, a, epsilon) {
38 | if(Jmat.isNaN(e) && Jmat.isNaN(a)) return; //both NaN is ok for test
39 | if(!Jmat.near(e, a, epsilon)) throw 'fail: expected ' + Jmat.toString(e) + ' got ' + Jmat.toString(a);
40 | };
41 |
42 | // Expect that the result of the mathematical function f with the arguments of var_arg, is near the expected result. Some numerical intolerance is allowed.
43 | Jmat.Test.testFunction = function(expected, epsilon, f, var_arg) {
44 | var result = f.apply(this, Array.prototype.slice.call(arguments).slice(3) /*var_arg*/);
45 | Jmat.Test.expectNear(expected, result, epsilon);
46 | };
47 |
48 | //u,s,v = expected values
49 | //m = input matrix
50 | Jmat.Test.testSVD = function(u, s, v, epsilon, m) {
51 | var svd = Matrix.svd(Matrix.cast(m));
52 | Jmat.Test.expectNear(u, svd.u, epsilon);
53 | Jmat.Test.expectNear(s, svd.s, epsilon);
54 | Jmat.Test.expectNear(v, svd.v, epsilon);
55 | };
56 |
57 | // throws on fail, prints 'success' on success
58 | Jmat.doUnitTest = function() {
59 | // check that the test framework itself can actually fail
60 | var thrown = false;
61 | try {
62 | Jmat.Test.testFunction(3, eps, Jmat.add, 1, 1);
63 | } catch(error) {
64 | thrown = true; // this is expected
65 | }
66 | if(!thrown) throw 'that should have thrown error!';
67 |
68 | var eps = 1e-10;
69 | // basic operators
70 | Jmat.Test.testFunction(5, eps, Jmat.add, 2, 3);
71 | Jmat.Test.testFunction(6, eps, Jmat.mul, 2, 3);
72 |
73 | // advanced functions
74 | Jmat.Test.testFunction(0, eps, Jmat.sin, Math.PI);
75 |
76 | // special functions
77 | Jmat.Test.testFunction(24, eps, Jmat.gamma, 5);
78 | Jmat.Test.testFunction('-0.15494982830181-0.498015668118356i', eps, Jmat.gamma, 'i');
79 | Jmat.Test.testFunction('0.9303796037430951+0.0389361908951213i', 1e-5, Jmat.erf, '5+5i');
80 |
81 | // distributions
82 | Jmat.Test.testFunction(0.274997, 1e-2, Jmat.qf_chi_square, 0.4, 1); // This one is very imprecise currently :(
83 | Jmat.Test.testFunction(0.198964, 1e-6, Jmat.pdf_studentt, 0.5, 0.5); // This one is very imprecise currently :(
84 |
85 | // matrix basic operators
86 | Jmat.Test.testFunction([[6,8],[10,12]], eps, Jmat.add, [[1,2],[3,4]], [[5,6],[7,8]]);
87 | Jmat.Test.testFunction([[19,22],[43,50]], eps, Jmat.mul, [[1,2],[3,4]], [[5,6],[7,8]]);
88 |
89 | // matrix advanced operators
90 | Jmat.Test.testFunction([[-2,1],[1.5,-0.5]], eps, Jmat.inv, [[1,2],[3,4]]);
91 | Jmat.Test.testFunction([[5,-1],[-2,0]], eps, Jmat.fft, [[1,2],[3,4]]);
92 | Jmat.Test.testSVD([[0.404554, 0.914514], [0.914514, -0.404554]],
93 | [[5.46499, 0],[0,0.365966]],
94 | [[0.576048, -0.817416],[0.817416, 0.576048]],
95 | 1e-5, [[1,2],[3,4]]);
96 | Jmat.Test.testSVD([[1]],
97 | [[2.23607, 0]],
98 | [[0.447214, -0.894427], [0.894427, 0.447214]],
99 | 1e-5, [[1,2]]);
100 | Jmat.Test.testSVD([[0.447214, -0.894427], [0.894427, 0.447214]],
101 | [[2.23607], [0]],
102 | [[1]],
103 | 1e-5, [[1],[2]]);
104 |
105 | // matrix parsing
106 | Jmat.Test.testFunction([[1,2],[3,4]], eps, Jmat.Matrix.parse, '[[1,2],[3,4]]');
107 | Jmat.Test.testFunction([[1,2,3,4]], eps, Jmat.Matrix.parse, '[[1,2,3,4]]');
108 | Jmat.Test.testFunction([[1],[2],[3],[4]], eps, Jmat.Matrix.parse, '[1,2,3,4]');
109 |
110 | // numerical algorithms
111 | Jmat.Test.testFunction(333.33333333333, eps, Jmat.integrate, 0, 10, function(z) { return z.mul(z); });
112 |
113 |
114 | console.log('success');
115 | return 'success';
116 | };
117 |
--------------------------------------------------------------------------------
/jmat_demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Jmat.js Demo
6 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
255 |
256 |
257 |
258 |
--------------------------------------------------------------------------------
/jmat_plot.js:
--------------------------------------------------------------------------------
1 | /*
2 | Jmat.js
3 |
4 | Copyright (c) 2011-2014, Lode Vandevenne
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification,
8 | are permitted provided that the following conditions are met:
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 | 3. The name of the author may not be used to endorse or promote products
15 | derived from this software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | // For documentation, see jmat.js
30 | // NOTE: requires charset="utf-8" to render navigation arrow characters correctly
31 |
32 | ////////////////////////////////////////////////////////////////////////////////
33 | ////////////////////////////////////////////////////////////////////////////////
34 | ////////////////////////////////////////////////////////////////////////////////
35 | // Plotting - public API
36 | ////////////////////////////////////////////////////////////////////////////////
37 | ////////////////////////////////////////////////////////////////////////////////
38 |
39 | // TODO: support any size rather than always 320x320 pixels
40 |
41 | // e.g.: new Jmat.PlotParams({p: 1})
42 | // constructor
43 | Jmat.PlotParams = function(o) {
44 | if(!o) o = {};
45 |
46 | this.p = o.p != undefined ? o.p : 2; //pixel size (==> resolution). 1 = highest resolution, 2 = half resolution, etc...
47 |
48 | this.xsize = o.xsize != undefined ? o.xsize : 20; // e.g. an xsize of 10 makes it go from x=-5 to +5 if shift is 0
49 | this.ysize = o.ysize != undefined ? o.ysize : 20;
50 | if(o.s && !o.xsize && !o.ysize) this.xsize = this.ysize = o.s; // 's' is shortcut to make both that size
51 | this.xshift = o.xshift != undefined ? o.xshift : 0; // point which you want in the center of the plot
52 | this.yshift = o.yshift != undefined ? o.yshift : 0;
53 |
54 | // The value at which the complex color wheel has highest saturation (pure red for positive real). Higher value gives more white color, lower gives darker color.
55 | // Not used by real plot.
56 | this.v = o.v != undefined ? o.v : 1;
57 | };
58 |
59 | //fun = mathematical function taking 1 Jmat.Complex arguments, e.g. Jmat.sin
60 | //params = parameter object with plot size, resolution, etc.... See Jmat.PlotParams.
61 | //parent = HTML parent element, best of type div. All necessary elements (e.g. canvas) will be created inside of it.
62 | Jmat.plotReal = function(fun, parent, params, label) {
63 | if(!params) params = new Jmat.PlotParams();
64 | if(!(params instanceof Jmat.PlotParams)) {
65 | params = new Jmat.PlotParams(params);
66 | }
67 | if(!parent) parent = document.body;
68 | Jmat.Plot.plotReal_(fun, params, parent, label);
69 | };
70 |
71 | //fun = mathematical function taking 1 Jmat.Complex arguments, e.g. Jmat.gamma
72 | //params = parameter object with plot size, resolution, etc.... See Jmat.PlotParams.
73 | //parent = HTML parent element, best of type div. All necessary elements (e.g. canvas) will be created inside of it.
74 | Jmat.plotComplex = function(fun, parent, params, label) {
75 | if(!params) params = new Jmat.PlotParams({p: 4});
76 | if(!(params instanceof Jmat.PlotParams)) {
77 | params = new Jmat.PlotParams(params);
78 | }
79 | if(!parent) parent = document.body;
80 | Jmat.Plot.plot2D_(function(x, y) {
81 | return fun(Jmat.Complex(x.re, y.re));
82 | }, params, parent, label, 're', 'im');
83 | };
84 |
85 | //fun = mathematical function taking 2 Jmat.Complex arguments, e.g. Jmat.besselj
86 | //params = parameter object with plot size, resolution, etc.... See Jmat.PlotParams.
87 | //parent = HTML parent element, best of type div. All necessary elements (e.g. canvas) will be created inside of it.
88 | Jmat.plot2D = function(fun, parent, params, label) {
89 | if(!params) params = new Jmat.PlotParams();
90 | if(!(params instanceof Jmat.PlotParams)) {
91 | params = new Jmat.PlotParams(params);
92 | }
93 | if(!parent) parent = document.body;
94 | Jmat.Plot.plot2D_(fun, params, parent, label);
95 | };
96 |
97 | // Stop plot2D or plotComplex, if they are taking a very long time. May still
98 | // calculate several pixels before actually stopping: they do several lines at the time.
99 | Jmat.stopPlotting = function() {
100 | Jmat.Plot.stopIndex_ = (Jmat.Plot.stopIndex_ ? Jmat.Plot.stopIndex_ + 1 : 1);
101 | };
102 |
103 | ////////////////////////////////////////////////////////////////////////////////
104 | ////////////////////////////////////////////////////////////////////////////////
105 | ////////////////////////////////////////////////////////////////////////////////
106 | // Graphics (color & DOM) helper functions
107 | ////////////////////////////////////////////////////////////////////////////////
108 | ////////////////////////////////////////////////////////////////////////////////
109 |
110 |
111 | // Jmat graphics, graphing and plotting library
112 | // internal functions are grouped in here
113 | Jmat.Plot = function() {
114 | };
115 |
116 | //input and output in range [0-255]
117 | Jmat.Plot.hslToRgb = function(h, s, l) {
118 | var r, g, b;
119 | var temp1, temp2, tempr, tempg, tempb;
120 | h /= 256.0;
121 | s /= 256.0;
122 | l /= 256.0;
123 | if(s == 0) {
124 | r = g = b = l;
125 | } else {
126 | if(l < 0.5) temp2 = l * (1 + s);
127 | else temp2 = (l + s) - (l * s);
128 | temp1 = 2 * l - temp2;
129 | tempr = h + 1.0 / 3.0;
130 | if(tempr > 1) tempr--;
131 | tempg = h;
132 | tempb = h - 1.0 / 3.0;
133 | if(tempb < 0) tempb++;
134 |
135 | //Red
136 | if(tempr < 1.0 / 6.0) r = temp1 + (temp2 - temp1) * 6.0 * tempr;
137 | else if(tempr < 0.5) r = temp2;
138 | else if(tempr < 2.0 / 3.0) r = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - tempr) * 6.0;
139 | else r = temp1;
140 | //Green
141 | if(tempg < 1.0 / 6.0) g = temp1 + (temp2 - temp1) * 6.0 * tempg;
142 | else if(tempg < 0.5) g = temp2;
143 | else if(tempg < 2.0 / 3.0) g = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - tempg) * 6.0;
144 | else g = temp1;
145 | //Blue
146 | if(tempb < 1.0 / 6.0) b = temp1 + (temp2 - temp1) * 6.0 * tempb;
147 | else if(tempb < 0.5) b = temp2;
148 | else if(tempb < 2.0 / 3.0) b = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - tempb) * 6.0;
149 | else b = temp1;
150 | }
151 | return [Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)];
152 | };
153 |
154 | //input and output in range [0-255]
155 | Jmat.Plot.hsvToRgb = function(h, s, v) {
156 | var r, g, b;
157 | h /= 256.0;
158 | s /= 256.0;
159 | v /= 256.0;
160 | if(s == 0) {
161 | r = g = b = v;
162 | } else {
163 | h *= 6;
164 | var i = Math.floor(h);
165 | var f = h - i;
166 | var p = v * (1 - s);
167 | var q = v * (1 - (s * f));
168 | var t = v * (1 - (s * (1 - f)));
169 | if(i == 0) { r = v; g = t; b = p; }
170 | if(i == 1) { r = q; g = v; b = p; }
171 | if(i == 2) { r = p; g = v; b = t; }
172 | if(i == 3) { r = p; g = q; b = v; }
173 | if(i == 4) { r = t; g = p; b = v; }
174 | if(i == 5) { r = v; g = p; b = q; }
175 | }
176 | return [Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)];
177 | };
178 |
179 | // rgb: [r, g, b], each in range 0-255
180 | Jmat.Plot.rgbToCss = function(rgb) {
181 | var r = rgb[0].toString(16);
182 | var g = rgb[1].toString(16);
183 | var b = rgb[2].toString(16);
184 |
185 | if(r.length < 2) r = '0' + r;
186 | if(g.length < 2) g = '0' + g;
187 | if(b.length < 2) b = '0' + b;
188 | if(r.length > 2) r = 'ff';
189 | if(g.length > 2) g = 'ff';
190 | if(b.length > 2) b = 'ff';
191 |
192 | return '#' + r + g + b;
193 | };
194 |
195 | Jmat.Plot.complexColorFormula_ = 0; //0 = "tweaked wikipedia", 1 = "wikipedia", 2 = "original"
196 |
197 | // For "Color wheel graphs of complex functions" (not for "Domain coloring" which has repeating pattern of lightness rather than black for 0, white for infinity)
198 | //maxvalue matches the halfway brightness of the HSL color model. Higher values go towards white, lower values go towards black, but are capped.
199 | //hue is the argument. Positive real values are red, negative real values are cyan, positive imag values are grassgreen, negative imag values are purple, other colors are complex.
200 | Jmat.Plot.getComplexColor = function(y, maxval) {
201 | var rgb;
202 |
203 | if(Jmat.Complex.isNaN(y)) {
204 | rgb = [128, 128, 128];
205 | } else {
206 | // Intention of the color (if maxval = 1):
207 | // +1 = pure red (255,0,0)
208 | // -1 = pure cyan (0,255,255)
209 | // +-Infinity = white (small mod applied to that below: set to lightness 254 instead of 255, to show complex argument of it)
210 | // 0 = black
211 | // hue = complex argument: red for 0 deg, yellow/green for 90 deg, cyan for 180 deg, purple for 270 deg.
212 | // |z| = 1: best possible saturation, lightness of hsv model == 128
213 | // |z| > 1: brigher than that
214 | // |z| < 1: darker than that
215 |
216 | var h = Jmat.Complex.argr1(y) * 255;
217 | var s = 255;
218 | var l;
219 | var a = Jmat.Complex.absr(y) / maxval;
220 |
221 | var m = 254; //max lightness for non-infinity (e.g. 240 or 250)
222 |
223 | // Original formula. Use with Jmat.Plot.hslToRgb
224 | if(Jmat.Plot.complexColorFormula_ == 2) {
225 | l = (m / 255) * a / (a + 1);
226 | s = 255;
227 | }
228 |
229 | // The Wikipedia formula. Use with hsvToRgb
230 | if(Jmat.Plot.complexColorFormula_ == 1) {
231 | l = 1 - 1 / (1.1 + 5*Math.log(a + 1));
232 | s = 255 / (1 + 0.3*Math.log(a + 1));
233 | }
234 |
235 | // Tweaked version of the Wikipedia formula. Use with hsvToRgb
236 | // Advantage over "original" formula: prettier, less visible "transition lines"
237 | // Disadvantage over "original" formula: less clear, less difference between magnitudes, value 1 is not perfectly #ff0000
238 | if(Jmat.Plot.complexColorFormula_ == 0) {
239 | var mm = 1 - m/255;
240 | l = (1 - 4*mm) - (1 - 8*mm) / (1 + 15*Math.log(a + 1));
241 | s = 255 / (1 + 0.3*Math.log(a + 1));
242 | }
243 |
244 | l *= 255;
245 | if(l < (255-m) & a > 0) l = (255-m);
246 | if(l > m) l = m;
247 |
248 | if(Jmat.Plot.complexColorFormula_ == 2) rgb = Jmat.Plot.hslToRgb(h, s, l);
249 | else rgb = Jmat.Plot.hsvToRgb(h, s, l);
250 | }
251 |
252 | return rgb;
253 | };
254 |
255 | Jmat.Plot.makeSizedDiv = function(parent, x, y, w, h) {
256 | var el = document.createElement('div');
257 | el.style.position = 'absolute';
258 | el.style.left = '' + Math.floor(x) + 'px';
259 | el.style.top = '' + Math.floor(y) + 'px';
260 | el.style.width = Math.floor(w) + 'px';
261 | el.style.height = Math.floor(h) + 'px';
262 | parent.appendChild(el);
263 | return el;
264 | };
265 |
266 | //'align' meaning: 0 = left/top, 1 = center, 2 = right/bottom
267 | // if width is given (> 0), it is multiline text. Else it is single line text
268 | // fontSize is a CSS value like 'small'
269 | Jmat.Plot.makeAlignedText = function(parent, text, width, x, y, alignx, aligny, fontSize) {
270 | var div = document.createElement('div');
271 | if(fontSize) div.style.fontSize = fontSize;
272 | div.innerHTML = text;
273 | div.style.position = 'absolute';
274 | div.style.textAlign = alignx == 0 ? 'left' : alignx == 1 ? 'center' : 'right';
275 | if(!width) div.style.overflow = 'hidden';
276 | if(!width) div.style.whiteSpace = 'nowrap';
277 |
278 | if(width) div.style.width = width + 'px';
279 | parent.appendChild(div);
280 | var finalwidth = width;
281 | var h = div.clientHeight;
282 | var w = div.clientWidth;
283 | if(!width) finalwidth = w;
284 |
285 | if(aligny == 0) div.style.top = '' + Math.floor(y) + 'px';
286 | if(aligny == 1) div.style.top = '' + Math.floor(y - h / 2) + 'px';
287 | if(aligny == 2) div.style.top = '' + Math.floor(y - h) + 'px';
288 |
289 | if(alignx == 0) div.style.left = '' + Math.floor(x) + 'px';
290 | if(alignx == 1) div.style.left = '' + Math.floor(x - finalwidth / 2) + 'px';
291 | if(alignx == 2) div.style.left = '' + Math.floor(x - finalwidth) + 'px';
292 |
293 | return div;
294 | };
295 |
296 | //adds text vertically and horizontally centered, multiline depending on width.
297 | //returns the text element
298 | // if width is given (> 0), it is multiline text. Else it is single line text
299 | Jmat.Plot.makeCenteredText = function(parent, text, width, x, y, fontSize) {
300 | return Jmat.Plot.makeAlignedText(parent, text, width, x, y, 1, 1, fontSize)
301 | };
302 |
303 | Jmat.Plot.useHTML5canvas_ = true;
304 |
305 | // Plot a pixel or rectangle to the given element. E.g. if w and h are 2, it's a 2x2 pixel.
306 | // Position: x, y
307 | // Size: w, h
308 | // Color: [r, g, b], each in range 0-255
309 | Jmat.Plot.rect = function(parent, x, y, w, h, rgb) {
310 | // NON-canvas version (slower)
311 | if(!Jmat.Plot.useHTML5canvas_) {
312 | var el = Jmat.Plot.makeSizedDiv(parent, x, y, w, h);
313 | el.style.backgroundColor = Jmat.Plot.rgbToCss(rgb);
314 | return el;
315 | }
316 |
317 | // canvas version (faster)
318 | var data = parent.data;
319 | var id = parent.idd;
320 | var ctx = parent.ctx;
321 | if(!data) {
322 | var canvas = document.createElement('canvas');
323 | canvas.style.position = 'absolute';
324 | canvas.style.left = 0;
325 | canvas.style.top = 0;
326 | canvas.width = parseInt(parent.style.width);
327 | canvas.height = parseInt(parent.style.height);
328 | ctx = canvas.getContext("2d");
329 | id = ctx.createImageData(1,1);
330 | data = id.data;
331 | parent.idd = id;
332 | parent.data = data;
333 | parent.ctx = ctx;
334 | parent.appendChild(canvas);
335 | }
336 |
337 | if(w == 1 && h == 1) {
338 | data[0] = rgb[0];
339 | data[1] = rgb[1];
340 | data[2] = rgb[2];
341 | data[3] = 255;
342 | ctx.putImageData(id, x, y);
343 | } else {
344 | ctx.fillStyle = "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ",1)";
345 | ctx.fillRect(x, y, w, h);
346 | }
347 | };
348 |
349 | ////////////////////////////////////////////////////////////////////////////////
350 | ////////////////////////////////////////////////////////////////////////////////
351 | ////////////////////////////////////////////////////////////////////////////////
352 | // Plotting - Internal implementation
353 | ////////////////////////////////////////////////////////////////////////////////
354 | ////////////////////////////////////////////////////////////////////////////////
355 |
356 | Jmat.Plot.addPlotLabels_ = function(xlabel, ylabel, x0, x1, y0, y1, parent) {
357 | var plotx0 = 32;
358 | var plotx1 = plotx0 + 322;
359 | var ploty0 = 32;
360 | var ploty1 = ploty0 + 322;
361 |
362 | var yc = (y0 + y1) / 2;
363 | Jmat.Plot.makeSizedDiv(parent, plotx0 - 8, ploty0, 8, 1).style.backgroundColor = 'black';
364 | Jmat.Plot.makeSizedDiv(parent, plotx0 - 8, (ploty0 + ploty1) / 2, 8, 1).style.backgroundColor = 'black';
365 | Jmat.Plot.makeSizedDiv(parent, plotx0 - 8, ploty1 - 1, 8, 1).style.backgroundColor = 'black';
366 | Jmat.Plot.makeAlignedText(parent, '' + y1, 0, plotx0 - 8 - 2, ploty0, 2, 1, 'small');
367 | Jmat.Plot.makeAlignedText(parent, '' + yc, 0, plotx0 - 8 - 2, (ploty0 + ploty1) / 2, 2, 1, 'small');
368 | Jmat.Plot.makeAlignedText(parent, '' + y0, 0, plotx0 - 8 - 2, ploty1, 2, 1, 'small');
369 | Jmat.Plot.makeAlignedText(parent, ylabel, 0, plotx0 - 2, (ploty0 + ploty1 * 3) / 4, 2, 1, 'small');
370 |
371 | var xc = (x0 + x1) / 2;
372 | Jmat.Plot.makeSizedDiv(parent, plotx0, ploty1, 1, 8).style.backgroundColor = 'black';
373 | Jmat.Plot.makeSizedDiv(parent, (plotx0 + plotx1) / 2, ploty1, 1, 8).style.backgroundColor = 'black';
374 | Jmat.Plot.makeSizedDiv(parent, plotx1 - 1, ploty1, 1, 8).style.backgroundColor = 'black';
375 | Jmat.Plot.makeAlignedText(parent, '' + x0, 0, plotx0, ploty1 + 8, 1, 0, 'small');
376 | Jmat.Plot.makeAlignedText(parent, '' + xc, 0, (plotx0 + plotx1) / 2, ploty1 + 8, 1, 0, 'small');
377 | Jmat.Plot.makeAlignedText(parent, '' + x1, 0, plotx1, ploty1 + 8, 1, 0, 'small');
378 | Jmat.Plot.makeAlignedText(parent, xlabel, 0, (plotx0 * 3 + plotx1) / 4, ploty1, 1, 0, 'small');
379 | };
380 |
381 | ////////////////////////////////////////////////////////////////////////////////
382 |
383 | Jmat.Plot.makeRealPixel_ = function(div, width, height, params, px, y, prevy, rgb, title) {
384 | var p = params.p;
385 | var ysize = params.ysize;
386 |
387 | var py = Math.floor(height / 2 - ((y+params.yshift) / ysize * height) - 1);
388 | var prevpy = Math.floor(height / 2 - ((prevy+params.yshift) / ysize * height) - 1);
389 |
390 | if(py < 0 && prevpy < 0) return;
391 | if(py > height && prevpy > height) return;
392 |
393 | if(isNaN(y)) {
394 | var d = Jmat.Plot.rect(div, px, 0, p, height, [160,160,160]);
395 | if(d) d.title = title;
396 | return;
397 | }
398 |
399 | if(py >= 0 && py < height) {
400 | var d = Jmat.Plot.rect(div, px, py, p, p, rgb);
401 | if(d) d.title = title;
402 | }
403 |
404 | if(py < 0) py = 0;
405 | if(py > height) py = height;
406 | if(prevpy < 0) prevpy = 0;
407 | if(prevpy > height) prevpy = height;
408 |
409 | if(prevpy + p < py) {
410 | var d = Jmat.Plot.rect(div, px, prevpy + p, p, (py - prevpy), rgb);
411 | if(d) d.title = title;
412 | } else if(prevpy > py + p) {
413 | var d = Jmat.Plot.rect(div, px, py, p, (prevpy - py), rgb);
414 | if(d) d.title = title;
415 | }
416 | };
417 |
418 | Jmat.Plot.plotReal_ = function(fun, params, parent, label) {
419 | //parent.style.backgroundColor = 'white'; // TODO: make element inside it instead to alter style
420 | var plotfun = function() {
421 | var width = 320;
422 | var height = 320;
423 | var xsize = params.xsize;
424 | var ysize = params.ysize;
425 | var p = params.p;
426 |
427 | var L = 32;
428 | var steps = Math.floor(width / p);
429 | parent.innerHTML = '';
430 | var div = Jmat.Plot.makeSizedDiv(parent, L, L, width, height);
431 | div.style.backgroundColor = '#eee';
432 | div.style.border = '1px solid black';
433 |
434 | // axis lines
435 | var d = Jmat.Plot.makeSizedDiv(div, 0, width / 2, height, 2);
436 | d.style.backgroundColor = '#ccc';
437 | d = Jmat.Plot.makeSizedDiv(div, width / 2, 0, 2, height);
438 | d.style.backgroundColor = '#ccc';
439 |
440 | Jmat.Plot.addPlotLabels_('x', 'y', xsize/2+params.xshift, -xsize/2+params.xshift, -ysize/2-params.yshift, ysize/2-params.yshift, parent);
441 | if(label) Jmat.Plot.makeAlignedText(parent, label, 0, L + width, L, 2, 2);
442 |
443 | d = Jmat.Plot.makeCenteredText(parent, '←', 0, L + width / 2 - 35, L - 10);
444 | d.onclick = function() { params.xshift -= params.xsize / 8; plotfun(); };
445 | d.style.color = '#ddd';
446 | d = Jmat.Plot.makeCenteredText(parent, '→', 0, L + width / 2 - 15, L - 10);
447 | d.onclick = function() { params.xshift += params.xsize / 8; plotfun(); };
448 | d.style.color = '#ddd';
449 |
450 | d = Jmat.Plot.makeCenteredText(parent, '[+]', 0, L + width / 2 + 15, L - 10);
451 | d.onclick = function() { params.xsize /= 2; params.ysize /= 2; plotfun(); };
452 | d.style.color = '#ddd';
453 | d = Jmat.Plot.makeCenteredText(parent, '[-]', 0, L + width / 2 + 35, L - 10);
454 | d.onclick = function() { params.xsize *= 2; params.ysize *= 2; plotfun(); };
455 | d.style.color = '#ddd';
456 |
457 | d = Jmat.Plot.makeCenteredText(parent, '+', 0, L + width + 8, L + 70);
458 | d.onclick = function() { params.ysize /= 2; plotfun(); };
459 | d.style.color = '#ddd';
460 | d = Jmat.Plot.makeCenteredText(parent, '-', 0, L + width + 8, L + 90);
461 | d.onclick = function() { params.ysize *= 2; plotfun(); };
462 | d.style.color = '#ddd';
463 |
464 | d = Jmat.Plot.makeCenteredText(parent, '↑', 0, L + width + 8, L + 50);
465 | d.onclick = function() { params.yshift -= params.ysize / 8; plotfun(); };
466 | d.style.color = '#ddd';
467 | d = Jmat.Plot.makeCenteredText(parent, '↓', 0, L + width + 8, L + 110);
468 | d.onclick = function() { params.yshift += params.ysize / 8; plotfun(); };
469 | d.style.color = '#ddd';
470 |
471 | var prevy;
472 |
473 | for(var i = 0; i < steps; i++) {
474 | var px = i * p;
475 | var re = -xsize / 2 + (i / steps * xsize) + params.xshift;
476 |
477 | var x = Jmat.Complex(re, 0);
478 | var y = fun(x);
479 | if(!prevy) prevy = y;
480 | if(y.re == Infinity && y.im == Infinity) y = Jmat.Complex(NaN); // plot undirected infinity as NaN (should show up as vertical line)
481 |
482 | if(y.im == 0 || (Math.abs(y.im) < Math.abs(y.re) * 1e-10 /*imag likely due to numerical imprecisions*/)) {
483 | Jmat.Plot.makeRealPixel_(div, 320, 320, params, px, y.re, prevy.re, [0,0,0], Jmat.Complex.toString(x) + ': ' + Jmat.Complex.toString(y));
484 | } else {
485 | // Abs and arg-color-wheel, always in positive zone
486 | var h = Jmat.Complex.argr1(y) * 255;
487 | var rgb = Jmat.Plot.hslToRgb(h, 255, 128);
488 | var a = Jmat.Complex.abs(y).re;
489 | var pa = Jmat.Complex.abs(prevy).re;
490 | Jmat.Plot.makeRealPixel_(div, 320, 320, params, px, a, pa, rgb, Jmat.Complex.toString(x) + ': ' + Jmat.Complex.toString(y));
491 | }
492 |
493 | prevy = y;
494 | }
495 | };
496 |
497 | plotfun();
498 | };
499 |
500 | //p = pixel cell size
501 | // For "Color wheel graphs of complex functions" (not for "Domain coloring" which has repeating pattern of lightness rather than black for 0, white for infinity)
502 | Jmat.Plot.plotColorPixel = function(y, maxval, p, px, py, div) {
503 | var rgb = Jmat.Plot.getComplexColor(y, maxval);
504 | var d = Jmat.Plot.rect(div, px * p, py * p, p, p, rgb);
505 | return d;
506 | };
507 |
508 | ////////////////////////////////////////////////////////////////////////////////
509 |
510 | //p = pixel cell size
511 | Jmat.Plot.plot2DPixel_ = function(fun, size, steps, params, px, py, div) {
512 | var x = -size + (px / steps * size * 2);
513 | var y = size - (py / steps * size * 2);
514 |
515 | var z = fun(Jmat.Complex(x + params.xshift), Jmat.Complex(y + params.yshift));
516 |
517 | var d = Jmat.Plot.plotColorPixel(z, params.v, params.p, px, py, div);
518 | if(d) d.title = x + ', ' + y + ': ' + Jmat.Complex.toString(z);
519 | };
520 |
521 |
522 | Jmat.Plot.plot2DLineTimeout_ = function(fun, size, steps, params, py, div) {
523 | var stopindex = Jmat.Plot.stopIndex_;
524 |
525 | // This is for first rendering fast, and only then at full resolution
526 | var stage = 1;
527 | var params1 = params;
528 | var steps1 = steps;
529 |
530 | if(params.p <= 2) {
531 | stage = 0;
532 | params = JSON.parse(JSON.stringify(params));
533 | params.p *= 4;
534 | steps /= 4;
535 | }
536 |
537 | var linefun = function(py) {
538 | window.setTimeout(function() {
539 | for(var i = 0; i < 4; i++) {
540 | if(py == steps) { stage++; params = params1; steps = steps1; py = 0; }
541 | if(stage == 2) return;
542 | if(py == steps) return;
543 | if(stage == 2) return;
544 | for(var px = 0; px < steps; px++) {
545 | Jmat.Plot.plot2DPixel_(fun, size, steps, params, px, py, div);
546 | }
547 | py++;
548 | }
549 | if(stopindex != Jmat.Plot.stopIndex_) return;
550 | linefun(py);
551 | }, 0);
552 | };
553 | linefun(py);
554 | };
555 |
556 | Jmat.Plot.plot2DNonBlocking_ = function(fun, size, steps, params, div) {
557 | Jmat.Plot.plot2DLineTimeout_(fun, size, steps, params, 0, div);
558 | };
559 |
560 | Jmat.Plot.plot2D_ = function(fun, params, parent, label, xlabel, ylabel) {
561 | //parent.style.backgroundColor = 'white'; // TODO: make element inside it instead to alter style
562 | if(!xlabel) xlabel = 'x';
563 | if(!ylabel) ylabel = 'y';
564 | var plotfun = function() {
565 | var width = 320;
566 | var height = 320;
567 | var size = params.xsize / 2;
568 | // TODO: support x and y size
569 | //var xsize = params.xsize;
570 | //var ysize = params.ysize;
571 | var p = params.p;
572 | var maxval = params.v;
573 |
574 | var L = 32;
575 | var steps = Math.floor(width / p);
576 | parent.innerHTML = '';
577 | var div = Jmat.Plot.makeSizedDiv(parent, L, L, steps * p, steps * p);
578 | div.style.backgroundColor = '#888888';
579 | div.style.border = '1px solid black';
580 |
581 | Jmat.Plot.addPlotLabels_(xlabel, ylabel, -size+params.xshift, size+params.xshift, -size+params.yshift, size+params.yshift, parent);
582 | if(label) Jmat.Plot.makeAlignedText(parent, label, 0, L + width, L, 2, 2);
583 |
584 | d = Jmat.Plot.makeCenteredText(parent, '←', 0, L + width / 2 - 35, L - 10);
585 | d.onclick = function() { params.xshift -= params.xsize / 2; Jmat.stopPlotting(); plotfun(); };
586 | d.style.color = '#ddd';
587 | d = Jmat.Plot.makeCenteredText(parent, '→', 0, L + width / 2 - 15, L - 10);
588 | d.onclick = function() { params.xshift += params.xsize / 2; Jmat.stopPlotting(); plotfun(); };
589 | d.style.color = '#ddd';
590 |
591 | d = Jmat.Plot.makeCenteredText(parent, '[+]', 0, L + width / 2 + 15, L - 10);
592 | d.onclick = function() { params.xsize /= 2; params.ysize /= 2; Jmat.stopPlotting(); plotfun(); };
593 | d.style.color = '#ddd';
594 | d = Jmat.Plot.makeCenteredText(parent, '[-]', 0, L + width / 2 + 35, L - 10);
595 | d.onclick = function() { params.xsize *= 2; params.ysize *= 2; Jmat.stopPlotting(); plotfun(); };
596 | d.style.color = '#ddd';
597 |
598 | d = Jmat.Plot.makeCenteredText(parent, '↑', 0, L + width + 8, L + 70);
599 | d.onclick = function() { params.yshift += params.ysize / 2; Jmat.stopPlotting(); plotfun(); };
600 | d.style.color = '#ddd';
601 | d = Jmat.Plot.makeCenteredText(parent, '↓', 0, L + width + 8, L + 90);
602 | d.onclick = function() { params.yshift -= params.ysize / 2; Jmat.stopPlotting(); plotfun(); };
603 | d.style.color = '#ddd';
604 |
605 | Jmat.Plot.plot2DNonBlocking_(fun, size, steps, params, div);
606 | }
607 | plotfun();
608 | };
609 |
610 |
--------------------------------------------------------------------------------