changes for markers etc

This commit is contained in:
SimonDanisch 2016-11-17 17:29:22 +01:00
parent e2a4a1e2ea
commit 34a64db91d

View File

@ -1,4 +1,4 @@
#= ``#=
TODO TODO
* move all gl_ methods to GLPlot * move all gl_ methods to GLPlot
* integrate GLPlot UI * integrate GLPlot UI
@ -156,46 +156,34 @@ end
function create_window(plt::Plot{GLVisualizeBackend}, visible) function create_window(plt::Plot{GLVisualizeBackend}, visible)
name = Symbol("Plots.jl") name = Symbol("Plots.jl")
screen = if isempty(GLVisualize.get_screens()) # make sure we have any screen open
if isempty(GLVisualize.get_screens())
# create a fresh, new screen # create a fresh, new screen
parent = GLVisualize.glscreen( screen = GLVisualize.glscreen(
"Plot", "Plot",
resolution = plt[:size], resolution = plt[:size],
visible = visible visible = visible
) )
@async GLWindow.waiting_renderloop(parent) @async GLWindow.waiting_renderloop(screen)
screen = GLWindow.Screen(parent, end
name = name, # now lets get ourselves a permanent Plotting screen
area = map(identity, parent.area) plot_screens = get_plot_screen(GLVisualize.get_screens(), name)
screen = if isempty(plot_screens) # no screen with `name`
parent = GLVisualize.current_screen()
screen = GLWindow.Screen(
parent, area = map(GLWindow.zeroposition, parent.area),
name = name
) )
for (k, s) in screen.inputs # copy signals, so we can clean them up better for (k, s) in screen.inputs # copy signals, so we can clean them up better
screen.inputs[k] = map(identity, s) screen.inputs[k] = map(identity, s)
end end
screen screen
elseif length(plot_screens) == 1
plot_screens[1]
else else
plot_screens = get_plot_screen(GLVisualize.get_screens(), name) # okay this is silly! Lets see if we can. There is an ID we could use
if isempty(plot_screens) # will not be fine for more than 255 screens though -.-.
parent = GLVisualize.current_screen() error("multiple Plot screens. Please don't use any screen with the name Plots.jl")
empty!(parent)
inputs = Dict{Symbol, Any}()
for (k, s) in parent.inputs # copy signals, so we can clean them up better
inputs[k] = map(identity, s)
end
screen = GLWindow.Screen(parent,
name = name,
area = map(identity, parent.area)
)
for (k, s) in screen.inputs # copy signals, so we can clean them up better
screen.inputs[k] = map(identity, s)
end
screen
elseif length(plot_screens) == 1
plot_screens[1]
else
# okay this is silly! Lets see if we can. There is an ID we could use
# will not be fine for more than 255 screens though -.-.
error("multiple Plot screens. Please don't use any screen with the name Plots.jl")
end
end end
# Since we own this window, we can do deep cleansing # Since we own this window, we can do deep cleansing
empty_screen!(screen) empty_screen!(screen)
@ -226,6 +214,7 @@ const _gl_marker_map = KW(
:hline => '━', :hline => '━',
:+ => '+', :+ => '+',
:x => 'x', :x => 'x',
:circle => '●'
) )
function gl_marker(shape) function gl_marker(shape)
@ -241,6 +230,12 @@ function gl_marker(shape::Shape)
GeometryTypes.GLNormalMesh(points) GeometryTypes.GLNormalMesh(points)
end end
# create a marker/shape type # create a marker/shape type
function gl_marker(shape::Vector{Symbol})
String(map(shape) do sym
get(_gl_marker_map, sym, '●')
end)
end
function gl_marker(shape::Symbol) function gl_marker(shape::Symbol)
if shape == :rect if shape == :rect
GeometryTypes.HyperRectangle(Vec2f0(0), Vec2f0(1)) GeometryTypes.HyperRectangle(Vec2f0(0), Vec2f0(1))
@ -265,6 +260,12 @@ function extract_limits(sp, d, kw_args)
nothing nothing
end end
to_vec{T <: FixedVector}(::Type{T}, vec::T) = vec
to_vec{T <: FixedVector{2}}(::Type{T}, vec::FixedVector{3}) = T(vec[1], vec[2])
to_vec{T <: FixedVector{3}}(::Type{T}, vec::FixedVector{2}) = T(vec[1], vec[2], 0)
to_vec{T <: FixedVector}(::Type{T}, vecs::Vector) = map(x-> to_vec(T, x), vecs)
function extract_marker(d, kw_args) function extract_marker(d, kw_args)
dim = Plots.is3d(d) ? 3 : 2 dim = Plots.is3d(d) ? 3 : 2
scaling = dim == 3 ? 0.003 : 2 scaling = dim == 3 ? 0.003 : 2
@ -278,13 +279,11 @@ function extract_marker(d, kw_args)
dim = isa(kw_args[:primitive], GLVisualize.Sprites) ? 2 : 3 dim = isa(kw_args[:primitive], GLVisualize.Sprites) ? 2 : 3
if haskey(d, :markersize) if haskey(d, :markersize)
msize = d[:markersize] msize = d[:markersize]
if isa(msize, AbstractArray) kw_args[:scale] = to_vec(GeometryTypes.Vec{dim, Float32}, msize .* scaling)
kw_args[:scale] = map(x->GeometryTypes.Vec{dim, Float32}(x*scaling), msize) end
else if haskey(d, :offset)
kw_args[:scale] = GeometryTypes.Vec{dim, Float32}(msize*scaling) kw_args[:offset] = d[:offset]
end
end end
# get the color # get the color
key = :markercolor key = :markercolor
haskey(d, key) || return haskey(d, key) || return
@ -315,6 +314,7 @@ end
function _extract_surface(d::AbstractArray) function _extract_surface(d::AbstractArray)
d d
end end
# TODO when to transpose?? # TODO when to transpose??
function extract_surface(d) function extract_surface(d)
map(_extract_surface, (d[:x], d[:y], d[:z])) map(_extract_surface, (d[:x], d[:y], d[:z]))
@ -327,7 +327,7 @@ function extract_points(d)
array = (d[:x], d[:y], d[:z])[1:dim] array = (d[:x], d[:y], d[:z])[1:dim]
topoints(Point{dim, Float32}, array) topoints(Point{dim, Float32}, array)
end end
function make_gradient{C<:Colorant}(grad::Vector{C}) function make_gradient{C <: Colorant}(grad::Vector{C})
grad grad
end end
function make_gradient(grad::ColorGradient) function make_gradient(grad::ColorGradient)
@ -373,9 +373,10 @@ end
function extract_stroke(d, kw_args) function extract_stroke(d, kw_args)
extract_c(d, kw_args, :line) extract_c(d, kw_args, :line)
if haskey(d, :linewidth) if haskey(d, :linewidth)
kw_args[:thickness] = d[:linewidth]*3 kw_args[:thickness] = d[:linewidth] * 3
end end
end end
function extract_color(d, sym) function extract_color(d, sym)
d[Symbol("$(sym)color")] d[Symbol("$(sym)color")]
end end
@ -413,6 +414,7 @@ end
dist(a, b) = abs(a-b) dist(a, b) = abs(a-b)
mindist(x, a, b) = min(dist(a, x), dist(b, x)) mindist(x, a, b) = min(dist(a, x), dist(b, x))
function gappy(x, ps) function gappy(x, ps)
n = length(ps) n = length(ps)
x <= first(ps) && return first(ps) - x x <= first(ps) && return first(ps) - x
@ -426,7 +428,7 @@ function gappy(x, ps)
return last(ps) - x return last(ps) - x
end end
function ticks(points, resolution) function ticks(points, resolution)
Float16[gappy(x, points) for x=linspace(first(points),last(points), resolution)] Float16[gappy(x, points) for x = linspace(first(points),last(points), resolution)]
end end
@ -461,9 +463,11 @@ function extract_linestyle(d, kw_args)
extract_c(d, kw_args, :line) extract_c(d, kw_args, :line)
nothing nothing
end end
function hover(to_hover::Vector, to_display, window) function hover(to_hover::Vector, to_display, window)
hover(to_hover[], to_display, window) hover(to_hover[], to_display, window)
end end
function get_cam(x) function get_cam(x)
if isa(x, GLAbstraction.Context) if isa(x, GLAbstraction.Context)
return get_cam(x.children) return get_cam(x.children)
@ -474,6 +478,7 @@ function get_cam(x)
end end
end end
function hover(to_hover, to_display, window) function hover(to_hover, to_display, window)
if isa(to_hover, GLAbstraction.Context) if isa(to_hover, GLAbstraction.Context)
return hover(to_hover.children, to_display, window) return hover(to_hover.children, to_display, window)
@ -523,7 +528,7 @@ function hover(to_hover, to_display, window)
end end
function extract_extrema(d, kw_args) function extract_extrema(d, kw_args)
xmin,xmax = extrema(d[:x]); ymin,ymax = extrema(d[:y]) xmin, xmax = extrema(d[:x]); ymin, ymax = extrema(d[:y])
kw_args[:primitive] = GeometryTypes.SimpleRectangle{Float32}(xmin, ymin, xmax-xmin, ymax-ymin) kw_args[:primitive] = GeometryTypes.SimpleRectangle{Float32}(xmin, ymin, xmax-xmin, ymax-ymin)
nothing nothing
end end
@ -554,6 +559,7 @@ function extract_colornorm(d, kw_args)
kw_args[:intensity] = map(Float32, collect(z)) kw_args[:intensity] = map(Float32, collect(z))
end end
end end
function extract_gradient(d, kw_args, sym) function extract_gradient(d, kw_args, sym)
key = Symbol("$(sym)color") key = Symbol("$(sym)color")
haskey(d, key) || return haskey(d, key) || return
@ -563,6 +569,7 @@ function extract_gradient(d, kw_args, sym)
kw_args[:color_map] = c kw_args[:color_map] = c
return return
end end
function extract_c(d, kw_args, sym) function extract_c(d, kw_args, sym)
key = Symbol("$(sym)color") key = Symbol("$(sym)color")
haskey(d, key) || return haskey(d, key) || return
@ -624,20 +631,24 @@ function align_offset(startpos, lastpos, atlas, rscale, font, align)
error("Align $align not known") error("Align $align not known")
end end
end end
function align_offset(startpos, lastpos, atlas, rscale, font, align::Vec) function align_offset(startpos, lastpos, atlas, rscale, font, align::Vec)
xscale, yscale = GLVisualize.glyph_scale!('X', rscale) xscale, yscale = GLVisualize.glyph_scale!('X', rscale)
xmove = (lastpos-startpos)[1] + xscale xmove = (lastpos-startpos)[1] + xscale
return -Vec2f0(xmove, yscale) .* align return -Vec2f0(xmove, yscale) .* align
end end
function alignment2num(x::Symbol) function alignment2num(x::Symbol)
(x in (:hcenter, :vcenter)) && return 0.5 (x in (:hcenter, :vcenter)) && return 0.5
(x in (:left, :bottom)) && return 0.0 (x in (:left, :bottom)) && return 0.0
(x in (:right, :top)) && return 1.0 (x in (:right, :top)) && return 1.0
0.0 # 0 default, or better to error? 0.0 # 0 default, or better to error?
end end
function alignment2num(font::Plots.Font) function alignment2num(font::Plots.Font)
Vec2f0(map(alignment2num, (font.halign, font.valign))) Vec2f0(map(alignment2num, (font.halign, font.valign)))
end end
pointsize(font) = font.pointsize * 2 pointsize(font) = font.pointsize * 2
function draw_ticks( function draw_ticks(
@ -683,16 +694,18 @@ function draw_ticks(
end end
text, positions, offsets text, positions, offsets
end end
function text(position, text, kw_args) function text(position, text, kw_args)
text_align = alignment2num(text.font) text_align = alignment2num(text.font)
startpos = Vec2f0(position) startpos = Vec2f0(position)
atlas = GLVisualize.get_texture_atlas() atlas = GLVisualize.get_texture_atlas()
font = GLVisualize.defaultfont() font = GLVisualize.defaultfont()
rscale = kw_args[:relative_scale] rscale = kw_args[:relative_scale]
m = Reactive.value(kw_args[:model])
position = GLVisualize.calc_position(text.str, startpos, rscale, font, atlas) position = GLVisualize.calc_position(text.str, startpos, rscale, font, atlas)
offset = GLVisualize.calc_offset(text.str, rscale, font, atlas) offset = GLVisualize.calc_offset(text.str, rscale, font, atlas)
alignoff = align_offset(startpos, last(position), atlas, rscale, font, text_align) alignoff = align_offset(startpos, last(position), atlas, rscale, font, text_align)
map!(position) do pos map!(position) do pos
pos .+ alignoff pos .+ alignoff
end end
@ -969,6 +982,39 @@ end
# ---------------------------------------------------------------- # ----------------------------------------------------------------
function scale_for_annotations!(series::Series, scaletype::Symbol = :pixels)
anns = series[:series_annotations]
if anns != nothing && !isnull(anns.baseshape)
# we use baseshape to overwrite the markershape attribute
# with a list of custom shapes for each
msw, msh = anns.scalefactor
offsets = Array(Vec2f0, length(anns.strs))
series[:markersize] = map(1:length(anns.strs)) do i
str = cycle(anns.strs, i)
# get the width and height of the string (in mm)
sw, sh = text_size(str, anns.font.pointsize)
# how much to scale the base shape?
# note: it's a rough assumption that the shape fills the unit box [-1,-1,1,1],
# so we scale the length-2 shape by 1/2 the total length
xscale = 0.5to_pixels(sw) * 1.8
yscale = 0.5to_pixels(sh) * 1.8
# we save the size of the larger direction to the markersize list,
# and then re-scale a copy of baseshape to match the w/h ratio
s = Vec2f0(xscale, yscale)
offsets[i] = -s
s
end
series[:offset] = offsets
end
return
end
function _display(plt::Plot{GLVisualizeBackend}, visible = true) function _display(plt::Plot{GLVisualizeBackend}, visible = true)
screen = create_window(plt, visible) screen = create_window(plt, visible)
sw, sh = plt[:size] sw, sh = plt[:size]
@ -1002,14 +1048,7 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true)
screen.area, sub_area, screen.area, sub_area,
Signal(rel_plotarea), Signal(sp) Signal(rel_plotarea), Signal(sp)
) )
for ann in sp[:annotations]
x, y, plot_text = ann
txt_args = Dict{Symbol, Any}(:model => eye(GeometryTypes.Mat4f0))
x, y, _1, _1 = Reactive.value(model_m) * Vec{4,Float32}(x, y, 0, 1)
extract_font(plot_text.font, txt_args)
t = text(Point2f0(x, y), plot_text, txt_args)
GLVisualize._view(t, sp_screen, camera=:perspective)
end
# loop over the series and add them to the subplot # loop over the series and add them to the subplot
if !_3d if !_3d
axis = gl_draw_axes_2d(sp, model_m, Reactive.value(sub_area)) axis = gl_draw_axes_2d(sp, model_m, Reactive.value(sub_area))
@ -1035,7 +1074,7 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true)
if !_3d # 3D is treated differently, since we need boundingboxes for camera if !_3d # 3D is treated differently, since we need boundingboxes for camera
kw_args[:boundingbox] = nothing # don't calculate bb, we dont need it kw_args[:boundingbox] = nothing # don't calculate bb, we dont need it
end end
scale_for_annotations!(series)
if st in (:surface, :wireframe) if st in (:surface, :wireframe)
x, y, z = extract_surface(d) x, y, z = extract_surface(d)
extract_gradient(d, kw_args, :fill) extract_gradient(d, kw_args, :fill)
@ -1064,7 +1103,7 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true)
if d[:fillrange] != nothing if d[:fillrange] != nothing
kw = copy(kw_args) kw = copy(kw_args)
fr = d[:fillrange] fr = d[:fillrange]
ps = if all(x->x>=0, diff(d[:x])) # if is monotonic ps = if all(x-> x >= 0, diff(d[:x])) # if is monotonic
vcat(points, Point2f0[(points[i][1], cycle(fr, i)) for i=length(points):-1:1]) vcat(points, Point2f0[(points[i][1], cycle(fr, i)) for i=length(points):-1:1])
else else
points points
@ -1116,6 +1155,7 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true)
else else
error("failed to display plot type $st") error("failed to display plot type $st")
end end
isa(vis, Array) && isempty(vis) && continue # nothing to see here isa(vis, Array) && isempty(vis) && continue # nothing to see here
GLVisualize._view(vis, sp_screen, camera=:perspective) GLVisualize._view(vis, sp_screen, camera=:perspective)
@ -1126,6 +1166,16 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true)
del_signal = Main.GLPlot.register_plot!(vis, sp_screen, create_gizmo=false) del_signal = Main.GLPlot.register_plot!(vis, sp_screen, create_gizmo=false)
append!(_glplot_deletes, del_signal) append!(_glplot_deletes, del_signal)
end end
anns = series[:series_annotations]
for (x, y, str) in EachAnn(anns, d[:x], d[:y])
txt_args = Dict{Symbol, Any}(:model => eye(GLAbstraction.Mat4f0))
x, y = Reactive.value(model_m) * Vec{4, Float32}(x, y, 0, 1)
extract_font(anns.font, txt_args)
pt = isa(str, String) ? PlotText(str, anns.font) : str
t = text(Point2f0(x, y), pt, txt_args)
GLVisualize._view(t, sp_screen, camera = :perspective)
end
end end
generate_legend(sp, sp_screen, model_m) generate_legend(sp, sp_screen, model_m)
if _3d if _3d
@ -1208,14 +1258,7 @@ function gl_shape(d, kw_args)
result result
end end
tovec2(x::FixedSizeArrays.Vec{2, Float32}) = x
tovec2(x::AbstractVector) = map(tovec2, x)
tovec2(x::FixedSizeArrays.Vec) = Vec2f0(x[1], x[2])
tovec3(x) = x
tovec3(x::FixedSizeArrays.Vec{3}) = Vec3f0(x)
tovec3(x::AbstractVector) = map(tovec3, x)
tovec3(x::FixedSizeArrays.Vec{2}) = Vec3f0(x[1], x[2], 1)
function gl_scatter(points, kw_args) function gl_scatter(points, kw_args)
prim = get(kw_args, :primitive, GeometryTypes.Circle) prim = get(kw_args, :primitive, GeometryTypes.Circle)
@ -1225,21 +1268,31 @@ function gl_scatter(points, kw_args)
kw_args[:scale] = GLAbstraction.const_lift(kw_args[:model], kw_args[:scale], p) do m, sc, p kw_args[:scale] = GLAbstraction.const_lift(kw_args[:model], kw_args[:scale], p) do m, sc, p
s = Vec3f0(m[1,1], m[2,2], m[3,3]) s = Vec3f0(m[1,1], m[2,2], m[3,3])
ps = Vec3f0(p[1,1], p[2,2], p[3,3]) ps = Vec3f0(p[1,1], p[2,2], p[3,3])
r = sc./(s.*ps) r = sc ./ (s .* ps)
r r
end end
end end
else # 2D prim else # 2D prim
kw_args[:scale] = tovec2(kw_args[:scale]) kw_args[:scale] = to_vec(Vec2f0, kw_args[:scale])
end end
if haskey(kw_args, :stroke_width) if haskey(kw_args, :stroke_width)
s = Reactive.value(kw_args[:scale]) s = Reactive.value(kw_args[:scale])
sw = kw_args[:stroke_width] sw = kw_args[:stroke_width]
if sw*5 > cycle(Reactive.value(s), 1)[1] # restrict marker stroke to 1/10th of scale (and handle arrays of scales) if sw*5 > cycle(Reactive.value(s), 1)[1] # restrict marker stroke to 1/10th of scale (and handle arrays of scales)
kw_args[:stroke_width] = s[1]/5f0 kw_args[:stroke_width] = s[1] / 5f0
end end
end end
kw_args[:scale_primitive] = false kw_args[:scale_primitive] = false
if isa(prim, String)
kw_args[:position] = points
if !isa(kw_args[:scale], Vector) # if not vector, we can assume it's relative scale
kw_args[:relative_scale] = kw_args[:scale]
delete!(kw_args, :scale)
end
return visualize(prim, Style(:default), kw_args)
end
visualize((prim, points), Style(:default), kw_args) visualize((prim, points), Style(:default), kw_args)
end end
@ -1259,6 +1312,9 @@ function gl_poly(points, kw_args)
result result
end end
function gl_surface(x,y,z, kw_args) function gl_surface(x,y,z, kw_args)
if isa(x, Range) && isa(y, Range) if isa(x, Range) && isa(y, Range)
main = z main = z
@ -1267,8 +1323,8 @@ function gl_surface(x,y,z, kw_args)
if isa(x, AbstractMatrix) && isa(y, AbstractMatrix) if isa(x, AbstractMatrix) && isa(y, AbstractMatrix)
main = map(s->map(Float32, s), (x, y, z)) main = map(s->map(Float32, s), (x, y, z))
elseif isa(x, AbstractVector) || isa(y, AbstractVector) elseif isa(x, AbstractVector) || isa(y, AbstractVector)
x = Float32[x[i] for i=1:size(z,1), j=1:size(z,2)] x = Float32[x[i] for i = 1:size(z,1), j = 1:size(z,2)]
y = Float32[y[j] for i=1:size(z,1), j=1:size(z,2)] y = Float32[y[j] for i = 1:size(z,1), j = 1:size(z,2)]
main = (x, y, map(Float32, z)) main = (x, y, map(Float32, z))
else else
error("surface: combination of types not supported: $(typeof(x)) $(typeof(y)) $(typeof(z))") error("surface: combination of types not supported: $(typeof(x)) $(typeof(y)) $(typeof(z))")
@ -1278,8 +1334,10 @@ function gl_surface(x,y,z, kw_args)
faces = Cuint[] faces = Cuint[]
idx = (i,j) -> sub2ind(size(z), i, j) - 1 idx = (i,j) -> sub2ind(size(z), i, j) - 1
for i=1:size(z,1), j=1:size(z,2) for i=1:size(z,1), j=1:size(z,2)
i < size(z,1) && push!(faces, idx(i, j), idx(i+1, j)) i < size(z,1) && push!(faces, idx(i, j), idx(i+1, j))
j < size(z,2) && push!(faces, idx(i, j), idx(i, j+1)) j < size(z,2) && push!(faces, idx(i, j), idx(i, j+1))
end end
color = get(kw_args, :stroke_color, RGBA{Float32}(0,0,0,1)) color = get(kw_args, :stroke_color, RGBA{Float32}(0,0,0,1))
kw_args[:color] = color kw_args[:color] = color
@ -1295,15 +1353,18 @@ function gl_surface(x,y,z, kw_args)
end end
function gl_contour(x,y,z, kw_args) function gl_contour(x, y, z, kw_args)
if kw_args[:fillrange] != nothing if kw_args[:fillrange] != nothing
delete!(kw_args, :intensity) delete!(kw_args, :intensity)
I = GLVisualize.Intensity{1, Float32} I = GLVisualize.Intensity{1, Float32}
main = I[z[j,i] for i=1:size(z, 2), j=1:size(z, 1)] main = I[z[j,i] for i=1:size(z, 2), j=1:size(z, 1)]
return visualize(main, Style(:default), kw_args) return visualize(main, Style(:default), kw_args)
else else
h = kw_args[:levels] h = kw_args[:levels]
levels = Contour.contours(x, y, z, h) T = eltype(z)
levels = Contour.contours(map(T, x), map(T, y), z, h)
result = Point2f0[] result = Point2f0[]
zmin, zmax = get(kw_args, :limits, Vec2f0(extrema(z))) zmin, zmax = get(kw_args, :limits, Vec2f0(extrema(z)))
cmap = get(kw_args, :color_map, get(kw_args, :color, RGBA{Float32}(0,0,0,1))) cmap = get(kw_args, :color_map, get(kw_args, :color, RGBA{Float32}(0,0,0,1)))
@ -1313,7 +1374,7 @@ function gl_contour(x,y,z, kw_args)
append!(result, elem.vertices) append!(result, elem.vertices)
push!(result, Point2f0(NaN32)) push!(result, Point2f0(NaN32))
col = GLVisualize.color_lookup(cmap, c.level, zmin, zmax) col = GLVisualize.color_lookup(cmap, c.level, zmin, zmax)
append!(colors, fill(col, length(elem.vertices)+1)) append!(colors, fill(col, length(elem.vertices) + 1))
end end
end end
kw_args[:color] = colors kw_args[:color] = colors