public int FiniteIterationLimit() { return (_viewPort.Resolution.GetPositionsRowFirst(). AsParallel(). Select(p => ScalarDoubleKernel.FindEscapeTimeNoCycleDetection(_viewPort.GetComplex(p), MaxIterations).IsInfinite). Count()); }
public void ShouldRoundTripComplexNumbers() { var viewPort = new ViewPort( new ComplexArea( new DoubleRange(-1, 1), new DoubleRange(-1, 1)), new Size(101, 101)); var point = new Point(-1, 1); var roundTripped = viewPort.GetPosition(viewPort.GetComplex(point)); Assert.That(roundTripped, Is.EqualTo(point), "Returned wrong position."); }
public void ShouldRoundTripPositions() { var viewPort = new ViewPort( new ComplexArea( new DoubleRange(-1, 1), new DoubleRange(-1, 1)), new Size(101, 101)); var c = new Complex(-1, 1); var roundTripped = viewPort.GetComplex(viewPort.GetPosition(c)); Assert.That(roundTripped, Is.EqualTo(c), "Returned wrong complex number."); }
public static void Compute( string filePath, ViewPort viewPort, ComputationType computationType, CancellationToken token) { Log.Info($"Outputting to: {filePath}"); Log.Info($"Resolution: {viewPort.Resolution.Width:N0}x{viewPort.Resolution.Height:N0}"); Log.Info($"Area: {viewPort.Area}"); Log.Info($"Computation type: {computationType}"); IEnumerable <bool> GetPointsInSetScalar() { var kernel = KernelBuilder.BuildScalarKernel(computationType); var rowPointsInSet = new bool[viewPort.Resolution.Width]; using var progress = TimedOperation.Start("points", totalWork: viewPort.Resolution.Area()); for (int row = 0; row < viewPort.Resolution.Height; row++) { Parallel.For( 0, viewPort.Resolution.Width, col => rowPointsInSet[col] = kernel.FindEscapeTime(viewPort.GetComplex(col, row), Constant.IterationRange.Max).IsInfinite); for (int x = 0; x < viewPort.Resolution.Width; x++) { yield return(rowPointsInSet[x]); } progress.AddWorkDone(viewPort.Resolution.Width); } } IEnumerable <bool> GetPointsInSetVectorDoubles() { using var progress = TimedOperation.Start("points", totalWork: viewPort.Resolution.Area()); var vWidth = VectorDoubleKernel.Capacity; var vectorBatches = viewPort.Resolution.Width / vWidth; var remainder = viewPort.Resolution.Width % vWidth; if (remainder != 0) { vectorBatches++; } var lastIndex = vectorBatches - 1; var rowPointsInSet = new bool[viewPort.Resolution.Width]; // TODO: Why is the Parallel.For inside a loop? for (int row = 0; row < viewPort.Resolution.Height; row++) { Parallel.For( 0, vectorBatches, batchIndex => { var realBatch = new double[vWidth]; var imagBatch = new double[vWidth]; var times = new EscapeTime[vWidth]; var batchSize = (batchIndex == lastIndex) ? remainder : vWidth; for (int i = 0; i < batchSize; i++) { var c = viewPort.GetComplex(batchIndex * vWidth + i, row); realBatch[i] = c.Real; imagBatch[i] = c.Imaginary; } VectorDoubleKernel.FindEscapeTimes( realBatch, imagBatch, Constant.IterationRange.Max, times); for (int i = 0; i < batchSize; i++) { rowPointsInSet[batchIndex * vWidth + i] = times[i].Iterations == Constant.IterationRange.Max; } }); for (int x = 0; x < viewPort.Resolution.Width; x++) { yield return(rowPointsInSet[x]); } progress.AddWorkDone(viewPort.Resolution.Width); } } IEnumerable <bool> ChooseEnumerator() => computationType switch { ComputationType.ScalarDouble => GetPointsInSetScalar(), ComputationType.ScalarFloat => GetPointsInSetScalar(), ComputationType.VectorDouble => GetPointsInSetVectorDoubles(), _ => throw new ArgumentException("Unsupported computation type: " + computationType) }; Write(filePath, viewPort, computationType, ChooseEnumerator()); } }
public static void RenderSingleSpan(string edgeSpansPath, int spanIndex, int sideResolution) { var resolution = new Size(sideResolution, sideResolution); using var spans = EdgeSpanStream.Load(edgeSpansPath); using var timer = TimedOperation.Start("points", totalWork: resolution.Area()); var random = new Random(); var index = spanIndex >= 0 ? spanIndex : random.Next(0, spans.Count); var imageFilePath = Path.Combine( Path.GetDirectoryName(edgeSpansPath) ?? throw new Exception($"Could not get directory name: {edgeSpansPath}"), Path.GetFileNameWithoutExtension(edgeSpansPath) + $"_{index}_{sideResolution}x{sideResolution}.png"); Log.Info($"Using edge span index {index:N0}"); Log.Info($"Output file: {imageFilePath}"); var span = spans.ElementAt(index).ToConcreteDouble(spans.ViewPort); var spanLength = span.Length(); Log.Info($"Edge span: {span} (length: {spanLength})"); var image = new FastImage(resolution); var viewPort = new ViewPort(GetArea(span), resolution); Log.Info($"View port: {viewPort}"); var positionInSet = viewPort.GetPosition(span.InSet); var positionNotInSet = viewPort.GetPosition(span.NotInSet); var highlightPixelRadius = resolution.Width / 100; var borderPoint = span.FindBoundaryPoint(Constant.IterationRange.Max); Log.Info($"Border point: {borderPoint} (escape time: {ScalarDoubleKernel.FindEscapeTime(borderPoint)})"); var borderPointPosition = viewPort.GetPosition(borderPoint); // TODO: Why is the inner loop parallelized? for (int row = 0; row < resolution.Height; row++) { Parallel.For(0, resolution.Width, col => { var position = new Point(col, row); var c = viewPort.GetComplex(position); Color PickColor() { if (position.DistanceSquaredFrom(positionInSet) <= highlightPixelRadius) { return(Color.Red); } if (position.DistanceSquaredFrom(positionNotInSet) <= highlightPixelRadius) { return(Color.ForestGreen); } if (position.DistanceSquaredFrom(borderPointPosition) <= highlightPixelRadius) { return(Color.Fuchsia); } var isInSet = ScalarDoubleKernel.FindEscapeTime(c, Constant.IterationRange.Max).IsInfinite; return(isInSet ? Color.FromArgb(0x20, 0x20, 0x20) : Color.White); } image.SetPixel(position, PickColor()); }); timer.AddWorkDone(resolution.Width); } image.Save(imageFilePath); }