add not-yet-working prefs menu
[mandelbrot.git] / xulapp / chrome / mandelbrot / content / mandelbrot.js
CommitLineData
37b05b56
RK
1var gColorPalette = getColorPalette('kairo');
2
3function drawImage() {
8a9c8e3f 4 let canvas = document.getElementById("mbrotImage");
37b05b56 5 if (canvas.getContext) {
8a9c8e3f 6 let context = canvas.getContext("2d");
37b05b56
RK
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
8a9c8e3f
RK
15 let Cr_min = -2.0;
16 let Cr_max = 1.0;
17 let Cr_scale = Cr_max - Cr_min;
37b05b56 18
8a9c8e3f
RK
19 let Ci_min = -1.5;
20 let Ci_max = 1.5;
21 let Ci_scale = Ci_max - Ci_min;
37b05b56 22
8a9c8e3f 23 let iterMax = 500;
37b05b56 24
8a9c8e3f
RK
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,
37b05b56
RK
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
35function 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
50function mandelbrotValue (aC, aIterMax) {
8444612a
RK
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);
37b05b56
RK
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 }
8444612a
RK
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 }
37b05b56
RK
70 return iter;
71}
72
73function getColor(aIterValue, aIterMax) {
8a9c8e3f 74 let standardizedValue = Math.round(aIterValue * 1024 / aIterMax);
37b05b56
RK
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
85function getColorPalette(palName) {
86 var palette = [];
87 switch (palName) {
88 case 'bw':
8a9c8e3f 89 for (let i = 0; i < 1024; i++) {
37b05b56
RK
90 palette[i] = 'rgb(255,255,255)';
91 }
92 palette[1024] = 'rgb(0,0,0)';
93 break;
94 case 'kairo':
95 // outer areas
8a9c8e3f
RK
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);
37b05b56
RK
99 palette[i] = 'rgb(' + cc1 + ',' + cc2 + ',' + cc1 + ')';
100 }
101 // inner areas
8a9c8e3f
RK
102 for (let i = 0; i < 51; i++) {
103 let cc = Math.floor(i * 170 / 50);
37b05b56
RK
104 palette[32 + i] = 'rgb(' + cc + ',0,' + (170 + cc) + ')';
105 }
106 // corona
8a9c8e3f
RK
107 for (let i = 0; i < 101; i++) {
108 let cc = Math.floor(i * 200 / 100);
37b05b56
RK
109 palette[83 + i] = 'rgb(255,' + cc + ',0)';
110 }
111 // inner corona
8a9c8e3f
RK
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);
37b05b56
RK
116 palette[184 + i] = 'rgb(' + cc1 + ',' + cc2 + ',' + cc3 + ')';
117 }
8a9c8e3f
RK
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);
37b05b56
RK
121 palette[385 + i] = 'rgb(' + cc1 + ',' + cc1 + ',' + cc2 + ')';
122 }
8a9c8e3f
RK
123 for (let i = 0; i < 338; i++) {
124 let cc = 127 + Math.floor(i * 128 / 337);
37b05b56
RK
125 palette[686 + i] = 'rgb(' + cc + ',' + cc + ',255)';
126 }
127 palette[1024] = 'rgb(0,0,0)';
128 break;
129 case 'rainbow-linear1':
8a9c8e3f 130 for (let i = 0; i < 256; i++) {
37b05b56
RK
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/*
140Select Case palnr
141Case 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
149Case 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
166Case 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
175Case 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
200function drawPoint(context, img_x, img_y, C, iterMax) {
8a9c8e3f 201 let itVal = mandelbrotValue(C, iterMax);
37b05b56
RK
202 context.fillStyle = getColor(itVal, iterMax);
203 context.fillRect (img_x, img_y, 1, 1); // x, y, width, height
204}
205
206function 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
212function 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
242function 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}