├── example ├── GMTPlots │ └── ex49.jl ├── marchingtetrahedra.jl ├── parametric.jl ├── text.jl ├── vectorfield.jl ├── surface.jl ├── image.jl ├── surface2.jl ├── fractal.jl ├── volume.jl ├── rgb_volume_slice.jl ├── hover_unicode.jl ├── lorenz.jl ├── volume_slice.jl ├── mandelbulb.jl ├── boids.jl ├── sliders.jl └── SignalProcessing │ └── imfilter_pipeline.jl ├── .gitignore ├── test └── runtests.jl ├── docs ├── glplot.jpg ├── julia.png ├── volume.png ├── anaglyph.png ├── catsobelt.png ├── surface.png └── interaction.png ├── src ├── icons │ ├── play.png │ ├── break.png │ ├── center.png │ ├── delete.png │ ├── ortho.png │ ├── pause.png │ ├── record.png │ ├── showing.png │ ├── notshowing.png │ ├── perspective.png │ └── screenshot.png ├── precompile.jl ├── GLPlot.jl ├── gui.jl ├── screen.jl └── plot.jl ├── REQUIRE ├── .travis.yml ├── README.md └── LICENSE.md /example/GMTPlots/ex49.jl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.nrrd 2 | *.rawb 3 | /src/example 4 | *.db -------------------------------------------------------------------------------- /test/runtests.jl: -------------------------------------------------------------------------------- 1 | using GLPlot 2 | GLPlot.init() 3 | -------------------------------------------------------------------------------- /example/marchingtetrahedra.jl: -------------------------------------------------------------------------------- 1 | using GLPlot, GeometryTypes 2 | GLPlot.init() 3 | -------------------------------------------------------------------------------- /docs/glplot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/docs/glplot.jpg -------------------------------------------------------------------------------- /docs/julia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/docs/julia.png -------------------------------------------------------------------------------- /docs/volume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/docs/volume.png -------------------------------------------------------------------------------- /docs/anaglyph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/docs/anaglyph.png -------------------------------------------------------------------------------- /docs/catsobelt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/docs/catsobelt.png -------------------------------------------------------------------------------- /docs/surface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/docs/surface.png -------------------------------------------------------------------------------- /src/icons/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/src/icons/play.png -------------------------------------------------------------------------------- /docs/interaction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/docs/interaction.png -------------------------------------------------------------------------------- /src/icons/break.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/src/icons/break.png -------------------------------------------------------------------------------- /src/icons/center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/src/icons/center.png -------------------------------------------------------------------------------- /src/icons/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/src/icons/delete.png -------------------------------------------------------------------------------- /src/icons/ortho.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/src/icons/ortho.png -------------------------------------------------------------------------------- /src/icons/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/src/icons/pause.png -------------------------------------------------------------------------------- /src/icons/record.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/src/icons/record.png -------------------------------------------------------------------------------- /src/icons/showing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/src/icons/showing.png -------------------------------------------------------------------------------- /REQUIRE: -------------------------------------------------------------------------------- 1 | julia 0.4 2 | GLVisualize 3 | GLWindow 4 | ModernGL 5 | GeometryTypes 6 | Iterators 7 | NIfTI 8 | -------------------------------------------------------------------------------- /src/icons/notshowing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/src/icons/notshowing.png -------------------------------------------------------------------------------- /src/icons/perspective.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/src/icons/perspective.png -------------------------------------------------------------------------------- /src/icons/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpsanders/GLPlot.jl/master/src/icons/screenshot.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: julia 2 | os: 3 | - linux 4 | - osx 5 | julia: 6 | - 0.4 7 | - nightly 8 | notifications: 9 | email: false 10 | # uncomment the following lines to override the default test script 11 | -------------------------------------------------------------------------------- /example/parametric.jl: -------------------------------------------------------------------------------- 1 | using GLPlot, GLAbstraction 2 | GLPlot.init() 3 | import GLAbstraction: @frage_str 4 | 5 | f = frag""" 6 | float function(float x) { 7 | return sin(x*x*x)*sin(x); 8 | } 9 | """ 10 | 11 | glplot(f) 12 | -------------------------------------------------------------------------------- /example/text.jl: -------------------------------------------------------------------------------- 1 | using GLAbstraction, GLPlot, Reactive 2 | GLPlot.init() 3 | text = "whatup internet!?\n#Crusont" 4 | 5 | glplot(text, color = [RGBA(rand(), rand(), 0,1) for i=1:length(text)]) # You can either, supply a texture with colors 6 | -------------------------------------------------------------------------------- /example/vectorfield.jl: -------------------------------------------------------------------------------- 1 | using GLPlot, GLAbstraction, ModernGL, GeometryTypes 2 | GLPlot.init() 3 | 4 | function funcy(x,y,z) 5 | Vec3f0 6 | end 7 | 8 | N = 10 9 | r = linspace(0, 6, N) 10 | directions = Vec3f0[(sin(x),cos(y),sin(z)) for x=r,y=r, z=r] 11 | obj = glplot(directions) 12 | -------------------------------------------------------------------------------- /example/surface.jl: -------------------------------------------------------------------------------- 1 | using GLPlot, GLAbstraction, ModernGL, GeometryTypes 2 | n = 100 3 | h = 1./n 4 | r = h:h:1. 5 | t = (-1:h:1+h)*π 6 | x = map(Float32, r*cos(t)') 7 | y = map(Float32, r*sin(t)') 8 | 9 | f(x,y) = exp(-10x.^2-20y.^2) # arbitrary function of f 10 | z = Float32[(f(x[k,j],y[k,j])) for k=1:size(x,1),j=1:size(x,2)] 11 | obj = glplot((x, y, z), :surface) 12 | -------------------------------------------------------------------------------- /example/image.jl: -------------------------------------------------------------------------------- 1 | using GLPlot, GLAbstraction, GeometryTypes, FileIO, GLVisualize, Colors 2 | 3 | GLPlot.init() 4 | 5 | ########################################################## 6 | # Image 7 | 8 | a = [RGBA{U8}(i/512,j/512,0,1)for i=1:512, j=1:512] 9 | # Without ImmutableArays, the color dimension is not known and you need to supply it 10 | b = [Gray{Float32}((i*j)/512^2) for i=1:512, j=1:512] 11 | c = loadasset("racoon.png") 12 | d = loadasset("kitty.png") 13 | layout!(SimpleRectangle(0f0, 0f0, 100f0, 100f0), glplot(a)) 14 | layout!(SimpleRectangle(0f0, 100f0, 100f0, 100f0), glplot(b)) 15 | layout!(SimpleRectangle(100f0, 0f0, 100f0, 100f0), glplot(c)) 16 | layout!(SimpleRectangle(100f0, 100f0, 100f0, 100f0), glplot(d)) 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GLPlot 2 | #### Master is now using GLVisualize for rendering 3 | 4 | If you want to discuss anything just open an issue or join the chat via gitter. 5 | [![Join the chat at https://gitter.im/SimonDanisch/GLPlot.jl](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/SimonDanisch/GLPlot.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 6 | 7 | ![Overview](docs/glplot.jpg) 8 | 9 | Everything is in the wiki now: 10 | https://github.com/SimonDanisch/GLPlot.jl/wiki 11 | 12 | Please read about the installation process: 13 | Installation of master is a little tricky, but not overly complicated if you execute this script: 14 | ```Julia 15 | Pkg.add("GLPlot") 16 | Pkg.checkout("GLPlot") 17 | ``` 18 | -------------------------------------------------------------------------------- /src/precompile.jl: -------------------------------------------------------------------------------- 1 | using SnoopCompile 2 | 3 | SnoopCompile.@snoop "glp_compiles.csv" begin 4 | using GLPlot;GLPlot.init() 5 | using Colors, GeometryTypes 6 | glplot(rand(Float32, 32,32)) 7 | glplot(rand(Float32, 32,32), :surface) 8 | glplot(rand(Point3f0,32)) 9 | glplot(rand(Point3f0,32), :lines) 10 | glplot(rand(Point2f0,32), :lines) 11 | glplot(rand(Point2f0,32)) 12 | glplot(RGBA{Float32}[RGBA{Float32}(rand(), rand(), rand(), rand()) for i=1:512, j=1:512]) 13 | while isopen(GLPlot.viewing_screen) 14 | yield() 15 | end 16 | end 17 | 18 | using GLPlot 19 | data = SnoopCompile.read("glp_compiles.csv") 20 | blacklist = ["MIME"] 21 | pc = SnoopCompile.format_userimg(data[end:-1:1,2], blacklist=blacklist) 22 | SnoopCompile.write(Pkg.dir("GLPlot", "src", "glp_userimg.jl"), pc) 23 | -------------------------------------------------------------------------------- /example/surface2.jl: -------------------------------------------------------------------------------- 1 | using GLAbstraction, GLPlot, Reactive, Colors 2 | GLPlot.init() 3 | 4 | function zdata(i, j, t) 5 | x = (i - 0.5) 6 | z = (j - 0.5) 7 | radius = sqrt((x * x) + (z * z)) 8 | 9 | r = sin(10.0f0 * radius + t) 10 | Float32(r + rand(1.0:0.01:1.1)) 11 | end 12 | function zcolor(i, j, t) 13 | x = (i - 0.5) 14 | z = (j - 0.5) 15 | radius = sqrt((x * x) + (z * z)) 16 | 17 | r = sin(10.0f0 * radius + t) 18 | g = cos(10.0f0 * radius + t) 19 | b = radius 20 | return RGBA{Float32}(r, g, b, 1) 21 | end 22 | 23 | N = 128 24 | texdata = [zdata(i/N, j/N, 15) for i=1:N, j=1:N] 25 | color = [zcolor(i/N, j/N, 15) for i=1:N, j=1:N] # Example on how to use react to change the color over time 26 | 27 | 28 | obj = glplot(texdata, :surface, color = color) 29 | -------------------------------------------------------------------------------- /src/GLPlot.jl: -------------------------------------------------------------------------------- 1 | __precompile__(true) 2 | module GLPlot 3 | 4 | 5 | using GLVisualize, GLWindow, ModernGL, Reactive, GLAbstraction, Colors 6 | using FixedPointNumbers, FreeType, SignedDistanceFields, Images, Packing 7 | using GeometryTypes, GLFW, FileIO, FixedSizeArrays, Quaternions 8 | 9 | import GLVisualize: toggle_button, toggle, button 10 | import GLVisualize: mm, extract_edit_menu 11 | 12 | # Some not officially supported file formats from FileIO 13 | # FileIO.load(file::File{format"Julia"}) = include(filename(file)) 14 | # function __init__() 15 | # add_format(format"Julia", (), ".jl") 16 | # end 17 | 18 | function imload(name) 19 | rotl90(Matrix{BGRA{U8}}(load(Pkg.dir("GLPlot", "src", "icons", name)))) 20 | end 21 | 22 | 23 | 24 | include("gui.jl") 25 | export play_control 26 | include("plot.jl") 27 | export glplot 28 | export register_plot! 29 | include("screen.jl") 30 | export register_compute 31 | 32 | 33 | #include("glp_userimg.jl") 34 | 35 | end 36 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The GLPlot.jl package is licensed under the MIT "Expat" License: 2 | 3 | > Copyright (c) 2014: Simon Danisch. 4 | > 5 | > Permission is hereby granted, free of charge, to any person obtaining 6 | > a copy of this software and associated documentation files (the 7 | > "Software"), to deal in the Software without restriction, including 8 | > without limitation the rights to use, copy, modify, merge, publish, 9 | > distribute, sublicense, and/or sell copies of the Software, and to 10 | > permit persons to whom the Software is furnished to do so, subject to 11 | > the following conditions: 12 | > 13 | > The above copyright notice and this permission notice shall be 14 | > included in all copies or substantial portions of the Software. 15 | > 16 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | > IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | > CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | > TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | > SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /example/fractal.jl: -------------------------------------------------------------------------------- 1 | using GeometryTypes, Colors 2 | 3 | function loop{T}(point::T, radius, imax, i=1, points=Point3f0[], scales=Vec3f0[]) 4 | i == imax && return points, scales 5 | push!(points, point); push!(scales, radius) 6 | for point in decompose(T, Sphere(point, radius), 4) 7 | loop(point, radius/2, imax, i+1, points, scales) 8 | end 9 | points, scales 10 | end 11 | const cmap2 = map(RGBA{Float32}, colormap("RdBu", 3)) 12 | function loop2{T}(point::T, normal, radius, imax, mesh=loadasset("cat.obj"), i=1, points=Point3f0[], scales=Vec3f0[], rotations=Vec3f0[], colors=RGBA{Float32}[]) 13 | i > imax && return points, scales, rotations, colors 14 | push!(points, point);push!(scales, radius);push!(rotations, normal);push!(colors, cmap2[i]) 15 | for (point, normal) in zip(decompose(T, mesh), decompose(Normal{3, Float32}, mesh)) 16 | loop2(point, normal, radius/10f0, imax, mesh, i+1, points, scales, rotations, colors) 17 | end 18 | points, scales, rotations, colors 19 | end 20 | using GLVisualize 21 | p, s, r, c = loop2(Point3f0(0), Normal{3,Float32}(0,0,1), 1f0, 2, loadasset("cat.obj")); 22 | 23 | 24 | using GLPlot;GLPlot.init() 25 | glplot((loadasset("cat.obj"), p), scale=s, rotation=r, color=c) 26 | -------------------------------------------------------------------------------- /example/volume.jl: -------------------------------------------------------------------------------- 1 | using GLPlot, GLVisualize, GLAbstraction, Colors, GeometryTypes, Plots, FileIO 2 | using Reactive, GLWindow 3 | GLPlot.init() 4 | glvisualize() 5 | # load a volume 6 | vol = load(joinpath(homedir(), "Desktop", "brain.nii")).raw; 7 | vol = vol ./ maximum(vol); 8 | 9 | # plot it with blue colormap 10 | p1 = plot(vol, fill = colormap("Blues", 7)) 11 | 12 | # prepare the slices 13 | axes = ntuple(i-> linspace(0, 1, size(vol, i)), 3); 14 | p2 = heatmap(vol[100, : , :], title = "X Slice", labels = false); 15 | p3 = heatmap(vol[:, 100 , :], title = "Y Slice", labels = false); 16 | p4 = heatmap(vol[:, : , 100], title = "Z Slice", labels = false); 17 | 18 | plt = plot(p1, p2, p3, p4); 19 | gui() 20 | 21 | for i=1:3 22 | # since plots updating mechanism still doesn't work perfectly with GLVisualize 23 | # we need to get the raw visualization objects and gpu objects from the plots. 24 | # This will be exposed by a more straightforward API in the future! 25 | robj = plt[i+1].o.renderlist[1][end] 26 | tex = robj[:intensity] # image slice residing on the GPU 27 | range_s = play_widget(1:size(vol, i)) 28 | preserve(map(range_s) do slice_idx 29 | idx = ntuple(d-> d==i ? slice_idx : (:), 3) 30 | # This conversion is necessary but will be automatic soon! 31 | slice = permutedims(map(Intensity{1, Float32}, vol[idx...]), (2,1)) 32 | GLAbstraction.update!(tex, slice) # upload to memory 33 | end) 34 | end 35 | -------------------------------------------------------------------------------- /example/rgb_volume_slice.jl: -------------------------------------------------------------------------------- 1 | using GLPlot, GLVisualize, GLAbstraction, Colors, GeometryTypes, Plots, FileIO 2 | using Reactive, GLWindow 3 | import Quaternions: qrotation 4 | 5 | GLPlot.init() 6 | # load a volume 7 | N = 100 8 | ranges = ntuple(i-> linspace(-2f0, 2f0, N), 3) 9 | xr = ranges[1] 10 | vol = map(((x, y, z) for x=xr, y=xr, z=xr)) do xyz 11 | RGBA(sin(xyz[1]), cos(xyz[2]), sqrt(1 + sin(xyz[3])*cos(xyz[3]))) 12 | end 13 | 14 | # prepare the slices 15 | 16 | slices = ntuple(3) do i 17 | range_s = play_widget(1:size(vol, i)) 18 | slice = map(range_s) do j 19 | vol[ifelse(i == 1, j, :), ifelse(i == 2, j, :) , ifelse(i == 3, j, :)] 20 | end 21 | slice, range_s 22 | end 23 | 24 | rotations = ( 25 | Mat4f0(qrotation(unit(Vec3f0, 1), Float32(pi/2))), 26 | rotationmatrix_x(Float32(pi/2)) * Mat4f0(qrotation(unit(Vec3f0, 2), Float32(pi/2))), 27 | rotationmatrix_z(Float32(pi/2)) 28 | ) 29 | 30 | plots = map(1:3) do i 31 | rot = rotations[i] 32 | slice, slider = slices[i] 33 | axis = unit(Vec3f0, 1) 34 | model = map(slider) do r 35 | trans = unit(Vec3f0, mod1(3-i, 3)) * ranges[i][r] 36 | translationmatrix(trans) * rot 37 | end 38 | rs = ranges[mod1(i + 1, 3)], ranges[mod1(i + 2, 3)] 39 | glplot( 40 | slice, 41 | model = model, 42 | primitive = SimpleRectangle(-2, -2, 4, 4), # TODO, this is an inconsistency in the API and should also just accept ranges kw_arg 43 | preferred_camera = :perspective, 44 | ) 45 | end 46 | -------------------------------------------------------------------------------- /example/hover_unicode.jl: -------------------------------------------------------------------------------- 1 | using GLPlot;GLPlot.init() 2 | using Plots;glvisualize() 3 | using GLVisualize, FileIO, Colors, Images 4 | 5 | imfolder = filter(readdir(GLVisualize.assetpath())) do path 6 | endswith(path, "jpg") || endswith(path, "png") 7 | end 8 | images = map(imfolder) do impath 9 | map(RGBA{U8}, restrict(loadasset(impath))).data 10 | end; 11 | 12 | mean_colors = map(images) do img 13 | mean(img) 14 | end 15 | x = map(comp1, mean_colors); y=map(comp2, mean_colors); z = map(comp3, mean_colors); 16 | p1 = scatter( 17 | x,y,z, markercolor=mean_colors, 18 | shape = :circle, markerstrokewidth = 1, markerstrokecolor = "white", 19 | hover = images, ms = 12 20 | ) 21 | p2 = scatter(x,y,z, markerstrokecolor = mean_colors, shape = images, ms = 15) 22 | plot(p1, p2) 23 | gui() 24 | 25 | 26 | using Plots;glvisualize() 27 | using GLVisualize, FileIO, Colors, Images 28 | using GeometryTypes, GLAbstraction 29 | 30 | x = ["(。◕‿◕。)", "◔ ⌣ ◔","(づ。◕‿‿◕。)づ", "┬──┬ ノ( ゜-゜ノ)", "(╯°□°)╯︵ ┻━┻", "¯\\_(ツ)_/¯"] 31 | y = [-4, -3, -2, -1, 6, 0] 32 | scatter(x, y) 33 | t = bounce(linspace(-1.0f0,1f0, 20)) 34 | translation = map(t) do t 35 | rotationmatrix_y(1f0)*rotationmatrix_z(deg2rad(90f0)) * translationmatrix(Vec3f0(3,3,t)) 36 | end 37 | hover = [ 38 | "Plotting\nsomething!", 39 | "Something\nscientific?", 40 | "For once?", 41 | visualize(loadasset("cat.obj"), model=translation), 42 | "Noo! That\ncat again!", 43 | "😸$(Char(0x1F63B))", 44 | ] 45 | scatter(x, y, m=(0.8, :diamond, 20), hover=hover) 46 | 47 | title!("ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ 2H₂ + O₂ ⇌ 2H₂O") 48 | xaxis!(" ⠍⠊⠣ ახლავე გაიაროთ все вещи, вы можете построить") 49 | yaxis!("Anger") 50 | -------------------------------------------------------------------------------- /example/lorenz.jl: -------------------------------------------------------------------------------- 1 | using GeometryTypes, Reactive, GLVisualize 2 | using GLPlot;GLPlot.init() 3 | 4 | 5 | """ 6 | Lorenz function 7 | """ 8 | function lorenz(t0, a, b, c, h) 9 | Point3f0( 10 | t0[1] + h * a * (t0[2] - t0[1]), 11 | t0[2] + h * (t0[1] * (b - t0[3]) - t0[2]), 12 | t0[3] + h * (t0[1] * t0[2] - c * t0[3]), 13 | ) 14 | end 15 | # step through the `time` 16 | function lorenz(array::Vector, a=5.0,b=2.0,c=6.0,d=0.01) 17 | t0 = Point3f0(0.1, 0, 0) 18 | for i=eachindex(array) 19 | t0 = lorenz(t0, a,b,c,d) 20 | array[i] = t0 21 | end 22 | array 23 | end 24 | 25 | # create sliders to interact with the parameters of the lorenz function 26 | slidera = GLPlot.play_widget(linspace(0.1f0, 40f0, 30)) 27 | sliderb = GLPlot.play_widget(linspace(0.1f0, 40f0, 30)) 28 | sliderc = GLPlot.play_widget(linspace(0.1f0, 40f0, 30)) 29 | sliderd = GLPlot.play_widget(linspace(0.001f0, 0.5f0, 30)) 30 | 31 | 32 | # N time steps 33 | n = 100_000 34 | # foldp registers a callback (in this case lorenz), which updates the points 35 | # whenever the slider changes. 36 | points = foldp(lorenz, Array(Point3f0, n), slidera, sliderb, sliderc, sliderd) 37 | # we set the boundingbox to nothing, since we don't need it and don't want to 38 | # calculate it for every update (which is the default) 39 | glplot(points, :lines, boundingbox=nothing, preferred_camera=:perspective) 40 | # you can also visualize the points 41 | glplot((Circle(Point2f0(0), 0.01f0), points), boundingbox=nothing, preferred_camera=:perspective) 42 | # the above renders an antia aliased nice looking circle which could have outlines and glows. 43 | # this makes it relatively slow. For optimal performance, one might need the command below 44 | # which draws one (or n) pixel per point 45 | #glplot(points, :speed, boundingbox=nothing, preferred_camera=:perspective) 46 | -------------------------------------------------------------------------------- /example/volume_slice.jl: -------------------------------------------------------------------------------- 1 | using GLPlot, GLVisualize, GLAbstraction, Colors, GeometryTypes, Plots, FileIO 2 | using Reactive, GLWindow 3 | import Quaternions: qrotation 4 | 5 | GLPlot.init() 6 | # load a volume 7 | vol = load(joinpath(homedir(), "Desktop", "brain.nii")).raw; 8 | vol = vol ./ maximum(vol); 9 | 10 | # prepare the slices 11 | ranges = ntuple(i-> linspace(-2f0, 2f0, size(vol, i)), 3) 12 | slices = ntuple(3) do i 13 | range_s = play_widget(1:size(vol, i)) 14 | slice = map(range_s) do j 15 | s = vol[ifelse(i == 1, j, :), ifelse(i == 2, j, :) , ifelse(i == 3, j, :)] 16 | reinterpret(Intensity{1, Float32}, s) 17 | end 18 | slice, range_s 19 | end 20 | 21 | rotations = ( 22 | Mat4f0(qrotation(unit(Vec3f0, 1), Float32(pi/2))), 23 | rotationmatrix_x(Float32(pi/2)) * Mat4f0(qrotation(unit(Vec3f0, 2), Float32(pi/2))), 24 | rotationmatrix_z(Float32(pi/2)) 25 | ) 26 | 27 | plots = map(1:3) do i 28 | rot = rotations[i] 29 | slice, slider = slices[i] 30 | axis = unit(Vec3f0, 1) 31 | model = map(slider) do r 32 | trans = unit(Vec3f0, mod1(3-i, 3)) * ranges[i][r] 33 | translationmatrix(trans) * rot 34 | end 35 | rs = ranges[mod1(i + 1, 3)], ranges[mod1(i + 2, 3)] 36 | glplot( 37 | slice, 38 | model = model, 39 | ranges = rs, 40 | preferred_camera = :perspective, 41 | stroke_width = 0.0f0, 42 | stroke_color = RGBA{Float32}(0,0,0,0.4), 43 | color_norm = Vec2f0(0, 1) 44 | ) 45 | end 46 | 47 | # low level hack to share attributes... Gotta figure out a nice API for this 48 | attributes = GLPlot.to_edit_dict(plots[1].children[]) 49 | for p in plots[2:end] 50 | for (k, v) in attributes 51 | k == :intensity && continue # don't share the intensity values 52 | p.children[].uniforms[k] = v 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /example/mandelbulb.jl: -------------------------------------------------------------------------------- 1 | using Plots, GLPlot; GLPlot.init() 2 | using Reactive, GeometryTypes, Colors, GLAbstraction 3 | 4 | function mandelbulb{T}(x0::T,y0::T,z0::T, n, iter) 5 | x,y,z = x0,y0,z0 6 | for i=1:iter 7 | r = sqrt(x*x + y*y + z*z) 8 | theta = atan2(sqrt(x*x + y*y) , z) 9 | phi = atan2(y,x) 10 | rn = r^n 11 | x1 = rn * sin(theta*n) * cos(phi*n) + x0 12 | y1 = rn * sin(theta*n) * sin(phi*n) + y0 13 | z1 = rn * cos(theta*n) + z0 14 | (x1*x1 + y1*y1 + z1*z1) > n && return T(i) 15 | x,y,z = x1,y1,z1 16 | end 17 | T(iter) 18 | end 19 | 20 | dims = (100, 100, 100) 21 | x, y, z = ntuple(3) do i 22 | # linearly spaced array (not dense) from -1 to 1 23 | reshape(linspace(-1f0, 1f0, dims[i]), ntuple(j-> j == i ? dims[i] : 1, 3)) 24 | end 25 | volume = zeros(Float32, dims) 26 | # create two sliders to interact with the 2 parameters of the mandelbulb function 27 | itslider = GLPlot.play_widget(1:15); 28 | nslider = GLPlot.play_widget(linspace(1f0,30f0, 100)); 29 | 30 | 31 | # register a callback to the sliders with map ("map over updates") 32 | mandelvol_s = map(nslider, itslider) do n, it 33 | volume .= mandelbulb.(x, y, z, n, it) 34 | end 35 | 36 | 37 | glplot(mandelvol_s, color_norm = Vec2f0(0, 50)) 38 | 39 | function iso_particle(v, isoval) 40 | particles = Point3f0[] 41 | sz = 1f0./(Point3f0(size(v))-1f0) 42 | @inbounds for z=1:size(v, 3), y=1:size(v, 2), x=1:size(v, 1) 43 | if abs(v[x,y,z] - isoval) <= 0.001f0 44 | push!(particles, (Point3f0(x-1f0,y-1f0,z-1f0)).*sz) 45 | end 46 | end 47 | particles 48 | end 49 | using GLAbstraction, Meshing 50 | isoval = GLPlot.play_widget(1:50); 51 | particles = async_map2(iso_particle, Point3f0[], volume[2], isoval); 52 | pp = GLBuffer(particles[2]) 53 | glplot((Sphere(Point2f0(0), 0.005f0),pp), boundingbox=nothing, color=pp) 54 | mesh = GLNormalMesh(volume[2].value, 4f0) 55 | map!(mesh.vertices) do v 56 | (v-1f0) ./ 298f0 57 | end 58 | glplot(mesh) 59 | 60 | using GLAbstraction 61 | -------------------------------------------------------------------------------- /example/boids.jl: -------------------------------------------------------------------------------- 1 | #---------------------------------------------------------------------- 2 | # Boids algorithm visualization 3 | # Based on pseudocode at http://www.kfish.org/boids/pseudocode.html 4 | # Simulate flocking birds using simple rules. 5 | # Initial version developed and contributed by Iain Dunning (@IainNZ) 6 | # as part of the JuliaCon 2015 workshop 7 | # TODO: 8 | # - Add interactivity for parameters that govern behavior. 9 | # - Add a force that attracts to mouse pointer. 10 | #---------------------------------------------------------------------- 11 | using Reactive, GeometryTypes, GLAbstraction, GLPlot, GLVisualize, Colors, ColorTypes 12 | GLPlot.init() 13 | #---------------------------------------------------------------------- 14 | typealias Position Point{2, Float32} 15 | typealias Velocity Vec{2, Float32} 16 | 17 | immutable Boids 18 | position::Vector{Position} 19 | velocity::Vector{Velocity} 20 | end 21 | 22 | # Create new boid at random location and velocity 23 | Boids(n=200) = Boids([rand(Position) for i=1:n], [rand(Velocity)/1000 for i=1:n]) 24 | 25 | #---------------------------------------------------------------------- 26 | 27 | function simulate!(t, boids) 28 | len = Float32(length(boids.position)) 29 | boid_center = sum(boids.position)/len 30 | @inbounds for (i, vel) in enumerate(boids.velocity) 31 | boids.velocity[i] = vel + Vec(boid_center - boids.position[i])/600 32 | end 33 | 34 | # Force 2: Avoid others 35 | @inbounds for (i, boidpos) in enumerate(boids.position) 36 | avoidance = zero(Velocity) 37 | for (j, other_boidpos) in enumerate(boids.position) 38 | i == j && continue 39 | if norm(boidpos - other_boidpos) <= 0.2 40 | avoidance -= Vec(other_boidpos - boidpos) 41 | end 42 | end 43 | bvel = boids.velocity[i] 44 | boids.velocity[i] = bvel + (avoidance/1000f0) 45 | end 46 | @inbounds for (i, bvel) in enumerate(boids.velocity) 47 | perceived_vel = zero(Velocity) 48 | for (j, other_boidvel) in enumerate(boids.velocity) 49 | i == j && continue 50 | perceived_vel += other_boidvel 51 | end 52 | perceived_vel /= len-1f0 53 | boids.velocity[i] = bvel + (perceived_vel - bvel)/900f0 54 | end 55 | # Limit max velocity 56 | MAXVEL = 0.5f0 57 | @inbounds for (i, vel) in boids.velocity 58 | absvel = norm(vel) 59 | if absvel >= MAXVEL 60 | boids.velocity[i] = vel / absvel*MAXVEL 61 | end 62 | end 63 | # Update positions 64 | @inbounds for (i, vel) in enumerate(boids.velocity) 65 | boids.position[i] = boids.position[i] + Point(vel) 66 | end 67 | boids.position, boids.velocity 68 | end 69 | Base.clamp{T}(x::T) = clamp(x, zero(T), one(T)) 70 | 71 | to_color(velocities, mul) = RGBA{U8}[RGBA{U8}(clamp(velocity[1]*mul), clamp(velocity[2]*mul), 1.0, 0.8) for velocity in velocities] 72 | function main() 73 | # Create population of boids 74 | boids = Boids() 75 | pv = map(simulate!, bounce(0:1000), Signal(boids)) 76 | positions = map(first, pv) 77 | velocity = map(pv) do pv 78 | map(norm, pv[2]) 79 | end 80 | glplot( 81 | (Circle, positions), scale = Vec2f0(0.05), 82 | intensity = velocity, 83 | color_map = GLVisualize.default(Vector{RGBA}), 84 | color_norm = Vec2f0(0, 0.02) 85 | ) 86 | 87 | end 88 | 89 | 90 | x = main() 91 | -------------------------------------------------------------------------------- /example/sliders.jl: -------------------------------------------------------------------------------- 1 | using GLPlot, GLAbstraction, GeometryTypes, Colors; GLPlot.init() 2 | using GLWindow, Reactive, ModernGL, GLVisualize 3 | 4 | f(x,y,a,b,c) = sin(x*a)./b^cos(y/c) 5 | 6 | 7 | function init(n, a, b, c) 8 | x = linspace(-4f0, 4f0, n) 9 | # map the slider signals and great a matrix 10 | # this foldp is geared towards performance, which is why it mutates v0 in place 11 | # it's also fairly performant to do this: 12 | # map((a,b,c)-> [f(x,y,a,b,c) for x=x, y=x], a,b,c) 13 | v0 = Float32[f(x,y,value(a),value(b),value(c)) for x=x, y=x] 14 | surface = foldp(v0, a,b,c) do v0, a,b,c 15 | @inbounds for i=1:n, j=1:n 16 | v0[i,j] = f(x[i],x[j],a,b,c) 17 | end 18 | v0 19 | end; 20 | # plots surface 21 | surface_robj = glplot( 22 | surface, :surface, 23 | ranges=(x,x), boundinbox=nothing # doesn't calculate boundingbox, which is faster 24 | ).children[] 25 | #plot the grid, starting at -4,-4,-2, with width 8,8,5 26 | glplot(AABB(Vec3f0(-4,-4,-2), Vec3f0(8,8,5)), :grid) 27 | # Now we draw the controle lines. This should be available as a widget in the Future! 28 | # allocate an array for point position (performance optimization as well) 29 | pos_tmp = Point3f0[0] 30 | w = GLPlot.viewing_screen() 31 | m2id = mouse2id(w) 32 | pin_plot = doubleclick(w.inputs[:mouse_buttons_pressed], 0.1) 33 | index = droprepeats(foldp(((0,0), false), m2id, pin_plot) do v0, m2id, pin 34 | idx, waspinned = v0 35 | waspinned && !pin && return v0 36 | if m2id.id == surface_robj.id 37 | if m2id.index >= 1 && m2id.index <= n*n 38 | return ind2sub((n,n), m2id.index), pin 39 | end 40 | end 41 | idx, pin 42 | end) 43 | p_l_position = map(surface, index) do data, ij_pinned 44 | ij, _ = ij_pinned 45 | if ij != (0,0) 46 | slicex = Point3f0[(x[_x], x[ij[2]], data[_x, ij[2]]) for _x=1:n] 47 | slicey = Point3f0[(x[ij[1]], x[y], data[ij[1], y]) for y=1:n] 48 | pos_tmp[1] = (x[ij[1]], x[ij[2]], data[ij...]) 49 | return vcat(slicex, Point3f0(Inf), slicey), pos_tmp 50 | end 51 | Point3f0[], pos_tmp 52 | end 53 | linepos = map(first, p_l_position) 54 | _view(visualize( 55 | linepos, :lines, 56 | prerender=()->glDisable(GL_DEPTH_TEST), # draw over other items 57 | postrender=()->glEnable(GL_DEPTH_TEST) 58 | ), GLPlot.viewing_screen(), camera=:perspective) 59 | point = map(last, p_l_position) 60 | _view(visualize( 61 | (Circle(Point2f0(0), 0.05f0), point), 62 | color = RGBA{Float32}(0.99, 0, 0.1), 63 | billboard = true, 64 | prerender=()->glDisable(GL_DEPTH_TEST), # draw over other items 65 | postrender=()->glEnable(GL_DEPTH_TEST) 66 | ), GLPlot.viewing_screen(), camera=:perspective) 67 | text = map(point) do pa 68 | p = pa[] # is an array 69 | _x = @sprintf("% 0.3f", p[1]) 70 | _y = @sprintf("% 0.3f", p[2]) 71 | _z = @sprintf("% 0.3f", p[3]) 72 | "x:$_x,y:$_y,z: $_z" 73 | end 74 | vis = visualize(text, color=RGBA{Float32}(0.7,0.7,0.7)) 75 | add_widget!(vis) 76 | end 77 | 78 | # Create sliders for the surface 79 | a = GLPlot.play_widget(linspace(-4f0,4f0, 50)); 80 | b = GLPlot.play_widget(linspace(1.5f0,6f0, 50)); 81 | c = GLPlot.play_widget(linspace(-4f0,4f0, 50)); 82 | # create surface and controle points/lines 83 | init(100, a, b, c) 84 | -------------------------------------------------------------------------------- /example/SignalProcessing/imfilter_pipeline.jl: -------------------------------------------------------------------------------- 1 | using VideoIO, Colors, GLVisualize, Reactive, Images, GLAbstraction, GeometryTypes 2 | using ImageFeatures 3 | using GLPlot, Plots, GLWindow; GLPlot.init(); glvisualize() 4 | 5 | function playv(buffer, video_stream, t) 6 | eof(video_stream) && seekstart(video_stream) 7 | _,w,h = size(buffer) 8 | read!(video_stream, buffer) 9 | return map(RGB{Float32}, restrict(restrict(reinterpret(RGB{U8}, buffer, (w,h))))) 10 | end 11 | function topoints(v0, img) 12 | @inbounds for i in eachindex(img) 13 | x = img[i] 14 | v0[i] = Point3f0(x.r, x.g, x.b) 15 | end 16 | v0 17 | end 18 | 19 | 20 | function setup_pipeline() 21 | window = GLPlot.viewing_screen 22 | f = VideoIO.opencamera() 23 | img1 = read(f) 24 | vw,vh = size(img1, 2), size(img1, 3) 25 | img_scaled = map(RGB{Float32}, restrict(restrict(reinterpret(RGB{U8}, img1, (vw, vh))))) 26 | imw, imh = size(img_scaled) 27 | buffer = zeros(UInt8, 3, vw,vh) 28 | t = bounce(1:10) 29 | imstream = map(playv, Signal(buffer), Signal(f), t) 30 | imw *= 2 31 | img_area = map(window.area) do x 32 | SimpleRectangle(x.w-imw, 0, imw, x.h) 33 | end 34 | point_area = map(window.area) do x 35 | w,h = x.w-imw, div(x.h, 2) 36 | SimpleRectangle(0, x.h-h, w, h) 37 | end 38 | plot_area = map(window.area) do x 39 | w,h = x.w-imw, div(x.h, 2) 40 | SimpleRectangle(0, 0, w, h) 41 | end 42 | 43 | point_screen = Screen(window, area=point_area) 44 | plot_screen = Screen(window, area=plot_area) 45 | img_screen = Screen(window, area=img_area) 46 | GLVisualize.add_screen(plot_screen) 47 | s1 = play_widget(linspace(1f0, 10f0, 50)) 48 | s2 = play_widget(linspace(0f0, 1f0, 100)) 49 | s3 = play_widget(linspace(0f0, 1f0, 100)) 50 | keypoint_thresh = play_widget(linspace(0.0, 1.0, 100)) 51 | 52 | 53 | points = foldp(topoints, zeros(Point3f0, length(value(imstream))), imstream); 54 | colors = map(vec, imstream); 55 | glplot(points, :speed, screen=point_screen, color=colors, boundingbox=nothing); 56 | 57 | 58 | grayed = map(imstream) do img 59 | convert(Matrix{Gray{Float32}}, img) 60 | end 61 | 62 | keypoints = map(grayed, keypoint_thresh) do img, tresh 63 | kp = Keypoints(fastcorners(img, 500, tresh)) 64 | w,h = size(img) 65 | map(kp) do x 66 | p = x.I 67 | Point2f0(p[1], h-p[2]) 68 | end 69 | end 70 | 71 | canned = map(grayed, s1, s2, s3) do img, gamma, lower, upper 72 | canny(img, gamma, upper, lower) 73 | end 74 | visses = map(x->visualize(x, model=eye(Mat4f0)), [imstream, grayed, canned]) 75 | _view( 76 | visualize(visses, direction=2), 77 | img_screen, camera=:orthographic_pixel 78 | ) 79 | grayedrobj = visses[2].children[] 80 | 81 | keypointvis = visualize( 82 | (Circle(Point2f0(0), 2f0), keypoints), 83 | color=RGBA{Float32}(0,0,0,0.3), stroke_width=2f0, 84 | stroke_color=RGBA{Float32}(1,1,1,1), 85 | model=grayedrobj[:model], boundingbox=nothing 86 | ) 87 | _view(keypointvis, img_screen, camera=:orthographic_pixel) 88 | register_plot!(keypointvis, img_screen) 89 | 90 | n = 80 91 | xls = linspace(0,1,n) 92 | yls = linspace(0, 10_000, n) 93 | pl = plot([xls,xls,xls],[yls,yls,yls], color=[:red :green :blue]) 94 | gui() 95 | histps = plot_screen.children[1].renderlist[1][end-2:end] 96 | preserve(map(imstream) do img 97 | seps = separate(img) 98 | for i=1:3 99 | x,y = imhist(view(seps, :, :, i), n, 0.0, 1.0) 100 | mn = min(length(x), length(y)) 101 | new_vals = map(Point2f0, zip(view(x, 1:mn), view(y, 1:mn))) 102 | set_arg!(histps[i], :vertex, new_vals) 103 | end 104 | nothing 105 | end) 106 | end 107 | setup_pipeline() 108 | GLPlot.block() 109 | -------------------------------------------------------------------------------- /src/gui.jl: -------------------------------------------------------------------------------- 1 | function add_drag(w, range, point_id, slider_length, slideridx_s) 2 | m2id = mouse2id(w) 3 | # interaction 4 | @materialize mouse_buttons_pressed, mouseposition = w.inputs 5 | isoverpoint = const_lift(is_same_id, m2id, point_id) 6 | # single left mousekey pressed (while no other mouse key is pressed) 7 | key_pressed = const_lift(GLAbstraction.singlepressed, mouse_buttons_pressed, GLFW.MOUSE_BUTTON_LEFT) 8 | # dragg while key_pressed. Drag only starts if isoverpoint is true 9 | mousedragg = GLAbstraction.dragged(mouseposition, key_pressed, isoverpoint) 10 | preserve(foldp(value(slideridx_s), mousedragg) do v0, dragg 11 | if dragg != Vec2f0(0) 12 | idx_steps = round(Int, (dragg[1]/slider_length)*length(range)) 13 | new_idx = clamp(v0 + idx_steps, 1, length(range)) 14 | push!(slideridx_s, new_idx) 15 | return v0 16 | else # dragging started 17 | return value(slideridx_s) 18 | end 19 | end) 20 | slideridx_s 21 | end 22 | function add_play(slideridx_s, play_signal, range, rate=30.0) 23 | play_s = fpswhen(play_signal, rate) 24 | preserve(map(play_s, init=nothing) do t 25 | push!(slideridx_s, mod1(value(slideridx_s)+1, length(range))) 26 | nothing 27 | end) 28 | end 29 | 30 | function maxdigits(range) 31 | if eltype(range) <: AbstractFloat 32 | return 7 33 | else 34 | return max(ndigits(first(range)), ndigits(last(range))) 35 | end 36 | end 37 | function play_widget( 38 | range, window = GLPlot.widget_screen!(); 39 | startidx::Int=1 40 | ) 41 | numberbox = map(icon_size()) do is 42 | AABB(Vec3f0(0, -1mm, 0), Vec3f0(2is, is, 1)) 43 | end 44 | target_s = value(icon_size()) * 0.4 45 | scale = target_s 46 | sliderlen = 70mm-(3 * value(icon_size())) - 2mm 47 | play_button, play_stop_signal = GLVisualize.toggle_button( 48 | rot180(GLPlot.imload("play.png")), GLPlot.imload("break.png"), window 49 | ) 50 | play_s = map(!, play_stop_signal) 51 | slider_w, slider_s = GLVisualize.slider(range, window, 52 | startidx = startidx, play_signal = play_s, 53 | slider_length = sliderlen 54 | ) 55 | number = visualize( 56 | map(GLVisualize.printforslider, slider_s), 57 | color = RGBA{Float32}(0.6, 0.6, 0.6,1), 58 | boundingbox = numberbox, 59 | relative_scale = scale 60 | ) 61 | add_widget!(play_button, number, slider_w, window=window) 62 | 63 | slider_s 64 | end 65 | export play_widget 66 | export add_widget! 67 | function add_widget!(widgets...; 68 | delete = Signal(false), 69 | window = widget_screen!(delete = delete), 70 | height = value(icon_size()) 71 | ) 72 | scalings = map(widgets) do widget 73 | bb = value(boundingbox(widget)) 74 | w = widths(bb) 75 | if w[2] > height # only scale when to big 76 | s = height/w[2] 77 | w.*s, minimum(bb) 78 | else 79 | w, minimum(bb) 80 | end 81 | end 82 | last_x = 0f0 83 | foreach(zip(scalings, widgets)) do scaling_widget 84 | scaling, widget = scaling_widget 85 | scale, offset = scaling 86 | xwidth = scale[1]-1mm 87 | place = SimpleRectangle{Float32}(last_x-offset[1]+0.5mm, -offset[2]+0.5mm, xwidth, scale[2]-1mm) 88 | layout!(place, widget) 89 | _view(widget, window, camera=:fixed_pixel) 90 | last_x += xwidth 91 | end 92 | end 93 | 94 | 95 | function item_area(la, deleted, item_height) 96 | y = la.y-item_height-2 97 | deleted && return SimpleRectangle(la.x, la.y, la.w, 0) 98 | return SimpleRectangle(la.x, y, la.w, item_height) 99 | end 100 | 101 | function widget_screen!(parentscreen = edit_screen(); left_gap=1.5mm, delete=Signal(false)) 102 | scroll = parentscreen.inputs[:menu_scroll] 103 | if isempty(parentscreen.children) 104 | last_area = map(parentscreen.area, icon_size(), scroll) do a, ih, s 105 | return SimpleRectangle{Int}(left_gap, a.h-ih+s, a.w-left_gap, ih) 106 | end 107 | else 108 | last_area = last(parentscreen.children).area 109 | end 110 | itemarea = map(GLPlot.item_area, last_area, delete, icon_size()) 111 | Screen(parentscreen, area=itemarea) 112 | end 113 | 114 | 115 | function choices(x::Vector) 116 | signal, vis = GLVisualize.choice_widget(x, edit_screen(), area=(48mm, 8mm)) 117 | w = widget_screen!() 118 | _view(vis, w, camera=:fixed_pixel) 119 | signal 120 | end 121 | 122 | 123 | function edit_toggle() 124 | edit_button, no_edit_signal = toggle_button( 125 | imload("play.png"), rotr90(imload("play.png")), edit_screen() 126 | ) 127 | end 128 | 129 | function visible_toggle() 130 | toggle_button( 131 | imload("showing.png"), imload("notshowing.png"), edit_screen() 132 | ) 133 | end 134 | function delete_toggle() 135 | delete_button, del_signal = button( 136 | imload("delete.png"), edit_screen() 137 | ) 138 | end 139 | 140 | 141 | 142 | function async_map2(f, init, inputs...; typ=typeof(init)) 143 | node = Signal(typ, init, inputs) 144 | worker_task = @async init 145 | map(inputs...) do args... 146 | outer_task = current_task() 147 | hasworked = istaskdone(worker_task) # 148 | if istaskdone(worker_task) # 149 | worker_task = @async begin 150 | try 151 | inner_worker = @async begin 152 | x = f(args...) 153 | push!(node, x) 154 | end 155 | wait(inner_worker) 156 | catch err 157 | Base.throwto(outer_task, CapturedException(err, catch_backtrace())) 158 | end 159 | end 160 | end 161 | worker_task 162 | end, node 163 | end 164 | -------------------------------------------------------------------------------- /src/screen.jl: -------------------------------------------------------------------------------- 1 | function handle_drop(files::Vector{String}) 2 | for f in files 3 | try 4 | glplot(load(f)) 5 | catch e 6 | warn(e) 7 | end 8 | end 9 | end 10 | 11 | toolbar_area(pa, toolbar_width) = SimpleRectangle{Int}(0, 0, toolbar_width, pa.h) 12 | 13 | function viewing_area(area_l, area_r) 14 | SimpleRectangle{Int}(area_l.x+area_l.w, 0, area_r.x-area_l.w, area_r.h) 15 | end 16 | function edit_rectangle(visible, area, tarea) 17 | w = visible ? 70mm : 1.5mm 18 | x = area.w-w 19 | SimpleRectangle{Int}(x, 0, w, area.h) 20 | end 21 | function edit_item_area(la, item_height, left_gap) 22 | y = la.y-item_height-2 23 | return SimpleRectangle{Int}(left_gap, y, la.w, item_height) 24 | end 25 | 26 | layout_pos_ho(i) = map(icon_size()) do ip 27 | SimpleRectangle{Float32}(0, i*ip + i*2, ip, ip) 28 | end 29 | layout_pos_ver(i, border) = map(icon_size()) do ip 30 | SimpleRectangle{Float32}(i*ip + i*border, 0, ip, ip) 31 | end 32 | 33 | 34 | 35 | 36 | function save_record(frames) 37 | path = joinpath(homedir(), "Desktop") 38 | GLVisualize.create_video(frames, "test.webm", path, 0) 39 | end 40 | 41 | const _compute_callbacks = [] 42 | register_compute(f) = push!(_compute_callbacks, f) 43 | poll_reactive() = (Base.n_avail(Reactive._messages) > 1) && Reactive.run_till_now() 44 | 45 | const plotting_screens = Screen[] 46 | 47 | viewing_screen() = (init(); plotting_screens[1]) 48 | edit_screen() = (init(); plotting_screens[2]) 49 | tool_screen() = (init(); plotting_screens[3]) 50 | 51 | function glplot_renderloop(window, compute_s, record_s) 52 | was_recording = false 53 | frames = [] 54 | i = 0 55 | Reactive.stop() 56 | yield() 57 | while isopen(window) 58 | tic() 59 | GLWindow.poll_glfw() # GLFW poll 60 | 61 | if Base.n_avail(Reactive._messages) > 0 62 | GLWindow.poll_reactive() # reactive poll 63 | GLWindow.poll_reactive() # two times for secondary signals 64 | record = !value(record_s) 65 | GLWindow.render_frame(window) 66 | GLWindow.swapbuffers(window) 67 | end 68 | if record 69 | push!(frames, screenbuffer(window)) 70 | elseif was_recording && !record 71 | save_record(frames) 72 | frames = [] 73 | gc() 74 | end 75 | yield() 76 | diff = (1/60) - toq() 77 | while diff >= 0.001 78 | tic() 79 | sleep(0.001) # sleep for the minimal amount of time 80 | diff -= toq() 81 | end 82 | was_recording = record 83 | end 84 | GLWindow.destroy!(window) 85 | 86 | end 87 | 88 | const _icon_size = Signal(10mm) 89 | 90 | function icon_size() 91 | _icon_size 92 | end 93 | 94 | function init() 95 | if !isempty(plotting_screens) && isopen(first(plotting_screens)) 96 | return # already initialized 97 | end 98 | empty!(plotting_screens) 99 | w = glscreen("GLPlot") 100 | 101 | preserve(map(handle_drop, w.inputs[:dropped_files])) 102 | 103 | w.inputs[:key_pressed] = const_lift(GLAbstraction.singlepressed, 104 | w.inputs[:mouse_buttons_pressed], 105 | GLFW.MOUSE_BUTTON_LEFT 106 | ) 107 | button_pos = map(w.area) do a 108 | Point2f0[(0, a.h/2)] 109 | end 110 | button_width = 1.5mm 111 | edit_screen_show_button = visualize( 112 | (SimpleRectangle{Float32}(0, 0, button_width, button_width*2), button_pos), 113 | offset=Vec2f0(0, -button_width), 114 | color=RGBA{Float32}(0.6,0.6,0.6,1) 115 | ) 116 | tarea = map(toolbar_area, w.area, icon_size()) 117 | 118 | show_edit_screen = toggle(edit_screen_show_button, w, false) 119 | edit_screen_area = map(edit_rectangle, 120 | show_edit_screen, w.area, tarea 121 | ) 122 | 123 | 124 | viewing_screen = Screen(w, 125 | name = Symbol("Viewing Screen"), 126 | area = map(viewing_area, tarea, edit_screen_area), 127 | color = RGBA{Float32}(1,1,1,1) 128 | ) 129 | toolbar_screen = Screen(w, area=tarea) 130 | edit_screen = Screen( 131 | w, area = edit_screen_area, 132 | color = RGBA{Float32}(0.9,0.9,0.9,1) 133 | ) 134 | push!(plotting_screens, viewing_screen) 135 | push!(plotting_screens, edit_screen) 136 | push!(plotting_screens, toolbar_screen) 137 | 138 | _view(edit_screen_show_button, edit_screen, camera=:fixed_pixel) 139 | play_record, record_sig = toggle_button(imload("record.png"), imload("break.png"), w) 140 | compute_record, compute_sig = toggle_button(imload("play.png"), imload("break.png"), w) 141 | persp_ortho, persp_ortho_toggle_sig = toggle_button(imload("perspective.png"), imload("ortho.png"), w) 142 | persp_ortho_sig = map(persp_ortho_toggle_sig) do isp 143 | isp && return GLAbstraction.PERSPECTIVE 144 | GLAbstraction.ORTHOGRAPHIC 145 | end 146 | cube = cubecamera(viewing_screen, persp_ortho_sig) 147 | 148 | image_names = ["center", "screenshot"] 149 | tools = Matrix{BGRA{U8}}[imload("$name.png") for name in image_names] 150 | center_b, center_s = button(tools[1], w) 151 | screenshot_b, screenshot_s = button(tools[2], w) 152 | 153 | tools = [center_b, screenshot_b, play_record, persp_ortho, compute_record] 154 | tools_robjs = Any[] 155 | 156 | i = 0 157 | for tool in tools 158 | robj = layout!(layout_pos_ho(i), visualize(tool)) 159 | _view(robj, toolbar_screen, camera=:fixed_pixel) 160 | push!(tools_robjs, robj.children[]) 161 | i += 1 162 | end 163 | preserve(map(center_s) do pressed 164 | if pressed 165 | center!(viewing_screen) 166 | end 167 | nothing 168 | end) 169 | preserve(map(screenshot_s) do pressed 170 | if pressed 171 | screenshot(viewing_screen, path = joinpath(homedir(), "Desktop", "glplot.png")) 172 | end 173 | nothing 174 | end) 175 | rot = cube.children[][:model] 176 | 177 | cube.children[][:model] = map(rot, icon_size()) do r, ip 178 | half = ip/2 179 | translationmatrix(Vec3f0(half,i*ip + i*2 + half,0))*r*scalematrix(Vec3f0(half)) 180 | end 181 | _view(cube, toolbar_screen, camera=:fixed_pixel) 182 | 183 | GLVisualize.add_screen(viewing_screen) 184 | @materialize scroll, mouseposition = edit_screen.inputs 185 | should_scroll = map(mouseposition) do mb 186 | isinside(value(w.area), mb...) 187 | end 188 | scroll = filterwhen(should_scroll, value(scroll), scroll) 189 | edit_screen.inputs[:menu_scroll] = foldp(0, scroll) do v0, s 190 | v0+(ceil(Int, s[2])*15) 191 | end 192 | global _renderloop_task = @async glplot_renderloop(w, compute_sig, record_sig) 193 | viewing_screen 194 | end 195 | 196 | function block() 197 | global _renderloop_task 198 | wait(_renderloop_task) 199 | end 200 | -------------------------------------------------------------------------------- /src/plot.jl: -------------------------------------------------------------------------------- 1 | 2 | function glplot(arg1, style = :default; 3 | screen = viewing_screen(), camera = :perspective, 4 | kw_args... 5 | ) 6 | robj = visualize(arg1, style; kw_args...) 7 | _view(robj, screen, camera = camera) 8 | register_plot!(robj, screen) 9 | robj 10 | end 11 | 12 | 13 | function register_plot!(robj::Vector, screen = viewing_screen(); create_gizmo = false) 14 | vcat(map(robj) do elem 15 | register_plot!(elem, screen, create_gizmo = create_gizmo) 16 | end...) 17 | end 18 | function register_plot!(robj::Context, screen = viewing_screen(); create_gizmo = false) 19 | register_plot!(robj.children, screen, create_gizmo = create_gizmo) 20 | end 21 | 22 | function left_clicked(button::Set{Int}) 23 | return GLAbstraction.singlepressed(button, GLFW.MOUSE_BUTTON_LEFT) 24 | end 25 | 26 | function to_clip(p, width, proj_view) 27 | clip = proj_view * Vec4f0(p, 1f0) 28 | clip = clip ./ clip[4] 29 | nv = (clip + 1f0) * 0.5f0 30 | Point2f0(nv[1], nv[2]) .* Point2f0(width-1) 31 | end 32 | function gizmo(w, is_selected) 33 | colors = Array(RGBA{Float32}, 6) 34 | positions = Array(Point3f0, 6) 35 | for axis in 1:3 36 | c = RGBA{Float32}(ntuple(i->i==axis ? 1f0 : 0f0, 3)...) 37 | v = unit(Vec3f0, axis) 38 | positions[2(axis-1)+1] = Point3f0(0) 39 | positions[2(axis-1)+2] = v 40 | colors[2(axis-1)+1] = RGBA{Float32}(0,0,0,1) 41 | colors[2(axis-1)+2] = c 42 | end 43 | robj = visualize( 44 | positions, :linesegment, 45 | color=colors, 46 | prerender=()->glDisable(GL_DEPTH_TEST), # draw over other items 47 | postrender=()->glEnable(GL_DEPTH_TEST), 48 | thickness=10f0 49 | ).children[] 50 | _view(robj, camera = :perspective) 51 | @materialize mouseposition, mouse_buttons_pressed, buttons_pressed = w.inputs 52 | m2id = mouse2id(w) 53 | show_gizmo = map(buttons_pressed) do bp 54 | GLAbstraction.singlepressed(bp, GLFW.KEY_M) && value(is_selected) 55 | end 56 | set_arg!(robj, :visible, show_gizmo) 57 | start_drag = foldp((false, 0), m2id) do v0, id 58 | ison = id.id == robj.id && id.index > 0 && id.index <= 6 && value(show_gizmo) 59 | if ison 60 | return (true, id.index) 61 | else 62 | return (false, v0[2]) 63 | end 64 | end 65 | left_pressed = const_lift(GLAbstraction.pressed, mouse_buttons_pressed, GLFW.MOUSE_BUTTON_LEFT) 66 | view = w.cameras[:perspective].projectionview 67 | ddiff = GLAbstraction.dragged_diff(mouseposition, left_pressed, map(first, start_drag)) 68 | 69 | translation = foldp(Vec3f0(0), ddiff) do v0, diff 70 | idx = value(start_drag)[2] 71 | pv = value(view) 72 | if idx != 0 73 | i = ceil(Int, idx/2) 74 | uv = unit(Vec3f0, i) 75 | wh = widths(w) 76 | nv2d = to_clip(uv, wh, pv) 77 | origin = to_clip(Vec3f0(0), wh, pv) 78 | nv2d = nv2d-origin 79 | mv = -Vec2f0(diff).*10f0 80 | return v0 + (uv*(dot(mv, normalize(nv2d))./(norm(wh)))) 81 | end 82 | v0 83 | end 84 | model = map(translationmatrix, translation) 85 | set_arg!(robj, :model, model) 86 | model 87 | end 88 | 89 | """ 90 | There's a lot of noise if we just go through all parameters of a `RenderObject`. 91 | This function filters out the internal values 92 | """ 93 | function is_editable(k, v_v) 94 | v = value(v_v) 95 | !( 96 | k == :objectid || 97 | k == :is_fully_opaque || 98 | k == :instances || 99 | k == Symbol("position.multiplicator") || 100 | k == Symbol("position.dims") || 101 | k == Symbol("resolution") || 102 | k == Symbol("fxaa") || 103 | k == Symbol("light") || 104 | k == Symbol("light") || 105 | k == Symbol("doc_string") || 106 | k == Symbol("faces") || 107 | k == Symbol("vertices") || 108 | k == Symbol("texturecoordinates") || 109 | k == Symbol("ranges") || 110 | k == Symbol("model") || 111 | k == :visible || 112 | startswith(string(k), "boundingbox") || 113 | (k == Symbol("color") && isa(v, AbstractArray)) || 114 | k in fieldnames(PerspectiveCamera) || 115 | k == :instances || 116 | isa(v, Symbol) || 117 | isa(v, Void) || 118 | isa(v, NativeMesh) || 119 | isa(v, Int) || 120 | isa(v, Int32) || 121 | isa(v, UInt32) || 122 | isa(v, UInt) || 123 | (isa(v, FixedVector) && eltype(v) <: Integer) 124 | ) 125 | end 126 | function to_edit_dict(robj) 127 | filter(collect(robj.uniforms)) do kv 128 | is_editable(kv...) 129 | end 130 | end 131 | function register_plot!( 132 | robj::RenderObject, screen = viewing_screen(); 133 | create_gizmo = false 134 | ) 135 | left_gap = 1.5mm 136 | visible_button, visible_s = visible_toggle() 137 | set_arg!(robj, :visible, visible_s) 138 | delete_button, del_signal = button(imload("delete.png"), edit_screen()) 139 | edit_button, no_edit_signal = edit_toggle() 140 | 141 | item_height = Signal(0) 142 | not_del_signal = droprepeats(foldp(false, del_signal) do deleted, to_delete 143 | deleted && return deleted 144 | if to_delete 145 | push!(item_height, 0) 146 | delete!(screen, robj) 147 | end 148 | return to_delete 149 | end) 150 | 151 | edit_signal = map(!, no_edit_signal) 152 | item_screen = widget_screen!(delete=not_del_signal) 153 | edititemarea = map(edit_item_area, item_screen.area, item_height, Signal(left_gap)) 154 | edit_item_screen = Screen(edit_screen(), area = edititemarea) 155 | preserve(foldp((false, value(item_height)), edit_signal) do v0, edit 156 | if edit 157 | if !v0[1] # only do this at the first time 158 | vis, signal_dict = extract_edit_menu( 159 | to_edit_dict(robj), 160 | edit_item_screen, 161 | edit_signal, 162 | ) 163 | for (k, v) in signal_dict 164 | robj.uniforms[k] = v 165 | end 166 | _view(vis, edit_item_screen, camera = :fixed_pixel) 167 | new_heights = widths(value(boundingbox(vis)))[2] 168 | nh = ceil(Int, new_heights) 169 | push!(item_height, nh) 170 | return true, nh 171 | else 172 | push!(item_height, v0[2]) 173 | end 174 | else 175 | push!(item_height, 0) 176 | end 177 | return v0 178 | end) 179 | @materialize buttons_pressed, mouse_buttons_pressed, mouseinside = screen.inputs 180 | dc = doubleclick(mouse_buttons_pressed, 0.1) 181 | selected = foldp(false, mouse2id(screen), dc, mouseinside) do v0, id, dc, mi 182 | dc && mi && return id.id == robj.id # if double clicked and inside and hovers over robj 183 | return v0 184 | end 185 | color = map(selected) do s 186 | s ? RGBA{Float32}(0.9, 0.99, 1, 1) : RGBA{Float32}(1, 1, 1, 1) 187 | end 188 | select_icon = visualize(SimpleRectangle(0,0,1.5mm, 10mm), color=color) 189 | add_widget!(select_icon, visible_button, delete_button, edit_button, window=item_screen) 190 | if create_gizmo 191 | model = gizmo(screen, selected) 192 | set_arg!(robj, :model, model) 193 | end 194 | [del_signal] 195 | end 196 | --------------------------------------------------------------------------------