ColorMaps = {A colormap is an array of colors. A ColorMapProto Array prototype is provided to simplify access to the colormap’s colors.
Maps are extremely useful:
ColorMaps = {Ask the browser to use the canvas gradient feature to create nColors given the gradient color stops and locs.
Stops are css strings or rgba arrays. Locs are floats from 0-1
This is a powerful browser feature, can be used to create all the MatLab colormaps. See these gradient sites: Mozilla Gradient Doc, Colorzilla Gradient Editor, GitHub ColorMap Project
gradientImageData: (nColors, stops, locs) ->Convert the color stops to css strings
stops = (Color.convertColor c, "css" for c in stops)default locations for colors is equally spaced
locs = u.aRamp 0, 1, stops.length if not locs?create a nColors x 1 canvas context
ctx = u.createCtx nColors, 1create a new gradient and fill it with the color stops
grad = ctx.createLinearGradient 0, 0, nColors, 0
grad.addColorStop locs[i], stops[i] for i in [0...stops.length]draw the gradient returning the image data TypedArray
ctx.fillStyle = grad
ctx.fillRect 0, 0, nColors, 1
u.ctxToImageData(ctx).dataConvert Uint8Array into Array of 4 element Uint8 subarrays, 4 element JS Arrays, or colors. Useful for converting ImageData objects like gradients to color arrays.
uint8ArrayToUint8s: (a) ->
( a.subarray(i,i+4) for i in [0...a.length] by 4 )
uint8ArrayToRgbas: (a) ->
( [ a[i], a[i+1], a[i+2], a[i+3] ] for i in [0...a.length] by 4 )
uint8ArrayToColors: (array, type) ->
return new Uint32Array( array.buffer ) if type is "pixel"
@arrayToColors(@uint8ArrayToUint8s(array), type)Convert array of colors or rgba arrays to array of colors of given type
arrayToColors: (array, type) ->
return array if Color.colorType(array[0]) is type
array[i] = Color.convertColor(a, type) for a,i in array
arrayUtility to permute 3 arrays.
Result is an array of arrays of len 3 permuting A1, A2, A3. Used by rgbColorMap and hslColorMap
permuteColors: (A1, A2=A1, A3=A2, max=[255,255,255]) ->
[A1, A2, A3] = for A, i in [A1, A2, A3] # multi-line comprehension
if typeof A is "number"
if A is 1 then [max[i]] else u.aIntRamp(0, max[i], A)
else A
@permuteArrays A1, A2, A3Permute simple arrays w/o conversions above.
permuteArrays: (A1, A2=A1, A3=A2) ->
array = []
((array.push [a1,a2,a3] for a1 in A1) for a2 in A2) for a3 in A3
arrayConvert an array of colors to a colormap. Returns the original array, just for convenience.
colorMap: (array, indexToo = false) ->
array.__proto__ = @ColorMapProto
array.init indexTooUse prototypal inheritance for converting array to colormap.
ColorMapProto: {
__proto__: Array.prototypeInitialize array to be colormap. Create an index object for direct lookup of color in array if indexToo. If color type is “typed” add properties for map and index in map.
init: (indexToo = false) ->
@type = Color.colorType @[0]
@index = {} if indexToo
u.error "ColorMap type error" unless @type?
if @type is "typed"
for color,i in @ then color.ix = i; color.map = @
@index[ @indexKey(color) ] = i for color,i in @ if @index
@ # this is just the original array, returned for convenience.Given a color in the map, return the key it uses in the index object. The value will be the array index of the color.
indexKey: (color) -> # make css strings lower case?
if @type is "typed" then color.pixel else colorUse the indexKey to test two map color’s equality.
colorsEqual: (color1, color2) ->
@indexKey(color1) is @indexKey(color2)Get a random index or color from this map given the input range, defaults to entire map.
randomIndex: (start=0, stop=@length) -> u.randomInt2 start, stop
randomColor: (start=0, stop=@length) -> @[ @randomIndex start, stop ]Use Array.sort, augmented by updating index if present and color.ix for typedColors
sort: (compareFcn) ->
Array.prototype.sort.call @, compareFcn
@index[ @indexKey(color) ] = i for color,i in @ if @index
color.ix = i for color,i in @ if @type is "typed"
@Lookup color in map, returning index or undefined if not found
lookup: (color) ->
color = Color.convertColor color, @type # make sure color is our type
return @index[ @indexKey(color) ] if @index
for c,i in @ then return i if @colorsEqual(color, c)
undefinedReturn the map color proportional to the number value between min, max. This is a linear interpolation based on the map indices.
scaleColor: (number, min, max) ->
if number < min then number = min
if number > max then number = max
scale = (@length-1)*((number-min)/(max-min))
@[ Math.round scale ]Find the index/color closest to this r,g,b,a. Alpha only used for exact lookup optimization. Note: slow for large maps unless color cube or exact match.
findClosestIndex: (r, g, b, a=255) -> # alpha not in rgbDistance functionconvert r to r,g,b,a if g not set
[r, g, b, a] = Color.colorToArray r unless g?First directly find if rgb cube
if @cube
step = 255/(@cube-1)
[rLoc, gLoc, bLoc] = (Math.round(c/step) for c in [r, g, b])
return rLoc + gLoc*@cube + bLoc*@cube*@cubeThen check if is exact match. Only use of alpha for opacity maps
return ix if ix = @lookup [r,g,b,a]Finally use color distance to find closest color
minDist = Infinity; ixMin = 0
for color, i in @
[r0, g0, b0] = Color.colorToArray color
d = Color.rgbDistance r0, g0, b0, r, g, b
if d < minDist then minDist = d; ixMin = i
ixMin
findClosestColor: (r, g, b, a=255) -> @[ @findClosestIndex r, g, b, a ]
}Utilities for creating color arrays and associated maps. This is not exhaustive, you can follow these examples for your own use.
Convert any array of rgb(a) or color values into colormap. Good for converting css names, pixels/image data
basicColorMap: (array, type="typed", indexToo=false) ->
array = @arrayToColors array, type
@colorMap array, indexTooCreate a gray map (gray: r=g=b) These are typically 256 entries but can be smaller by passing a size parameter.
grayColorMap: (size=256, type="typed", indexToo=false) ->
array = ( [i,i,i] for i in u.aIntRamp 0, 255, size )
@basicColorMap array, type, indexTooCreate a map with a random set of colors. randomColorMap: (nColors, type=”typed”, indexToo=false) -> array = (Color.randomRgb() for i in [0…nColors]) @basicColorMap array, type, indexToo
Create a colormap by permuted rgb values.
R, G, B can be either a number, (the number of steps beteen 0-255), or an array of values to use for the color.
Ex: R = 3, corresponds to R = [0, 128, 255]
The resulting map permutes the R, G, B values. Thus if
R=G=B=4, the resulting map has 4*4*4=64 colors.
rgbColorMap: (R, G=R, B=R, type="typed", indexToo=true) ->
array = @permuteColors(R, G, B)
array.cube = R if (typeof R is "number") and (R is G is B)
@colorMap @arrayToColors(array, type), indexToo
rgbColorCube: (cubeSide, type="typed", indexToo=false) ->
@rgbColorMap cubeSide, cubeSide, cubeSide, type, indexTooCreate an hsl map, inputs similar to above. Convert the HSL values to typedColors, default to bright hue ramp (L=50).
hslColorMap: (H, S=1, L=1, type="typed", indexToo=false) ->
hslArray = @permuteColors(H, S, L, [359,100,50])
array = (Color.hslString a... for a in hslArray)
@colorMap @arrayToColors(array, type), indexTooUse gradient to build an rgba array, then convert to colormap. This easily creates all the MatLab colormaps.
gradientColorMap: (nColors, stops, locs, type="typed", indexToo=false) ->
id = @gradientImageData(nColors, stops, locs)
@colorMap @uint8ArrayToColors(id, type), indexTooThe most popular MatLab gradient, “jet”:
jetColors: [ [0,0,127], [0,0,255], [0,127,255], [0,255,255],
[127,255,127], [255,255,0], [255,127,0], [255,0,0], [127,0,0] ]Ramp of a single color from black to color and to white if whiteToo rampStops: (color, whiteToo) -> if whiteToo then [“black”, color, “white”] else [“black”, color]
rampColorMap: (color, width, whiteToo = false) ->
stops = if whiteToo then ["black", color, "white"] else ["black", color]
@gradientColorMap width, stops # @rampStops(color, whiteToo)NetLogo maps are sets of color ramps for the basic colors, but with slightly different r,g,b values designed for good color ballance. They typically go from black to near white shades. We provide an alternative to ramp from black to full color as well.
netLogoColorMap: (width = 10, whiteToo = true) ->
@namedColorMap @netLogoColors, width, whiteToo
cssBasicColorMap: (width=18, whiteToo=false) ->
@namedColorMap @basicCssColors, width, whiteToo
namedColorMap: (names, width, whiteToo) ->
map = []; ramps = {}
if u.isArray names # convert array of named colors to object of name: name
o = {}; o[n] = n for n in names; names = o
for name, color of names
ramps[name] = @rampColorMap color, width, whiteToo
map = map.concat ramps[name]
map = @basicColorMap map
map[k] = v for k, v of rampsHack: will remove; helps migration of turtle/patch.scaleColor
map[Color.convertColor k, "css"] = v for k, v of ramps
mapOur basic css named color strings
basicCssColors: ["gray", "red", "orange", "brown", "yellow", "green", "lime",
"turquoise", "cyan", "skyblue", "blue", "violet", "magenta", "pink"]Netlogo’s basic named colors. NOTE: sky -> skyblue; css legal named color.
netLogoColors:
gray: [141, 141, 141], red: [215, 50, 41]
orange: [241, 106, 21], brown: [157, 110, 72]
yellow: [237, 237, 49], green: [89, 176, 60]
lime: [44, 209, 59], turquoise: [29, 159, 120]
cyan: [84, 196, 196], skyblue: [45, 141, 190]
blue: [52, 93, 169], violet: [124, 80, 164]
magenta: [167, 27, 106], pink: [224, 127, 150]Create opacity map of the given base r,g,b color, with nOpacity opacity values, default to all 256
opacityColorMap: (rgb, nOpacities = 256, type="typed", indexToo=false) ->
[r, g, b] = rgb
array = ( [r, g, b, a] for a in u.aIntRamp 0, 255, nOpacities )
@colorMap @arrayToColors(array, type), indexTooCreate shared maps and utilities
createSharedMaps: ->A map of all 256 gray colors
@Gray = @grayColorMap()A popular rgb map of the best 256 colors
@Rgb256 = @rgbColorMap(8,8,4)A large 4K color map with good matches for most colors
@Rgb = @rgbColorCube(16)The HTML “web safe colors”
@Safe = @rgbColorCube(6)The popular MatLab jet gradient
@Jet = @gradientColorMap 256, @jetColorsThe netlogo map. NetLogo.yellow etc are individual netlogo hue ramps
@NetLogo = @netLogoColorMap 10Like above but largest < 256 map (252). Does not diminish to white. NetLogoRamps.yellow is thus a ramp from black to brightest NetLogo yellow hue while CssRamps.yellow is black to Css “yellow”
@NetLogoRamps = @netLogoColorMap 18, false
@CssRamps = @cssBasicColorMap 18, falseReturn random gray/color from a shared map. Colormap colors are immutable, so if you don’t want a shared gray/color but one who’s color you will change via color.setColor(..), then use clone:
color = ColorMaps.randomColor().clone()
color = ColorMaps.Jet.scaleColor(.5).clone()
randomGray: (min, max) -> @Gray.randomColor(min, max)
randomColor: () -> @Rgb256.randomColor()Legacy/Temporary: replace util/patch/turtle scaleColor
scaleColor: (color, number, min=0, max=1) ->
if color.scaleColor? # colormap ramp
return color.scaleColor number, min, max
else if ramp = @CssRamps[Color.convertColor color, "css"]
return ramp.scaleColor number, min, max
else
return @Rgb.findClosestColor Color.rgbLerp(color, number, min, max)
}
ColorMaps.createSharedMaps()