Skip to content
This repository has been archived by the owner on Apr 28, 2021. It is now read-only.

more interactivity, buttons, textfields and better examples #155

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
166 changes: 166 additions & 0 deletions examples/gui/overview.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
using GLVisualize, Colors, GeometryTypes, Reactive, GLAbstraction, GLFW, GLWindow
import GLVisualize: mm

if !isdefined(:runtests)
window = glscreen()
end

text_scale = 6mm
function text_with_background(txt;
background_color = RGBA(1f0, 1f0, 1f0, 1f0), gap = 1mm,
size = nothing,
kw_args...
)
robj = visualize(txt; kw_args...).children[]
rect = if size == nothing
map(boundingbox(robj)) do bb
mini, w = minimum(bb), widths(bb)
[Point2f0(mini[1] - gap, mini[2] - gap)], Vec2f0(w[1] + 3gap, w[2] + 2gap)
end
else
Signal(([Point2f0(-gap, -gap)], Vec2f0(size[1] + 3gap, size[2] + 2gap)))
end
bg = visualize(
(RECTANGLE, map(first, rect)),
scale = map(last, rect),
offset = Vec2f0(0),
color = background_color,
glow_color = RGBA(0f0, 0f0, 0f0, 0.1f0),
glow_width = 3f0
)
Context(bg, robj)
end
slider_val = Signal(1)
slider_str = map(string, slider_val)
color = Signal(RGBA(1f0, 1f0, 1f0, 1f0))
x = text_with_background(
slider_str, relative_scale = text_scale,
background_color = color,
size = (8*text_scale, text_scale)
)
GLAbstraction.translate!(x, Vec3f0(4mm, 4mm, 0))
_view(x, window, camera = :fixed_pixel)

ids = (map(x-> x.id, extract_renderable(x))..., )
@materialize mouse_buttons_pressed, buttons_pressed, unicode_input = window.inputs
mouseclick = const_lift(GLAbstraction.singlepressed, mouse_buttons_pressed, GLFW.MOUSE_BUTTON_LEFT)
const enterkey = Set([GLFW.KEY_ENTER])
text_edit = droprepeats(foldp(false, doubleclick(mouseclick, 0.2), buttons_pressed) do v0, clicked, kb
kb == enterkey && return false
clicked && is_same_id(value(mouse2id(window)), ids) && return !v0
v0
end)
backspace = Set([GLFW.KEY_BACKSPACE])
arrowleft = Set([GLFW.KEY_LEFT])
arrowright = Set([GLFW.KEY_RIGHT])
ctrl_keyes = (backspace, arrowleft, arrowright)


function dont_repeat_value{T}(val, input::Signal{T})
n = Signal(value(input))
connect_dont_repeat_value(val, n, input)
n
end

function connect_dont_repeat_value(val, output, input)
let prev_value = value(input)
Reactive.add_action!(input, output) do output, timestep
nval = value(input)
println(nval)
if prev_value != val
Reactive.send_value!(outputm, nval, timestep)
end
prev_value = nval
end
end
end

empty_or_ctrl = map(buttons_pressed) do buttons
if buttons in ctrl_keyes
buttons
else
Set{Int}()
end
end
# only let controls through
ctrl_buttons = empty_or_ctrl#dont_repeat_value(Set{Int}(), empty_or_ctrl)
println(x.children[2].id)
txt_edit_s = foldp((false, [' '], 1), unicode_input, text_edit, ctrl_buttons) do v0, chars, edit, ctrl
was_edit, str, cursor = v0
move(num) = (cursor = clamp(cursor + num, 0, length(str)))
if edit
if !was_edit && isempty(ctrl)# reset
id, idx = value(mouse2id(window))
empty!(str)
append!(str, string(value(slider_val)))
@show id idx
cursor = if id == x.children[2].id && checkbounds(str, idx)
idx
else
1
end

end
if !isempty(ctrl)
if ctrl == backspace
if !isempty(str) && checkbounds(Bool, str, cursor)
splice!(str, cursor)
move(-1)
end
elseif ctrl == arrowleft
move(-1)
elseif ctrl == arrowright
move(1)
end
else
for char in chars
if isnumber(char)
push!(str, char)
move(1)
end
end
end
strstr = join(str)
if isempty(strstr) # GLVisualize needs to get patched to work with emtpy strings........
push!(slider_str, " ")
else
push!(slider_str, strstr)
end
push!(color, RGBA(0.8f0, 0.8f0, 0.8f0, 0.4f0))
else
strstr = replace(join(str), ' ', "")
if !isempty(str)
num = parse(eltype(value(slider_val)), strstr)
push!(slider_val, num)
end
str = [' ']
push!(color, RGBA(1f0, 1f0, 1f0, 1f0))
end
edit, str, cursor
end

