private static void RunChaosGame(Variables v, FlameFunction[] Functions, double[] SumWeights, FlameState P, bool Preview, double Gamma, double LightFactor, ScriptNode Node, bool ShowStatus) { Random Gen = new Random(); FlameFunction f; double Weight; int i, j; int c = Functions.Length; for (i = 0; i < 20; i++) { Weight = Gen.NextDouble(); j = 0; while (j < c - 1 && SumWeights[j] <= Weight) { j++; } f = Functions[j]; if (!f.Operate(P, v)) { P.N = 0; break; } } if (Preview || Node.Expression.HandlesStatus) { System.DateTime Start = System.DateTime.Now; System.DateTime NextPreview = Start.AddSeconds(1); System.DateTime PrevPreview = Start; System.DateTime Temp; System.DateTime Temp2; TimeSpan TimeLeft; int NextPreviewDeciSeconds = 10; int NrIterationsPerPreview = 4096; int Pos = NrIterationsPerPreview; long NrIterations; long PrevNrIterations = 0; long NrIterationsSinceLast; double PercentDone; double IterationsPerSeconds; do { if (Pos-- <= 0) { Pos = NrIterationsPerPreview; Temp = System.DateTime.Now; if (Temp > NextPreview) { NextPreview = Temp.AddSeconds(NextPreviewDeciSeconds * 0.1); if (NextPreviewDeciSeconds < 50) { NextPreviewDeciSeconds++; } if (Preview) { Node.Expression.Preview(new GraphBitmap(P.RenderBitmapHsl(Gamma, LightFactor, true, SKColors.Black))); Temp2 = System.DateTime.Now; double d = (Temp2 - Temp).TotalSeconds; double d2 = (Temp - PrevPreview).TotalSeconds; if (d / d2 > 0.1) { NrIterationsPerPreview <<= 1; if (NrIterationsPerPreview < 0) { NrIterationsPerPreview = int.MaxValue; } } } NrIterations = P.N0 - P.N; NrIterationsSinceLast = NrIterations - PrevNrIterations; IterationsPerSeconds = NrIterationsSinceLast / (Temp - PrevPreview).TotalSeconds; PercentDone = (100 * (1.0 - ((double)P.N) / P.N0)); TimeLeft = new TimeSpan((long)((Temp - Start).Ticks * 100 / PercentDone)); Node.Expression.Status(P.N.ToString() + " iterations left, " + NrIterations.ToString() + " iterations done, " + IterationsPerSeconds.ToString("F0") + " iterations/s, " + PercentDone.ToString("F1") + "% done, Time Left: " + TimeLeft.ToString() + "."); PrevNrIterations = NrIterations; PrevPreview = Temp; } } Weight = Gen.NextDouble(); j = 0; while (j < c - 1 && SumWeights[j] <= Weight) { j++; } f = Functions[j]; if (!f.Operate(P, v)) { break; } }while (P.IncHistogram()); Node.Expression.Status(string.Empty); } else { do { Weight = Gen.NextDouble(); j = 0; while (j < c - 1 && SumWeights[j] <= Weight) { j++; } f = Functions[j]; if (!f.Operate(P, v)) { break; } }while (P.IncHistogram()); } }
public static FractalGraph CalcFlame(double xCenter, double yCenter, double rDelta, long N, FlameFunction[] Functions, int Width, int Height, int Seed, int SuperSampling, double Gamma, double LightFactor, bool Preview, bool Parallel, Variables Variables, ScriptNode Node, FractalZoomScript FractalZoomScript, object State) { double TotWeight = 0; double Weight; int i, c = Functions.Length; Random Gen = new Random(Seed); if (c < 1) { throw new ScriptRuntimeException("At least one flame function needs to be provided.", Node); } if (SuperSampling < 1) { throw new ScriptRuntimeException("SuperSampling must be a postitive integer.", Node); } if (!Node.Expression.HandlesPreview) { Preview = false; } Array.Sort <FlameFunction>(Functions, (f1, f2) => { double d = f2.Weight - f1.Weight; if (d < 0) { return(-1); } else if (d > 0) { return(1); } else { return(0); } }); double[] SumWeights = new double[c]; FlameFunction f; for (i = 0; i < c; i++) { f = Functions[i]; Weight = f.Weight; if (Weight < 0) { throw new ScriptRuntimeException("Weights must be non-negative.", Node); } f.DefinitionDone(); TotWeight += Weight; SumWeights[i] = TotWeight; } if (TotWeight == 0) { throw new ScriptRuntimeException("The total weight of all functions must be postitive.", Node); } for (i = 0; i < c; i++) { SumWeights[i] /= TotWeight; } double AspectRatio = ((double)Width) / Height; double xMin, xMax, yMin, yMax; xMin = xCenter - rDelta / 2; xMax = xMin + rDelta; yMin = yCenter - rDelta / (2 * AspectRatio); yMax = yMin + rDelta / AspectRatio; int NrGames = Parallel ? System.Environment.ProcessorCount : 1; if (NrGames <= 1) { FlameState P = new FlameState(Gen, xMin, xMax, yMin, yMax, Width, Height, SuperSampling, N, ColorMode.Hsl, Node); Variables v = new Variables(); Variables.CopyTo(v); RunChaosGame(v, Functions, SumWeights, P, Preview, Gamma, LightFactor, Node, true); return(new FractalGraph(P.RenderBitmapHsl(Gamma, LightFactor, false, SKColors.Black), xMin, yMin, xMax, yMax, rDelta, false, Node, FractalZoomScript, State)); } else { FlameState[] P = new FlameState[NrGames]; WaitHandle[] Done = new WaitHandle[NrGames]; Thread[] T = new Thread[NrGames]; try { for (i = 0; i < NrGames; i++) { Done[i] = new ManualResetEvent(false); P[i] = new FlameState(Gen, xMin, xMax, yMin, yMax, Width, Height, SuperSampling, i < NrGames - 1 ? N / NrGames : N - (N / NrGames) * (NrGames - 1), ColorMode.Hsl, Node); Variables v = new Variables(); Variables.CopyTo(v); T[i] = new Thread(new ParameterizedThreadStart(ChaosGameThread)) { Name = "FlameFractal thread #" + (i + 1).ToString(), Priority = ThreadPriority.BelowNormal }; T[i].Start(new object[] { Done[i], i, v, Functions, SumWeights, P[i], Node, i == 0 ? Preview : false, Gamma, LightFactor }); } WaitHandle.WaitAll(Done); for (i = 1; i < NrGames; i++) { P[0].Add(P[i]); } return(new FractalGraph(P[0].RenderBitmapHsl(Gamma, LightFactor, false, SKColors.Black), xMin, yMin, xMax, yMax, rDelta, false, Node, FractalZoomScript, State)); } catch (ThreadAbortException) { for (i = 0; i < NrGames; i++) { try { if (T[i] == null) { continue; } if (Done[i] == null || !Done[i].WaitOne(0)) { T[i].Abort(); } } catch (Exception) { // Ignore } } return(null); } finally { for (i = 0; i < NrGames; i++) { try { if (Done[i] != null) { Done[i].Close(); } } catch (Exception) { // Ignore } } } } }