Skip to content

Commit 449d9be

Browse files
committed
- Moved EdgeComponents to its own file
- Created "Geometry" subdirectory - Moved Rect and Size to their own files inside "Geometry"
1 parent 28f4984 commit 449d9be

File tree

5 files changed

+254
-237
lines changed

5 files changed

+254
-237
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
2+
/// A container which has a value at each `RectEdge`.
3+
///
4+
public struct EdgeComponents<T> {
5+
public var left: T
6+
public var top: T
7+
public var right: T
8+
public var bottom: T
9+
10+
public subscript(edge: RectEdge) -> T {
11+
get {
12+
switch edge {
13+
case .left: return left
14+
case .right: return right
15+
case .top: return top
16+
case .bottom: return bottom
17+
}
18+
}
19+
set {
20+
switch edge {
21+
case .left: left = newValue
22+
case .right: right = newValue
23+
case .top: top = newValue
24+
case .bottom: bottom = newValue
25+
}
26+
}
27+
}
28+
29+
/// Returns an `EdgeComponents` which has the given `value` on every edge.
30+
///
31+
public static func all(_ value: T) -> EdgeComponents<T> {
32+
EdgeComponents(left: value, top: value, right: value, bottom: value)
33+
}
34+
35+
/// Returns a new `EdgeComponents` by applying the given closure to each edge.
36+
///
37+
public func mapByEdge<U>(_ block: (RectEdge, T) throws -> U) rethrows -> EdgeComponents<U> {
38+
EdgeComponents<U>(left: try block(.left, left), top: try block(.top, top),
39+
right: try block(.right, right), bottom: try block(.bottom, bottom))
40+
}
41+
42+
/// Returns a new `EdgeComponents` by applying the given closure to each edge.
43+
///
44+
public func map<U>(_ block: (T) throws -> U) rethrows -> EdgeComponents<U> {
45+
try mapByEdge { _, val in try block(val) }
46+
}
47+
}
48+
49+
extension EdgeComponents where T: ExpressibleByIntegerLiteral {
50+
public static var zero: Self { .all(0) }
51+
}
52+
53+
extension EdgeComponents where T: RangeReplaceableCollection {
54+
public static var empty: Self {
55+
EdgeComponents(left: .init(), top: .init(), right: .init(), bottom: .init())
56+
}
57+
}
58+
59+
extension Rect {
60+
public func inset(by insets: EdgeComponents<Float>) -> Rect {
61+
var rect = self
62+
rect.height -= insets.top + insets.bottom
63+
rect.width -= insets.left + insets.right
64+
rect.origin.x += insets.left
65+
rect.origin.y += insets.bottom
66+
return rect
67+
}
68+
}

Sources/SwiftPlot/Geometry/Rect.swift

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
2+
/// A Rectangle in a bottom-left coordinate space.
3+
///
4+
public struct Rect: Equatable, Hashable {
5+
public var origin: Point
6+
public var size: Size
7+
8+
public init(origin: Point, size: Size) {
9+
self.origin = origin
10+
self.size = size
11+
}
12+
}
13+
14+
extension Rect {
15+
16+
public static let empty = Rect(origin: .zero, size: .zero)
17+
18+
public var normalized: Rect {
19+
let normalizedOrigin = Point(origin.x + (size.width < 0 ? size.width : 0),
20+
origin.y + (size.height < 0 ? size.height : 0))
21+
let normalizedSize = Size(width: abs(size.width), height: abs(size.height))
22+
return Rect(origin: normalizedOrigin, size: normalizedSize)
23+
}
24+
25+
// Coordinate accessors.
26+
27+
public var minX: Float {
28+
return normalized.origin.x
29+
}
30+
31+
public var minY: Float {
32+
return normalized.origin.y
33+
}
34+
35+
public var midX: Float {
36+
return origin.x + (size.width/2)
37+
}
38+
39+
public var midY: Float {
40+
return origin.y + (size.height/2)
41+
}
42+
43+
public var maxX: Float {
44+
let norm = normalized
45+
return norm.origin.x + norm.size.width
46+
}
47+
48+
public var maxY: Float {
49+
let norm = normalized
50+
return norm.origin.y + norm.size.height
51+
}
52+
53+
public var center: Point {
54+
return Point(midX, midY)
55+
}
56+
57+
public init(size: Size, centeredOn center: Point) {
58+
self = Rect(
59+
origin: Point(center.x - (size.width/2), center.y - (size.height/2)),
60+
size: size
61+
)
62+
}
63+
64+
// Size accessors.
65+
66+
public var width: Float {
67+
get { return size.width }
68+
set { size.width = newValue }
69+
}
70+
71+
public var height: Float {
72+
get { return size.height }
73+
set { size.height = newValue }
74+
}
75+
76+
// Rounding.
77+
78+
/// Returns a version of this `Rect` rounded by `roundOutwards`.
79+
public var roundedOutwards: Rect {
80+
var rect = self
81+
rect.roundOutwards()
82+
return rect
83+
}
84+
85+
/// Rounds this `Rect` to integer coordinates, by rounding all points away from the center.
86+
public mutating func roundOutwards() {
87+
origin.x.round(.down)
88+
origin.y.round(.down)
89+
size.width.round(.up)
90+
size.height.round(.up)
91+
}
92+
93+
/// Returns a version of this `Rect` rounded by `roundInwards`.
94+
public var roundedInwards: Rect {
95+
var rect = self
96+
rect.roundInwards()
97+
return rect
98+
}
99+
100+
/// Rounds this `Rect` to integer coordinates, by rounding all points towards the center.
101+
public mutating func roundInwards() {
102+
origin.x.round(.up)
103+
origin.y.round(.up)
104+
size.width.round(.down)
105+
size.height.round(.down)
106+
}
107+
108+
// Subtraction operations.
109+
110+
mutating func contract(by distance: Float) {
111+
origin.x += distance
112+
origin.y += distance
113+
size.width -= 2 * distance
114+
size.height -= 2 * distance
115+
}
116+
117+
mutating func clampingShift(dx: Float = 0, dy: Float = 0) {
118+
origin.x += dx
119+
origin.y += dy
120+
size.width -= dx
121+
size.height -= dy
122+
}
123+
124+
// Other Utilities.
125+
126+
/// The range of Y coordinates considered inside this `Rect`.
127+
/// If a coordinate `y` is inside this Range, it means that `minY < y < maxY`,
128+
/// - i.e., it is _within_ and not _on_ the bounds of the `Rect`.
129+
internal var internalYCoordinates: Range<Float> {
130+
let norm = normalized
131+
return norm.origin.y.nextUp..<norm.origin.y.nextUp + norm.size.height
132+
}
133+
134+
/// The range of X coordinates considered inside this `Rect`.
135+
/// If a coordinate `x` is inside this Range, it means that `minX < x < maxX`
136+
/// - i.e., it is _within_ and not _on_ the bounds of the `Rect`
137+
internal var internalXCoordinates: Range<Float> {
138+
let norm = normalized
139+
return norm.origin.x.nextUp..<norm.origin.x.nextUp + norm.size.width
140+
}
141+
}
142+
143+
/// An edge of a `Rect`.
144+
///
145+
public enum RectEdge: Equatable, Hashable, CaseIterable {
146+
case left
147+
case right
148+
case top
149+
case bottom
150+
151+
/// Whether or not this is a horizontal edge (i.e. top or bottom edge).
152+
///
153+
public var isHorizontal: Bool {
154+
return self == .top || self == .bottom
155+
}
156+
157+
/// Whether or not this is a vertical edge (i.e.left or right edge).
158+
///
159+
public var isVertical: Bool {
160+
return self == .left || self == .right
161+
}
162+
}