function calc_position(glyphs, idx; scale = text_scale, start_pos = Point2f0(0))
if checkbounds(Bool, glyphs, idx)
fonts, atlas = GLVisualize.defaultfont(), GLVisualize.get_texture_atlas()
GLVisualize.calc_position(glyphs[1:idx], start_pos, scale, fonts, atlas)[end:end]
elseif idx == 0
[Point2f0(-1, 1mm)]
else
[start_pos]
end
end

cursor_pos = map(txt_edit_s) do edit_tup
edit, str, cursor = edit_tup
calc_position(str, cursor, scale = text_scale, start_pos = Point2f0(text_scale/2 + 1, 1mm))
end
_view(visualize(
"|",
position = cursor_pos, visible = text_edit,
model = transformation(x.children[2]),
relative_scale = 5mm,
), camera = :fixed_pixel)

if !isdefined(:runtests)
renderloop(window)
end
48 changes: 27 additions & 21 deletions examples/introduction/video_recording.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,43 @@ end
description = """
Example of how to record a video from GLVisualize
"""

kitty = visualize(loadasset("cat.obj"))
_view(kitty, window)

# save video to report dir, or in some tmp dir we'll delete later
path = if haskey(ENV, "CI_REPORT_DIR")
name = if haskey(ENV, "CI_REPORT_DIR")
ENV["CI_REPORT_DIR"] * "/videorecord.mkv"
else
homedir()
end
name = ""
while true # for some reason, folder retured by mktempdir isn't usable -.-
name = path * "/$(randstring()).mkv"
isfile(name) || break
path = homedir()
while true # for some reason, folder retured by mktempdir isn't usable -.-
name = path * "/$(randstring()).mkv"
isfile(name) || break
end
name
end

# create a stream to which we can add frames
io, buffer = GLVisualize.create_video_stream(name, window)
for i=1:10 # record 10 frames
# do something
GLAbstraction.set_arg!(kitty, :color, RGBA{Float32}(1, 0, 1-(i/10), i/10))
#render current frame
# if you call @async renderloop(window) you can replace this part with yield
GLWindow.render_frame(window)
GLWindow.swapbuffers(window)
GLWindow.poll_reactive()
# only try recording when ffmpeg is installed
if success(`ffmpeg -h`)
# create a stream to which we can add frames
io, buffer = GLVisualize.create_video_stream(name, window)
for i=1:10 # record 10 frames
# do something
GLAbstraction.set_arg!(kitty, :color, RGBA{Float32}(1, 0, 1-(i/10), i/10))
#render current frame
# if you call @async renderloop(window) you can replace this part with yield
GLWindow.render_frame(window)
GLWindow.swapbuffers(window)
GLWindow.poll_reactive()

# add the frame from the current window
GLVisualize.add_frame!(io, window, buffer)
# add the frame from the current window
GLVisualize.add_frame!(io, window, buffer)
end
# closing the stream will trigger writing the video!
close(io)
else
info("skipped ffmpged video recording, since ffmpeg is not installed!")
end
# closing the stream will trigger writing the video!
close(io)

if !isdefined(:runtests)
renderloop(window)
Expand Down
4 changes: 3 additions & 1 deletion examples/text/annotations.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using GLVisualize, GeometryTypes, Colors, GLAbstraction, Reactive, Images, ModernGL
import GLVisualize: mm, calc_position, glyph_bearing!, glyph_uv_width!, glyph_scale!

import GLVisualize: mm, annotated_text

if !isdefined(:runtests)
Expand Down Expand Up @@ -32,13 +34,13 @@ fbuffer = foldp(rand(RGB{N0f8}, 500, 500), timesignal) do v0, t
end
end
frame_viz = visualize(fbuffer)

# instead of circle you can also use unicode charactes (e.g. '+')
position_viz = visualize(
(Circle{Float32}(0, 1.5mm), positions),
color = RGBA(1f0, 0f0, 0f0, 0.6f0)
)
gpu_position = position_viz.children[][:position]

