use let wherever possible in our code
[mandelbrot.git] / xulapp / chrome / mandelbrot / content / mandelbrot.js
1 var gColorPalette = getColorPalette('kairo');
2
3 function drawImage() {
4   let canvas = document.getElementById("mbrotImage");
5   if (canvas.getContext) {
6     let context = canvas.getContext("2d");
7
8     // example:
9     // context.fillStyle = "rgb(200,0,0)";
10     // context.fillRect (10, 10, 55, 50); // x, y, width, height
11     //
12     // context.fillStyle = "rgba(0, 0, 200, 0.5)";
13     // context.fillRect (30, 30, 55, 50);
14
15     let Cr_min = -2.0;
16     let Cr_max = 1.0;
17     let Cr_scale = Cr_max - Cr_min;
18
19     let Ci_min = -1.5;
20     let Ci_max = 1.5;
21     let Ci_scale = Ci_max - Ci_min;
22
23     let iterMax = 500;
24
25     for (let img_x = 0; img_x < canvas.width; img_x++) {
26       for (let img_y = 0; img_y < canvas.height; img_y++) {
27         let C = new complex(Cr_min + (img_x / canvas.width) * Cr_scale,
28                             Ci_min + (img_y / canvas.height) * Ci_scale);
29         window.setTimeout(drawPoint, 0, context, img_x, img_y, C, iterMax);
30       }
31     }
32   }
33 }
34
35 function complex(aReal, aImag) {
36   this.r = aReal;
37   this.i = aImag;
38   this.square = function() {
39     return new complex(this.r * this.r - this.i * this.i,
40                        2 * this.r * this.i);
41   }
42   this.dist = function() {
43     return Math.sqrt(this.r * this.r + this.i * this.i);
44   }
45   this.add = function(aComplex) {
46     return new complex(this.r + aComplex.r, this.i + aComplex.i);
47   }
48 }
49
50 function mandelbrotValue (aC, aIterMax) {
51   /* XXX: this would be nice code but it looks like JS objects are too heavy for this.
52   let Z = new complex(0.0, 0.0);
53   for (var iter = 0; iter < aIterMax; iter++) {
54     Z = Z.square().add(aC);
55     if (Z.r * Z.r + Z.i * Z.i > 256) { break; }
56   }
57   */
58
59   // highly optimized code for fast calculation
60   let Cr = aC.r, Ci = aC.i;
61   let Zr = 0.0, Zi = 0.0;
62   let Zr2 = Zr * Zr, Zi2 = Zi * Zi;
63   for (var iter = 0; iter < aIterMax; iter++) {
64     Zi = 2 * Zr * Zi + Ci;
65     Zr = Zr2 - Zi2 + Cr;
66
67     Zr2 = Zr * Zr; Zi2 = Zi * Zi;
68     if (Zr2 + Zi2 > 256) { break; }
69   }
70   return iter;
71 }
72
73 function getColor(aIterValue, aIterMax) {
74   let standardizedValue = Math.round(aIterValue * 1024 / aIterMax);
75   return gColorPalette[standardizedValue];
76   if (aIterValue == aIterMax) {
77     return "rgb(0,0,0)";
78   }
79   else {
80     //return "rgb(" + img_x + "," + img_y + ",0)";
81     return "rgb(255,255,255)";
82   }
83 }
84
85 function getColorPalette(palName) {
86   var palette = [];
87   switch (palName) {
88     case 'bw':
89       for (let i = 0; i < 1024; i++) {
90         palette[i] = 'rgb(255,255,255)';
91       }
92       palette[1024] = 'rgb(0,0,0)';
93       break;
94     case 'kairo':
95       // outer areas
96       for (let i = 0; i < 32; i++) {
97         let cc1 = Math.floor(i * 127 / 31);
98         let cc2 = 170 - Math.floor(i * 43 / 31);
99         palette[i] = 'rgb(' + cc1 + ',' + cc2 + ',' + cc1 + ')';
100       }
101       // inner areas
102       for (let i = 0; i < 51; i++) {
103         let cc = Math.floor(i * 170 / 50);
104         palette[32 + i] = 'rgb(' + cc + ',0,' + (170 + cc) + ')';
105       }
106       // corona
107       for (let i = 0; i < 101; i++) {
108         let cc = Math.floor(i * 200 / 100);
109         palette[83 + i] = 'rgb(255,' + cc + ',0)';
110       }
111       // inner corona
112       for (let i = 0; i < 201; i++) {
113         let cc1 = 255 - Math.floor(i * 85 / 200);
114         let cc2 = 200 - Math.floor(i * 30 / 200);
115         let cc3 = Math.floor(i * 170 / 200);
116         palette[184 + i] = 'rgb(' + cc1 + ',' + cc2 + ',' + cc3 + ')';
117       }
118       for (let i = 0; i < 301; i++) {
119         let cc1 = 170 - Math.floor(i * 43 / 300);
120         let cc2 = 170 + Math.floor(i * 85 / 300);
121         palette[385 + i] = 'rgb(' + cc1 + ',' + cc1 + ',' + cc2 + ')';
122       }
123       for (let i = 0; i < 338; i++) {
124         let cc = 127 + Math.floor(i * 128 / 337);
125         palette[686 + i] = 'rgb(' + cc + ',' + cc + ',255)';
126       }
127       palette[1024] = 'rgb(0,0,0)';
128       break;
129     case 'rainbow-linear1':
130       for (let i = 0; i < 256; i++) {
131         palette[i] = 'rgb(' + i + ',0,0)';
132         palette[256 + i] = 'rgb(255,' + i + ',0)';
133         palette[512 + i] = 'rgb(' + (255 - i) + ',255,' + i + ')';
134         palette[768 + i] = 'rgb(' + i + ',' + (255 - i) + ',255)';
135       }
136       palette[1024] = 'rgb(0,0,0)';
137       break;
138   }
139 /*
140 Select Case palnr
141 Case 1  'Standard-Palette (QB-Colors)
142      For i = 0 To 1024
143          xx = CInt(i * 500 / 1024 + 2)
144          If xx <= 15 Then clr = xx
145          If xx > 15 Then clr = CInt(Sqr((xx - 15 + 1) * 15 ^ 2 / 485))
146          If xx >= 500 Then clr = 0
147          palette(i) = QBColor(clr)
148      Next
149 Case 3  'Regenbogen-Palette 1 (qu.)
150      For i = 0 To 33
151          clr = CInt(i * 255 / 33)
152          palette(i) = RGB(clr, 0, 0)
153      Next
154      For i = 0 To 136
155          clr = CInt(i * 255 / 136)
156          palette(34 + i) = RGB(255, clr, 0)
157      Next
158      For i = 0 To 306
159          clr = CInt(i * 255 / 306)
160          palette(171 + i) = RGB(255 - clr, 255, clr)
161      Next
162      For i = 0 To 545
163          clr = CInt(i * 255 / 545)
164          palette(478 + i) = RGB(clr, 255 - clr, 255)
165      Next
166 Case 4  'Regenbogen-Palette 2 (linear)
167      For i = 0 To 204
168          clr = CInt(i * 255 / 204)
169          palette(i) = RGB(255, clr, 0)
170          palette(204 + i) = RGB(255 - clr, 255, 0)
171          palette(409 + i) = RGB(0, 255, clr)
172          palette(614 + i) = RGB(0, 255 - clr, 255)
173          palette(819 + i) = RGB(clr, 0, 255)
174      Next
175 Case 5  'Regenbogen-Palette 2 (qu.)
176      For i = 0 To 18
177          clr = CInt(i * 255 / 18)
178          palette(i) = RGB(255, clr, 0)
179      Next
180      For i = 0 To 73
181          clr = CInt(i * 255 / 73)
182          palette(20 + i) = RGB(255 - clr, 255, 0)
183      Next
184      For i = 0 To 167
185          clr = CInt(i * 255 / 167)
186          palette(93 + i) = RGB(0, 255, clr)
187      Next
188      For i = 0 To 297
189          clr = CInt(i * 255 / 297)
190          palette(261 + i) = RGB(0, 255 - clr, 255)
191      Next
192      For i = 0 To 464
193          clr = CInt(i * 255 / 464)
194          palette(559 + i) = RGB(clr, 0, 255)
195      Next
196 */
197   return palette;
198 }
199
200 function drawPoint(context, img_x, img_y, C, iterMax) {
201   let itVal = mandelbrotValue(C, iterMax);
202   context.fillStyle = getColor(itVal, iterMax);
203   context.fillRect (img_x, img_y, 1, 1); // x, y, width, height
204 }
205
206 function saveImage() {
207   // should call filepicker!
208   saveCanvas(document.getElementById("mbrotImage"), "/home/robert/temp/canvas-save.png")
209 }
210
211 // function below is from from http://developer.mozilla.org/en/docs/Code_snippets:Canvas
212 function saveCanvas(canvas, destFile) {
213   // convert string filepath to an nsIFile
214   var file = Components.classes["@mozilla.org/file/local;1"]
215                        .createInstance(Components.interfaces.nsILocalFile);
216   file.initWithPath(destFile);
217
218   // create a data url from the canvas and then create URIs of the source and targets  
219   var io = Components.classes["@mozilla.org/network/io-service;1"]
220                      .getService(Components.interfaces.nsIIOService);
221   var source = io.newURI(canvas.toDataURL("image/png", ""), "UTF8", null);
222   var target = io.newFileURI(file);
223
224   // prepare to save the canvas data
225   var persist = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
226                           .createInstance(Components.interfaces.nsIWebBrowserPersist);
227
228   persist.persistFlags = Components.interfaces.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
229   persist.persistFlags |= Components.interfaces.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
230
231   // displays a download dialog (remove these 3 lines for silent download)
232   var xfer = Components.classes["@mozilla.org/transfer;1"]
233                        .createInstance(Components.interfaces.nsITransfer);
234   xfer.init(source, target, "", null, null, null, persist);
235   persist.progressListener = xfer;
236
237   // save the canvas data to the file
238   persist.saveURI(source, null, null, null, null, file);
239 }
240
241 // function below is from http://developer.mozilla.org/en/docs/How_to_Quit_a_XUL_Application
242 function quitApp(aForceQuit) {
243   var appStartup = Components.classes['@mozilla.org/toolkit/app-startup;1']
244                              .getService(Components.interfaces.nsIAppStartup);
245
246   // eAttemptQuit will try to close each XUL window, but the XUL window can cancel the quit
247   // process if there is unsaved data. eForceQuit will quit no matter what.
248   var quitSeverity = aForceQuit ? Components.interfaces.nsIAppStartup.eForceQuit :
249                                   Components.interfaces.nsIAppStartup.eAttemptQuit;
250   appStartup.quit(quitSeverity);
251 }