Skip to content

Commit

Permalink
update Geom.polygon and Geom.ribbon (#1511)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mattriks authored Jan 15, 2021
1 parent 7144517 commit eef6074
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 124 deletions.
2 changes: 1 addition & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Each release typically has a number of minor bug fixes beyond what is listed her

# Version 1.x


* Support one-length aesthetics for `Geom.polygon` and `Geom.ribbon` (#1511)
* Enable `color` grouping for `Geom.density2d` (#1508)

# Version 1.3.1
Expand Down
33 changes: 17 additions & 16 deletions docs/src/gallery/geometries.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,19 +206,15 @@ set_default_plot_size(21cm, 8cm)
D = dataset("datasets","faithful")
D.g = D.Eruptions.>3.0
coord = Coord.cartesian(ymin=40, ymax=100)
pa = plot(D, coord,
x=:Eruptions, y=:Waiting, group=:g,
Geom.point, Geom.ellipse,
Theme(lowlight_color=c->"gray") )
pb = plot(D, coord, Guide.ylabel(nothing),
x=:Eruptions, y=:Waiting, color=:g,
pa = plot(D, coord, x=:Eruptions, y=:Waiting, group=:g,
Geom.point, Geom.ellipse, Theme(lowlight_color=c->"gray"))
pb = plot(D, coord, Guide.ylabel(nothing), x=:Eruptions, y=:Waiting, color=:g,
Geom.point, Geom.ellipse(levels=[0.95, 0.99]),
Theme(key_position=:none, lowlight_color=identity, line_style=[:solid,:dot]))
pc = plot(D, coord, Guide.ylabel(nothing),
x=:Eruptions, y=:Waiting, color=:g,
Theme(key_position=:none, lowlight_color=identity, line_style=[:solid,:dot]))
pc = plot(D, coord, Guide.ylabel(nothing), x=:Eruptions, y=:Waiting, color=:g,
Geom.point, Geom.ellipse(fill=true),
layer(Geom.ellipse(levels=[0.99]), style(line_style=[:dot])),
Theme(key_position=:none) )
Theme(key_position=:none))
hstack(pa,pb,pc)
```

Expand Down Expand Up @@ -434,13 +430,18 @@ plot(layer(x=rdata[1,:], y=rdata[2,:], color=[colorant"red"], Geom.point),
## [`Geom.polygon`](@ref)

```@example
using Gadfly
set_default_plot_size(14cm, 8cm)
plot(x=[0, 1, 1, 2, 2, 3, 3, 2, 2, 1, 1, 0, 4, 5, 5, 4],
using Gadfly, DataFrames
set_default_plot_size(21cm, 8cm)
p1 = plot(x=[0, 1, 1, 2, 2, 3, 3, 2, 2, 1, 1, 0, 4, 5, 5, 4],
y=[0, 0, 1, 1, 0, 0, 3, 3, 2, 2, 3, 3, 0, 0, 3, 3],
group=["H", "H", "H", "H", "H", "H", "H", "H",
"H", "H", "H", "H", "I", "I", "I", "I"],
group=reduce(vcat, fill.(["H", "I"], [12,4])),
Geom.polygon(preserve_order=true, fill=true))
Dps = reduce(vcat, [DataFrame(x=[1, 5, 5, 1].+d, y=[14, 14, 10, 10].-d, id=d+1) for d in 0:9])
p2 = plot(Dps, x=:x, y=:y, color=:id, alpha=[0.3], linestyle=[:dash],
Geom.polygon(fill=true), Scale.color_discrete,
Theme(line_width=2pt, lowlight_color=identity, discrete_highlight_color=identity))
hstack(p1, p2)
```


Expand Down Expand Up @@ -478,7 +479,7 @@ Db = [DataFrame(x=x, ymax=pdf.(Normal(μ),x), ymin=0.0, u="μ=$μ") for μ in [-
# 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])
Geom.line, Geom.ribbon, alpha=[0.6]
)
p2 = plot(vcat(Db...), x = :x, y=:ymax, ymin = :ymin, ymax = :ymax,
color = :u, alpha=:u, Theme(alphas=[0.8,0.2]),
Expand Down
56 changes: 31 additions & 25 deletions src/geom/polygon.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Draw polygons with vertices specified by the `x` and `y` aesthetics.
Optionally plot multiple polygons according to the `group`, `color`, `linestyle`, and/or `alpha`
aesthetics. `order` controls whether the polygon(s) are underneath or on top
of other forms. If `fill=true`, fill the polygons using `Theme.lowlight_color` and stroke the polygons using
`Theme.discrete_highlight_color`. If `fill=false` stroke the polygons using `Theme.lowlight_color` and `Theme.line_style`.
`Theme.discrete_highlight_color`. If `fill=false` stroke the polygons using `Theme.lowlight_color`.
If `preserve_order=true` connect points in the order they are given, otherwise order the points
around their centroid.
"""
Expand Down Expand Up @@ -51,41 +51,47 @@ function render(geom::PolygonGeometry, theme::Gadfly.Theme, aes::Gadfly.Aestheti
Gadfly.assert_aesthetics_defined("Geom.polygon", aes, :x, :y)

default_aes = Gadfly.Aesthetics()
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))
default_aes.group = IndirectArray([1])
default_aes.color = Colorant[theme.default_color]
default_aes.linestyle = [1]
default_aes.alpha = [1]
aes = inherit(aes, default_aes)

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, 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, AT}), zip(aes_color, aes_group, aes_linestyle, aes_alpha))
ug = unique(groups)
groups = collect((Tuple{CT, GT, LST, AT}), Compose.cyclezip(aes_color, aes_group, aes_linestyle, aes_alpha))
ugroups = unique(groups)
nugroups = length(ugroups)

n = length(ug)
polys = Vector{Vector{Tuple{XT,YT}}}(undef, n)
θs = Vector{Float64}
colors = Vector{CT}(undef, n)
line_styles = Vector{LST}(undef, n)
polys = Vector{Vector{Tuple{XT,YT}}}(undef, nugroups)
colors = Vector{Colorant}(undef, nugroups)
stroke_colors = Vector{Colorant}(undef, nugroups)
linestyles = Vector{Vector{Measure}}(undef, nugroups)
linestyle_palette_length = length(theme.line_style)
alphas = Vector{Float64}(undef, n)
alphas = Vector{Float64}(undef, nugroups)
alpha_discrete = AT <: Int
linestyle_discrete = LST <: Int

if nugroups==1
polys[1] = polygon_points(aes_x, aes_y, geom.preserve_order)
elseif nugroups>1
for (k,g) in enumerate(ugroups)
i = groups.==[g]
polys[k] = polygon_points(aes_x[i], aes_y[i], geom.preserve_order)
end
end

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])
for (k, (c, g, ls, a)) in enumerate(ugroups)
colors[k] = parse_colorant(theme.lowlight_color(c))
stroke_colors[k] = parse_colorant(theme.discrete_highlight_color(c))
linestyles[k] = linestyle_discrete ? get_stroke_vector(theme.line_style[mod1(ls, linestyle_palette_length)]) : get_stroke_vector(ls)
alphas[k] = alpha_discrete ? theme.alphas[a] : a
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)), fillopacity(alphas)) :
(fill(nothing), stroke(pcolors), strokedash(plinestyles))
properties = geom.fill ? (fill(colors), stroke(stroke_colors), fillopacity(alphas), strokedash(linestyles)) :
(fill(nothing), stroke(colors), strokedash(linestyles))

ctx = context(order=geom.order)
compose!(ctx, Compose.polygon(polys, geom.tag), properties...)
Expand Down
43 changes: 21 additions & 22 deletions src/geom/ribbon.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,30 @@ element_aesthetics(::RibbonGeometry) = [:x, :ymin, :ymax, :color, :linestyle, :a

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, :x, :ymin, :ymax)

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))
default_aes.linestyle = [1]
default_aes.color = Colorant[theme.default_color]
default_aes.alpha = [1]
aes = inherit(aes, default_aes)

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 = eltype(aes_ymin)
groups = collect((Tuple{CT, LST, AT}), zip(aes_color, aes_linestyle, aes_alpha))
ug = unique(groups)
groups = collect((Tuple{CT, LST, AT}), Compose.cyclezip(aes_color, aes_linestyle, aes_alpha))
ugroups = unique(groups)
nugroups = length(ugroups)

V = Vector{Tuple{XT, YT}}
K = Tuple{CT, LST, AT}

max_points = Dict{K, V}(g=>V[] for g in ug)
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, a) in zip(aes_x, aes_ymin, aes_color, aes_linestyle, aes_alpha)
push!(min_points[(c,ls,a)], (x, y))
max_points = Dict{K, V}(g=>V[] for g in ugroups)
min_points = Dict{K, V}(g=>V[] for g in ugroups)
for (x, ymin, ymax, c, ls, a) in Compose.cyclezip(aes_x, aes_ymin, aes_ymax, aes_color, aes_linestyle, aes_alpha)
push!(max_points[(c,ls,a)], (x, ymax))
push!(min_points[(c,ls,a)], (x, ymin))
end

for k in keys(max_points)
Expand All @@ -58,22 +56,23 @@ 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, Union{YT, Missing}}, Iterators.flatten((min_points[k], [(last(min_points[k])[1], missing)], max_points[k]))) for k in kys]

n = length(kys)
colors = Vector{Union{Colorant, String}}(undef, n)
linestyles = Vector{Vector{Measure}}(undef, n)
alphas = Vector{Float64}(undef, n)
colors = Vector{Colorant}(undef, nugroups)
linestyles = Vector{Vector{Measure}}(undef, nugroups)
linestyle_palette_length = length(theme.line_style)
alphas = Vector{Float64}(undef, nugroups)
alpha_discrete = AT <: Int
linestyle_discrete = LST <: 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])
colors[i] = parse_colorant(theme.lowlight_color(c))
linestyles[i] = linestyle_discrete ? get_stroke_vector(theme.line_style[mod1(ls, linestyle_palette_length)]) : get_stroke_vector(ls)
alphas[i] = alpha_discrete ? theme.alphas[a] : a
end

ctx = context()

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))
geom.fill ? compose!(ctx, Compose.polygon(polys, geom.tag), fill(colors), fillopacity(alphas)) :
compose!(ctx, Compose.line(lines, geom.tag), stroke(colors), strokedash(linestyles))

return compose!(ctx, svgclass("geometry"), linewidth(theme.line_width))
end
2 changes: 1 addition & 1 deletion src/geometry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Compose.combine # Prevent DataFrame.combine from taking over.
import Gadfly: render, layers, element_aesthetics, inherit, escape_id,
default_statistic, default_scales, element_coordinate_type,
ScaleElement, svg_color_class_from_label, isconcrete,
concretize, discretize_make_ia
concretize, discretize_make_ia, get_stroke_vector, parse_colorant
import IterTools: takestrict

const empty_tag = Symbol("")
Expand Down
43 changes: 6 additions & 37 deletions src/misc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,47 +16,16 @@ end


function concretize(xss::AbstractVector...)
if all(map(isallconcrete, xss))
return xss
end

count = 0
for j in 1:length(xss[1])
for xs in xss
if !isconcrete(xs[j])
@goto next_j1
end
end

count += 1
@label next_j1
end

yss = Vector{AbstractVector}(undef, length(xss))
for (i, xs) in enumerate(xss)
yss[i] = Vector{eltype(xs)}(undef, count)
end
all(isallconcrete, xss) && return xss

k = 1
for j in 1:length(xss[1])
for xs in xss
if !isconcrete(xs[j])
@goto next_j2
end
end

for (i, xs) in enumerate(xss)
yss[i][k] = xs[j]
end
k += 1

@label next_j2
end

return tuple(yss...)
cf = mapreduce(x->isconcrete.(x), (x,y)->x.&y, xss)
n = length(cf)
yss = [(length(xs)==n ? xs[cf] : xs) for xs in xss]
return yss
end



# How many concrete elements in an iterable
function concrete_length(xs)
n = 0
Expand Down
2 changes: 1 addition & 1 deletion src/theme.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function default_lowlight_color(fill_color::Color)
end

function default_lowlight_color(fill_color::TransparentColor)
@warn "For opacity, use `Theme(alphas=[a])` and/or `Scale.alpha_discrete()`, or use `Scale.alpha_continuous()`"
@warn "For opacity, use `alpha=[a]`, or 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

Expand Down
25 changes: 12 additions & 13 deletions test/testscripts/polygon.jl
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
using Gadfly

set_default_plot_size(6inch, 3inch)

plot(x=[0, 1, 1, 2, 2, 3, 3, 2, 2, 1, 1, 0, 4, 5, 5, 4],
y=[0, 0, 1, 1, 0, 0, 3, 3, 2, 2, 3, 3, 0, 0, 3, 3],
group=["H", "H", "H", "H", "H", "H", "H", "H",
"H", "H", "H", "H", "I", "I", "I", "I"],
Geom.polygon(preserve_order=false, fill=true))

plot(x=[0, 1, 1, 2, 2, 3, 3, 2, 2, 1, 1, 0, 4, 5, 5, 4],
y=[0, 0, 1, 1, 0, 0, 3, 3, 2, 2, 3, 3, 0, 0, 3, 3],
group=["H", "H", "H", "H", "H", "H", "H", "H",
"H", "H", "H", "H", "I", "I", "I", "I"],
Geom.polygon(preserve_order=true, fill=true))
set_default_plot_size(8inch, 3inch)

x = [0, 1, 1, 2, 2, 3, 3, 2, 2, 1, 1, 0, 4, 5, 5, 4]
y = [0, 0, 1, 1, 0, 0, 3, 3, 2, 2, 3, 3, 0, 0, 3, 3]
group = reduce(vcat, fill.(["H", "I"], [12,4]))


p1 = plot(x=x, y=y, group=group, Geom.polygon(preserve_order=false, fill=true))
p2 = plot(x=x, y=y, group=group, Geom.polygon(preserve_order=true, fill=false),
Theme(line_width=2mm), linestyle=[:dash], color=[colorant"orange"] )

hstack(p1, p2)
15 changes: 7 additions & 8 deletions test/testscripts/ribbon.jl
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
using Gadfly, DataFrames

set_default_plot_size(6inch, 3inch)
set_default_plot_size(12inch, 3inch)

xs = 0:0.1:20

df = DataFrame(
x=xs,
y=cos.(xs),
ymin=cos.(xs) .- 0.5,
ymax=cos.(xs) .+ 0.5,
)
df = [DataFrame(x=xs, y=y, ymin=y.-0.5, ymax=y.+0.5, f=f) for (y,f) in zip((cos.(xs), sin.(xs)), ("cos", "sin"))]

plot(df, x=:x, y=:y, ymin=:ymin, ymax=:ymax, Geom.line, Geom.ribbon)
p1 = plot(df[1], x=:x, y=:y, ymin=:ymin, ymax=:ymax, Geom.line, Geom.ribbon)
p2 = plot(df[1], Geom.ribbon, x=:x, ymin=:ymin, ymax=:ymax, color=[colorant"red"], alpha=[0.3], Theme(lowlight_color=identity))
p3 = plot(vcat(df...), x=:x, y=:y, ymin=:ymin, ymax=:ymax, color=:f, Geom.line, Geom.ribbon)

hstack(p1, p2, p3)

0 comments on commit eef6074

Please sign in to comment.