Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 558627e7a9 | |||
| 4b276f977c | |||
| 99fe4f615d | |||
| 928c306803 | |||
| a3eddf8ba7 | |||
| 78e0ae31af | |||
| 1a90ce0950 | |||
| 2465b3eb5a | |||
| 16a2e34e95 | |||
| 5c52d68091 | |||
| 1a2e180f4f | |||
| 9048053669 | |||
| 69b69714cb | |||
| 1ecd9f9e19 | |||
| 2bca9ca9ec | |||
| d66028ed0d | |||
| 1cf33044bc | |||
| 2c138c8355 | |||
| 73ffcb9bc1 | |||
| 730025e144 | |||
| 91aa1d718a | |||
| f881bfc4fc | |||
| e76c3fff69 | |||
| 9f168071ff | |||
| b6652b7619 | |||
| f6d501f69e | |||
| 4c052cb3b3 | |||
| ea8ccf38db | |||
| cf12ff8070 | |||
| b7a95244db | |||
| 4c884651a8 | |||
| e9eca577aa | |||
| cff78b477c | |||
| b232410d25 | |||
| 5ff338d4ac | |||
| a5ceea153d | |||
| f3544dc826 | |||
| b0bc68ae8a | |||
| c38e947dc6 | |||
| ec826fc8ce | |||
| e5c4f782a5 | |||
| a5ddebf44d | |||
| 38804898c5 | |||
| 2bd67f3519 | |||
| c8ed611c9c | |||
| 3d7d8caa82 | |||
| f64108523c | |||
| a7493504ed | |||
| bc5293b5a4 |
@@ -9,6 +9,43 @@
|
||||
|
||||
## 0.7 (current master/dev)
|
||||
|
||||
#### 0.7.3
|
||||
|
||||
- rebuild violin and boxplot recipes
|
||||
- "plot recipes"
|
||||
- `cgrad` method for easy color gradient creation
|
||||
- improvements to inset subplots
|
||||
- Segments and iter_segments for NaN-separated vectors
|
||||
- `bar` recipe now creates a `shape` series
|
||||
- writemime fix for Interact.jl
|
||||
- `link = :square` option
|
||||
- !!! set `shape` attributes with line/fill, NOT marker/markerstroke !!!
|
||||
- basic DPI support
|
||||
- moved chorddiagram to PlotRecipes
|
||||
- GR:
|
||||
- use temp files for img output
|
||||
- basic support for marker strokes and other marker fixes
|
||||
- PyPlot:
|
||||
- Switch to recipes for bar, histogram, histogram2d
|
||||
- GLVisualize
|
||||
- subplots
|
||||
- path/scatter and path3d/scatter3d
|
||||
- initial drawing of axes
|
||||
- many smaller fixes and improvements
|
||||
|
||||
#### 0.7.2
|
||||
|
||||
- line_z arg for multicolored line segments
|
||||
- pyplot
|
||||
- line_z (2d and 3d)
|
||||
- pushed all fig updates into display pipeline
|
||||
- remove native sticks/hline/vline in favor of recipes
|
||||
- unicodeplots cleanup, ijulia fixes, ascii canvas
|
||||
- `curves` series type
|
||||
- `iter_segments` iterator
|
||||
- moved arcdiagram out and into PlotRecipes (thanks @diegozea)
|
||||
- several other fixes/checks
|
||||
|
||||
#### 0.7.1
|
||||
|
||||
- inset (floating) subplots
|
||||
|
||||
@@ -22,8 +22,6 @@ Use the [preprocessing pipeline](http://plots.readthedocs.io/en/latest/pipeline/
|
||||
|
||||
```julia
|
||||
using Plots
|
||||
pyplot(reuse=true)
|
||||
|
||||
@gif for i in linspace(0,2π,100)
|
||||
X = Y = linspace(-5,5,40)
|
||||
surface(X, Y, (x,y) -> sin(x+10sin(i))+cos(y))
|
||||
|
||||
+4
-3
@@ -10,6 +10,7 @@ using Reexport
|
||||
using FixedSizeArrays
|
||||
@reexport using RecipesBase
|
||||
using Base.Meta
|
||||
# using PlotUtils
|
||||
|
||||
export
|
||||
AbstractPlot,
|
||||
@@ -78,6 +79,7 @@ export
|
||||
Surface,
|
||||
OHLC,
|
||||
arrow,
|
||||
Segments,
|
||||
|
||||
colorscheme,
|
||||
ColorScheme,
|
||||
@@ -103,12 +105,10 @@ export
|
||||
@animate,
|
||||
@gif,
|
||||
|
||||
PlotRecipe,
|
||||
spy,
|
||||
arcdiagram,
|
||||
chorddiagram,
|
||||
|
||||
test_examples,
|
||||
iter_segments,
|
||||
|
||||
translate,
|
||||
translate!,
|
||||
@@ -183,6 +183,7 @@ end
|
||||
@shorthands boxplot
|
||||
@shorthands violin
|
||||
@shorthands quiver
|
||||
@shorthands curves
|
||||
|
||||
pie(args...; kw...) = plot(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
||||
pie!(args...; kw...) = plot!(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
||||
|
||||
+4
-4
@@ -24,7 +24,7 @@ immutable AnimatedGif
|
||||
filename::Compat.ASCIIString
|
||||
end
|
||||
|
||||
function gif(anim::Animation, fn = "tmp.gif"; fps::Integer = 20)
|
||||
function gif(anim::Animation, fn = (isijulia() ? "tmp.gif" : tempname()*".gif"); fps::Integer = 20)
|
||||
fn = abspath(fn)
|
||||
|
||||
try
|
||||
@@ -35,8 +35,8 @@ function gif(anim::Animation, fn = "tmp.gif"; fps::Integer = 20)
|
||||
if isfile(file) && !haskey(ENV, "MAGICK_CONFIGURE_PATH")
|
||||
include(file)
|
||||
end
|
||||
prefix = get(ENV, "MAGICK_CONFIGURE_PATH", "")
|
||||
run(`$(joinpath(prefix, "convert")) -delay $speed -loop 0 $(joinpath(anim.dir, "*.png")) -alpha off $fn`)
|
||||
# prefix = get(ENV, "MAGICK_CONFIGURE_PATH", "")
|
||||
run(`convert -delay $speed -loop 0 $(joinpath(anim.dir, "*.png")) -alpha off $fn`)
|
||||
|
||||
catch err
|
||||
warn("""Tried to create gif using convert (ImageMagick), but got error: $err
|
||||
@@ -56,7 +56,7 @@ end
|
||||
|
||||
# write out html to view the gif... note the rand call which is a hack so the image doesn't get cached
|
||||
function Base.writemime(io::IO, ::MIME"text/html", agif::AnimatedGif)
|
||||
write(io, "<img src=\"$(relpath(agif.filename))?$(rand())>\" />")
|
||||
write(io, "<img src=\"$(relpath(agif.filename))?$(rand())>\" />")
|
||||
end
|
||||
|
||||
|
||||
|
||||
+3
-1
@@ -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)",
|
||||
@@ -61,6 +62,7 @@ const _arg_desc = KW(
|
||||
:overwrite_figure => "Bool. Should we reuse the same GUI window/figure when plotting (true) or open a new one (false).",
|
||||
:html_output_format => "Symbol. When writing html output, what is the format? `:png` and `:svg` are currently supported.",
|
||||
:inset_subplots => "nothing or vector of 2-tuple (parent,bbox). optionally pass a vector of (parent,bbox) tuples which are the parent layout and the relative bounding box of inset subplots",
|
||||
:dpi => "Number. Dots Per Inch of output figures",
|
||||
|
||||
# subplot args
|
||||
:title => "String. Subplot title.",
|
||||
|
||||
+21
-2
@@ -11,6 +11,16 @@ function add_aliases(sym::Symbol, aliases::Symbol...)
|
||||
end
|
||||
end
|
||||
|
||||
function add_non_underscore_aliases!(aliases::KW)
|
||||
for (k,v) in aliases
|
||||
s = string(k)
|
||||
if '_' in s
|
||||
aliases[Symbol(replace(s, "_", ""))] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ------------------------------------------------------------
|
||||
|
||||
const _allAxes = [:auto, :left, :right]
|
||||
@@ -61,8 +71,12 @@ const _allTypes = vcat([
|
||||
:imagesc => :image,
|
||||
:hist => :histogram,
|
||||
:hist2d => :histogram2d,
|
||||
:bezier => :curves,
|
||||
:bezier_curves => :curves,
|
||||
)
|
||||
|
||||
add_non_underscore_aliases!(_typeAliases)
|
||||
|
||||
like_histogram(seriestype::Symbol) = seriestype in (:histogram, :density)
|
||||
like_line(seriestype::Symbol) = seriestype in (:line, :path, :steppre, :steppost)
|
||||
like_surface(seriestype::Symbol) = seriestype in (:contour, :contour3d, :heatmap, :surface, :wireframe, :image)
|
||||
@@ -163,6 +177,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)
|
||||
@@ -201,6 +216,7 @@ const _plot_defaults = KW(
|
||||
:html_output_format => :auto,
|
||||
:inset_subplots => nothing, # optionally pass a vector of (parent,bbox) tuples which are
|
||||
# the parent layout and the relative bounding box of inset subplots
|
||||
:dpi => DPI, # dots per inch for images, etc
|
||||
)
|
||||
|
||||
|
||||
@@ -375,7 +391,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)
|
||||
@@ -415,6 +432,8 @@ add_aliases(:projection, :proj)
|
||||
add_aliases(:title_location, :title_loc, :titleloc, :title_position, :title_pos, :titlepos, :titleposition, :title_align, :title_alignment)
|
||||
add_aliases(:series_annotations, :series_ann, :seriesann, :series_anns, :seriesanns, :series_annotation)
|
||||
add_aliases(:html_output_format, :format, :fmt, :html_format)
|
||||
add_aliases(:orientation, :direction, :dir)
|
||||
add_aliases(:inset_subplots, :inset, :floating)
|
||||
|
||||
|
||||
# add all pluralized forms to the _keyAliases dict
|
||||
@@ -696,7 +715,7 @@ end
|
||||
function extractGroupArgs(v::AVec, args...)
|
||||
groupLabels = sort(collect(unique(v)))
|
||||
n = length(groupLabels)
|
||||
if n > 20
|
||||
if n > 100
|
||||
warn("You created n=$n groups... Is that intended?")
|
||||
end
|
||||
groupIds = Vector{Int}[filter(i -> v[i] == glab, 1:length(v)) for glab in groupLabels]
|
||||
|
||||
+226
-24
@@ -29,68 +29,270 @@ supported_args(::GLVisualizeBackend) = merge_with_base_supported([
|
||||
# :clims,
|
||||
# :inset_subplots,
|
||||
])
|
||||
supported_types(::GLVisualizeBackend) = [:surface]
|
||||
supported_types(::GLVisualizeBackend) = [:surface, :scatter, :scatter3d, :path, :path3d]
|
||||
supported_styles(::GLVisualizeBackend) = [:auto, :solid]
|
||||
supported_markers(::GLVisualizeBackend) = [:none, :auto, :circle]
|
||||
supported_markers(::GLVisualizeBackend) = vcat([:none, :auto, :circle], collect(keys(_gl_marker_map)))
|
||||
supported_scales(::GLVisualizeBackend) = [:identity]
|
||||
is_subplot_supported(::GLVisualizeBackend) = false
|
||||
is_subplot_supported(::GLVisualizeBackend) = true
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function _initialize_backend(::GLVisualizeBackend; kw...)
|
||||
@eval begin
|
||||
import GLVisualize
|
||||
import GLVisualize, GeometryTypes, GLAbstraction, GLWindow
|
||||
import GeometryTypes: Point2f0, Point3f0, Vec2f0, Vec3f0
|
||||
export GLVisualize
|
||||
|
||||
# TODO: remove this when PlotUtils is registered
|
||||
import PlotUtils
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
immutable GLScreenWrapper
|
||||
window
|
||||
end
|
||||
|
||||
# initialize the figure/window
|
||||
function _create_backend_figure(plt::Plot{GLVisualizeBackend})
|
||||
# init a window
|
||||
window = GLVisualize.glscreen()
|
||||
@async GLVisualize.renderloop(window)
|
||||
window
|
||||
# init a screen
|
||||
screen = if isdefined(GLVisualize, :ROOT_SCREEN)
|
||||
GLVisualize.ROOT_SCREEN
|
||||
else
|
||||
s = GLVisualize.glscreen()
|
||||
@async GLVisualize.renderloop(s)
|
||||
s
|
||||
end
|
||||
empty!(screen)
|
||||
screen
|
||||
end
|
||||
|
||||
function gl_display(plt::Plot{GLVisualizeBackend})
|
||||
for sp in plt.subplots
|
||||
# TODO: setup subplot
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# size as a percentage of the window size
|
||||
function gl_relative_size(plt::Plot{GLVisualizeBackend}, msize::Number)
|
||||
winsz = min(plt[:size]...)
|
||||
Float32(msize / winsz)
|
||||
end
|
||||
|
||||
const _gl_marker_map = KW(
|
||||
:rect => '■',
|
||||
:star5 => '★',
|
||||
:diamond => '◆',
|
||||
:hexagon => '⬢',
|
||||
:cross => '✚',
|
||||
:xcross => '❌',
|
||||
:utriangle => '▲',
|
||||
:dtriangle => '▼',
|
||||
:pentagon => '⬟',
|
||||
:octagon => '⯄',
|
||||
:star4 => '✦',
|
||||
:star6 => '✶',
|
||||
:star8 => '✷',
|
||||
:vline => '┃',
|
||||
:hline => '━',
|
||||
)
|
||||
|
||||
|
||||
# create a marker/shape type
|
||||
function gl_marker(shape::Symbol, msize::Number, _3d::Bool)
|
||||
GeometryTypes.HyperSphere((_3d ? Point3f0 : Point2f0)(0), msize)
|
||||
end
|
||||
|
||||
gl_color(c::RGBA{Float32}) = c
|
||||
|
||||
# convert to RGBA
|
||||
function gl_color(c, a=nothing)
|
||||
c = convertColor(c, a)
|
||||
RGBA{Float32}(getColor(c))
|
||||
end
|
||||
|
||||
function gl_viewport(bb, rect)
|
||||
l, b, bw, bh = bb
|
||||
rw, rh = rect.w, rect.h
|
||||
GLVisualize.SimpleRectangle(
|
||||
round(Int, rect.x + rw * l),
|
||||
round(Int, rect.y + rh * b),
|
||||
round(Int, rw * bw),
|
||||
round(Int, rh * bh)
|
||||
)
|
||||
end
|
||||
|
||||
gl_make_points(x, y) = Point2f0[Point2f0(x[i], y[i]) for i=1:length(x)]
|
||||
gl_make_points(x, y, z) = Point3f0[Point3f0(x[i], y[i], z[i]) for i=1:length(x)]
|
||||
|
||||
function gl_draw_lines_2d(x, y, color, linewidth, sp_screen)
|
||||
color = gl_color(color)
|
||||
thickness = Float32(linewidth)
|
||||
for rng in iter_segments(x, y)
|
||||
n = length(rng)
|
||||
n < 2 && continue
|
||||
viz = GLVisualize.visualize(
|
||||
gl_make_points(x[rng], y[rng]),
|
||||
n==2 ? :linesegment : :lines,
|
||||
color=color,
|
||||
thickness = Float32(linewidth)
|
||||
)
|
||||
GLVisualize.view(viz, sp_screen, camera=:orthographic_pixel)
|
||||
end
|
||||
end
|
||||
|
||||
function gl_draw_lines_3d(x, y, z, color, linewidth, sp_screen)
|
||||
color = gl_color(color)
|
||||
thickness = Float32(linewidth)
|
||||
for rng in iter_segments(x, y, z)
|
||||
n = length(rng)
|
||||
n < 2 && continue
|
||||
viz = GLVisualize.visualize(
|
||||
gl_make_points(x[rng], y[rng], z[rng]),
|
||||
n==2 ? :linesegment : :lines,
|
||||
color=color,
|
||||
thickness = Float32(linewidth)
|
||||
)
|
||||
GLVisualize.view(viz, sp_screen, camera=:perspective)
|
||||
end
|
||||
end
|
||||
|
||||
function gl_annotate(sp::Subplot{GLVisualizeBackend}, x, y, txt::PlotText)
|
||||
end
|
||||
|
||||
function gl_draw_axes_2d(sp::Subplot{GLVisualizeBackend})
|
||||
sp_screen = sp.o
|
||||
xaxis = sp[:xaxis]
|
||||
xmin, xmax = axis_limits(xaxis)
|
||||
yaxis = sp[:yaxis]
|
||||
ymin, ymax = axis_limits(yaxis)
|
||||
|
||||
# x axis
|
||||
xsegs, ysegs = Segments(), Segments()
|
||||
ticksz = 0.03*(ymax-ymin)
|
||||
push!(xsegs, [xmin,xmax]); push!(ysegs, [ymin,ymin])
|
||||
for tick in PlotUtils.optimize_ticks(xmin, xmax)[1]
|
||||
push!(xsegs, [tick,tick]); push!(ysegs, [ymin,ymin+ticksz])
|
||||
# TODO: add the ticklabel
|
||||
end
|
||||
gl_draw_lines_2d(xsegs.pts, ysegs.pts, xaxis[:foreground_color_border], 1, sp_screen)
|
||||
|
||||
# y axis
|
||||
xsegs, ysegs = Segments(), Segments()
|
||||
push!(xsegs, [xmin,xmin]); push!(ysegs, [ymin,ymax])
|
||||
for tick in PlotUtils.optimize_ticks(xmin, xmax)[1]
|
||||
push!(xsegs, [xmin,xmin+ticksz]); push!(ysegs, [tick,tick])
|
||||
# TODO: add the ticklabel
|
||||
end
|
||||
gl_draw_lines_2d(xsegs.pts, ysegs.pts, yaxis[:foreground_color_border], 1, sp_screen)
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# draw everything
|
||||
function gl_display(plt::Plot{GLVisualizeBackend})
|
||||
screen = plt.o
|
||||
sw, sh = plt[:size]
|
||||
sw, sh = sw*px, sh*px
|
||||
for (name, sp) in plt.spmap
|
||||
|
||||
_3d = is3d(sp)
|
||||
camera = _3d ? :perspective : :orthographic_pixel
|
||||
# camera = :perspective
|
||||
|
||||
# initialize the sub-screen for this subplot
|
||||
# note: we create a lift function to update the size on resize
|
||||
rel_bbox = bbox_to_pcts(bbox(sp), sw, sh)
|
||||
f = rect -> gl_viewport(rel_bbox, rect)
|
||||
sp_screen = GLVisualize.Screen(
|
||||
screen,
|
||||
name = name,
|
||||
area = GLVisualize.const_lift(f, screen.area)
|
||||
)
|
||||
|
||||
sp.o = sp_screen
|
||||
if !is3d(sp)
|
||||
gl_draw_axes_2d(sp)
|
||||
end
|
||||
|
||||
# loop over the series and add them to the subplot
|
||||
for series in series_list(sp)
|
||||
# TODO: setup series
|
||||
d = series.d
|
||||
st = d[:seriestype]
|
||||
x, y, z = map(Float32, d[:x]), map(Float32, d[:y]), d[:z]
|
||||
x, y = map(Float32, d[:x]), map(Float32, d[:y])
|
||||
msize = gl_relative_size(plt, d[:markersize])
|
||||
|
||||
if st == :surface
|
||||
viz = if st == :surface
|
||||
# TODO: can pass just the ranges and surface
|
||||
ismatrix(x) || (x = repmat(x', length(y), 1))
|
||||
ismatrix(y) || (y = repmat(y, 1, length(x)))
|
||||
z = transpose_z(d, map(Float32, z.surf), false)
|
||||
z = transpose_z(d, map(Float32, d[:z].surf), false)
|
||||
viz = GLVisualize.visualize((x, y, z), :surface)
|
||||
GLVisualize.view(viz, plt.o)
|
||||
return
|
||||
GLVisualize.view(viz, sp_screen, camera = :perspective)
|
||||
|
||||
else
|
||||
error("Series type $st not supported by GLVisualize")
|
||||
end
|
||||
# paths and scatters
|
||||
|
||||
_3d && (z = map(Float32, d[:z]))
|
||||
|
||||
# paths?
|
||||
lw = d[:linewidth]
|
||||
if lw > 0
|
||||
c = gl_color(d[:linecolor], d[:linealpha])
|
||||
if _3d
|
||||
gl_draw_lines_3d(x, y, z, c, lw, sp_screen)
|
||||
else
|
||||
gl_draw_lines_2d(x, y, c, lw, sp_screen)
|
||||
end
|
||||
end
|
||||
|
||||
# markers?
|
||||
if st in (:scatter, :scatter3d) || d[:markershape] != :none
|
||||
extrakw = KW()
|
||||
c = gl_color(d[:markercolor], d[:markeralpha])
|
||||
|
||||
# get the marker
|
||||
shape = d[:markershape]
|
||||
shape = get(_gl_marker_map, shape, shape)
|
||||
marker = if isa(shape, Char)
|
||||
# extrakw[:scale] = Vec2f0(_3d ? 0.6*d[:markersize] : msize)
|
||||
extrakw[:scale] = Vec2f0(msize)
|
||||
shape
|
||||
else
|
||||
gl_marker(d[:markershape], msize, _3d)
|
||||
end
|
||||
|
||||
if !_3d
|
||||
extrakw[:billboard] = true
|
||||
end
|
||||
|
||||
points = _3d ? gl_make_points(x,y,z) : gl_make_points(x,y)
|
||||
viz = GLVisualize.visualize(
|
||||
(marker, points);
|
||||
color = c,
|
||||
extrakw...
|
||||
)
|
||||
GLVisualize.view(viz, sp_screen, camera = camera)
|
||||
|
||||
# TODO: might need to switch to these forms later?
|
||||
# GLVisualize.visualize((marker ,(x, y, z)))
|
||||
#GLVisualize.visualize((marker , map(Point3f0, zip(x, y, z),
|
||||
# billboard=true
|
||||
#))
|
||||
end
|
||||
end
|
||||
end
|
||||
GLAbstraction.center!(sp_screen, camera)
|
||||
end
|
||||
|
||||
# TODO: render one frame at a time? (no renderloop)
|
||||
# GLWindow.render_frame(screen)
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _update_plot_object(plt::Plot{GLVisualizeBackend})
|
||||
gl_display(plt)
|
||||
end
|
||||
|
||||
# function _writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{GLVisualizeBackend})
|
||||
# # TODO: write a png to io
|
||||
# end
|
||||
|
||||
function _display(plt::Plot{GLVisualizeBackend})
|
||||
gl_display(plt)
|
||||
end
|
||||
|
||||
+70
-68
@@ -41,7 +41,6 @@ supported_scales(::GRBackend) = [:identity, :log10]
|
||||
is_subplot_supported(::GRBackend) = true
|
||||
|
||||
|
||||
|
||||
function _initialize_backend(::GRBackend; kw...)
|
||||
@eval begin
|
||||
import GR
|
||||
@@ -121,33 +120,6 @@ gr_set_textcolor(c, a=nothing) = GR.settextcolorind(gr_getcolorind(c, a))
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function gr_setmarkershape(d)
|
||||
if d[:markershape] != :none
|
||||
shape = d[:markershape]
|
||||
if isa(shape, Shape)
|
||||
d[:vertices] = vertices(shape)
|
||||
else
|
||||
GR.setmarkertype(gr_markertype[shape])
|
||||
d[:vertices] = :none
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function gr_polymarker(d, x, y)
|
||||
if d[:vertices] != :none
|
||||
vertices= d[:vertices]
|
||||
dx = Float64[el[1] for el in vertices] * 0.03
|
||||
dy = Float64[el[2] for el in vertices] * 0.03
|
||||
GR.selntran(0)
|
||||
for i = 1:length(x)
|
||||
xn, yn = GR.wctondc(x[i], y[i])
|
||||
GR.fillarea(xn + dx, yn + dy)
|
||||
end
|
||||
GR.selntran(1)
|
||||
else
|
||||
GR.polymarker(x, y)
|
||||
end
|
||||
end
|
||||
|
||||
# draw line segments, splitting x/y into contiguous/finite segments
|
||||
# note: this can be used for shapes by passing func `GR.fillarea`
|
||||
@@ -260,48 +232,64 @@ function normalize_zvals(zv::AVec)
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
function gr_draw_markers(d::KW, x, y, msize, mz, c, a)
|
||||
if length(x) > 0
|
||||
mz == nothing && gr_set_markercolor(c, a)
|
||||
|
||||
if typeof(msize) <: Number && mz == nothing
|
||||
# draw the markers all the same
|
||||
GR.setmarkersize(msize)
|
||||
gr_polymarker(d, x, y)
|
||||
else
|
||||
# draw each marker differently
|
||||
for i = 1:length(x)
|
||||
if mz != nothing
|
||||
ci = round(Int, 1000 + mz[i] * 255)
|
||||
GR.setmarkercolorind(ci)
|
||||
end
|
||||
GR.setmarkersize(isa(msize, Number) ? msize : msize[mod1(i, length(msize))])
|
||||
gr_polymarker(d, [x[i]], [y[i]])
|
||||
# draw ONE Shape
|
||||
function gr_draw_marker(xi, yi, msize, shape::Shape)
|
||||
sx, sy = shape_coords(shape)
|
||||
GR.selntran(0)
|
||||
xi, yi = GR.wctondc(xi, yi)
|
||||
GR.fillarea(xi + sx * 0.0015msize,
|
||||
yi + sy * 0.0015msize)
|
||||
GR.selntran(1)
|
||||
end
|
||||
|
||||
# draw ONE symbol marker
|
||||
function gr_draw_marker(xi, yi, msize::Number, shape::Symbol)
|
||||
GR.setmarkertype(gr_markertype[shape])
|
||||
GR.setmarkersize(0.3msize)
|
||||
GR.polymarker([xi], [yi])
|
||||
end
|
||||
|
||||
|
||||
# draw the markers, one at a time
|
||||
function gr_draw_markers(d::KW, x, y, msize, mz)
|
||||
shape = d[:markershape]
|
||||
if shape != :none
|
||||
for i=1:length(x)
|
||||
msize = cycle(msize, i)
|
||||
cfunc = isa(shape, Shape) ? gr_set_fillcolor : gr_set_markercolor
|
||||
cfuncind = isa(shape, Shape) ? GR.setfillcolorind : GR.setmarkercolorind
|
||||
|
||||
# draw a filled in shape, slightly bigger, to estimate a stroke
|
||||
cfunc(d[:markerstrokecolor], d[:markerstrokealpha])
|
||||
gr_draw_marker(x[i], y[i], msize*1.2, shape, )
|
||||
|
||||
# draw the shape
|
||||
if mz == nothing
|
||||
cfunc(d[:markercolor], d[:markeralpha])
|
||||
else
|
||||
# pick a color from the pre-loaded gradient
|
||||
ci = round(Int, 1000 + cycle(mz, i) * 255)
|
||||
cfuncind(ci)
|
||||
end
|
||||
gr_draw_marker(x[i], y[i], msize, shape)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function gr_draw_markers(series::Series, x, y)
|
||||
isempty(x) && return
|
||||
d = series.d
|
||||
msize = 0.5 * d[:markersize]
|
||||
mz = normalize_zvals(d[:marker_z])
|
||||
|
||||
# draw the marker
|
||||
gr_setmarkershape(d)
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
gr_draw_markers(d, x, y, msize, mz, d[:markercolor], d[:markeralpha])
|
||||
|
||||
# # draw the stroke
|
||||
# GR.setfillintstyle(GR.INTSTYLE_HOLLOW)
|
||||
# gr_draw_markers(d, x, y, msize, mz, d[:markerstrokecolor], d[:markerstrokealpha])
|
||||
|
||||
gr_draw_markers(d, x, y, d[:markersize], mz)
|
||||
if mz != nothing
|
||||
gr_colorbar(d[:subplot])
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
function gr_set_line(w, style, c, a)
|
||||
GR.setlinetype(gr_linetype[style])
|
||||
@@ -495,6 +483,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
data_lims = gr_xy_axislims(sp)
|
||||
xmin, xmax, ymin, ymax = data_lims
|
||||
scale = 0
|
||||
xtick, ytick = 1, 1
|
||||
if xmax > xmin && ymax > ymin
|
||||
# NOTE: for log axes, the major_x and major_y - if non-zero (omit labels) - control the minor grid lines (1 = draw 9 minor grid lines, 2 = no minor grid lines)
|
||||
# NOTE: for log axes, the x_tick and y_tick - if non-zero (omit axes) - only affect the output appearance (1 = nomal, 2 = scientiic notation)
|
||||
@@ -775,11 +764,11 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
elseif st == :shape
|
||||
# draw the shapes
|
||||
gr_set_line(d[:markerstrokewidth], :solid, d[:markerstrokecolor], d[:markerstrokealpha])
|
||||
gr_set_line(d[:linewidth], :solid, d[:linecolor], d[:linealpha])
|
||||
gr_polyline(d[:x], d[:y])
|
||||
|
||||
# draw the interior
|
||||
gr_set_fill(d[:markercolor], d[:markeralpha])
|
||||
gr_set_fill(d[:fillcolor], d[:fillalpha])
|
||||
gr_polyline(d[:x], d[:y], GR.fillarea)
|
||||
|
||||
|
||||
@@ -845,15 +834,24 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
GR.setlinetype(gr_linetype[d[:linestyle]])
|
||||
GR.polyline([xpos - 0.07, xpos - 0.01], [ypos, ypos])
|
||||
end
|
||||
if st == :scatter || d[:markershape] != :none
|
||||
gr_set_markercolor(d[:markercolor], d[:markeralpha])
|
||||
gr_setmarkershape(d)
|
||||
if st == :path
|
||||
gr_polymarker(d, [xpos - 0.06, xpos - 0.02], [ypos, ypos])
|
||||
else
|
||||
gr_polymarker(d, [xpos - 0.06, xpos - 0.04, xpos - 0.02], [ypos, ypos, ypos])
|
||||
end
|
||||
end
|
||||
|
||||
gr_draw_markers(d, xpos-[0.06,0.02], [ypos,ypos], 10, nothing)
|
||||
# shape = d[:markershape]
|
||||
# if shape != :none #st == :scatter || d[:markershape] != :none
|
||||
# msize = 10
|
||||
# for xoff in [0.06,0.02]
|
||||
# gr_set_markercolor(d[:markerstrokecolor], d[:markerstrokealpha])
|
||||
# gr_draw_marker(xpos-xoff, ypos, msize*1.1, shape)
|
||||
# gr_set_markercolor(d[:markercolor], d[:markeralpha])
|
||||
# gr_draw_marker(xpos-xoff, ypos, msize, shape)
|
||||
# end
|
||||
# # gr_setmarkershape(d)
|
||||
# # if st == :path
|
||||
# # gr_polymarker(d, [xpos - 0.06, xpos - 0.02], [ypos, ypos])
|
||||
# # else
|
||||
# # gr_polymarker(d, [xpos - 0.06, xpos - 0.04, xpos - 0.02], [ypos, ypos, ypos])
|
||||
# # end
|
||||
# end
|
||||
if typeof(d[:label]) <: Array
|
||||
i += 1
|
||||
lab = d[:label][i]
|
||||
@@ -895,11 +893,15 @@ const _gr_mimeformats = Dict(
|
||||
for (mime, fmt) in _gr_mimeformats
|
||||
@eval function _writemime(io::IO, ::MIME{Symbol($mime)}, plt::Plot{GRBackend})
|
||||
GR.emergencyclosegks()
|
||||
wstype = haskey(ENV, "GKS_WSTYPE") ? ENV["GKS_WSTYPE"] : "0"
|
||||
filepath = tempname() * "." * $fmt
|
||||
ENV["GKS_WSTYPE"] = $fmt
|
||||
ENV["GKS_FILEPATH"] = filepath
|
||||
gr_display(plt)
|
||||
GR.emergencyclosegks()
|
||||
write(io, readall("gks." * $fmt))
|
||||
ENV["GKS_WSTYPE"] = ""
|
||||
write(io, readall(filepath))
|
||||
ENV["GKS_WSTYPE"] = wstype
|
||||
rm(filepath)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -374,12 +374,12 @@ function plotly_series(plt::Plot, series::Series)
|
||||
# @show map(length, (x,y,d_out[:x],d_out[:y]))
|
||||
# @show d_out[:x] d_out[:y]
|
||||
d_out[:fill] = "tozeroy"
|
||||
d_out[:fillcolor] = webcolor(d[:markercolor], d[:markeralpha])
|
||||
d_out[:fillcolor] = webcolor(d[:fillcolor], d[:fillalpha])
|
||||
if d[:markerstrokewidth] > 0
|
||||
d_out[:line] = KW(
|
||||
:color => webcolor(d[:markerstrokecolor], d[:markerstrokealpha]),
|
||||
:width => d[:markerstrokewidth],
|
||||
:dash => string(d[:markerstrokestyle]),
|
||||
:color => webcolor(d[:linecolor], d[:linealpha]),
|
||||
:width => d[:linewidth],
|
||||
:dash => string(d[:linestyle]),
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ supported_args(::PlotlyJSBackend) = supported_args(PlotlyBackend())
|
||||
supported_types(::PlotlyJSBackend) = supported_types(PlotlyBackend())
|
||||
supported_styles(::PlotlyJSBackend) = supported_styles(PlotlyBackend())
|
||||
supported_markers(::PlotlyJSBackend) = supported_markers(PlotlyBackend())
|
||||
supported_scales(::PlotlyJSBackend) = supported_scales(PlotlyBackend())
|
||||
is_subplot_supported(::PlotlyJSBackend) = true
|
||||
is_string_supported(::PlotlyJSBackend) = true
|
||||
|
||||
@@ -65,7 +66,7 @@ function _writemime(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyJSBackend})
|
||||
end
|
||||
|
||||
function _writemime(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend})
|
||||
tmpfn = tempname() * "png"
|
||||
tmpfn = tempname() * ".png"
|
||||
PlotlyJS.savefig(plt.o, tmpfn)
|
||||
write(io, read(open(tmpfn)))
|
||||
end
|
||||
|
||||
+232
-171
@@ -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,
|
||||
@@ -28,12 +30,13 @@ supported_args(::PyPlotBackend) = merge_with_base_supported([
|
||||
:match_dimensions,
|
||||
:clims,
|
||||
:inset_subplots,
|
||||
:dpi,
|
||||
])
|
||||
supported_types(::PyPlotBackend) = [
|
||||
:path, :steppre, :steppost, :shape,
|
||||
:scatter, :histogram2d, :hexbin, :histogram,
|
||||
:bar, :sticks,
|
||||
:hline, :vline, :heatmap, :pie, :image,
|
||||
:scatter, :hexbin, #:histogram2d, :histogram,
|
||||
# :bar,
|
||||
:heatmap, :pie, :image,
|
||||
:contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
|
||||
]
|
||||
supported_styles(::PyPlotBackend) = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
@@ -60,7 +63,10 @@ function _initialize_backend(::PyPlotBackend)
|
||||
const pyticker = PyPlot.pywrap(PyPlot.pyimport("matplotlib.ticker"))
|
||||
const pycmap = PyPlot.pywrap(PyPlot.pyimport("matplotlib.cm"))
|
||||
const pynp = PyPlot.pywrap(PyPlot.pyimport("numpy"))
|
||||
pynp.seterr(invalid="ignore")
|
||||
const pytransforms = PyPlot.pywrap(PyPlot.pyimport("matplotlib.transforms"))
|
||||
const pycollections = PyPlot.pywrap(PyPlot.pyimport("matplotlib.collections"))
|
||||
const pyart3d = PyPlot.pywrap(PyPlot.pyimport("mpl_toolkits.mplot3d.art3d"))
|
||||
end
|
||||
|
||||
# we don't want every command to update the figure
|
||||
@@ -135,7 +141,7 @@ function py_path(x, y)
|
||||
mat[i,1] = x[i]
|
||||
mat[i,2] = y[i]
|
||||
nan = !ok(x[i], y[i])
|
||||
codes[i] = if nan
|
||||
codes[i] = if nan && i>1
|
||||
_path_CLOSEPOLY
|
||||
else
|
||||
lastnan ? _path_MOVETO : _path_LINETO
|
||||
@@ -184,13 +190,13 @@ function py_stepstyle(seriestype::Symbol)
|
||||
return "default"
|
||||
end
|
||||
|
||||
# untested... return a FontProperties object from a Plots.Font
|
||||
function py_font(font::Font)
|
||||
pyfont.pymember("FontProperties")(
|
||||
family = font.family,
|
||||
size = font.size
|
||||
)
|
||||
end
|
||||
# # untested... return a FontProperties object from a Plots.Font
|
||||
# function py_font(font::Font)
|
||||
# pyfont.pymember("FontProperties")(
|
||||
# family = font.family,
|
||||
# size = font.size
|
||||
# )
|
||||
# end
|
||||
|
||||
function get_locator_and_formatter(vals::AVec)
|
||||
pyticker.pymember("FixedLocator")(1:length(vals)), pyticker.pymember("FixedFormatter")(vals)
|
||||
@@ -323,6 +329,10 @@ function py_bbox_title(ax)
|
||||
bb
|
||||
end
|
||||
|
||||
function py_dpi_scale(plt::Plot{PyPlotBackend}, ptsz)
|
||||
ptsz * DPI / plt[:dpi]
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Create the window/figure for this backend.
|
||||
@@ -339,12 +349,14 @@ function _create_backend_figure(plt::Plot{PyPlotBackend})
|
||||
end
|
||||
|
||||
# clear the figure
|
||||
PyPlot.clf()
|
||||
# PyPlot.clf()
|
||||
fig
|
||||
end
|
||||
|
||||
# Set up the subplot within the backend object.
|
||||
function _initialize_subplot(plt::Plot{PyPlotBackend}, sp::Subplot{PyPlotBackend})
|
||||
# function _initialize_subplot(plt::Plot{PyPlotBackend}, sp::Subplot{PyPlotBackend})
|
||||
|
||||
function py_init_subplot(plt::Plot{PyPlotBackend}, sp::Subplot{PyPlotBackend})
|
||||
fig = plt.o
|
||||
proj = sp[:projection]
|
||||
proj = (proj in (nothing,:none) ? nothing : string(proj))
|
||||
@@ -364,7 +376,9 @@ end
|
||||
|
||||
# function _series_added(pkg::PyPlotBackend, plt::Plot, d::KW)
|
||||
# TODO: change this to accept Subplot??
|
||||
function _series_added(plt::Plot{PyPlotBackend}, series::Series)
|
||||
# function _series_added(plt::Plot{PyPlotBackend}, series::Series)
|
||||
|
||||
function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
d = series.d
|
||||
st = d[:seriestype]
|
||||
sp = d[:subplot]
|
||||
@@ -406,17 +420,49 @@ 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 = py_dpi_scale(plt, 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)
|
||||
kw = KW(
|
||||
:label => d[:label],
|
||||
:zorder => plt.n,
|
||||
:cmap => py_linecolormap(d),
|
||||
:linewidth => py_dpi_scale(plt, d[:linewidth]),
|
||||
:linestyle => py_linestyle(st, d[:linestyle])
|
||||
)
|
||||
handle = if is3d(st)
|
||||
for i=1:n
|
||||
segments[i] = [(cycle(x,i), cycle(y,i), cycle(z,i)), (cycle(x,i+1), cycle(y,i+1), cycle(z,i+1))]
|
||||
end
|
||||
lc = pyart3d.Line3DCollection(segments; kw...)
|
||||
lc[:set_array](d[:line_z])
|
||||
ax[:add_collection3d](lc, zs=z) #, zdir='y')
|
||||
lc
|
||||
else
|
||||
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; kw...)
|
||||
lc[:set_array](d[:line_z])
|
||||
ax[:add_collection](lc)
|
||||
lc
|
||||
end
|
||||
push!(handles, handle)
|
||||
needs_colorbar = true
|
||||
end
|
||||
|
||||
a = d[:arrow]
|
||||
if a != nothing && !is3d(st) # TODO: handle 3d later
|
||||
@@ -429,7 +475,7 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
|
||||
:shrinkB => 0,
|
||||
:edgecolor => py_linecolor(d),
|
||||
:facecolor => py_linecolor(d),
|
||||
:linewidth => d[:linewidth],
|
||||
:linewidth => py_dpi_scale(plt, d[:linewidth]),
|
||||
:linestyle => py_linestyle(st, d[:linestyle]),
|
||||
)
|
||||
add_arrows(x, y) do xyprev, xy
|
||||
@@ -445,47 +491,47 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
|
||||
end
|
||||
end
|
||||
|
||||
if st == :bar
|
||||
bw = d[:bar_width]
|
||||
if bw == nothing
|
||||
bw = mean(diff(isvertical(d) ? x : y))
|
||||
end
|
||||
extrakw[isvertical(d) ? :width : :height] = bw
|
||||
fr = get(d, :fillrange, nothing)
|
||||
if fr != nothing
|
||||
extrakw[:bottom] = fr
|
||||
d[:fillrange] = nothing
|
||||
end
|
||||
handle = ax[isvertical(d) ? :bar : :barh](x, y;
|
||||
label = d[:label],
|
||||
zorder = plt.n,
|
||||
color = py_fillcolor(d),
|
||||
edgecolor = py_linecolor(d),
|
||||
linewidth = d[:linewidth],
|
||||
align = d[:bar_edges] ? "edge" : "center",
|
||||
extrakw...
|
||||
)[1]
|
||||
push!(handles, handle)
|
||||
end
|
||||
# if st == :bar
|
||||
# bw = d[:bar_width]
|
||||
# if bw == nothing
|
||||
# bw = mean(diff(isvertical(d) ? x : y))
|
||||
# end
|
||||
# extrakw[isvertical(d) ? :width : :height] = bw
|
||||
# fr = get(d, :fillrange, nothing)
|
||||
# if fr != nothing
|
||||
# extrakw[:bottom] = fr
|
||||
# d[:fillrange] = nothing
|
||||
# end
|
||||
# handle = ax[isvertical(d) ? :bar : :barh](x, y;
|
||||
# label = d[:label],
|
||||
# zorder = plt.n,
|
||||
# color = py_fillcolor(d),
|
||||
# edgecolor = py_linecolor(d),
|
||||
# linewidth = d[:linewidth],
|
||||
# align = d[:bar_edges] ? "edge" : "center",
|
||||
# extrakw...
|
||||
# )[1]
|
||||
# push!(handles, handle)
|
||||
# end
|
||||
|
||||
if st == :sticks
|
||||
extrakw[isvertical(d) ? :width : :height] = 0.0
|
||||
handle = ax[isvertical(d) ? :bar : :barh](x, y;
|
||||
label = d[:label],
|
||||
zorder = plt.n,
|
||||
color = py_linecolor(d),
|
||||
edgecolor = py_linecolor(d),
|
||||
linewidth = d[:linewidth],
|
||||
align = "center",
|
||||
extrakw...
|
||||
)[1]
|
||||
push!(handles, handle)
|
||||
end
|
||||
# if st == :sticks
|
||||
# extrakw[isvertical(d) ? :width : :height] = 0.0
|
||||
# handle = ax[isvertical(d) ? :bar : :barh](x, y;
|
||||
# label = d[:label],
|
||||
# zorder = plt.n,
|
||||
# color = py_linecolor(d),
|
||||
# edgecolor = py_linecolor(d),
|
||||
# linewidth = d[:linewidth],
|
||||
# align = "center",
|
||||
# extrakw...
|
||||
# )[1]
|
||||
# push!(handles, handle)
|
||||
# end
|
||||
|
||||
# add markers?
|
||||
if d[:markershape] != :none && st in (:path, :scatter, :path3d,
|
||||
:scatter3d, :steppre, :steppost,
|
||||
:bar, :sticks)
|
||||
:bar)
|
||||
extrakw = KW()
|
||||
if d[:marker_z] == nothing
|
||||
extrakw[:c] = py_color_fix(py_markercolor(d), x)
|
||||
@@ -493,13 +539,13 @@ 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
|
||||
needs_colorbar = true
|
||||
end
|
||||
xyargs = if st in (:bar, :sticks) && !isvertical(d)
|
||||
xyargs = if st == :bar && !isvertical(d)
|
||||
(y, x)
|
||||
else
|
||||
xyargs
|
||||
@@ -508,66 +554,66 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
|
||||
label = d[:label],
|
||||
zorder = plt.n + 0.5,
|
||||
marker = py_marker(d[:markershape]),
|
||||
s = d[:markersize] .^ 2,
|
||||
s = py_dpi_scale(plt, d[:markersize] .^ 2),
|
||||
edgecolors = py_markerstrokecolor(d),
|
||||
linewidths = d[:markerstrokewidth],
|
||||
linewidths = py_dpi_scale(plt, d[:markerstrokewidth]),
|
||||
extrakw...
|
||||
)
|
||||
push!(handles, handle)
|
||||
end
|
||||
|
||||
if st == :histogram
|
||||
handle = ax[:hist](y;
|
||||
label = d[:label],
|
||||
zorder = plt.n,
|
||||
color = py_fillcolor(d),
|
||||
edgecolor = py_linecolor(d),
|
||||
linewidth = d[:linewidth],
|
||||
bins = d[:bins],
|
||||
normed = d[:normalize],
|
||||
weights = d[:weights],
|
||||
orientation = (isvertical(d) ? "vertical" : "horizontal"),
|
||||
histtype = (d[:bar_position] == :stack ? "barstacked" : "bar")
|
||||
)[3]
|
||||
push!(handles, handle)
|
||||
# if st == :histogram
|
||||
# handle = ax[:hist](y;
|
||||
# label = d[:label],
|
||||
# zorder = plt.n,
|
||||
# color = py_fillcolor(d),
|
||||
# edgecolor = py_linecolor(d),
|
||||
# linewidth = d[:linewidth],
|
||||
# bins = d[:bins],
|
||||
# normed = d[:normalize],
|
||||
# weights = d[:weights],
|
||||
# orientation = (isvertical(d) ? "vertical" : "horizontal"),
|
||||
# histtype = (d[:bar_position] == :stack ? "barstacked" : "bar")
|
||||
# )[3]
|
||||
# push!(handles, handle)
|
||||
|
||||
# expand the extrema... handle is a list of Rectangle objects
|
||||
for rect in handle
|
||||
xmin, ymin, xmax, ymax = rect[:get_bbox]()[:extents]
|
||||
expand_extrema!(sp, xmin, xmax, ymin, ymax)
|
||||
# expand_extrema!(sp[:xaxis], (xmin, xmax))
|
||||
# expand_extrema!(sp[:yaxis], (ymin, ymax))
|
||||
end
|
||||
end
|
||||
# # expand the extrema... handle is a list of Rectangle objects
|
||||
# for rect in handle
|
||||
# xmin, ymin, xmax, ymax = rect[:get_bbox]()[:extents]
|
||||
# expand_extrema!(sp, xmin, xmax, ymin, ymax)
|
||||
# # expand_extrema!(sp[:xaxis], (xmin, xmax))
|
||||
# # expand_extrema!(sp[:yaxis], (ymin, ymax))
|
||||
# end
|
||||
# end
|
||||
|
||||
if st == :histogram2d
|
||||
clims = sp[:clims]
|
||||
if isa(clims, Tuple) && length(clims) == 2
|
||||
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
|
||||
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
||||
end
|
||||
handle = ax[:hist2d](x, y;
|
||||
label = d[:label],
|
||||
zorder = plt.n,
|
||||
bins = d[:bins],
|
||||
normed = d[:normalize],
|
||||
weights = d[:weights],
|
||||
cmap = py_fillcolormap(d), # applies to the pcolorfast object
|
||||
extrakw...
|
||||
)[4]
|
||||
push!(handles, handle)
|
||||
needs_colorbar = true
|
||||
# if st == :histogram2d
|
||||
# clims = sp[:clims]
|
||||
# if is_2tuple(clims)
|
||||
# isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
|
||||
# isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
||||
# end
|
||||
# handle = ax[:hist2d](x, y;
|
||||
# label = d[:label],
|
||||
# zorder = plt.n,
|
||||
# bins = d[:bins],
|
||||
# normed = d[:normalize],
|
||||
# weights = d[:weights],
|
||||
# cmap = py_fillcolormap(d), # applies to the pcolorfast object
|
||||
# extrakw...
|
||||
# )[4]
|
||||
# push!(handles, handle)
|
||||
# needs_colorbar = true
|
||||
|
||||
# expand the extrema... handle is a AxesImage object
|
||||
expand_extrema!(sp, handle[:get_extent]()...)
|
||||
# xmin, xmax, ymin, ymax = handle[:get_extent]()
|
||||
# expand_extrema!(sp[:xaxis], (xmin, xmax))
|
||||
# expand_extrema!(sp[:yaxis], (ymin, ymax))
|
||||
end
|
||||
# # expand the extrema... handle is a AxesImage object
|
||||
# expand_extrema!(sp, handle[:get_extent]()...)
|
||||
# # xmin, xmax, ymin, ymax = handle[:get_extent]()
|
||||
# # expand_extrema!(sp[:xaxis], (xmin, xmax))
|
||||
# # expand_extrema!(sp[:yaxis], (ymin, ymax))
|
||||
# end
|
||||
|
||||
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
|
||||
@@ -575,7 +621,7 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
|
||||
label = d[:label],
|
||||
zorder = plt.n,
|
||||
gridsize = d[:bins],
|
||||
linewidths = d[:linewidth],
|
||||
linewidths = py_dpi_scale(plt, d[:linewidth]),
|
||||
edgecolors = py_linecolor(d),
|
||||
cmap = py_fillcolormap(d), # applies to the pcolorfast object
|
||||
extrakw...
|
||||
@@ -584,17 +630,17 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
|
||||
needs_colorbar = true
|
||||
end
|
||||
|
||||
if st in (:hline,:vline)
|
||||
for yi in d[:y]
|
||||
func = ax[st == :hline ? :axhline : :axvline]
|
||||
handle = func(yi;
|
||||
linewidth=d[:linewidth],
|
||||
color=py_linecolor(d),
|
||||
linestyle=py_linestyle(st, d[:linestyle])
|
||||
)
|
||||
push!(handles, handle)
|
||||
end
|
||||
end
|
||||
# if st in (:hline,:vline)
|
||||
# for yi in d[:y]
|
||||
# func = ax[st == :hline ? :axhline : :axvline]
|
||||
# handle = func(yi;
|
||||
# linewidth=d[:linewidth],
|
||||
# color=py_linecolor(d),
|
||||
# linestyle=py_linestyle(st, d[:linestyle])
|
||||
# )
|
||||
# push!(handles, handle)
|
||||
# end
|
||||
# end
|
||||
|
||||
if st in (:contour, :contour3d)
|
||||
# z = z.surf'
|
||||
@@ -602,7 +648,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
|
||||
@@ -615,7 +661,7 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
|
||||
handle = ax[:contour](x, y, z, levelargs...;
|
||||
label = d[:label],
|
||||
zorder = plt.n,
|
||||
linewidths = d[:linewidth],
|
||||
linewidths = py_dpi_scale(plt, d[:linewidth]),
|
||||
linestyles = py_linestyle(st, d[:linestyle]),
|
||||
cmap = py_linecolormap(d),
|
||||
extrakw...
|
||||
@@ -648,7 +694,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
|
||||
@@ -662,7 +708,7 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
|
||||
zorder = plt.n,
|
||||
rstride = 1,
|
||||
cstride = 1,
|
||||
linewidth = d[:linewidth],
|
||||
linewidth = py_dpi_scale(plt, d[:linewidth]),
|
||||
edgecolor = py_linecolor(d),
|
||||
extrakw...
|
||||
)
|
||||
@@ -690,7 +736,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
|
||||
@@ -698,7 +744,7 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
|
||||
label = d[:label],
|
||||
zorder = plt.n,
|
||||
cmap = py_fillcolormap(d),
|
||||
linewidth = d[:linewidth],
|
||||
linewidth = py_dpi_scale(plt, d[:linewidth]),
|
||||
edgecolor = py_linecolor(d),
|
||||
extrakw...
|
||||
)
|
||||
@@ -744,7 +790,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
|
||||
@@ -775,9 +821,9 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
|
||||
patches = pypatches.pymember("PathPatch")(path;
|
||||
label = d[:label],
|
||||
zorder = plt.n,
|
||||
edgecolor = py_markerstrokecolor(d),
|
||||
facecolor = py_markercolor(d),
|
||||
linewidth = d[:markerstrokewidth],
|
||||
edgecolor = py_linecolor(d),
|
||||
facecolor = py_fillcolor(d),
|
||||
linewidth = py_dpi_scale(plt, d[:linewidth]),
|
||||
fill = true
|
||||
)
|
||||
handle = ax[:add_patch](patches)
|
||||
@@ -852,28 +898,28 @@ end
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
function update_limits!(sp::Subplot{PyPlotBackend}, series::Series, letters)
|
||||
for letter in letters
|
||||
py_set_lims(sp.o, sp[Symbol(letter, :axis)])
|
||||
end
|
||||
end
|
||||
# function update_limits!(sp::Subplot{PyPlotBackend}, series::Series, letters)
|
||||
# for letter in letters
|
||||
# py_set_lims(sp.o, sp[Symbol(letter, :axis)])
|
||||
# end
|
||||
# end
|
||||
|
||||
function _series_updated(plt::Plot{PyPlotBackend}, series::Series)
|
||||
d = series.d
|
||||
for handle in d[:serieshandle]
|
||||
if is3d(series)
|
||||
handle[:set_data](d[:x], d[:y])
|
||||
handle[:set_3d_properties](d[:z])
|
||||
else
|
||||
try
|
||||
handle[:set_data](d[:x], d[:y])
|
||||
catch
|
||||
handle[:set_offsets](hcat(d[:x], d[:y]))
|
||||
end
|
||||
end
|
||||
end
|
||||
update_limits!(d[:subplot], series, is3d(series) ? (:x,:y,:z) : (:x,:y))
|
||||
end
|
||||
# function _series_updated(plt::Plot{PyPlotBackend}, series::Series)
|
||||
# d = series.d
|
||||
# for handle in get(d, :serieshandle, [])
|
||||
# if is3d(series)
|
||||
# handle[:set_data](d[:x], d[:y])
|
||||
# handle[:set_3d_properties](d[:z])
|
||||
# else
|
||||
# try
|
||||
# handle[:set_data](d[:x], d[:y])
|
||||
# catch
|
||||
# handle[:set_offsets](hcat(d[:x], d[:y]))
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# update_limits!(d[:subplot], series, is3d(series) ? (:x,:y,:z) : (:x,:y))
|
||||
# end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
@@ -912,7 +958,10 @@ function py_compute_axis_minval(axis::Axis)
|
||||
minval = 1.0
|
||||
sp = axis.sp
|
||||
for series in series_list(axis.sp)
|
||||
minval = min(minval, minimum(abs(series.d[axis[:letter]])))
|
||||
v = series.d[axis[:letter]]
|
||||
if !isempty(v)
|
||||
minval = min(minval, minimum(abs(v)))
|
||||
end
|
||||
end
|
||||
|
||||
# now if the axis limits go to a smaller abs value, use that instead
|
||||
@@ -963,19 +1012,31 @@ end
|
||||
|
||||
|
||||
function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
# update the specs
|
||||
# update the fig
|
||||
w, h = plt[:size]
|
||||
fig = plt.o
|
||||
fig[:set_size_inches](px2inch(w), px2inch(h), forward = true)
|
||||
fig[:clear]()
|
||||
# fig[:set_size_inches](px2inch(w), px2inch(h), forward = true)
|
||||
dpi = plt[:dpi]
|
||||
fig[:set_size_inches](w/dpi, h/dpi, forward = true)
|
||||
fig[:set_facecolor](py_color(plt[:background_color_outside]))
|
||||
fig[:set_dpi](DPI)
|
||||
fig[:set_dpi](dpi)
|
||||
|
||||
# resize the window
|
||||
PyPlot.plt[:get_current_fig_manager]()[:resize](w, h)
|
||||
|
||||
# initialize subplots
|
||||
for sp in plt.subplots
|
||||
py_init_subplot(plt, sp)
|
||||
end
|
||||
|
||||
# add the series
|
||||
for series in plt.series_list
|
||||
py_add_series(plt, series)
|
||||
end
|
||||
|
||||
# update subplots
|
||||
for sp in plt.subplots
|
||||
# ax = getAxis(sp)
|
||||
ax = sp.o
|
||||
if ax == nothing
|
||||
continue
|
||||
@@ -989,16 +1050,16 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
# title
|
||||
if sp[:title] != ""
|
||||
loc = lowercase(string(sp[:title_location]))
|
||||
field = if loc == "left"
|
||||
func = if loc == "left"
|
||||
:_left_title
|
||||
elseif loc == "right"
|
||||
:_right_title
|
||||
else
|
||||
:title
|
||||
end
|
||||
ax[field][:set_text](sp[:title])
|
||||
ax[field][:set_fontsize](sp[:titlefont].pointsize)
|
||||
ax[field][:set_color](py_color(sp[:foreground_color_title]))
|
||||
ax[func][:set_text](sp[:title])
|
||||
ax[func][:set_fontsize](py_dpi_scale(plt, sp[:titlefont].pointsize))
|
||||
ax[func][:set_color](py_color(sp[:foreground_color_title]))
|
||||
# ax[:set_title](sp[:title], loc = loc)
|
||||
end
|
||||
|
||||
@@ -1014,9 +1075,9 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
if get(axis.d, :flip, false)
|
||||
ax[Symbol("invert_", letter, "axis")]()
|
||||
end
|
||||
ax[axissym][:label][:set_fontsize](axis[:guidefont].pointsize)
|
||||
ax[axissym][:label][:set_fontsize](py_dpi_scale(plt, axis[:guidefont].pointsize))
|
||||
for lab in ax[Symbol("get_", letter, "ticklabels")]()
|
||||
lab[:set_fontsize](axis[:tickfont].pointsize)
|
||||
lab[:set_fontsize](py_dpi_scale(plt, axis[:tickfont].pointsize))
|
||||
lab[:set_rotation](axis[:rotation])
|
||||
end
|
||||
if sp[:grid]
|
||||
@@ -1094,7 +1155,7 @@ function py_add_annotations(sp::Subplot{PyPlotBackend}, x, y, val::PlotText)
|
||||
horizontalalignment = val.font.halign == :hcenter ? "center" : string(val.font.halign),
|
||||
verticalalignment = val.font.valign == :vcenter ? "center" : string(val.font.valign),
|
||||
rotation = val.font.rotation * 180 / π,
|
||||
size = val.font.pointsize,
|
||||
size = py_dpi_scale(sp.plt, val.font.pointsize),
|
||||
zorder = 999
|
||||
)
|
||||
end
|
||||
@@ -1137,7 +1198,7 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
|
||||
if should_add_to_legend(series)
|
||||
# add a line/marker and a label
|
||||
push!(handles, if series.d[:seriestype] == :histogram
|
||||
PyPlot.plt[:Line2D]((0,1),(0,0), color=py_fillcolor(series.d), linewidth=4)
|
||||
PyPlot.plt[:Line2D]((0,1),(0,0), color=py_fillcolor(series.d), linewidth=py_dpi_scale(plt, 4))
|
||||
else
|
||||
series.d[:serieshandle][1]
|
||||
end)
|
||||
@@ -1151,7 +1212,7 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
|
||||
labels,
|
||||
loc = get(_pyplot_legend_pos, leg, "best"),
|
||||
scatterpoints = 1,
|
||||
fontsize = sp[:legendfont].pointsize
|
||||
fontsize = py_dpi_scale(plt, sp[:legendfont].pointsize)
|
||||
# framealpha = 0.6
|
||||
)
|
||||
leg[:set_zorder](1000)
|
||||
@@ -1224,7 +1285,7 @@ for (mime, fmt) in _pyplot_mimeformats
|
||||
# figsize = map(px2inch, plt[:size]),
|
||||
facecolor = fig.o["get_facecolor"](),
|
||||
edgecolor = "none",
|
||||
dpi = DPI
|
||||
dpi = plt[:dpi]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
+53
-182
@@ -4,12 +4,12 @@
|
||||
supported_args(::UnicodePlotsBackend) = merge_with_base_supported([
|
||||
:label,
|
||||
:legend,
|
||||
:seriescolor, :seriesalpha,
|
||||
:seriescolor,
|
||||
:seriesalpha,
|
||||
:linestyle,
|
||||
:markershape,
|
||||
:bins,
|
||||
:title,
|
||||
:window_title,
|
||||
:guide, :lims,
|
||||
])
|
||||
supported_types(::UnicodePlotsBackend) = [
|
||||
@@ -28,19 +28,19 @@ warnOnUnsupported_args(pkg::UnicodePlotsBackend, d::KW) = nothing
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function _initialize_backend(::UnicodePlotsBackend; kw...)
|
||||
@eval begin
|
||||
import UnicodePlots
|
||||
export UnicodePlots
|
||||
end
|
||||
@eval begin
|
||||
import UnicodePlots
|
||||
export UnicodePlots
|
||||
end
|
||||
end
|
||||
|
||||
# -------------------------------
|
||||
|
||||
# convert_size_from_pixels(sz) =
|
||||
|
||||
# do all the magic here... build it all at once, since we need to know about all the series at the very beginning
|
||||
function rebuildUnicodePlot!(plt::Plot)
|
||||
plt.o = []
|
||||
|
||||
for sp in plt.subplots
|
||||
xaxis = sp[:xaxis]
|
||||
yaxis = sp[:yaxis]
|
||||
@@ -58,12 +58,14 @@ function rebuildUnicodePlot!(plt::Plot)
|
||||
|
||||
# create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
|
||||
width, height = plt[:size]
|
||||
o = UnicodePlots.Plot(x, y;
|
||||
canvas_type = isijulia() ? UnicodePlots.AsciiCanvas : UnicodePlots.BrailleCanvas
|
||||
o = UnicodePlots.Plot(x, y, canvas_type;
|
||||
width = width,
|
||||
height = height,
|
||||
title = sp[:title],
|
||||
xlim = xlim,
|
||||
ylim = ylim
|
||||
ylim = ylim,
|
||||
border = isijulia() ? :ascii : :solid
|
||||
)
|
||||
|
||||
# set the axis labels
|
||||
@@ -80,213 +82,82 @@ function rebuildUnicodePlot!(plt::Plot)
|
||||
end
|
||||
end
|
||||
|
||||
# # do all the magic here... build it all at once, since we need to know about all the series at the very beginning
|
||||
# function rebuildUnicodePlot!(plt::Plot)
|
||||
#
|
||||
# # figure out the plotting area xlim = [xmin, xmax] and ylim = [ymin, ymax]
|
||||
# sargs = plt.seriesargs
|
||||
# iargs = plt.attr
|
||||
#
|
||||
# # get the x/y limits
|
||||
# if get(iargs, :xlims, :auto) == :auto
|
||||
# xlim = [Inf, -Inf]
|
||||
# for d in sargs
|
||||
# _expand_limits(xlim, d[:x])
|
||||
# end
|
||||
# else
|
||||
# xmin, xmax = iargs[:xlims]
|
||||
# xlim = [xmin, xmax]
|
||||
# end
|
||||
#
|
||||
# if get(iargs, :ylims, :auto) == :auto
|
||||
# ylim = [Inf, -Inf]
|
||||
# for d in sargs
|
||||
# _expand_limits(ylim, d[:y])
|
||||
# end
|
||||
# else
|
||||
# ymin, ymax = iargs[:ylims]
|
||||
# ylim = [ymin, ymax]
|
||||
# end
|
||||
#
|
||||
# # we set x/y to have a single point, since we need to create the plot with some data.
|
||||
# # since this point is at the bottom left corner of the plot, it shouldn't actually be shown
|
||||
# x = Float64[xlim[1]]
|
||||
# y = Float64[ylim[1]]
|
||||
#
|
||||
# # create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
|
||||
# width, height = iargs[:size]
|
||||
# o = UnicodePlots.Plot(x, y; width = width,
|
||||
# height = height,
|
||||
# title = iargs[:title],
|
||||
# # labels = iargs[:legend],
|
||||
# xlim = xlim,
|
||||
# ylim = ylim)
|
||||
#
|
||||
# # set the axis labels
|
||||
# UnicodePlots.xlabel!(o, iargs[:xguide])
|
||||
# UnicodePlots.ylabel!(o, iargs[:yguide])
|
||||
#
|
||||
# # now use the ! functions to add to the plot
|
||||
# for d in sargs
|
||||
# addUnicodeSeries!(o, d, iargs[:legend] != :none, xlim, ylim)
|
||||
# end
|
||||
#
|
||||
# # save the object
|
||||
# plt.o = o
|
||||
# end
|
||||
|
||||
|
||||
# add a single series
|
||||
function addUnicodeSeries!(o, d::KW, addlegend::Bool, xlim, ylim)
|
||||
|
||||
# get the function, or special handling for step/bar/hist
|
||||
st = d[:seriestype]
|
||||
|
||||
# handle hline/vline separately
|
||||
if st in (:hline,:vline)
|
||||
for yi in d[:y]
|
||||
if st == :hline
|
||||
UnicodePlots.lineplot!(o, xlim, [yi,yi])
|
||||
else
|
||||
UnicodePlots.lineplot!(o, [yi,yi], ylim)
|
||||
end
|
||||
end
|
||||
return
|
||||
|
||||
# elseif st == :bar
|
||||
# UnicodePlots.barplot!(o, d[:x], d[:y])
|
||||
# return
|
||||
|
||||
# elseif st == :histogram
|
||||
# UnicodePlots.histogram!(o, d[:y], bins = d[:bins])
|
||||
# return
|
||||
|
||||
elseif st == :histogram2d
|
||||
if st == :histogram2d
|
||||
UnicodePlots.densityplot!(o, d[:x], d[:y])
|
||||
return
|
||||
|
||||
end
|
||||
|
||||
stepstyle = :post
|
||||
if st == :path
|
||||
func = UnicodePlots.lineplot!
|
||||
elseif st == :scatter || d[:markershape] != :none
|
||||
func = UnicodePlots.scatterplot!
|
||||
elseif st == :steppost
|
||||
func = UnicodePlots.stairs!
|
||||
elseif st == :steppre
|
||||
func = UnicodePlots.stairs!
|
||||
stepstyle = :pre
|
||||
else
|
||||
error("Linestyle $st not supported by UnicodePlots")
|
||||
end
|
||||
if st == :path
|
||||
func = UnicodePlots.lineplot!
|
||||
elseif st == :scatter || d[:markershape] != :none
|
||||
func = UnicodePlots.scatterplot!
|
||||
else
|
||||
error("Linestyle $st not supported by UnicodePlots")
|
||||
end
|
||||
|
||||
# get the series data and label
|
||||
x, y = [collect(float(d[s])) for s in (:x, :y)]
|
||||
label = addlegend ? d[:label] : ""
|
||||
# get the series data and label
|
||||
x, y = [collect(float(d[s])) for s in (:x, :y)]
|
||||
label = addlegend ? d[:label] : ""
|
||||
|
||||
# if we happen to pass in allowed color symbols, great... otherwise let UnicodePlots decide
|
||||
color = d[:linecolor] in UnicodePlots.color_cycle ? d[:linecolor] : :auto
|
||||
# if we happen to pass in allowed color symbols, great... otherwise let UnicodePlots decide
|
||||
color = d[:linecolor] in UnicodePlots.color_cycle ? d[:linecolor] : :auto
|
||||
|
||||
# add the series
|
||||
func(o, x, y; color = color, name = label, style = stepstyle)
|
||||
# add the series
|
||||
func(o, x, y; color = color, name = label)
|
||||
end
|
||||
|
||||
|
||||
# function handlePlotColors(::UnicodePlotsBackend, d::KW)
|
||||
# # TODO: something special for unicodeplots, since it doesn't take kindly to people messing with its color palette
|
||||
# d[:color_palette] = [RGB(0,0,0)]
|
||||
# end
|
||||
|
||||
# -------------------------------
|
||||
|
||||
|
||||
# function _create_plot(pkg::UnicodePlotsBackend, d::KW)
|
||||
# plt = Plot(nothing, pkg, 0, d, KW[])
|
||||
|
||||
function _create_backend_figure(plt::Plot{UnicodePlotsBackend})
|
||||
# do we want to give a new default size?
|
||||
# if !haskey(plt.attr, :size) || plt.attr[:size] == default(:size)
|
||||
# plt.attr[:size] = (60,20)
|
||||
# end
|
||||
w, h = plt[:size]
|
||||
plt.attr[:size] = div(w, 10), div(h, 20)
|
||||
plt.attr[:color_palette] = [RGB(0,0,0)]
|
||||
nothing
|
||||
|
||||
# plt
|
||||
end
|
||||
|
||||
# function _series_added(plt::Plot{UnicodePlotsBackend}, series::Series)
|
||||
# d = series.d
|
||||
# # TODO don't need these once the "bar" series recipe is done
|
||||
# if d[:seriestype] in (:sticks, :bar)
|
||||
# d = barHack(; d...)
|
||||
# elseif d[:seriestype] == :histogram
|
||||
# d = barHack(; histogramHack(; d...)...)
|
||||
# end
|
||||
# # push!(plt.seriesargs, d)
|
||||
# # plt
|
||||
# end
|
||||
#
|
||||
#
|
||||
# function _update_plot_object(plt::Plot{UnicodePlotsBackend}, d::KW)
|
||||
# for k in (:title, :xguide, :yguide, :xlims, :ylims)
|
||||
# if haskey(d, k)
|
||||
# plt.attr[k] = d[k]
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
|
||||
# -------------------------------
|
||||
|
||||
# since this is such a hack, it's only callable using `png`... should error during normal `writemime`
|
||||
function png(plt::AbstractPlot{UnicodePlotsBackend}, fn::AbstractString)
|
||||
fn = addExtension(fn, "png")
|
||||
fn = addExtension(fn, "png")
|
||||
|
||||
# make some whitespace and show the plot
|
||||
println("\n\n\n\n\n\n")
|
||||
gui(plt)
|
||||
# make some whitespace and show the plot
|
||||
println("\n\n\n\n\n\n")
|
||||
gui(plt)
|
||||
|
||||
# @osx_only begin
|
||||
@compat @static if is_apple()
|
||||
# BEGIN HACK
|
||||
# @osx_only begin
|
||||
@compat @static if is_apple()
|
||||
# BEGIN HACK
|
||||
|
||||
# wait while the plot gets drawn
|
||||
sleep(0.5)
|
||||
# wait while the plot gets drawn
|
||||
sleep(0.5)
|
||||
|
||||
# use osx screen capture when my terminal is maximized and cursor starts at the bottom (I know, right?)
|
||||
# TODO: compute size of plot to adjust these numbers (or maybe implement something good??)
|
||||
run(`screencapture -R50,600,700,420 $fn`)
|
||||
# use osx screen capture when my terminal is maximized and cursor starts at the bottom (I know, right?)
|
||||
# TODO: compute size of plot to adjust these numbers (or maybe implement something good??)
|
||||
run(`screencapture -R50,600,700,420 $fn`)
|
||||
|
||||
# END HACK (phew)
|
||||
return
|
||||
end
|
||||
# END HACK (phew)
|
||||
return
|
||||
end
|
||||
|
||||
error("Can only savepng on osx with UnicodePlots (though even then I wouldn't do it)")
|
||||
error("Can only savepng on osx with UnicodePlots (though even then I wouldn't do it)")
|
||||
end
|
||||
|
||||
# -------------------------------
|
||||
|
||||
# we don't do very much for subplots... just stack them vertically
|
||||
|
||||
# function _create_subplot(subplt::Subplot{UnicodePlotsBackend}, isbefore::Bool)
|
||||
# isbefore && return false
|
||||
# true
|
||||
# end
|
||||
|
||||
|
||||
function _display(plt::Plot{UnicodePlotsBackend})
|
||||
function _update_plot_object(plt::Plot{UnicodePlotsBackend})
|
||||
w, h = plt[:size]
|
||||
plt.attr[:size] = div(w, 10), div(h, 20)
|
||||
plt.attr[:color_palette] = [RGB(0,0,0)]
|
||||
rebuildUnicodePlot!(plt)
|
||||
end
|
||||
|
||||
function _writemime(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend})
|
||||
map(show, plt.o)
|
||||
nothing
|
||||
end
|
||||
|
||||
|
||||
function _display(plt::Plot{UnicodePlotsBackend})
|
||||
map(show, plt.o)
|
||||
nothing
|
||||
end
|
||||
|
||||
# function Base.display(::PlotsDisplay, subplt::Subplot{UnicodePlotsBackend})
|
||||
# for plt in subplt.plts
|
||||
# gui(plt)
|
||||
# end
|
||||
# end
|
||||
|
||||
@@ -1,6 +1,33 @@
|
||||
|
||||
abstract ColorScheme
|
||||
|
||||
Base.getindex(scheme::ColorScheme, i::Integer) = getColor(scheme, i)
|
||||
|
||||
export
|
||||
cgrad
|
||||
|
||||
cgrad() = default_gradient()
|
||||
|
||||
function cgrad(arg, values = nothing; alpha = nothing, scale = :identity)
|
||||
colors = ColorGradient(arg, alpha=alpha).colors
|
||||
values = if values != nothing
|
||||
values
|
||||
elseif scale in (:log, :log10)
|
||||
log10(linspace(1,10,30))
|
||||
elseif scale == :log2
|
||||
log2(linspace(1,2,30))
|
||||
elseif scale == :ln
|
||||
log(linspace(1,pi,30))
|
||||
elseif scale in (:exp, :exp10)
|
||||
(exp10(linspace(0,1,30)) - 1) / 9
|
||||
else
|
||||
linspace(0, 1, length(colors))
|
||||
end
|
||||
ColorGradient(colors, values)
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
getColor(scheme::ColorScheme) = getColor(scheme, 1)
|
||||
getColorVector(scheme::ColorScheme) = [getColor(scheme)]
|
||||
|
||||
@@ -103,6 +130,12 @@ immutable ColorGradient <: ColorScheme
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
Base.getindex(cs::ColorGradient, i::Integer) = getColor(cs, i)
|
||||
Base.getindex(cs::ColorGradient, z::Number) = getColorZ(cs, z)
|
||||
|
||||
|
||||
# create a gradient from a symbol (blues, reds, etc) and vector of boundary values
|
||||
function ColorGradient{T<:Real}(s::Symbol, vals::AVec{T} = 0:0; kw...)
|
||||
haskey(_gradients, s) || error("Invalid gradient symbol. Choose from: ", sort(collect(keys(_gradients))))
|
||||
|
||||
+2
-2
@@ -303,8 +303,8 @@ PlotExample("Boxplot and Violin series recipes",
|
||||
[:(begin
|
||||
import RDatasets
|
||||
singers = RDatasets.dataset("lattice", "singer")
|
||||
violin(singers, :VoicePart, :Height, marker = (0.2, :blue, stroke(0)))
|
||||
boxplot!(singers, :VoicePart, :Height, marker = (0.3, :orange, stroke(2)))
|
||||
violin(singers, :VoicePart, :Height, line = 0, fill = (0.2, :blue))
|
||||
boxplot!(singers, :VoicePart, :Height, line = (2,:black), fill = (0.3, :orange))
|
||||
end)]
|
||||
)
|
||||
|
||||
|
||||
+43
-4
@@ -101,19 +101,46 @@ end
|
||||
|
||||
Base.show(io::IO, layout::AbstractLayout) = print(io, "$(typeof(layout))$(size(layout))")
|
||||
|
||||
make_measure_hor(n::Number) = n * w
|
||||
make_measure_hor(m::Measure) = m
|
||||
|
||||
make_measure_vert(n::Number) = n * h
|
||||
make_measure_vert(m::Measure) = m
|
||||
|
||||
|
||||
function bbox(x, y, w, h, oarg1::Symbol, originargs::Symbol...)
|
||||
oargs = vcat(oarg1, originargs...)
|
||||
orighor = :left
|
||||
origver = :top
|
||||
for oarg in oargs
|
||||
if oarg in (:left, :right)
|
||||
orighor = oarg
|
||||
elseif oarg in (:top, :bottom)
|
||||
origver = oarg
|
||||
else
|
||||
warn("Unused origin arg in bbox construction: $oarg")
|
||||
end
|
||||
end
|
||||
bbox(x, y, w, h; h_anchor = orighor, v_anchor = origver)
|
||||
end
|
||||
|
||||
# create a new bbox
|
||||
function bbox(x, y, w, h; h_anchor = :left, v_anchor = :top)
|
||||
function bbox(x, y, width, height; h_anchor = :left, v_anchor = :top)
|
||||
x = make_measure_hor(x)
|
||||
y = make_measure_vert(y)
|
||||
width = make_measure_hor(width)
|
||||
height = make_measure_vert(height)
|
||||
left = if h_anchor == :left
|
||||
x
|
||||
else
|
||||
x - w * (h_anchor == :right ? 1.0 : 0.5)
|
||||
1w - x - width
|
||||
end
|
||||
top = if v_anchor == :top
|
||||
y
|
||||
else
|
||||
y - h * (v_anchor == :bottom ? 1.0 : 0.5)
|
||||
1h - y - height
|
||||
end
|
||||
BoundingBox(left, top, w, h)
|
||||
BoundingBox(left, top, width, height)
|
||||
end
|
||||
|
||||
# this is the available area for drawing everything in this layout... as percentages of total canvas
|
||||
@@ -479,6 +506,8 @@ function build_layout(layout::GridLayout, n::Integer)
|
||||
append!(subplots, sps)
|
||||
merge!(spmap, m)
|
||||
i += length(sps)
|
||||
elseif isa(l, Subplot)
|
||||
error("Subplot exists. Cannot re-use existing layout. Please make a new one.")
|
||||
end
|
||||
i >= n && break # only add n subplots
|
||||
end
|
||||
@@ -662,6 +691,16 @@ function link_axes!(layout::GridLayout, link::Symbol)
|
||||
link_axes!(layout.grid[r,:], :yaxis)
|
||||
end
|
||||
end
|
||||
if link == :square
|
||||
sps = filter(l -> isa(l, Subplot), layout.grid)
|
||||
if !isempty(sps)
|
||||
base_axis = sps[1][:xaxis]
|
||||
for sp in sps
|
||||
link_axes!(base_axis, sp[:xaxis])
|
||||
link_axes!(base_axis, sp[:yaxis])
|
||||
end
|
||||
end
|
||||
end
|
||||
if link == :all
|
||||
link_axes!(layout.grid, :xaxis)
|
||||
link_axes!(layout.grid, :yaxis)
|
||||
|
||||
+16
-1
@@ -119,11 +119,13 @@ const _mimeformats = Dict(
|
||||
"application/pdf" => "pdf",
|
||||
"image/png" => "png",
|
||||
"application/postscript" => "ps",
|
||||
"image/svg+xml" => "svg"
|
||||
"image/svg+xml" => "svg",
|
||||
"text/plain" => "txt",
|
||||
)
|
||||
|
||||
const _best_html_output_type = KW(
|
||||
:pyplot => :png,
|
||||
:unicodeplots => :txt,
|
||||
)
|
||||
|
||||
# a backup for html... passes to svg or png depending on the html_output_format arg
|
||||
@@ -138,6 +140,8 @@ function Base.writemime(io::IO, ::MIME"text/html", plt::Plot)
|
||||
elseif output_type == :svg
|
||||
# info("writing svg to html output")
|
||||
writemime(io, MIME("image/svg+xml"), plt)
|
||||
elseif output_type == :txt
|
||||
writemime(io, MIME("text/plain"), plt)
|
||||
else
|
||||
error("only png or svg allowed. got: $output_type")
|
||||
end
|
||||
@@ -145,6 +149,12 @@ 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)
|
||||
@@ -218,6 +228,11 @@ function setup_ijulia()
|
||||
global _ijulia_output
|
||||
Dict{Compat.ASCIIString, ByteString}(_ijulia_output[1] => sprint(writemime, _ijulia_output[1], plt))
|
||||
end
|
||||
|
||||
# default text/plain passes to html... handles Interact issues
|
||||
function Base.writemime(io::IO, m::MIME"text/plain", plt::Plot)
|
||||
writemime(io, MIME("text/html"), plt)
|
||||
end
|
||||
end
|
||||
set_ijulia_output("text/html")
|
||||
end
|
||||
|
||||
+145
-79
@@ -162,8 +162,11 @@ end
|
||||
# this method recursively applies series recipes when the seriestype is not supported
|
||||
# natively by the backend
|
||||
function _apply_series_recipe(plt::Plot, d::KW)
|
||||
# replace seriestype aliases
|
||||
st = d[:seriestype]
|
||||
# @show st
|
||||
st = d[:seriestype] = get(_typeAliases, st, st)
|
||||
|
||||
# if it's natively supported, finalize processing and pass along to the backend, otherwise recurse
|
||||
if st in supported_types()
|
||||
|
||||
# getting ready to add the series... last update to subplot from anything
|
||||
@@ -172,6 +175,11 @@ function _apply_series_recipe(plt::Plot, d::KW)
|
||||
sp_idx = get_subplot_index(plt, sp)
|
||||
_update_subplot_args(plt, sp, d, sp_idx)
|
||||
|
||||
# do we want to override the series type?
|
||||
if !is3d(st) && d[:z] != nothing && (size(d[:x]) == size(d[:y]) == size(d[:z]))
|
||||
st = d[:seriestype] = (st == :scatter ? :scatter3d : :path3d)
|
||||
end
|
||||
|
||||
# change to a 3d projection for this subplot?
|
||||
if is3d(st)
|
||||
sp.attr[:projection] = "3d"
|
||||
@@ -183,6 +191,18 @@ function _apply_series_recipe(plt::Plot, d::KW)
|
||||
sp.attr[:init] = true
|
||||
end
|
||||
|
||||
# strip out series annotations (those which are based on series x/y coords)
|
||||
# and add them to the subplot attr
|
||||
sp_anns = annotations(sp[:annotations])
|
||||
anns = annotations(pop!(d, :series_annotations, []))
|
||||
if length(anns) > 0
|
||||
x, y = d[:x], d[:y]
|
||||
nx, ny, na = map(length, (x,y,anns))
|
||||
n = max(nx, ny, na)
|
||||
anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(anns[mod1(i,na)])) for i=1:n]
|
||||
end
|
||||
sp.attr[:annotations] = vcat(sp_anns, anns)
|
||||
|
||||
# adjust extrema and discrete info
|
||||
if st == :image
|
||||
w, h = size(d[:z])
|
||||
@@ -205,12 +225,13 @@ function _apply_series_recipe(plt::Plot, d::KW)
|
||||
|
||||
else
|
||||
# get a sub list of series for this seriestype
|
||||
datalist = try
|
||||
RecipesBase.apply_recipe(d, Val{st}, d[:x], d[:y], d[:z])
|
||||
catch
|
||||
warn("Exception during apply_recipe(Val{$st}, ...) with types ($(typeof(d[:x])), $(typeof(d[:y])), $(typeof(d[:z])))")
|
||||
rethrow()
|
||||
end
|
||||
datalist = RecipesBase.apply_recipe(d, Val{st}, d[:x], d[:y], d[:z])
|
||||
# datalist = try
|
||||
# RecipesBase.apply_recipe(d, Val{st}, d[:x], d[:y], d[:z])
|
||||
# catch
|
||||
# warn("Exception during apply_recipe(Val{$st}, ...) with types ($(typeof(d[:x])), $(typeof(d[:y])), $(typeof(d[:z])))")
|
||||
# rethrow()
|
||||
# end
|
||||
|
||||
# assuming there was no error, recursively apply the series recipes
|
||||
for data in datalist
|
||||
@@ -224,12 +245,15 @@ function _apply_series_recipe(plt::Plot, d::KW)
|
||||
end
|
||||
end
|
||||
|
||||
function command_idx(kw_list::AVec{KW}, kw::KW)
|
||||
kw[:series_plotindex] - kw_list[1][:series_plotindex] + 1
|
||||
end
|
||||
|
||||
|
||||
# this is the core plotting function. recursively apply recipes to build
|
||||
# a list of series KW dicts.
|
||||
# note: at entry, we only have those preprocessed args which were passed in... no default values yet
|
||||
function _plot!(plt::Plot, d::KW, args...)
|
||||
# d = plt.user_attr
|
||||
d[:plot_object] = plt
|
||||
|
||||
# the grouping mechanism is a recipe on a GroupBy object
|
||||
@@ -239,6 +263,40 @@ function _plot!(plt::Plot, d::KW, args...)
|
||||
args = (extractGroupArgs(d[:group], args...), args...)
|
||||
end
|
||||
|
||||
# if we were passed a vector/matrix of seriestypes and there's more than one row,
|
||||
# we want to duplicate the inputs, once for each seriestype row.
|
||||
kw_list = KW[]
|
||||
still_to_process = if isempty(args)
|
||||
[]
|
||||
else
|
||||
sts = get(d, :seriestype, :path)
|
||||
if typeof(sts) <: AbstractArray
|
||||
[begin
|
||||
dc = copy(d)
|
||||
dc[:seriestype] = sts[r,:]
|
||||
RecipeData(dc, args)
|
||||
end for r=1:size(sts,1)]
|
||||
else
|
||||
[RecipeData(copy(d), args)]
|
||||
end
|
||||
end
|
||||
|
||||
# remove subplot and axis args from d... they will be passed through in the kw_list
|
||||
if !isempty(args)
|
||||
for (k,v) in d
|
||||
for defdict in (_subplot_defaults,
|
||||
_axis_defaults,
|
||||
_axis_defaults_byletter)
|
||||
if haskey(defdict, k)
|
||||
delete!(d, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# --------------------------------
|
||||
# "USER RECIPES"
|
||||
# --------------------------------
|
||||
|
||||
# for plotting recipes, swap out the args and update the parameter dictionary
|
||||
# we are keeping a queue of series that still need to be processed.
|
||||
@@ -246,8 +304,6 @@ function _plot!(plt::Plot, d::KW, args...)
|
||||
# the recipe will return a list a Series objects... the ones that are
|
||||
# finished (no more args) get added to the kw_list, and the rest go into the queue
|
||||
# for processing.
|
||||
kw_list = KW[]
|
||||
still_to_process = isempty(args) ? [] : [RecipeData(copy(d), args)]
|
||||
while !isempty(still_to_process)
|
||||
|
||||
# grab the first in line to be processed and pass it through apply_recipe
|
||||
@@ -272,7 +328,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
|
||||
@@ -315,9 +376,6 @@ function _plot!(plt::Plot, d::KW, args...)
|
||||
:label => "",
|
||||
:primary => false,
|
||||
)))
|
||||
|
||||
# don't allow something else to handle it
|
||||
d[:smooth] = false
|
||||
end
|
||||
|
||||
else
|
||||
@@ -327,25 +385,62 @@ function _plot!(plt::Plot, d::KW, args...)
|
||||
end
|
||||
end
|
||||
|
||||
# merge in anything meant for plot/subplot/axis
|
||||
for kw in kw_list
|
||||
for (k,v) in kw
|
||||
for defdict in (_plot_defaults,)
|
||||
# _subplot_defaults,
|
||||
# _axis_defaults,
|
||||
# _axis_defaults_byletter)
|
||||
if haskey(defdict, k)
|
||||
d[k] = pop!(kw, k)
|
||||
# don't allow something else to handle it
|
||||
d[:smooth] = false
|
||||
|
||||
# --------------------------------
|
||||
# "PLOT RECIPES"
|
||||
# --------------------------------
|
||||
|
||||
# "plot recipe", which acts like a series type, and is processed before
|
||||
# the plot layout is created, which allows for setting layouts and other plot-wide attributes.
|
||||
# we get inputs which have been fully processed by "user recipes" and "type recipes",
|
||||
# so we can expect standard vectors, surfaces, etc. No defaults have been set yet.
|
||||
still_to_process = kw_list
|
||||
kw_list = KW[]
|
||||
while !isempty(still_to_process)
|
||||
# Grab the first in line to be processed and pass it through apply_recipe
|
||||
# to generate a list of RecipeData objects (data + attributes).
|
||||
# If we applied a "plot recipe" without error, then add the returned datalist's KWs,
|
||||
# otherwise we just add the original KW.
|
||||
next_kw = shift!(still_to_process)
|
||||
if !isa(get(next_kw, :seriestype, nothing), Symbol)
|
||||
# seriestype was never set, or it's not a Symbol, so it can't be a plot recipe
|
||||
push!(kw_list, next_kw)
|
||||
continue
|
||||
end
|
||||
try
|
||||
st = next_kw[:seriestype]
|
||||
st = next_kw[:seriestype] = get(_typeAliases, st, st)
|
||||
datalist = RecipesBase.apply_recipe(next_kw, Val{st}, plt)
|
||||
for data in datalist
|
||||
if data.d[:seriestype] == st
|
||||
error("Plot recipe $st returned the same seriestype: $(data.d)")
|
||||
end
|
||||
push!(still_to_process, data.d)
|
||||
end
|
||||
catch err
|
||||
if isa(err, MethodError)
|
||||
push!(kw_list, next_kw)
|
||||
else
|
||||
rethrow()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# --------------------------------
|
||||
# Plot/Subplot/Layout setup
|
||||
# --------------------------------
|
||||
|
||||
# merge in anything meant for the Plot
|
||||
for kw in kw_list, (k,v) in kw
|
||||
haskey(_plot_defaults, k) && (d[k] = pop!(kw, k))
|
||||
end
|
||||
|
||||
# TODO: init subplots here
|
||||
_update_plot_args(plt, d)
|
||||
if !plt.init
|
||||
plt.o = _create_backend_figure(plt)
|
||||
# DD(d)
|
||||
|
||||
# create the layout and subplots from the inputs
|
||||
plt.layout, plt.subplots, plt.spmap = build_layout(plt.attr)
|
||||
@@ -361,6 +456,9 @@ function _plot!(plt::Plot, d::KW, args...)
|
||||
# handle inset subplots
|
||||
insets = plt[:inset_subplots]
|
||||
if insets != nothing
|
||||
if !(typeof(insets) <: AVec)
|
||||
insets = [insets]
|
||||
end
|
||||
for inset in insets
|
||||
parent, bb = is_2tuple(inset) ? inset : (nothing, inset)
|
||||
P = typeof(parent)
|
||||
@@ -374,27 +472,24 @@ function _plot!(plt::Plot, d::KW, args...)
|
||||
sp = Subplot(backend(), parent=parent)
|
||||
sp.plt = plt
|
||||
sp.attr[:relative_bbox] = bb
|
||||
push!(plt.subplots, sp)
|
||||
sp.attr[:subplot_index] = length(plt.subplots)
|
||||
push!(plt.subplots, sp)
|
||||
push!(plt.inset_subplots, sp)
|
||||
end
|
||||
end
|
||||
|
||||
# we'll keep a map of subplot to an attribute override dict.
|
||||
# any series which belong to that subplot
|
||||
# Subplot/Axis attributes set by a user/series recipe apply only to the
|
||||
# Subplot object which they belong to.
|
||||
# TODO: allow matrices to still apply to all subplots
|
||||
sp_attrs = Dict{Subplot,Any}()
|
||||
for (i,kw) in enumerate(kw_list)
|
||||
# get the Subplot object to which the series belongs
|
||||
sp = get(kw, :subplot, :auto)
|
||||
sp = if sp == :auto
|
||||
mod1(i,length(plt.subplots))
|
||||
else
|
||||
slice_arg(sp, i)
|
||||
end
|
||||
sp = kw[:subplot] = get_subplot(plt, sp)
|
||||
# idx = get_subplot_index(plt, sp)
|
||||
attr = KW()
|
||||
for kw in kw_list
|
||||
# get the Subplot object to which the series belongs.
|
||||
sps = get(kw, :subplot, :auto)
|
||||
sp = get_subplot(plt, cycle(sps == :auto ? plt.subplots : plt.subplots[sps], command_idx(kw_list,kw)))
|
||||
kw[:subplot] = sp
|
||||
|
||||
attr = KW()
|
||||
for (k,v) in kw
|
||||
for defdict in (_subplot_defaults,
|
||||
_axis_defaults,
|
||||
@@ -407,60 +502,31 @@ function _plot!(plt::Plot, d::KW, args...)
|
||||
sp_attrs[sp] = attr
|
||||
end
|
||||
|
||||
|
||||
|
||||
# # just in case the backend needs to set up the plot (make it current or something)
|
||||
# _prepare_plot_object(plt)
|
||||
|
||||
# first apply any args for the subplots
|
||||
# override subplot/axis args. `sp_attrs` take precendence
|
||||
for (idx,sp) in enumerate(plt.subplots)
|
||||
# if we picked up any subplot-specific overrides, merge them here
|
||||
attr = merge(d, get(sp_attrs, sp, KW()))
|
||||
# DD(attr, "sp$idx")
|
||||
_update_subplot_args(plt, sp, attr, idx, remove_pair = false)
|
||||
end
|
||||
|
||||
|
||||
|
||||
# do we need to link any axes together?
|
||||
link_axes!(plt.layout, plt[:link])
|
||||
|
||||
# !!! note: At this point, kw_list is fully decomposed into individual series... one KW per series. !!!
|
||||
# !!! The next step is to recursively apply series recipes until the backend supports that series type !!!
|
||||
|
||||
# this is it folks!
|
||||
# TODO: we probably shouldn't use i for tracking series index, but rather explicitly track it in recipes
|
||||
for (i,kw) in enumerate(kw_list)
|
||||
command_idx = kw[:series_plotindex] - kw_list[1][:series_plotindex] + 1
|
||||
|
||||
# # get the Subplot object to which the series belongs
|
||||
# sp = get(kw, :subplot, :auto)
|
||||
# sp = if sp == :auto
|
||||
# mod1(i,length(plt.subplots))
|
||||
# else
|
||||
# slice_arg(sp, i)
|
||||
# end
|
||||
# sp = kw[:subplot] = get_subplot(plt, sp)
|
||||
# --------------------------------
|
||||
# "SERIES RECIPES"
|
||||
# --------------------------------
|
||||
|
||||
for kw in kw_list
|
||||
sp = kw[:subplot]
|
||||
idx = get_subplot_index(plt, sp)
|
||||
|
||||
# strip out series annotations (those which are based on series x/y coords)
|
||||
# and add them to the subplot attr
|
||||
sp_anns = annotations(sp[:annotations])
|
||||
anns = annotations(pop!(kw, :series_annotations, []))
|
||||
if length(anns) > 0
|
||||
x, y = kw[:x], kw[:y]
|
||||
nx, ny, na = map(length, (x,y,anns))
|
||||
n = max(nx, ny, na)
|
||||
anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(anns[mod1(i,na)])) for i=1:n]
|
||||
end
|
||||
sp.attr[:annotations] = vcat(sp_anns, anns)
|
||||
|
||||
# we update subplot args in case something like the color palatte is part of the recipe
|
||||
_update_subplot_args(plt, sp, kw, idx)
|
||||
# # we update subplot args in case something like the color palatte is part of the recipe
|
||||
# _update_subplot_args(plt, sp, kw, idx)
|
||||
|
||||
# set default values, select from attribute cycles, and generally set the final attributes
|
||||
_add_defaults!(kw, plt, sp, command_idx)
|
||||
_add_defaults!(kw, plt, sp, command_idx(kw_list,kw))
|
||||
|
||||
# now we have a fully specified series, with colors chosen. we must recursively handle
|
||||
# series recipes, which dispatch on seriestype. If a backend does not natively support a seriestype,
|
||||
@@ -471,12 +537,12 @@ function _plot!(plt::Plot, d::KW, args...)
|
||||
_apply_series_recipe(plt, kw)
|
||||
end
|
||||
|
||||
# --------------------------------
|
||||
|
||||
current(plt)
|
||||
|
||||
# note: lets ignore the show param and effectively use the semicolon at the end of the REPL statement
|
||||
# # do we want to show it?
|
||||
# if haskey(d, :show) && d[:show]
|
||||
if get(d, :show, default(:show))
|
||||
# do we want to force display?
|
||||
if plt[:show]
|
||||
gui()
|
||||
end
|
||||
|
||||
|
||||
+267
-298
@@ -98,19 +98,21 @@ end
|
||||
|
||||
# ----------------------------------------------------------------------------------
|
||||
|
||||
abstract PlotRecipe
|
||||
# abstract PlotRecipe
|
||||
|
||||
getRecipeXY(recipe::PlotRecipe) = Float64[], Float64[]
|
||||
getRecipeArgs(recipe::PlotRecipe) = ()
|
||||
# getRecipeXY(recipe::PlotRecipe) = Float64[], Float64[]
|
||||
# getRecipeArgs(recipe::PlotRecipe) = ()
|
||||
|
||||
plot(recipe::PlotRecipe, args...; kw...) = plot(getRecipeXY(recipe)..., args...; getRecipeArgs(recipe)..., kw...)
|
||||
plot!(recipe::PlotRecipe, args...; kw...) = plot!(getRecipeXY(recipe)..., args...; getRecipeArgs(recipe)..., kw...)
|
||||
plot!(plt::Plot, recipe::PlotRecipe, args...; kw...) = plot!(getRecipeXY(recipe)..., args...; getRecipeArgs(recipe)..., kw...)
|
||||
# plot(recipe::PlotRecipe, args...; kw...) = plot(getRecipeXY(recipe)..., args...; getRecipeArgs(recipe)..., kw...)
|
||||
# plot!(recipe::PlotRecipe, args...; kw...) = plot!(getRecipeXY(recipe)..., args...; getRecipeArgs(recipe)..., kw...)
|
||||
# plot!(plt::Plot, recipe::PlotRecipe, args...; kw...) = plot!(getRecipeXY(recipe)..., args...; getRecipeArgs(recipe)..., kw...)
|
||||
|
||||
num_series(x::AMat) = size(x,2)
|
||||
num_series(x) = 1
|
||||
|
||||
|
||||
RecipesBase.apply_recipe{T}(d::KW, ::Type{T}, plt::Plot) = throw(MethodError("Unmatched plot recipe: $T"))
|
||||
|
||||
# # if it's not a recipe, just do nothing and return the args
|
||||
# function RecipesBase.apply_recipe(d::KW, args...; issubplot=false)
|
||||
# if issubplot && !isempty(args) && !haskey(d, :n) && !haskey(d, :layout)
|
||||
@@ -214,8 +216,20 @@ end
|
||||
# end
|
||||
# @deps sticks path
|
||||
|
||||
function hvline_limits(axis::Axis)
|
||||
vmin, vmax = axis_limits(axis)
|
||||
if vmin >= vmax
|
||||
if isfinite(vmin)
|
||||
vmax = vmin + 1
|
||||
else
|
||||
vmin, vmax = 0.0, 1.1
|
||||
end
|
||||
end
|
||||
vmin, vmax
|
||||
end
|
||||
|
||||
@recipe function f(::Type{Val{:hline}}, x, y, z)
|
||||
xmin, xmax = axis_limits(d[:subplot][:xaxis])
|
||||
xmin, xmax = hvline_limits(d[:subplot][:xaxis])
|
||||
n = length(y)
|
||||
newx = repmat(Float64[xmin, xmax, NaN], n)
|
||||
newy = vec(Float64[yi for i=1:3,yi=y])
|
||||
@@ -227,7 +241,7 @@ end
|
||||
@deps hline path
|
||||
|
||||
@recipe function f(::Type{Val{:vline}}, x, y, z)
|
||||
ymin, ymax = axis_limits(d[:subplot][:yaxis])
|
||||
ymin, ymax = hvline_limits(d[:subplot][:yaxis])
|
||||
n = length(y)
|
||||
newx = vec(Float64[yi for i=1:3,yi=y])
|
||||
newy = repmat(Float64[ymin, ymax, NaN], n)
|
||||
@@ -338,10 +352,83 @@ end
|
||||
@deps sticks path scatter
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# bezier curves
|
||||
|
||||
# get the value of the curve point at position t
|
||||
function bezier_value(pts::AVec, t::Real)
|
||||
val = 0.0
|
||||
n = length(pts)-1
|
||||
for (i,p) in enumerate(pts)
|
||||
val += p * binomial(n, i-1) * (1-t)^(n-i+1) * t^(i-1)
|
||||
end
|
||||
val
|
||||
end
|
||||
|
||||
# create segmented bezier curves in place of line segments
|
||||
@recipe function f(::Type{Val{:curves}}, x, y, z)
|
||||
args = z != nothing ? (x,y,z) : (x,y)
|
||||
newx, newy = zeros(0), zeros(0)
|
||||
fr = d[:fillrange]
|
||||
newfr = fr != nothing ? zeros(0) : nothing
|
||||
newz = z != nothing ? zeros(0) : nothing
|
||||
lz = d[:line_z]
|
||||
newlz = lz != nothing ? zeros(0) : nothing
|
||||
npoints = pop!(d, :npoints, 30)
|
||||
|
||||
# for each line segment (point series with no NaNs), convert it into a bezier curve
|
||||
# where the points are the control points of the curve
|
||||
for rng in iter_segments(args...)
|
||||
length(rng) < 2 && continue
|
||||
ts = linspace(0, 1, npoints)
|
||||
nanappend!(newx, map(t -> bezier_value(cycle(x,rng), t), ts))
|
||||
nanappend!(newy, map(t -> bezier_value(cycle(y,rng), t), ts))
|
||||
if z != nothing
|
||||
nanappend!(newz, map(t -> bezier_value(cycle(z,rng), t), ts))
|
||||
end
|
||||
if fr != nothing
|
||||
nanappend!(newfr, map(t -> bezier_value(cycle(fr,rng), t), ts))
|
||||
end
|
||||
if lz != nothing
|
||||
lzrng = cycle(lz, rng) # the line_z's for this segment
|
||||
# @show lzrng, sizeof(lzrng) map(t -> 1+floor(Int, t * (length(rng)-1)), ts)
|
||||
# choose the line_z value of the control point just before this t
|
||||
push!(newlz, 0.0)
|
||||
append!(newlz, map(t -> lzrng[1+floor(Int, t * (length(rng)-1))], ts))
|
||||
# lzrng = vcat()
|
||||
# nanappend!(newlz, #map(t -> bezier_value(cycle(lz,rng), t), ts))
|
||||
end
|
||||
end
|
||||
|
||||
x := newx
|
||||
y := newy
|
||||
if z == nothing
|
||||
seriestype := :path
|
||||
else
|
||||
seriestype := :path3d
|
||||
z := newz
|
||||
end
|
||||
if fr != nothing
|
||||
fillrange := newfr
|
||||
end
|
||||
if lz != nothing
|
||||
line_z := newlz
|
||||
linecolor := (isa(d[:linecolor], ColorGradient) ? d[:linecolor] : default_gradient())
|
||||
end
|
||||
# Plots.DD(d)
|
||||
()
|
||||
end
|
||||
@deps curves path
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# create a bar plot as a filled step function
|
||||
@recipe function f(::Type{Val{:bar}}, x, y, z)
|
||||
# if horizontal, switch x/y
|
||||
if !isvertical(d)
|
||||
x, y = y, x
|
||||
end
|
||||
|
||||
nx, ny = length(x), length(y)
|
||||
edges = if nx == ny
|
||||
# x is centers, calc the edges
|
||||
@@ -365,36 +452,25 @@ end
|
||||
# make fillto a vector... default fills to 0
|
||||
fillto = d[:fillrange]
|
||||
if fillto == nothing
|
||||
fillto = zeros(1)
|
||||
elseif isa(fillto, Number)
|
||||
fillto = Float64[fillto]
|
||||
fillto = 0
|
||||
end
|
||||
nf = length(fillto)
|
||||
|
||||
npts = 3ny + 1
|
||||
heights = y
|
||||
x = zeros(npts)
|
||||
y = zeros(npts)
|
||||
fillrng = zeros(npts)
|
||||
|
||||
# create the path in triplets. after the first bottom-left coord of the first bar:
|
||||
# add the top-left, top-right, and bottom-right coords for each height
|
||||
x[1] = edges[1]
|
||||
y[1] = fillto[1]
|
||||
fillrng[1] = fillto[1]
|
||||
# create the bar shapes by adding x/y segments
|
||||
xseg, yseg = Segments(), Segments()
|
||||
for i=1:ny
|
||||
idx = 3i
|
||||
rng = idx-1:idx+1
|
||||
fi = fillto[mod1(i,nf)]
|
||||
x[rng] = [edges[i], edges[i+1], edges[i+1]]
|
||||
y[rng] = [heights[i], heights[i], fi]
|
||||
fillrng[rng] = [fi, fi, fi]
|
||||
fi = cycle(fillto,i)
|
||||
push!(xseg, edges[i], edges[i], edges[i+1], edges[i+1])
|
||||
push!(yseg, y[i], fi, fi, y[i])
|
||||
end
|
||||
|
||||
x := x
|
||||
y := y
|
||||
fillrange := fillrng
|
||||
seriestype := :path
|
||||
# switch back
|
||||
if !isvertical(d)
|
||||
xseg, yseg = yseg, xseg
|
||||
end
|
||||
|
||||
x := xseg.pts
|
||||
y := yseg.pts
|
||||
seriestype := :shape
|
||||
()
|
||||
end
|
||||
@deps bar path
|
||||
@@ -489,6 +565,7 @@ centers(v::AVec) = v[1] + cumsum(diff(v))
|
||||
x := centers(xedges)
|
||||
y := centers(yedges)
|
||||
z := Surface(counts)
|
||||
linewidth := 0
|
||||
seriestype := :heatmap
|
||||
()
|
||||
end
|
||||
@@ -517,27 +594,25 @@ const _box_halfwidth = 0.4
|
||||
|
||||
notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N)
|
||||
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:box}})
|
||||
|
||||
@recipe function f(::Type{Val{:boxplot}}, x, y, z; notch=false, range=1.5)
|
||||
# Plots.dumpdict(d, "box before", true)
|
||||
|
||||
# create a list of shapes, where each shape is a single boxplot
|
||||
shapes = Shape[]
|
||||
groupby = extractGroupArgs(x)
|
||||
outliers_y = Float64[]
|
||||
outliers_x = Float64[]
|
||||
|
||||
delete!(d, :notch)
|
||||
delete!(d, :range)
|
||||
xsegs, ysegs = Segments(), Segments()
|
||||
glabels = sort(collect(unique(x)))
|
||||
warning = false
|
||||
outliers_x, outliers_y = zeros(0), zeros(0)
|
||||
for glabel in glabels
|
||||
# filter y
|
||||
values = y[filter(i -> cycle(x,i) == glabel, 1:length(y))]
|
||||
|
||||
for (i, glabel) in enumerate(groupby.groupLabels)
|
||||
|
||||
# filter y values
|
||||
values = d[:y][groupby.groupIds[i]]
|
||||
# then compute quantiles
|
||||
# compute quantiles
|
||||
q1,q2,q3,q4,q5 = quantile(values, linspace(0,1,5))
|
||||
|
||||
# notch
|
||||
n = notch_width(q2, q4, length(values))
|
||||
|
||||
# warn on inverted notches?
|
||||
if notch && !warning && ( (q2>(q3-n)) || (q4<(q3+n)) )
|
||||
warn("Boxplot's notch went outside hinges. Set notch to false.")
|
||||
warning = true # Show the warning only one time
|
||||
@@ -546,8 +621,10 @@ notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N)
|
||||
# make the shape
|
||||
center = discrete_value!(d[:subplot][:xaxis], glabel)[1]
|
||||
l, m, r = center - _box_halfwidth, center, center + _box_halfwidth
|
||||
|
||||
# internal nodes for notches
|
||||
L, R = center - 0.5 * _box_halfwidth, center + 0.5 * _box_halfwidth
|
||||
|
||||
# outliers
|
||||
if Float64(range) != 0.0 # if the range is 0.0, the whiskers will extend to the data
|
||||
limit = range*(q4-q2)
|
||||
@@ -564,58 +641,144 @@ notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N)
|
||||
# using maximum and minimum values inside the limits
|
||||
q1, q5 = extrema(inside)
|
||||
end
|
||||
|
||||
# Box
|
||||
xcoords = notch::Bool ? [
|
||||
m, l, r, m, m, NaN, # lower T
|
||||
l, l, L, R, r, r, l, NaN, # lower box
|
||||
l, l, L, R, r, r, l, NaN, # upper box
|
||||
m, l, r, m, m, NaN, # upper T
|
||||
] : [
|
||||
m, l, r, m, m, NaN, # lower T
|
||||
l, l, r, r, l, NaN, # lower box
|
||||
l, l, r, r, l, NaN, # upper box
|
||||
m, l, r, m, m, NaN, # upper T
|
||||
]
|
||||
ycoords = notch::Bool ? [
|
||||
q1, q1, q1, q1, q2, NaN, # lower T
|
||||
q2, q3-n, q3, q3, q3-n, q2, q2, NaN, # lower box
|
||||
q4, q3+n, q3, q3, q3+n, q4, q4, NaN, # upper box
|
||||
q5, q5, q5, q5, q4, NaN, # upper T
|
||||
] : [
|
||||
q1, q1, q1, q1, q2, NaN, # lower T
|
||||
q2, q3, q3, q2, q2, NaN, # lower box
|
||||
q4, q3, q3, q4, q4, NaN, # upper box
|
||||
q5, q5, q5, q5, q4, NaN, # upper T
|
||||
]
|
||||
push!(shapes, Shape(xcoords, ycoords))
|
||||
if notch
|
||||
push!(xsegs, m, l, r, m, m) # lower T
|
||||
push!(xsegs, l, l, L, R, r, r, l) # lower box
|
||||
push!(xsegs, l, l, L, R, r, r, l) # upper box
|
||||
push!(xsegs, m, l, r, m, m) # upper T
|
||||
|
||||
push!(ysegs, q1, q1, q1, q1, q2) # lower T
|
||||
push!(ysegs, q2, q3-n, q3, q3, q3-n, q2, q2) # lower box
|
||||
push!(ysegs, q4, q3+n, q3, q3, q3+n, q4, q4) # upper box
|
||||
push!(ysegs, q5, q5, q5, q5, q4) # upper T
|
||||
else
|
||||
push!(xsegs, m, l, r, m, m) # lower T
|
||||
push!(xsegs, l, l, r, r, l) # lower box
|
||||
push!(xsegs, l, l, r, r, l) # upper box
|
||||
push!(xsegs, m, l, r, m, m) # upper T
|
||||
|
||||
push!(ysegs, q1, q1, q1, q1, q2) # lower T
|
||||
push!(ysegs, q2, q3, q3, q2, q2) # lower box
|
||||
push!(ysegs, q4, q3, q3, q4, q4) # upper box
|
||||
push!(ysegs, q5, q5, q5, q5, q4) # upper T
|
||||
end
|
||||
end
|
||||
|
||||
# d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
|
||||
|
||||
seriestype := :shape
|
||||
# n = length(groupby.groupLabels)
|
||||
# xticks --> (linspace(0.5,n-0.5,n), groupby.groupLabels)
|
||||
|
||||
# clean d
|
||||
pop!(d, :notch)
|
||||
pop!(d, :range)
|
||||
|
||||
# we want to set the fields directly inside series recipes... args are ignored
|
||||
d[:x], d[:y] = Plots.shape_coords(shapes)
|
||||
|
||||
# Outliers
|
||||
@series begin
|
||||
seriestype := :scatter
|
||||
markershape := :circle
|
||||
x := outliers_x
|
||||
y := outliers_y
|
||||
label := ""
|
||||
primary := false
|
||||
seriestype := :scatter
|
||||
markershape --> :circle
|
||||
x := outliers_x
|
||||
y := outliers_y
|
||||
primary := false
|
||||
()
|
||||
end
|
||||
|
||||
() # expects a tuple returned
|
||||
seriestype := :shape
|
||||
x := xsegs.pts
|
||||
y := ysegs.pts
|
||||
()
|
||||
end
|
||||
|
||||
# @recipe function f(::Type{Val{:boxplot}}, x, y, z; notch=false, range=1.5)
|
||||
# # Plots.dumpdict(d, "box before", true)
|
||||
|
||||
# # create a list of shapes, where each shape is a single boxplot
|
||||
# shapes = Shape[]
|
||||
# groupby = extractGroupArgs(x)
|
||||
# outliers_y = Float64[]
|
||||
# outliers_x = Float64[]
|
||||
|
||||
# warning = false
|
||||
|
||||
# for (i, glabel) in enumerate(groupby.groupLabels)
|
||||
|
||||
# # filter y values
|
||||
# values = d[:y][groupby.groupIds[i]]
|
||||
# # then compute quantiles
|
||||
# q1,q2,q3,q4,q5 = quantile(values, linspace(0,1,5))
|
||||
# # notch
|
||||
# n = notch_width(q2, q4, length(values))
|
||||
|
||||
# if notch && !warning && ( (q2>(q3-n)) || (q4<(q3+n)) )
|
||||
# warn("Boxplot's notch went outside hinges. Set notch to false.")
|
||||
# warning = true # Show the warning only one time
|
||||
# end
|
||||
|
||||
# # make the shape
|
||||
# center = discrete_value!(d[:subplot][:xaxis], glabel)[1]
|
||||
# l, m, r = center - _box_halfwidth, center, center + _box_halfwidth
|
||||
# # internal nodes for notches
|
||||
# L, R = center - 0.5 * _box_halfwidth, center + 0.5 * _box_halfwidth
|
||||
# # outliers
|
||||
# if Float64(range) != 0.0 # if the range is 0.0, the whiskers will extend to the data
|
||||
# limit = range*(q4-q2)
|
||||
# inside = Float64[]
|
||||
# for value in values
|
||||
# if (value < (q2 - limit)) || (value > (q4 + limit))
|
||||
# push!(outliers_y, value)
|
||||
# push!(outliers_x, center)
|
||||
# else
|
||||
# push!(inside, value)
|
||||
# end
|
||||
# end
|
||||
# # change q1 and q5 to show outliers
|
||||
# # using maximum and minimum values inside the limits
|
||||
# q1, q5 = extrema(inside)
|
||||
# end
|
||||
# # Box
|
||||
# xcoords = notch::Bool ? [
|
||||
# m, l, r, m, m, NaN, # lower T
|
||||
# l, l, L, R, r, r, l, NaN, # lower box
|
||||
# l, l, L, R, r, r, l, NaN, # upper box
|
||||
# m, l, r, m, m, NaN, # upper T
|
||||
# ] : [
|
||||
# m, l, r, m, m, NaN, # lower T
|
||||
# l, l, r, r, l, NaN, # lower box
|
||||
# l, l, r, r, l, NaN, # upper box
|
||||
# m, l, r, m, m, NaN, # upper T
|
||||
# ]
|
||||
# ycoords = notch::Bool ? [
|
||||
# q1, q1, q1, q1, q2, NaN, # lower T
|
||||
# q2, q3-n, q3, q3, q3-n, q2, q2, NaN, # lower box
|
||||
# q4, q3+n, q3, q3, q3+n, q4, q4, NaN, # upper box
|
||||
# q5, q5, q5, q5, q4, NaN, # upper T
|
||||
# ] : [
|
||||
# q1, q1, q1, q1, q2, NaN, # lower T
|
||||
# q2, q3, q3, q2, q2, NaN, # lower box
|
||||
# q4, q3, q3, q4, q4, NaN, # upper box
|
||||
# q5, q5, q5, q5, q4, NaN, # upper T
|
||||
# ]
|
||||
# push!(shapes, Shape(xcoords, ycoords))
|
||||
# end
|
||||
|
||||
# # d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
|
||||
|
||||
# seriestype := :shape
|
||||
# # n = length(groupby.groupLabels)
|
||||
# # xticks --> (linspace(0.5,n-0.5,n), groupby.groupLabels)
|
||||
|
||||
# # clean d
|
||||
# pop!(d, :notch)
|
||||
# pop!(d, :range)
|
||||
|
||||
# # we want to set the fields directly inside series recipes... args are ignored
|
||||
# d[:x], d[:y] = Plots.shape_coords(shapes)
|
||||
|
||||
# # Outliers
|
||||
# @series begin
|
||||
# seriestype := :scatter
|
||||
# markershape --> :circle
|
||||
# x := outliers_x
|
||||
# y := outliers_y
|
||||
# primary := false
|
||||
# ()
|
||||
# end
|
||||
|
||||
# () # expects a tuple returned
|
||||
# end
|
||||
@deps boxplot shape scatter
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -644,20 +807,13 @@ else
|
||||
end
|
||||
|
||||
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:violin}})
|
||||
@recipe function f(::Type{Val{:violin}}, x, y, z; trim=true)
|
||||
# dumpdict(d, "box before", true)
|
||||
# TODO: add scatter series with outliers
|
||||
|
||||
# create a list of shapes, where each shape is a single boxplot
|
||||
shapes = Shape[]
|
||||
groupby = extractGroupArgs(d[:x])
|
||||
|
||||
for (i, glabel) in enumerate(groupby.groupLabels)
|
||||
|
||||
# get the edges and widths
|
||||
y = d[:y][groupby.groupIds[i]]
|
||||
widths, centers = violin_coords(y, trim=trim)
|
||||
delete!(d, :trim)
|
||||
xsegs, ysegs = Segments(), Segments()
|
||||
glabels = sort(collect(unique(x)))
|
||||
for glabel in glabels
|
||||
widths, centers = violin_coords(y[filter(i -> cycle(x,i) == glabel, 1:length(y))], trim=trim)
|
||||
isempty(widths) && continue
|
||||
|
||||
# normalize
|
||||
widths = _box_halfwidth * widths / maximum(widths)
|
||||
@@ -666,18 +822,14 @@ end
|
||||
xcenter = discrete_value!(d[:subplot][:xaxis], glabel)[1]
|
||||
xcoords = vcat(widths, -reverse(widths)) + xcenter
|
||||
ycoords = vcat(centers, reverse(centers))
|
||||
push!(shapes, Shape(xcoords, ycoords))
|
||||
|
||||
push!(xsegs, xcoords)
|
||||
push!(ysegs, ycoords)
|
||||
end
|
||||
|
||||
# d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
|
||||
seriestype := :shape
|
||||
# n = length(groupby.groupLabels)
|
||||
# xticks --> (linspace(0.5,n-0.5,n), groupby.groupLabels)
|
||||
|
||||
# clean up d
|
||||
pop!(d, :trim)
|
||||
|
||||
d[:x], d[:y] = shape_coords(shapes)
|
||||
x := xsegs.pts
|
||||
y := ysegs.pts
|
||||
()
|
||||
end
|
||||
@deps violin shape
|
||||
@@ -971,6 +1123,9 @@ end
|
||||
|
||||
# -------------------------------------------------
|
||||
|
||||
# TODO: everything below here should be either changed to a
|
||||
# series recipe or moved to PlotRecipes
|
||||
|
||||
|
||||
"Sparsity plot... heatmap of non-zero values of a matrix"
|
||||
function spy{T<:Real}(z::AMat{T}; kw...)
|
||||
@@ -987,189 +1142,3 @@ function abline!(plt::Plot, a, b; kw...)
|
||||
end
|
||||
|
||||
abline!(args...; kw...) = abline!(current(), args...; kw...)
|
||||
|
||||
# =================================================
|
||||
# Arc and chord diagrams
|
||||
|
||||
"Takes an adjacency matrix and returns source, destiny and weight lists"
|
||||
function mat2list{T}(mat::AbstractArray{T,2})
|
||||
nrow, ncol = size(mat) # rows are sources and columns are destinies
|
||||
|
||||
nosymmetric = !issym(mat) # plots only triu for symmetric matrices
|
||||
nosparse = !issparse(mat) # doesn't plot zeros from a sparse matrix
|
||||
|
||||
L = length(mat)
|
||||
|
||||
source = Array(Int, L)
|
||||
destiny = Array(Int, L)
|
||||
weight = Array(T, L)
|
||||
|
||||
idx = 1
|
||||
for i in 1:nrow, j in 1:ncol
|
||||
value = mat[i, j]
|
||||
if !isnan(value) && ( nosparse || value != zero(T) ) # TODO: deal with Nullable
|
||||
|
||||
if i < j
|
||||
source[idx] = i
|
||||
destiny[idx] = j
|
||||
weight[idx] = value
|
||||
idx += 1
|
||||
elseif nosymmetric && (i > j)
|
||||
source[idx] = i
|
||||
destiny[idx] = j
|
||||
weight[idx] = value
|
||||
idx += 1
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
resize!(source, idx-1), resize!(destiny, idx-1), resize!(weight, idx-1)
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Arc Diagram
|
||||
|
||||
curvecolor(value, min, max, grad) = getColorZ(grad, (value-min)/(max-min))
|
||||
|
||||
"Plots a clockwise arc, from source to destiny, colored by weight"
|
||||
function arc!(source, destiny, weight, min, max, grad)
|
||||
radius = (destiny - source) / 2
|
||||
arc = Plots.partialcircle(0, π, 30, radius)
|
||||
x, y = Plots.unzip(arc)
|
||||
plot!(x .+ radius .+ source, y, line = (curvecolor(weight, min, max, grad), 0.5, 2), legend=false)
|
||||
end
|
||||
|
||||
"""
|
||||
`arcdiagram(source, destiny, weight[, grad])`
|
||||
|
||||
Plots an arc diagram, form `source` to `destiny` (clockwise), using `weight` to determine the colors.
|
||||
"""
|
||||
function arcdiagram(source, destiny, weight; kargs...)
|
||||
|
||||
args = KW(kargs)
|
||||
grad = pop!(args, :grad, ColorGradient([colorant"darkred", colorant"darkblue"]))
|
||||
|
||||
if length(source) == length(destiny) == length(weight)
|
||||
|
||||
vertices = unique(vcat(source, destiny))
|
||||
sort!(vertices)
|
||||
|
||||
xmin, xmax = extrema(vertices)
|
||||
plot(xlim=(xmin - 0.5, xmax + 0.5), legend=false)
|
||||
|
||||
wmin,wmax = extrema(weight)
|
||||
|
||||
for (i, j, value) in zip(source,destiny,weight)
|
||||
arc!(i, j, value, wmin, wmax, grad)
|
||||
end
|
||||
|
||||
scatter!(vertices, zeros(length(vertices)); legend=false, args...)
|
||||
|
||||
else
|
||||
|
||||
throw(ArgumentError("source, destiny and weight should have the same length"))
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
`arcdiagram(mat[, grad])`
|
||||
|
||||
Plots an arc diagram from an adjacency matrix, form rows to columns (clockwise),
|
||||
using the values on the matrix as weights to determine the colors.
|
||||
Doesn't show edges with value zero if the input is sparse.
|
||||
For simmetric matrices, only the upper triangular values are used.
|
||||
"""
|
||||
arcdiagram{T}(mat::AbstractArray{T,2}; kargs...) = arcdiagram(mat2list(mat)...; kargs...)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Chord diagram
|
||||
|
||||
arcshape(θ1, θ2) = Shape(vcat(Plots.partialcircle(θ1, θ2, 15, 1.1),
|
||||
reverse(Plots.partialcircle(θ1, θ2, 15, 0.9))))
|
||||
|
||||
colorlist(grad, ::Void) = :darkgray
|
||||
|
||||
function colorlist(grad, z)
|
||||
zmin, zmax = extrema(z)
|
||||
RGBA{Float64}[getColorZ(grad, (zi-zmin)/(zmax-zmin)) for zi in z]'
|
||||
end
|
||||
|
||||
"""
|
||||
`chorddiagram(source, destiny, weight[, grad, zcolor, group])`
|
||||
|
||||
Plots a chord diagram, form `source` to `destiny`,
|
||||
using `weight` to determine the edge colors using `grad`.
|
||||
`zcolor` or `group` can be used to determine the node colors.
|
||||
"""
|
||||
function chorddiagram(source, destiny, weight; kargs...)
|
||||
|
||||
args = KW(kargs)
|
||||
grad = pop!(args, :grad, ColorGradient([colorant"darkred", colorant"darkblue"]))
|
||||
zcolor= pop!(args, :zcolor, nothing)
|
||||
group = pop!(args, :group, nothing)
|
||||
|
||||
if zcolor !== nothing && group !== nothing
|
||||
throw(ErrorException("group and zcolor can not be used together."))
|
||||
end
|
||||
|
||||
if length(source) == length(destiny) == length(weight)
|
||||
|
||||
plt = plot(xlim=(-2,2), ylim=(-2,2), legend=false, grid=false,
|
||||
xticks=nothing, yticks=nothing,
|
||||
xlim=(-1.2,1.2), ylim=(-1.2,1.2))
|
||||
|
||||
nodemin, nodemax = extrema(vcat(source, destiny))
|
||||
|
||||
weightmin, weightmax = extrema(weight)
|
||||
|
||||
A = 1.5π # Filled space
|
||||
B = 0.5π # White space (empirical)
|
||||
|
||||
Δα = A / nodemax
|
||||
Δβ = B / nodemax
|
||||
|
||||
δ = Δα + Δβ
|
||||
|
||||
for i in 1:length(source)
|
||||
curve = BezierCurve(P2[ (cos((source[i ]-1)*δ + 0.5Δα), sin((source[i ]-1)*δ + 0.5Δα)), (0,0),
|
||||
(cos((destiny[i]-1)*δ + 0.5Δα), sin((destiny[i]-1)*δ + 0.5Δα)) ])
|
||||
plot!(curve_points(curve), line = (Plots.curvecolor(weight[i], weightmin, weightmax, grad), 1, 1))
|
||||
end
|
||||
|
||||
if group === nothing
|
||||
c = colorlist(grad, zcolor)
|
||||
elseif length(group) == nodemax
|
||||
|
||||
idx = collect(0:(nodemax-1))
|
||||
|
||||
for g in group
|
||||
plot!([arcshape(n*δ, n*δ + Δα) for n in idx[group .== g]]; args...)
|
||||
end
|
||||
|
||||
return plt
|
||||
|
||||
else
|
||||
throw(ErrorException("group should the ", nodemax, " elements."))
|
||||
end
|
||||
|
||||
plot!([arcshape(n*δ, n*δ + Δα) for n in 0:(nodemax-1)]; mc=c, args...)
|
||||
|
||||
return plt
|
||||
|
||||
else
|
||||
throw(ArgumentError("source, destiny and weight should have the same length"))
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
`chorddiagram(mat[, grad, zcolor, group])`
|
||||
|
||||
Plots a chord diagram from an adjacency matrix,
|
||||
using the values on the matrix as weights to determine edge colors.
|
||||
Doesn't show edges with value zero if the input is sparse.
|
||||
For simmetric matrices, only the upper triangular values are used.
|
||||
`zcolor` or `group` can be used to determine the node colors.
|
||||
"""
|
||||
chorddiagram(mat::AbstractMatrix; kargs...) = chorddiagram(mat2list(mat)...; kargs...)
|
||||
|
||||
+2
-453
@@ -7,7 +7,7 @@
|
||||
|
||||
typealias FuncOrFuncs @compat(Union{Function, AVec{Function}})
|
||||
|
||||
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :heatmap, :surface, :wireframe, :contour3d), get(d, :seriestype, :none))
|
||||
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :heatmap, :surface, :wireframe, :contour3d, :image), get(d, :seriestype, :none))
|
||||
|
||||
# missing
|
||||
convertToAnyVector(v::@compat(Void), d::KW) = Any[nothing], nothing
|
||||
@@ -21,8 +21,7 @@ convertToAnyVector{T<:Number}(v::AVec{T}, d::KW) = Any[v], nothing
|
||||
# string vector
|
||||
convertToAnyVector{T<:@compat(AbstractString)}(v::AVec{T}, d::KW) = Any[v], nothing
|
||||
|
||||
# numeric matrix
|
||||
function convertToAnyVector{T<:Number}(v::AMat{T}, d::KW)
|
||||
function convertToAnyVector(v::AMat, d::KW)
|
||||
if all3D(d)
|
||||
Any[Surface(v)]
|
||||
else
|
||||
@@ -30,14 +29,6 @@ function convertToAnyVector{T<:Number}(v::AMat{T}, d::KW)
|
||||
end, nothing
|
||||
end
|
||||
|
||||
# other matrix... vector of columns
|
||||
function convertToAnyVector(m::AMat, d::KW)
|
||||
Any[begin
|
||||
v = vec(m[:,i])
|
||||
length(v) == 1 ? v[1] : v
|
||||
end for i=1:size(m,2)], nothing
|
||||
end
|
||||
|
||||
# function
|
||||
convertToAnyVector(f::Function, d::KW) = Any[f], nothing
|
||||
|
||||
@@ -106,445 +97,3 @@ compute_xyz(x::Void, y::Void, z::FuncOrFuncs) = error("If you want to plot the f
|
||||
compute_xyz(x::Void, y::Void, z::Void) = error("x/y/z are all nothing!")
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# # create n=max(mx,my) series arguments. the shorter list is cycled through
|
||||
# # note: everything should flow through this
|
||||
# function build_series_args(plt::AbstractPlot, kw::KW) #, idxfilter)
|
||||
# x, y, z = map(sym -> pop!(kw, sym, nothing), (:x, :y, :z))
|
||||
# if nothing == x == y == z
|
||||
# return [], nothing, nothing
|
||||
# end
|
||||
#
|
||||
# xs, xmeta = convertToAnyVector(x, kw)
|
||||
# ys, ymeta = convertToAnyVector(y, kw)
|
||||
# zs, zmeta = convertToAnyVector(z, kw)
|
||||
#
|
||||
# fr = pop!(kw, :fillrange, nothing)
|
||||
# fillranges, _ = if typeof(fr) <: Number
|
||||
# ([fr],nothing)
|
||||
# else
|
||||
# convertToAnyVector(fr, kw)
|
||||
# end
|
||||
#
|
||||
# mx = length(xs)
|
||||
# my = length(ys)
|
||||
# mz = length(zs)
|
||||
# ret = Any[]
|
||||
# for i in 1:max(mx, my, mz)
|
||||
#
|
||||
# # try to set labels using ymeta
|
||||
# d = copy(kw)
|
||||
# if !haskey(d, :label) && ymeta != nothing
|
||||
# if isa(ymeta, Symbol)
|
||||
# d[:label] = string(ymeta)
|
||||
# elseif isa(ymeta, AVec{Symbol})
|
||||
# d[:label] = string(ymeta[mod1(i,length(ymeta))])
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # build the series arg dict
|
||||
# numUncounted = pop!(d, :numUncounted, 0)
|
||||
# commandIndex = i + numUncounted
|
||||
# n = plt.n + i
|
||||
#
|
||||
# dumpdict(d, "before getSeriesArgs")
|
||||
# d = getSeriesArgs(plt.backend, getattr(plt, n), d, commandIndex, convertSeriesIndex(plt, n), n)
|
||||
# dumpdict(d, "after getSeriesArgs")
|
||||
#
|
||||
# d[:x], d[:y], d[:z] = compute_xyz(xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)])
|
||||
# st = d[:seriestype]
|
||||
#
|
||||
# # for seriestype `line`, need to sort by x values
|
||||
# if st == :line
|
||||
# # order by x
|
||||
# indices = sortperm(d[:x])
|
||||
# d[:x] = d[:x][indices]
|
||||
# d[:y] = d[:y][indices]
|
||||
# d[:seriestype] = :path
|
||||
# end
|
||||
#
|
||||
# # special handling for missing x in box plot... all the same category
|
||||
# if st == :box && xs[mod1(i,mx)] == nothing
|
||||
# d[:x] = ones(Int, length(d[:y]))
|
||||
# end
|
||||
#
|
||||
# # map functions to vectors
|
||||
# if isa(d[:marker_z], Function)
|
||||
# d[:marker_z] = map(d[:marker_z], d[:x])
|
||||
# end
|
||||
#
|
||||
# # @show fillranges
|
||||
# d[:fillrange] = fillranges[mod1(i,length(fillranges))]
|
||||
# if isa(d[:fillrange], Function)
|
||||
# d[:fillrange] = map(d[:fillrange], d[:x])
|
||||
# end
|
||||
#
|
||||
# # handle error bars
|
||||
# for esym in (:xerror, :yerror)
|
||||
# if get(d, esym, nothing) != nothing
|
||||
# # we make a copy of the KW and apply an errorbar recipe
|
||||
# append!(ret, apply_series_recipe(copy(d), Val{esym}))
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # handle ribbons
|
||||
# if get(d, :ribbon, nothing) != nothing
|
||||
# rib = d[:ribbon]
|
||||
# d[:fillrange] = (d[:y] - rib, d[:y] + rib)
|
||||
# end
|
||||
#
|
||||
# # handle quiver plots
|
||||
# # either a series of velocity vectors are passed in (`:quiver` keyword),
|
||||
# # or we just add arrows to the path
|
||||
#
|
||||
# # if st == :quiver
|
||||
# # d[:seriestype] = st = :path
|
||||
# # d[:linewidth] = 0
|
||||
# # end
|
||||
# if get(d, :quiver, nothing) != nothing
|
||||
# append!(ret, apply_series_recipe(copy(d), Val{:quiver}))
|
||||
# elseif st == :quiver
|
||||
# d[:seriestype] = st = :path
|
||||
# d[:arrow] = arrow()
|
||||
# end
|
||||
#
|
||||
# # now that we've processed a given series... optionally split into
|
||||
# # multiple dicts through a recipe (for example, a box plot is split into component
|
||||
# # parts... polygons, lines, and scatters)
|
||||
# # note: we pass in a Val type (i.e. Val{:box}) so that we can dispatch on the seriestype
|
||||
# kwlist = apply_series_recipe(d, Val{st})
|
||||
# append!(ret, kwlist)
|
||||
#
|
||||
# # # add it to our series list
|
||||
# # push!(ret, d)
|
||||
# end
|
||||
#
|
||||
# ret, xmeta, ymeta
|
||||
# end
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # process_inputs
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# # These methods take a plot and the keyword arguments, and processes the input
|
||||
# # arguments (x/y/z, group, etc), populating the KW dict with appropriate values.
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # 0 arguments
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# # don't do anything
|
||||
# function process_inputs(plt::AbstractPlot, d::KW)
|
||||
# end
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # 1 argument
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, n::Integer)
|
||||
# # d[:x], d[:y], d[:z] = zeros(0), zeros(0), zeros(0)
|
||||
# d[:x] = d[:y] = d[:z] = n
|
||||
# end
|
||||
#
|
||||
# # no special handling... assume x and z are nothing
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, y)
|
||||
# d[:y] = y
|
||||
# end
|
||||
#
|
||||
# # matrix... is it z or y?
|
||||
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, mat::AMat{T})
|
||||
# if all3D(d)
|
||||
# n,m = size(mat)
|
||||
# d[:x], d[:y], d[:z] = 1:n, 1:m, mat
|
||||
# else
|
||||
# d[:y] = mat
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # images - grays
|
||||
# function process_inputs{T<:Gray}(plt::AbstractPlot, d::KW, mat::AMat{T})
|
||||
# d[:seriestype] = :image
|
||||
# n,m = size(mat)
|
||||
# d[:x], d[:y], d[:z] = 1:n, 1:m, Surface(mat)
|
||||
# # handle images... when not supported natively, do a hack to use heatmap machinery
|
||||
# if !nativeImagesSupported()
|
||||
# d[:seriestype] = :heatmap
|
||||
# d[:yflip] = true
|
||||
# d[:z] = Surface(convert(Matrix{Float64}, mat.surf))
|
||||
# d[:fillcolor] = ColorGradient([:black, :white])
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # images - colors
|
||||
# function process_inputs{T<:Colorant}(plt::AbstractPlot, d::KW, mat::AMat{T})
|
||||
# d[:seriestype] = :image
|
||||
# n,m = size(mat)
|
||||
# d[:x], d[:y], d[:z] = 1:n, 1:m, Surface(mat)
|
||||
# # handle images... when not supported natively, do a hack to use heatmap machinery
|
||||
# if !nativeImagesSupported()
|
||||
# d[:yflip] = true
|
||||
# imageHack(d)
|
||||
# end
|
||||
# end
|
||||
#
|
||||
#
|
||||
# # plotting arbitrary shapes/polygons
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, shape::Shape)
|
||||
# d[:x], d[:y] = shape_coords(shape)
|
||||
# d[:seriestype] = :shape
|
||||
# end
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, shapes::AVec{Shape})
|
||||
# d[:x], d[:y] = shape_coords(shapes)
|
||||
# d[:seriestype] = :shape
|
||||
# end
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, shapes::AMat{Shape})
|
||||
# x, y = [], []
|
||||
# for j in 1:size(shapes, 2)
|
||||
# tmpx, tmpy = shape_coords(vec(shapes[:,j]))
|
||||
# push!(x, tmpx)
|
||||
# push!(y, tmpy)
|
||||
# end
|
||||
# d[:x], d[:y] = x, y
|
||||
# d[:seriestype] = :shape
|
||||
# end
|
||||
#
|
||||
#
|
||||
# # function without range... use the current range of the x-axis
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs)
|
||||
# process_inputs(plt, d, f, xmin(plt), xmax(plt))
|
||||
# end
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # 2 arguments
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, x, y)
|
||||
# d[:x], d[:y] = x, y
|
||||
# end
|
||||
#
|
||||
# # if functions come first, just swap the order (not to be confused with parametric functions...
|
||||
# # as there would be more than one function passed in)
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs, x)
|
||||
# @assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here
|
||||
# process_inputs(plt, d, x, f)
|
||||
# end
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # 3 arguments
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# # no special handling... just pass them through
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, x, y, z)
|
||||
# d[:x], d[:y], d[:z] = x, y, z
|
||||
# end
|
||||
#
|
||||
# # 3d line or scatter
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, x::AVec, y::AVec, zvec::AVec)
|
||||
# # default to path3d if we haven't set a 3d seriestype
|
||||
# st = get(d, :seriestype, :none)
|
||||
# if st == :scatter
|
||||
# d[:seriestype] = :scatter3d
|
||||
# elseif !(st in _3dTypes)
|
||||
# d[:seriestype] = :path3d
|
||||
# end
|
||||
# d[:x], d[:y], d[:z] = x, y, zvec
|
||||
# end
|
||||
#
|
||||
# # surface-like... function
|
||||
# function process_inputs{TX,TY}(plt::AbstractPlot, d::KW, x::AVec{TX}, y::AVec{TY}, zf::Function)
|
||||
# x = TX <: Number ? sort(x) : x
|
||||
# y = TY <: Number ? sort(y) : y
|
||||
# # x, y = sort(x), sort(y)
|
||||
# d[:z] = Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
|
||||
# d[:x], d[:y] = x, y
|
||||
# end
|
||||
#
|
||||
# # surface-like... matrix grid
|
||||
# function process_inputs{TX,TY,TZ}(plt::AbstractPlot, d::KW, x::AVec{TX}, y::AVec{TY}, zmat::AMat{TZ})
|
||||
# # @assert size(zmat) == (length(x), length(y))
|
||||
# # if TX <: Number && !issorted(x)
|
||||
# # idx = sortperm(x)
|
||||
# # x, zmat = x[idx], zmat[idx, :]
|
||||
# # end
|
||||
# # if TY <: Number && !issorted(y)
|
||||
# # idx = sortperm(y)
|
||||
# # y, zmat = y[idx], zmat[:, idx]
|
||||
# # end
|
||||
# d[:x], d[:y], d[:z] = x, y, Surface{Matrix{TZ}}(zmat)
|
||||
# if !like_surface(get(d, :seriestype, :none))
|
||||
# d[:seriestype] = :contour
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # surfaces-like... general x, y grid
|
||||
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, x::AMat{T}, y::AMat{T}, zmat::AMat{T})
|
||||
# @assert size(zmat) == size(x) == size(y)
|
||||
# # d[:x], d[:y], d[:z] = Any[x], Any[y], Surface{Matrix{Float64}}(zmat)
|
||||
# d[:x], d[:y], d[:z] = map(Surface{Matrix{Float64}}, (x, y, zmat))
|
||||
# if !like_surface(get(d, :seriestype, :none))
|
||||
# d[:seriestype] = :contour
|
||||
# end
|
||||
# end
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # Parametric functions
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# # special handling... xmin/xmax with function(s)
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs, xmin::Number, xmax::Number)
|
||||
# width = get(plt.attr, :size, (100,))[1]
|
||||
# x = linspace(xmin, xmax, width)
|
||||
# process_inputs(plt, d, x, f)
|
||||
# end
|
||||
#
|
||||
# # special handling... xmin/xmax with parametric function(s)
|
||||
# process_inputs{T<:Number}(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, u::AVec{T}) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u))
|
||||
# process_inputs{T<:Number}(plt::AbstractPlot, d::KW, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u))
|
||||
# process_inputs(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Number, umax::Number, numPoints::Int = 1000) = process_inputs(plt, d, fx, fy, linspace(umin, umax, numPoints))
|
||||
#
|
||||
# # special handling... 3D parametric function(s)
|
||||
# process_inputs{T<:Number}(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, u::AVec{T}) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u))
|
||||
# process_inputs{T<:Number}(plt::AbstractPlot, d::KW, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u))
|
||||
# process_inputs(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, umin::Number, umax::Number, numPoints::Int = 1000) = process_inputs(plt, d, fx, fy, fz, linspace(umin, umax, numPoints))
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # Lists of tuples and FixedSizeArrays
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# # if we get an unhandled tuple, just splat it in
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, tup::Tuple)
|
||||
# process_inputs(plt, d, tup...)
|
||||
# end
|
||||
#
|
||||
# # (x,y) tuples
|
||||
# function process_inputs{R1<:Number,R2<:Number}(plt::AbstractPlot, d::KW, xy::AVec{Tuple{R1,R2}})
|
||||
# process_inputs(plt, d, unzip(xy)...)
|
||||
# end
|
||||
# function process_inputs{R1<:Number,R2<:Number}(plt::AbstractPlot, d::KW, xy::Tuple{R1,R2})
|
||||
# process_inputs(plt, d, [xy[1]], [xy[2]])
|
||||
# end
|
||||
#
|
||||
# # (x,y,z) tuples
|
||||
# function process_inputs{R1<:Number,R2<:Number,R3<:Number}(plt::AbstractPlot, d::KW, xyz::AVec{Tuple{R1,R2,R3}})
|
||||
# process_inputs(plt, d, unzip(xyz)...)
|
||||
# end
|
||||
# function process_inputs{R1<:Number,R2<:Number,R3<:Number}(plt::AbstractPlot, d::KW, xyz::Tuple{R1,R2,R3})
|
||||
# process_inputs(plt, d, [xyz[1]], [xyz[2]], [xyz[3]])
|
||||
# end
|
||||
#
|
||||
# # 2D FixedSizeArrays
|
||||
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xy::AVec{FixedSizeArrays.Vec{2,T}})
|
||||
# process_inputs(plt, d, unzip(xy)...)
|
||||
# end
|
||||
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xy::FixedSizeArrays.Vec{2,T})
|
||||
# process_inputs(plt, d, [xy[1]], [xy[2]])
|
||||
# end
|
||||
#
|
||||
# # 3D FixedSizeArrays
|
||||
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xyz::AVec{FixedSizeArrays.Vec{3,T}})
|
||||
# process_inputs(plt, d, unzip(xyz)...)
|
||||
# end
|
||||
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xyz::FixedSizeArrays.Vec{3,T})
|
||||
# process_inputs(plt, d, [xyz[1]], [xyz[2]], [xyz[3]])
|
||||
# end
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # handle grouping
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# # function process_inputs(plt::AbstractPlot, d::KW, groupby::GroupBy, args...)
|
||||
# # ret = Any[]
|
||||
# # error("unfinished after series reorg")
|
||||
# # for (i,glab) in enumerate(groupby.groupLabels)
|
||||
# # # TODO: don't automatically overwrite labels
|
||||
# # kwlist, xmeta, ymeta = process_inputs(plt, d, args...,
|
||||
# # idxfilter = groupby.groupIds[i],
|
||||
# # label = string(glab),
|
||||
# # numUncounted = length(ret)) # we count the idx from plt.n + numUncounted + i
|
||||
# # append!(ret, kwlist)
|
||||
# # end
|
||||
# # ret, nothing, nothing # TODO: handle passing meta through
|
||||
# # end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# For DataFrame support. Imports DataFrames and defines the necessary methods which support them.
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# function setup_dataframes()
|
||||
# @require DataFrames begin
|
||||
# # @eval begin
|
||||
# # import DataFrames
|
||||
#
|
||||
# DFS = Union{Symbol, AbstractArray{Symbol}}
|
||||
#
|
||||
# function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, dfs::DFS)
|
||||
# if isa(dfs, Symbol)
|
||||
# get!(d, Symbol(letter * "label"), string(dfs))
|
||||
# collect(df[dfs])
|
||||
# else
|
||||
# get!(d, :label, reshape(dfs, 1, length(dfs)))
|
||||
# Any[collect(df[s]) for s in dfs]
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# function handle_group(df::DataFrames.AbstractDataFrame, d::KW)
|
||||
# if haskey(d, :group)
|
||||
# g = d[:group]
|
||||
# if isa(g, Symbol)
|
||||
# d[:group] = collect(df[g])
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# @recipe function plot(df::DataFrames.AbstractDataFrame, sy::DFS)
|
||||
# handle_group(df, d)
|
||||
# handle_dfs(df, d, "y", sy)
|
||||
# end
|
||||
#
|
||||
# @recipe function plot(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS)
|
||||
# handle_group(df, d)
|
||||
# x = handle_dfs(df, d, "x", sx)
|
||||
# y = handle_dfs(df, d, "y", sy)
|
||||
# x, y
|
||||
# end
|
||||
#
|
||||
# @recipe function plot(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS, sz::DFS)
|
||||
# handle_group(df, d)
|
||||
# x = handle_dfs(df, d, "x", sx)
|
||||
# y = handle_dfs(df, d, "y", sy)
|
||||
# z = handle_dfs(df, d, "z", sz)
|
||||
# x, y, z
|
||||
# end
|
||||
#
|
||||
# # get_data(df::DataFrames.AbstractDataFrame, arg::Symbol) = df[arg]
|
||||
# # get_data(df::DataFrames.AbstractDataFrame, arg) = arg
|
||||
# #
|
||||
# # function process_inputs(plt::AbstractPlot, d::KW, df::DataFrames.AbstractDataFrame, args...)
|
||||
# # # d[:dataframe] = df
|
||||
# # process_inputs(plt, d, map(arg -> get_data(df, arg), args)...)
|
||||
# # end
|
||||
# #
|
||||
# # # expecting the column name of a dataframe that was passed in... anything else should error
|
||||
# # function extractGroupArgs(s::Symbol, df::DataFrames.AbstractDataFrame, args...)
|
||||
# # if haskey(df, s)
|
||||
# # return extractGroupArgs(df[s])
|
||||
# # else
|
||||
# # error("Got a symbol, and expected that to be a key in d[:dataframe]. s=$s d=$d")
|
||||
# # end
|
||||
# # end
|
||||
#
|
||||
# # function getDataFrameFromKW(d::KW)
|
||||
# # get(d, :dataframe) do
|
||||
# # error("Missing dataframe argument!")
|
||||
# # end
|
||||
# # end
|
||||
#
|
||||
# # # the conversion functions for when we pass symbols or vectors of symbols to reference dataframes
|
||||
# # convertToAnyVector(s::Symbol, d::KW) = Any[getDataFrameFromKW(d)[s]], s
|
||||
# # convertToAnyVector(v::AVec{Symbol}, d::KW) = (df = getDataFrameFromKW(d); Any[df[s] for s in v]), v
|
||||
#
|
||||
# end
|
||||
# end
|
||||
|
||||
+12
-21
@@ -180,15 +180,6 @@ end
|
||||
newargs
|
||||
end
|
||||
|
||||
# @recipe f(x, y, z) = SliceIt, apply_recipe(typeof(x), x), apply_recipe(typeof(y), y), apply_recipe(typeof(z), z)
|
||||
# @recipe f(x, y) = SliceIt, apply_recipe(typeof(x), x), apply_recipe(typeof(y), y), nothing
|
||||
# @recipe f(y) = SliceIt, nothing, apply_recipe(typeof(y), y), nothing
|
||||
|
||||
# # pass these through to the slicer
|
||||
# @recipe f(x, y, z) = SliceIt, x, y, z
|
||||
# @recipe f(x, y) = SliceIt, x, y, nothing
|
||||
# @recipe f(y) = SliceIt, nothing, y, nothing
|
||||
|
||||
|
||||
# # --------------------------------------------------------------------
|
||||
# # 1 argument
|
||||
@@ -292,22 +283,22 @@ end
|
||||
# # 3d line or scatter
|
||||
|
||||
@recipe function f(x::AVec, y::AVec, z::AVec)
|
||||
st = get(d, :seriestype, :none)
|
||||
if st == :scatter
|
||||
d[:seriestype] = :scatter3d
|
||||
elseif !is3d(st)
|
||||
d[:seriestype] = :path3d
|
||||
end
|
||||
# st = get(d, :seriestype, :none)
|
||||
# if st == :scatter
|
||||
# d[:seriestype] = :scatter3d
|
||||
# elseif !is3d(st)
|
||||
# d[:seriestype] = :path3d
|
||||
# end
|
||||
SliceIt, x, y, z
|
||||
end
|
||||
|
||||
@recipe function f(x::AMat, y::AMat, z::AMat)
|
||||
st = get(d, :seriestype, :none)
|
||||
if size(x) == size(y) == size(z)
|
||||
if !is3d(st)
|
||||
seriestype := :path3d
|
||||
end
|
||||
end
|
||||
# st = get(d, :seriestype, :none)
|
||||
# if size(x) == size(y) == size(z)
|
||||
# if !is3d(st)
|
||||
# seriestype := :path3d
|
||||
# end
|
||||
# end
|
||||
SliceIt, x, y, z
|
||||
end
|
||||
|
||||
|
||||
@@ -136,16 +136,94 @@ function imageHack(d::KW)
|
||||
d[:z], d[:fillcolor] = replace_image_with_heatmap(d[:z].surf)
|
||||
end
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
|
||||
type Segments
|
||||
pts::Vector{Float64}
|
||||
end
|
||||
|
||||
Segments() = Segments(zeros(0))
|
||||
|
||||
function Base.push!(segments::Segments, vs...)
|
||||
push!(segments.pts, NaN)
|
||||
for v in vs
|
||||
push!(segments.pts, v)
|
||||
end
|
||||
segments
|
||||
end
|
||||
|
||||
function Base.push!(segments::Segments, vs::AVec)
|
||||
push!(segments.pts, NaN)
|
||||
for v in vs
|
||||
push!(segments.pts, v)
|
||||
end
|
||||
segments
|
||||
end
|
||||
|
||||
|
||||
# -----------------------------------------------------
|
||||
# helper to manage NaN-separated segments
|
||||
|
||||
type SegmentsIterator
|
||||
args::Tuple
|
||||
nextidx::Int
|
||||
n::Int
|
||||
end
|
||||
function iter_segments(args...)
|
||||
tup = Plots.wraptuple(args)
|
||||
n = maximum(map(length, tup))
|
||||
SegmentsIterator(tup, 0, n)
|
||||
end
|
||||
|
||||
# helpers to figure out if there are NaN values in a list of array types
|
||||
anynan(i::Int, args...) = any(a -> !isfinite(cycle(a,i)), args)
|
||||
anynan(istart::Int, iend::Int, args...) = any(i -> anynan(i, args...), istart:iend)
|
||||
allnan(istart::Int, iend::Int, args...) = all(i -> anynan(i, args...), istart:iend)
|
||||
|
||||
Base.start(itr::SegmentsIterator) = (itr.nextidx = 1) #resets
|
||||
Base.done(itr::SegmentsIterator, unused::Int) = itr.nextidx > itr.n
|
||||
function Base.next(itr::SegmentsIterator, unused::Int)
|
||||
i = istart = iend = itr.nextidx
|
||||
|
||||
# find the next NaN, and iend is the one before
|
||||
while i <= itr.n + 1
|
||||
if i > itr.n || anynan(i, itr.args...)
|
||||
# done... array end or found NaN
|
||||
iend = i-1
|
||||
break
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
|
||||
# find the next non-NaN, and set itr.nextidx
|
||||
while i <= itr.n
|
||||
if !anynan(i, itr.args...)
|
||||
break
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
|
||||
itr.nextidx = i
|
||||
istart:iend, 0
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
nop() = nothing
|
||||
notimpl() = error("This has not been implemented yet")
|
||||
|
||||
Base.cycle(wrapper::InputWrapper, idx::Int) = wrapper.obj
|
||||
Base.cycle(wrapper::InputWrapper, idx::AVec{Int}) = wrapper.obj
|
||||
|
||||
Base.cycle(v::AVec, idx::Int) = v[mod1(idx, length(v))]
|
||||
Base.cycle(v::AMat, idx::Int) = size(v,1) == 1 ? v[1, mod1(idx, size(v,2))] : v[:, mod1(idx, size(v,2))]
|
||||
Base.cycle(v, idx::Int) = v
|
||||
|
||||
Base.cycle(v::AVec, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
|
||||
Base.cycle(v::AMat, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
|
||||
Base.cycle(v, idx::AVec{Int}) = v
|
||||
|
||||
makevec(v::AVec) = v
|
||||
makevec{T}(v::T) = T[v]
|
||||
|
||||
|
||||
+1
-1
@@ -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.3"
|
||||
|
||||
|
||||
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ facts("GR") do
|
||||
@fact gr() --> Plots.GRBackend()
|
||||
@fact backend() --> Plots.GRBackend()
|
||||
|
||||
@linux_only image_comparison_facts(:gr, skip=[30], eps=img_eps)
|
||||
# @linux_only image_comparison_facts(:gr, skip=[], eps=img_eps)
|
||||
end
|
||||
|
||||
facts("Plotly") do
|
||||
|
||||
Reference in New Issue
Block a user