diff --git a/README.md b/README.md index 668b3ee..b8d4965 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # MandelbrotEngine -A description of this package. +A Mandelbrot set generator. diff --git a/Sources/MandelbrotEngine/ColourMaps/ColourMapFactory.swift b/Sources/MandelbrotEngine/ColourMaps/ColourMapFactory.swift new file mode 100644 index 0000000..e65b0f8 --- /dev/null +++ b/Sources/MandelbrotEngine/ColourMaps/ColourMapFactory.swift @@ -0,0 +1,26 @@ +// +// ColourMapFactory.swift +// MandelbrotApp +// +// Created by gary on 13/09/2018. +// Copyright © 2018 Gary Kerr. All rights reserved. +// + +struct ColourMapFactory { + static var maps: [ColourMapProtocol] { + return [ + GreyScale(numberOfGreys: 200), + YellowScale(numberOfYellows: 100), + SmoothScale(), + ManyColourGradient( + n: 100, + colours: (r: 255, g: 0, b: 0), (r: 255, g: 255, b: 0) + ), + ManyColourGradient( + n: 70, + colours: (r: 255, g: 0, b: 0), (r: 255, g: 255, b: 0), (r: 255, g: 255, b: 255) + ), + SmoothTest() + ] + } +} diff --git a/Sources/MandelbrotEngine/ColourMaps/ColourMapProtocol.swift b/Sources/MandelbrotEngine/ColourMaps/ColourMapProtocol.swift new file mode 100644 index 0000000..5296f27 --- /dev/null +++ b/Sources/MandelbrotEngine/ColourMaps/ColourMapProtocol.swift @@ -0,0 +1,56 @@ +// +// ColourMapProtocol.swift +// Mandelbrot +// +// Created by gary on 29/04/2017. +// Copyright © 2017 Gary Kerr. All rights reserved. +// + +import CoreGraphics + + +protocol ColourMapProtocol { + typealias RGB = (r: UInt8, g: UInt8, b: UInt8) + + var id: String { get } + var title: String { get } + var blackPixel: Pixel { get } + var pixels: [Pixel] { get } + func pixel(from test: MandelbrotSetPoint.Test) -> Pixel +} + + +extension ColourMapProtocol { + var id: String { title } + + + func pixel(from test: MandelbrotSetPoint.Test) -> Pixel { + switch test { + case .inSet: + return blackPixel + case .notInSet(let iterations, _): + return pixels[iterations % pixels.count] + } + } + + + static func gradient(from: RGB, to: RGB, n: Int) -> [Pixel] { + let dr = diff(m: to.r, n: from.r)/Double(n) + let dg = diff(m: to.g, n: from.g)/Double(n) + let db = diff(m: to.b, n: from.b)/Double(n) + var (r, g, b) = (Double(from.r), Double(from.g), Double(from.b)) + var pixels: [Pixel] = [] + for _ in 0 ..< n { + pixels.append(Pixel(r: UInt8(r), g: UInt8(g), b: UInt8(b))) + r += dr + g += dg + b += db + } + return pixels + } + + + static func diff(m: UInt8, n: UInt8) -> Double { + return Double(m) - Double(n) + } +} diff --git a/Sources/MandelbrotEngine/ColourMaps/GreyScale.swift b/Sources/MandelbrotEngine/ColourMaps/GreyScale.swift new file mode 100644 index 0000000..ad4fbc0 --- /dev/null +++ b/Sources/MandelbrotEngine/ColourMaps/GreyScale.swift @@ -0,0 +1,29 @@ +// +// GreyScale.swift +// Mandelbrot +// +// Created by gary on 29/04/2017. +// Copyright © 2017 Gary Kerr. All rights reserved. +// + +import Foundation + +struct GreyScale: ColourMapProtocol { + + internal let title = "Grey scale" + internal let pixels: [Pixel] + internal let blackPixel = Pixel(r: 0, g: 0, b: 0) + private let pixelMin = 20 + private let pixelMax = 255 + + + init(numberOfGreys: Int) { + var pixelList: [Pixel] = [] + let step = max(Int(Double(pixelMax - pixelMin)/Double(numberOfGreys)), 1) + for i in stride(from: pixelMin, to: pixelMax, by: step) { + let j = UInt8(i) + pixelList.append(Pixel(r: j, g: j, b: j)) + } + pixels = pixelList + pixelList.reversed() + } +} diff --git a/Sources/MandelbrotEngine/ColourMaps/ManyColourGradient.swift b/Sources/MandelbrotEngine/ColourMaps/ManyColourGradient.swift new file mode 100644 index 0000000..f2b0f8f --- /dev/null +++ b/Sources/MandelbrotEngine/ColourMaps/ManyColourGradient.swift @@ -0,0 +1,31 @@ +// +// ManyColourGradient.swift +// Mandelbrot +// +// Created by gary on 06/05/2017. +// Copyright © 2017 Gary Kerr. All rights reserved. +// + +import Foundation + +struct ManyColourGradient: ColourMapProtocol { + + var title: String { + return "Many colour gradient: \(colourCount)" + } + + internal let pixels: [Pixel] + internal let blackPixel = Pixel(r: 0, g: 0, b: 0) + + private let colourCount: Int + + + init(n: Int, colours: RGB...) { + colourCount = colours.count + var secondColours = Array(colours[1 ... colours.count - 1]) + secondColours.append(colours.first!) + pixels = zip(colours, secondColours) + .map({ ManyColourGradient.gradient(from: $0, to: $1, n: n) }) + .reduce([], { x, y in x + y }) + } +} diff --git a/Sources/MandelbrotEngine/ColourMaps/SmoothScale.swift b/Sources/MandelbrotEngine/ColourMaps/SmoothScale.swift new file mode 100644 index 0000000..584a816 --- /dev/null +++ b/Sources/MandelbrotEngine/ColourMaps/SmoothScale.swift @@ -0,0 +1,77 @@ +// +// SmoothScale.swift +// Mandelbrot +// +// Created by gary on 02/05/2017. +// Copyright © 2017 Gary Kerr. All rights reserved. +// +// http://stackoverflow.com/questions/369438/smooth-spectrum-for-mandelbrot-set-rendering + +import Foundation + +struct SmoothScale: ColourMapProtocol { + + static let (h1, s1, v1): (CGFloat, CGFloat, CGFloat) = (0.0, 1.0, 1.0) + static let (h2, s2, v2): (CGFloat, CGFloat, CGFloat) = (360.0, 1.0, 1.0) + + let nColours = 500 + + internal let title = "Smooth scale" + internal let blackPixel = Pixel(r: 0, g: 0, b: 0) + let pixels: [Pixel] + + + init() { + pixels = SmoothScale.makePixels(nColours: nColours) + } +} + + +// MARK: - Private + +private extension SmoothScale { + private static func makePixels(nColours: Int) -> [Pixel] { + var pixels: [Pixel] = [] + for i in stride(from: 0.0, through: 1.0, by: 1.0/Double(nColours)) { + let h = (h2 - h1) * CGFloat(i) + h1 + let s = (s2 - s1) * CGFloat(i) + s1 + let v = (v2 - v1) * CGFloat(i) + v1 + let (r, g, b) = hsv_to_rgb(h: h, s: s, v: v) + + +// let colour = UIColor(hue: h, saturation: s, brightness: v, alpha: 1.0) +// let (r, g, b, _) = colour.rgbaInt + + pixels.append(Pixel(r: r, g: g, b: b)) + } + return pixels + } + + + private static func hsv_to_rgb(h: CGFloat, s: CGFloat, v: CGFloat) -> (r: UInt8, g: UInt8, b: UInt8) { + let c = s * v + let hp = h / 60 + let x = c * (1 - abs(hp.truncatingRemainder(dividingBy: 2) - 1)) + let r1: CGFloat + let g1: CGFloat + let b1: CGFloat + switch hp { + case 0...1: + (r1, g1, b1) = (c, x, 0) + case 1...2: + (r1, g1, b1) = (x, c, 0) + case 2...3: + (r1, g1, b1) = (0, c, x) + case 3...4: + (r1, g1, b1) = (0, x, c) + case 4...5: + (r1, g1, b1) = (x, 0, c) + case 5...6: + (r1, g1, b1) = (c, 0, x) + default: + (r1, g1, b1) = (0, 0, 0) + } + let m = v - c + return (UInt8(256 * (r1 + m)), UInt8(256 * (g1 + m)), UInt8(256 * (b1 + m))) + } +} diff --git a/Sources/MandelbrotEngine/ColourMaps/SmoothTest.swift b/Sources/MandelbrotEngine/ColourMaps/SmoothTest.swift new file mode 100644 index 0000000..4b40b45 --- /dev/null +++ b/Sources/MandelbrotEngine/ColourMaps/SmoothTest.swift @@ -0,0 +1,83 @@ +// +// SmoothTest.swift +// MandelbrotApp +// +// Created by gary on 16/09/2018. +// Copyright © 2018 Gary Kerr. All rights reserved. +// + +import Foundation + +struct SmoothTest: ColourMapProtocol { + private let log2Value: Double = log(2) + private let log4Value: Double = log(4) + + internal let title = "Smooth Test" + internal let blackPixel = Pixel(r: 0, g: 0, b: 0) + let pixels: [Pixel] = [] + + + func pixel(from test: MandelbrotSetPoint.Test) -> Pixel { + switch test { + case .inSet: + return blackPixel + case .notInSet(let N, let zN): + return makePixel(N: N, zN: zN) +// return makePixel2(N: N, zN: zN) + } + } +} + + +private extension SmoothTest { + func makePixel(N: Int, zN: ComplexNumber) -> Pixel { + let modulus = sqrt(zN.modulus()) + let mu = Double(N + 1) - log(log(modulus))/log2Value +// var hue = CGFloat(0.95 + 20.0 * mu) // adjust to make it prettier + var hue = CGFloat(mu) + while hue > 1 { + hue -= 1 + } + while hue < 0.0 { + hue += 1 + } + let (r, g, b) = hsv_to_rgb(h: hue, s: 0.8, v: 1) + return Pixel(r: r, g: g, b: b) + } + + + func makePixel2(N: Int, zN: ComplexNumber) -> Pixel { + let log_zn = log(zN.modulus()) + let mu = Double(N + 1) - log(log_zn/log2Value)/log2Value + print(mu) + return blackPixel + } + + + func hsv_to_rgb(h: CGFloat, s: CGFloat, v: CGFloat) -> (r: UInt8, g: UInt8, b: UInt8) { + let c = s * v + let hp = h / 60 + let x = c * (1 - abs(hp.truncatingRemainder(dividingBy: 2) - 1)) + let r1: CGFloat + let g1: CGFloat + let b1: CGFloat + switch hp { + case 0...1: + (r1, g1, b1) = (c, x, 0) + case 1...2: + (r1, g1, b1) = (x, c, 0) + case 2...3: + (r1, g1, b1) = (0, c, x) + case 3...4: + (r1, g1, b1) = (0, x, c) + case 4...5: + (r1, g1, b1) = (x, 0, c) + case 5...6: + (r1, g1, b1) = (c, 0, x) + default: + (r1, g1, b1) = (0, 0, 0) + } + let m = v - c + return (UInt8(256 * (r1 + m)), UInt8(256 * (g1 + m)), UInt8(256 * (b1 + m))) + } +} diff --git a/Sources/MandelbrotEngine/ColourMaps/YellowScale.swift b/Sources/MandelbrotEngine/ColourMaps/YellowScale.swift new file mode 100644 index 0000000..a22eef4 --- /dev/null +++ b/Sources/MandelbrotEngine/ColourMaps/YellowScale.swift @@ -0,0 +1,29 @@ +// +// YellowScale.swift +// Mandelbrot +// +// Created by gary on 29/04/2017. +// Copyright © 2017 Gary Kerr. All rights reserved. +// + +import Foundation + +struct YellowScale: ColourMapProtocol { + + internal let title = "Yellow scale" + internal let pixels: [Pixel] + internal let blackPixel = Pixel(r: 0, g: 0, b: 0) + private let pixelMin = 20 + private let pixelMax = 255 + + + init(numberOfYellows: Int) { + var pixelList: [Pixel] = [] + let step = max(Int(Double(pixelMax - pixelMin)/Double(numberOfYellows)), 1) + for i in stride(from: pixelMin, to: pixelMax, by: step) { + let j = UInt8(i) + pixelList.append(Pixel(r: j, g: j, b: 0)) + } + pixels = pixelList + pixelList.reversed() + } +} diff --git a/Sources/MandelbrotEngine/ComplexNumber.swift b/Sources/MandelbrotEngine/ComplexNumber.swift new file mode 100644 index 0000000..9812abd --- /dev/null +++ b/Sources/MandelbrotEngine/ComplexNumber.swift @@ -0,0 +1,34 @@ +// +// ComplexNumber.swift +// Mandelbrot +// +// Created by gary on 29/04/2017. +// Copyright © 2017 Gary Kerr. All rights reserved. +// + +import Foundation + +struct ComplexNumber { + let x: Double + let y: Double + + + func modulus() -> Double { + return x*x + y*y + } + + + static func + (lhs: ComplexNumber, rhs: ComplexNumber) -> ComplexNumber { + return ComplexNumber(x: lhs.x + rhs.x, y: lhs.y + rhs.y) + } + + + static func - (lhs: ComplexNumber, rhs: ComplexNumber) -> ComplexNumber { + return ComplexNumber(x: lhs.x - rhs.x, y: lhs.y - rhs.y) + } + + + static func * (lhs: ComplexNumber, rhs: ComplexNumber) -> ComplexNumber { + return ComplexNumber(x: lhs.x * rhs.x - lhs.y * rhs.y, y: lhs.x * rhs.y + lhs.y * rhs.x) + } +} diff --git a/Sources/MandelbrotEngine/MandelbrotSet.swift b/Sources/MandelbrotEngine/MandelbrotSet.swift new file mode 100644 index 0000000..270e50c --- /dev/null +++ b/Sources/MandelbrotEngine/MandelbrotSet.swift @@ -0,0 +1,254 @@ +// +// MandelbrotSet.swift +// Mandelbrot +// +// Created by gary on 29/04/2017. +// Copyright © 2017 Gary Kerr. All rights reserved. +// + +import Foundation + +/// Mandelbrot set calculation +/// +/// Links for improvements +/// ---------------------- +/// +/// https://en.wikibooks.org/wiki/Fractals/Iterations_in_the_complex_plane/Mandelbrot_set#Real_Escape_Time +/// +/// http://www.mrob.com/pub/muency/speedimprovements.html +/// +/// https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set +/// +struct MandelbrotSet { + + let config: MandelbrotSetConfig + + var grid: [MandelbrotSetPoint] = [] + var imageSize: (width: Int, height: Int) + + + init(config: MandelbrotSetConfig, progress: Progress) { + self.config = config + let ys = Array(stride(from: config.yMin, to: config.yMax, by: config.dy)) + let xs = Array(stride(from: config.xMin, to: config.xMax, by: config.dx)) + imageSize = (xs.count, ys.count) + grid.reserveCapacity(xs.count * ys.count) + + let progressHelper = ProgressHelper(steps: imageSize.height, progress: progress) + let timerHelper = TimerHelper() + + for (i, y) in ys.enumerated() { + for x in xs { + let z = ComplexNumber(x: x, y: y) + + if inCardiod(x: x, y: y) { + let result = MandelbrotSetPoint(point: z, test: .inSet) + grid.append(result) + continue + } + +// let result = MandelbrotSetPoint(point: z, test: isInSetFast(point: z)) +// let result = MandelbrotSetPoint(point: z, test: isInSetFast1(point: z)) + let result = MandelbrotSetPoint(point: z, test: isInSetFast1a(x0: x, y0: y)) + grid.append(result) + } + progressHelper.update(step: i) + } + timerHelper.end() + } + + + func gridIterations(config: MandelbrotSetConfig) -> Int { + var total = 0 + for point in grid { + switch point.test { + case .inSet: + total += config.iterations + case .notInSet(let iterations, _): + total += iterations + } + } + return total + } +} + + +// MARK: - Is in set tests + +private extension MandelbrotSet { + func isInSet(point: ComplexNumber) -> MandelbrotSetPoint.Test { + var z = point + for i in 0 ..< config.iterations { + if z.modulus() >= 4 { + return .notInSet(iterations: i, finalPoint: z) + } + z = z*z + point + } + return .inSet + } + + + // Maybe this could be faster because not using operator overloading on the ComplexNumber struct + func isInSetFast(point: ComplexNumber) -> MandelbrotSetPoint.Test { + let (u, v) = (point.x, point.y) + var (x, y) = (point.x, point.y) + for i in 0 ..< config.iterations { + if x*x + y*y >= 4 { + return .notInSet(iterations: i, finalPoint: ComplexNumber(x: x, y: y)) + } + (x, y) = (x*x - y*y + u, 2*x*y + v) + } + return .inSet + } + + + + /// Calculates `MandelbrotSetPoint.Test` for a complex number + /// + /// https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set#Optimized_escape_time_algorithms + /// + /// - Parameter point: The complex number + /// - Returns: The result + func isInSetFast1(point: ComplexNumber) -> MandelbrotSetPoint.Test { + let x0 = point.x + let y0 = point.y + var x = 0.0 + var y = 0.0 + var x2 = 0.0 + var y2 = 0.0 + var w = 0.0 + for i in 0.. 4 { + let z_i = ComplexNumber(x: x, y: y) + return .notInSet(iterations: i, finalPoint: z_i) + } + x = x2 - y2 + x0 + y = w - x2 - y2 + y0 + x2 = x * x + y2 = y * y + w = (x + y) * (x + y) + } + return .inSet + } + + + /// Calculates `MandelbrotSetPoint.Test` for a complex number + /// + /// https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set#Optimized_escape_time_algorithms + /// + /// - Parameter point: The complex number + /// - Returns: The result + func isInSetFast1a(x0: Double, y0: Double) -> MandelbrotSetPoint.Test { + var x = 0.0 + var y = 0.0 + var x2 = 0.0 + var y2 = 0.0 + for i in 0.. 4 { + let z_i = ComplexNumber(x: x, y: y) + return .notInSet(iterations: i, finalPoint: z_i) + } + y = 2 * x * y + y0 + x = x2 - y2 + x0 + x2 = x * x + y2 = y * y + } + return .inSet + } +} + + +// MARK: - Cardiod checks + +private extension MandelbrotSet { + /// Is a point in the main Mandelbrot bulbs? + /// + /// Main cardioid + /// https://www.reenigne.org/blog/algorithm-for-mandelbrot-cardioid/ + /// + /// |c|^2 (8|c|^2 - 3) + Re(c) <= 3/32 + /// + /// First bulb: + /// + /// (x + 1)^2 + y^2 <= 1/16 + /// + func inCardiod(x: Double, y: Double) -> Bool { + // main cardioid check + let x2 = x * x + let y2 = y * y + let xy2 = x2 + y2 + if xy2 * (8 * xy2 - 3) + x <= 0.09375 { + return true + } + + // bulb at c = -1, with radius 1/4 +// let x1_2 = (x + 1) * (x + 1) +// return x1_2 + y2 < 0.015625 + return xy2 + 2*x + 1 <= 0.015625 + } +} + + +// MARK: - Private: average pixel colour + +private extension MandelbrotSet { + func resample(pixels: [Pixel]) -> [Pixel] { + var newPixels: [Pixel] = [] + let width = config.imageWidth + let maxValue = config.imageWidth * config.imageHeight + for (i, pixel) in pixels.enumerated() { + if pixel.isBlack { + newPixels.append(pixel) + continue + } + var neighbours = getNeighbours(index: i, pixels: pixels, width: width, maxValue: maxValue) + neighbours.append(pixel) + let averagePixel = getAverage(pixels: neighbours) + newPixels.append(averagePixel) + } + return newPixels + } + + + func getNeighbours(index: Int, pixels: [Pixel], width: Int, maxValue: Int) -> [Pixel] { + var neighbours: [Pixel] = [] + // Left + let left = index - 1 + if index % width != 0 { + neighbours.append(pixels[left]) + } + // Right + let right = index + 1 + if right % width != 0 && right < maxValue { + neighbours.append(pixels[right]) + } + // Top + let top = index - width + if top >= 0 { + neighbours.append(pixels[top]) + } + // Bottom + let bottom = index + width + if bottom < maxValue { + neighbours.append(pixels[bottom]) + } + return neighbours + } + + + func getAverage(pixels: [Pixel]) -> Pixel { + var r = 0 + var g = 0 + var b = 0 + for pixel in pixels { + r += Int(pixel.r) + g += Int(pixel.g) + b += Int(pixel.b) + } + let count = Double(pixels.count) + let averageR = UInt8(Double(r)/count) + let averageG = UInt8(Double(g)/count) + let averageB = UInt8(Double(b)/count) + return Pixel(r: averageR, g: averageG, b: averageB) + } +} diff --git a/Sources/MandelbrotEngine/MandelbrotSetConfig.swift b/Sources/MandelbrotEngine/MandelbrotSetConfig.swift new file mode 100644 index 0000000..525fa6e --- /dev/null +++ b/Sources/MandelbrotEngine/MandelbrotSetConfig.swift @@ -0,0 +1,87 @@ +// +// MandelbrotSetConfig.swift +// Mandelbrot +// +// Created by gary on 03/05/2017. +// Copyright © 2017 Gary Kerr. All rights reserved. +// + +struct MandelbrotSetConfig: CustomStringConvertible { + let imageWidth: Int + let imageHeight: Int + let width: Double + let height: Double + let centre: ComplexNumber + let iterations: Int + + + init(imageWidth: Int, imageHeight: Int, width: Double, height: Double, centre: ComplexNumber, iterations: Int) { + self.imageWidth = imageWidth + self.imageHeight = imageHeight + self.width = width + self.height = height + self.centre = centre + self.iterations = iterations + } + + + init(imageWidth: Int, imageHeight: Int) { + let centre = ComplexNumber(x: -0.5, y: 0) + let height: Double = 4 + let width = height * Double(imageWidth) / Double(imageHeight) + self.init( + imageWidth: imageWidth, + imageHeight: imageHeight, + width: width, + height: height, + centre: centre, + iterations: 300 + ) + } + + var xMin: Double { + return centre.x - width/2 + } + + var xMax: Double { + return centre.x + width/2 + } + + var yMin: Double { + return centre.y - height/2 + } + + var yMax: Double { + return centre.y + height/2 + } + + var dx: Double { + return width/Double(imageWidth) + } + + var dy: Double { + return height/Double(imageHeight) + } + + var description: String { + return "Config(imageWidth: \(imageWidth), imageHeight: \(imageHeight), width: \(width), height: \(height), centre: \(centre), iterations: \(iterations), xMin: \(xMin), xMax: \(xMax), yMin: \(yMin), yMax: \(yMax), dx: \(dx), dy: \(dy))" + } + + func description(set: MandelbrotSet) -> String { + let totalIterations = set.gridIterations(config: self) + let averageIterations = Double(totalIterations) / Double(imageWidth * imageHeight) + return "dx:\n\(xMax - xMin)\n\ndy:\n\(yMax - yMin)\n\nmax iterations:\n\(iterations)\n\ntotal iterations:\n\(totalIterations)\n\nav. per pixel:\n\(averageIterations)" + } + + + func zoomIn(centre: ComplexNumber) -> MandelbrotSetConfig { + return MandelbrotSetConfig( + imageWidth: imageWidth, + imageHeight: imageHeight, + width: width/2, + height: height/2, + centre: centre, + iterations: Int(1.2*Double(iterations)) + ) + } +} diff --git a/Sources/MandelbrotEngine/MandelbrotSetPoint.swift b/Sources/MandelbrotEngine/MandelbrotSetPoint.swift new file mode 100644 index 0000000..fcaf984 --- /dev/null +++ b/Sources/MandelbrotEngine/MandelbrotSetPoint.swift @@ -0,0 +1,21 @@ +// +// MandelbrotSetPoint.swift +// Mandelbrot +// +// Created by gary on 29/04/2017. +// Copyright © 2017 Gary Kerr. All rights reserved. +// + +import Foundation + + +struct MandelbrotSetPoint { + + enum Test { + case inSet + case notInSet(iterations: Int, finalPoint: ComplexNumber) + } + + let point: ComplexNumber + var test: Test +} diff --git a/Sources/MandelbrotEngine/Pixel.swift b/Sources/MandelbrotEngine/Pixel.swift new file mode 100644 index 0000000..6a788d5 --- /dev/null +++ b/Sources/MandelbrotEngine/Pixel.swift @@ -0,0 +1,26 @@ +// +// Pixel.swift +// Mandelbrot +// +// Created by gary on 03/05/2017. +// Copyright © 2017 Gary Kerr. All rights reserved. +// + +struct Pixel { + var a: UInt8 = 255 + var r: UInt8 + var g: UInt8 + var b: UInt8 + + + init(r: UInt8, g: UInt8, b: UInt8) { + self.r = r + self.g = g + self.b = b + } + + + var isBlack: Bool { + return r == 0 && g == 0 && b == 0 + } +} diff --git a/Sources/MandelbrotEngine/ProgressHelper.swift b/Sources/MandelbrotEngine/ProgressHelper.swift new file mode 100644 index 0000000..7cc9cc6 --- /dev/null +++ b/Sources/MandelbrotEngine/ProgressHelper.swift @@ -0,0 +1,28 @@ +// +// ProgressHelper.swift +// MandelbrotApp +// +// Created by gary on 13/06/2021. +// Copyright © 2021 Gary Kerr. All rights reserved. +// + +import Foundation + +struct ProgressHelper { + let countFraction: Double + let progress: Progress + + + init(steps: Int, progress: Progress) { + self.countFraction = Double(100) / Double(steps) + self.progress = progress + } + + + func update(step: Int) { + let completedUnitCount = Int64(countFraction * Double(step)) + DispatchQueue.main.async { + progress.completedUnitCount = completedUnitCount + } + } +} diff --git a/Sources/MandelbrotEngine/Rectangle.swift b/Sources/MandelbrotEngine/Rectangle.swift new file mode 100644 index 0000000..d65590f --- /dev/null +++ b/Sources/MandelbrotEngine/Rectangle.swift @@ -0,0 +1,38 @@ +// +// Rectangle.swift +// Mandelbrot +// +// Created by gary on 29/04/2017. +// Copyright © 2017 Gary Kerr. All rights reserved. +// + +struct Rectangle { + let xMin: Double + let yMin: Double + let xMax: Double + let yMax: Double + + + init(xMin: Double, yMin: Double, xMax: Double, yMax: Double) { + self.xMin = xMin + self.yMin = yMin + self.xMax = xMax + self.yMax = yMax + } + + + init(xMin: Int, yMin: Int, xMax: Int, yMax: Int) { + self.xMin = Double(xMin) + self.yMin = Double(yMin) + self.xMax = Double(xMax) + self.yMax = Double(yMax) + } + + + init(config: MandelbrotSetConfig) { + self.xMin = config.xMin + self.xMax = config.xMax + self.yMin = config.yMin + self.yMax = config.yMax + } +} diff --git a/Sources/MandelbrotEngine/TimerHelper.swift b/Sources/MandelbrotEngine/TimerHelper.swift new file mode 100644 index 0000000..63b639b --- /dev/null +++ b/Sources/MandelbrotEngine/TimerHelper.swift @@ -0,0 +1,26 @@ +// +// TimerHelper.swift +// MandelbrotApp +// +// Created by gary on 13/06/2021. +// Copyright © 2021 Gary Kerr. All rights reserved. +// + + +import Foundation + +struct TimerHelper { + private let nano = 1_000_000_000.0 + let start: DispatchTime + + init() { + self.start = DispatchTime.now() + } + + + func end() { + let e = DispatchTime.now() + let dt = e.uptimeNanoseconds - start.uptimeNanoseconds + print(Double(dt) / nano) + } +}