Skip to content

Commit f23d554

Browse files
authored
Merge pull request #1160 from bubski/cg-api
2 parents 2ec3015 + 400195a commit f23d554

File tree

2 files changed

+868
-1
lines changed

2 files changed

+868
-1
lines changed

Foundation/NSGeometry.swift

Lines changed: 163 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,11 +237,173 @@ extension CGRect {
237237
y: -CGFloat.greatestFiniteMagnitude / 2,
238238
width: CGFloat.greatestFiniteMagnitude,
239239
height: CGFloat.greatestFiniteMagnitude)
240+
241+
public var width: CGFloat { return abs(self.size.width) }
242+
public var height: CGFloat { return abs(self.size.height) }
243+
244+
public var minX: CGFloat { return self.origin.x + min(self.size.width, 0) }
245+
public var midX: CGFloat { return (self.minX + self.maxX) * 0.5 }
246+
public var maxX: CGFloat { return self.origin.x + max(self.size.width, 0) }
247+
248+
public var minY: CGFloat { return self.origin.y + min(self.size.height, 0) }
249+
public var midY: CGFloat { return (self.minY + self.maxY) * 0.5 }
250+
public var maxY: CGFloat { return self.origin.y + max(self.size.height, 0) }
251+
252+
public var isEmpty: Bool { return self.isNull || self.size.width == 0 || self.size.height == 0 }
253+
public var isInfinite: Bool { return self == .infinite }
254+
public var isNull: Bool { return self.origin.x == .infinity || self.origin.y == .infinity }
255+
256+
public func contains(_ point: CGPoint) -> Bool {
257+
if self.isNull || self.isEmpty { return false }
258+
259+
return (self.minX..<self.maxX).contains(point.x) && (self.minY..<self.maxY).contains(point.y)
260+
}
261+
262+
public func contains(_ rect2: CGRect) -> Bool {
263+
return self.union(rect2) == self
264+
}
265+
266+
public var standardized: CGRect {
267+
if self.isNull { return .null }
268+
269+
return CGRect(x: self.minX,
270+
y: self.minY,
271+
width: self.width,
272+
height: self.height)
273+
}
274+
275+
public var integral: CGRect {
276+
if self.isNull { return self }
277+
278+
let standardized = self.standardized
279+
let x = standardized.origin.x.rounded(.down)
280+
let y = standardized.origin.y.rounded(.down)
281+
let width = (standardized.origin.x + standardized.size.width).rounded(.up) - x
282+
let height = (standardized.origin.y + standardized.size.height).rounded(.up) - y
283+
return CGRect(x: x, y: y, width: width, height: height)
284+
}
285+
286+
public func insetBy(dx: CGFloat, dy: CGFloat) -> CGRect {
287+
if self.isNull { return self }
288+
289+
var rect = self.standardized
290+
291+
rect.origin.x += dx
292+
rect.origin.y += dy
293+
rect.size.width -= 2 * dx
294+
rect.size.height -= 2 * dy
295+
296+
if rect.size.width < 0 || rect.size.height < 0 {
297+
return .null
298+
}
299+
300+
return rect
301+
}
302+
303+
public func union(_ r2: CGRect) -> CGRect {
304+
if self.isNull {
305+
return r2
306+
}
307+
else if r2.isNull {
308+
return self
309+
}
310+
311+
let rect1 = self.standardized
312+
let rect2 = r2.standardized
313+
314+
let minX = min(rect1.minX, rect2.minX)
315+
let minY = min(rect1.minY, rect2.minY)
316+
let maxX = max(rect1.maxX, rect2.maxX)
317+
let maxY = max(rect1.maxY, rect2.maxY)
318+
319+
return CGRect(x: minX,
320+
y: minY,
321+
width: maxX - minX,
322+
height: maxY - minY)
323+
}
324+
325+
public func intersection(_ r2: CGRect) -> CGRect {
326+
if self.isNull || r2.isNull { return .null }
327+
328+
let rect1 = self.standardized
329+
let rect2 = r2.standardized
330+
331+
let rect1SpanH = rect1.minX...rect1.maxX
332+
let rect1SpanV = rect1.minY...rect1.maxY
333+
334+
let rect2SpanH = rect2.minX...rect2.maxX
335+
let rect2SpanV = rect2.minY...rect2.maxY
336+
337+
if !rect1SpanH.overlaps(rect2SpanH) || !rect1SpanV.overlaps(rect2SpanV) {
338+
return .null
339+
}
340+
341+
let overlapH = rect1SpanH.clamped(to: rect2SpanH)
342+
let overlapV = rect1SpanV.clamped(to: rect2SpanV)
343+
344+
return CGRect(x: overlapH.lowerBound,
345+
y: overlapV.lowerBound,
346+
width: overlapH.upperBound - overlapH.lowerBound,
347+
height: overlapV.upperBound - overlapV.lowerBound)
348+
}
349+
350+
public func intersects(_ r2: CGRect) -> Bool {
351+
return !self.intersection(r2).isNull
352+
}
353+
354+
public func offsetBy(dx: CGFloat, dy: CGFloat) -> CGRect {
355+
if self.isNull { return self }
356+
357+
var rect = self.standardized
358+
rect.origin.x += dx
359+
rect.origin.y += dy
360+
return rect
361+
}
362+
363+
public func divided(atDistance: CGFloat, from fromEdge: CGRectEdge) -> (slice: CGRect, remainder: CGRect) {
364+
if self.isNull { return (.null, .null) }
365+
366+
let splitLocation: CGFloat
367+
switch fromEdge {
368+
case .minXEdge: splitLocation = min(max(atDistance, 0), self.width)
369+
case .maxXEdge: splitLocation = min(max(self.width - atDistance, 0), self.width)
370+
case .minYEdge: splitLocation = min(max(atDistance, 0), self.height)
371+
case .maxYEdge: splitLocation = min(max(self.height - atDistance, 0), self.height)
372+
}
373+
374+
let rect = self.standardized
375+
var rect1 = rect
376+
var rect2 = rect
377+
378+
switch fromEdge {
379+
case .minXEdge: fallthrough
380+
case .maxXEdge:
381+
rect1.size.width = splitLocation
382+
rect2.origin.x = rect1.maxX
383+
rect2.size.width = rect.width - splitLocation
384+
case .minYEdge: fallthrough
385+
case .maxYEdge:
386+
rect1.size.height = splitLocation
387+
rect2.origin.y = rect1.maxY
388+
rect2.size.height = rect.height - splitLocation
389+
}
390+
391+
switch fromEdge {
392+
case .minXEdge: fallthrough
393+
case .minYEdge: return (rect1, rect2)
394+
case .maxXEdge: fallthrough
395+
case .maxYEdge: return (rect2, rect1)
396+
}
397+
}
240398
}
241399

242400
extension CGRect: Equatable {
243401
public static func ==(lhs: CGRect, rhs: CGRect) -> Bool {
244-
return lhs.origin == rhs.origin && lhs.size == rhs.size
402+
if lhs.isNull && rhs.isNull { return true }
403+
404+
let r1 = lhs.standardized
405+
let r2 = rhs.standardized
406+
return r1.origin == r2.origin && r1.size == r2.size
245407
}
246408
}
247409

0 commit comments

Comments
 (0)