|
| 1 | +packageOthers; |
| 2 | + |
| 3 | +importjava.awt.*; |
| 4 | +importjava.awt.image.BufferedImage; |
| 5 | +importjava.io.File; |
| 6 | +importjava.io.IOException; |
| 7 | +importjavax.imageio.ImageIO; |
| 8 | + |
| 9 | +/** |
| 10 | + * The Mandelbrot set is the set of complex numbers "c" for which the series "z_(n+1) = z_n * z_n + |
| 11 | + * c" does not diverge, i.e. remains bounded. Thus, a complex number "c" is a member of the |
| 12 | + * Mandelbrot set if, when starting with "z_0 = 0" and applying the iteration repeatedly, the |
| 13 | + * absolute value of "z_n" remains bounded for all "n > 0". Complex numbers can be written as "a + |
| 14 | + * b*i": "a" is the real component, usually drawn on the x-axis, and "b*i" is the imaginary |
| 15 | + * component, usually drawn on the y-axis. Most visualizations of the Mandelbrot set use a |
| 16 | + * color-coding to indicate after how many steps in the series the numbers outside the set cross the |
| 17 | + * divergence threshold. Images of the Mandelbrot set exhibit an elaborate and infinitely |
| 18 | + * complicated boundary that reveals progressively ever-finer recursive detail at increasing |
| 19 | + * magnifications, making the boundary of the Mandelbrot set a fractal curve. (description adapted |
| 20 | + * from https://en.wikipedia.org/wiki/Mandelbrot_set ) (see also |
| 21 | + * https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set ) |
| 22 | + */ |
| 23 | +publicclassMandelbrot { |
| 24 | + |
| 25 | +publicstaticvoidmain(String[]args) { |
| 26 | +// Test black and white |
| 27 | +BufferedImageblackAndWhiteImage =getImage(800,600, -0.6,0,3.2,50,false); |
| 28 | + |
| 29 | +// Pixel outside the Mandelbrot set should be white. |
| 30 | +assertblackAndWhiteImage.getRGB(0,0) ==newColor(255,255,255).getRGB(); |
| 31 | + |
| 32 | +// Pixel inside the Mandelbrot set should be black. |
| 33 | +assertblackAndWhiteImage.getRGB(400,300) ==newColor(0,0,0).getRGB(); |
| 34 | + |
| 35 | +// Test color-coding |
| 36 | +BufferedImagecoloredImage =getImage(800,600, -0.6,0,3.2,50,true); |
| 37 | + |
| 38 | +// Pixel distant to the Mandelbrot set should be red. |
| 39 | +assertcoloredImage.getRGB(0,0) ==newColor(255,0,0).getRGB(); |
| 40 | + |
| 41 | +// Pixel inside the Mandelbrot set should be black. |
| 42 | +assertcoloredImage.getRGB(400,300) ==newColor(0,0,0).getRGB(); |
| 43 | + |
| 44 | +// Save image |
| 45 | +try { |
| 46 | +ImageIO.write(coloredImage,"png",newFile("Mandelbrot.png")); |
| 47 | + }catch (IOExceptione) { |
| 48 | +e.printStackTrace(); |
| 49 | + } |
| 50 | + } |
| 51 | + |
| 52 | +/** |
| 53 | + * Method to generate the image of the Mandelbrot set. Two types of coordinates are used: |
| 54 | + * image-coordinates that refer to the pixels and figure-coordinates that refer to the complex |
| 55 | + * numbers inside and outside the Mandelbrot set. The figure-coordinates in the arguments of this |
| 56 | + * method determine which section of the Mandelbrot set is viewed. The main area of the Mandelbrot |
| 57 | + * set is roughly between "-1.5 < x < 0.5" and "-1 < y < 1" in the figure-coordinates. |
| 58 | + * |
| 59 | + * @param imageWidth The width of the rendered image. |
| 60 | + * @param imageHeight The height of the rendered image. |
| 61 | + * @param figureCenterX The x-coordinate of the center of the figure. |
| 62 | + * @param figureCenterY The y-coordinate of the center of the figure. |
| 63 | + * @param figureWidth The width of the figure. |
| 64 | + * @param maxStep Maximum number of steps to check for divergent behavior. |
| 65 | + * @param useDistanceColorCoding Render in color or black and white. |
| 66 | + * @return The image of the rendered Mandelbrot set. |
| 67 | + */ |
| 68 | +publicstaticBufferedImagegetImage( |
| 69 | +intimageWidth, |
| 70 | +intimageHeight, |
| 71 | +doublefigureCenterX, |
| 72 | +doublefigureCenterY, |
| 73 | +doublefigureWidth, |
| 74 | +intmaxStep, |
| 75 | +booleanuseDistanceColorCoding) { |
| 76 | +if (imageWidth <=0) { |
| 77 | +thrownewIllegalArgumentException("imageWidth should be greater than zero"); |
| 78 | + } |
| 79 | + |
| 80 | +if (imageHeight <=0) { |
| 81 | +thrownewIllegalArgumentException("imageHeight should be greater than zero"); |
| 82 | + } |
| 83 | + |
| 84 | +if (maxStep <=0) { |
| 85 | +thrownewIllegalArgumentException("maxStep should be greater than zero"); |
| 86 | + } |
| 87 | + |
| 88 | +BufferedImageimage =newBufferedImage(imageWidth,imageHeight,BufferedImage.TYPE_INT_RGB); |
| 89 | +doublefigureHeight =figureWidth /imageWidth *imageHeight; |
| 90 | + |
| 91 | +// loop through the image-coordinates |
| 92 | +for (intimageX =0;imageX <imageWidth;imageX++) { |
| 93 | +for (intimageY =0;imageY <imageHeight;imageY++) { |
| 94 | +// determine the figure-coordinates based on the image-coordinates |
| 95 | +doublefigureX =figureCenterX + ((double)imageX /imageWidth -0.5) *figureWidth; |
| 96 | +doublefigureY =figureCenterY + ((double)imageY /imageHeight -0.5) *figureHeight; |
| 97 | + |
| 98 | +doubledistance =getDistance(figureX,figureY,maxStep); |
| 99 | + |
| 100 | +// color the corresponding pixel based on the selected coloring-function |
| 101 | +image.setRGB( |
| 102 | +imageX, |
| 103 | +imageY, |
| 104 | +useDistanceColorCoding |
| 105 | + ?colorCodedColorMap(distance).getRGB() |
| 106 | + :blackAndWhiteColorMap(distance).getRGB()); |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | +returnimage; |
| 111 | + } |
| 112 | + |
| 113 | +/** |
| 114 | + * Black and white color-coding that ignores the relative distance. The Mandelbrot set is black, |
| 115 | + * everything else is white. |
| 116 | + * |
| 117 | + * @param distance Distance until divergence threshold |
| 118 | + * @return The color corresponding to the distance. |
| 119 | + */ |
| 120 | +privatestaticColorblackAndWhiteColorMap(doubledistance) { |
| 121 | +returndistance >=1 ?newColor(0,0,0) :newColor(255,255,255); |
| 122 | + } |
| 123 | + |
| 124 | +/** |
| 125 | + * Color-coding taking the relative distance into account. The Mandelbrot set is black. |
| 126 | + * |
| 127 | + * @param distance Distance until divergence threshold. |
| 128 | + * @return The color corresponding to the distance. |
| 129 | + */ |
| 130 | +privatestaticColorcolorCodedColorMap(doubledistance) { |
| 131 | +if (distance >=1) { |
| 132 | +returnnewColor(0,0,0); |
| 133 | + }else { |
| 134 | +// simplified transformation of HSV to RGB |
| 135 | +// distance determines hue |
| 136 | +doublehue =360 *distance; |
| 137 | +doublesaturation =1; |
| 138 | +doubleval =255; |
| 139 | +inthi = (int) (Math.floor(hue /60)) %6; |
| 140 | +doublef =hue /60 -Math.floor(hue /60); |
| 141 | + |
| 142 | +intv = (int)val; |
| 143 | +intp =0; |
| 144 | +intq = (int) (val * (1 -f *saturation)); |
| 145 | +intt = (int) (val * (1 - (1 -f) *saturation)); |
| 146 | + |
| 147 | +switch (hi) { |
| 148 | +case0: |
| 149 | +returnnewColor(v,t,p); |
| 150 | +case1: |
| 151 | +returnnewColor(q,v,p); |
| 152 | +case2: |
| 153 | +returnnewColor(p,v,t); |
| 154 | +case3: |
| 155 | +returnnewColor(p,q,v); |
| 156 | +case4: |
| 157 | +returnnewColor(t,p,v); |
| 158 | +default: |
| 159 | +returnnewColor(v,p,q); |
| 160 | + } |
| 161 | + } |
| 162 | + } |
| 163 | + |
| 164 | +/** |
| 165 | + * Return the relative distance (ratio of steps taken to maxStep) after which the complex number |
| 166 | + * constituted by this x-y-pair diverges. Members of the Mandelbrot set do not diverge so their |
| 167 | + * distance is 1. |
| 168 | + * |
| 169 | + * @param figureX The x-coordinate within the figure. |
| 170 | + * @param figureX The y-coordinate within the figure. |
| 171 | + * @param maxStep Maximum number of steps to check for divergent behavior. |
| 172 | + * @return The relative distance as the ratio of steps taken to maxStep. |
| 173 | + */ |
| 174 | +privatestaticdoublegetDistance(doublefigureX,doublefigureY,intmaxStep) { |
| 175 | +doublea =figureX; |
| 176 | +doubleb =figureY; |
| 177 | +intcurrentStep =0; |
| 178 | +for (intstep =0;step <maxStep;step++) { |
| 179 | +currentStep =step; |
| 180 | +doubleaNew =a *a -b *b +figureX; |
| 181 | +b =2 *a *b +figureY; |
| 182 | +a =aNew; |
| 183 | + |
| 184 | +// divergence happens for all complex number with an absolute value |
| 185 | +// greater than 4 (= divergence threshold) |
| 186 | +if (a *a +b *b >4) { |
| 187 | +break; |
| 188 | + } |
| 189 | + } |
| 190 | +return (double)currentStep / (maxStep -1); |
| 191 | + } |
| 192 | +} |