Created
September 22, 2018 16:55
-
-
Save aabewhite/9839999483c5fe079351cde6b1fef824 to your computer and use it in GitHub Desktop.
Sequence that generates points on a plane expanding from the center in a spiral pattern.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SpiralPointSequence.swift | |
// ARPaint | |
// | |
// Created by Abe White on 9/20/18. | |
// Copyright © 2018 Hedonic Software. All rights reserved. | |
// | |
import Foundation | |
/** | |
Generates points in a plane expanding from the center in a spiral pattern. | |
*/ | |
public struct SpiralPointSequence: Sequence, IteratorProtocol { | |
private let _width: Int | |
private let _height: Int | |
private let _directionSteps: [(x: Int, y: Int)] | |
private var _nextPoint: (x: Int, y: Int)? | |
private var _xRange = (min: 0, max: 0) | |
private var _yRange = (min: 0, max: 0) | |
private var _direction = 0 | |
public init(width: Int, height: Int, step: Int = 1) { | |
_width = width | |
_height = height | |
_directionSteps = [(x: 0, y: step), (x: step, y: 0), (x: 0, y: -step), (x: -step, y: 0)] // down, right, up, left | |
if (width == 0 || height == 0) { | |
_nextPoint = nil | |
} else { | |
_nextPoint = (width / 2, height / 2) | |
_xRange = (min: _nextPoint!.x, max: _nextPoint!.x) | |
_yRange = (min: _nextPoint!.y, max: _nextPoint!.y) | |
} | |
} | |
public mutating func next() -> (x: Int, y: Int)? { | |
defer { _updateNextPoint() } | |
return _nextPoint | |
} | |
private mutating func _updateNextPoint() { | |
guard var exploreFromPoint = _nextPoint else { | |
return | |
} | |
for _ in 0..<_directionSteps.count { | |
let directionStep = _directionSteps[_direction] | |
let nextX = Swift.max(0, Swift.min(_width - 1, exploreFromPoint.x + directionStep.x)) | |
let nextY = Swift.max(0, Swift.min(_height - 1, exploreFromPoint.y + directionStep.y)) | |
let nextPoint = (nextX, nextY) | |
if nextPoint == exploreFromPoint { | |
// We've reached an edge we've explored before. Change | |
// direction and move to the previous max/min in the new | |
// direction and try to continue from there | |
_changeDirection() | |
exploreFromPoint = _maximumPointInDirection(from: exploreFromPoint) | |
} else { | |
if _updateRanges(for: nextPoint) { | |
// When we reach a new max/min in any direction, change | |
// direction for the next step | |
_changeDirection() | |
} | |
_nextPoint = nextPoint | |
return | |
} | |
} | |
_nextPoint = nil | |
} | |
private func _maximumPointInDirection(from point: (x: Int, y: Int)) -> (x: Int, y: Int) { | |
let directionStep = _directionSteps[_direction] | |
if directionStep.x > 0 { | |
return (_xRange.max, point.y) | |
} else if directionStep.x < 0 { | |
return (_xRange.min, point.y) | |
} else if directionStep.y > 0 { | |
return (point.x, _yRange.max) | |
} else if directionStep.y < 0 { | |
return (point.x, _yRange.min) | |
} | |
return point | |
} | |
private mutating func _changeDirection() { | |
_direction += 1 | |
if _direction >= _directionSteps.count { | |
_direction = 0 | |
} | |
} | |
private mutating func _updateRanges(for nextPoint: (x: Int, y: Int)) -> Bool { | |
if nextPoint.x > _xRange.max { | |
_xRange.max = nextPoint.x | |
return true | |
} else if nextPoint.x < _xRange.min { | |
_xRange.min = nextPoint.x | |
return true | |
} else if nextPoint.y > _yRange.max { | |
_yRange.max = nextPoint.y | |
return true | |
} else if nextPoint.y < _yRange.min { | |
_yRange.min = nextPoint.y | |
return true | |
} else { | |
return false | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment