Last active
September 28, 2019 21:13
-
-
Save BigZaphod/3a88b305457b9bf2656cc098f43503b5 to your computer and use it in GitHub Desktop.
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
typealias GeometricValue = Numeric & Comparable | |
struct GenericPoint<ValueType: GeometricValue>: Equatable { | |
var x: ValueType | |
var y: ValueType | |
static var zero: Self { Self(x: 0, y: 0) } | |
} | |
extension GenericPoint { | |
init(_ x: ValueType, _ y: ValueType) { | |
self.init(x: x, y: y) | |
} | |
static func +(lhs: Self, rhs: Self) -> Self { | |
Self(x: lhs.x + rhs.x, y: lhs.y + rhs.y) | |
} | |
static func +=(lhs: inout Self, rhs: Self) { | |
lhs = lhs + rhs | |
} | |
static func -(lhs: Self, rhs: Self) -> Self { | |
lhs + (rhs * -1) | |
} | |
static func -=(lhs: inout Self, rhs: Self) { | |
lhs = lhs - rhs | |
} | |
static func *(lhs: Self, scale: ValueType) -> Self { | |
Self(x: lhs.x * scale, y: lhs.y * scale) | |
} | |
static func *=(lhs: inout Self, scale: ValueType) { | |
lhs = lhs * scale | |
} | |
static prefix func - (point: Self) -> Self { | |
point * -1 | |
} | |
func offsetBy(dx: ValueType = 0, dy: ValueType = 0) -> Self { | |
self + Self(x: dx, y: dy) | |
} | |
} | |
extension GenericPoint where ValueType: FloatingPoint { | |
static func /(lhs: Self, scale: ValueType) -> Self { | |
lhs * (1 / scale) | |
} | |
static func /=(lhs: inout Self, scale: ValueType) { | |
lhs *= (1 / scale) | |
} | |
} | |
struct GenericSize<ValueType: GeometricValue>: Equatable { | |
var width: ValueType | |
var height: ValueType | |
static var zero: Self { Self(width: 0, height: 0) } | |
} | |
extension GenericSize { | |
init(_ w: ValueType, _ h: ValueType) { | |
self.init(width: w, height: h) | |
} | |
} | |
extension GenericSize: Sequence where ValueType: Strideable { | |
typealias Element = GenericPoint<ValueType> | |
func makeIterator() -> AnyIterator<Element> { | |
return GenericRect(origin: .zero, size: self).makeIterator() | |
} | |
} | |
struct GenericRect<ValueType: GeometricValue>: Equatable { | |
typealias PointType = GenericPoint<ValueType> | |
typealias SizeType = GenericSize<ValueType> | |
var origin: PointType | |
var size: SizeType | |
var x: ValueType { | |
get { origin.x } | |
set { origin.x = newValue } | |
} | |
var y: ValueType { | |
get { origin.y } | |
set { origin.y = newValue } | |
} | |
var width: ValueType { | |
get { size.width } | |
set { size.width = newValue } | |
} | |
var height: ValueType { | |
get { size.height } | |
set { size.height = newValue } | |
} | |
var maxX: ValueType { Swift.max(x, x + width) } | |
var maxY: ValueType { Swift.max(y, y + height) } | |
var minX: ValueType { Swift.min(x, x + width) } | |
var minY: ValueType { Swift.min(y, y + height) } | |
var bottomLeft: PointType { PointType(x: minX, y: maxY) } | |
var bottomRight: PointType { PointType(x: maxX, y: maxY) } | |
var area: ValueType { | |
let copy = standardized() | |
return copy.width * copy.height | |
} | |
func contains(_ point: PointType) -> Bool { | |
let test = standardized() | |
return point.x >= test.x && point.y >= test.y && point.x < test.x + test.width && point.y < test.y + test.height | |
} | |
mutating func standardize() { | |
if width < 0 { | |
x += width | |
width *= -1 | |
} | |
if height < 0 { | |
y += height | |
height *= -1 | |
} | |
} | |
func standardized() -> Self { | |
var copy = self | |
copy.standardize() | |
return copy | |
} | |
func translatedBy(_ v: PointType) -> Self { | |
Self(origin: PointType(x: x + v.x, y: y + v.y), size: SizeType(width: width, height: height)) | |
} | |
func intersection(_ r2: Self) -> Self? { | |
if minX < r2.maxX, maxX > r2.minX, minY < r2.maxY, maxY > r2.minY { | |
return Self(minX: Swift.max(minX, r2.minX), | |
minY: Swift.max(minY, r2.minY), | |
maxX: Swift.min(maxX, r2.maxX), | |
maxY: Swift.min(maxY, r2.maxY)) | |
} else { | |
return nil | |
} | |
} | |
func inset(by insets: GenericInsets<ValueType>) -> Self { | |
Self(minX: minX + insets.left, | |
minY: minY + insets.top, | |
maxX: maxX - insets.bottom, | |
maxY: maxY - insets.right) | |
} | |
static var zero: Self { Self(origin: .zero, size: .zero) } | |
} | |
extension GenericRect { | |
init(x: ValueType, y: ValueType, width: ValueType, height: ValueType) { | |
self.init(origin: PointType(x: x, y: y), | |
size: SizeType(width: width, height: height)) | |
} | |
init(_ x: ValueType, _ y: ValueType, _ w: ValueType, _ h: ValueType) { | |
self.init(x: x, y: y, width: w, height: h) | |
} | |
init(minX: ValueType, minY: ValueType, maxX: ValueType, maxY: ValueType) { | |
self.init(minX, minY, maxX - minX, maxY - minY) | |
} | |
init(origin: PointType, bottomRight: PointType) { | |
self.init(minX: origin.x, minY: origin.y, maxX: bottomRight.x, maxY: bottomRight.y) | |
} | |
} | |
protocol Divisible { | |
static func / (lhs: Self, rhs: Self) -> Self | |
} | |
extension Float: Divisible {} | |
extension Int: Divisible {} | |
extension GenericRect where ValueType: Divisible { | |
var center: PointType { | |
get { PointType(x: midX, y: midY) } | |
set { midX = newValue.x; midY = newValue.y } | |
} | |
var midX: ValueType { | |
get { x + (width / 2) } | |
set { x = newValue - width / 2 } | |
} | |
var midY: ValueType { | |
get { y + (height / 2) } | |
set { y = newValue - height / 2 } | |
} | |
var bottomCenter: PointType { PointType(x: midX, y: maxY) } | |
} | |
extension GenericRect: Sequence where ValueType: Strideable { | |
typealias Element = PointType | |
func makeIterator() -> AnyIterator<PointType> { | |
var at = origin | |
return AnyIterator { | |
if at.x == self.width { | |
at.y += 1 | |
at.x = 0 | |
} | |
guard at.y < self.height else { return nil } | |
defer { at.x += 1 } | |
return at | |
} | |
} | |
} | |
struct GenericInsets<ValueType: GeometricValue>: Equatable { | |
var top: ValueType | |
var left: ValueType | |
var bottom: ValueType | |
var right: ValueType | |
static var zero: Self { Self(top: 0, left: 0, bottom: 0, right: 0) } | |
} | |
typealias Point = GenericPoint<Int> | |
typealias Size = GenericSize<Int> | |
typealias Rect = GenericRect<Int> | |
typealias EdgeInsets = GenericInsets<Int> | |
typealias PointF = GenericPoint<Float> | |
typealias SizeF = GenericSize<Float> | |
typealias RectF = GenericRect<Float> | |
typealias EdgeInsetsF = GenericInsets<Float> | |
extension Point { | |
init(_ point: PointF) { | |
self.init(Int(point.x.rounded(.down)), Int(point.y.rounded(.down))) | |
} | |
} | |
extension PointF { | |
init(_ point: Point) { | |
self.init(Float(point.x), Float(point.y)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment