Source code: com/arranger/jarl/widget/filter/Fractal.java
1 package com.arranger.jarl.widget.filter;
2
3 import com.arranger.jarl.base.IContext;
4 import com.arranger.jarl.base.IGradientManager;
5 import com.arranger.jarl.base.IJarlObjectInfo;
6 import com.arranger.jarl.util.InterpolateUtil;
7 import com.arranger.jarl.util.ObjectUtil;
8 import com.arranger.jarl.util.WidgetConfigSegment;
9 import com.arranger.jarl.util.JarlInfoUtil;
10 import com.jhlabs.image.Gradient;
11 import org.w3c.dom.Element;
12
13 import java.awt.*;
14 import java.awt.geom.Point2D;
15
16 /**
17 * Fractal creates a mandelbrot fractal. note, this currently will ignore
18 * traits, strokes & filters...
19 *
20 * @widgetAttribute gradientIndex ## xs:integer ## the gradient index
21 * @widgetAttribute iterations ## xs:integer ## the max number of iterations
22 * @widgetAttribute complexRect ## xs:string ## the complex rectangle to show
23 *
24 * @widgetAttribute startGradientIndex ## xs:integer ## the starting gradient index
25 * @widgetAttribute startIterations ## xs:integer ## the starting max number of iterations
26 * @widgetAttribute startComplexRect ## xs:string ## the starting complex rectangle to show
27 * @widgetAttribute endGradientIndex ## xs:integer ## the ending gradient index
28 * @widgetAttribute endIterations ## xs:integer ## the ending max number of iterations
29 * @widgetAttribute endComplexRect ## xs:string ## the ending complex rectangle to show
30 */
31 public class Fractal extends BaseFilterWidget {
32
33 protected static final int NUM_COLORS = 512;
34
35 protected int m_gradientIndex = Integer.MAX_VALUE;
36 protected int m_iterations = Integer.MAX_VALUE;
37 protected ComplexRect m_complexRect;
38
39 protected int m_startGradientIndex = Integer.MAX_VALUE;
40 protected int m_endGradientIndex = Integer.MAX_VALUE;
41 protected int m_startIterations = Integer.MAX_VALUE;
42 protected int m_endIterations = Integer.MAX_VALUE;
43 protected ComplexRect m_startComplexRect;
44 protected ComplexRect m_endComplexRect;
45
46
47 protected void _paint(IContext context, Graphics2D graphics2D) {
48 //calc real width & height
49 double width = getWidth(context);
50 double height = getHeight(context);
51
52 //get & fixup complex rect
53 ComplexRect complexRect = null;
54 if (m_complexRect != null) {
55 complexRect = m_complexRect;
56 } else if (m_startComplexRect != null && m_endComplexRect != null) {
57
58 //interpolate
59 double realMin = InterpolateUtil.interpolate(m_startTime,
60 m_endTime,
61 m_startComplexRect.m_realMin,
62 m_endComplexRect.m_realMin,
63 context.getTime());
64
65 double realMax = InterpolateUtil.interpolate(m_startTime,
66 m_endTime,
67 m_startComplexRect.m_realMax,
68 m_endComplexRect.m_realMax,
69 context.getTime());
70
71 double imagMin = InterpolateUtil.interpolate(m_startTime,
72 m_endTime,
73 m_startComplexRect.m_imagMin,
74 m_endComplexRect.m_imagMin,
75 context.getTime());
76
77 double imagMax = InterpolateUtil.interpolate(m_startTime,
78 m_endTime,
79 m_startComplexRect.m_imagMax,
80 m_endComplexRect.m_imagMax,
81 context.getTime());
82 complexRect = new ComplexRect(realMin, realMax, imagMin, imagMax);
83 } else {
84 FractalConfigSegment fractalConfigSegment = (FractalConfigSegment) getCurrentSegment(context);
85 double currentTimePct = getCurrentSegmentTimePct(context, fractalConfigSegment);
86
87 ComplexRect startComplexRect = fractalConfigSegment.getStartComplexRect();
88 ComplexRect endComplexRect = fractalConfigSegment.getEndComplexRect();
89
90 double realMin = InterpolateUtil.interpolate(0,
91 1,
92 startComplexRect.m_realMin,
93 endComplexRect.m_realMin,
94 currentTimePct);
95
96 double realMax = InterpolateUtil.interpolate(0,
97 1,
98 startComplexRect.m_realMax,
99 endComplexRect.m_realMax,
100 currentTimePct);
101
102 double imagMin = InterpolateUtil.interpolate(0,
103 1,
104 startComplexRect.m_imagMin,
105 endComplexRect.m_imagMin,
106 currentTimePct);
107
108 double imagMax = InterpolateUtil.interpolate(0,
109 1,
110 startComplexRect.m_imagMax,
111 endComplexRect.m_imagMax,
112 currentTimePct);
113 complexRect = new ComplexRect(realMin, realMax, imagMin, imagMax);
114 }
115
116
117 complexRect = ComplexRect.adjust(complexRect, width, height);
118
119 //get the color map from the gradient (try caching?)
120 Color[] colors = prepareColorArray(context);
121
122 //iterations
123 int iterations = 0;
124 if (m_iterations != Integer.MAX_VALUE) {
125 iterations = m_iterations;
126 } else if (m_startIterations != Integer.MAX_VALUE && m_endIterations != Integer.MAX_VALUE) {
127 iterations = (int) InterpolateUtil.interpolate(m_startTime,
128 m_endTime,
129 m_startIterations,
130 m_endIterations,
131 context.getTime());
132 } else {
133 FractalConfigSegment fractalConfigSegment = (FractalConfigSegment) getCurrentSegment(context);
134 double currentTimePct = getCurrentSegmentTimePct(context, fractalConfigSegment);
135
136 iterations = (int) InterpolateUtil.interpolate(0,
137 1,
138 fractalConfigSegment.getStartIterations(),
139 fractalConfigSegment.getEndIterations(),
140 currentTimePct);
141 }
142
143 //for each pixel get the color
144 Color color = graphics2D.getColor();
145 paintFractal(width, height, iterations, complexRect, graphics2D, colors, context);
146 graphics2D.setColor(color);
147 }
148
149 protected void paintFractal(double width, double height, int maxIterations, ComplexRect complexRect, Graphics2D graphics2D, Color[] colors, IContext context) {
150 double delta = (complexRect.m_realMax - complexRect.m_realMin) / width;
151
152 Point2D centerPoint = new Point2D.Double(context.getWidth() / 2.0, context.getHeight() / 2.0);
153 int offsetX = (int) (centerPoint.getX() - (width / 2.0));
154 int offsetY = (int) (centerPoint.getY() - (height / 2.0));
155
156 for (int x = 0; x < width; x++) {
157 for (int y = 0; y < height; y++) {
158 Color color = getColorForPoint(x, y, delta, height, maxIterations, complexRect, colors);
159 graphics2D.setColor(color);
160 graphics2D.drawLine(x + offsetX, y + offsetY, x + offsetX, y + offsetY);
161 }
162 }
163 }
164
165 protected Color getColorForPoint(double x, double y, double delta, double height, int maxIterations, ComplexRect complexRect, Color[] colors) {
166 Color c = Color.black;
167 double zR = complexRect.m_realMin + x * delta;
168 double zI = complexRect.m_imagMin + ((height - y)) * delta;
169
170 // Is the point inside the set?
171 int numIterations = testPoint(zR, zI, maxIterations);
172
173 if (numIterations != 0) {
174 // The point is outside the set. It gets a color based on the number
175 // of iterations it took to know this.
176 int colorNum = (int) ((float) colors.length * (1.0 -
177 (float) numIterations / (float) maxIterations));
178 colorNum = (colorNum == colors.length) ? 0 : colorNum;
179
180 c = colors[colorNum];
181 }
182 return c;
183 }
184
185 /**
186 * Is the given complex point, (cR, cI), in the Mandelbrot set?
187 * Use the formula: z <= z*z + c, where z is initially equal to c.
188 * If |z| >= 2, then the point is not in the set.
189 * Return 0 if the point is in the set; else return the number of
190 * iterations it took to decide that the point is not in the set.
191 */
192 protected int testPoint(double cR, double cI, int maxIterations) {
193 double zR = cR;
194 double zI = cI;
195
196 for (int i = 1; i <= maxIterations; i++) {
197 // To square a complex number: (a+bi)(a+bi) = a*a - b*b + 2abi
198 double zROld = zR;
199 zR = zR * zR - zI * zI + cR;
200 zI = 2 * zROld * zI + cI;
201
202 // We know that if the distance from z to the origin is >= 2
203 // then the point is out of the set. To avoid a square root,
204 // we'll instead check if the distance squared >= 4.
205 double distSquared = zR * zR + zI * zI;
206 if (distSquared >= 4) {
207 return i;
208 }
209 }
210 return 0;
211 }
212
213 /**
214 * Get the gradient color map
215 */
216 protected Color[] prepareColorArray(IContext context) {
217
218 IGradientManager gradientManager = context.getGradientManager();
219
220 Gradient gradient;
221 if (m_gradientIndex != Integer.MAX_VALUE) {
222 gradient = gradientManager.getGradient(m_gradientIndex);
223 } else if (m_startGradientIndex != Integer.MAX_VALUE && m_endGradientIndex != Integer.MAX_VALUE) {
224 //interpolate
225 double currentTimePct = InterpolateUtil.interpolate(this, context.getTime());
226 Gradient gradient1 = gradientManager.getGradient(m_startGradientIndex);
227 Gradient gradient2 = gradientManager.getGradient(m_endGradientIndex);
228 gradient = gradientManager.interpolate(gradient1, gradient2, currentTimePct);
229
230 } else {
231 FractalConfigSegment fractalConfigSegment = (FractalConfigSegment) getCurrentSegment(context);
232 double currentTimePct = getCurrentSegmentTimePct(context, fractalConfigSegment);
233
234 //interpolate
235 Gradient gradient1 = gradientManager.getGradient(fractalConfigSegment.getStartGradientIndex());
236 Gradient gradient2 = gradientManager.getGradient(fractalConfigSegment.getEndGradientIndex());
237 gradient = gradientManager.interpolate(gradient1, gradient2, currentTimePct);
238 }
239
240 Color[] colorMap = new Color[NUM_COLORS];
241 for (int colorNum = 0; colorNum < NUM_COLORS; colorNum++) {
242 float f = (float) colorNum / (float) NUM_COLORS;
243 int color = gradient.getColor(f);
244 colorMap[colorNum] = new Color(color);
245 }
246
247 return colorMap;
248 }
249
250 protected void initAttributes(IContext context) {
251 super.initAttributes(context);
252
253 ObjectUtil.initializeField("gradientIndex", m_configElement, this, ObjectUtil.PRIMITIVE_CONVERSION);
254 ObjectUtil.initializeField("iterations", m_configElement, this, ObjectUtil.PRIMITIVE_CONVERSION);
255 ObjectUtil.initializeField("complexRect", m_configElement, this, ComplexRect.COMPLEX_CONVERSION);
256
257 ObjectUtil.initializeField("startGradientIndex", m_configElement, this, ObjectUtil.PRIMITIVE_CONVERSION);
258 ObjectUtil.initializeField("startIterations", m_configElement, this, ObjectUtil.PRIMITIVE_CONVERSION);
259 ObjectUtil.initializeField("startComplexRect", m_configElement, this, ComplexRect.COMPLEX_CONVERSION);
260 ObjectUtil.initializeField("endGradientIndex", m_configElement, this, ObjectUtil.PRIMITIVE_CONVERSION);
261 ObjectUtil.initializeField("endIterations", m_configElement, this, ObjectUtil.PRIMITIVE_CONVERSION);
262 ObjectUtil.initializeField("endComplexRect", m_configElement, this, ComplexRect.COMPLEX_CONVERSION);
263 }
264
265 protected void addJarlObjectInfo(IJarlObjectInfo jarlObjectInfo) {
266 super.addJarlObjectInfo(jarlObjectInfo);
267 populateInfo(jarlObjectInfo, "gradientIndex", "Gradient Index", JarlInfoUtil.PRIMITIVE_DISPLAY);
268 populateInfo(jarlObjectInfo, "iterations", "Iterations", JarlInfoUtil.PRIMITIVE_DISPLAY);
269 populateInfo(jarlObjectInfo, "complexRect", "Complex Rect", ComplexRect.COMPLEX_DISPLAY);
270
271 populateInfo(jarlObjectInfo, "startGradientIndex", "Start Gradient Index", JarlInfoUtil.PRIMITIVE_DISPLAY);
272 populateInfo(jarlObjectInfo, "endGradientIndex", "End Gradient Index", JarlInfoUtil.PRIMITIVE_DISPLAY);
273 populateInfo(jarlObjectInfo, "startIterations", "Start Iterations", JarlInfoUtil.PRIMITIVE_DISPLAY);
274 populateInfo(jarlObjectInfo, "endIterations", "End Iterations", JarlInfoUtil.PRIMITIVE_DISPLAY);
275 populateInfo(jarlObjectInfo, "startComplexRect", "Start Complex Rect", ComplexRect.COMPLEX_DISPLAY);
276 populateInfo(jarlObjectInfo, "endComplexRect", "End Complex Rect", ComplexRect.COMPLEX_DISPLAY);
277 }
278
279 public WidgetConfigSegment createSegment(Element element) {
280 return new FractalConfigSegment(element);
281 }
282
283 public static class FractalConfigSegment extends WidgetConfigSegment {
284
285 protected int m_startGradientIndex = Integer.MAX_VALUE;
286 protected int m_endGradientIndex = Integer.MAX_VALUE;
287 protected int m_startIterations = Integer.MAX_VALUE;
288 protected int m_endIterations = Integer.MAX_VALUE;
289 protected ComplexRect m_startComplexRect;
290 protected ComplexRect m_endComplexRect;
291
292
293 public FractalConfigSegment(Element element) {
294 super(element);
295
296 ObjectUtil.initializeFieldStrict("startGradientIndex", element, this, ObjectUtil.PRIMITIVE_CONVERSION);
297 ObjectUtil.initializeFieldStrict("startIterations", element, this, ObjectUtil.PRIMITIVE_CONVERSION);
298 ObjectUtil.initializeFieldStrict("startComplexRect", element, this, ComplexRect.COMPLEX_CONVERSION);
299 ObjectUtil.initializeFieldStrict("endGradientIndex", element, this, ObjectUtil.PRIMITIVE_CONVERSION);
300 ObjectUtil.initializeFieldStrict("endIterations", element, this, ObjectUtil.PRIMITIVE_CONVERSION);
301 ObjectUtil.initializeFieldStrict("endComplexRect", element, this, ComplexRect.COMPLEX_CONVERSION);
302 }
303
304 protected void addJarlObjectInfo(IJarlObjectInfo jarlObjectInfo) {
305 super.addJarlObjectInfo(jarlObjectInfo);
306 populateInfo(jarlObjectInfo, "startGradientIndex", "Start Gradient Index", JarlInfoUtil.PRIMITIVE_DISPLAY);
307 populateInfo(jarlObjectInfo, "endGradientIndex", "End Gradient Index", JarlInfoUtil.PRIMITIVE_DISPLAY);
308 populateInfo(jarlObjectInfo, "startIterations", "Start Iterations", JarlInfoUtil.PRIMITIVE_DISPLAY);
309 populateInfo(jarlObjectInfo, "endIterations", "End Iterations", JarlInfoUtil.PRIMITIVE_DISPLAY);
310 populateInfo(jarlObjectInfo, "startComplexRect", "Start Complex Rect", ComplexRect.COMPLEX_DISPLAY);
311 populateInfo(jarlObjectInfo, "endComplexRect", "End Complex Rect", ComplexRect.COMPLEX_DISPLAY);
312 }
313
314 public int getStartGradientIndex() {
315 return m_startGradientIndex;
316 }
317
318 public int getEndGradientIndex() {
319 return m_endGradientIndex;
320 }
321
322 public int getStartIterations() {
323 return m_startIterations;
324 }
325
326 public int getEndIterations() {
327 return m_endIterations;
328 }
329
330 public ComplexRect getStartComplexRect() {
331 return m_startComplexRect;
332 }
333
334 public ComplexRect getEndComplexRect() {
335 return m_endComplexRect;
336 }
337 }
338 }