Skip to content

Commit 8458336

Browse files
committed
expanded docs for scales and added (and tested) power scale implementation
1 parent 0330bcf commit 8458336

File tree

220 files changed

+35443
-19181
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

220 files changed

+35443
-19181
lines changed

Sources/SwiftViz/Documentation.docc/Documentation.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,23 @@ A collection of components to provide structures that support data visualization
44

55
## Overview
66

7+
SwiftViz includes components useful to creating visualizations of data.
8+
Many such visualizations require mapping from an abstract set of input values to another output value.
9+
The ``SwiftViz/Scale`` protocol defines the common set of functions required.
10+
Concrete scales that map linearly and logrithmicly are provided, with convenience methods to create them on the enumerations that host the collections mapping to different underlying types.
11+
712
Loosely based on the APIs and the visualization constructs created by Mike Bostock and contributors to [D3.js](https://d3js.org)
813

914
## Topics
1015

1116
### Scales
1217

1318
- ``SwiftViz/Scale``
19+
- ``SwiftViz/TickScale``
1420
- ``SwiftViz/NiceValue``
1521
- ``SwiftViz/LinearScale``
1622
- ``SwiftViz/LogScale``
23+
- ``SwiftViz/PowerScale``
1724

1825
### Axis
1926

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# ``SwiftViz/PowerScale``
2+
3+
## Topics
4+
5+
### Creating Power Scales
6+
7+
- ``SwiftViz/PowerScale/create(_:_:exponent:)``
8+
- ``SwiftViz/PowerScale/create(_:exponent:)-3wlyt``
9+
- ``SwiftViz/PowerScale/create(_:exponent:)-8clnb``
10+
11+
### Types of Power Scales
12+
13+
- ``SwiftViz/PowerScale/DoubleScale``

Sources/SwiftViz/Documentation.docc/Scale.md

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
- ``SwiftViz/Scale/domainLower``
88
- ``SwiftViz/Scale/domainHigher``
99
- ``SwiftViz/Scale/domainExtent``
10-
- ``SwiftViz/Scale/desiredTicks``
1110
- ``SwiftViz/Scale/InputType``
1211
- ``SwiftViz/Scale/OutputType``
1312
- ``SwiftViz/Scale/transformType``
@@ -18,20 +17,7 @@
1817
- ``SwiftViz/Scale/invert(_:from:to:)``
1918
- ``SwiftViz/Scale/scale(_:from:to:)``
2019

21-
### Generating Ticks for a Scale
22-
23-
- ``SwiftViz/Scale/ticks(_:from:to:)``
24-
- ``SwiftViz/Scale/ticks(rangeLower:rangeHigher:)-2tmfq``
25-
- ``SwiftViz/Scale/ticks(rangeLower:rangeHigher:)-35b4f``
26-
- ``SwiftViz/Scale/ticks(rangeLower:rangeHigher:)-8gl6r``
27-
28-
### Generating and Validating TickLabels
29-
30-
- ``SwiftViz/Scale/validatedTickLabels(_:from:to:)``
31-
- ``SwiftViz/Scale/labeledTickValues(_:from:to:)``
32-
3320
### Comparing Values
3421

3522
- ``SwiftViz/Scale/domainContains(_:)-2i0wq``
36-
- ``SwiftViz/Scale/domainContains(_:)-6e065``
3723
- ``SwiftViz/Scale/transformAgainstDomain(_:)``
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# ``SwiftViz/TickScale``
2+
3+
## Topics
4+
5+
### Inspecting Tick Scales
6+
7+
- ``SwiftViz/TickScale/desiredTicks``
8+
9+
### Generating Ticks for a Scale
10+
11+
- ``SwiftViz/TickScale/ticks(_:from:to:)``
12+
- ``SwiftViz/TickScale/ticks(rangeLower:rangeHigher:)-1vugh``
13+
- ``SwiftViz/TickScale/ticks(rangeLower:rangeHigher:)-4uxek``
14+
- ``SwiftViz/TickScale/ticks(rangeLower:rangeHigher:)-5n5j1``
15+
16+
### Generating and Validating TickLabels
17+
18+
- ``SwiftViz/TickScale/validatedTickLabels(_:from:to:)``
19+
- ``SwiftViz/TickScale/labeledTickValues(_:from:to:)``

Sources/SwiftViz/LinearScale.swift

Lines changed: 114 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,36 @@ import Numerics
55
/// A collection of linear scales.
66
public enum LinearScale {
77
/// A linear scale is created using a continuous input of type double converting to an output of type float.
8-
public struct DoubleScale: Scale {
8+
public struct DoubleScale: TickScale {
9+
/// The type used for the scale's domain.
910
public typealias InputType = Double
11+
/// The type used for the scale's range.
1012
public typealias OutputType = Float
1113

12-
public let domainLower: Double
13-
public let domainHigher: Double
14-
public let domainExtent: Double
15-
14+
/// The lower bound of the input domain.
15+
public let domainLower: InputType
16+
/// The upper bound of the input domain.
17+
public let domainHigher: InputType
18+
/// The distance or length between the upper and lower bounds of the input domain.
19+
public let domainExtent: InputType
20+
21+
/// A boolean value that indicates whether the output vales are constrained to the min and max of the output range.
22+
///
23+
/// If `true`, values processed by the scale are constrained to the output range, and values processed backwards through the scale
24+
/// are constrained to the input domain.
1625
public var transformType: DomainDataTransform
26+
27+
/// The number of ticks desired when creating the scale.
28+
///
29+
/// This number may not match the number of ticks returned by ``TickScale/ticks(_:from:to:)``
1730
public let desiredTicks: Int
1831

32+
/// Creates a new linear scale for the upper and lower bounds of the domain you provide.
33+
/// - Parameters:
34+
/// - lower: The lower bound of the scale's domain.
35+
/// - higher: The upper bound of the scale's domain.
36+
/// - transform: The transform constraint to apply when values fall outside the domain of the scale.
37+
/// - desiredTicks: The desired number of ticks when visually representing the scale.
1938
public init(from lower: InputType, to higher: InputType, transform: DomainDataTransform = .none, desiredTicks: Int = 10) {
2039
precondition(lower < higher)
2140
transformType = transform
@@ -27,6 +46,12 @@ public enum LinearScale {
2746

2847
// MARK: - Double
2948

49+
/// Transforms the input value using a linear function to the resulting value into the range you provide.
50+
///
51+
/// - Parameter domainValue: A value in the domain of the scale.
52+
/// - Parameter lower: The lower bound to the range to map to.
53+
/// - Parameter higher: The upper bound of the range to map to.
54+
/// - Returns: A value mapped to the range you provide.
3055
public func scale(_ domainValue: Double, from lower: Float, to higher: Float) -> Float? {
3156
if let domainValue = transformAgainstDomain(domainValue) {
3257
let normalizedInput = normalize(domainValue, lower: domainLower, higher: domainHigher)
@@ -36,6 +61,12 @@ public enum LinearScale {
3661
return nil
3762
}
3863

64+
/// Transforms a value within the range into the associated domain value.
65+
/// - Parameters:
66+
/// - rangeValue: A value in the range of the scale.
67+
/// - lower: The lower bound to the range to map from.
68+
/// - higher: The upper bound to the range to map from.
69+
/// - Returns: A value linearly mapped from the range back into the domain.
3970
public func invert(_ rangeValue: Float, from lower: Float, to higher: Float) -> Double? {
4071
// inverts the scale, taking a value in the output range and returning the relevant value from the input domain
4172
let normalizedRangeValue = normalize(Double(rangeValue), lower: Double(lower), higher: Double(higher))
@@ -45,17 +76,36 @@ public enum LinearScale {
4576
}
4677

4778
/// A linear scale is created using a continuous input of type float converting to an output of type float.
48-
public struct FloatScale: Scale {
79+
public struct FloatScale: TickScale {
80+
/// The type used for the scale's domain.
4981
public typealias InputType = Float
82+
/// The type used for the scale's range.
5083
public typealias OutputType = Float
5184

52-
public let domainLower: Float
53-
public let domainHigher: Float
54-
public let domainExtent: Float
55-
85+
/// The lower bound of the input domain.
86+
public let domainLower: InputType
87+
/// The upper bound of the input domain.
88+
public let domainHigher: InputType
89+
/// The distance or length between the upper and lower bounds of the input domain.
90+
public let domainExtent: InputType
91+
92+
/// A boolean value that indicates whether the output vales are constrained to the min and max of the output range.
93+
///
94+
/// If `true`, values processed by the scale are constrained to the output range, and values processed backwards through the scale
95+
/// are constrained to the input domain.
5696
public var transformType: DomainDataTransform
97+
98+
/// The number of ticks desired when creating the scale.
99+
///
100+
/// This number may not match the number of ticks returned by ``TickScale/ticks(_:from:to:)``
57101
public let desiredTicks: Int
58102

103+
/// Creates a new linear scale for the upper and lower bounds of the domain you provide.
104+
/// - Parameters:
105+
/// - lower: The lower bound of the scale's domain.
106+
/// - higher: The upper bound of the scale's domain.
107+
/// - transform: The transform constraint to apply when values fall outside the domain of the scale.
108+
/// - desiredTicks: The desired number of ticks when visually representing the scale.
59109
public init(from lower: InputType, to higher: InputType, transform: DomainDataTransform = .none, desiredTicks: Int = 10) {
60110
precondition(lower < higher)
61111
transformType = transform
@@ -67,6 +117,12 @@ public enum LinearScale {
67117

68118
// MARK: - Float
69119

120+
/// Transforms the input value using a linear function to the resulting value into the range you provide.
121+
///
122+
/// - Parameter domainValue: A value in the domain of the scale.
123+
/// - Parameter lower: The lower bound to the range to map to.
124+
/// - Parameter higher: The upper bound of the range to map to.
125+
/// - Returns: A value mapped to the range you provide.
70126
public func scale(_ domainValue: Float, from lower: Float, to higher: Float) -> Float? {
71127
if let domainValue = transformAgainstDomain(domainValue) {
72128
let normalizedInput = normalize(domainValue, lower: domainLower, higher: domainHigher)
@@ -76,6 +132,12 @@ public enum LinearScale {
76132
return nil
77133
}
78134

135+
/// Transforms a value within the range into the associated domain value.
136+
/// - Parameters:
137+
/// - rangeValue: A value in the range of the scale.
138+
/// - lower: The lower bound to the range to map from.
139+
/// - higher: The upper bound to the range to map from.
140+
/// - Returns: A value linearly mapped from the range back into the domain.
79141
public func invert(_ rangeValue: Float, from lower: Float, to higher: Float) -> Float? {
80142
// inverts the scale, taking a value in the output range and returning the relevant value from the input domain
81143
let normalizedRangeValue = normalize(rangeValue, lower: lower, higher: higher)
@@ -85,17 +147,36 @@ public enum LinearScale {
85147
}
86148

87149
/// A linear scale is created using a continuous input of type int converting to an output of type float.
88-
public struct IntScale: Scale {
150+
public struct IntScale: TickScale {
151+
/// The type used for the scale's domain.
89152
public typealias InputType = Int
153+
/// The type used for the scale's range.
90154
public typealias OutputType = Float
91155

92-
public let domainLower: Int
93-
public let domainHigher: Int
94-
public let domainExtent: Int
95-
156+
/// The lower bound of the input domain.
157+
public let domainLower: InputType
158+
/// The upper bound of the input domain.
159+
public let domainHigher: InputType
160+
/// The distance or length between the upper and lower bounds of the input domain.
161+
public let domainExtent: InputType
162+
163+
/// A boolean value that indicates whether the output vales are constrained to the min and max of the output range.
164+
///
165+
/// If `true`, values processed by the scale are constrained to the output range, and values processed backwards through the scale
166+
/// are constrained to the input domain.
96167
public var transformType: DomainDataTransform
168+
169+
/// The number of ticks desired when creating the scale.
170+
///
171+
/// This number may not match the number of ticks returned by ``TickScale/ticks(_:from:to:)``
97172
public let desiredTicks: Int
98173

174+
/// Creates a new linear scale for the upper and lower bounds of the domain you provide.
175+
/// - Parameters:
176+
/// - lower: The lower bound of the scale's domain.
177+
/// - higher: The upper bound of the scale's domain.
178+
/// - transform: The transform constraint to apply when values fall outside the domain of the scale.
179+
/// - desiredTicks: The desired number of ticks when visually representing the scale.
99180
public init(from lower: InputType, to higher: InputType, transform: DomainDataTransform = .none, desiredTicks: Int = 10) {
100181
precondition(lower < higher)
101182
transformType = transform
@@ -107,6 +188,12 @@ public enum LinearScale {
107188

108189
// MARK: - Int
109190

191+
/// Transforms the input value using a linear function to the resulting value into the range you provide.
192+
///
193+
/// - Parameter domainValue: A value in the domain of the scale.
194+
/// - Parameter lower: The lower bound to the range to map to.
195+
/// - Parameter higher: The upper bound of the range to map to.
196+
/// - Returns: A value mapped to the range you provide.
110197
public func scale(_ domainValue: Int, from lower: Float, to higher: Float) -> Float? {
111198
if let domainValue = transformAgainstDomain(domainValue) {
112199
let convertedDomain = Float(domainValue)
@@ -119,18 +206,24 @@ public enum LinearScale {
119206
return nil
120207
}
121208

209+
/// Transforms a value within the range into the associated domain value.
210+
/// - Parameters:
211+
/// - rangeValue: A value in the range of the scale.
212+
/// - lower: The lower bound to the range to map from.
213+
/// - higher: The upper bound to the range to map from.
214+
/// - Returns: A value linearly mapped from the range back into the domain.
122215
public func invert(_ rangeValue: Float, from lower: Float, to higher: Float) -> Int? {
123216
// inverts the scale, taking a value in the output range and returning the relevant value from the input domain
124217
let normalizedRangeValue = normalize(Double(rangeValue), lower: Double(lower), higher: Double(higher))
125218
let mappedToDomain = interpolate(normalizedRangeValue, lower: Double(domainLower), higher: Double(domainHigher))
126219
return transformAgainstDomain(Int(mappedToDomain))
127220
}
128221
}
129-
222+
130223
// MARK: - Factory (convenience) Methods
131-
224+
132225
// Double
133-
226+
134227
/// Creates a linear scale that maps values between the lower and upper bounds you provide.
135228
/// - Parameters:
136229
/// - low: The lower bounds of the domain.
@@ -144,21 +237,21 @@ public enum LinearScale {
144237
public static func create(_ range: ClosedRange<Double>) -> LinearScale.DoubleScale {
145238
LinearScale.DoubleScale(from: range.lowerBound, to: range.upperBound)
146239
}
147-
240+
148241
/// Creates a linear scale that maps values from 0 to the upper bound you provide.
149242
/// - Parameter high: The upper bounds of the domain.
150243
public static func create(_ high: Double) -> LinearScale.DoubleScale {
151244
LinearScale.DoubleScale(from: 0, to: high)
152245
}
153-
246+
154247
/// Creates a linear scale for dates that maps values between the lower and upper bounds you provide.
155248
/// - Parameters:
156249
/// - low: The lower bounds of the domain.
157250
/// - high: The upper bounds of the domain.
158251
public static func create(_ low: Date, _ high: Date) -> LinearScale.DoubleScale {
159252
LinearScale.DoubleScale(from: low.timeIntervalSince1970, to: high.timeIntervalSince1970)
160253
}
161-
254+
162255
/// Creates a linear scale for dates that maps values within the range you provide.
163256
/// - Parameter range: The range of the domain.
164257
public static func create(_ range: ClosedRange<Date>) -> LinearScale.DoubleScale {
@@ -208,6 +301,4 @@ public enum LinearScale {
208301
public static func create(_ range: ClosedRange<Int>) -> LinearScale.IntScale {
209302
LinearScale.IntScale(from: range.lowerBound, to: range.upperBound)
210303
}
211-
212-
213304
}

0 commit comments

Comments
 (0)