Skip to content

Commit

Permalink
Segment Geometry
Browse files Browse the repository at this point in the history
A geometry for line segments/vectors/arrows. There is an example in the
file geom_segment.md.
  • Loading branch information
Mattriks committed Aug 30, 2016
1 parent f6183ac commit 9adbbf9
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 4 deletions.
49 changes: 49 additions & 0 deletions doc/geom_segment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
title: segment
author: Mattriks
part: Geometry
order: 1019
...
Draw separate line segments/vectors/arrows.
Note that if you want arrows, then you need to provide a `Scale` object for both axes. See example below.

# Aesthetics

* `x`: Start of line segment.
* `y`: Start of line segment.
* `xend`: End of line segment.
* `yend`: End of line segment.
* `color` (optional): Color of line segments.

# Arguments

* `arrow`: Default behavior for `Geom.segment` is to draw line segments without arrows.
`Geom.vector()` is `Geom.segment(arrow=true)`.


# Examples

```{.julia hide="true" results="none"}
using RDatasets
using Gadfly
Gadfly.set_default_plot_size(6.6inch, 6.6inch)
```

```julia
seals = RDatasets.dataset("ggplot2","seals")
seals[:Latb] = seals[:Lat] + seals[:DeltaLat]
seals[:Longb] = seals[:Long] + seals[:DeltaLong]
seals[:Angle] = atan2(seals[:DeltaLat], seals[:DeltaLong])
coord = Coord.cartesian(xmin=-175.0, xmax=-119, ymin=29, ymax=50)
# Geom.vector also needs scales for both axes:
xsc = Scale.x_continuous(minvalue=-175.0, maxvalue=-119)
ysc = Scale.y_continuous(minvalue=29, maxvalue=50)
colsc = Scale.color_continuous(minvalue=-3, maxvalue=3)
layer1 = layer(seals, x=:Long, y=:Lat, xend=:Longb, yend=:Latb, Geom.vector, color=:Angle)
plot(layer1, xsc, ysc, colsc, coord)
```

