Skip to content

Commit 556db93

Browse files
committed
- Namespace the Sequence heatmap APIs
- Limit rounding on heatmap to elements with size > 1
1 parent f20d2d1 commit 556db93

File tree

2 files changed

+46
-25
lines changed

2 files changed

+46
-25
lines changed

Sources/SwiftPlot/Heatmap.swift

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,15 @@ extension Heatmap: HasGraphLayout, Plot {
7373
width: size.width / Float(results.columns),
7474
height: size.height / Float(results.rows)
7575
)
76-
results.itemSize.width.round(.down)
77-
results.itemSize.height.round(.down)
76+
// If we have at enough space to give each element a pixel,
77+
// round the items down to integer dimensions to remove aliasing
78+
// in the rendered image.
79+
if results.itemSize.width > 1 {
80+
results.itemSize.width = max(results.itemSize.width.rounded(.down), 1)
81+
}
82+
if results.itemSize.height > 1 {
83+
results.itemSize.height = max(results.itemSize.height.rounded(.down), 1)
84+
}
7885
// Calculate markers.
7986
markers.xMarkers = (0..<results.columns).map {
8087
(Float($0) + 0.5) * results.itemSize.width
@@ -154,18 +161,18 @@ extension Heatmap
154161

155162
// Collection construction shorthand.
156163

157-
extension Sequence where Element: Sequence {
164+
extension SequencePlots where Base.Element: Sequence {
158165

159166
/// Returns a heatmap of values from this 2-dimensional sequence.
160167
/// - parameters:
161168
/// - interpolator: A function or `KeyPath` which maps values to a continuum between 0 and 1.
162169
/// - returns: A heatmap plot of the sequence's inner items.
163-
public func heatmap(interpolator: Interpolator<Element.Element>) -> Heatmap<Self> {
164-
return Heatmap(values: self, interpolator: interpolator)
170+
public func heatmap(interpolator: Interpolator<Base.Element.Element>) -> Heatmap<Base> {
171+
return Heatmap(values: base, interpolator: interpolator)
165172
}
166173
}
167174

168-
extension Collection {
175+
extension SequencePlots where Base: Collection {
169176

170177
/// Returns a heatmap of this collection's values, generated by slicing rows with the given width.
171178
/// - parameters:
@@ -174,51 +181,53 @@ extension Collection {
174181
/// - returns: A heatmap plot of the collection's values.
175182
/// - complexity: O(n). Consider though, that rendering a heatmap or copying to a `RamdomAccessCollection`
176183
/// is also at least O(n), and this does not copy the data.
177-
public func heatmap(width: Int, interpolator: Interpolator<Element>) -> Heatmap<[SubSequence]> {
184+
public func heatmap(width: Int, interpolator: Interpolator<Base.Element>) -> Heatmap<[Base.SubSequence]> {
178185
precondition(width > 0, "Cannot build a histogram with zero or negative width")
179-
var rows = [SubSequence]()
180-
var rowStart = startIndex
181-
while rowStart != endIndex {
182-
guard let rowEnd = index(rowStart, offsetBy: width, limitedBy: endIndex) else {
183-
rows.append(self[rowStart..<endIndex])
186+
var rows = [Base.SubSequence]()
187+
var rowStart = base.startIndex
188+
while rowStart != base.endIndex {
189+
guard let rowEnd = base.index(rowStart, offsetBy: width, limitedBy: base.endIndex) else {
190+
rows.append(base[rowStart..<base.endIndex])
184191
break
185192
}
186-
rows.append(self[rowStart..<rowEnd])
193+
rows.append(base[rowStart..<rowEnd])
187194
rowStart = rowEnd
188195
}
189-
return rows.heatmap(interpolator: interpolator)
196+
return rows.plots.heatmap(interpolator: interpolator)
190197
}
191198
}
192199

193-
extension RandomAccessCollection {
200+
extension SequencePlots where Base: RandomAccessCollection {
194201

195202
/// Returns a heatmap of this collection's values, generated by slicing rows with the given width.
196203
/// - parameters:
197204
/// - width: The width of the heatmap to generate. Must be greater than 0.
198205
/// - interpolator: A function or `KeyPath` which maps values to a continuum between 0 and 1.
199206
/// - returns: A heatmap plot of the collection's values.
200-
public func heatmap(width: Int, interpolator: Interpolator<Element>) -> Heatmap<[SubSequence]> {
207+
public func heatmap(width: Int, interpolator: Interpolator<Base.Element>) -> Heatmap<[Base.SubSequence]> {
201208
precondition(width > 0, "Cannot build a histogram with zero or negative width")
202-
let height = Int((Float(count) / Float(width)).rounded(.up))
209+
let height = Int((Float(base.count) / Float(width)).rounded(.up))
203210
return (0..<height)
204-
.map { _sliceForRow($0, width: width) }
205-
.heatmap(interpolator: interpolator)
211+
.map { base._sliceForRow($0, width: width) }
212+
.plots.heatmap(interpolator: interpolator)
206213
}
207214

208215
/// Returns a heatmap of this collection's values, generated by the data in to `height` rows.
209216
/// - parameters:
210217
/// - height: The height of the heatmap to generate. Must be greater than 0.
211218
/// - interpolator: A function or `KeyPath` which maps values to a continuum between 0 and 1.
212219
/// - returns: A heatmap plot of the collection's values.
213-
public func heatmap(height: Int, interpolator: Interpolator<Element>) -> Heatmap<[SubSequence]> {
220+
public func heatmap(height: Int, interpolator: Interpolator<Base.Element>) -> Heatmap<[Base.SubSequence]> {
214221
precondition(height > 0, "Cannot build a histogram with zero or negative height")
215-
let width = Int((Float(count) / Float(height)).rounded(.up))
222+
let width = Int((Float(base.count) / Float(height)).rounded(.up))
216223
return (0..<height)
217-
.map { _sliceForRow($0, width: width) }
218-
.heatmap(interpolator: interpolator)
224+
.map { base._sliceForRow($0, width: width) }
225+
.plots.heatmap(interpolator: interpolator)
219226
}
220-
221-
private func _sliceForRow(_ row: Int, width: Int) -> SubSequence {
227+
}
228+
229+
extension RandomAccessCollection {
230+
fileprivate func _sliceForRow(_ row: Int, width: Int) -> SubSequence {
222231
guard let start = index(startIndex, offsetBy: row * width, limitedBy: endIndex) else {
223232
return self[startIndex..<startIndex]
224233
}

Sources/SwiftPlot/SequencePlots.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
/// A namespace for graphs which may be drawn from this sequence.
3+
public struct SequencePlots<Base> where Base: Sequence {
4+
var base: Base
5+
}
6+
7+
extension Sequence {
8+
/// Graphs which may be drawn from this sequence.
9+
public var plots: SequencePlots<Self> {
10+
return SequencePlots(base: self)
11+
}
12+
}

0 commit comments

Comments
 (0)