Skip to content

Instantly share code, notes, and snippets.

@BigZaphod
Last active September 28, 2019 21:13
Show Gist options
  • Save BigZaphod/3a88b305457b9bf2656cc098f43503b5 to your computer and use it in GitHub Desktop.
Save BigZaphod/3a88b305457b9bf2656cc098f43503b5 to your computer and use it in GitHub Desktop.
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