This project has retired. For details please refer to its
Attic page.
FractalGenerator xref
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.chemistry.opencmis.util.content.fractal;
29
30 import java.awt.Color;
31 import java.awt.Rectangle;
32 import java.awt.image.BufferedImage;
33 import java.io.ByteArrayOutputStream;
34 import java.io.IOException;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.Locale;
38 import java.util.Map;
39 import java.util.Random;
40
41 import javax.imageio.IIOImage;
42 import javax.imageio.ImageIO;
43 import javax.imageio.ImageWriteParam;
44 import javax.imageio.ImageWriter;
45 import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
46 import javax.imageio.stream.ImageOutputStream;
47
48 import org.apache.commons.logging.Log;
49 import org.apache.commons.logging.LogFactory;
50
51
52 public class FractalGenerator {
53 private static final Log LOG = LogFactory.getLog(FractalGenerator.class);
54
55 private final static int ZOOM_STEPS_PER_BATCH = 10;
56 private final static int DEFAULT_MAX_ITERATIONS = 33;
57 private final static ComplexRectangle INITIAL_RECT = new ComplexRectangle(-2.1, 1.1, -1.3, 1.3);
58 private final static ComplexRectangle INITIAL_JULIA_RECT = new ComplexRectangle(-2.0, 2.0, -2.0, 2.0);
59 private final static int INITIAL_ITERATIONS = 33;
60
61
62 private Map<String, int[]> colorTable;
63 private final String COLORS_BLACK_AND_WHITE = "black & white";
64 private final String COLORS_BLUE_ICE = "blue ice";
65 private final String COLORS_FUNKY = "funky";
66 private final String COLORS_PASTEL = "pastel";
67 private final String COLORS_PSYCHEDELIC = "psychedelic";
68 private final String COLORS_PURPLE_HAZE = "purple haze";
69 private final String COLORS_RADICAL = "radical";
70 private final String COLORS_RAINBOW = "rainbow";
71 private final String COLORS_RAINBOWS = "rainbows";
72 private final String COLORS_SCINTILLATION = "scintillation";
73 private final String COLORS_WARPED = "warped";
74 private final String COLORS_WILD = "wild";
75 private final String COLORS_ZEBRA = "zebra";
76 private final String[] colorSchemes = {COLORS_BLACK_AND_WHITE, COLORS_BLUE_ICE, COLORS_FUNKY, COLORS_PASTEL,
77 COLORS_PSYCHEDELIC, COLORS_PURPLE_HAZE, COLORS_RADICAL, COLORS_RAINBOW, COLORS_RAINBOWS,
78 COLORS_SCINTILLATION, COLORS_WARPED, COLORS_WILD, COLORS_ZEBRA};
79 private final int imageHeight = 512;
80 private final int imageWidth = 512;
81 private final int numColors = 512;
82 private FractalCalculator calculator;
83 private int previousIterations = 1;
84 private int maxIterations;
85 String color;
86 int counter = 0;
87 int newRowTile, newColTile;
88 int parts = 16;
89 private int stepInBatch = 0;
90 ComplexRectangle rect;
91 ComplexPoint juliaPoint;
92
93 public FractalGenerator() {
94 reset();
95 }
96
97 private void reset() {
98 rect = new ComplexRectangle(-1.6, -1.2, -0.1, 0.1);
99 juliaPoint = null;
100 maxIterations = DEFAULT_MAX_ITERATIONS;
101
102 Random ran = new Random();
103 color = colorSchemes[ran.nextInt(colorSchemes.length)];
104 parts = ran.nextInt(13)+3;
105 LOG.debug("Parts: " + parts);
106 maxIterations = DEFAULT_MAX_ITERATIONS;
107 LOG.debug("Original rect " + ": (" + rect.getRMin() + "r," + rect.getRMax() +
108 "r, " + rect.getIMin() + "i, " + rect.getIMax() + "i)");
109 randomizeRect(rect);
110 }
111
112 public ByteArrayOutputStream generateFractal() throws IOException {
113 ByteArrayOutputStream bos = null;
114
115 if (stepInBatch == ZOOM_STEPS_PER_BATCH) {
116 stepInBatch = 0;
117 reset();
118 }
119
120 ++stepInBatch;
121 LOG.debug("Generating rect no " + stepInBatch + ": (" + rect.getRMin() + "r," +
122 rect.getRMax() + "r, " + rect.getIMin() + "i, " + rect.getIMax() + "i)");
123 LOG.debug(" width: " + rect.getWidth() + " height: " + rect.getHeight());
124 bos = genFractal(rect, juliaPoint);
125
126 double r1New = rect.getWidth() * newColTile / parts + rect.getRMin();
127 double r2New = rect.getWidth() * (newColTile+1) / parts + rect.getRMin();
128 double i1New = rect.getIMax() - (rect.getHeight() * newRowTile / parts);
129 double i2New = rect.getIMax() - (rect.getHeight() * (newRowTile+1) / parts);
130 rect.set(r1New, r2New, i1New, i2New);
131 randomizeRect(rect);
132 LOG.debug("Done generating fractals.");
133
134 return bos;
135 }
136
137 private void randomizeRect( ComplexRectangle rect) {
138 double jitterFactor = 0.15;
139 double ran = Math.random() * jitterFactor + (1.0 - jitterFactor);
140 double width = rect.getWidth() * ran;
141 ran = Math.random() * jitterFactor + (1.0 - jitterFactor);
142 double height = rect.getHeight() * ran;
143 ran = Math.random() * jitterFactor + (1.0 - jitterFactor);
144 double r1 = (rect.getWidth() - width) * ran + rect.getRMin();
145 ran = Math.random() * jitterFactor + (1.0 - jitterFactor);
146 double i1 = (rect.getHeight() - height) * ran + rect.getIMin();
147 rect.set(r1, r1+width, i1, i1+height);
148 }
149
150
151
152
153
154
155
156
157
158
159
160 public ByteArrayOutputStream genFractal(ComplexRectangle rect, ComplexPoint juliaPoint) throws IOException {
161
162 boolean isJulia = null != juliaPoint;
163 expandRectToFitImage(rect);
164 initializeColors();
165
166 maxIterations = maybeGuessMaxIterations(maxIterations, rect, isJulia);
167 LOG.debug("using " + maxIterations + " iterations.");
168 detectDeepZoom(rect);
169
170 calculator = new FractalCalculator(rect, maxIterations, imageWidth, imageHeight, getCurrentColorMap(),
171 juliaPoint);
172 int[][] iterations = calculator.calcFractal();
173 BufferedImage image = calculator.mapItersToColors(iterations);
174 findNewRect(image, iterations);
175
176
177
178
179
180 ByteArrayOutputStream bos = new ByteArrayOutputStream(200*1024);
181 ImageOutputStream ios = ImageIO.createImageOutputStream(bos);
182 Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName( "jpg" );
183 ImageWriter imageWriter = writers.next();
184
185 JPEGImageWriteParam params = new JPEGImageWriteParam( Locale.getDefault() );
186 params.setCompressionMode( ImageWriteParam.MODE_EXPLICIT );
187 params.setCompressionQuality( 0.9f );
188
189 imageWriter.setOutput( ios );
190 imageWriter.write( null, new IIOImage( image, null, null ), params );
191 ios.close();
192
193
194
195
196
197
198
199
200 return bos;
201 }
202
203 protected int[] getCurrentColorMap() {
204 return colorTable.get(getColor());
205 }
206
207 protected String getColor() {
208 return color;
209 }
210
211 protected void expandRectToFitImage(ComplexRectangle complexRect) {
212
213
214 double imageWHRatio = 1.0;
215 double complexWHRatio = 1.0;
216 double iMin = complexRect.getIMin();
217 double iMax = complexRect.getIMax();
218 double rMin = complexRect.getRMin();
219 double rMax = complexRect.getRMax();
220 double complexWidth = rMax - rMin;
221 double complexHeight = iMax - iMin;
222
223 if ((imageWidth != 0) && (imageHeight != 0)) {
224 imageWHRatio = ((double) imageWidth / (double) imageHeight);
225 } else
226 return;
227
228 if ((complexWidth != 0) && (complexHeight != 0)) {
229 complexWHRatio = complexWidth / complexHeight;
230 } else
231 return;
232
233 if (imageWHRatio == complexWHRatio)
234 return;
235
236 if (imageWHRatio < complexWHRatio) {
237
238 double newHeight = complexWidth / imageWHRatio;
239 double heightDifference = Math.abs(newHeight - complexHeight);
240 iMin = iMin - heightDifference / 2;
241 iMax = iMax + heightDifference / 2;
242 } else {
243
244 double newWidth = complexHeight * imageWHRatio;
245 double widthDifference = Math.abs(newWidth - complexWidth);
246 rMin = rMin - widthDifference / 2;
247 rMax = rMax + widthDifference / 2;
248 }
249 complexRect.set(rMin, rMax, iMin, iMax);
250 }
251
252 private int guessNewMaxIterations(ComplexRectangle cr, boolean isJulia) {
253
254
255
256 double zoom = INITIAL_RECT.getWidth() / cr.getWidth();
257 if (zoom < 1.0) {
258 zoom = 1.0;
259 }
260 double logZoom = Math.log(zoom);
261 double magnitude = (logZoom / 2.3) - 2.0;
262 if (magnitude < 1.0) {
263 magnitude = 1.0;
264 }
265 double iterations = INITIAL_ITERATIONS * (magnitude * logZoom + 1.0);
266 if (isJulia)
267 iterations *= 2.0;
268 return (int) iterations;
269 }
270
271 private int maybeGuessMaxIterations(int maxIterations, ComplexRectangle cr, boolean isJulia) {
272
273 if (previousIterations == maxIterations) {
274 maxIterations = guessNewMaxIterations(cr, isJulia);
275 }
276 previousIterations = maxIterations;
277 return maxIterations;
278 }
279
280 private boolean detectDeepZoom(ComplexRectangle cr) {
281
282
283
284
285 double deltaDiv2 = cr.getWidth() / ((imageWidth) * 2.0);
286 String min = "" + (cr.getRMin());
287 String minPlus = "" + (cr.getRMin() + deltaDiv2);
288
289 if (Double.valueOf(min).doubleValue() == Double.valueOf(minPlus).doubleValue()) {
290 LOG.warn("Deep Zoom... Drawing resolution will be degraded ;-(");
291 return true;
292 }
293 return false;
294 }
295
296 private void initializeColors() {
297 colorTable = new HashMap<String, int[]>();
298
299 int red = 255;
300 int green = 255;
301 int blue = 255;
302
303 float hue = (float) 1.0;
304 float saturation = (float) 1.0;
305 float brightness = (float) 1.0;
306
307
308 int[] colorMap = new int[numColors];
309 for (int colorNum = numColors - 1; colorNum >= 0; colorNum--) {
310 colorMap[colorNum] = Color.white.getRGB();
311 }
312 colorTable.put(COLORS_BLACK_AND_WHITE, colorMap);
313
314
315 blue = 255;
316 colorMap = new int[numColors];
317 for (int colorNum = numColors - 1; colorNum >= 0; colorNum--) {
318 red = (int) ((255 * (float) colorNum / numColors)) % 255;
319 green = (int) ((255 * (float) colorNum / numColors)) % 255;
320 colorMap[colorNum] = new Color(red, green, blue).getRGB();
321 }
322 colorTable.put(COLORS_BLUE_ICE, colorMap);
323
324
325 colorMap = new int[numColors];
326 for (int colorNum = numColors - 1; colorNum >= 0; colorNum--) {
327 red = (int) ((1024 * (float) colorNum / numColors)) % 255;
328 green = (int) ((512 * (float) colorNum / numColors)) % 255;
329 blue = (int) ((256 * (float) colorNum / numColors)) % 255;
330 colorMap[numColors - colorNum - 1] = new Color(red, green, blue).getRGB();
331 }
332 colorTable.put(COLORS_FUNKY, colorMap);
333
334
335 brightness = (float) 1.0;
336 colorMap = new int[numColors];
337 for (int colorNum = 0; colorNum < numColors; colorNum++) {
338 hue = ((float) (colorNum * 4) / (float) numColors) % numColors;
339 saturation = ((float) (colorNum * 2) / (float) numColors) % numColors;
340 colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
341 }
342 colorTable.put(COLORS_PASTEL, colorMap);
343
344
345 saturation = (float) 1.0;
346 colorMap = new int[numColors];
347 for (int colorNum = 0; colorNum < numColors; colorNum++) {
348 hue = ((float) (colorNum * 5) / (float) numColors) % numColors;
349 brightness = ((float) (colorNum * 20) / (float) numColors) % numColors;
350 colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
351 }
352 colorTable.put(COLORS_PSYCHEDELIC, colorMap);
353
354
355 red = 255;
356 blue = 255;
357 colorMap = new int[numColors];
358 for (int colorNum = numColors - 1; colorNum >= 0; colorNum--) {
359 green = (int) ((255 * (float) colorNum / numColors)) % 255;
360 colorMap[numColors - colorNum - 1] = new Color(red, green, blue).getRGB();
361 }
362 colorTable.put(COLORS_PURPLE_HAZE, colorMap);
363
364
365 saturation = (float) 1.0;
366 colorMap = new int[numColors];
367 for (int colorNum = 0; colorNum < numColors; colorNum++) {
368 hue = ((float) (colorNum * 7) / (float) numColors) % numColors;
369 brightness = ((float) (colorNum * 49) / (float) numColors) % numColors;
370 colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
371 }
372 colorTable.put(COLORS_RADICAL, colorMap);
373
374
375 saturation = (float) 1.0;
376 brightness = (float) 1.0;
377 colorMap = new int[numColors];
378 for (int colorNum = 0; colorNum < numColors; colorNum++) {
379 hue = (float) colorNum / (float) numColors;
380 colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
381 }
382 colorTable.put(COLORS_RAINBOW, colorMap);
383
384
385 saturation = (float) 1.0;
386 brightness = (float) 1.0;
387 colorMap = new int[numColors];
388 for (int colorNum = 0; colorNum < numColors; colorNum++) {
389 hue = ((float) (colorNum * 5) / (float) numColors) % numColors;
390 colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
391 }
392 colorTable.put(COLORS_RAINBOWS, colorMap);
393
394
395 brightness = (float) 1.0;
396 saturation = (float) 1.0;
397 colorMap = new int[numColors];
398 for (int colorNum = 0; colorNum < numColors; colorNum++) {
399 hue = ((float) (colorNum * 2) / (float) numColors) % numColors;
400 brightness = ((float) (colorNum * 5) / (float) numColors) % numColors;
401 colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
402 }
403 colorTable.put(COLORS_SCINTILLATION, colorMap);
404
405
406 colorMap = new int[numColors];
407 for (int colorNum = numColors - 1; colorNum >= 0; colorNum--) {
408 red = (int) ((1024 * (float) colorNum / numColors)) % 255;
409 green = (int) ((256 * (float) colorNum / numColors)) % 255;
410 blue = (int) ((512 * (float) colorNum / numColors)) % 255;
411 colorMap[numColors - colorNum - 1] = new Color(red, green, blue).getRGB();
412 }
413 colorTable.put(COLORS_WARPED, colorMap);
414
415
416 colorMap = new int[numColors];
417 for (int colorNum = 0; colorNum < numColors; colorNum++) {
418 hue = ((float) (colorNum * 1) / (float) numColors) % numColors;
419 saturation = ((float) (colorNum * 2) / (float) numColors) % numColors;
420 brightness = ((float) (colorNum * 4) / (float) numColors) % numColors;
421 colorMap[colorNum] = Color.HSBtoRGB(hue, saturation, brightness);
422 }
423 colorTable.put(COLORS_WILD, colorMap);
424
425
426 colorMap = new int[numColors];
427 for (int colorNum = 0; colorNum < numColors; colorNum++) {
428 if (colorNum % 2 == 0) {
429 colorMap[colorNum] = Color.white.getRGB();
430 ;
431 } else {
432 colorMap[colorNum] = Color.black.getRGB();
433 ;
434 }
435 }
436 colorTable.put(COLORS_ZEBRA, colorMap);
437 }
438
439
440 private void findNewRect(BufferedImage image, int[][] iterations) {
441
442 int newWidth = image.getWidth() / parts;
443 int newHeight = image.getHeight() / parts;
444 int i=0, j=0;
445 int noTiles = (image.getWidth() / newWidth) * (image.getHeight() / newHeight);
446 double[] stdDev = new double [noTiles];
447
448 for (int y = 0; y+newHeight <= image.getHeight(); y+=newHeight) {
449 for (int x = 0; x+newWidth <= image.getWidth(); x+=newWidth) {
450 Rectangle subRect = new Rectangle(x, y, newWidth, newHeight);
451 stdDev[i*parts+j] = calcStdDev(iterations, subRect);
452 ++j;
453 }
454 ++i;
455 j=0;
456 }
457
458
459 double max = 0;
460 int index = 0;
461 for (i=0; i<noTiles; i++) {
462 if (stdDev[i] > max) {
463 index = i;
464 max = stdDev[i];
465 }
466 }
467 newRowTile = index / parts;
468 newColTile = index % parts;
469 }
470
471 private double calcStdDev(int[][] iterations, Rectangle rect) {
472
473 int sum=0;
474 long sumSquare=0;
475
476 for (int x = rect.x; x < rect.x+rect.width; x+=1) {
477 for (int y = rect.y; y < rect.y+rect.height; y+=1) {
478 int iters = iterations[x][y];
479 sum +=iters;
480 sumSquare +=iters*iters;
481 }
482 }
483 int count = rect.width * rect.height;
484 double mean = 0.0;
485
486 mean = sum / count;
487 return Math.sqrt(sumSquare/count - (mean * mean));
488 }
489
490 }