I am writing a multi-threaded fractal drawing program using JavaFX 2.2, and now I need to be guided.
What I'm trying to achieve is to create a task or service (not yet decided) that then launches some other tasks that actually do the calculations and return sections of the whole image when they are ready. When all the parts return to the initiating task, they stack the pieces and return them to the main stream so that they can be visualized.
Obviously, all this should happen without blocking the user interface.
The problem is that I cannot understand how these tasks can interact with each other. For example, I need to update the progress property of the initiating task based on the average progress of the tasks inside it (or something like this), so their progress properties should be tied to the progress property of the initiating task. Parts of the image should be placed in a list or some container and redraw on a separate image when they are all available.
I have already written a simpler (albeit still experimental) version of this program that creates only one task, which calculates the entire fractal. Progress is associated with the progressBar GUI. The return value is processed by the EventHandler when the task succeeds.
I am not asking for a complete solution, but some ideas, perhaps a little example code, will really help me.
This is the class that should be changed:
package fractal; import fractalUtil.DefaultPalette; import fractalUtil.PaletteInterface; import javafx.concurrent.Task; import javafx.scene.image.WritableImage; import javafx.scene.paint.Color; import org.apache.commons.math3.complex.Complex; public abstract class AbstractFractal extends Task implements FractalInterface { private PaletteInterface palette; protected final int width, height, order, iterations; protected final double scale, xReal, xIm, xCenter, yCenter, zoom; protected final boolean julia; protected AbstractFractal(final int width, final int height, final double xReal, final double xIm, final double xCenter, final double yCenter, final int order, final boolean julia, final int iterations, final double zoom) { this.width = width; this.height = height; this.xReal = xReal; this.xIm = xIm; this.xCenter = xCenter; this.yCenter = yCenter; this.order = order; this.julia = julia; this.iterations = iterations; this.zoom = zoom; this.scale = (double) width / (double) height; palette = new DefaultPalette(); } @Override public final void setPalette(final PaletteInterface palette) { this.palette = palette; } @Override public abstract Complex formula(final Complex z, final Complex c, final int order, final Complex center); @Override public final Color calculatePoint(final Complex z, final Complex c, final int order, final Complex center, final int iterations) { Complex zTemp = z; int iter = iterations; while (zTemp.abs() <= 2.0 && iter > 0) { zTemp = formula(zTemp, c, order, center); iter--; } if (iter == 0) { return Color.rgb(0, 0, 0); } else { return palette.pickColor((double) (iterations - iter) / (double) iterations); } } @Override public final WritableImage call() { Complex z; Complex c; Complex center = new Complex(xCenter, yCenter); final WritableImage image = new WritableImage(width, height); if (julia) { c = new Complex(xReal, xIm); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { z = new Complex(((double) x) / (double) (width - 1) * 2.0 * scale * (1.0 / zoom) - scale * (1.0 / zoom), ((double) y) / (double) (height - 1) * 2.0 * (1.0 / zoom) - 1.0 * (1.0 / zoom)); image.getPixelWriter().setColor(x, y, calculatePoint(z, c, order, center, iterations)); } } } else { z = new Complex(xReal, xIm); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { c = new Complex(((double) x) / (double) (width - 1) * 2.0 * scale * (1.0 / zoom) - scale * (1.0 / zoom), ((double) y) / (double) (height - 1) * 2.0 * (1.0 / zoom) - 1.0 * (1.0 / zoom)); image.getPixelWriter().setColor(x, y, calculatePoint(z, c, order, center, iterations)); } updateProgress(y, height); } } return image; } }