6 changes: 5 additions & 1 deletion src/Gadfly.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1124,7 +1124,9 @@ const default_aes_scales = @compat Dict{Symbol, Dict}(
:numerical => Dict{Symbol, Any}(:x => Scale.x_continuous(),
:xmin => Scale.x_continuous(),
:xmax => Scale.x_continuous(),
:xintercept => Scale.x_continuous(),
:xintercept => Scale.x_continuous(),
:xend => Scale.x_continuous(),
:yend => Scale.y_continuous(),
:y => Scale.y_continuous(),
:ymin => Scale.y_continuous(),
:ymax => Scale.y_continuous(),
Expand All @@ -1145,6 +1147,8 @@ const default_aes_scales = @compat Dict{Symbol, Dict}(
:xmin => Scale.x_discrete(),
:xmax => Scale.x_discrete(),
:xintercept => Scale.x_discrete(),
:xend => Scale.x_discrete(),
:yend => Scale.y_discrete(),
:y => Scale.y_discrete(),
:ymin => Scale.y_discrete(),
:ymax => Scale.y_discrete(),
Expand Down
2 changes: 2 additions & 0 deletions src/aesthetics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ typealias NumericalAesthetic
x, @compat(Union{NumericalOrCategoricalAesthetic, Distribution})
y, @compat(Union{NumericalOrCategoricalAesthetic, Distribution})
z, @compat(Union{(@compat Void), Function, Matrix})
xend, NumericalAesthetic
yend, NumericalAesthetic
size, Maybe(Vector{Measure})
shape, CategoricalAesthetic
color, Maybe(@compat(Union{AbstractVector{RGBA{Float32}},
Expand Down
2 changes: 2 additions & 0 deletions src/data.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
x
y
z
xend
yend
xmin
xmax
ymin
Expand Down
117 changes: 117 additions & 0 deletions src/geom/segment.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@

abstract SegmentGeometry <: Gadfly.GeometryElement

# Geometry for vectors/arrows/segments
immutable SegmentGeom <: SegmentGeometry

arrow::Bool
filled::Bool
tag::Symbol

function SegmentGeom(;arrow=false, filled=false, tag=empty_tag)
new(arrow, filled, tag)
end
end

const segment = SegmentGeom

# Leave this as a function, pending extra arguments e.g. arrow attributes
function vector(;filled::Bool=false, tag::Symbol=empty_tag)
return SegmentGeom(arrow=true, filled=filled, tag=tag)
end


function element_aesthetics(::SegmentGeom)
return [:x, :y, :xend, :yend, :color]
end


function Gadfly.render(geom::SegmentGeom, theme::Gadfly.Theme, aes::Gadfly.Aesthetics,
subplot_layer_aess::@compat(Union{(@compat Void), Vector{Gadfly.Aesthetics}}),
subplot_layer_datas::@compat(Union{(@compat Void), Vector{Gadfly.Data}}),
scales::Dict{Symbol, Gadfly.ScaleElement})
render(geom, theme, aes, scales)
end





function render(geom::SegmentGeom, theme::Gadfly.Theme, aes::Gadfly.Aesthetics,
scales::Dict{Symbol, Gadfly.ScaleElement})

Gadfly.assert_aesthetics_defined("Geom.segment", aes, :x, :y, :xend, :yend)

function arrow{T<:Real}(x::T, y::T, xmax::T, ymax::T, xyrange::Vector{T})
dx = xmax-x
dy = ymax-y
vl = 0.225*hypot(dy/xyrange[2], dx/xyrange[1])
θ = atan2(dy/xyrange[2], dx/xyrange[1])
ϕ = pi/15
xr = -vl*xyrange[1]*[cos+ϕ), cos-ϕ)]
yr = -vl*xyrange[2]*[sin+ϕ), sin-ϕ)]
arr = [(xmax+xr[1],ymax+yr[1]), (xmax,ymax), (xmax+xr[2],ymax+yr[2]) ]
return arr
end

n = length(aes.x)
color = ColorTypes.RGBA{Float32}(theme.default_color)
default_aes = Gadfly.Aesthetics()
default_aes.color = DataArrays.DataArray(fill(color,n))

aes = inherit(aes, default_aes)

# line_style = Gadfly.get_stroke_vector(theme.line_style)
line_style = theme.line_style
if is(line_style, nothing)
line_style = []
end


# Geom.vector requires information about scales

if geom.arrow
xscale = scales[:x]
yscale = scales[:y]
check = [xscale.minvalue, xscale.maxvalue, yscale.minvalue, yscale.maxvalue]
if any( map(x -> is(x,nothing), check) )
error("For Geom.vector, Scale minvalue and maxvalue must be manually provided for both axes")
end
fx = xscale.trans.f
fy = yscale.trans.f
xyrange = [fx(xscale.maxvalue)-fx(xscale.minvalue),
fy(yscale.maxvalue)-fy(yscale.minvalue)]

arrows = [ arrow(x, y, xend, yend, xyrange)
for (x, y, xend, yend) in zip(aes.x, aes.y, aes.xend, aes.yend) ]

end


segments = [ [(x,y), (xend,yend)]
for (x, y, xend, yend) in zip(aes.x, aes.y, aes.xend, aes.yend) ]

classes = [string("geometry ", svg_color_class_from_label( aes.color_label([c])[1] ))
for c in aes.color ]

ctx = context()

compose!( ctx, Compose.line(segments, geom.tag), stroke(aes.color), linewidth(theme.line_width),
strokedash(line_style), svgclass( classes ) )
if geom.arrow
if geom.filled
compose!(ctx, (context(), Compose.polygon(arrows), fill(aes.color), strokedash([])) )
else
compose!(ctx, (context(), Compose.line(arrows), stroke(aes.color), linewidth(theme.line_width),
strokedash([])) )
end
end


return ctx
end





1 change: 1 addition & 0 deletions src/geometry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,6 @@ include("geom/ribbon.jl")
include("geom/violin.jl")
include("geom/polygon.jl")
include("geom/beeswarm.jl")
include("geom/segment.jl")

end # module Geom
4 changes: 2 additions & 2 deletions src/scale.jl
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,10 @@ function make_labeler(scale::ContinuousScale)
end


const x_vars = [:x, :xmin, :xmax, :xintercept, :xviewmin, :xviewmax]
const x_vars = [:x, :xmin, :xmax, :xintercept, :xviewmin, :xviewmax, :xend]
const y_vars = [:y, :ymin, :ymax, :yintercept, :middle,
:upper_fence, :lower_fence, :upper_hinge, :lower_hinge,
:yviewmin, :yviewmax]
:yviewmin, :yviewmax, :yend]

function continuous_scale_partial(vars::Vector{Symbol},
trans::ContinuousScaleTransform)
Expand Down
3 changes: 2 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ tests = [
("rug", 6inch, 3inch),
("beeswarm", 6inch, 3inch),
("issue871", 6inch, 3inch),
("issue882", 6inch, 3inch)
("issue882", 6inch, 3inch),
("vector", 3.3inch, 3.3inch)
]


Expand Down
14 changes: 14 additions & 0 deletions test/vector.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@


using DataFrames, Gadfly


srand(123)
D = convert(DataFrame, 99*rand(4, 4)+0.5)

xsc = Scale.x_continuous(minvalue=0.0, maxvalue=100)
ysc = Scale.y_continuous(minvalue=0.0, maxvalue=100)

plot(D, x=:x1, y=:x2, xend=:x3, yend=:x4, Geom.segment(arrow=true), xsc, ysc)
plot(D, x=:x1, y=:x2, xend=:x3, yend=:x4, Geom.vector, xsc, ysc)

0 comments on commit 9adbbf9

Please sign in to comment.