make UI prefs actually work, add menus for debug options, i.e. turning TraceMonkey...
[mandelbrot.git] / xulapp / chrome / mandelbrot / content / mandelbrot.js
... / ...
CommitLineData
1var gColorPalette = [];
2var gPref = Components.classes["@mozilla.org/preferences-service;1"]
3 .getService(Components.interfaces.nsIPrefService)
4 .getBranch(null);
5var gStartTime = 0;
6
7function Startup() {
8 updateIterMenu();
9 updatePaletteMenu();
10 document.getElementById("statusLabel").value =
11 document.getElementById("mbrotBundle").getString("statusEmpty");
12}
13
14function drawImage() {
15 let canvas = document.getElementById("mbrotImage");
16 if (canvas.getContext) {
17 let context = canvas.getContext("2d");
18
19 document.getElementById("statusLabel").value =
20 document.getElementById("mbrotBundle").getString("statusDrawing");
21
22 // example:
23 // context.fillStyle = "rgb(200,0,0)";
24 // context.fillRect (10, 10, 55, 50); // x, y, width, height
25 //
26 // context.fillStyle = "rgba(0, 0, 200, 0.5)";
27 // context.fillRect (30, 30, 55, 50);
28
29 let Cr_min = -2.0;
30 let Cr_max = 1.0;
31 let Cr_scale = Cr_max - Cr_min;
32
33 let Ci_min = -1.5;
34 let Ci_max = 1.5;
35 let Ci_scale = Ci_max - Ci_min;
36
37 let iterMax = gPref.getIntPref("mandelbrot.iteration_max");
38 let algorithm = gPref.getCharPref("mandelbrot.use_algorithm");
39
40 gStartTime = new Date();
41
42 for (let img_x = 0; img_x < canvas.width; img_x++) {
43 for (let img_y = 0; img_y < canvas.height; img_y++) {
44 let C = new complex(Cr_min + (img_x / canvas.width) * Cr_scale,
45 Ci_min + (img_y / canvas.height) * Ci_scale);
46 window.setTimeout(drawPoint, 0, context, img_x, img_y, C, iterMax, algorithm);
47 }
48 }
49 window.setTimeout(EndCalc, 0);
50 }
51}
52
53function EndCalc() {
54 let endTime = new Date();
55 let timeUsed = (endTime.getTime() - gStartTime.getTime()) / 1000;
56 document.getElementById("statusLabel").value =
57 document.getElementById("mbrotBundle").getFormattedString("statusTime", [timeUsed.toFixed(3)]);
58}
59
60function complex(aReal, aImag) {
61 this.r = aReal;
62 this.i = aImag;
63 this.square = function() {
64 return new complex(this.r * this.r - this.i * this.i,
65 2 * this.r * this.i);
66 }
67 this.dist = function() {
68 return Math.sqrt(this.r * this.r + this.i * this.i);
69 }
70 this.add = function(aComplex) {
71 return new complex(this.r + aComplex.r, this.i + aComplex.i);
72 }
73}
74
75function mandelbrotValueOO (aC, aIterMax) {
76 // this would be nice code in general but it looks like JS objects are too heavy for normal use.
77 let Z = new complex(0.0, 0.0);
78 for (var iter = 0; iter < aIterMax; iter++) {
79 Z = Z.square().add(aC);
80 if (Z.r * Z.r + Z.i * Z.i > 256) { break; }
81 }
82 return iter;
83}
84
85function mandelbrotValueNumeric (aC, aIterMax) {
86 // optimized numeric code for fast calculation
87 let Cr = aC.r, Ci = aC.i;
88 let Zr = 0.0, Zi = 0.0;
89 let Zr2 = Zr * Zr, Zi2 = Zi * Zi;
90 for (var iter = 0; iter < aIterMax; iter++) {
91 Zi = 2 * Zr * Zi + Ci;
92 Zr = Zr2 - Zi2 + Cr;
93
94 Zr2 = Zr * Zr; Zi2 = Zi * Zi;
95 if (Zr2 + Zi2 > 256) { break; }
96 }
97 return iter;
98}
99
100function getColor(aIterValue, aIterMax) {
101 let standardizedValue = Math.round(aIterValue * 1024 / aIterMax);
102 if (gColorPalette && gColorPalette.length)
103 return gColorPalette[standardizedValue];
104
105 // fallback to simple b/w if for some reason we don't have a palette
106 if (aIterValue == aIterMax)
107 return "rgb(0,0,0)";
108 else
109 return "rgb(255,255,255)";
110}
111
112function getColorPalette(palName) {
113 var palette = [];
114 switch (palName) {
115 case 'bw':
116 for (let i = 0; i < 1024; i++) {
117 palette[i] = 'rgb(255,255,255)';
118 }
119 palette[1024] = 'rgb(0,0,0)';
120 break;
121 case 'kairo':
122 // outer areas
123 for (let i = 0; i < 32; i++) {
124 let cc1 = Math.floor(i * 127 / 31);
125 let cc2 = 170 - Math.floor(i * 43 / 31);
126 palette[i] = 'rgb(' + cc1 + ',' + cc2 + ',' + cc1 + ')';
127 }
128 // inner areas
129 for (let i = 0; i < 51; i++) {
130 let cc = Math.floor(i * 170 / 50);
131 palette[32 + i] = 'rgb(' + cc + ',0,' + (170 + cc) + ')';
132 }
133 // corona
134 for (let i = 0; i < 101; i++) {
135 let cc = Math.floor(i * 200 / 100);
136 palette[83 + i] = 'rgb(255,' + cc + ',0)';
137 }
138 // inner corona
139 for (let i = 0; i < 201; i++) {
140 let cc1 = 255 - Math.floor(i * 85 / 200);
141 let cc2 = 200 - Math.floor(i * 30 / 200);
142 let cc3 = Math.floor(i * 170 / 200);
143 palette[184 + i] = 'rgb(' + cc1 + ',' + cc2 + ',' + cc3 + ')';
144 }
145 for (let i = 0; i < 301; i++) {
146 let cc1 = 170 - Math.floor(i * 43 / 300);
147 let cc2 = 170 + Math.floor(i * 85 / 300);
148 palette[385 + i] = 'rgb(' + cc1 + ',' + cc1 + ',' + cc2 + ')';
149 }
150 for (let i = 0; i < 338; i++) {
151 let cc = 127 + Math.floor(i * 128 / 337);
152 palette[686 + i] = 'rgb(' + cc + ',' + cc + ',255)';
153 }
154 palette[1024] = 'rgb(0,0,0)';
155 break;
156 case 'rainbow-linear1':
157 for (let i = 0; i < 256; i++) {
158 palette[i] = 'rgb(' + i + ',0,0)';
159 palette[256 + i] = 'rgb(255,' + i + ',0)';
160 palette[512 + i] = 'rgb(' + (255 - i) + ',255,' + i + ')';
161 palette[768 + i] = 'rgb(' + i + ',' + (255 - i) + ',255)';
162 }
163 palette[1024] = 'rgb(0,0,0)';
164 break;
165 }
166/*
167Select Case palnr
168Case 1 'Standard-Palette (QB-Colors)
169 For i = 0 To 1024
170 xx = CInt(i * 500 / 1024 + 2)
171 If xx <= 15 Then clr = xx
172 If xx > 15 Then clr = CInt(Sqr((xx - 15 + 1) * 15 ^ 2 / 485))
173 If xx >= 500 Then clr = 0
174 palette(i) = QBColor(clr)
175 Next
176Case 3 'Regenbogen-Palette 1 (qu.)
177 For i = 0 To 33
178 clr = CInt(i * 255 / 33)
179 palette(i) = RGB(clr, 0, 0)
180 Next
181 For i = 0 To 136
182 clr = CInt(i * 255 / 136)
183 palette(34 + i) = RGB(255, clr, 0)
184 Next
185 For i = 0 To 306
186 clr = CInt(i * 255 / 306)
187 palette(171 + i) = RGB(255 - clr, 255, clr)
188 Next
189 For i = 0 To 545
190 clr = CInt(i * 255 / 545)
191 palette(478 + i) = RGB(clr, 255 - clr, 255)
192 Next
193Case 4 'Regenbogen-Palette 2 (linear)
194 For i = 0 To 204
195 clr = CInt(i * 255 / 204)
196 palette(i) = RGB(255, clr, 0)
197 palette(204 + i) = RGB(255 - clr, 255, 0)
198 palette(409 + i) = RGB(0, 255, clr)
199 palette(614 + i) = RGB(0, 255 - clr, 255)
200 palette(819 + i) = RGB(clr, 0, 255)
201 Next
202Case 5 'Regenbogen-Palette 2 (qu.)
203 For i = 0 To 18
204 clr = CInt(i * 255 / 18)
205 palette(i) = RGB(255, clr, 0)
206 Next
207 For i = 0 To 73
208 clr = CInt(i * 255 / 73)
209 palette(20 + i) = RGB(255 - clr, 255, 0)
210 Next
211 For i = 0 To 167
212 clr = CInt(i * 255 / 167)
213 palette(93 + i) = RGB(0, 255, clr)
214 Next
215 For i = 0 To 297
216 clr = CInt(i * 255 / 297)
217 palette(261 + i) = RGB(0, 255 - clr, 255)
218 Next
219 For i = 0 To 464
220 clr = CInt(i * 255 / 464)
221 palette(559 + i) = RGB(clr, 0, 255)
222 Next
223*/
224 return palette;
225}
226
227function drawPoint(context, img_x, img_y, C, iterMax, algorithm) {
228 var itVal;
229 switch (algorithm) {
230 case 'oo':
231 itVal = mandelbrotValueOO(C, iterMax);
232 break;
233 case 'numeric':
234 default:
235 itVal = mandelbrotValueNumeric(C, iterMax);
236 break;
237 }
238 context.fillStyle = getColor(itVal, iterMax);
239 context.fillRect (img_x, img_y, 1, 1); // x, y, width, height
240}
241
242/***** pure UI functions *****/
243
244function saveImage() {
245 // XXX: should call filepicker!
246 saveCanvas(document.getElementById("mbrotImage"), "/home/robert/temp/canvas-save.png")
247}
248
249function updateIterMenu() {
250 try {
251 var currentIter = gPref.getIntPref("mandelbrot.iteration_max");
252 }
253 catch(e) {
254 var currentIter = 0;
255 }
256 if (currentIter < 10) {
257 currentIter = 500;
258 setIter(currentIter);
259 }
260
261 var popup = document.getElementById("menu_iterPopup");
262 var item = popup.firstChild;
263 while (item) {
264 if (item.getAttribute("name") == "iter") {
265 if (item.getAttribute("value") == currentIter)
266 item.setAttribute("checked","true");
267 else
268 item.removeAttribute("checked");
269 }
270 item = item.nextSibling;
271 }
272}
273
274function setIter(aIter) {
275 gPref.setIntPref("mandelbrot.iteration_max", aIter);
276}
277
278function updatePaletteMenu() {
279 try {
280 var currentPalette = gPref.getCharPref("mandelbrot.color_palette");
281 }
282 catch(e) {
283 var currentPalette = '';
284 }
285 if (!currentPalette.length) {
286 currentPalette = 'kairo';
287 setPalette(currentPalette);
288 }
289 if (!gColorPalette || !gColorPalette.length)
290 gColorPalette = getColorPalette(currentPalette);
291
292 var popup = document.getElementById("menu_palettePopup");
293 var item = popup.firstChild;
294 while (item) {
295 if (item.getAttribute("name") == "palette") {
296 if (item.getAttribute("value") == currentPalette)
297 item.setAttribute("checked", "true");
298 else
299 item.removeAttribute("checked");
300 }
301 item = item.nextSibling;
302 }
303}
304
305function setPalette(aPaletteID) {
306 gPref.setCharPref("mandelbrot.color_palette", aPaletteID);
307 gColorPalette = getColorPalette(aPaletteID);
308}
309
310function updateDebugMenu() {
311 var jitMenuItem = document.getElementById("jitEnabled");
312 jitMenuItem.setAttribute("checked", gPref.getBoolPref("javascript.options.jit.chrome"));
313}
314
315function toggleJITState(jitMenuItem) {
316 var jitEnabled = !gPref.getBoolPref("javascript.options.jit.chrome");
317 gPref.setBoolPref("javascript.options.jit.chrome", jitEnabled)
318 jitMenuItem.setAttribute("checked", jitEnabled? "true" : "false");
319}
320
321function updateAlgoMenu() {
322 try {
323 var currentAlgo = gPref.getCharPref("mandelbrot.use_algorithm");
324 }
325 catch(e) {
326 var currentAlgo = '';
327 }
328 if (!currentAlgo.length) {
329 currentAlgo = 'numeric';
330 setAlgorithm(currentAlgo);
331 }
332
333 var popup = document.getElementById("menu_algoPopup");
334 var item = popup.firstChild;
335 while (item) {
336 if (item.getAttribute("name") == "algorithm") {
337 if (item.getAttribute("value") == currentAlgo)
338 item.setAttribute("checked", "true");
339 else
340 item.removeAttribute("checked");
341 }
342 item = item.nextSibling;
343 }
344}
345
346function setAlgorithm(algoID) {
347 gPref.setCharPref("mandelbrot.use_algorithm", algoID);
348}
349
350
351/***** helper functions from external sources *****/
352
353// function below is from from http://developer.mozilla.org/en/docs/Code_snippets:Canvas
354function saveCanvas(canvas, destFile) {
355 // convert string filepath to an nsIFile
356 var file = Components.classes["@mozilla.org/file/local;1"]
357 .createInstance(Components.interfaces.nsILocalFile);
358 file.initWithPath(destFile);
359
360 // create a data url from the canvas and then create URIs of the source and targets
361 var io = Components.classes["@mozilla.org/network/io-service;1"]
362 .getService(Components.interfaces.nsIIOService);
363 var source = io.newURI(canvas.toDataURL("image/png", ""), "UTF8", null);
364 var target = io.newFileURI(file);
365
366 // prepare to save the canvas data
367 var persist = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
368 .createInstance(Components.interfaces.nsIWebBrowserPersist);
369
370 persist.persistFlags = Components.interfaces.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
371 persist.persistFlags |= Components.interfaces.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
372
373 // displays a download dialog (remove these 3 lines for silent download)
374 var xfer = Components.classes["@mozilla.org/transfer;1"]
375 .createInstance(Components.interfaces.nsITransfer);
376 xfer.init(source, target, "", null, null, null, persist);
377 persist.progressListener = xfer;
378
379 // save the canvas data to the file
380 persist.saveURI(source, null, null, null, null, file);
381}
382
383// function below is from http://developer.mozilla.org/en/docs/How_to_Quit_a_XUL_Application
384function quitApp(aForceQuit) {
385 var appStartup = Components.classes['@mozilla.org/toolkit/app-startup;1']
386 .getService(Components.interfaces.nsIAppStartup);
387
388 // eAttemptQuit will try to close each XUL window, but the XUL window can cancel the quit
389 // process if there is unsaved data. eForceQuit will quit no matter what.
390 var quitSeverity = aForceQuit ? Components.interfaces.nsIAppStartup.eForceQuit :
391 Components.interfaces.nsIAppStartup.eAttemptQuit;
392 appStartup.quit(quitSeverity);
393}