From 2de017086d7c4a089b3d9e6a0653877c77ba8cae Mon Sep 17 00:00:00 2001 From: Thomas Breloff Date: Wed, 7 Oct 2015 12:50:07 -0400 Subject: [PATCH] working on colors and args --- docs/example_generation.jl | 25 +- examples/colors.ipynb | 520 ++++++++++++---------------------- src/args.jl | 56 ++-- src/backends/gadfly.jl | 18 +- src/backends/gadfly_shapes.jl | 6 +- src/backends/winston.jl | 13 +- src/colors.jl | 104 +++++-- src/plot.jl | 10 +- src/utils.jl | 25 ++ 9 files changed, 358 insertions(+), 419 deletions(-) diff --git a/docs/example_generation.jl b/docs/example_generation.jl index b1511284..1980d3a1 100644 --- a/docs/example_generation.jl +++ b/docs/example_generation.jl @@ -21,7 +21,7 @@ end const examples = PlotExample[ PlotExample("Lines", "A simple line plot of the columns.", - [:(plot(rand(50,5), w=3))]), + [:(plot(cumsum(randn(50,10),1), w=3))]), PlotExample("Functions", "Plot multiple functions. You can also put the function first.", [:(plot(0:0.01:4π, [sin,cos]))]), @@ -36,10 +36,10 @@ const examples = PlotExample[ [:(plot(rand(10), title="TITLE", xlabel="XLABEL", ylabel="YLABEL", background_color = RGB(0.2,0.2,0.2), xlim=(-3,13), yticks=0:0.1:1))]), PlotExample("Two-axis", "Use the `axis` arguments.\n\nNote: Currently only supported with Qwt and PyPlot", - [:(plot(Vector[randn(100), randn(100)*100]; axis = [:l,:r], ylabel="LEFT", yrightlabel="RIGHT"))]), + [:(plot(Vector[randn(100), randn(100)*100]; axis = [:l :r], ylabel="LEFT", yrightlabel="RIGHT"))]), PlotExample("Vectors w/ pluralized args", "Plot multiple series with different numbers of points. Mix arguments that apply to all series (marker/markersize) with arguments unique to each series (colors).", - [:(plot(Vector[rand(10), rand(20)]; marker=:ellipse, markersize=8, c=[:red,:blue]))]), + [:(plot(Vector[rand(10), rand(20)]; marker=:ellipse, markersize=8, c=(:red, :blue)))]), PlotExample("Build plot in pieces", "Start with a base plot...", [:(plot(rand(100)/3, reg=true, fill=0))]), @@ -51,17 +51,17 @@ const examples = PlotExample[ [:(heatmap(randn(10000),randn(10000), nbins=100))]), PlotExample("Line types", "", - [:(types = intersect(supportedTypes(), [:line, :path, :steppre, :steppost, :sticks, :scatter])), + [:(types = intersect(supportedTypes(), [:line, :path, :steppre, :steppost, :sticks, :scatter])'), :(n = length(types)), :(x = Vector[sort(rand(20)) for i in 1:n]), :(y = rand(20,n)), - :(plot(x, y, t=types, lab=map(string,types)))]), + :(plot(x, y, line=(types,3), lab=map(string,types), ms=15))]), PlotExample("Line styles", "", - [:(styles = setdiff(supportedStyles(), [:auto])), :(plot(cumsum(randn(20,length(styles)),1); style=:auto, label=map(string,styles), w=5))]), + [:(styles = setdiff(supportedStyles(), [:auto])'), :(plot(cumsum(randn(20,length(styles)),1); style=:auto, label=map(string,styles), w=5))]), PlotExample("Marker types", "", - [:(markers = setdiff(supportedMarkers(), [:none,:auto])), :(scatter(0.5:9.5, [fill(i-0.5,10) for i=length(markers):-1:1]; marker=:auto, label=map(string,markers), ms=10))]), + [:(markers = setdiff(supportedMarkers(), [:none,:auto])'), :(scatter(0.5:9.5, [fill(i-0.5,10) for i=length(markers):-1:1]; marker=:auto, label=map(string,markers), ms=12))]), PlotExample("Bar", "x is the midpoint of the bar. (todo: allow passing of edges instead of midpoints)", [:(bar(randn(1000)))]), @@ -74,7 +74,7 @@ const examples = PlotExample[ You can define the layout with keyword params... either set the number of plots `n` (and optionally number of rows `nr` or number of columns `nc`), or you can set the layout directly with `layout`. """, - [:(subplot(randn(100,5), layout=[1,1,3], t=[:line,:hist,:scatter,:step,:bar], nbins=10, leg=false))]), + [:(subplot(randn(100,5), layout=[1,1,3], t=[:line :hist :scatter :step :bar], nbins=10, leg=false))]), PlotExample("Adding to subplots", "Note here the automatic grid layout, as well as the order in which new series are added to the plots.", [:(subplot(randn(100,5), n=4))]), @@ -162,7 +162,8 @@ end # make and display one plot -function test_example(pkgname::Symbol, idx::Int) +function test_example(pkgname::Symbol, idx::Int, debug = true) + Plots._debugMode.on = debug println("Testing plot: $pkgname:$idx:$(examples[idx].header)") backend(pkgname) backend() @@ -173,7 +174,7 @@ function test_example(pkgname::Symbol, idx::Int) end # generate all plots and create a dict mapping idx --> plt -function test_all_examples(pkgname::Symbol) +function test_all_examples(pkgname::Symbol, debug = false) plts = Dict() for i in 1:length(examples) # if examples[i].header == "Subplots" && !subplotSupported() @@ -181,7 +182,7 @@ function test_all_examples(pkgname::Symbol) # end try - plt = test_example(pkgname, i) + plt = test_example(pkgname, i, debug) plts[i] = plt catch ex # TODO: put error info into markdown? @@ -280,6 +281,8 @@ function buildReadme() Plots.dumpSupportGraphs() end +default(size=(600,400)) + # run it! # note: generate separately so it's easy to comment out # @osx_only generate_markdown(:unicodeplots) diff --git a/examples/colors.ipynb b/examples/colors.ipynb index 17c01b5a..3a50cfa4 100644 --- a/examples/colors.ipynb +++ b/examples/colors.ipynb @@ -119,23 +119,16 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 40, "metadata": { "collapsed": false }, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO: Recompiling stale cache file /home/tom/.julia/lib/v0.4/Plots.ji for module Plots.\n" - ] - }, { "name": "stdout", "output_type": "stream", "text": [ - "[Plots.jl] Default backend: immerse" + "0.0" ] }, { @@ -145,344 +138,95 @@ "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + " width=\"4.0mm\" height=\"25.0mm\"\n", + " fill=\"#0000CC\" stroke=\"none\" />\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "" ], "text/plain": [ - "101-element Array{ColorTypes.RGB{T<:Union{AbstractFloat,FixedPointNumbers.FixedPoint}},1}:\n", - " RGB{Float64}(0.95,0.95,0.95) \n", - " RGB{Float64}(0.941,0.941,0.941)\n", - " RGB{Float64}(0.932,0.932,0.932)\n", - " RGB{Float64}(0.923,0.923,0.923)\n", - " RGB{Float64}(0.914,0.914,0.914)\n", - " RGB{Float64}(0.905,0.905,0.905)\n", - " RGB{Float64}(0.896,0.896,0.896)\n", - " RGB{Float64}(0.887,0.887,0.887)\n", - " RGB{Float64}(0.878,0.878,0.878)\n", - " RGB{Float64}(0.869,0.869,0.869)\n", - " RGB{Float64}(0.86,0.86,0.86) \n", - " RGB{Float64}(0.851,0.851,0.851)\n", - " RGB{Float64}(0.842,0.842,0.842)\n", - " ⋮ \n", - " RGB{Float64}(0.149,0.149,0.149)\n", - " RGB{Float64}(0.14,0.14,0.14) \n", - " RGB{Float64}(0.131,0.131,0.131)\n", - " RGB{Float64}(0.122,0.122,0.122)\n", - " RGB{Float64}(0.113,0.113,0.113)\n", - " RGB{Float64}(0.104,0.104,0.104)\n", - " RGB{Float64}(0.095,0.095,0.095)\n", - " RGB{Float64}(0.086,0.086,0.086)\n", - " RGB{Float64}(0.077,0.077,0.077)\n", - " RGB{Float64}(0.068,0.068,0.068)\n", - " RGB{Float64}(0.059,0.059,0.059)\n", - " RGB{Float64}(0.05,0.05,0.05) " + "20-element Array{ColorTypes.RGB{T<:Union{AbstractFloat,FixedPointNumbers.FixedPoint}},1}:\n", + " RGB{Float64}(0.0,0.0,0.8) \n", + " RGB{Float64}(0.301961,0.0,0.301961)\n", + " RGB{Float64}(0.8,0.447059,0.0) \n", + " RGB{Float64}(0.0,0.301961,0.0) \n", + " RGB{Float64}(0.8,0.0,0.0) \n", + " RGB{Float64}(0.0,0.15098,0.4) \n", + " RGB{Float64}(0.4,0.37451,0.0) \n", + " RGB{Float64}(0.8,0.223529,0.0) \n", + " RGB{Float64}(0.55098,0.0,0.15098) \n", + " RGB{Float64}(0.0,0.0754902,0.6) \n", + " RGB{Float64}(0.0,0.226471,0.2) \n", + " RGB{Float64}(0.2,0.338235,0.0) \n", + " RGB{Float64}(0.6,0.410784,0.0) \n", + " RGB{Float64}(0.8,0.335294,0.0) \n", + " RGB{Float64}(0.8,0.111765,0.0) \n", + " RGB{Float64}(0.67549,0.0,0.0754902)\n", + " RGB{Float64}(0.426471,0.0,0.226471)\n", + " RGB{Float64}(0.0,0.0377451,0.7) \n", + " RGB{Float64}(0.0,0.113235,0.5) \n", + " RGB{Float64}(0.0,0.188725,0.3) " ] }, - "execution_count": 1, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" }, @@ -490,7 +234,26 @@ "name": "stdout", "output_type": "stream", "text": [ - "\n" + "\n", + "1.0\n", + "0.5\n", + "0.25\n", + "0.75\n", + "0.125\n", + "0.375\n", + "0.625\n", + "0.875\n", + "0.0625\n", + "0.1875\n", + "0.3125\n", + "0.4375\n", + "0.5625\n", + "0.6875\n", + "0.8125\n", + "0.9375\n", + "0.03125\n", + "0.09375\n", + "0.15625\n" ] } ], @@ -498,7 +261,29 @@ "using Plots\n", "#grad = Plots.ColorGradient([colorant\"blue\", RGB(0.7,0.85,0.7), colorant\"darkred\"], [0,0.5,1])\n", "#grad = Plots.ColorGradient(:bluesreds)\n", - "grad = Plots.ColorGradient([RGB(.95,.95,.95),RGB(.05,.05,.05)])\n", + "#grad = Plots.ColorGradient([RGB(.95,.95,.95),RGB(.05,.05,.05)])\n", + "\n", + "function darken(c, v=0.2)\n", + " rgb = RGB(c)\n", + " r = max(0, rgb.r - v)\n", + " g = max(0, rgb.g - v)\n", + " b = max(0, rgb.b - v)\n", + " RGB(r,g,b)\n", + "end\n", + "function lighten(c, v=0.2)\n", + " darken(c, -v)\n", + "end\n", + "\n", + "func = darken\n", + "grad = Plots.ColorGradient(map(func, [\n", + " colorant\"blue\",\n", + " colorant\"green\",\n", + " #colorant\"yellow\",\n", + " colorant\"orange\",\n", + " colorant\"red\",\n", + " colorant\"purple\",\n", + " ]))\n", + "\n", "#grad = Plots.ColorGradient([\n", "# colorant\"lightyellow\",\n", "# colorant\"orange\",\n", @@ -506,7 +291,56 @@ "# #colorant\"black\"\n", "# ])\n", "#grad = Plots.ColorGradient(:heat)\n", - "colors = RGB[Plots.getColor(grad, x) for x in 0:0.01:1]" + "\n", + "function getrange(n::Int)\n", + " zs = zeros(n)\n", + " offset = 0.0\n", + " baseoffset = 0.1\n", + " z = offset\n", + " chg = 0.5\n", + " for i in 1:n\n", + " zs[i] = z\n", + " \n", + " z += chg\n", + " if z > 1.0\n", + " offset += 0.1\n", + " if offset > 0.5\n", + " baseoffset *= 0.5\n", + " offset = baseoffset\n", + " end\n", + " z = offset\n", + "# chg *= 0.5\n", + " #z += chg\n", + " end\n", + " end\n", + " zs\n", + "end\n", + "\n", + "function getrange2(n::Int)\n", + " n > 0 || error()\n", + " n == 1 && return zeros(1)\n", + " zs = [0.0, 1.0]\n", + " for i in 3:n\n", + " sorted = sort(zs)\n", + " diffs = diff(sorted)\n", + " widestj = 0\n", + " widest = 0.0\n", + " for (j,d) in enumerate(diffs)\n", + " if d > widest\n", + " widest = d\n", + " widestj = j\n", + " end\n", + " end\n", + " push!(zs, sorted[widestj] + 0.5 * diffs[widestj])\n", + " end\n", + " zs\n", + "end\n", + "\n", + "#colors = RGB[Plots.getColorZ(grad,z) for z in 0:0.01:1]\n", + "#zs = map(z->z%1.0, 0:.285:3)\n", + "zs = getrange2(20)\n", + "map(println, zs)\n", + "colors = RGB[Plots.getColorZ(grad, z) for z in zs]" ] }, { diff --git a/src/args.jl b/src/args.jl index dd6ea2ea..6c6bd6db 100644 --- a/src/args.jl +++ b/src/args.jl @@ -309,7 +309,7 @@ end wraptuple(x::Tuple) = x wraptuple(x) = (x,) -trueOrAllTrue(f::Function, x::AVec) = all(f, x) +trueOrAllTrue(f::Function, x::AbstractArray) = all(f, x) trueOrAllTrue(f::Function, x) = f(x) function handleColors!(d::Dict, arg, csym::Symbol) @@ -336,6 +336,9 @@ function processAxisArg(d::Dict, axisletter::AbstractString, arg) d[symbol(axisletter * "flip")] = true # end + elseif T <: AbstractString + d[symbol(axisletter * "label")] = arg + # xlims/ylims elseif (T <: Tuple || T <: AVec) && length(arg) == 2 d[symbol(axisletter * "lims")] = arg @@ -492,21 +495,38 @@ function warnOnUnsupportedScales(pkg::PlottingPackage, d::Dict) end +# ----------------------------------------------------------------------------- + +# Tuples and 1-row matrices will give an element +# multi-row matrices will give a column +# anything else is returned as-is +getArgValue(v::Tuple, idx::Int) = v[mod1(idx, length(v))] +function getArgValue(v::AMat, idx::Int) + c = mod1(idx, size(v,2)) + size(v,1) == 1 ? v[1,c] : v[:,c] +end +getArgValue(v, idx) = v + + +# given an argument key (k), we want to extract the argument value for this index. +# if nothing is set (or container is empty), return the default. +function setDictValue(d::Dict, k::Symbol, idx::Int, defaults::Dict) + if haskey(d, k) && !(typeof(d[k]) <: Union{AbstractArray, Tuple} && isempty(d[k])) + d[k] = getArgValue(d[k], idx) + else + d[k] = defaults[k] + end +end + +# ----------------------------------------------------------------------------- + # build the argument dictionary for the plot function getPlotArgs(pkg::PlottingPackage, kw, idx::Int) d = Dict(kw) # add defaults? for k in keys(_plotDefaults) - if haskey(d, k) - v = d[k] - if isa(v, AbstractVector) && !isempty(v) - # we got a vector, cycling through - d[k] = autopick(v, idx) - end - else - d[k] = _plotDefaults[k] - end + setDictValue(d, k, idx, _plotDefaults) end for k in (:xscale, :yscale) @@ -517,7 +537,6 @@ function getPlotArgs(pkg::PlottingPackage, kw, idx::Int) # convert color handlePlotColors(pkg, d) - # d[:background_color] = getBackgroundRGBColor(d[:background_color], d) # no need for these delete!(d, :x) @@ -534,15 +553,7 @@ function getSeriesArgs(pkg::PlottingPackage, initargs::Dict, kw, commandIndex::I # add defaults? for k in keys(_seriesDefaults) - if haskey(d, k) - v = d[k] - if isa(v, AbstractVector) && !isempty(v) - # we got a vector, cycling through - d[k] = autopick(v, commandIndex) - end - else - d[k] = _seriesDefaults[k] - end + setDictValue(d, k, commandIndex, _seriesDefaults) end if haskey(_typeAliases, d[:linetype]) @@ -561,6 +572,11 @@ function getSeriesArgs(pkg::PlottingPackage, initargs::Dict, kw, commandIndex::I mc = (mc == :match ? d[:color] : getSeriesRGBColor(mc, initargs, plotIndex)) d[:markercolor] = mc + # update fillcolor + mc = d[:fillcolor] + mc = (mc == :match ? d[:color] : getSeriesRGBColor(mc, initargs, plotIndex)) + d[:fillcolor] = mc + # set label label = d[:label] label = (label == "AUTO" ? "y$globalIndex" : label) diff --git a/src/backends/gadfly.jl b/src/backends/gadfly.jl index 79d77450..c5ff5124 100644 --- a/src/backends/gadfly.jl +++ b/src/backends/gadfly.jl @@ -82,7 +82,7 @@ function createGadflyPlotObject(d::Dict) unshift!(gplt.guides, Gadfly.Guide.manual_color_key("", AbstractString[], Color[])) end - gplt.theme = Gadfly.Theme(background_color = d[:background_color]) + gplt.theme = Gadfly.Theme(background_color = getColor(d[:background_color])) gplt end @@ -111,12 +111,12 @@ end # serious hack (I think?) to draw my own shapes as annotations... will it work? who knows... -function getMarkerGeomsAndGuides(d::Dict) +function getMarkerGeomsAndGuides(d::Dict, initargs::Dict) marker = d[:markershape] if marker == :none && d[:linetype] != :ohlc return [],[] end - return [], [createGadflyAnnotation(d)] + return [], [createGadflyAnnotation(d, initargs)] end @@ -156,7 +156,7 @@ function addGadflySeries!(gplt, d::Dict, initargs::Dict) # set theme: color, line linewidth, and point size line_width = d[:linewidth] * (d[:linetype] in (:none, :ohlc, :scatter) ? 0 : 1) * Gadfly.px # 0 linewidth when we don't show a line # line_color = isa(d[:color], AbstractVector) ? colorant"black" : d[:color] - line_color = getColor(d[:color], 1) + line_color = getColor(d[:color]) # fg = initargs[:foreground_color] theme = Gadfly.Theme(; default_color = line_color, line_width = line_width, @@ -175,7 +175,7 @@ function addGadflySeries!(gplt, d::Dict, initargs::Dict) # add the annotation if dScatter[:markershape] != :none - push!(gplt.guides, createGadflyAnnotation(dScatter)) + push!(gplt.guides, createGadflyAnnotation(dScatter, initargs)) end elseif d[:linetype] in (:hline, :vline) @@ -222,7 +222,7 @@ function addGadflySeries!(gplt, d::Dict, initargs::Dict) if !isa(d[:markercolor], ColorGradient) d[:markercolor] = colorscheme(:bluesreds) end - push!(gplt.scales, Gadfly.Scale.ContinuousColorScale(p -> getColor(d[:markercolor], p, 1))) # minz + p * (maxz - minz)))) + push!(gplt.scales, Gadfly.Scale.ContinuousColorScale(p -> getColorZ(d[:markercolor], p))) # minz + p * (maxz - minz)))) # nothing special... else @@ -264,7 +264,7 @@ function addGadflySeries!(gplt, d::Dict, initargs::Dict) # end # handle markers - geoms, guides = getMarkerGeomsAndGuides(d) + geoms, guides = getMarkerGeomsAndGuides(d, initargs) append!(gfargs, geoms) append!(gplt.guides, guides) @@ -282,7 +282,7 @@ function addGadflySeries!(gplt, d::Dict, initargs::Dict) # Should ensure from this side that colors which are the same are merged together push!(guide.labels, d[:label]) - push!(guide.colors, getColor(d[d[:markershape] == :none ? :color : :markercolor], 1)) + push!(guide.colors, getColor(d[d[:markershape] == :none ? :color : :markercolor])) end # end end @@ -339,7 +339,7 @@ function getGadflyScaleFunction(d::Dict, isx::Bool) scale = d[scalekey] scale == :log && return isx ? Gadfly.Scale.x_log : Gadfly.Scale.y_log, hasScaleKey scale == :log2 && return isx ? Gadfly.Scale.x_log2 : Gadfly.Scale.y_log2, hasScaleKey - scale == :log10 && return isx ? Gadfly.Scale.x_log2 : Gadfly.Scale.y_log10, hasScaleKey + scale == :log10 && return isx ? Gadfly.Scale.x_log10 : Gadfly.Scale.y_log10, hasScaleKey scale == :asinh && return isx ? Gadfly.Scale.x_asinh : Gadfly.Scale.y_asinh, hasScaleKey scale == :sqrt && return isx ? Gadfly.Scale.x_sqrt : Gadfly.Scale.y_sqrt, hasScaleKey end diff --git a/src/backends/gadfly_shapes.jl b/src/backends/gadfly_shapes.jl index 27b7df6e..9388b238 100644 --- a/src/backends/gadfly_shapes.jl +++ b/src/backends/gadfly_shapes.jl @@ -4,7 +4,7 @@ # using Compose: x_measure, y_measure -function createGadflyAnnotation(d::Dict) +function createGadflyAnnotation(d::Dict, initargs::Dict) sz = [d[:markersize] * Gadfly.px] x, y = d[:x], d[:y] @@ -14,7 +14,7 @@ function createGadflyAnnotation(d::Dict) shape = ohlcshape(x, y, d[:markersize]) d[:y] = Float64[z.open for z in y] d[:linetype] = :none - return Gadfly.Guide.annotation(Gadfly.compose(Gadfly.context(), shape, Gadfly.fill(nothing), Gadfly.stroke(getColor(d[:color],1)))) + return Gadfly.Guide.annotation(Gadfly.compose(Gadfly.context(), shape, Gadfly.fill(nothing), Gadfly.stroke(getColor(d[:color])))) elseif marker == :rect shape = square(x, y, sz) @@ -54,7 +54,7 @@ function createGadflyAnnotation(d::Dict) shape = Gadfly.circle(xs,ys,[sz]) end - Gadfly.Guide.annotation(Gadfly.compose(Gadfly.context(), shape, Gadfly.fill(getColorVector(d[:markercolor])), Gadfly.stroke(colorant"white"))) + Gadfly.Guide.annotation(Gadfly.compose(Gadfly.context(), shape, Gadfly.fill(getColorVector(d[:markercolor])), Gadfly.stroke(getColor(initargs[:foreground_color])))) end diff --git a/src/backends/winston.jl b/src/backends/winston.jl index d4d5470a..9c304a9f 100644 --- a/src/backends/winston.jl +++ b/src/backends/winston.jl @@ -36,7 +36,8 @@ supportedArgs(::WinstonPackage) = [ # :axis, # :background_color, :color, - # :fill, + :fillrange, + :fillcolor, # :foreground_color, :group, # :heatmap_c, @@ -152,12 +153,12 @@ function plot!(::WinstonPackage, plt::Plot; kw...) x, y = d[:x], d[:y] Winston.add(wplt, Winston.Curve(x, y; e...)) - fillto = d[:fill] - if fillto != nothing - if isa(fillto, AbstractVector) - y2 = fillto + fillrange = d[:fillrange] + if fillrange != nothing + if isa(fillrange, AbstractVector) + y2 = fillrange else - y2 = Float64[fillto for yi in y] + y2 = Float64[fillrange for yi in y] end Winston.add(wplt, Winston.FillBetween(x, y, x, y2, fillcolor=d[:color])) end diff --git a/src/colors.jl b/src/colors.jl index ecc537e2..92796f7b 100644 --- a/src/colors.jl +++ b/src/colors.jl @@ -69,6 +69,17 @@ const _masterColorList = [ ] +function darken(c, v=0.1) + rgb = RGB(c) + r = max(0, rgb.r - v) + g = max(0, rgb.g - v) + b = max(0, rgb.b - v) + RGB(r,g,b) +end +function lighten(c, v=0.1) + darken(c, -v) +end + # -------------------------------------------------------------- @@ -80,9 +91,10 @@ convertColor(cvec::AbstractVector) = map(convertColor, cvec) abstract ColorScheme -getColor(scheme::ColorScheme, idx::Integer) = getColor(scheme, 0.0, idx) -getColor(scheme::ColorScheme, z::AbstractFloat) = getColor(scheme, z, 0) -getColorVector(scheme::ColorScheme) = [getColor(scheme, 0.0, 1)] +getColor(scheme::ColorScheme) = getColor(scheme, 1) +# getColor(scheme::ColorScheme, idx::Integer) = getColor(scheme, 0.0, idx) +# getColorZ(scheme::ColorScheme, z::AbstractFloat) = getColor(scheme, z, 0) +getColorVector(scheme::ColorScheme) = [getColor(scheme)] colorscheme(scheme::ColorScheme) = scheme colorscheme(s::Symbol) = haskey(_gradients, s) ? ColorGradient(s) : ColorWrapper(convertColor(s)) @@ -94,6 +106,7 @@ colorscheme(v::AVec) = ColorVector(v) colorscheme(m::AMat) = Any[ColorVector(m[:,i]) for i in 1:size(m,2)] colorscheme(c::Colorant) = ColorWrapper(c) +const _rainbowColors = [colorant"blue", colorant"purple", colorant"green", colorant"orange", colorant"red"] const _gradients = Dict( :blues => [colorant"lightblue", colorant"darkblue"], @@ -103,6 +116,9 @@ const _gradients = Dict( :bluesreds => [colorant"darkblue", RGB(0.8,0.85,0.8), colorant"darkred"], :heat => [colorant"lightyellow", colorant"orange", colorant"darkred"], :grays => [RGB(.95,.95,.95),RGB(.05,.05,.05)], + :rainbow => _rainbowColors, + :lightrainbow => map(lighten, _rainbowColors), + :darkrainbow => map(darken, _rainbowColors), ) # -------------------------------------------------------------- @@ -131,8 +147,9 @@ function ColorGradient{T<:Real}(s::Symbol, vals::AVec{T} = 0:1) ColorGradient(cs, vals) end +getColor(gradient::ColorGradient, idx::Int) = gradient.cs[mod1(idx, length(gradient.cs))] -function getColor(gradient::ColorGradient, z::Real, idx::Int) +function getColorZ(gradient::ColorGradient, z::Real) cs = gradient.colors vs = gradient.values n = length(cs) @@ -154,7 +171,7 @@ function getColor(gradient::ColorGradient, z::Real, idx::Int) cs[end] end -getColorVector(gradient::ColorGradient) = [gradient.colors[1]] +getColorVector(gradient::ColorGradient) = gradient.colors function interpolate_lab(c1::Colorant, c2::Colorant, w::Real) lab1 = Lab(c1) @@ -181,26 +198,31 @@ end # -------------------------------------------------------------- -"Wraps a function, taking a z-value and index and returning a Colorant" +"Wraps a function, taking an index and returning a Colorant" immutable ColorFunction <: ColorScheme f::Function end -typealias CFun ColorFunction +getColor(scheme::ColorFunction, idx::Int) = scheme.f(idx) -getColor(scheme::ColorFunction, z::Real, idx::Int) = scheme.f(z, idx) +# -------------------------------------------------------------- + +"Wraps a function, taking an z-value and returning a Colorant" +immutable ColorZFunction <: ColorScheme + f::Function +end + +getColorZ(scheme::ColorFunction, z::Real) = scheme.f(z) # -------------------------------------------------------------- "Wraps a vector of colors... may be vector of Symbol/String/Colorant" immutable ColorVector <: ColorScheme v::Vector{Colorant} - ColorVector(v::AVec) = convertColor(v) + ColorVector(v::AVec) = new(convertColor(v)) end -typealias CVec ColorVector - -getColor(scheme::ColorVector, z::Real, idx::Int) = convertColor(scheme.v[mod1(idx, length(scheme.v))]) +getColor(scheme::ColorVector, idx::Int) = convertColor(scheme.v[mod1(idx, length(scheme.v))]) getColorVector(scheme::ColorVector) = scheme.v @@ -211,9 +233,8 @@ immutable ColorWrapper{C<:Colorant} <: ColorScheme c::C end -typealias CWrap ColorWrapper - -getColor(scheme::ColorWrapper, z::Real, idx::Int) = scheme.c +getColor(scheme::ColorWrapper, idx::Int) = scheme.c +getColorZ(scheme::ColorWrapper, z::Real) = scheme.c # -------------------------------------------------------------- @@ -276,6 +297,37 @@ function getPaletteUsingColorDiffFromBackground(bgcolor::Colorant, numcolors::In filter(c -> colordiff(c, bgcolor) >= mindiff, _allColors) end +function getPaletteUsingRainbow(bgcolor::Colorant, numcolors::Int = _defaultNumColors) + grad = ColorGradient(_gradients[isdark(bgcolor) ? :lightrainbow : :darkrainbow]) + zrng = getpctrange(numcolors) + RGB[getColorZ(grad, z) for z in zrng] +end + +# ---------------------------------------------------------------------------------- + + +function getpctrange(n::Int) + n > 0 || error() + n == 1 && return zeros(1) + zs = [0.0, 1.0] + for i in 3:n + sorted = sort(zs) + diffs = diff(sorted) + widestj = 0 + widest = 0.0 + for (j,d) in enumerate(diffs) + if d > widest + widest = d + widestj = j + end + end + push!(zs, sorted[widestj] + 0.5 * diffs[widestj]) + end + zs +end + +# ---------------------------------------------------------------------------------- + # TODO: try to use the algorithms from https://github.com/timothyrenner/ColorBrewer.jl # TODO: allow the setting of the algorithm, either by passing a symbol (:colordiff, :fixed, etc) or a function? @@ -289,22 +341,29 @@ function handlePlotColors(::PlottingPackage, d::Dict) warn("Cannot set background_color with backend $(backend())") end end - d[:background_color] = bgcolor # d[:color_palette] = getPaletteUsingDistinguishableColors(bgcolor) # d[:color_palette] = getPaletteUsingFixedColorList(bgcolor) - d[:color_palette] = getPaletteUsingColorDiffFromBackground(bgcolor) + # d[:color_palette] = getPaletteUsingColorDiffFromBackground(bgcolor) + d[:color_palette] = getPaletteUsingRainbow(bgcolor) # set the foreground color (text, ticks, gridlines) to be white or black depending # on how dark the background is. - if !haskey(d, :foreground_color) || d[:foreground_color] == :auto - d[:foreground_color] = isdark(bgcolor) ? colorant"white" : colorant"black" + fgcolor = get(d, :foreground_color, :auto) + if fgcolor == :auto + fgcolor = isdark(bgcolor) ? colorant"white" : colorant"black" else - d[:foreground_color] = convertColor(d[:foreground_color]) + fgcolor = convertColor(fgcolor) end + # if !haskey(d, :foreground_color) || d[:foreground_color] == :auto + # d[:foreground_color] = isdark(bgcolor) ? colorant"white" : colorant"black" + # else + # d[:foreground_color] = convertColor(d[:foreground_color]) + # end # bgcolor - d[:background_color] = bgcolor + d[:background_color] = colorscheme(bgcolor) + d[:foreground_color] = colorscheme(fgcolor) end # converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically @@ -312,9 +371,6 @@ function getSeriesRGBColor(c, d::Dict, n::Int) if c == :auto c = autopick(d[:color_palette], n) - # else - # # c = convertColor(c) - # c = colorscheme(c) end # c should now be a subtype of ColorScheme diff --git a/src/plot.jl b/src/plot.jl index aa63f1a1..f8f549d5 100644 --- a/src/plot.jl +++ b/src/plot.jl @@ -47,9 +47,7 @@ function plot(args...; kw...) d = Dict(kw) preprocessArgs!(d) - println() - for k in sort(collect(keys(d))); @printf("%14s: ", k); println(d[k]); end - println() + dumpdict(d, "After plot preprocessing") # # ensure we're passing in an RGB # if haskey(d, :background_color) @@ -86,6 +84,8 @@ function plot!(plt::Plot, args...; kw...) d = Dict(kw) preprocessArgs!(d) + dumpdict(d, "After plot! preprocessing") + warnOnUnsupportedArgs(plt.backend, d) # handle a "group by" mechanism. @@ -108,6 +108,8 @@ function plot!(plt::Plot, args...; kw...) setTicksFromStringVector(d, di, :x, :xticks) setTicksFromStringVector(d, di, :y, :yticks) + dumpdict(di, "Series $i") + plot!(plt.backend, plt; di...) end @@ -115,6 +117,8 @@ function plot!(plt::Plot, args...; kw...) warnOnUnsupportedScales(plt.backend, d) + dumpdict(d, "Updating plot items:") + # add title, axis labels, ticks, etc updatePlotItems(plt, d) current(plt) diff --git a/src/utils.jl b/src/utils.jl index 69c77418..21f03b6c 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -104,6 +104,8 @@ function sticksHack(; kw...) dLine, dScatter end +get_mod(v, idx::Int) = v[mod1(idx, length(v))] + makevec(v::AVec) = v makevec{T}(v::T) = T[v] @@ -143,8 +145,31 @@ ticksType(ticks) = :invalid limsType{T<:Real,S<:Real}(lims::Tuple{T,S}) = :limits limsType(lims) = :invalid + # --------------------------------------------------------------- +type DebugMode + on::Bool +end +const _debugMode = DebugMode(false) + + +function dumpdict(d::Dict, prefix = "") + _debugMode.on || return + println() + if prefix != "" + println(prefix, ":") + end + for k in sort(collect(keys(d))) + @printf("%14s: ", k) + println(d[k]) + end + println() +end + +# --------------------------------------------------------------- + + # push/append/clear/set the underlying plot data # NOTE: backends should implement the getindex and setindex! methods to get/set the x/y data objects