Compare commits

...

16 Commits

Author SHA1 Message Date
Thomas Breloff 73af635be6 bump version 2016-07-19 11:17:27 -04:00
Thomas Breloff c1c97a5fc1 labelfunc for pyplot 2016-07-19 10:32:23 -04:00
Thomas Breloff 6a97dc8825 extrema fix for bool/nothing 2016-07-18 16:19:58 -04:00
Thomas Breloff d832f34733 travis 2016-07-18 15:53:02 -04:00
Thomas Breloff 859a600b92 travis 2016-07-18 15:38:07 -04:00
Thomas Breloff 6b3cf50f38 travis 2016-07-18 15:21:51 -04:00
Thomas Breloff 3ccd1bc368 added Showoff dependency; fix PLOTS_DEFAULTS; fix auto fill to 0; fix scale warning; tick labels with Showoff; fontsize in GR; travis deps 2016-07-18 15:00:02 -04:00
Thomas Breloff 624d33b96f Merge branch 'master' into dev 2016-07-18 09:13:46 -04:00
Tom Breloff 156f61f526 Merge pull request #391 from odow/axisstyle
pass axis style to PGFPlots
2016-07-16 13:13:59 -04:00
odow fd3a04fce3 pass axis style to PGFPlots 2016-07-16 16:48:46 +12:00
Thomas Breloff 62be4e1274 scalefunc fix and cleanup 2016-07-15 09:09:46 -04:00
Thomas Breloff db096196c9 Merge branch 'master' into dev 2016-07-14 15:47:40 -04:00
Thomas Breloff a0ac70be3c axes log scales, colors, ticks, and more; proper 2D axis drawing in GR 2016-07-14 15:46:44 -04:00
Tom Breloff 1f0f89f83c Merge pull request #389 from odow/patch-1
Legend location in subplot
2016-07-14 13:52:37 -04:00
Thomas Breloff ee706ad8c7 axis_drawing_info and get_ticks; parameterized Segments; GR manual drawing of 2D axes 2016-07-13 16:31:21 -04:00
Oscar Dowson ce31ea8bf3 Legend location in subplot 2016-07-13 10:01:48 +12:00
14 changed files with 283 additions and 86 deletions
+1
View File
@@ -6,3 +6,4 @@ Reexport
Compat
FixedSizeArrays
Measures
Showoff
+4 -12
View File
@@ -11,6 +11,7 @@ using FixedSizeArrays
@reexport using RecipesBase
using Base.Meta
@reexport using PlotUtils
import Showoff
export
AbstractPlot,
@@ -81,16 +82,6 @@ export
arrow,
Segments,
# colorscheme,
# ColorScheme,
# ColorGradient,
# ColorVector,
# ColorWrapper,
# ColorFunction,
# ColorZFunction,
# getColor,
# getColorZ,
debugplots,
supported_args,
@@ -109,6 +100,7 @@ export
test_examples,
iter_segments,
coords,
translate,
translate!,
@@ -245,8 +237,8 @@ function __init__()
setup_ijulia()
setup_atom()
if haskey(ENV, "PLOTS_DEFAULTS")
for (k,v) in eval(parse(ENV["PLOTS_DEFAULTS"]))
if isdefined(Main, :PLOTS_DEFAULTS)
for (k,v) in Main.PLOTS_DEFAULTS
default(k, v)
end
end
+7 -6
View File
@@ -585,15 +585,15 @@ end
function processFillArg(d::KW, arg)
fr = get(d, :fillrange, 0)
# fr = get(d, :fillrange, 0)
if typeof(arg) <: Brush
arg.size == nothing || (fr = arg.size)
arg.size == nothing || (d[:fillrange] = arg.size)
arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : plot_color(arg.color))
arg.alpha == nothing || (d[:fillalpha] = arg.alpha)
# fillrange function
elseif allFunctions(arg)
fr = arg
d[:fillrange] = arg
# fillalpha
elseif allAlphas(arg)
@@ -601,9 +601,9 @@ function processFillArg(d::KW, arg)
elseif !handleColors!(d, arg, :fillcolor)
fr = arg
d[:fillrange] = arg
end
d[:fillrange] = fr
# d[:fillrange] = fr
return
end
@@ -778,7 +778,7 @@ function warnOnUnsupported(pkg::AbstractBackend, d::KW)
end
function warnOnUnsupported_scales(pkg::AbstractBackend, d::KW)
for k in (:xscale, :yscale, :zscale)
for k in (:xscale, :yscale, :zscale, :scale)
if haskey(d, k)
v = d[k]
v = get(_scaleAliases, v, v)
@@ -805,6 +805,7 @@ function convertLegendValue(val::Symbol)
end
convertLegendValue(val::Bool) = val ? :best : :none
convertLegendValue(val::Void) = :none
convertLegendValue(v::AbstractArray) = map(convertLegendValue, v)
# -----------------------------------------------------------------------------
+140 -13
View File
@@ -117,22 +117,97 @@ Base.setindex!(axis::Axis, v, ks::Symbol...) = setindex!(axis.d, v, ks...)
Base.haskey(axis::Axis, k::Symbol) = haskey(axis.d, k)
Base.extrema(axis::Axis) = (ex = axis[:extrema]; (ex.emin, ex.emax))
# get discrete ticks, or not
function get_ticks(axis::Axis)
ticks = axis[:ticks]
dvals = axis[:discrete_values]
if !isempty(dvals) && ticks == :auto
cv, dv = axis[:continuous_values], dvals
# TODO: better/smarter cutoff values for sampling ticks
if length(cv) > 30
rng = Int[round(Int,i) for i in linspace(1, length(cv), 15)]
cv[rng], dv[rng]
else
cv, dv
end
const _scale_funcs = Dict{Symbol,Function}(
:log10 => log10,
:log2 => log2,
:ln => log,
)
const _inv_scale_funcs = Dict{Symbol,Function}(
:log10 => exp10,
:log2 => exp2,
:ln => exp,
)
const _label_func = Dict{Symbol,Function}(
:log10 => x -> "10^$x",
:log2 => x -> "2^$x",
:ln => x -> "e^$x",
)
scalefunc(scale::Symbol) = x -> get(_scale_funcs, scale, identity)(Float64(x))
invscalefunc(scale::Symbol) = x -> get(_inv_scale_funcs, scale, identity)(Float64(x))
labelfunc(scale::Symbol, backend::AbstractBackend) = get(_label_func, scale, string)
function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
lims = axis_limits(axis)
# scale the limits
scale = axis[:scale]
scaled_lims = map(scalefunc(scale), lims)
# @show lims scaled_lims
# get a list of well-laid-out ticks
cv = if ticks == nothing
optimize_ticks(scaled_lims...,
k_min = 5, # minimum number of ticks
k_max = 8, # maximum number of ticks
# span_buffer = 0.0 # padding buffer in case nice ticks are closeby
)[1]
else
ticks
end
# # expand to ensure we see all the ticks
# expand_extrema!(axis, cv)
# rescale and return values and labels
# @show cv
ticklabels = if any(isfinite, cv)
map(labelfunc(scale, backend()), Showoff.showoff(cv, :plain))
else
UTF8String[]
end
tickvals = map(invscalefunc(scale), cv)
# @show tickvals ticklabels
# ticklabels = Showoff.showoff(tickvals, scale == :log10 ? :scientific : :auto)
tickvals, ticklabels
# basestr = scale == :log10 ? "10^" : scale == :log2 ? "2^" : scale == :ln ? "e^" : ""
# tickvals, ["$basestr$cvi" for cvi in cv]
end
# return (continuous_values, discrete_values) for the ticks on this axis
function get_ticks(axis::Axis)
ticks = axis[:ticks]
ticks in (nothing, false) && return nothing
dvals = axis[:discrete_values]
cv, dv = if !isempty(dvals) && ticks == :auto
# discrete ticks...
axis[:continuous_values], dvals
elseif ticks == :auto
# compute optimal ticks and labels
optimal_ticks_and_labels(axis)
elseif typeof(ticks) <: AVec
# override ticks, but get the labels
optimal_ticks_and_labels(axis, ticks)
elseif typeof(ticks) <: NTuple{2}
# assuming we're passed (ticks, labels)
ticks
else
error("Unknown ticks type in get_ticks: $(typeof(ticks))")
end
# @show ticks dvals cv dv
# TODO: better/smarter cutoff values for sampling ticks
if length(cv) > 30
rng = Int[round(Int,i) for i in linspace(1, length(cv), 15)]
cv[rng], dv[rng]
else
cv, dv
end
end
# -------------------------------------------------------------------------
@@ -146,6 +221,12 @@ end
function expand_extrema!(axis::Axis, v::Number)
expand_extrema!(axis[:extrema], v)
end
# these shouldn't impact the extrema
expand_extrema!(axis::Axis, ::Void) = axis[:extrema]
expand_extrema!(axis::Axis, ::Bool) = axis[:extrema]
function expand_extrema!{MIN<:Number,MAX<:Number}(axis::Axis, v::Tuple{MIN,MAX})
ex = axis[:extrema]
ex.emin = min(v[1], ex.emin)
@@ -339,3 +420,49 @@ function pie_labels(sp::Subplot, series::Series)
d[:x]
end
end
# -------------------------------------------------------------------------
# compute the line segments which should be drawn for this axis
function axis_drawing_info(sp::Subplot)
xaxis, yaxis = sp[:xaxis], sp[:yaxis]
xmin, xmax = axis_limits(xaxis)
ymin, ymax = axis_limits(yaxis)
xticks = get_ticks(xaxis)
yticks = get_ticks(yaxis)
spine_segs = Segments(2)
grid_segs = Segments(2)
if !(xaxis[:ticks] in (nothing, false))
f = scalefunc(yaxis[:scale])
invf = invscalefunc(yaxis[:scale])
t1 = invf(f(ymin) + 0.015*(f(ymax)-f(ymin)))
t2 = invf(f(ymax) - 0.015*(f(ymax)-f(ymin)))
push!(spine_segs, (xmin,ymin), (xmax,ymin)) # bottom spine
push!(spine_segs, (xmin,ymax), (xmax,ymax)) # top spine
for xtick in xticks[1]
push!(spine_segs, (xtick, ymin), (xtick, t1)) # bottom tick
push!(grid_segs, (xtick, t1), (xtick, t2)) # vertical grid
push!(spine_segs, (xtick, ymax), (xtick, t2)) # top tick
end
end
if !(yaxis[:ticks] in (nothing, false))
f = scalefunc(xaxis[:scale])
invf = invscalefunc(xaxis[:scale])
t1 = invf(f(xmin) + 0.015*(f(xmax)-f(xmin)))
t2 = invf(f(xmax) - 0.015*(f(xmax)-f(xmin)))
push!(spine_segs, (xmin,ymin), (xmin,ymax)) # left spine
push!(spine_segs, (xmax,ymin), (xmax,ymax)) # right spine
for ytick in yticks[1]
push!(spine_segs, (xmin, ytick), (t1, ytick)) # left tick
push!(grid_segs, (t1, ytick), (t2, ytick)) # horizontal grid
push!(spine_segs, (xmax, ytick), (t2, ytick)) # right tick
end
end
xticks, yticks, spine_segs, grid_segs
end
+53 -18
View File
@@ -328,15 +328,15 @@ end
const _gr_point_mult = zeros(1)
# set the font attributes... assumes _gr_point_mult has been populated already
function gr_set_font(f::Font)
function gr_set_font(f::Font; halign = f.halign, valign = f.valign, color = f.color)
family = lowercase(f.family)
GR.setcharheight(_gr_point_mult[1] * f.pointsize)
GR.setcharup(sin(f.rotation), cos(f.rotation))
if haskey(gr_font_family, family)
GR.settextfontprec(100 + gr_font_family[family], GR.TEXT_PRECISION_STRING)
end
gr_set_textcolor(f.color)
GR.settextalign(gr_halign[f.halign], gr_valign[f.valign])
gr_set_textcolor(color)
GR.settextalign(gr_halign[halign], gr_valign[valign])
end
# --------------------------------------------------------------------------------------
@@ -448,7 +448,7 @@ function gr_display(plt::Plot)
# update point mult
px_per_pt = px / pt
_gr_point_mult[1] = px_per_pt / h
_gr_point_mult[1] = 1.5 * px_per_pt / max(h,w)
# subplots:
for sp in plt.subplots
@@ -561,26 +561,61 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
gr_polaraxes(rmin, rmax)
elseif draw_axes
if xmax > xmin && ymax > ymin
GR.setwindow(xmin, xmax, ymin, ymax)
end
xticks, yticks, spine_segs, grid_segs = axis_drawing_info(sp)
# @show xticks yticks #spine_segs grid_segs
# draw the grid lines
# TODO: control line style/width
# GR.setlinetype(GR.LINETYPE_DOTTED)
if sp[:grid]
gr_set_linecolor(sp[:foreground_color_grid])
GR.grid(xtick, ytick, 0, 0, majorx, majory)
# gr_set_linecolor(sp[:foreground_color_grid])
# GR.grid(xtick, ytick, 0, 0, majorx, majory)
gr_set_line(1, :dot, sp[:foreground_color_grid])
GR.settransparency(0.5)
gr_polyline(coords(grid_segs)...)
end
GR.settransparency(1.0)
# spine (border) and tick marks
gr_set_line(1, :solid, sp[:xaxis][:foreground_color_axis])
gr_polyline(coords(spine_segs)...)
if !(xticks in (nothing, false))
# x labels
flip = sp[:yaxis][:flip]
gr_set_font(sp[:xaxis][:tickfont], valign = :top, color = sp[:xaxis][:foreground_color_axis])
for (cv, dv) in zip(xticks...)
xi, yi = GR.wctondc(cv, flip ? ymax : ymin)
# @show cv dv ymin xi yi
gr_text(xi, yi-0.01, string(dv))
end
end
window_diag = sqrt(gr_view_xdiff()^2 + gr_view_ydiff()^2)
ticksize = 0.0075 * window_diag
if outside_ticks
ticksize = -ticksize
if !(yticks in (nothing, false))
# y labels
flip = sp[:xaxis][:flip]
gr_set_font(sp[:yaxis][:tickfont], halign = :right, color = sp[:yaxis][:foreground_color_axis])
for (cv, dv) in zip(yticks...)
xi, yi = GR.wctondc(flip ? xmax : xmin, cv)
# @show cv dv xmin xi yi
gr_text(xi-0.01, yi, string(dv))
end
end
# TODO: this should be done for each axis separately
gr_set_linecolor(xaxis[:foreground_color_axis])
x1, x2 = xaxis[:flip] ? (xmax,xmin) : (xmin,xmax)
y1, y2 = yaxis[:flip] ? (ymax,ymin) : (ymin,ymax)
GR.axes(xtick, ytick, x1, y1, 1, 1, ticksize)
GR.axes(xtick, ytick, x2, y2, -1, -1, -ticksize)
# window_diag = sqrt(gr_view_xdiff()^2 + gr_view_ydiff()^2)
# ticksize = 0.0075 * window_diag
# if outside_ticks
# ticksize = -ticksize
# end
# # TODO: this should be done for each axis separately
# gr_set_linecolor(xaxis[:foreground_color_axis])
# x1, x2 = xaxis[:flip] ? (xmax,xmin) : (xmin,xmax)
# y1, y2 = yaxis[:flip] ? (ymax,ymin) : (ymin,ymax)
# GR.axes(xtick, ytick, x1, y1, 1, 1, ticksize)
# GR.axes(xtick, ytick, x2, y2, -1, -1, -ticksize)
end
# end
+8
View File
@@ -212,6 +212,11 @@ function pgf_axis(sp::Subplot, letter)
scale == :ln || push!(style, "log basis $letter=$(scale == :log2 ? 2 : 10)")
end
# ticks on or off
if axis[:ticks] in (nothing, false)
push!(style, "$(letter)majorticks=false")
end
# limits
# TODO: support zlims
if letter != :z
@@ -239,6 +244,9 @@ function _make_pgf_plot!(plt::Plot)
if letter != :z || is3d(sp)
axisstyle, axiskw = pgf_axis(sp, letter)
merge!(kw, axiskw)
for sty in axisstyle
push!(style, sty)
end
end
end
+1 -1
View File
@@ -191,7 +191,7 @@ function plotly_axis(axis::Axis, sp::Subplot)
# ticks
ticks = get_ticks(axis)
if ticks != :auto
if ticks != :auto && ax[:type] != "-"
ttype = ticksType(ticks)
if ttype == :ticks
ax[:tickmode] = "array"
+13
View File
@@ -54,6 +54,7 @@ function _initialize_backend(::PyPlotBackend)
# solution: hack from @stevengj: https://github.com/stevengj/PyPlot.jl/pull/223#issuecomment-229747768
otherdisplays = splice!(Base.Multimedia.displays, 2:length(Base.Multimedia.displays))
import PyPlot
import LaTeXStrings: latexstring
append!(Base.Multimedia.displays, otherdisplays)
export PyPlot
@@ -221,6 +222,18 @@ function add_pyfixedformatter(cbar, vals::AVec)
end
function labelfunc(scale::Symbol, backend::PyPlotBackend)
if scale == :log10
x -> latexstring("10^{$x}")
elseif scale == :log2
x -> latexstring("2^{$x}")
elseif scale == :ln
x -> latexstring("e^{$x}")
else
string
end
end
# ---------------------------------------------------------------------------
function fix_xy_lengths!(plt::Plot{PyPlotBackend}, series::Series)
+19 -20
View File
@@ -147,14 +147,15 @@ function Base.writemime(io::IO, ::MIME"text/html", plt::Plot)
end
end
function _writemime(io::IO, m, plt::Plot)
warn("_writemime is not defined for this backend. m=", string(m))
end
function _display(plt::Plot)
warn("_display is not defined for this backend.")
end
# for writing to io streams... first prepare, then callback
for mime in keys(_mimeformats)
@eval function _writemime(io::IO, m, plt::Plot)
warn("_writemime is not defined for this backend. m=", string(m))
end
@eval function _display(plt::Plot)
warn("_display is not defined for this backend.")
end
@eval function Base.writemime(io::IO, m::MIME{Symbol($mime)}, plt::Plot)
prepare_output(plt)
_writemime(io, m, plt)
@@ -166,24 +167,22 @@ end
# A backup, if no PNG generation is defined, is to try to make a PDF and use FileIO to convert
if is_installed("FileIO")
@eval begin
import FileIO
function _writemime(io::IO, ::MIME"image/png", plt::Plot)
fn = tempname()
@eval import FileIO
function _writemime(io::IO, ::MIME"image/png", plt::Plot)
fn = tempname()
# first save a pdf file
pdf(plt, fn)
# first save a pdf file
pdf(plt, fn)
# load that pdf into a FileIO Stream
s = FileIO.load(fn * ".pdf")
# load that pdf into a FileIO Stream
s = FileIO.load(fn * ".pdf")
# save a png
pngfn = fn * ".png"
FileIO.save(pngfn, s)
# save a png
pngfn = fn * ".png"
FileIO.save(pngfn, s)
# now write from the file
write(io, readall(open(pngfn)))
end
# now write from the file
write(io, readall(open(pngfn)))
end
end
+1 -1
View File
@@ -77,7 +77,7 @@ function seriestype_supported(pkg::AbstractBackend, st::Symbol)
end
macro deps(st, args...)
:(series_recipe_dependencies($(quot(st)), $(map(quot, args)...)))
:(Plots.series_recipe_dependencies($(quot(st)), $(map(quot, args)...)))
end
# get a list of all seriestypes
+26 -9
View File
@@ -138,24 +138,41 @@ end
# ---------------------------------------------------------------
type Segments
pts::Vector{Float64}
type Segments{T}
pts::Vector{T}
end
Segments() = Segments(zeros(0))
# Segments() = Segments{Float64}(zeros(0))
function Base.push!(segments::Segments, vs...)
push!(segments.pts, NaN)
Segments() = Segments(Float64)
Segments{T}(::Type{T}) = Segments(T[])
Segments(p::Int) = Segments(NTuple{2,Float64}[])
# Segments() = Segments(zeros(0))
to_nan(::Type{Float64}) = NaN
to_nan(::Type{NTuple{2,Float64}}) = (NaN, NaN)
coords(segs::Segments{Float64}) = segs.pts
coords(segs::Segments{NTuple{2,Float64}}) = Float64[p[1] for p in segs.pts], Float64[p[2] for p in segs.pts]
function Base.push!{T}(segments::Segments{T}, vs...)
if !isempty(segments.pts)
push!(segments.pts, to_nan(T))
end
for v in vs
push!(segments.pts, v)
push!(segments.pts, convert(T,v))
end
segments
end
function Base.push!(segments::Segments, vs::AVec)
push!(segments.pts, NaN)
function Base.push!{T}(segments::Segments{T}, vs::AVec)
if !isempty(segments.pts)
push!(segments.pts, to_nan(T))
end
for v in vs
push!(segments.pts, v)
push!(segments.pts, convert(T,v))
end
segments
end
+2
View File
@@ -5,8 +5,10 @@ PlotUtils
StatPlots
Reexport
Measures
Showoff
FactCheck
Images
ImageMagick
@osx QuartzImageIO
GR
DataFrames
+1 -1
View File
@@ -24,7 +24,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.8.0"
const _current_plots_version = v"0.8.1"
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)
+7 -5
View File
@@ -1,14 +1,15 @@
Pkg.clone("ImageMagick")
Pkg.build("ImageMagick")
# Pkg.clone("ImageMagick")
# Pkg.build("ImageMagick")
Pkg.clone("GR")
Pkg.build("GR")
# Pkg.clone("GR")
# Pkg.build("GR")
Pkg.clone("https://github.com/JuliaPlots/PlotReferenceImages.jl.git")
# Pkg.clone("https://github.com/JuliaStats/KernelDensity.jl.git")
Pkg.clone("StatPlots")
Pkg.checkout("PlotUtils")
# Pkg.clone("https://github.com/JunoLab/Blink.jl.git")
# Pkg.build("Blink")
@@ -17,8 +18,9 @@ Pkg.clone("StatPlots")
# Pkg.clone("https://github.com/spencerlyon2/PlotlyJS.jl.git")
# Pkg.checkout("RecipesBase")
Pkg.clone("VisualRegressionTests")
# Pkg.clone("VisualRegressionTests")
# need this to use Conda
ENV["PYTHON"] = ""
Pkg.add("PyPlot")
Pkg.build("PyPlot")