Sources/SwiftPlot/Geometry/Size.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
public struct Size: Hashable {
3+
public var width: Float
4+
public var height: Float
5+
6+
public init(width: Float, height: Float) {
7+
self.width = width
8+
self.height = height
9+
}
10+
}
11+
12+
extension Size {
13+
public static let zero = Size(width: 0, height: 0)
14+
}
15+
16+
extension Size {
17+
18+
/// Returns a `Size` whose `width` is equal to this size's `height`,
19+
/// and whose `height` is equal to this size's `width`.
20+
///
21+
public func swappingComponents() -> Size {
22+
Size(width: height, height: width)
23+
}
24+
}

Sources/SwiftPlot/GraphLayout.swift

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -5,71 +5,6 @@ public enum LegendIcon {
55
case shape(ScatterPlotSeriesOptions.ScatterPattern, Color)
66
}
77

8-
/// A container which has a value at each `RectEdge`.
9-
public struct EdgeComponents<T> {
10-
public var left: T
11-
public var top: T
12-
public var right: T
13-
public var bottom: T
14-
15-
public subscript(edge: RectEdge) -> T {
16-
get {
17-
switch edge {
18-
case .left: return left
19-
case .right: return right
20-
case .top: return top
21-
case .bottom: return bottom
22-
}
23-
}
24-
set {
25-
switch edge {
26-
case .left: left = newValue
27-
case .right: right = newValue
28-
case .top: top = newValue
29-
case .bottom: bottom = newValue
30-
}
31-
}
32-
}
33-
34-
/// Returns an `EdgeComponents` which has the given `value` on every edge.
35-
///
36-
public static func all(_ value: T) -> EdgeComponents<T> {
37-
EdgeComponents(left: value, top: value, right: value, bottom: value)
38-
}
39-
40-
/// Returns a new `EdgeComponents` by applying the given closure to each edge.
41-
///
42-
public func mapByEdge<U>(_ block: (RectEdge, T) throws -> U) rethrows -> EdgeComponents<U> {
43-
EdgeComponents<U>(left: try block(.left, left), top: try block(.top, top),
44-
right: try block(.right, right), bottom: try block(.bottom, bottom))
45-
}
46-
47-
/// Returns a new `EdgeComponents` by applying the given closure to each edge.
48-
///
49-
public func map<U>(_ block: (T) throws -> U) rethrows -> EdgeComponents<U> {
50-
try mapByEdge { _, val in try block(val) }
51-
}
52-
}
53-
extension EdgeComponents where T: ExpressibleByIntegerLiteral {
54-
static var zero: Self { .all(0) }
55-
}
56-
extension EdgeComponents where T: RangeReplaceableCollection {
57-
static var empty: Self {
58-
EdgeComponents(left: .init(), top: .init(), right: .init(), bottom: .init())
59-
}
60-
}
61-
extension Rect {
62-
func inset(by insets: EdgeComponents<Float>) -> Rect {
63-
var rect = self
64-
rect.height -= insets.top + insets.bottom
65-
rect.width -= insets.left + insets.right
66-
rect.origin.x += insets.left
67-
rect.origin.y += insets.bottom
68-
return rect
69-
}
70-
}
71-
72-
738
/// A component for laying-out and rendering rectangular graphs.
749
///
7510
/// The principle 3 components of a `GraphLayout` are:

0 commit comments

Comments
 (0)