bg_viz = visualize(
(ROUNDED_RECTANGLE, gpu_position),
scale = widths,
Expand Down
20 changes: 16 additions & 4 deletions src/gui/buttons.jl
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
function button(a, screen; kw_args...)
function button(
a, screen;
background_color = RGBA(1f0, 1f0, 1f0, 0.0f0),
gap = 0.5mm,
kw_args...
)
robj = visualize(a; kw_args...).children[]
const m2id = mouse2id(screen)
m2id = mouse2id(screen)
result = [robj]
bb = value(boundingbox(robj))
mini, w = minimum(bb), widths(bb)
rect = SimpleRectangle(mini[1] - 2gap, mini[2] - 2gap, w[1] + 2gap, w[2] + 2gap)
bg = visualize(rect, color = background_color, model = robj[:model]).children[]
ids = (robj.id, bg.id)
is_pressed = droprepeats(map(screen.inputs[:key_pressed]) do isclicked
isclicked && value(m2id).id == robj.id
id = value(m2id)
isclicked && is_same_id(id, ids)
end)

robj[:model] = const_lift(is_pressed, robj[:model]) do ip, old
ip && return old
scalematrix(Vec3f0(0.95))*old
end
robj, is_pressed
Context(bg, robj), is_pressed
end


Expand Down
31 changes: 16 additions & 15 deletions src/gui/numbers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -93,35 +93,36 @@ function widget{T <: FixedVector}(
last_x += w[1] + gap
vizz
end

Context(le_tuple...), map(T, le_sigs...)
end
function widget{T <: Real}(
slider_value::Signal{T}, window;
text_scale = 4mm,
numberwidth = 5, range = range_default(T), kw_args...
)
@materialize mouse_buttons_pressed, mouseposition = window.inputs
startvalue = value(slider_value)
slider_value_str = map(printforslider, slider_value)
vizz = visualize(
slider_value_str; relative_scale=text_scale,
numberwidth = 5, range = range_default(T),
color = RGBA{Float32}(0.1, 0.1, 0.1),
glow_color = RGBA{Float32}(0.97, 0.97, 0.97),
stroke_color = RGBA{Float32}(0.97, 0.97, 0.97),
scale_primitive = true,
kw_args...
)
bb = value(boundingbox(vizz))
@materialize mouse_buttons_pressed, mouseposition = window.inputs
startvalue = value(slider_value)
slider_value_str = map(printforslider, slider_value)
slider_robj = visualize(
slider_value_str;
relative_scale = text_scale,
color = color, glow_color = glow_color, stroke_color = stroke_color,
scale_primitive = true,
kw_args...
).children[]
bb = value(boundingbox(slider_robj))
mini, maxi = minimum(bb)-5f0, widths(bb)+10f0
bb_rect = SimpleRectangle{Float32}(mini[1],mini[2], maxi[1], maxi[2])
bb_vizz = visualize(
bb_rect = SimpleRectangle{Float32}(mini[1], mini[2], maxi[1], maxi[2])
bb_vizz = visualize(
bb_rect;
color=RGBA{Float32}(0.97, 0.97, 0.97, 0.4),
offset=Vec2f0(0), kw_args...
color = RGBA{Float32}(0.97, 0.97, 0.97, 0.4),
offset = Vec2f0(0), kw_args...
).children[]

slider_robj = vizz.children[]
# current tuple of renderobject id and index into the gpu array
m2id = GLWindow.mouse2id(window)
ids = (slider_robj.id, bb_vizz.id)
Expand Down
8 changes: 5 additions & 3 deletions src/texture_atlas.jl
Original file line number Diff line number Diff line change
Expand Up @@ -190,21 +190,23 @@ function sdistancefield(img, downsample = 8, pad = 8*downsample)

in_or_out = Array(Bool, w, h)
@inbounds for i=1:w, j=1:h
x, y = i-pad, j-pad
x, y = i - pad, j - pad
in_or_out[i,j] = checkbounds(Bool, img, x, y) && img[x,y] > 0.5*255
end
yres, xres = div(w, downsample), div(h, downsample)
sd = sdf(in_or_out, xres, yres)
map(Float16, sd)
end

const _downsample_rate = parse(Int, get(ENV, "GLVISUALIZE_DOWNSAMPLE_RATE", "5"))

function GLAbstraction.render(atlas::TextureAtlas, glyph::Char, font)
#select_font_face(cc, font)
if glyph == '\n' # don't render newline
glyph = ' '
end
downsample = 5
pad = 8
downsample = _downsample_rate
pad = 20
bitmap, extent = renderface(font, glyph, (50*downsample, 50*downsample))
sd = sdistancefield(bitmap, downsample, downsample*pad)
extent = extent ./ Vec2f0(downsample)
Expand Down
2 changes: 1 addition & 1 deletion src/visualize/particles.jl
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ function _default{S <: AbstractString}(main::TOrSignal{S}, s::Style, data::Dict)
uv_offset_width = const_lift(main) do str
Vec4f0[glyph_uv_width!(atlas, c, font) for c = str]
end
scale = const_lift(main, relative_scale) do str, s
scale = const_lift(main, relative_scale) do str, s
Vec2f0[glyph_scale!(atlas, c, font, s) for c = str]
end
end
Expand Down
4 changes: 2 additions & 2 deletions src/visualize/text.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ immutable SpriteElem{N,T}
offset::Vec{2,T}
uv_offset_width::Vec{4,T}
position::Point{N, T}
color::RGBA{Float32}
color::RGBA{N0f8}
scale::Vec{2,T}
end
type Text
Expand Down Expand Up @@ -202,7 +202,7 @@ texttext<current pos>texttext\n
texttext<finds this pos>text\n
=#
function down_after_newline(text, current_position)
i = current_position
i = current_position
pnl = previous_newline(text, i)
nnl = next_newline(text, i)
nl_distance = i-pnl # distance from previous newline
Expand Down
Loading