From af681b7cbd6119e6c0d2f619b57f7a31c6cf9a31 Mon Sep 17 00:00:00 2001 From: SimonDanisch Date: Sun, 12 Mar 2017 11:02:13 +0100 Subject: [PATCH 01/12] working on better ui --- src/gui/buttons.jl | 20 +++++++++++--- src/gui/numbers.jl | 31 +++++++++++----------- src/gui/overview.jl | 64 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 19 deletions(-) create mode 100644 src/gui/overview.jl diff --git a/src/gui/buttons.jl b/src/gui/buttons.jl index fb80b18..dbc3a50 100644 --- a/src/gui/buttons.jl +++ b/src/gui/buttons.jl @@ -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 diff --git a/src/gui/numbers.jl b/src/gui/numbers.jl index 59adb60..cd19c8e 100644 --- a/src/gui/numbers.jl +++ b/src/gui/numbers.jl @@ -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) diff --git a/src/gui/overview.jl b/src/gui/overview.jl new file mode 100644 index 0000000..bbe6cf5 --- /dev/null +++ b/src/gui/overview.jl @@ -0,0 +1,64 @@ +using GLVisualize, Colors, GeometryTypes, Reactive, GLAbstraction, GLFW, GLWindow +import GLVisualize: mm + +w = glscreen(); @async GLWindow.waiting_renderloop(w) + +function text_with_background(txt; + background_color = RGBA(1f0, 1f0, 1f0, 1f0), gap = 1mm, + kw_args... + ) + robj = visualize(txt; kw_args...).children[] + rect = 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 + 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(GLVisualize.printforslider, slider_val) +color = Signal(RGBA(1f0, 1f0, 1f0, 1f0)) +x = text_with_background(slider_str, relative_scale = 6mm, background_color = color) +GLAbstraction.translate!(x, Vec3f0(4mm, 4mm, 0)) +_view(x, w, camera = :fixed_pixel) + +ids = (map(x-> x.id, extract_renderable(x))..., ) +@materialize mouse_buttons_pressed, buttons_pressed, unicode_input = w.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(w)), ids) && return !v0 + v0 +end) + +preserve(foldp((false, " "), unicode_input, text_edit) do v0, chars, edit + was_edit, str = v0 + if edit + if !was_edit + str = string(value(slider_val)) + end + for char in chars + if isnumber(char) + str *= string(char) + end + end + push!(slider_str, str) + push!(color, RGBA(0.8f0, 0.8f0, 0.8f0, 0.4f0)) + else + num = parse(eltype(value(slider_val)), str) + push!(slider_val, num) + str = " " + push!(color, RGBA(1f0, 1f0, 1f0, 1f0)) + end + edit, str +end) + +map(println, unicode_input) From b0a55eba9770889027185a9403a401e55a40a098 Mon Sep 17 00:00:00 2001 From: SimonDanisch Date: Mon, 13 Mar 2017 13:46:08 +0100 Subject: [PATCH 02/12] gui stuff --- examples/gui/overview.jl | 163 +++++++++++++++++++++++++++++++++++ examples/text/annotations.jl | 144 +++++++++++++++++++++++++++++++ src/gui/overview.jl | 64 -------------- src/texture_atlas.jl | 4 +- src/visualize/particles.jl | 2 +- src/visualize/text.jl | 2 +- 6 files changed, 311 insertions(+), 68 deletions(-) create mode 100644 examples/gui/overview.jl create mode 100644 examples/text/annotations.jl delete mode 100644 src/gui/overview.jl diff --git a/examples/gui/overview.jl b/examples/gui/overview.jl new file mode 100644 index 0000000..8fac94d --- /dev/null +++ b/examples/gui/overview.jl @@ -0,0 +1,163 @@ +using GLVisualize, Colors, GeometryTypes, Reactive, GLAbstraction, GLFW, GLWindow +import GLVisualize: mm + +w = glscreen(); @async GLWindow.waiting_renderloop(w) +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, w, camera = :fixed_pixel) + +ids = (map(x-> x.id, extract_renderable(x))..., ) +@materialize mouse_buttons_pressed, buttons_pressed, unicode_input = w.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(w)), 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(w)) + 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) + + +GLWindow.renderloop(w) +#Profile.print() diff --git a/examples/text/annotations.jl b/examples/text/annotations.jl new file mode 100644 index 0000000..123ab5c --- /dev/null +++ b/examples/text/annotations.jl @@ -0,0 +1,144 @@ +using GLVisualize, GeometryTypes, Colors, GLAbstraction, Reactive, Images, ModernGL +import GLVisualize: mm, calc_position, glyph_bearing!, glyph_uv_width!, glyph_scale! + +window = glscreen() + +description = """ +Example of showing animated annotations over an image stream +""" + +# Displayes an array of points with text labels. This should get moved into GLVisualize! +function annotated_text( + points, labels_s; + color = RGBA(0f0,0f0,0f0,1f0), + scale = 5mm, + base_offset = Point2f0(scale / 2) + ) + # make this work for Signal(points) as well as constant points + points_s = isa(points, Signal) ? points : Signal(points) + font, atlas = GLVisualize.defaultfont(), GLVisualize.get_texture_atlas() + # to display a text particle system, one needs + # 1, glyph segmentation_centroids, offset to the bottom, position in the texture atlas + # and the scale of the glyph. Then for bg labels, we also save a width + v0 = (Point2f0[], Point2f0[], Vec4f0[], Vec2f0[], Vec2f0[]) + text = map(points_s) do points + labels = value(labels_s) + if length(labels) != length(points) + error(""" + Colors, labels and points must have the same length! Found: + Points: $(length(points)), labels: $(length(labels))""") + end + n = mapreduce(length, +, labels) + rpoints, offsets, uv_widths, scales, widths = v0 + if length(labels) != length(widths) + resize!(widths, length(labels)) + end + if n != length(rpoints) # only resize if size changed + resize!(rpoints, n); resize!(offsets, n) + resize!(uv_widths, n); resize!(scales, n) + end + idx = 1 + # glyph height of a long glyph + glyph_height = glyph_scale!(atlas, '|', font, scale)[2] + for (i, (start_pos, label)) in enumerate(zip(points, labels)) + last_pos = start_pos .+ base_offset + for c in label # calculate segmentation_centroids for each character/glyph + rpoints[idx] = last_pos + last_pos = calc_position(last_pos, start_pos, atlas, c, font, scale) + offsets[idx] = glyph_bearing!(atlas, c, font, scale) + uv_widths[idx] = glyph_uv_width!(atlas, c, font) + scales[idx] = glyph_scale!(atlas, c, font, scale) + idx += 1 + end + # calculate label lengths + extra width + widths[i] = Vec2f0(last_pos[1] - start_pos[1] + 1mm, glyph_height + 1mm) + end + rpoints, offsets, uv_widths, scales, widths + end + viz = visualize( + (DISTANCEFIELD, map(x->x[1], text)), # render segmentation_centroids as a distance field particle type + offset = map(x->x[2], text), + uv_offset_width = map(x->x[3], text), + scale = map(x->x[4], text), + color = color, + # drop down to low level opengl, to always render over image! (there should be a better API for this) + prerender = () -> begin + glDisable(GL_DEPTH_TEST) + glDepthMask(GL_TRUE) + glDisable(GL_CULL_FACE) + enabletransparency() + end, + distancefield = atlas.images + ) + viz, map(last, text) # return viz and widths +end + + +# _view and visualize it! +# you could also pass segmentation_centroids as a keyword argument or make +# the scale/rotation per glyph by supplying a Vector of them. +N = 20 +segmentation_centroids = Signal(rand(Point2f0, N) .* 1000f0) +label_str = Signal([string(i) for i=1:N]) + +labels, widths = annotated_text(segmentation_centroids, label_str) +fbuffer = rand(Gray{N0f8}, 1200, 1600) +frame_viz = visualize(fbuffer) + +# instead of circle you can also use unicode charactes (e.g. '+') +position_viz = visualize( + (Circle{Float32}(0, 1.5mm), segmentation_centroids), + color = RGBA(1f0, 0f0, 0f0, 0.6f0) +) + +gpu_position = position_viz.children[][:position] +bg_viz = visualize( + (ROUNDED_RECTANGLE, segmentation_centroids), + scale = widths, + # you can give each label a color (random looks terrible, which is why whe use just white instead) + # color = RGBA{Float32}[rand(RGB) for i = 1:N], + color = RGBA(1f0, 1f0, 1f0, 0.6f0), + offset = Vec2f0(1.0mm) +) +_view(frame_viz, window) +_view(bg_viz, window) +_view(labels, window) +_view(position_viz, window) +# N0f8, like UInt8, but between 0-1 to make for more realistic gray values + +# For optimal performance, we need to get the gpu handle of the image +# need to acces children for that, since visualize always returns a tree structure, +# which contains just one element in our case +gpu_image = frame_viz.children[1][:image] +center!(window, :orthographic_pixel) # center orthographic_pixel camera to what we just visualized +Reactive.stop() # stop reactives event loop, we poll ourselves! +idx = 0 +while isopen(window) + + # update position values inplace + map!(value(segmentation_centroids)) do p + p .+ (rand(Point2f0) .- 0.5f0) .* 10f0 + end + # # push! to signal to update visualization + push!(segmentation_centroids, value(segmentation_centroids)) + + # update text! (ZOOP!!!) + push!(label_str, [string(i + idx) for i=1:N]) + # you could also update the labels here!, if you put them in a signal + + # update frame buffer inplace with new "camera capture" + map!(fbuffer) do v + v > 0.5 ? sin(v) : cos(v) # yeah, real creative.. + end + # UPDATE FRAME FROM CAMERA!!!! + gpu_image[1:end, 1:end] = fbuffer # update data on GPU (ZING!!!) + + # do the rendering + GLWindow.render_frame(window) + GLWindow.swapbuffers(window) + GLWindow.poll_glfw() + GLWindow.poll_reactive() + idx += 1 +end + +GLWindow.destroy!(window) diff --git a/src/gui/overview.jl b/src/gui/overview.jl deleted file mode 100644 index bbe6cf5..0000000 --- a/src/gui/overview.jl +++ /dev/null @@ -1,64 +0,0 @@ -using GLVisualize, Colors, GeometryTypes, Reactive, GLAbstraction, GLFW, GLWindow -import GLVisualize: mm - -w = glscreen(); @async GLWindow.waiting_renderloop(w) - -function text_with_background(txt; - background_color = RGBA(1f0, 1f0, 1f0, 1f0), gap = 1mm, - kw_args... - ) - robj = visualize(txt; kw_args...).children[] - rect = 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 - 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(GLVisualize.printforslider, slider_val) -color = Signal(RGBA(1f0, 1f0, 1f0, 1f0)) -x = text_with_background(slider_str, relative_scale = 6mm, background_color = color) -GLAbstraction.translate!(x, Vec3f0(4mm, 4mm, 0)) -_view(x, w, camera = :fixed_pixel) - -ids = (map(x-> x.id, extract_renderable(x))..., ) -@materialize mouse_buttons_pressed, buttons_pressed, unicode_input = w.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(w)), ids) && return !v0 - v0 -end) - -preserve(foldp((false, " "), unicode_input, text_edit) do v0, chars, edit - was_edit, str = v0 - if edit - if !was_edit - str = string(value(slider_val)) - end - for char in chars - if isnumber(char) - str *= string(char) - end - end - push!(slider_str, str) - push!(color, RGBA(0.8f0, 0.8f0, 0.8f0, 0.4f0)) - else - num = parse(eltype(value(slider_val)), str) - push!(slider_val, num) - str = " " - push!(color, RGBA(1f0, 1f0, 1f0, 1f0)) - end - edit, str -end) - -map(println, unicode_input) diff --git a/src/texture_atlas.jl b/src/texture_atlas.jl index eae53b6..ed1957d 100644 --- a/src/texture_atlas.jl +++ b/src/texture_atlas.jl @@ -190,7 +190,7 @@ 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) @@ -204,7 +204,7 @@ function GLAbstraction.render(atlas::TextureAtlas, glyph::Char, font) glyph = ' ' end downsample = 5 - pad = 8 + pad = 20 bitmap, extent = renderface(font, glyph, (50*downsample, 50*downsample)) sd = sdistancefield(bitmap, downsample, downsample*pad) extent = extent ./ Vec2f0(downsample) diff --git a/src/visualize/particles.jl b/src/visualize/particles.jl index b13f77d..839a904 100644 --- a/src/visualize/particles.jl +++ b/src/visualize/particles.jl @@ -505,7 +505,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 diff --git a/src/visualize/text.jl b/src/visualize/text.jl index 4372a2e..949e174 100644 --- a/src/visualize/text.jl +++ b/src/visualize/text.jl @@ -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 From 52aae851679e405369dd689079d776e6aeae3b64 Mon Sep 17 00:00:00 2001 From: SimonDanisch Date: Wed, 15 Mar 2017 09:47:32 +0100 Subject: [PATCH 03/12] cosmetic --- src/visualize/text.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/visualize/text.jl b/src/visualize/text.jl index 949e174..fc22c66 100644 --- a/src/visualize/text.jl +++ b/src/visualize/text.jl @@ -202,7 +202,7 @@ texttexttexttext\n texttexttext\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 From 231ff783fce0f557bfc299b7db75e2ad9248a8e3 Mon Sep 17 00:00:00 2001 From: SimonDanisch Date: Wed, 15 Mar 2017 14:52:39 +0100 Subject: [PATCH 04/12] make example test proof --- examples/gui/overview.jl | 16 ++--- examples/text/annotations.jl | 136 +---------------------------------- 2 files changed, 9 insertions(+), 143 deletions(-) diff --git a/examples/gui/overview.jl b/examples/gui/overview.jl index 8fac94d..bcc6b75 100644 --- a/examples/gui/overview.jl +++ b/examples/gui/overview.jl @@ -1,7 +1,7 @@ using GLVisualize, Colors, GeometryTypes, Reactive, GLAbstraction, GLFW, GLWindow import GLVisualize: mm -w = glscreen(); @async GLWindow.waiting_renderloop(w) +window = glscreen(); @async GLWindow.waiting_renderloop(window) text_scale = 6mm function text_with_background(txt; background_color = RGBA(1f0, 1f0, 1f0, 1f0), gap = 1mm, @@ -36,15 +36,15 @@ x = text_with_background( size = (8*text_scale, text_scale) ) GLAbstraction.translate!(x, Vec3f0(4mm, 4mm, 0)) -_view(x, w, camera = :fixed_pixel) +_view(x, window, camera = :fixed_pixel) ids = (map(x-> x.id, extract_renderable(x))..., ) -@materialize mouse_buttons_pressed, buttons_pressed, unicode_input = w.inputs +@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(w)), ids) && return !v0 + clicked && is_same_id(value(mouse2id(window)), ids) && return !v0 v0 end) backspace = Set([GLFW.KEY_BACKSPACE]) @@ -87,7 +87,7 @@ txt_edit_s = foldp((false, [' '], 1), unicode_input, text_edit, ctrl_buttons) do move(num) = (cursor = clamp(cursor + num, 0, length(str))) if edit if !was_edit && isempty(ctrl)# reset - id, idx = value(mouse2id(w)) + id, idx = value(mouse2id(window)) empty!(str) append!(str, string(value(slider_val))) @show id idx @@ -158,6 +158,6 @@ _view(visualize( relative_scale = 5mm, ), camera = :fixed_pixel) - -GLWindow.renderloop(w) -#Profile.print() +if !isdefined(:runtests) + renderloop(window) +end diff --git a/examples/text/annotations.jl b/examples/text/annotations.jl index 4da8bb8..4f93547 100644 --- a/examples/text/annotations.jl +++ b/examples/text/annotations.jl @@ -1,99 +1,17 @@ using GLVisualize, GeometryTypes, Colors, GLAbstraction, Reactive, Images, ModernGL -<<<<<<< HEAD import GLVisualize: mm, calc_position, glyph_bearing!, glyph_uv_width!, glyph_scale! -window = glscreen() -======= import GLVisualize: mm, annotated_text if !isdefined(:runtests) window = glscreen() timesignal = loop(linspace(0f0, 1f0, 360)) end ->>>>>>> master description = """ Example of showing animated annotations over an image stream """ -<<<<<<< HEAD -# Displayes an array of points with text labels. This should get moved into GLVisualize! -function annotated_text( - points, labels_s; - color = RGBA(0f0,0f0,0f0,1f0), - scale = 5mm, - base_offset = Point2f0(scale / 2) - ) - # make this work for Signal(points) as well as constant points - points_s = isa(points, Signal) ? points : Signal(points) - font, atlas = GLVisualize.defaultfont(), GLVisualize.get_texture_atlas() - # to display a text particle system, one needs - # 1, glyph segmentation_centroids, offset to the bottom, position in the texture atlas - # and the scale of the glyph. Then for bg labels, we also save a width - v0 = (Point2f0[], Point2f0[], Vec4f0[], Vec2f0[], Vec2f0[]) - text = map(points_s) do points - labels = value(labels_s) - if length(labels) != length(points) - error(""" - Colors, labels and points must have the same length! Found: - Points: $(length(points)), labels: $(length(labels))""") - end - n = mapreduce(length, +, labels) - rpoints, offsets, uv_widths, scales, widths = v0 - if length(labels) != length(widths) - resize!(widths, length(labels)) - end - if n != length(rpoints) # only resize if size changed - resize!(rpoints, n); resize!(offsets, n) - resize!(uv_widths, n); resize!(scales, n) - end - idx = 1 - # glyph height of a long glyph - glyph_height = glyph_scale!(atlas, '|', font, scale)[2] - for (i, (start_pos, label)) in enumerate(zip(points, labels)) - last_pos = start_pos .+ base_offset - for c in label # calculate segmentation_centroids for each character/glyph - rpoints[idx] = last_pos - last_pos = calc_position(last_pos, start_pos, atlas, c, font, scale) - offsets[idx] = glyph_bearing!(atlas, c, font, scale) - uv_widths[idx] = glyph_uv_width!(atlas, c, font) - scales[idx] = glyph_scale!(atlas, c, font, scale) - idx += 1 - end - # calculate label lengths + extra width - widths[i] = Vec2f0(last_pos[1] - start_pos[1] + 1mm, glyph_height + 1mm) - end - rpoints, offsets, uv_widths, scales, widths - end - viz = visualize( - (DISTANCEFIELD, map(x->x[1], text)), # render segmentation_centroids as a distance field particle type - offset = map(x->x[2], text), - uv_offset_width = map(x->x[3], text), - scale = map(x->x[4], text), - color = color, - # drop down to low level opengl, to always render over image! (there should be a better API for this) - prerender = () -> begin - glDisable(GL_DEPTH_TEST) - glDepthMask(GL_TRUE) - glDisable(GL_CULL_FACE) - enabletransparency() - end, - distancefield = atlas.images - ) - viz, map(last, text) # return viz and widths -end - - -# _view and visualize it! -# you could also pass segmentation_centroids as a keyword argument or make -# the scale/rotation per glyph by supplying a Vector of them. -N = 20 -segmentation_centroids = Signal(rand(Point2f0, N) .* 1000f0) -label_str = Signal([string(i) for i=1:N]) - -labels, widths = annotated_text(segmentation_centroids, label_str) -fbuffer = rand(Gray{N0f8}, 1200, 1600) -======= N = 20 positions = foldp(rand(Point2f0, N) .* 500f0, timesignal) do v0, t map!(v0) do p @@ -115,27 +33,16 @@ fbuffer = foldp(rand(RGB{N0f8}, 500, 500), timesignal) do v0, t RGB(v.r > 0.5 ? sin(v.r) : cos(v.r), (sin(t) + 1) / 2, 0.7) # yeah, real creative.. end end ->>>>>>> master frame_viz = visualize(fbuffer) - # instead of circle you can also use unicode charactes (e.g. '+') position_viz = visualize( -<<<<<<< HEAD - (Circle{Float32}(0, 1.5mm), segmentation_centroids), - color = RGBA(1f0, 0f0, 0f0, 0.6f0) -) - -gpu_position = position_viz.children[][:position] -bg_viz = visualize( - (ROUNDED_RECTANGLE, segmentation_centroids), -======= (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), ->>>>>>> master scale = widths, # you can give each label a color (random looks terrible, which is why whe use just white instead) # color = RGBA{Float32}[rand(RGB) for i = 1:N], @@ -146,50 +53,9 @@ _view(frame_viz, window) _view(bg_viz, window) _view(labels, window) _view(position_viz, window) -<<<<<<< HEAD -# N0f8, like UInt8, but between 0-1 to make for more realistic gray values - -# For optimal performance, we need to get the gpu handle of the image -# need to acces children for that, since visualize always returns a tree structure, -# which contains just one element in our case -gpu_image = frame_viz.children[1][:image] -center!(window, :orthographic_pixel) # center orthographic_pixel camera to what we just visualized -Reactive.stop() # stop reactives event loop, we poll ourselves! -idx = 0 -while isopen(window) - - # update position values inplace - map!(value(segmentation_centroids)) do p - p .+ (rand(Point2f0) .- 0.5f0) .* 10f0 - end - # # push! to signal to update visualization - push!(segmentation_centroids, value(segmentation_centroids)) - - # update text! (ZOOP!!!) - push!(label_str, [string(i + idx) for i=1:N]) - # you could also update the labels here!, if you put them in a signal - - # update frame buffer inplace with new "camera capture" - map!(fbuffer) do v - v > 0.5 ? sin(v) : cos(v) # yeah, real creative.. - end - # UPDATE FRAME FROM CAMERA!!!! - gpu_image[1:end, 1:end] = fbuffer # update data on GPU (ZING!!!) - - # do the rendering - GLWindow.render_frame(window) - GLWindow.swapbuffers(window) - GLWindow.poll_glfw() - GLWindow.poll_reactive() - idx += 1 -end - -GLWindow.destroy!(window) -======= center!(window, :orthographic_pixel) # center orthographic_pixel camera to what we just visualized if !isdefined(:runtests) renderloop(window) end ->>>>>>> master From e7d9071f7efe4137631b5e45ddc1721d851a8a7a Mon Sep 17 00:00:00 2001 From: SimonDanisch Date: Wed, 15 Mar 2017 18:05:25 +0100 Subject: [PATCH 05/12] don't cache fonts so hard on CI --- src/texture_atlas.jl | 4 ++-- test/runtests.jl | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/texture_atlas.jl b/src/texture_atlas.jl index ed1957d..0fe6513 100644 --- a/src/texture_atlas.jl +++ b/src/texture_atlas.jl @@ -197,13 +197,13 @@ function sdistancefield(img, downsample = 8, pad = 8*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 + downsample = _downsample_rate pad = 20 bitmap, extent = renderface(font, glyph, (50*downsample, 50*downsample)) sd = sdistancefield(bitmap, downsample, downsample*pad) diff --git a/test/runtests.jl b/test/runtests.jl index 7f8dcdc..396fb58 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -7,6 +7,7 @@ function isheadless() end if isheadless() + ENV["GLVISUALIZE_DOWNSAMPLE_RATE"] = 1 # need this branch for better coverage report! cd(Pkg.dir("GLAbstraction")) do run(`git fetch origin`) From a90210dd23a9419e649d66c0801217ee7d783648 Mon Sep 17 00:00:00 2001 From: SimonDanisch Date: Wed, 15 Mar 2017 18:05:38 +0100 Subject: [PATCH 06/12] don't open non test window --- examples/gui/overview.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/gui/overview.jl b/examples/gui/overview.jl index bcc6b75..782a3ac 100644 --- a/examples/gui/overview.jl +++ b/examples/gui/overview.jl @@ -1,7 +1,10 @@ using GLVisualize, Colors, GeometryTypes, Reactive, GLAbstraction, GLFW, GLWindow import GLVisualize: mm -window = glscreen(); @async GLWindow.waiting_renderloop(window) +if !isdefined(:runtests) + window = glscreen() +end + text_scale = 6mm function text_with_background(txt; background_color = RGBA(1f0, 1f0, 1f0, 1f0), gap = 1mm, From 4b793bcd412dc634a218fe29d07a0b9ff7a4a180 Mon Sep 17 00:00:00 2001 From: SimonDanisch Date: Wed, 15 Mar 2017 20:41:36 +0100 Subject: [PATCH 07/12] don't fail if optional ffmpeg is not installed --- examples/introduction/video_recording.jl | 35 ++++++++++++++---------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/examples/introduction/video_recording.jl b/examples/introduction/video_recording.jl index 55a6e8f..bfb78f9 100644 --- a/examples/introduction/video_recording.jl +++ b/examples/introduction/video_recording.jl @@ -6,6 +6,7 @@ end description = """ Example of how to record a video from GLVisualize """ + kitty = visualize(loadasset("cat.obj")) _view(kitty, window) @@ -20,23 +21,27 @@ while true # for some reason, folder retured by mktempdir isn't usable -.- name = path * "/$(randstring()).mkv" isfile(name) || break end +# 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() -# 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) From bfd1366ddb8b387798b57da91b064bc1e6b37970 Mon Sep 17 00:00:00 2001 From: SimonDanisch Date: Wed, 15 Mar 2017 20:43:22 +0100 Subject: [PATCH 08/12] faster font caching on ci for real now --- src/texture_atlas.jl | 2 ++ test/runtests.jl | 2 -- test/test_interactive.jl | 2 +- test/test_static.jl | 5 +++++ 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/texture_atlas.jl b/src/texture_atlas.jl index 0fe6513..04093e9 100644 --- a/src/texture_atlas.jl +++ b/src/texture_atlas.jl @@ -197,7 +197,9 @@ function sdistancefield(img, downsample = 8, pad = 8*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 diff --git a/test/runtests.jl b/test/runtests.jl index 396fb58..4fd252d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,3 @@ -include("micro.jl") - function isheadless() get(ENV, "TRAVIS", "") == "true" || get(ENV, "APPVEYOR", "") == "true" || diff --git a/test/test_interactive.jl b/test/test_interactive.jl index a475594..ee2eb21 100644 --- a/test/test_interactive.jl +++ b/test/test_interactive.jl @@ -32,5 +32,5 @@ config = ExampleRunner.RunnerConfig( files = files, resolution = (210, 210) ) - +include("micro.jl") ExampleRunner.run(config) diff --git a/test/test_static.jl b/test/test_static.jl index fa7fa24..a4f59cf 100644 --- a/test/test_static.jl +++ b/test/test_static.jl @@ -1,5 +1,10 @@ using GLVisualize +include("micro.jl") include(GLVisualize.dir("examples", "ExampleRunner.jl")) + + +@show GLVisualize._downsample_rate + using ExampleRunner importall ExampleRunner using GLAbstraction, GLWindow, Colors From e9cabddc24f1d5630825006c6b80ee85b6d83503 Mon Sep 17 00:00:00 2001 From: SimonDanisch Date: Wed, 15 Mar 2017 21:09:03 +0100 Subject: [PATCH 09/12] fix path --- examples/introduction/video_recording.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/introduction/video_recording.jl b/examples/introduction/video_recording.jl index bfb78f9..4955892 100644 --- a/examples/introduction/video_recording.jl +++ b/examples/introduction/video_recording.jl @@ -11,16 +11,16 @@ 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 end + # only try recording when ffmpeg is installed if success(`ffmpeg -h`) # create a stream to which we can add frames From 75bdc901124029b96a661ef0ee9a66401178ac11 Mon Sep 17 00:00:00 2001 From: SimonDanisch Date: Wed, 15 Mar 2017 21:09:15 +0100 Subject: [PATCH 10/12] fix report and micro --- test/test_interactive.jl | 3 ++- test/test_static.jl | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/test_interactive.jl b/test/test_interactive.jl index ee2eb21..43a555b 100644 --- a/test/test_interactive.jl +++ b/test/test_interactive.jl @@ -1,4 +1,5 @@ using GLVisualize +include("micro.jl") include(GLVisualize.dir("examples", "ExampleRunner.jl")) using ExampleRunner import ExampleRunner: flatten_paths @@ -32,5 +33,5 @@ config = ExampleRunner.RunnerConfig( files = files, resolution = (210, 210) ) -include("micro.jl") + ExampleRunner.run(config) diff --git a/test/test_static.jl b/test/test_static.jl index a4f59cf..ffdf926 100644 --- a/test/test_static.jl +++ b/test/test_static.jl @@ -23,7 +23,7 @@ function create_mosaic(io, folder, width = 150) images = filter(x-> endswith(x, ".jpg"), readdir(folder)) for im in images println(io, """$(im) + alt="$(im)" width=$(width)px/> """) end end From 61435c3a54a086641d68471e88c6e7302bdde621 Mon Sep 17 00:00:00 2001 From: SimonDanisch Date: Wed, 15 Mar 2017 21:30:57 +0100 Subject: [PATCH 11/12] really show error --- test/test_static.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_static.jl b/test/test_static.jl index ffdf926..0654dbf 100644 --- a/test/test_static.jl +++ b/test/test_static.jl @@ -129,7 +129,7 @@ if recording println(io, "### Failures:") for (k, dict) in failures println(io, "file: $k") - Base.showerror(io, "$(dict[:exception])") + Base.showerror(io, dict[:exception]) println("\n") end else From 7391444091f7fc40e2d65f4c485900919ac1fce2 Mon Sep 17 00:00:00 2001 From: SimonDanisch Date: Wed, 15 Mar 2017 22:05:17 +0100 Subject: [PATCH 12/12] fix path --- examples/introduction/video_recording.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/introduction/video_recording.jl b/examples/introduction/video_recording.jl index 4955892..8ad7f5e 100644 --- a/examples/introduction/video_recording.jl +++ b/examples/introduction/video_recording.jl @@ -19,6 +19,7 @@ else name = path * "/$(randstring()).mkv" isfile(name) || break end + name end # only try recording when ffmpeg is installed