diff --git a/src/arg_desc.jl b/src/arg_desc.jl index e2e4247e..ba0b1200 100644 --- a/src/arg_desc.jl +++ b/src/arg_desc.jl @@ -27,7 +27,8 @@ const _arg_desc = KW( :x => "Various. Input data. First Dimension", :y => "Various. Input data. Second Dimension", :z => "Various. Input data. Third Dimension. May be wrapped by a `Surface` for surface and heatmap types.", -:marker_z => "AbstractVector. z-values for each series data point, which correspond to the color to be used from a markercolor gradient.", +:marker_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or nothing. z-values for each series data point, which correspond to the color to be used from a markercolor gradient.", +:line_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or nothing. z-values for each series line segment, which correspond to the color to be used from a linecolor gradient. Note that for N points, only the first N-1 values are used (one per line-segment).", :levels => "Integer, NTuple{2,Integer}. Number of levels (or x-levels/y-levels) for a contour type.", :orientation => "Symbol. Horizontal or vertical orientation for bar types. Values `:h`, `:hor`, `:horizontal` correspond to horizontal (sideways, anchored to y-axis), and `:v`, `:vert`, and `:vertical` correspond to vertical (the default).", :bar_position => "Symbol. Choose from `:overlay` (default), `:stack`. (warning: May not be implemented fully)", diff --git a/src/args.jl b/src/args.jl index 495232be..bbbda4f3 100644 --- a/src/args.jl +++ b/src/args.jl @@ -163,6 +163,7 @@ const _series_defaults = KW( :y => nothing, :z => nothing, # depth for contour, surface, etc :marker_z => nothing, # value for color scale + :line_z => nothing, :levels => 15, :orientation => :vertical, :bar_position => :overlay, # for bar plots and histograms: could also be stack (stack up) or dodge (side by side) @@ -375,7 +376,8 @@ add_aliases(:linestyle, :style, :s, :ls) add_aliases(:marker, :m, :mark) add_aliases(:markershape, :shape) add_aliases(:markersize, :ms, :msize) -add_aliases(:marker_z, :markerz, :zcolor) +add_aliases(:marker_z, :markerz, :zcolor, :mz) +add_aliases(:line_z, :linez, :zline, :lz) add_aliases(:fill, :f, :area) add_aliases(:fillrange, :fillrng, :frange, :fillto, :fill_between) add_aliases(:group, :g, :grouping) diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index 7c25ab26..4d554250 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -18,7 +18,9 @@ supported_args(::PyPlotBackend) = merge_with_base_supported([ :guide, :lims, :ticks, :scale, :flip, :rotation, :tickfont, :guidefont, :legendfont, :grid, :legend, :colorbar, - :marker_z, :levels, + :marker_z, + :line_z, + :levels, :ribbon, :quiver, :arrow, :orientation, :overwrite_figure, @@ -61,6 +63,7 @@ function _initialize_backend(::PyPlotBackend) const pycmap = PyPlot.pywrap(PyPlot.pyimport("matplotlib.cm")) const pynp = PyPlot.pywrap(PyPlot.pyimport("numpy")) const pytransforms = PyPlot.pywrap(PyPlot.pyimport("matplotlib.transforms")) + const pycollections = PyPlot.pywrap(PyPlot.pyimport("matplotlib.collections")) end # we don't want every command to update the figure @@ -406,17 +409,37 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series) # line plot if st in (:path, :path3d, :steppre, :steppost) if d[:linewidth] > 0 - handle = ax[:plot](xyargs...; - label = d[:label], - zorder = plt.n, - color = py_linecolor(d), - linewidth = d[:linewidth], - linestyle = py_linestyle(st, d[:linestyle]), - solid_capstyle = "round", - # dash_capstyle = "round", - drawstyle = py_stepstyle(st) - )[1] - push!(handles, handle) + if d[:line_z] == nothing + handle = ax[:plot](xyargs...; + label = d[:label], + zorder = plt.n, + color = py_linecolor(d), + linewidth = d[:linewidth], + linestyle = py_linestyle(st, d[:linestyle]), + solid_capstyle = "round", + drawstyle = py_stepstyle(st) + )[1] + push!(handles, handle) + + else + # multicolored line segments + n = length(x) - 1 + segments = Array(Any,n) + for i=1:n + segments[i] = [(cycle(x,i), cycle(y,i)), (cycle(x,i+1), cycle(y,i+1))] + end + lc = pycollections.LineCollection(segments; + label = d[:label], + zorder = plt.n, + cmap = py_linecolormap(d), + linewidth = d[:linewidth], + linestyle = py_linestyle(st, d[:linestyle]) + ) + lc[:set_array](d[:line_z]) + handle = ax[:add_collection](lc) + push!(handles, handle) + needs_colorbar = true + end a = d[:arrow] if a != nothing && !is3d(st) # TODO: handle 3d later @@ -493,7 +516,7 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series) extrakw[:c] = convert(Vector{Float64}, d[:marker_z]) extrakw[:cmap] = py_markercolormap(d) clims = sp[:clims] - if isa(clims, Tuple) && length(clims) == 2 + if is_2tuple(clims) isfinite(clims[1]) && (extrakw[:vmin] = clims[1]) isfinite(clims[2]) && (extrakw[:vmax] = clims[2]) end @@ -542,7 +565,7 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series) if st == :histogram2d clims = sp[:clims] - if isa(clims, Tuple) && length(clims) == 2 + if is_2tuple(clims) isfinite(clims[1]) && (extrakw[:vmin] = clims[1]) isfinite(clims[2]) && (extrakw[:vmax] = clims[2]) end @@ -567,7 +590,7 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series) if st == :hexbin clims = sp[:clims] - if isa(clims, Tuple) && length(clims) == 2 + if is_2tuple(clims) isfinite(clims[1]) && (extrakw[:vmin] = clims[1]) isfinite(clims[2]) && (extrakw[:vmax] = clims[2]) end @@ -602,7 +625,7 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series) needs_colorbar = true clims = sp[:clims] - if isa(clims, Tuple) && length(clims) == 2 + if is_2tuple(clims) isfinite(clims[1]) && (extrakw[:vmin] = clims[1]) isfinite(clims[2]) && (extrakw[:vmax] = clims[2]) end @@ -648,7 +671,7 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series) extrakw[:facecolors] = py_shading(d[:fillcolor], d[:marker_z], d[:fillalpha]) extrakw[:shade] = false clims = sp[:clims] - if isa(clims, Tuple) && length(clims) == 2 + if is_2tuple(clims) isfinite(clims[1]) && (extrakw[:vmin] = clims[1]) isfinite(clims[2]) && (extrakw[:vmax] = clims[2]) end @@ -690,7 +713,7 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series) elseif typeof(z) <: AbstractVector # tri-surface plot (http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#tri-surface-plots) clims = sp[:clims] - if isa(clims, Tuple) && length(clims) == 2 + if is_2tuple(clims) isfinite(clims[1]) && (extrakw[:vmin] = clims[1]) isfinite(clims[2]) && (extrakw[:vmax] = clims[2]) end @@ -744,7 +767,7 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series) end clims = sp[:clims] - if isa(clims, Tuple) && length(clims) == 2 + if is_2tuple(clims) isfinite(clims[1]) && (extrakw[:vmin] = clims[1]) isfinite(clims[2]) && (extrakw[:vmax] = clims[2]) end diff --git a/src/plot.jl b/src/plot.jl index 3de99120..f1e82a0b 100644 --- a/src/plot.jl +++ b/src/plot.jl @@ -272,7 +272,12 @@ function _plot!(plt::Plot, d::KW, args...) # map marker_z if it's a Function if isa(get(kw, :marker_z, nothing), Function) # TODO: should this take y and/or z as arguments? - kw[:marker_z] = map(kw[:marker_z], kw[:x]) + kw[:marker_z] = map(kw[:marker_z], kw[:x], kw[:y], kw[:z]) + end + + # map line_z if it's a Function + if isa(get(kw, :line_z, nothing), Function) + kw[:line_z] = map(kw[:line_z], kw[:x], kw[:y], kw[:z]) end # convert a ribbon into a fillrange diff --git a/test/imgcomp.jl b/test/imgcomp.jl index 71839547..6d43acfd 100644 --- a/test/imgcomp.jl +++ b/test/imgcomp.jl @@ -22,7 +22,7 @@ default(size=(500,300)) # TODO: use julia's Condition type and the wait() and notify() functions to initialize a Window, then wait() on a condition that # is referenced in a button press callback (the button clicked callback will call notify() on that condition) -const _current_plots_version = v"0.7.1" +const _current_plots_version = v"0.7.2" function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)