@@ -25,15 +25,98 @@ public protocol ColorMapProtocol {
2525// Linear Gradients.
2626
2727struct LinearGradient : ColorMapProtocol {
28- var start : Color
29- var end : Color
28+ struct Stop {
29+ var color : Color
30+ var position : Double
31+
32+ init ( _ color: Color , at pos: Double ) {
33+ self . color = color; self . position = pos
34+ }
35+ }
36+
37+ var stops : [ Stop ]
38+
39+ init ( stops: [ Stop ] ) {
40+ self . stops = stops. sorted { $0. position < $1. position }
41+ }
42+
43+ init ( start: Color , end: Color ) {
44+ self . init ( stops: [ Stop ( start, at: 0 ) , Stop ( end, at: 1 ) ] )
45+ }
46+
3047 func colorForOffset( _ offset: Float ) -> Color {
31- return lerp ( startColor: start, endColor: end, offset)
48+ return colorForOffset ( Double ( offset) )
49+ }
50+
51+ func colorForOffset( _ offset: Double ) -> Color {
52+ guard ( 0 ... 1 ) . contains ( offset) ,
53+ let rightStopIdx = stops. firstIndex ( where: { $0. position > offset } ) else {
54+ return stops. last? . color ?? . black
55+ }
56+ let rightStop = stops [ rightStopIdx]
57+ guard rightStopIdx > stops. startIndex else { return rightStop. color }
58+ let leftStop = stops [ stops. index ( before: rightStopIdx) ]
59+ assert ( leftStop. position <= offset)
60+
61+ let distance = rightStop. position - leftStop. position
62+ guard distance > 0 else { return rightStop. color }
63+
64+ let offset = Float ( ( offset - leftStop. position) / distance)
65+ return leftStop. color. linearBlend ( with: rightStop. color, offset: offset)
3266 }
3367}
3468
3569extension ColorMap {
70+
3671 public static func linear( _ start: Color , _ end: Color ) -> ColorMap {
3772 return ColorMap ( LinearGradient ( start: start, end: end) )
3873 }
74+
75+ public static let fiveColorHeatMap = ColorMap ( LinearGradient ( stops: [
76+ LinearGradient . Stop ( Color ( 0 , 0 , 1 , 1 ) , at: 0 ) ,
77+ LinearGradient . Stop ( Color ( 0 , 1 , 1 , 1 ) , at: 0.25 ) ,
78+ LinearGradient . Stop ( Color ( 0 , 1 , 0 , 1 ) , at: 0.5 ) ,
79+ LinearGradient . Stop ( Color ( 1 , 1 , 0 , 1 ) , at: 0.75 ) ,
80+ LinearGradient . Stop ( Color ( 1 , 0 , 0 , 1 ) , at: 1 ) ,
81+ ] ) )
82+
83+ public static let sevenColorHeatMap = ColorMap ( LinearGradient ( stops: [
84+ LinearGradient . Stop ( Color ( 0 , 0 , 0 , 1 ) , at: 0 ) ,
85+ LinearGradient . Stop ( Color ( 0 , 0 , 1 , 1 ) , at: 0.1666666667 ) ,
86+ LinearGradient . Stop ( Color ( 0 , 1 , 1 , 1 ) , at: 0.3333333333 ) ,
87+ LinearGradient . Stop ( Color ( 0 , 1 , 0 , 1 ) , at: 0.5 ) ,
88+ LinearGradient . Stop ( Color ( 1 , 1 , 0 , 1 ) , at: 0.6666666667 ) ,
89+ LinearGradient . Stop ( Color ( 1 , 0 , 0 , 1 ) , at: 0.8333333333 ) ,
90+ LinearGradient . Stop ( Color ( 1 , 1 , 1 , 1 ) , at: 1 )
91+ ] ) )
92+ }
93+
94+ // Transforming.
95+
96+ struct ColorTransformer < T> : ColorMapProtocol where T: ColorMapProtocol {
97+ var base : T
98+ var transform : ( inout Color ) -> Void
99+ init ( _ base: T , transform: @escaping ( inout Color ) -> Void ) {
100+ self . base = base; self . transform = transform
101+ }
102+ func colorForOffset( _ offset: Float ) -> Color {
103+ var color = base. colorForOffset ( offset)
104+ transform ( & color)
105+ return color
106+ }
107+ }
108+
109+ extension ColorMap {
110+
111+ public func withAlpha( _ alpha: Float ) -> ColorMap {
112+ return ColorMap ( ColorTransformer ( self ) { $0. a = alpha } )
113+ }
114+
115+ public func lightened( by: Float ) -> ColorMap {
116+ return ColorMap ( ColorTransformer ( self ) { $0 = $0. linearBlend ( with: . white, offset: by) } )
117+ }
118+
119+ public func darkened( by: Float ) -> ColorMap {
120+ return ColorMap ( ColorTransformer ( self ) { $0 = $0. linearBlend ( with: . black, offset: by) } )
121+ }
39122}
0 commit comments