supported functions and gadfly shapes

This commit is contained in:
Thomas Breloff 2015-09-16 01:42:47 -04:00
parent 8df46ec0e9
commit 970686efaf
10 changed files with 189 additions and 96 deletions

View File

@ -55,11 +55,14 @@ const examples = PlotExample[
PlotExample("Lots of line styles",
"Options: (:solid, :dash, :dot, :dashdot, :dashdotdot) \nNote: some may not work with all backends",
[:(plot(rand(20,5); linestyles=[:solid, :dash, :dot, :dashdot, :dashdotdot], labels=["solid", "dash", "dot", "dashdot", "dashdotdot"]))]),
# PlotExample("Lots of marker types",
# "Options: (:none, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon) \nNote: some may not work with all backends",
# [:(plot(repmat(collect(1:10)',10,1); markers=[:ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon],
# labels=["ellipse", "rect", "diamond", "utriangle", "dtriangle", "cross", "xcross", "star1", "star2", "hexagon"],
# markersize=10))]),
PlotExample("Lots of marker types",
"Options: (:none, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon) \nNote: some may not work with all backends",
[:(plot(repmat(collect(1:10)',10,1); markers=[:ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon],
labels=["ellipse", "rect", "diamond", "utriangle", "dtriangle", "cross", "xcross", "star1", "star2", "hexagon"],
markersize=10))]),
"",
[:(markers = supportedMarkers(), :(plot(repmat(collect(1:length(markers))',10,1); marker=:auto, labels=map(string,markers), markersize=10))]),
PlotExample("Bar",
"x is the midpoint of the bar. (todo: allow passing of edges instead of midpoints)",
[:(bar(randn(1000)))]),

View File

@ -36,7 +36,12 @@ export
gadfly!,
unicodeplots!,
pyplot!,
immerse!
immerse!,
supportedAxes,
supportedTypes,
supportedStyles,
supportedMarkers
# ---------------------------------------------------------

View File

@ -2,14 +2,22 @@
# const COLORS = [:black, :blue, :green, :red, :darkGray, :darkCyan, :darkYellow, :darkMagenta,
# :darkBlue, :darkGreen, :darkRed, :gray, :cyan, :yellow, :magenta]
const COLORS = distinguishable_colors(20)
const NUMCOLORS = length(COLORS)
# these are valid choices... first one is default value if unset
const LINE_AXES = (:left, :right)
const LINE_TYPES = (:line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar)
const LINE_STYLES = (:solid, :dash, :dot, :dashdot, :dashdotdot)
const LINE_MARKERS = (:none, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon)
const COLORS = distinguishable_colors(20)
const AXES = [:left, :right]
const TYPES = [:line, :step, :stepinverted, :sticks, :dots, :heatmap, :hexbin, :hist, :bar]
const STYLES = [:solid, :dash, :dot, :dashdot, :dashdotdot]
const MARKERS = [:ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon]
supportedAxes(::PlottingPackage) = AXES
supportedTypes(::PlottingPackage) = TYPES
supportedStyles(::PlottingPackage) = STYLES
supportedMarkers(::PlottingPackage) = MARKERS
supportedAxes() = supportedAxes(plotter())
supportedTypes() = supportedTypes(plotter())
supportedStyles() = supportedStyles(plotter())
supportedMarkers() = supportedMarkers(plotter())
# -----------------------------------------------------------------------------
@ -60,7 +68,8 @@ end
# -----------------------------------------------------------------------------
makeplural(s::Symbol) = Symbol(string(s,"s"))
autocolor(idx::Integer) = COLORS[mod1(idx,NUMCOLORS)]
autopick(arr::AVec, idx::Integer) = arr[mod1(idx,length(arr))]
# converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically
@ -69,7 +78,7 @@ function getRGBColor(c, n::Int = 0)
# auto-assign a color based on plot index
if c == :auto && n > 0
c = autocolor(n)
c = autopick(COLORS, n)
end
# convert it from a symbol/string
@ -90,9 +99,16 @@ end
# const ALT_ARG_NAMES = Dict{Tuple{Symbol,Symbol}, Any}()
# ALT_ARG_NAMES[(:linetype, :scatter)] = :dots
function warnOnUnsupported(pkg::PlottingPackage, d::Dict)
d[:axis] in supportedAxes(pkg) || warn("axis $(d[:axis]) is unsupported with $pkg. Choose from: $(supportedAxes(pkg))")
d[:linetype] == :none || d[:linetype] in supportedTypes(pkg) || warn("linetype $(d[:linetype]) is unsupported with $pkg. Choose from: $(supportedTypes(pkg))")
d[:linestyle] in supportedStyles(pkg) || warn("linestyle $(d[:linestyle]) is unsupported with $pkg. Choose from: $(supportedStyles(pkg))")
d[:marker] == :none || d[:marker] in supportedMarkers(pkg) || warn("marker $(d[:marker]) is unsupported with $pkg. Choose from: $(supportedMarkers(pkg))")
end
# note: idx is the index of this series within this call, n is the index of the series from all calls to plot/subplot
function getPlotKeywordArgs(kw, idx::Int, n::Int)
function getPlotKeywordArgs(pkg::PlottingPackage, kw, idx::Int, n::Int)
d = Dict(kw)
# # replace alternate names
@ -120,6 +136,23 @@ function getPlotKeywordArgs(kw, idx::Int, n::Int)
delete!(d, plural)
end
# auto-pick
if n > 0
if d[:axis] == :auto
d[:axis] = autopick(supportedAxes(pkg), n)
end
if d[:linetype] == :auto
d[:linetype] = autopick(supportedTypes(pkg), n)
end
if d[:linestyle] == :auto
d[:linestyle] = autopick(supportedStyles(pkg), n)
end
if d[:marker] == :auto
d[:marker] = autopick(supportedMarkers(pkg), n)
end
end
# swap out dots for no line and a marker
if haskey(d, :linetype) && d[:linetype] == :dots
d[:linetype] = :none
@ -152,8 +185,11 @@ function getPlotKeywordArgs(kw, idx::Int, n::Int)
label = string(label, " (R)")
end
d[:label] = label
warnOnUnsupported(pkg, d)
end
d
end

View File

@ -6,6 +6,13 @@ immutable GadflyPackage <: PlottingPackage end
gadfly!() = plotter!(:gadfly)
supportedAxes(::GadflyPackage) = [:left]
supportedTypes(::GadflyPackage) = setdiff(TYPES, [:stepinverted])
supportedStyles(::GadflyPackage) = [:solid]
supportedMarkers(::GadflyPackage) = [:rect, :ellipse, :diamond, :cross]
include("gadfly_shapes.jl")
function createGadflyPlotObject(d::Dict)
@eval import DataFrames
@ -46,31 +53,31 @@ function getLineGeoms(d::Dict)
end
function createGadflyAnnotation(d::Dict)
# function createGadflyAnnotation(d::Dict)
if d[:marker] == :rect
# get the width/height of the square (both are sz)
sz = d[:markersize] * Gadfly.px
halfsz = sz/2
# if d[:marker] == :rect
# # get the width/height of the square (both are sz)
# sz = d[:markersize] * Gadfly.px
# halfsz = sz/2
# remap x/y to the corner position of the squares
xs = map(z -> Gadfly.Compose.Measure(;cx=z) - halfsz, float(d[:x]))
ys = map(z -> Gadfly.Compose.Measure(;cy=z) + halfsz, float(d[:y]))
# # remap x/y to the corner position of the squares
# xs = map(z -> Gadfly.Compose.Measure(;cx=z) - halfsz, float(d[:x]))
# ys = map(z -> Gadfly.Compose.Measure(;cy=z) + halfsz, float(d[:y]))
# return an Annotation which will add those shapes to each point in the series
return Gadfly.Guide.annotation(Gadfly.compose(Gadfly.context(), Gadfly.rectangle(xs,ys,[sz],[sz]), Gadfly.fill(d[:markercolor]), Gadfly.stroke(nothing)))
# # return an Annotation which will add those shapes to each point in the series
# return Gadfly.Guide.annotation(Gadfly.compose(Gadfly.context(), Gadfly.rectangle(xs,ys,[sz],[sz]), Gadfly.fill(d[:markercolor]), Gadfly.stroke(nothing)))
else
# make circles
sz = 0.5 * d[:markersize] * Gadfly.px
xs = collect(float(d[:x]))
ys = collect(float(d[:y]))
# else
# # make circles
# sz = 0.5 * d[:markersize] * Gadfly.px
# xs = collect(float(d[:x]))
# ys = collect(float(d[:y]))
# return an Annotation which will add those shapes to each point in the series
return Gadfly.Guide.annotation(Gadfly.compose(Gadfly.context(), Gadfly.circle(xs,ys,[sz]), Gadfly.fill(d[:markercolor]), Gadfly.stroke(nothing)))
# # return an Annotation which will add those shapes to each point in the series
# return Gadfly.Guide.annotation(Gadfly.compose(Gadfly.context(), Gadfly.circle(xs,ys,[sz]), Gadfly.fill(d[:markercolor]), Gadfly.stroke(nothing)))
end
end
# end
# end
# serious hack (I think?) to draw my own shapes as annotations... will it work? who knows...

View File

@ -1,62 +1,88 @@
# Compose pseudo-forms for simple symbols, all parameterized by center and size
using Compose: x_measure, y_measure
# using Compose: x_measure, y_measure
function createGadflyAnnotation(d::Dict)
sz = [d[:markersize] * Gadfly.px]
x, y = d[:x], d[:y]
marker = d[:marker]
if marker == :rect
shape = square(x, y, sz)
elseif marker == :diamond
shape = diamond(x, y, sz)
elseif marker == :cross
shape = cross(x, y, sz)
else
# make circles
sz = 0.8 * d[:markersize] * Gadfly.px
xs = collect(float(d[:x]))
ys = collect(float(d[:y]))
shape = Gadfly.circle(xs,ys,[sz])
end
Gadfly.Guide.annotation(Gadfly.compose(Gadfly.context(), shape, Gadfly.fill(d[:markercolor]), Gadfly.stroke(nothing)))
end
function create
function square(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)
n = max(length(xs), length(ys), length(rs))
n = max(length(xs), length(ys), length(rs))
rect_xs = Vector{Measure}(n)
rect_ys = Vector{Measure}(n)
rect_ws = Vector{Measure}(n)
s = 1/sqrt(2)
for i in 1:n
x = x_measure(xs[1 + i % length(xs)])
y = y_measure(ys[1 + i % length(ys)])
r = rs[1 + i % length(rs)]
rect_xs = Vector{Compose.Measure}(n)
rect_ys = Vector{Compose.Measure}(n)
rect_ws = Vector{Compose.Measure}(n)
s = 1/sqrt(2)
for i in 1:n
x = Compose.x_measure(xs[1 + i % length(xs)])
y = Compose.y_measure(ys[1 + i % length(ys)])
r = rs[1 + i % length(rs)]
rect_xs[i] = x - s*r
rect_ys[i] = y - s*r
rect_ws[i] = 2*s*r
end
rect_xs[i] = x - s*r
rect_ys[i] = y + s*r
rect_ws[i] = 2*s*r
end
return rectangle(rect_xs, rect_ys, rect_ws, rect_ws)
return Gadfly.rectangle(rect_xs, rect_ys, rect_ws, rect_ws)
end
function diamond(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)
n = max(length(xs), length(ys), length(rs))
n = max(length(xs), length(ys), length(rs))
polys = Vector{Vector{Tuple{Measure, Measure}}}(n)
for i in 1:n
x = x_measure(xs[1 + i % length(xs)])
y = y_measure(ys[1 + i % length(ys)])
r = rs[1 + i % length(rs)]
polys[i] = Tuple{Measure, Measure}[(x, y - r), (x + r, y), (x, y + r), (x - r, y)]
end
polys = Vector{Vector{Tuple{Compose.Measure, Compose.Measure}}}(n)
for i in 1:n
x = Compose.x_measure(xs[1 + i % length(xs)])
y = Compose.y_measure(ys[1 + i % length(ys)])
r = rs[1 + i % length(rs)]
polys[i] = Tuple{Compose.Measure, Compose.Measure}[(x, y - r), (x + r, y), (x, y + r), (x - r, y)]
end
return polygon(polys)
return Gadfly.polygon(polys)
end
function cross(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)
n = max(length(xs), length(ys), length(rs))
n = max(length(xs), length(ys), length(rs))
polys = Vector{Vector{Tuple{Measure, Measure}}}(n)
s = 1/sqrt(5)
for i in 1:n
x = x_measure(xs[1 + i % length(xs)])
y = y_measure(ys[1 + i % length(ys)])
r = rs[1 + i % length(rs)]
u = s*r
polys[i] = Tuple{Measure, Measure}[
(x, y - u), (x + u, y - 2u), (x + 2u, y - u),
(x + u, y), (x + 2u, y + u), (x + u, y + 2u),
(x, y + u), (x - u, y + 2u), (x - 2u, y + u),
(x - u, y), (x - 2u, y - u), (x - u, y - 2u) ]
end
polys = Vector{Vector{Tuple{Compose.Measure, Compose.Measure}}}(n)
s = 1/sqrt(5)
for i in 1:n
x = Compose.x_measure(xs[1 + i % length(xs)])
y = Compose.y_measure(ys[1 + i % length(ys)])
r = rs[1 + i % length(rs)]
u = s*r
polys[i] = Tuple{Compose.Measure, Compose.Measure}[
(x, y - u), (x + u, y - 2u), (x + 2u, y - u),
(x + u, y), (x + 2u, y + u), (x + u, y + 2u),
(x, y + u), (x - u, y + 2u), (x - 2u, y + u),
(x - u, y), (x - 2u, y - u), (x - u, y - 2u) ]
end
return polygon(polys)
return Gadfly.polygon(polys)
end

View File

@ -6,6 +6,12 @@ immutable ImmersePackage <: PlottingPackage end
immerse!() = plotter!(:immerse)
supportedAxes(::ImmersePackage) = supportedAxes(GadflyPackage())
supportedTypes(::ImmersePackage) = supportedTypes(GadflyPackage())
supportedStyles(::ImmersePackage) = supportedStyles(GadflyPackage())
supportedMarkers(::ImmersePackage) = supportedMarkers(GadflyPackage())
function createImmerseFigure(d::Dict)
# println("Creating immerse figure: ", d)
w,h = d[:size]

View File

@ -7,6 +7,10 @@ pyplot!() = plotter!(:pyplot)
# -------------------------------
# supportedAxes(::PyPlotPackage) = [:left]
# supportedTypes(::PyPlotPackage) = setdiff(TYPES, [:stepinverted])
supportedStyles(::PyPlotPackage) = [:solid,:dash,:dot,:dashdot]
supportedMarkers(::PyPlotPackage) = [:rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :hexagon]
# convert colorant to 4-tuple RGBA
getPyPlotColor(c::Colorant) = map(f->float(f(c)), (red, green, blue, alpha))
@ -18,7 +22,6 @@ function getPyPlotLineStyle(linetype::Symbol, linestyle::Symbol)
linestyle == :dash && return "--"
linestyle == :dot && return ":"
linestyle == :dashdot && return "-."
linestyle == :dashdotdot && return "-."
warn("Unknown linestyle $linestyle")
return "-"
end
@ -34,7 +37,7 @@ function getPyPlotMarker(marker::Symbol)
marker == :cross && return "x"
marker == :xcross && return "+"
marker == :star1 && return "*"
marker == :star2 && return "*"
# marker == :star2 && return "*"
marker == :hexagon && return "h"
warn("Unknown marker $marker")
return "o"

View File

@ -7,8 +7,14 @@ immutable [PkgName]Package <: PlottingPackage end
[pkgname]!() = plotter!(:[pkgname])
# ---------------------------------------------------------------------------
supportedAxes(::[PkgName]Package) = AXES
supportedTypes(::[PkgName]Package) = TYPES
supportedStyles(::[PkgName]Package) = STYLES
supportedMarkers(::[PkgName]Package) = MARKERS
# ---------------------------------------------------------------------------
function plot(pkg::[PkgName]Package; kw...)

View File

@ -87,7 +87,8 @@ When plotting multiple lines, you can give every line the same trait by using th
# this creates a new plot with args/kw and sets it to be the current plot
function plot(args...; kw...)
plt = plot(plotter(); getPlotKeywordArgs(kw, 1, 0)...) # create a new, blank plot
pkg = plotter()
plt = plot(pkg; getPlotKeywordArgs(pkg, kw, 1, 0)...) # create a new, blank plot
plot!(plt, args...; kw...) # add to it
end
@ -140,7 +141,7 @@ function createKWargsList(plt::PlottingObject; kw...)
if !haskey(d, :x)
d[:x] = 1:length(d[:y])
end
[getPlotKeywordArgs(d, 1, plt.n + 1)]
[getPlotKeywordArgs(plt.plotter, d, 1, plt.n + 1)]
end
@ -151,7 +152,7 @@ end
# create one series where y is vectors of numbers
function createKWargsList{T<:Real}(plt::PlottingObject, y::AVec{T}; kw...)
d = getPlotKeywordArgs(kw, 1, plt.n + 1)
d = getPlotKeywordArgs(plt.plotter, kw, 1, plt.n + 1)
d[:x] = 1:length(y)
d[:y] = y
[d]
@ -160,7 +161,7 @@ end
# create one series where x/y are vectors of numbers
function createKWargsList{T<:Real,S<:Real}(plt::PlottingObject, x::AVec{T}, y::AVec{S}; kw...)
@assert length(x) == length(y)
d = getPlotKeywordArgs(kw, 1, plt.n + 1)
d = getPlotKeywordArgs(plt.plotter, kw, 1, plt.n + 1)
d[:x] = x
d[:y] = y
[d]
@ -171,7 +172,7 @@ function createKWargsList(plt::PlottingObject, y::AMat; kw...)
n,m = size(y)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d = getPlotKeywordArgs(plt.plotter, kw, i, plt.n + i)
d[:x] = 1:n
d[:y] = y[:,i]
push!(ret, d)
@ -185,7 +186,7 @@ function createKWargsList(plt::PlottingObject, x::AVec, y::AMat; kw...)
@assert length(x) == n
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d = getPlotKeywordArgs(plt.plotter, kw, i, plt.n + i)
d[:x] = x
d[:y] = y[:,i]
push!(ret, d)
@ -199,7 +200,7 @@ function createKWargsList(plt::PlottingObject, x::AMat, y::AMat; kw...)
n,m = size(y)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d = getPlotKeywordArgs(plt.plotter, kw, i, plt.n + i)
d[:x] = x[:,i]
d[:y] = y[:,i]
push!(ret, d)
@ -214,7 +215,7 @@ end
# create 1 series, y = f(x), x ∈ [xmin, xmax]
function createKWargsList(plt::PlottingObject, f::Function, xmin::Real, xmax::Real; kw...)
d = getPlotKeywordArgs(kw, 1, plt.n + 1)
d = getPlotKeywordArgs(plt.plotter, kw, 1, plt.n + 1)
width = plt.initargs[:size][1]
d[:x] = collect(linspace(xmin, xmax, width)) # we don't need more than the width
d[:y] = map(f, d[:x])
@ -228,7 +229,7 @@ function createKWargsList(plt::PlottingObject, fs::Vector{Function}, xmin::Real,
width = plt.initargs[:size][1]
x = collect(linspace(xmin, xmax, width)) # we don't need more than the width
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d = getPlotKeywordArgs(plt.plotter, kw, i, plt.n + i)
d[:x] = x
d[:y] = map(fs[i], x)
push!(ret, d)
@ -238,7 +239,7 @@ end
# create 1 series, x = fx(u), y = fy(u); u ∈ [umin, umax]
function createKWargsList(plt::PlottingObject, fx::Function, fy::Function, umin::Real, umax::Real; kw...)
d = getPlotKeywordArgs(kw, 1, plt.n + 1)
d = getPlotKeywordArgs(plt.plotter, kw, 1, plt.n + 1)
width = plt.initargs[:size][1]
u = collect(linspace(umin, umax, width)) # we don't need more than the width
d[:x] = map(fx, u)
@ -248,7 +249,7 @@ end
# create 1 series, y = f(x)
function createKWargsList(plt::PlottingObject, x::AVec, f::Function; kw...)
d = getPlotKeywordArgs(kw, 1, plt.n + 1)
d = getPlotKeywordArgs(plt.plotter, kw, 1, plt.n + 1)
d[:x] = x
d[:y] = map(f, x)
[d]
@ -260,7 +261,7 @@ function createKWargsList(plt::PlottingObject, x::AMat, f::Function; kw...)
n,m = size(x)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d = getPlotKeywordArgs(plt.plotter, kw, i, plt.n + i)
d[:x] = x[:,i]
d[:y] = map(f, d[:x])
push!(ret, d)
@ -280,7 +281,7 @@ function createKWargsList(plt::PlottingObject, y::AVec; kw...)
m = length(y)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d = getPlotKeywordArgs(plt.plotter, kw, i, plt.n + i)
d[:x] = 1:length(y[i])
d[:y] = y[i]
push!(ret, d)
@ -300,7 +301,7 @@ function createKWargsList{T<:Real}(plt::PlottingObject, x::AVec{T}, y::AVec; kw.
m = length(y)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d = getPlotKeywordArgs(plt.plotter, kw, i, plt.n + i)
d[:x] = x
d[:y] = getyvec(x, y[i])
push!(ret, d)
@ -314,7 +315,7 @@ function createKWargsList(plt::PlottingObject, x::AVec, y::AVec; kw...)
m = length(y)
ret = []
for i in 1:m
d = getPlotKeywordArgs(kw, i, plt.n + i)
d = getPlotKeywordArgs(plt.plotter, kw, i, plt.n + i)
d[:x] = x[i]
d[:y] = getyvec(x[i], y[i])
push!(ret, d)
@ -326,7 +327,7 @@ end
function createKWargsList(plt::PlottingObject, n::Integer; kw...)
ret = []
for i in 1:n
d = getPlotKeywordArgs(kw, i, plt.n + i)
d = getPlotKeywordArgs(plt.plotter, kw, i, plt.n + i)
d[:x] = zeros(0)
d[:y] = zeros(0)
push!(ret, d)

View File

@ -94,8 +94,8 @@ function plotter()
elseif currentPackageSymbol == :gadfly
try
@eval import Gadfly
@eval export Gadfly
@eval import Gadfly, Compose
@eval export Gadfly, Compose
catch
error("Couldn't import Gadfly. Install it with: Pkg.add(\"Gadfly\")")
end
@ -118,8 +118,8 @@ function plotter()
elseif currentPackageSymbol == :immerse
try
@eval import Immerse, Gadfly, Gtk
@eval export Immerse, Gadfly
@eval import Immerse, Gadfly, Compose, Gtk
@eval export Immerse, Gadfly, Compose, Gtk
catch
error("Couldn't import Immerse. Install it with: Pkg.add(\"Immerse\")")
end