From 2ab689f5ece3b8aa3c1ed4eb1ae59cc5f44173ec Mon Sep 17 00:00:00 2001 From: Ben Arthur Date: Sun, 10 Feb 2019 20:17:22 -0500 Subject: [PATCH] Scale_alpha --- NEWS.md | 1 + docs/src/gallery/geometries.md | 10 ++--- docs/src/gallery/scales.md | 44 ++++++++++++++++++++ docs/src/gallery/statistics.md | 13 +++--- docs/src/tutorial.md | 7 +++- src/Gadfly.jl | 2 + src/aesthetics.jl | 1 + src/data.jl | 1 + src/gadfly.js | 16 ++++---- src/geom/point.jl | 10 +++-- src/geom/polygon.jl | 16 +++++--- src/geom/ribbon.jl | 47 +++++++++++++--------- src/guide.jl | 8 ++-- src/guide/keys.jl | 3 +- src/scale.jl | 30 ++++++++++++-- src/theme.jl | 9 ++++- test/testscripts/scale_alpha_continuous.jl | 20 +++++++++ test/testscripts/scale_alpha_discrete.jl | 19 +++++++++ test/testscripts/stat_smooth.jl | 3 +- 19 files changed, 196 insertions(+), 64 deletions(-) create mode 100644 test/testscripts/scale_alpha_continuous.jl create mode 100644 test/testscripts/scale_alpha_discrete.jl diff --git a/NEWS.md b/NEWS.md index 3b00980ee..3d998eb27 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,6 +3,7 @@ This is a log of major changes in Gadfly between releases. It is not exhaustive. Each release typically has a number of minor bug fixes beyond what is listed here. # Version 1.1.0 + * Add `alpha` aesthetic, `Scale.alpha_continuous` and `Scale.alpha_discrete` (#1252) * Add `limits=(min= , max= )` to `Stat.histogram` (#1249) * Add dodged boxplots (#1246) * Add `Stat.dodge` (#1240) diff --git a/docs/src/gallery/geometries.md b/docs/src/gallery/geometries.md index 519602c27..0daccc4c9 100644 --- a/docs/src/gallery/geometries.md +++ b/docs/src/gallery/geometries.md @@ -412,13 +412,13 @@ x = -4:0.1:4 Da = [DataFrame(x=0:0.1:20, y=X[:,j], ymin=X[:,j].-0.5, ymax=X[:,j].+0.5, f="$f") for (j,f) in enumerate(["cos","sin"])] Db = [DataFrame(x=x, ymax=pdf.(Normal(μ),x), ymin=0.0, u="μ=$μ") for μ in [-1,1] ] -# In the line below, 0.4 is the color opacity -p1 = plot(vcat(Da...), x=:x, y=:y, ymin=:ymin, ymax=:ymax, color=:f, Geom.line, Geom.ribbon, - Theme(lowlight_color=c->RGBA{Float32}(c.r, c.g, c.b, 0.4)) +# In the line below, 0.6 is the color opacity +p1 = plot(vcat(Da...), x=:x, y=:y, ymin=:ymin, ymax=:ymax, color=:f, + Geom.line, Geom.ribbon, Theme(alphas=[0.6]) ) -p2 = plot(vcat(Db...), x = :x, y=:ymax, ymin = :ymin, ymax = :ymax, color = :u, +p2 = plot(vcat(Db...), x = :x, y=:ymax, ymin = :ymin, ymax = :ymax, + color = :u, alpha=:u, Theme(alphas=[0.8,0.2]), Geom.line, Geom.ribbon, Guide.ylabel("Density"), - Theme(lowlight_color=c->RGBA{Float32}(c.r, c.g, c.b, 0.4)), Guide.colorkey(title="", pos=[2.5,0.6]), Guide.title("Parametric PDF") ) hstack(p1,p2) diff --git a/docs/src/gallery/scales.md b/docs/src/gallery/scales.md index e307aed42..168c67cc8 100644 --- a/docs/src/gallery/scales.md +++ b/docs/src/gallery/scales.md @@ -1,5 +1,49 @@ # Scales +## [`Scale.alpha_continuous`](@ref) + +```@example +using Gadfly +set_default_plot_size(21cm, 8cm) +palettef = Scale.lab_gradient("darkgreen", "orange", "blue") +p1 = plot(x=1:10, y=rand(10), color=[1:10;], Geom.point, + Scale.color_continuous(colormap=palettef, minvalue=0, maxvalue=10), + Guide.title("Scale.color_continuous, Theme(alphas=[0.5])"), + Theme(alphas=[0.5], continuous_highlight_color=identity, + point_size=2mm) +) +p2 = plot(x=1:10, y=rand(10), alpha=[1:10;], Geom.point, + Scale.alpha_continuous(minvalue=0, maxvalue=10), + Guide.title("Scale.alpha_continuous, Theme(default_color=\"blue\")"), + Theme(default_color="blue", discrete_highlight_color=c->"gray", + point_size=2mm) +) +hstack(p1, p2) +``` + +## [`Scale.alpha_discrete`](@ref) + +```@example +using DataFrames, Gadfly +set_default_plot_size(21cm, 8cm) +D = DataFrame(x=1:6, y=rand(6), Shape=repeat(["a","b","c"], outer=2)) +coord = Coord.cartesian(xmin=0, xmax=7, ymin=0, ymax=1.0) +p1 = plot(D, x=:x, y=:y, color=:x, coord, + Scale.color_discrete, Geom.point, Geom.hair, + Guide.title("Scale.color_discrete, Theme(alphas=[0.5])"), + Theme(alphas=[0.5], discrete_highlight_color=identity, + point_size=2mm) +) +p2 = plot(D, x=:x, y=:y, alpha=:x, shape=:Shape, coord, + Scale.alpha_discrete, Geom.point, Geom.hair, + Guide.title("Scale.alpha_discrete, Theme(default_color=\"green\")"), + Theme(default_color="green", discrete_highlight_color=c->"gray", + point_size=2mm, alphas=[0.0,0.2,0.4,0.6,0.8,1.0]) +) +hstack(p1,p2) +``` + + ## [`Scale.color_continuous`](@ref) ```@example diff --git a/docs/src/gallery/statistics.md b/docs/src/gallery/statistics.md index 33bea5ea8..3c70068ae 100644 --- a/docs/src/gallery/statistics.md +++ b/docs/src/gallery/statistics.md @@ -19,17 +19,15 @@ using Colors, DataFrames, Gadfly, Distributions set_default_plot_size(21cm, 8cm) x = -4:0.1:4 Da = [DataFrame(x=x, ymax=pdf.(Normal(μ),x), ymin=0.0, u="μ=$μ") for μ in [-1,1]] -Db = [DataFrame(x=randn(200).+μ, u="μ=$μ") for μ in [-1,1]] +Db = [DataFrame(x=randn(200).+μ, u="μ=$μ") for μ in [-1,1]] p1 = plot(vcat(Da...), x=:x, y=:ymax, ymin=:ymin, ymax=:ymax, color=:u, - Geom.line, Geom.ribbon, Guide.ylabel("Density"), - Theme(lowlight_color=c->RGBA{Float32}(c.r, c.g, c.b, 0.4)), + Geom.line, Geom.ribbon, Guide.ylabel("Density"), Theme(alphas=[0.6]), Guide.colorkey(title="", pos=[2.5,0.6]), Guide.title("Parametric PDF") ) -p2 = plot(vcat(Db...), x=:x, color=:u, +p2 = plot(vcat(Db...), x=:x, color=:u, Theme(alphas=[0.6]), Stat.density(bandwidth=0.5), Geom.polygon(fill=true, preserve_order=true), - Coord.cartesian(xmin=-4, xmax=4), - Theme(lowlight_color=c->RGBA{Float32}(c.r, c.g, c.b, 0.4)), + Coord.cartesian(xmin=-4, xmax=4, ymin=0, ymax=0.4), Guide.colorkey(title="", pos=[2.5,0.6]), Guide.title("Kernel PDF") ) hstack(p1,p2) @@ -88,8 +86,7 @@ p = plot(salaries[salaries.Rank.=="Prof",:], x=:YrsService, y=:Salary, layer(Stat.smooth(method=:lm, levels=[0.95, 0.99]), Geom.line, Geom.ribbon)), Scale.xgroup(levels=["Discipline A", "Discipline B"]), Guide.colorkey(title="", pos=[0.43w, -0.4h]), - Theme(point_size=2pt, - lowlight_color=c->RGBA{Float32}(c.r, c.g, c.b, 0.2) ) + Theme(point_size=2pt, alphas=[0.5] ) ) ``` diff --git a/docs/src/tutorial.md b/docs/src/tutorial.md index 24383e478..65d5a933f 100644 --- a/docs/src/tutorial.md +++ b/docs/src/tutorial.md @@ -171,7 +171,8 @@ All aesthetics (e.g. `x`, `y`, `color`) have a Scale e.g. `Scale.x_continuous()` | `x` | `x_continuous` | `xticks` | | `y` | `y_continuous` | `yticks` | | `color` | `color_continuous` | `colorkey` | -| `size` | `size_continuous` | sizekey (tbd) | +| `size` | `size_continuous` | sizekey (tbd) | +| `alpha` | `alpha_continuous` | alphakey (tbd) | e.g. `Scale.x_continuous(format= , minvalue= , maxvalue= )`\ `format` can be: `:plain`, `:scientific`, `:engineering`, or `:auto`. @@ -213,7 +214,8 @@ hstack(p3, p4) | `shape` | `shape_discrete` | `shapekey` | | `size` | `size_discrete` | sizekey (tbd) | | `linestyle` | `linestyle_discrete` | linekey (tbd) | -| `group` | `group_discrete` | | +| `alpha` | `alpha_discrete` | alphakey (tbd) | +| `group` | `group_discrete` | | | `xgroup` | `xgroup` | | | `ygroup` | `ygroup` | | @@ -236,6 +238,7 @@ hstack(p5, p6) If you don't supply Scales or Guides, Gadfly will make an educated guess. ```@example 1 +set_default_plot_size(14cm, 8cm) # hide gasoline = dataset("Ecdat", "Gasoline") plot(gasoline, x=:Year, y=:LGasPCar, color=:Country, Geom.point, Geom.line) ``` diff --git a/src/Gadfly.jl b/src/Gadfly.jl index 866fe8ef7..0d37d116b 100755 --- a/src/Gadfly.jl +++ b/src/Gadfly.jl @@ -1146,6 +1146,7 @@ const default_aes_scales = Dict{Symbol, Dict}( :size => Scale.size_continuous(), :group => Scale.group_discrete(), :label => Scale.label(), + :alpha => Scale.alpha_continuous(), :linestyle => Scale.linestyle_discrete() ), @@ -1166,6 +1167,7 @@ const default_aes_scales = Dict{Symbol, Dict}( :size => Scale.size_discrete(), :group => Scale.group_discrete(), :label => Scale.label(), + :alpha => Scale.alpha_discrete(), :linestyle => Scale.linestyle_discrete() ) ) diff --git a/src/aesthetics.jl b/src/aesthetics.jl index 0cefe5c4e..ae38ca1b7 100755 --- a/src/aesthetics.jl +++ b/src/aesthetics.jl @@ -18,6 +18,7 @@ const NumericalAesthetic = size, Union{CategoricalAesthetic,Vector,Nothing} shape, Union{CategoricalAesthetic,Vector,Nothing} color, Union{CategoricalAesthetic,Vector,Nothing} + alpha, NumericalOrCategoricalAesthetic linestyle, Union{CategoricalAesthetic,Vector,Nothing} label, CategoricalAesthetic diff --git a/src/data.jl b/src/data.jl index d4410a1c7..1685a469f 100644 --- a/src/data.jl +++ b/src/data.jl @@ -38,6 +38,7 @@ color group label + alpha func titles, Dict{Symbol, AbstractString}, Dict{Symbol, AbstractString}() end diff --git a/src/gadfly.js b/src/gadfly.js index 10a2882ef..e55d6bbf7 100644 --- a/src/gadfly.js +++ b/src/gadfly.js @@ -177,14 +177,14 @@ Gadfly.helpscreen_visible = function(event) { helpscreen_visible(this.plotroot()); }; var helpscreen_visible = function(root) { - root.select(".helpscreen").animate({opacity: 1.0}, 250); + root.select(".helpscreen").animate({"fill-opacity": 1.0}, 250); }; Gadfly.helpscreen_hidden = function(event) { helpscreen_hidden(this.plotroot()); }; var helpscreen_hidden = function(root) { - root.select(".helpscreen").animate({opacity: 0.0}, 250); + root.select(".helpscreen").animate({"fill-opacity": 0.0}, 250); }; // When the plot is moused over, emphasize the grid lines. @@ -226,7 +226,7 @@ Gadfly.plot_mouseover = function(event) { } else if (event.which == 67) { // c root.data("crosshair",!root.data("crosshair")); root.select(".crosshair") - .animate({opacity: root.data("crosshair") ? 1.0 : 0.0}, 250); + .animate({"fill-opacity": root.data("crosshair") ? 1.0 : 0.0}, 250); } }; root.data("keyboard_pan_zoom", keyboard_pan_zoom); @@ -254,8 +254,8 @@ Gadfly.plot_mouseover = function(event) { } root.select(".crosshair") - .animate({opacity: root.data("crosshair") ? 1.0 : 0.0}, 250); - root.select(".questionmark").animate({opacity: 1.0}, 250); + .animate({"fill-opacity": root.data("crosshair") ? 1.0 : 0.0}, 250); + root.select(".questionmark").animate({"fill-opacity": 1.0}, 250); }; // Reset pan and zoom on double click @@ -289,8 +289,8 @@ Gadfly.plot_mouseout = function(event) { .animate({stroke: destcolor}, 250); } - root.select(".crosshair").animate({opacity: 0.0}, 250); - root.select(".questionmark").animate({opacity: 0.0}, 250); + root.select(".crosshair").animate({"fill-opacity": 0.0}, 250); + root.select(".questionmark").animate({"fill-opacity": 0.0}, 250); helpscreen_hidden(root); }; @@ -698,7 +698,7 @@ var zoom_action = { var h = yscalable ? 0 : height; zoom_box = root.rect(x, y, w, h).attr({ "fill": "#000", - "opacity": 0.25 + "fill-opacity": 0.25 }); }, update: function(root, dx, dy, x, y, event) { diff --git a/src/geom/point.jl b/src/geom/point.jl index b2e481a77..0e22b3516 100644 --- a/src/geom/point.jl +++ b/src/geom/point.jl @@ -28,7 +28,7 @@ Draw scatter plots of the `x` and `y` aesthetics. """ const point = PointGeometry -element_aesthetics(::PointGeometry) = [:x, :y, :size, :color, :shape] +element_aesthetics(::PointGeometry) = [:x, :y, :size, :color, :shape, :alpha] # Generate a form for a point geometry. # @@ -48,6 +48,7 @@ function render(geom::PointGeometry, theme::Gadfly.Theme, aes::Gadfly.Aesthetics default_aes.shape = Function[Shape.circle] default_aes.color = discretize_make_ia(RGBA{Float32}[theme.default_color]) default_aes.size = Measure[theme.point_size] + default_aes.alpha = [theme.alphas[1]] aes = inherit(aes, default_aes) if eltype(aes.size) <: Int @@ -57,9 +58,11 @@ function render(geom::PointGeometry, theme::Gadfly.Theme, aes::Gadfly.Aesthetics interpolate_size(x) = theme.point_size_min + (x-size_min) / size_range * point_size_range end + aes_alpha = eltype(aes.alpha) <: Int ? theme.alphas[aes.alpha] : aes.alpha + ctx = context() - for (x, y, color, size, shape) in Compose.cyclezip(aes.x, aes.y, aes.color, aes.size, aes.shape) + for (x, y, color, size, shape, alpha) in Compose.cyclezip(aes.x, aes.y, aes.color, aes.size, aes.shape, aes_alpha) shapefun = typeof(shape) <: Function ? shape : theme.point_shapes[shape] sizeval = typeof(size) <: Int ? interpolate_size(size) : size strokecolor = aes.color_key_continuous != nothing && aes.color_key_continuous ? @@ -68,7 +71,8 @@ function render(geom::PointGeometry, theme::Gadfly.Theme, aes::Gadfly.Aesthetics class = svg_color_class_from_label(aes.color_label([color])[1]) compose!(ctx, (context(), (context(), shapefun([x], [y], [sizeval]), svgclass("marker")), - fill(color), stroke(strokecolor), svgclass(class))) + fill(color), stroke(strokecolor), fillopacity(alpha), + svgclass(class))) end compose!(ctx, linewidth(theme.highlight_width)) diff --git a/src/geom/polygon.jl b/src/geom/polygon.jl index 2a58a37f8..6804e4b09 100644 --- a/src/geom/polygon.jl +++ b/src/geom/polygon.jl @@ -54,13 +54,14 @@ function render(geom::PolygonGeometry, theme::Gadfly.Theme, aes::Gadfly.Aestheti default_aes.group = IndirectArray(fill(1,length(aes.x))) default_aes.color = fill(theme.default_color, length(aes.x)) default_aes.linestyle = fill(1, length(aes.x)) + default_aes.alpha = fill(1, length(aes.x)) aes = inherit(aes, default_aes) - aes_x, aes_y, aes_color, aes_linestyle, aes_group = concretize(aes.x, aes.y, aes.color, aes.linestyle, aes.group) + aes_x, aes_y, aes_color, aes_linestyle, aes_group, aes_alpha = concretize(aes.x, aes.y, aes.color, aes.linestyle, aes.group, aes.alpha) - XT, YT, CT, GT, LST = eltype(aes_x), eltype(aes_y), eltype(aes_color), eltype(aes_group), eltype(aes_linestyle) + XT, YT, CT, GT, LST, AT = eltype(aes_x), eltype(aes_y), eltype(aes_color), eltype(aes_group), eltype(aes_linestyle), eltype(aes_alpha) - groups = collect((Tuple{CT, GT, LST}), zip(aes_color, aes_group, aes_linestyle)) + groups = collect((Tuple{CT, GT, LST, AT}), zip(aes_color, aes_group, aes_linestyle, aes_alpha)) ug = unique(groups) n = length(ug) @@ -69,20 +70,25 @@ function render(geom::PolygonGeometry, theme::Gadfly.Theme, aes::Gadfly.Aestheti colors = Vector{CT}(undef, n) line_styles = Vector{LST}(undef, n) linestyle_palette_length = length(theme.line_style) + alphas = Vector{Float64}(undef, n) + alpha_discrete = AT <: Int + for (k,g) in enumerate(ug) i = groups.==[g] polys[k] = polygon_points(aes_x[i], aes_y[i], geom.preserve_order) colors[k] = first(aes_color[i]) line_styles[k] = mod1(first(aes_linestyle[i]), linestyle_palette_length) + alphas[k] = first(alpha_discrete ? theme.alphas[aes_alpha[i]] : aes_alpha[i]) end plinestyles = Gadfly.get_stroke_vector.(theme.line_style[line_styles]) pcolors = theme.lowlight_color.(colors) - properties = geom.fill ? (fill(pcolors), stroke(theme.discrete_highlight_color.(colors))) : + properties = geom.fill ? (fill(pcolors), stroke(theme.discrete_highlight_color.(colors)), fillopacity(alphas)) : (fill(nothing), stroke(pcolors), strokedash(plinestyles)) - ctx = compose!(context(order=geom.order), Compose.polygon(polys, geom.tag), properties...) + ctx = context(order=geom.order) + compose!(ctx, Compose.polygon(polys, geom.tag), properties...) return compose!(ctx, linewidth(theme.line_width), svgclass("geometry")) end diff --git a/src/geom/ribbon.jl b/src/geom/ribbon.jl index 6112ff1c7..90347d89f 100644 --- a/src/geom/ribbon.jl +++ b/src/geom/ribbon.jl @@ -16,35 +16,36 @@ const ribbon = RibbonGeometry default_statistic(geom::RibbonGeometry) = geom.default_statistic -element_aesthetics(::RibbonGeometry) = [:x, :ymin, :ymax, :color, :linestyle] +element_aesthetics(::RibbonGeometry) = [:x, :ymin, :ymax, :color, :linestyle, :alpha] function render(geom::RibbonGeometry, theme::Gadfly.Theme, aes::Gadfly.Aesthetics) Gadfly.assert_aesthetics_defined("Geom.ribbon", aes, :x, :ymin, :ymax) - Gadfly.assert_aesthetics_equal_length("Geom.ribbon", aes, - element_aesthetics(geom)...) + Gadfly.assert_aesthetics_equal_length("Geom.ribbon", aes, element_aesthetics(geom)...) default_aes = Gadfly.Aesthetics() default_aes.linestyle = fill(1, length(aes.x)) default_aes.color = fill(theme.default_color, length(aes.x)) + default_aes.alpha = fill(1, length(aes.x)) aes = inherit(aes, default_aes) - aes_x, aes_ymin, aes_ymax, aes_color, aes_linestyle = concretize(aes.x, aes.ymin, aes.ymax, aes.color, aes.linestyle) - XT, CT, LST = eltype(aes_x), eltype(aes_color), eltype(aes_linestyle) + aes_x, aes_ymin, aes_ymax, aes_color, aes_linestyle, aes_alpha = + concretize(aes.x, aes.ymin, aes.ymax, aes.color, aes.linestyle, aes.alpha) + XT, CT, LST, AT = eltype(aes_x), eltype(aes_color), eltype(aes_linestyle), eltype(aes_alpha) YT = Float64 - groups = collect((Tuple{CT, LST}), zip(aes_color, aes_linestyle)) + groups = collect((Tuple{CT, LST, AT}), zip(aes_color, aes_linestyle, aes_alpha)) ug = unique(groups) V = Vector{Tuple{XT, YT}} - K = Tuple{CT, LST} + K = Tuple{CT, LST, AT} max_points = Dict{K, V}(g=>V[] for g in ug) - for (x, y, c, ls) in zip(aes_x, aes_ymax, aes_color, aes_linestyle) - push!(max_points[(c,ls)], (x, y)) + for (x, y, c, ls, a) in zip(aes_x, aes_ymax, aes_color, aes_linestyle, aes_alpha) + push!(max_points[(c,ls,a)], (x, y)) end min_points = Dict{K, V}(g=>V[] for g in ug) - for (x, y, c, ls) in zip(aes_x, aes_ymin, aes_color, aes_linestyle) - push!(min_points[(c,ls)], (x, y)) + for (x, y, c, ls, a) in zip(aes_x, aes_ymin, aes_color, aes_linestyle, aes_alpha) + push!(min_points[(c,ls,a)], (x, y)) end for k in keys(max_points) @@ -56,14 +57,22 @@ function render(geom::RibbonGeometry, theme::Gadfly.Theme, aes::Gadfly.Aesthetic polys = [collect(Tuple{XT, YT}, Iterators.flatten((min_points[k], max_points[k]))) for k in kys] lines = [collect(Tuple{XT, YT}, Iterators.flatten((min_points[k], [(last(min_points[k])[1], NaN)], max_points[k]))) for k in kys] - colors = [theme.lowlight_color(c) for (c,ls) in kys] - linestyles = [Gadfly.get_stroke_vector(theme.line_style[ls]) for (c,ls) in kys] + n = length(kys) + colors = Vector{Union{Colorant, String}}(undef, n) + linestyles = Vector{Vector{Measure}}(undef, n) + alphas = Vector{Float64}(undef, n) + alpha_discrete = AT <: Int + + for (i, (c,ls,a)) in enumerate(kys) + colors[i] = theme.lowlight_color(c) + linestyles[i] = Gadfly.get_stroke_vector(theme.line_style[ls]) + alphas[i] = alpha_discrete ? theme.alphas[a] : a + end + + ctx = context() - ctx = geom.fill ? compose!(context(), Compose.polygon(polys, geom.tag), fill(colors)) : - compose!(context(), Compose.line(lines, geom.tag), fill(nothing), stroke(colors), strokedash(linestyles)) + geom.fill ? compose!(ctx, Compose.polygon(polys, geom.tag), fill(colors), fillopacity(alphas)) : + compose!(ctx, Compose.line(lines, geom.tag), fill(nothing), stroke(colors), strokedash(linestyles)) - return compose!( - ctx, - svgclass("geometry"), - linewidth(theme.line_width)) + return compose!(ctx, svgclass("geometry"), linewidth(theme.line_width)) end diff --git a/src/guide.jl b/src/guide.jl index 0acbb8ac9..e582dbac0 100644 --- a/src/guide.jl +++ b/src/guide.jl @@ -250,11 +250,8 @@ function render_discrete_color_key(colors::Vector{C}, swatches_shapes = Shape.circle([0.5cy], 1:nrows, [swatch_size/2]) end cs = colors[m+1:m+nrows] - swatches = compose!( - context(), - swatches_shapes, - stroke(nothing), - fill(cs)) + swatches = compose!(context(), swatches_shapes, stroke(theme.discrete_highlight_color.(cs)), + fill(cs), fillopacity(theme.alphas[1])) swatch_labels = compose!( context(), @@ -346,6 +343,7 @@ function render_continuous_color_key(colors::Dict, fill([color_function((i-1) / (theme.key_color_gradations - 1)) for i in 1:theme.key_color_gradations]), + fillopacity(theme.alphas[1]), stroke(nothing), svgattribute("shape-rendering", "crispEdges"))) diff --git a/src/guide/keys.jl b/src/guide/keys.jl index de1216994..422e34447 100644 --- a/src/guide/keys.jl +++ b/src/guide/keys.jl @@ -131,7 +131,8 @@ function render_discrete_key(labels::Vector{String}, title_ctx::Context, title_w clrs = colors[m+1:m+nrows] shps = shapes[m+1:m+nrows] swatches_shapes = [f(x, [y].*cy, [swatch_size/1.5]) for (y,f) in enumerate(shps)] - sw1 = [(context(), s, fill(c)) for (s,c) in zip(swatches_shapes, clrs)] + sw1 = [(context(), s, fill(c), fillopacity(theme.alphas[1]), stroke(theme.discrete_highlight_color(c))) + for (s,c) in zip(swatches_shapes, clrs)] swatches = compose!(context(), sw1...) swatch_labels = compose!( diff --git a/src/scale.jl b/src/scale.jl index e26e07694..73e53733e 100644 --- a/src/scale.jl +++ b/src/scale.jl @@ -167,6 +167,17 @@ const size_continuous = continuous_scale_partial([:size], identity_transform) """ const slope_continuous = continuous_scale_partial([:slope], identity_transform) +""" + alpha_continuous[(; minvalue=0.0, maxvalue=1.0, labels=nothing, + format=nothing, minticks=2, maxticks=10, scalable=true)] + +Rescale the data values between `minvalue` and `maxvalue` to opacity (alpha) values between 0 and 1. +""" +alpha_continuous(; minvalue=0.0, maxvalue=1.0, labels=nothing, format=nothing, minticks=2, maxticks=10, scalable=true) = + ContinuousScale([:alpha], identity_transform, minvalue=minvalue, maxvalue=maxvalue, + labels=labels, format=format, minticks=minticks, maxticks=maxticks, scalable=scalable) + + function apply_scale(scale::ContinuousScale, aess::Vector{Gadfly.Aesthetics}, datas::Gadfly.Data...) @@ -201,14 +212,16 @@ function apply_scale(scale::ContinuousScale, end end - if T <: Measure - T = Measure - end + T <: Measure && (T = Measure) ds = any(ismissing, vals) ? Array{Union{Missing,T}}(undef,length(vals)) : Array{T}(undef,length(vals)) apply_scale_typed!(ds, vals, scale) + if var == :alpha + ds = (vals.-scale.minvalue)./(scale.maxvalue-scale.minvalue) + end + if var == :xviewmin || var == :xviewmax || var == :yviewmin || var == :yviewmax setfield!(aes, var, ds[1]) @@ -313,7 +326,7 @@ anything in the data that's not respresented in `levels` will be set to default order. See also [`group_discrete`](@ref), [`shape_discrete`](@ref), -[`size_discrete`](@ref), and [`linestyle_discrete`](@ref). +[`size_discrete`](@ref), [`linestyle_discrete`](@ref), and [`alpha_discrete`](@ref). """ @doc xy_discrete_docstr("x", aes2str(element_aesthetics(x_discrete()))) x_discrete(; labels=nothing, levels=nothing, order=nothing) = @@ -337,6 +350,15 @@ Similar to [`Scale.x_discrete`](@ref), except applied to the `$aes` aesthetic. @doc type_discrete_docstr("size") size_discrete(; labels=nothing, levels=nothing, order=nothing) = DiscreteScale([:size], labels=labels, levels=levels, order=order) +""" + alpha_discrete[(; labels=nothing, levels=nothing, order=nothing)] + +Similar to [`Scale.x_discrete`](@ref), except applied to the `alpha` aesthetic. The alpha palette +is set by `Theme(alphas=[])`. +""" +alpha_discrete(; labels=nothing, levels=nothing, order=nothing) = + DiscreteScale([:alpha], labels=labels, levels=levels, order=order) + @doc type_discrete_docstr("linestyle") linestyle_discrete(; labels=nothing, levels=nothing, order=nothing) = DiscreteScale([:linestyle], labels=labels, levels=levels, order=order) diff --git a/src/theme.jl b/src/theme.jl index d188f3d57..8646ba5b1 100755 --- a/src/theme.jl +++ b/src/theme.jl @@ -32,8 +32,10 @@ function default_lowlight_color(fill_color::Color) LCHab(90, 20, c.h) end -default_lowlight_color(fill_color::TransparentColor) = RGBA{Float32}( - default_lowlight_color(color(fill_color)), fill_color.alpha) +function default_lowlight_color(fill_color::TransparentColor) + @warn "For opacity, use `Theme(alphas=[a])` and/or `Scale.alpha_discrete()`, or use `Scale.alpha_continuous()`" + RGBA{Float32}(Gadfly.default_lowlight_color(color(fill_color)), fill_color.alpha) +end # Choose a middle color by darkening the fill color function default_middle_color(fill_color::Color) @@ -90,6 +92,9 @@ $(FIELDS) "Style of lines in the line geometry. The default palette is `[:solid, :dash, :dot, :dashdot, :dashdotdot, :ldash, :ldashdash, :ldashdot, :ldashdashdot]` which is a Vector{Symbol}, or customize using Vector{Vector{<:Measure}}", line_style, (Vector{<:Union{Symbol, Vector{<:Measure}}}), [:solid, :dash, :dot, :dashdot, :dashdotdot, :ldash, :ldashdash, :ldashdot, :ldashdashdot] + "Alpha palette. The default palette is [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]. Customize using a Vector of length one or greater, with 0.0≤values≤1.0", + alphas, Vector{Float64}, [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0] + "Background color used in the main plot panel. (Color or Nothing)", panel_fill, ColorOrNothing, nothing diff --git a/test/testscripts/scale_alpha_continuous.jl b/test/testscripts/scale_alpha_continuous.jl new file mode 100644 index 000000000..af84c4a73 --- /dev/null +++ b/test/testscripts/scale_alpha_continuous.jl @@ -0,0 +1,20 @@ +using DataFrames, Gadfly + +set_default_plot_size(21cm, 8cm) + + +D = DataFrame(x=1:10, y=[0.11, 0.2, 0.1, 0.15, 0.3, 0.45, 0.37, 0.81, 0.62, 0.5]) +palettef = Scale.lab_gradient("darkgreen","orange", "blue") +p1 = plot(D, x=:x, y=:y, color=:x, Geom.point, + Scale.color_continuous(colormap=palettef, minvalue=0, maxvalue=10), + Guide.title("Scale.color_continuous, Theme(alphas=[0.5])"), + Theme(alphas=[0.5], continuous_highlight_color=identity, + point_size=2mm) +) +p2 = plot(D, x=:x, y=:y, alpha=:x, Geom.point, + Scale.alpha_continuous(minvalue=0, maxvalue=10), + Guide.title("Scale.alpha_continuous, Theme(default_color=\"blue\")"), + Theme(default_color="blue", discrete_highlight_color=c->"gray", + point_size=2mm) +) +hstack(p1, p2) diff --git a/test/testscripts/scale_alpha_discrete.jl b/test/testscripts/scale_alpha_discrete.jl new file mode 100644 index 000000000..3f8bb48cf --- /dev/null +++ b/test/testscripts/scale_alpha_discrete.jl @@ -0,0 +1,19 @@ +using DataFrames, Gadfly + +set_default_plot_size(21cm, 8cm) + +D = DataFrame(x=1:6, y=[0.39, 0.26, 0.31, 0.43, 0.1, 0.78], Shape=repeat(["a","b","c"], outer=2)) +coord = Coord.cartesian(xmin=0, xmax=7, ymin=0, ymax=1.0) +p1 = plot(D, x=:x, y=:y, color=:x, coord, + Scale.color_discrete, Geom.point, Geom.hair, + Guide.title("Scale.color_discrete, Theme(alphas=[0.5])"), + Theme(alphas=[0.5], discrete_highlight_color=identity, + point_size=2mm) +) +p2 = plot(D, x=:x, y=:y, alpha=:x, shape=:Shape, coord, + Scale.alpha_discrete, Geom.point, Geom.hair, + Guide.title("Scale.alpha_discrete, Theme(default_color=\"green\")"), + Theme(default_color="green", discrete_highlight_color=c->"gray", + point_size=2mm, alphas=[0.0,0.2,0.4,0.6,0.8,1.0]) +) +hstack(p1,p2) \ No newline at end of file diff --git a/test/testscripts/stat_smooth.jl b/test/testscripts/stat_smooth.jl index 15c7c3ad4..8cf0bd328 100644 --- a/test/testscripts/stat_smooth.jl +++ b/test/testscripts/stat_smooth.jl @@ -5,6 +5,5 @@ iris = dataset("datasets","iris") p = plot(iris, x=:SepalLength, y=:PetalLength, color=:Species, Geom.point, layer(Stat.smooth(method=:lm, levels=[0.90, 0.99]), Geom.line, Geom.ribbon), - Theme(lowlight_color=c->RGBA{Float32}(c.r, c.g, c.b, 0.2), - key_position=:inside) + Theme(alphas=[0.6], key_position=:inside) )