diff --git a/README.md b/README.md index e236744704e..2fde91f80d5 100644 --- a/README.md +++ b/README.md @@ -35,15 +35,14 @@ This package is not released yet so a bit awkward to set up. Here are the steps: ```julia Pkg.clone("https://github.com/SimonDanisch/Makie.jl.git") +Pkg.clone("https://github.com/SimonDanisch/AbstractNumbers.jl.git") Pkg.checkout("GLAbstraction") Pkg.checkout("GLVisualize") +Pkg.checkout("GeometryTypes") # For UV examples, e.g. earth texture on sphere, or textured cat Pkg.checkout("MeshIO") -# For image loading -Pkg.add("ImageMagick") - # For precompilation Pkg.clone("https://github.com/SimonDanisch/PackageCompiler.jl.git") Pkg.build("PackageCompiler") diff --git a/REQUIRE b/REQUIRE index d8182362ce7..6aa77b09d7b 100644 --- a/REQUIRE +++ b/REQUIRE @@ -18,3 +18,7 @@ Media Juno ModernGL GLFW 1.4.1 + +@windows ImageMagick +@linux ImageMagick +@osx QuartzImageIO diff --git a/src/Makie.jl b/src/Makie.jl index 0a2f1f936da..bb3db99ddb9 100644 --- a/src/Makie.jl +++ b/src/Makie.jl @@ -10,9 +10,11 @@ using Base: RefValue struct Backend{B} end -include("plotsbase/utils.jl") +include("plotutils/utils.jl") + include("plotsbase/scene.jl") -include("plotsbase/converts.jl") +include("plotsbase/conversions.jl") +include("plotutils/units.jl") const makie = Scene{:makie} @@ -20,7 +22,6 @@ include("plotutils/layout.jl") include("plotsbase/atomics.jl") # The actual implementation - include("atomics/shared.jl") include("atomics/scatter.jl") include("atomics/lines.jl") diff --git a/src/atomics/scatter.jl b/src/atomics/scatter.jl index a04f067e62b..b273fb986ba 100644 --- a/src/atomics/scatter.jl +++ b/src/atomics/scatter.jl @@ -40,13 +40,39 @@ function expand_for_glvisualize(kw_args) end +# function scatter() +# marker +# strokecolor +# strokewidth +# glowcolor +# glowwidth +# markersize +# rotations +# if haskey(scene, :positions) +# positions = to_positions(positions) +# elseif haskey(scene, :z) +# xyz = getindex.(scene, (:x, :y, :z)) +# positions = to_positions(xyz) +# elseif haskey(scene, :y) +# xy = getindex.(scene, (:x, :y, :z)) +# positions = to_positions(xy) +# end +# begin +# color = to_color(color) +# end +# begin +# colormap = to_colormap(colormap) +# intensity = to_intensity(intensity) +# colornorm = to_colornorm(colornorm, intensity) +# end +# end +# function _scatter(scene, kw_args) attributes = scatter_defaults(scene, kw_args) gl_data = expand_for_glvisualize(attributes) shape = to_signal(attributes[:marker]) main = (shape, to_signal(attributes[:positions])) - viz = GLVisualize.sprites(main, Style(:default), gl_data) - viz = GLVisualize.assemble_shader(viz).children[] + viz = visualize(main, Style(:default), gl_data) insert_scene!(scene, :scatter, viz, attributes) end diff --git a/src/plotsbase/atomics.jl b/src/plotsbase/atomics.jl index 57830586b50..f2670e3c6db 100644 --- a/src/plotsbase/atomics.jl +++ b/src/plotsbase/atomics.jl @@ -212,6 +212,21 @@ end colornorm = ((b, colornorm) -> to_colornorm(b, colornorm, heatmap))(colornorm) end +@default function axis(scene, kw_args) + axisnames = to_text(axisnames) + + showticks = to_bool(showticks) + tickfont2d = to_font(tickfont2d) + tickfont3d = to_font(tickfont3d) + showaxis = to_bool(showaxis) + showgrid = to_bool(showgrid) + + scalefuncs = to_scalefunc(scalefuncs) + gridcolors = to_color(gridcolors) + gridthickness = to_3floats(gridthickness) + axiscolors = to_color(axiscolors) +end + function expand_kwargs(scene, kw_args) # TODO get in all the shorthands from Plots.jl @@ -280,6 +295,12 @@ const atomic_funcs = ( :legend => """ legend(series, labels) creates a legend from an array of plots and labels + """, + + :axis => """ + axis(xrange, yrange, [zrange]) + + Creates a axis from a x,y,z ranges """ ) diff --git a/src/plotsbase/axis.jl b/src/plotsbase/axis.jl index 72847b310ff..306ba6f0438 100644 --- a/src/plotsbase/axis.jl +++ b/src/plotsbase/axis.jl @@ -7,22 +7,6 @@ function labelposition(ranges, dim) pos .* axis_vec .- (normal * 0.2f0) end -@default function axis(scene, kw_args) - axisnames = to_text(axisnames) - visible = to_bool(visible) - - showticks = to_bool(showticks) - tickfont2d = to_font(tickfont2d) - tickfont3d = to_font(tickfont3d) - showaxis = to_bool(showaxis) - showgrid = to_bool(showgrid) - - scalefuncs = to_scalefunc(scalefuncs) - gridcolors = to_color(gridcolors) - gridthickness = to_3floats(gridthickness) - axiscolors = to_color(axiscolors) - -end function GeometryTypes.widths(x::Range) mini, maxi = Float32.(extrema(x)) @@ -90,22 +74,20 @@ function draw_axis( return end -function axis(ranges...; kw_args...) - axis(to_node(ranges); kw_args...) +function axis(scene::Scene, x, y, attributes::Dict) + axis(scene, to_node((x, y)), attributes) end -""" -Creates an axis visualization for a certain bounding box. +function axis(scene::Scene, x, y, z, attributes::Dict) + axis(scene, to_node((x, y, z)), attributes) +end -## Attributes: -$(sprint(x-> Markdown.plain(x, Docs.doc(axis_defaults)))) -""" -function axis(ranges::Node{<: NTuple{N}}; kw_args...) where N +function axis(scene::Scene, ranges::Node{<: NTuple{N}}, attributes::Dict) where N textbuffer = TextBuffer(Point{N, Float32}(0)) linebuffer = LinesegmentBuffer(Point{N, Float32}(0)) scene = get_global_scene() - attributes = axis_defaults(scene, expand_kwargs(scene, kw_args)) + attributes = axis_defaults(scene, attributes) tickfont = N == 2 ? :tickfont2d : :tickfont3d names = ( :axisnames, :visible, :showaxis, :showticks, diff --git a/src/plotsbase/converts.jl b/src/plotsbase/conversions.jl similarity index 97% rename from src/plotsbase/converts.jl rename to src/plotsbase/conversions.jl index 6c8756cd914..cafd4c48d99 100644 --- a/src/plotsbase/converts.jl +++ b/src/plotsbase/conversions.jl @@ -137,7 +137,7 @@ to_index_buffer(b, x) = error( `NTuple{2, AbstractArray{Float}}` for 2D points """ function to_positions(b, x::Tuple{<: AbstractArray, <: AbstractArray}) - Point{2, Float32}.(x...) + to_position.(b, x...) end """ @@ -193,6 +193,10 @@ function to_position(b, x::VecLike{N}) where N Point{N, Float32}(x) end +function to_position(b, x, y) + Point{2, Float32}(to_absolute(b, x), to_absolute(b, y)) +end + """ to_array(b, arraylike) @@ -330,18 +334,20 @@ function to_spritemarker(b, marker::Symbol) end -to_spritemarker(b, marker::Vector{Char}) = String(marker) +to_spritemarker(b, marker::String) = marker +to_spritemarker(b, marker::AbstractVector{Char}) = String(marker) """ Vector of anything that is accepted as a single marker will give each point it's own marker. Note that it needs to be a uniform vector with the same element type! """ -function to_spritemarker(b, marker::Vector) +function to_spritemarker(b, marker::AbstractVector) + println(typeof(marker)) marker = map(marker) do sym to_spritemarker(b, sym) end - if isa(marker, Vector{Char}) - to_spritemarker(b, marker) + if isa(marker, AbstractVector{Char}) + String(marker) else marker end diff --git a/src/plotsbase/output.jl b/src/plotsbase/output.jl index 5a2fa79f474..d1fd74282fa 100644 --- a/src/plotsbase/output.jl +++ b/src/plotsbase/output.jl @@ -5,8 +5,9 @@ function scene2image(screen::Screen) yield() render_frame(screen) # let it render ModernGL.glFinish() - return GLWindow.screenbuffer(screen) + return Images.clamp01nan.(GLWindow.screenbuffer(screen)) end + function scene2image(scene::Scene) screen = getscreen(scene) if screen != nothing diff --git a/src/plotsbase/scene.jl b/src/plotsbase/scene.jl index 035b05a09cc..64306be9d48 100644 --- a/src/plotsbase/scene.jl +++ b/src/plotsbase/scene.jl @@ -30,6 +30,9 @@ end attributes(scene::Scene) = copy(scene.data) +scene_node(x) = to_node(x) +# there is not much use in having scene being a node, besides that it's awkward to work with +scene_node(x::Scene) = x const current_backend = Ref(:makie) function Scene(args...) @@ -53,11 +56,11 @@ end function Scene(parent::Scene{Backend}, scene::Dict, name = :scene) where Backend data = Dict{Symbol, Any}() for (k, v) in scene - data[Symbol(k)] = to_node(v) + data[Symbol(k)] = scene_node(v) end Scene{Backend}(name, Nullable(parent), data, nothing) end -function Scene(parent::Scene{Backend}, name = :scene; attributes...) where Backend +function Scene(parent::Scene{Backend}, name::Symbol = :scene; attributes...) where Backend Scene(parent, Dict{Symbol, Any}(attributes), name) end @@ -68,7 +71,7 @@ end function (::Type{Scene{Backend}})(pair1::Pair, tail::Pair...) where Backend args = [pair1, tail...] - Scene(Dict(map(x-> x[1] => to_node(x[2]), args))) + Scene(Dict(map(x-> x[1] => scene_node(x[2]), args))) end @@ -85,7 +88,6 @@ and manually added via `show` by doing e.g. """ function show!(scene::Scene{Backend}, childscene::Scene{Backend}) where Backend camera = to_value(childscene, :camera) # should always be available! - println(camera) screen = getscreen(scene) cams = collect(keys(screen.cameras)) viz = native_visual(childscene) @@ -281,8 +283,14 @@ function Scene(; resize!(w, Int.(resolution)...) GLVisualize.add_screen(w) - - dict = map(filter((k, v)-> k != :cursor_position, w.inputs)) do k_v + filtered = filter(w.inputs) do k, v + !(k in ( + :cursor_position, + :window_size, + :framebuffer_size + )) + end + dict = map(filtered) do k_v k_v[1] => to_node(k_v[2]) end dict[:screen] = w @@ -316,7 +324,7 @@ function setindex!(s::Scene, obj, key::Symbol) if haskey(s, key) # if in dictionary, just push a new value to the signal push!(s[key], obj) else - s.data[key] = to_node(obj) + s.data[key] = scene_node(obj) end end @@ -408,3 +416,7 @@ function find_default(scene, kw_args, func, attribute) Please provide attribute $attribute for $func") end end + +function GeometryTypes.widths(scene::Scene) + widths(getscreen(scene)) +end diff --git a/src/plotsbase/signals.jl b/src/plotsbase/signals.jl index 65ae398501d..645f3a5d929 100644 --- a/src/plotsbase/signals.jl +++ b/src/plotsbase/signals.jl @@ -106,14 +106,12 @@ function _unsafe_getindex!(dest::ArrayNode, src::AbstractArray, Is::Union{Real, return dest end - to_node(obj::AbstractNode) = obj function to_node(obj::AbstractNode, f) to_node(map(f, to_signal(obj)), f) end to_node(obj, f = identity) = to_node(Signal(f(obj)), f) to_node(obj::Signal, f = identity) = Node(map(f, obj), f) -to_node(obj::Scene, f = identity) = obj function to_node(obj::Signal{AT}, f::F = identity) where {AT <: AbstractArray, F} A = value(obj) ArrayNode{eltype(A), ndims(A), F, AT}(obj, f) diff --git a/src/plotsbase/themes.jl b/src/plotsbase/themes.jl index cb4b6d6284d..2dc77a25c38 100644 --- a/src/plotsbase/themes.jl +++ b/src/plotsbase/themes.jl @@ -158,7 +158,7 @@ function default_theme(scene) legend = begin backgroundcolor = to_color(:white) strokecolor = to_color(RGBA(0.3, 0.3, 0.3, 0.9)) - strokewidth = to_float(2) + strokewidth = to_float(1) position = to_position((0, 1)) gap = to_float(20) textgap = to_float(15) diff --git a/src/plotutils/layout.jl b/src/plotutils/layout.jl index 09d700cc6b1..cf89ebd4cfd 100644 --- a/src/plotutils/layout.jl +++ b/src/plotutils/layout.jl @@ -1,11 +1,8 @@ - - - """ calculates how much `child` needs to move to not touch `parent` """ function move_from_touch( - parent::HyperRectangle{N, T}, child::HyperRectangle{N}, + parent::GeometryPrimitive{N, T}, child::GeometryPrimitive{N}, pad::Vec{N} ) where {N, T} pmini, cmini = minimum(parent), minimum(child) .- pad @@ -23,27 +20,55 @@ end Moves `child` so that it doesn't touch parent. Leaves a gap to parent defined by `pad`. """ function dont_touch( - parent::HyperRectangle{N, T}, child::HyperRectangle{N}, + parent::GeometryPrimitive{N}, child::GeometryPrimitive{N}, pad::Vec{N} - ) where {N, T} - + ) where N child + move_from_touch(parent, child, pad) end -function dont_touch( - parent::SimpleRectangle, child::SimpleRectangle, - pad::Vec - ) - r = dont_touch(HyperRectangle(parent), HyperRectangle(child), pad) - SimpleRectangle(minimum(r)..., widths(r)...) + + +""" + fit_factor_stretch(rect, lims::NTuple{N}) where N + +Calculates the stretch factor to fill `rect` in all dimension. +Returns a stretch `N` dimensional fit factor. +""" +function fit_factor_stretch(rect, lims::NTuple{N, Any}) where N + w = widths(rect) + stretches = ntuple(Val{N}) do i + from, to = lims[i] + w[i] / abs(to - from) + end + stretches end -function move_from_touch( - parent::SimpleRectangle, child::SimpleRectangle, - pad::Vec - ) - move_from_touch(HyperRectangle(parent), HyperRectangle(child), pad) + +""" + fit_factor(rect, lims::NTuple{N}) where N + +Calculates the scaling one needs to apply to lims to fit `rect` without changing aspect ratio. +Returns float scaling and the full strech as given by [`fit_factor_stretch`](@ref) +""" +function fit_factor(rect, lims::NTuple{N, Any}) where N + stretches = fit_factor_stretch(rect, lims) + minimum(stretches), stretches end + +""" + fit_ratio(rect, lims) + +Calculates the ratio one needs to stretch `lims` in order to get the same aspect ratio +""" +function fit_ratio(rect, lims) + s, stretches = fit_factor(rect, lims) + stretches ./ s +end + + + + + # using GeometryTypes # # using Base.Test diff --git a/src/plotutils/units.jl b/src/plotutils/units.jl new file mode 100644 index 00000000000..71e07cf88c2 --- /dev/null +++ b/src/plotutils/units.jl @@ -0,0 +1,106 @@ +module Units + +using ..Makie: VecLike +#= +Absolute is the default, so any number not having a unit is treated as absolute +struct Absolute{T} + number::T +end +=# +using AbstractNumbers, StaticArrays, GeometryTypes + +abstract type Unit{T} <: AbstractNumber{T} end + +# We should always poison any calculation involving Units +promote_rule(::Type{<: Unit}, ::Type{T}) where T <: Number = Unit{T} +promote_rule(::Type{T}, ::Type{<: Unit}) where T <: Number = Unit{T} + +# This is kind of wrong, since we need a scene for correct conversion. +# The correct version is to_absolute(scene, x::Unit) +Base.convert(::Type{Number}, x::Unit) = x.number + +""" +Unit is relative to bounding frame. +E.g. if the area is IRect(0, 0, 100, 100) +Point(0.5rel, 0.5rel) == Point(50, 50) +""" +struct Relative{T <: Number} <: Unit{T} + number::T +end +AbstractNumbers.basetype(::Type{<: Relative}) = Relative + +""" +Unit is pixels on screen. +This one is a bit tricky, since it refers to a static attribute (pixels on screen don't change) +but since every visual is attached to a camera, the exact scale might change. +So in the end, this is just relative to some normed camera - the value on screen, depending on the camera, +will not actually sit on those pixels. Only camera that guarantees the correct mapping is the +`:pixel` camera type. +""" +struct Pixel{T} <: Unit{T} + number::T +end +AbstractNumbers.basetype(::Type{<: Pixel}) = Pixel + +""" +Millimeter on screen. This unit respects the dimension and pixel density of the screen +to represent millimeters on the screen. This is the must use unit for layouting, +that needs to look the same on all kind of screens. Similar as with the [`Pixel`](@ref) unit, +a camera can change the actually displayed dimensions of any object using the millimeter unit. +""" +struct Millimeter{T} <: Unit{T} + number::T +end +AbstractNumbers.basetype(::Type{<: Millimeter}) = Millimeter + +const rel = Relative(1) +const px = Pixel(1) +const mm = Millimeter(1) + + +""" +Default doesn't do anything +""" +function to_absolute(scene, x) + x +end + +function to_absolute(scene, x::AbstractVector{<: Millimeter}) + to_pixel(scene, x .* pixel_per_mm(scene)) +end + +function to_absolute(scene, x::Relative) + x * minimum(widths(scene)) +end + +similar_vec(::Type{V}, T) where V <: StaticVector = similar_type(V, T) +similar_vec(::Type{NTuple{N, T}}, t) where {N, T} = NTuple{N, t} + +function to_absolute(scene, x::V) where V <: VecLike{N, T} where {N, T <: Relative} + x .* convert(V, Relative.(widths(scene))) +end + +function to_absolute(scene, x::VecLike{N, <: Pixel}) where N + vec3 = to_nd(x, Val{3}, 0) + vec4 = to_nd(vec3, Val{4}, isa(x, Point) ? 1 : 0) + projected = to_value(scene[:camera].projectionview) .* vec4 + vec3 = to_nd(projected ./ projected[4], Val{3}, 0) +end + + +function get_scaled_dpi(window) + monitor = GLFW.GetPrimaryMonitor() + props = GLWindow.MonitorProperties(monitor) + # it seems like small displays with high dpi make mm look quite big. + # so lets scale it a bit. 518 is a bit arbitrary, but the scale of my + # screen on which I test everything, hence it will make you see things as I do. + scaling = minimum(props.physicalsize) / 518 + min(props.dpi...) * scaling # we do not start fiddling with differently scaled xy dpi's +end + +end + +using .Units + +using .Units: px, rel, mm, Millimeter, Pixel, Relative, to_absolute +export px, rel, mm, Millimeter, Pixel, Relative, to_absolute diff --git a/src/plotsbase/utils.jl b/src/plotutils/utils.jl similarity index 100% rename from src/plotsbase/utils.jl rename to src/plotutils/utils.jl diff --git a/test/REQUIRE b/test/REQUIRE new file mode 100644 index 00000000000..f1929efb8d5 --- /dev/null +++ b/test/REQUIRE @@ -0,0 +1 @@ +VisualRegressionTests diff --git a/test/all_samples.jl b/test/all_samples.jl new file mode 100644 index 00000000000..3a392dd4d7a --- /dev/null +++ b/test/all_samples.jl @@ -0,0 +1,356 @@ +# this is a bit of an odd design, but I think it does it's job. +# This file contains statements spereated by the comment # cell (without space), +# which makes it possible to extract each cell out of this file. +# There is also the setup cell indicated by # setup. +# We can then continue to generate files for visual regression tests +# snoop compiling and even create ijulia notebooks with cells containing this. + +#setup +using Makie, GLFW, GeometryTypes, Reactive, FileIO, ColorBrewer, Colors +using GLVisualize +using GLVisualize: loadasset, assetpath + +function xy_data(x, y) + r = sqrt(x*x + y*y) + r == 0.0 ? 1f0 : (sin(r)/r) +end + +function custom_theme(scene) + @theme theme = begin + linewidth = to_float(3) + colormap = to_colormap(:RdYlGn)#to_colormap(:RdPu) + scatter = begin + marker = to_spritemarker(Circle) + markersize = to_float(0.03) + strokecolor = to_color(:white) + strokewidth = to_float(0.01) + glowcolor = to_color(RGBA(0, 0, 0, 0.4)) + glowwidth = to_float(0.1) + end + end + # update theme values + scene[:theme] = theme +end + + +#cell +img = loadasset("doge.png") +scene = Scene(resolution = (500, 500)) + +display(scene) +show(scene) +println(scene) + +is = image(img) +center!(scene) +subscene = Scene(scene, Signal(SimpleRectangle(0, 0, 200, 200))) +scatter(subscene, rand(100) * 200, rand(100) * 200, markersize = 4) +center!(scene) + +#cell +scene = Scene(resolution = (500, 500)); +x = [0, 1, 2, 0]; +y = [0, 0, 1, 2]; +z = [0, 2, 0, 1]; +color = [:red, :green, :blue, :yellow]; +i = [0, 0, 0, 1]; +j = [1, 2, 3, 2]; +k = [2, 3, 1, 3]; + +indices = [1, 2, 3, 1, 3, 4, 1, 4, 2, 2, 3, 4]; +mesh(x, y, z, indices, color = color); +r = linspace(-0.5, 2.5, 4); +axis(r, r, r); +center!(scene); + +#cell +scene = Scene(resolution = (500, 500)) +Makie.Makie.volume(rand(32, 32, 32), algorithm = :iso) +center!(scene) + +#cell +scene = Scene(resolution = (500, 500)) +heatmap(rand(32, 32)) +center!(scene) + +#cell +scene = Scene(resolution = (500, 500)) +r = linspace(-10, 10, 512) +z = ((x, y)-> sin(x) + cos(y)).(r, r') +Makie.contour(r, r, z, levels = 5, color = ColorBrewer.palette("RdYlBu", 5)) +center!(scene) + +#cell +scene = Scene(resolution = (500, 500)) +vx = -1:0.1:1; +vy = -1:0.1:1; + +f(x, y) = (sin(x*10) + cos(y*10)) / 4 +psurf = surface(vx, vy, f) + +pos = lift_node(psurf[:x], psurf[:y], psurf[:z]) do x, y, z + vec(Point3f0.(x, y', z .+ 0.5)) +end +pscat = scatter(pos) +plines = lines(view(pos, 1:2:length(pos))) +center!(scene) +@theme theme = begin + markersize = to_markersize2d(0.01) + strokecolor = to_color(:white) + strokewidth = to_float(0.01) +end +# this pushes all the values from theme to the plot +push!(pscat, theme) +pscat[:glow_color] = to_node(RGBA(0, 0, 0, 0.4), x->to_color((), x)) +# apply it to the scene +custom_theme(scene) +# From now everything will be plotted with new theme +psurf = surface(vx, 1:0.1:2, psurf[:z]) +center!(scene) + +#cell +scene = Scene(resolution = (500, 500)) +sv = scatter(rand(Point3f0, 100)) +similar(sv, rand(10), rand(10), rand(10), color = :black, markersize = 0.4) +scene + +#cell +scene = Scene(resolution = (500, 500)) +large_sphere = HyperSphere(Point3f0(0), 1f0) +positions = decompose(Point3f0, large_sphere) +colS = [Colors.RGBA{Float32}(rand(), rand(), rand(), 1.) for i = 1:length(positions)] +sizesS = [rand(Vec3f0) .* 0.5f0 for i = 1:length(positions)] +meshscatter(positions, color = colS, markersize = sizesS) +center!(scene) + +#cell +scene = Scene(resolution = (500, 500)) +y = [ + -0.997669 + -0.979084 + -0.942261 + -0.887885 + -0.81697 + -0.730836 + -0.631088 + -0.519584 + -0.398401 + -0.269797 + -0.136167 + 0.0 + 0.136167 + 0.269797 + 0.398401 + 0.519584 + 0.631088 + 0.730836 + 0.81697 + 0.887885 + 0.942261 + 0.979084 + 0.997669 +] +contour(linspace(-0.99, 0.99, 23), y, rand(23, 23), levels = 10) +center!(scene) + +#cell +scene = Scene(resolution = (500, 500)) +# define points/edges +perturbfactor = 4e1 +N = 3; nbfacese = 30; radius = 0.02 +large_sphere = HyperSphere(Point3f0(0), 1f0) +positions = decompose(Point3f0, large_sphere, 30) +np = length(positions) +pts = [positions[k][l] for k = 1:length(positions), l = 1:3] +pts = vcat(pts, 1.1 * pts + randn(size(pts)) / perturbfactor) # light position influence ? +edges = hcat(collect(1:np), collect(1:np) + np) +ne = size(edges, 1); np = size(pts, 1) +# define markers meshes +meshC = GeometryTypes.GLNormalMesh(GeometryTypes.Cylinder{3, Float32}( + GeometryTypes.Point3f0(0., 0., 0.), + GeometryTypes.Point3f0(0., 0, 1.), + Float32(1)), nbfacese) + +meshS = GeometryTypes.GLNormalMesh(large_sphere, 20) +# define colors, markersizes and rotations +pG = [GeometryTypes.Point3f0(pts[k, 1], pts[k, 2], pts[k, 3]) for k = 1:np] +lengthsC = sqrt.(sum((pts[edges[:,1], :] .- pts[edges[:, 2], :]) .^ 2, 2)) +sizesC = [GeometryTypes.Vec3f0(radius, radius, lengthsC[i]) for i = 1:ne] +sizesC = [Vec3f0(1., 1., 1.) for i = 1:ne] +colorsp = [Colors.RGBA{Float32}(rand(), rand(), rand(), 1.) for i = 1:np] +colorsC = [(colorsp[edges[i, 1]] + colorsp[edges[i, 2]]) / 2. for i = 1:ne] +sizesC = [Vec3f0(radius, radius, lengthsC[i]) for i = 1:ne] +Qlist = zeros(ne, 4) +for k = 1:ne + ct = GeometryTypes.Cylinder{3, Float32}( + GeometryTypes.Point3f0(pts[edges[k, 1], 1], pts[edges[k, 1], 2], pts[edges[k, 1], 3]), + GeometryTypes.Point3f0(pts[edges[k, 2], 1], pts[edges[k, 2], 2], pts[edges[k, 2], 3]), + Float32(1)) + Q = GeometryTypes.rotation(ct) + r = 0.5 * sqrt(1 + Q[1, 1] + Q[2, 2] + Q[3, 3]); Qlist[k, 4] = r + Qlist[k, 1] = (Q[3, 2] - Q[2, 3]) / (4 * r) + Qlist[k, 2] = (Q[1, 3] - Q[3, 1]) / (4 * r) + Qlist[k, 3] = (Q[2, 1] - Q[1, 2]) / (4 * r) +end +rotationsC = AbstractVector[Vec4f0(Qlist[i, 1], Qlist[i, 2], Qlist[i, 3], Qlist[i, 4]) for i = 1:ne] +# plot +hm = Makie.meshscatter(pG[edges[:, 1]], color = colorsC, marker = meshC, + markersize = sizesC, rotations = rotationsC) +hp = Makie.meshscatter(pG, color = colorsp, marker = meshS, markersize = radius) + +r = linspace(-1.3, 1.3, 4); Makie.axis(r, r, r) +center!(scene) + +#cell +scene = Scene(resolution = (500, 500)) +large_sphere = HyperSphere(Point3f0(0), 1f0) +positions = decompose(Point3f0, large_sphere) +linepos = view(positions, rand(1:length(positions), 1000)) +lines(linepos, linewidth = 0.1, color = :black) +scatter(positions, strokewidth = 0.02, strokecolor = :white, color = RGBA(0.9, 0.2, 0.4, 0.6)) +r = linspace(-1.5, 1.5, 5) +axis(r, r, r) +scene + +#cell +scene = Scene(resolution = (500, 500)) +large_sphere = HyperSphere(Point3f0(0), 1f0) +positions = decompose(Point3f0, large_sphere) +meshscatter(positions, color = RGBA(0.9, 0.2, 0.4, 1)) +scene + +#cell +scene = Scene(resolution = (500, 500)) + +r = linspace(-2, 2, 40) +surf_func(i) = [Float32(xy_data(x*i, y*i)) for x = r, y = r] +z = surf_func(20) +surf = surface(r, r, z) + +wf = wireframe(r, r, surf[:z] .+ 1.0, + linewidth = 2f0, color = lift_node(x-> x[5], surf[:colormap]) +) +xy = linspace(-2.1, 2.1, 4) +axis(xy, xy, linspace(0, 2, 4)) +center!(scene) + +io = VideoStream(scene) +for i in linspace(0, 60, 100) + surf[:z] = surf_func(i) + recordframe!(io) +end +scene + + +#cell +scene = Scene(resolution = (500, 500)) + +N = 40 +r = linspace(-2, 2, 40) +surf_func(i) = [Float32(xy_data(x*i, y*i)) for x = r, y = r] +surface( + r, r, surf_func(10), + color = GLVisualize.loadasset("doge.png") +) +center!(scene) +scene + +#cell +scene = Scene(resolution = (500, 500)) +x = GLVisualize.loadasset("cat.obj") +Makie.mesh(x.vertices, x.faces, color = :black) +pos = map(x.vertices, x.normals) do p, n + p => p .+ (normalize(n) .* 0.05f0) +end +linesegment(pos) +scene + + +#cell +scene = Scene(resolution = (500, 500)) +mesh(GLVisualize.loadasset("cat.obj")) +r = linspace(-0.1, 1, 4) +center!(scene) +scene + +#cell +scene = Scene(resolution = (500, 500)) +cat = load(assetpath("cat.obj"), GLNormalUVMesh) +Makie.mesh(cat, color = loadasset("diffusemap.tga")) +center!(scene) + +#cell +scene = Scene(resolution = (500, 500)) +Makie.mesh(Sphere(Point3f0(0), 1f0)) +center!(scene) +scene + + +#cell +scene = Scene(resolution = (500, 500)) +wireframe(GLVisualize.loadasset("cat.obj")) +center!(scene) +scene + +#cell +scene = Scene(resolution = (500, 500)) +wireframe(Sphere(Point3f0(0), 1f0)) +center!(scene) +scene + +#cell +scene = Scene(resolution = (500, 500)) +heatmap(rand(32, 32)) +center!(scene) + +#cell +scene = Scene(resolution = (500, 500), color = :black) +stars = 100_000 +scatter((rand(Point3f0, stars) .- 0.5) .* 10, + glowwidth = 0.005, glow_color = :white, color = RGBA(0.8, 0.9, 0.95, 0.4), + markersize = rand(linspace(0.0001, 0.01, 100), stars) +) +scene + +#cell +scene = Scene(resolution = (500, 500)) +Makie.volume(rand(32, 32, 32), algorithm = :iso) +center!(scene) + +#cell +scene = Scene(resolution = (500, 500)) +scatter(Point3f0[(1,0,0), (0,1,0), (0,0,1)], marker=[:x, :circle, :cross]) +axis(scene, linspace(0, 1, 4), linspace(0, 1, 4), linspace(0, 1, 4)) +center!(scene); + + +#cell +scene = Scene(resolution = (500, 500)) + +x = map([:dot, :dash, :dashdot], [2, 3, 4]) do ls, lw + linesegment(linspace(1, 5, 100), rand(100), rand(100), linestyle = ls, linewidth = lw) +end +push!(x, scatter(linspace(1, 5, 100), rand(100), rand(100))) +center!(scene) +l = Makie.legend(x, ["attribute $i" for i in 1:4]) +l[:position] = (0, 1) +l[:backgroundcolor] = RGBA(0.95, 0.95, 0.95) +l[:strokecolor] = RGB(0.8, 0.8, 0.8) +l[:gap] = 30 +l[:textsize] = 19 +l[:linepattern] = Point2f0[(0,-0.2), (0.5, 0.2), (0.5, 0.2), (1.0, -0.2)] +l[:scatterpattern] = decompose(Point2f0, Circle(Point2f0(0.5, 0), 0.3f0), 9) +l[:markersize] = 2f0 +scene + +#cell +scene = Scene(resolution = (500, 500)) +cmap = collect(linspace(to_color(:red), to_color(:blue), 20)) +l = Makie.legend(cmap, 1:4) +l[:position] = (1.0,1.0) +l[:textcolor] = :blue +l[:strokecolor] = :black +l[:strokewidth] = 1 +l[:textsize] = 15 +l[:textgap] = 5 +scene diff --git a/test/parse_samples.jl b/test/parse_samples.jl new file mode 100644 index 00000000000..ffedb3eb294 --- /dev/null +++ b/test/parse_samples.jl @@ -0,0 +1,31 @@ +cd(@__DIR__) +samples = readstring("all_samples.jl") + +_, setup_rest = split(samples, "#setup") +setup_rest = split(setup_rest, "#cell") +setup = first(setup_rest) +cells = setup_rest[2:end] + +open("visual_regression_funcs.jl", "w") do io + println(io, setup) + for (i, body) in enumerate(cells) + body = chomp(body) + println(io, "function testfunc_$i()") + for line in split(body, "\n") + println(io, " "^4, line) + end + println(io, "end") + end +end + +open("sample_funcs.jl", "w") do io + println(io, setup) + for (i, body) in enumerate(cells) + body = chomp(body) + println(io, "function testfunc_$i()") + for line in split(body, "\n") + println(io, " "^4, line) + end + println(io, "end") + end +end diff --git a/test/runtests.jl b/test/runtests.jl index e68b6479b19..4129af5fc95 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,311 +1,25 @@ -module MakieTest +mode = get(ENV, "MAKIE_COMPILE", "") -using Makie, FileIO, GLFW, GeometryTypes, Reactive, FileIO, ColorBrewer, Colors -using GLVisualize -using GLVisualize: loadasset, assetpath - -function xy_data(x, y) - r = sqrt(x*x + y*y) - r == 0.0 ? 1f0 : (sin(r)/r) -end - -function custom_theme(scene) - @theme theme = begin - linewidth = to_float(3) - colormap = to_colormap(:RdYlGn)#to_colormap(:RdPu) - scatter = begin - marker = to_spritemarker(Circle) - markersize = to_float(0.03) - strokecolor = to_color(:white) - strokewidth = to_float(0.01) - glowcolor = to_color(RGBA(0, 0, 0, 0.4)) - glowwidth = to_float(0.1) - end - end - # update theme values - scene[:theme] = theme +function is_ci() + get(ENV, "TRAVIS", "") == "true" || + get(ENV, "APPVEYOR", "") == "true" || + get(ENV, "CI", "") == "true" end -function run() - - img = loadasset("doge.png") - scene = Scene() - is = image(img) - center!(scene) - subscene = Scene(scene, Signal(SimpleRectangle(0, 0, 200, 200))) - scatter(subscene, rand(100) * 200, rand(100) * 200, markersize = 4) - - scene = Scene(resolution = (500, 500)); - x = [0, 1, 2, 0]; - y = [0, 0, 1, 2]; - z = [0, 2, 0, 1]; - color = [:red, :green, :blue, :yellow]; - i = [0, 0, 0, 1]; - j = [1, 2, 3, 2]; - k = [2, 3, 1, 3]; - - indices = [1, 2, 3, 1, 3, 4, 1, 4, 2, 2, 3, 4]; - mesh(x, y, z, indices, color = color); - r = linspace(-0.5, 2.5, 4); - axis(r, r, r); - center!(scene); - - scene = Scene() - Makie.Makie.volume(rand(32, 32, 32), algorithm = :iso) - center!(scene) - - scene = Scene() - heatmap(rand(32, 32)) - center!(scene) - - scene = Scene() - r = linspace(-10, 10, 512) - z = ((x, y)-> sin(x) + cos(y)).(r, r') - Makie.contour(r, r, z, levels = 5, color = ColorBrewer.palette("RdYlBu", 5)) - center!(scene) - - scene = Scene() - vx = -1:0.1:1; - vy = -1:0.1:1; - - f(x, y) = (sin(x*10) + cos(y*10)) / 4 - psurf = surface(vx, vy, f) - - pos = lift_node(psurf[:x], psurf[:y], psurf[:z]) do x, y, z - vec(Point3f0.(x, y', z .+ 0.5)) - end - pscat = scatter(pos) - plines = lines(view(pos, 1:2:length(pos))) - center!(scene) - @theme theme = begin - markersize = to_markersize2d(0.01) - strokecolor = to_color(:white) - strokewidth = to_float(0.01) - end - - # this pushes all the values from theme to the plot - - push!(pscat, theme) - pscat[:glow_color] = to_node(RGBA(0, 0, 0, 0.4), x->to_color((), x)) - - # apply it to the scene - custom_theme(scene) - - # From now everything will be plotted with new theme - psurf = surface(vx, 1:0.1:2, psurf[:z]) - center!(scene) - - scene = Scene() - sv = scatter(rand(Point3f0, 100)) - similar(sv, rand(10), rand(10), rand(10), color = :black, markersize = 0.4) - - - scene = Scene() - x = map([:dot, :dash, :dashdot], [2, 3, 4]) do ls, lw - linesegment(linspace(1, 5, 100), rand(100), rand(100), linestyle = ls, linewidth = lw) - end - push!(x, scatter(linspace(1, 5, 100), rand(100), rand(100))) - center!(scene) - l = Makie.legend(x, ["attribute $i" for i in 1:4]) - - l[:position] = (0.089, 0.75) - l[:gap] = 20 - l[:textgap] = 20 - l[:padding] = 20 - l[:scatterpattern] - - scene = Scene(resolution = (500, 500)) - large_sphere = HyperSphere(Point3f0(0), 1f0) - positions = decompose(Point3f0, large_sphere) - colS = [Colors.RGBA{Float32}(rand(), rand(), rand(), 1.) for i = 1:length(positions)] - sizesS = [rand(Vec3f0) .* 0.5f0 for i = 1:length(positions)] - meshscatter(positions, color = colS, markersize = sizesS) - - scene = Scene() - y = [ - -0.997669 - -0.979084 - -0.942261 - -0.887885 - -0.81697 - -0.730836 - -0.631088 - -0.519584 - -0.398401 - -0.269797 - -0.136167 - 0.0 - 0.136167 - 0.269797 - 0.398401 - 0.519584 - 0.631088 - 0.730836 - 0.81697 - 0.887885 - 0.942261 - 0.979084 - 0.997669 - ] - contour(linspace(-0.99, 0.99, 23), y, rand(23, 23), levels = 10) - center!(scene) - - - scene = Makie.Scene(resolution = (900, 900)) - # define points/edges - perturbfactor = 4e1 - N = 3; nbfacese = 30; radius = 0.02 - large_sphere = HyperSphere(Point3f0(0), 1f0) - positions = decompose(Point3f0, large_sphere, 30) - np = length(positions) - pts = [positions[k][l] for k = 1:length(positions), l = 1:3] - pts = vcat(pts, 1.1 * pts + randn(size(pts)) / perturbfactor) # light position influence ? - edges = hcat(collect(1:np), collect(1:np) + np) - ne = size(edges, 1); np = size(pts, 1) - # define markers meshes - meshC = GeometryTypes.GLNormalMesh(GeometryTypes.Cylinder{3, Float32}( - GeometryTypes.Point3f0(0., 0., 0.), - GeometryTypes.Point3f0(0., 0, 1.), - Float32(1)), nbfacese) - - meshS = GeometryTypes.GLNormalMesh(large_sphere, 20) - # define colors, markersizes and rotations - pG = [GeometryTypes.Point3f0(pts[k, 1], pts[k, 2], pts[k, 3]) for k = 1:np] - lengthsC = sqrt.(sum((pts[edges[:,1], :] .- pts[edges[:, 2], :]) .^ 2, 2)) - sizesC = [GeometryTypes.Vec3f0(radius, radius, lengthsC[i]) for i = 1:ne] - sizesC = [Vec3f0(1., 1., 1.) for i = 1:ne] - colorsp = [Colors.RGBA{Float32}(rand(), rand(), rand(), 1.) for i = 1:np] - colorsC = [(colorsp[edges[i, 1]] + colorsp[edges[i, 2]]) / 2. for i = 1:ne] - sizesC = [Vec3f0(radius, radius, lengthsC[i]) for i = 1:ne] - Qlist = zeros(ne, 4) - for k = 1:ne - ct = GeometryTypes.Cylinder{3, Float32}( - GeometryTypes.Point3f0(pts[edges[k, 1], 1], pts[edges[k, 1], 2], pts[edges[k, 1], 3]), - GeometryTypes.Point3f0(pts[edges[k, 2], 1], pts[edges[k, 2], 2], pts[edges[k, 2], 3]), - Float32(1)) - Q = GeometryTypes.rotation(ct) - r = 0.5 * sqrt(1 + Q[1, 1] + Q[2, 2] + Q[3, 3]); Qlist[k, 4] = r - Qlist[k, 1] = (Q[3, 2] - Q[2, 3]) / (4 * r) - Qlist[k, 2] = (Q[1, 3] - Q[3, 1]) / (4 * r) - Qlist[k, 3] = (Q[2, 1] - Q[1, 2]) / (4 * r) - end - rotationsC = AbstractVector[Vec4f0(Qlist[i, 1], Qlist[i, 2], Qlist[i, 3], Qlist[i, 4]) for i = 1:ne] - # plot - hm = Makie.meshscatter(pG[edges[:, 1]], color = colorsC, marker = meshC, - markersize = sizesC, rotations = rotationsC) - hp = Makie.meshscatter(pG, color = colorsp, marker = meshS, markersize = radius) - - r = linspace(-1.3, 1.3, 4); Makie.axis(r, r, r) - - - scene = Scene(resolution = (500, 500)) - large_sphere = HyperSphere(Point3f0(0), 1f0) - positions = decompose(Point3f0, large_sphere) - linepos = view(positions, rand(1:length(positions), 1000)) - lines(linepos, linewidth = 0.1, color = :black) - scatter(positions, strokewidth = 0.02, strokecolor = :white, color = RGBA(0.9, 0.2, 0.4, 0.6)) - r = linspace(-1.5, 1.5, 5) - axis(r, r, r) - scene - - #julia - scene = Scene(resolution = (500, 500)) - large_sphere = HyperSphere(Point3f0(0), 1f0) - positions = decompose(Point3f0, large_sphere) - meshscatter(positions, color = RGBA(0.9, 0.2, 0.4, 1)) - scene - - #julia - scene = Scene(resolution = (500, 500)) - - r = linspace(-2, 2, 40) - surf_func(i) = [Float32(xy_data(x*i, y*i)) for x = r, y = r] - z = surf_func(20) - surf = surface(r, r, z) - - wf = wireframe(r, r, surf[:z] .+ 1.0, - linewidth = 2f0, color = lift_node(x-> x[5], surf[:colormap]) - ) - xy = linspace(-2.1, 2.1, 4) - axis(xy, xy, linspace(0, 2, 4)) - center!(scene) - - io = VideoStream(scene) - for i in linspace(0, 60, 100) - surf[:z] = surf_func(i) - recordframe!(io) - end - - #julia - scene = Scene(resolution = (500, 500)) - - r = linspace(-2, 2, 40) - N = 40 - r = linspace(-2, 2, 40) - surface( - r, r, surf_func(10), - color = GLVisualize.loadasset("doge.png") - ) - center!(scene) - scene - - #julia - scene = Scene(resolution = (500, 500)) - x = GLVisualize.loadasset("cat.obj") - Makie.mesh(x.vertices, x.faces, color = :black) - pos = map(x.vertices, x.normals) do p, n - p => p .+ (normalize(n) .* 0.05f0) - end - linesegment(pos) - scene - - - #julia - scene = Scene(resolution = (500, 500)) - mesh(GLVisualize.loadasset("cat.obj")) - r = linspace(-0.1, 1, 4) - center!(scene) - scene - - #julia - scene = Scene(resolution = (500, 500)) - cat = load(assetpath("cat.obj"), GLNormalUVMesh) - Makie.mesh(cat, color = loadasset("diffusemap.tga")) - center!(scene) - - scene = Scene(resolution = (500, 500)) - Makie.mesh(Sphere(Point3f0(0), 1f0)) - center!(scene) - scene - - scene = Scene(resolution = (500, 500)) - wireframe(GLVisualize.loadasset("cat.obj")) - center!(scene) - scene - - scene = Scene(resolution = (500, 500)) - wireframe(Sphere(Point3f0(0), 1f0)) - center!(scene) - scene - - scene = Scene(resolution = (500, 500)) - heatmap(rand(32, 32)) - center!(scene) - - scene = Scene(resolution = (500, 500), color = :black) - stars = 100_000 - scatter((rand(Point3f0, stars) .- 0.5) .* 10, - glowwidth = 0.005, glow_color = :white, color = RGBA(0.8, 0.9, 0.95, 0.4), - markersize = rand(linspace(0.0001, 0.01, 100), stars) - ) - - scene = Scene() - Makie.volume(rand(32, 32, 32), algorithm = :iso) - center!(scene) - nothing +if is_ci() + Pkg.clone("https://github.com/SimonDanisch/AbstractNumbers.jl.git") + Pkg.checkout("GLAbstraction") + Pkg.checkout("GeometryTypes") + Pkg.checkout("GLVisualize") + Pkg.checkout("MeshIO") + Pkg.add("VisualRegressionTests") + Pkg.checkout("GLWindow") end +if isempty(mode) + # we're not compiling, so we do a reference image test run + include("visual_regression.jl") +else + # when snoop compiling, we simply just execute all sample code directly + parse("all_samples.jl") end - -using .MakieTest -MakieTest.run() diff --git a/test/test.jl b/test/test.jl index a44a4273d84..cf6c103cf7b 100644 --- a/test/test.jl +++ b/test/test.jl @@ -1,7 +1,14 @@ using Makie, GeometryTypes, Colors scene = Scene() +scatter( + Point3f0[(1,0,0), (0,1,0), (0,0,1)], + marker = [:x, :circle, :cross] +) +GLVisualize.visualize(("helo", rand(Point3f0, length("helo")))) +scene[:theme][:scatter][:marker] = :cross +center!(scene) x = map([:dot, :dash, :dashdot], [2, 3, 4]) do ls, lw linesegment(linspace(1, 5, 100), rand(100), rand(100), linestyle = ls, linewidth = lw) @@ -79,9 +86,102 @@ sub = Scene(scene, rotation = Vec4f0(0, 0, 0, 1)) meshscatter(sub, rand(30) + 1.0, rand(30), rand(30)) meshscatter(sub, rand(30), rand(30), rand(30) .+ 1.0) +r = linspace(-2, 2, 4) +Makie.axis(r, r, r) +center!(scene) axis = Vec3f0(0, 0, 1) +io = VideoStream(scene, homedir()*"/Desktop/", "rotation") for angle = linspace(0, 2pi, 100) sub[:rotation] = Makie.qrotation(axis, angle) - sleep(0.1) + recordframe!(io) + sleep(1/15) end +finish(io, "gif") + +keys = ( + :lol, :pos, :test +) +positions, pos, test = get.(attributes, keys, scene) + + +function myvisual(scene, args, attributes) + keys = ( + :positions, :color, :blah, shared..., + ) + Scene(zip(keys, getindex.(attributes, keys)) + +end + + +using Makie, GeometryTypes + + +function plot(scene::S, A::AbstractMatrix{T}) where {T <: AbstractFloat, S <: Scene} + N, M = size(A) + sub = Scene(scene, scale = Vec3f0(1)) + attributes = Dict{Symbol, Any}() + + plots = map(1:M) do i + lines(sub, 1:N, A[:, i]) + end + labels = get(attributes, :labels) do + map(i-> "y $i", 1:M) + end + + lift_node(to_node(A), to_node(Makie.getscreen(scene).area)) do a, area + xlims = (1, size(A, 1)) + ylims = extrema(A) + stretch = Makie.to_nd(fit_ratio(area, (xlims, ylims)), Val{3}, 1) + sub[:scale] = stretch + end + l = legend(scene, plots, labels) + # Only create one axis per scene + xlims = linspace(1, size(A, 1), min(N, 10)) + ylims = linspace(extrema(A)..., 5) + a = get(scene, :axis) do + xlims = (1, size(A, 1)) + ylims = extrema(A) + area = Reactive.value(Makie.getscreen(scene).area) + stretch = Makie.to_nd(fit_ratio(area, (xlims, ylims)), Val{3}, 1) + axis(linspace((xlims .* stretch[1])..., 4), linspace((ylims .* stretch[2])..., 4)) + end + center!(scene) +end + +using Makie, GeometryTypes + +scene = Scene() + +x = (0.5rel, 0.5rel) + +x .* Point2f0(0.5, 0.5) +StaticArrays.similar_type(NTuple{2, Float32}, Int) + + +Makie.VecLike{3, Float32} +Makie.Units.to_absolute(scene, x) +to_positions(scene, (rand(10) .* rel, rand(10) .* rel)) + +convert(Relative{Float64}, 1) + +convert(NTuple{2, Relative{Float64}}, Tuple(Relative.(widths(scene)))) + +plot(scene, rand(11, 2)) + +using VisualRegressionTests + + +function test1(fn) + srand(1234) + scene = Scene(resolution = (500, 500)) + scatter(rand(10), rand(10)) + center!(scene) + save(fn, scene) +end + +cd(@__DIR__) + +test1("test.png") + +result = test_images(VisualTest(test1, "test.png")) diff --git a/test/visual_regression.jl b/test/visual_regression.jl new file mode 100644 index 00000000000..0809aa24eba --- /dev/null +++ b/test/visual_regression.jl @@ -0,0 +1,51 @@ +using VisualRegressionTests, Base.Test + +cd(@__DIR__) +include("visual_regression_funcs.jl") + +reference_image_installed = try + isa(Pkg.installed("ReferenceImages"), VersionNumber) +catch e + false +end +if !reference_image_installed + Pkg.clone("https://github.com/SimonDanisch/ReferenceImages.git") +end + +refpath(i) = Pkg.dir("ReferenceImages", "reference_images", "img_$i.png") + +function record(filepath, func) + srand(1234) + scene = func() + @assert isa(scene, Scene) + Makie.save(filepath, scene) +end + +function record_reference() + i = 1 + while true + # functions in visual_regression_funcs.jl all are named after the same sceme + func = Symbol("testfunc_$i") + isdefined(Main, func) || break # we only have n functions in visual_regression_funcs + func_inst = getfield(Main, func) + record(refpath(i), func_inst) + i += 1 + end +end +# record_reference() + +@testset "Makie visual regression" begin + i = 1 + while true + # functions in visual_regression_funcs.jl all are named after the same sceme + func = Symbol("testfunc_$i") + isdefined(Main, func) || break # we only have n functions in visual_regression_funcs + func_inst = getfield(Main, func) + path = refpath(i) + f(fn) = record(fn, func_inst) + @testset "$func" begin + @test test_images(VisualTest(f, path), popup = false) |> success + end + i += 1 + end +end diff --git a/test/visual_regression_funcs.jl b/test/visual_regression_funcs.jl new file mode 100644 index 00000000000..daba6f45fa2 --- /dev/null +++ b/test/visual_regression_funcs.jl @@ -0,0 +1,402 @@ + +using Makie, GLFW, GeometryTypes, Reactive, FileIO, ColorBrewer, Colors +using GLVisualize +using GLVisualize: loadasset, assetpath + +function xy_data(x, y) + r = sqrt(x*x + y*y) + r == 0.0 ? 1f0 : (sin(r)/r) +end + +function custom_theme(scene) + @theme theme = begin + linewidth = to_float(3) + colormap = to_colormap(:RdYlGn)#to_colormap(:RdPu) + scatter = begin + marker = to_spritemarker(Circle) + markersize = to_float(0.03) + strokecolor = to_color(:white) + strokewidth = to_float(0.01) + glowcolor = to_color(RGBA(0, 0, 0, 0.4)) + glowwidth = to_float(0.1) + end + end + # update theme values + scene[:theme] = theme +end + + + +function testfunc_1() + + img = loadasset("doge.png") + scene = Scene(resolution = (500, 500)) + + display(scene) + show(scene) + println(scene) + + is = image(img) + center!(scene) + subscene = Scene(scene, Signal(SimpleRectangle(0, 0, 200, 200))) + scatter(subscene, rand(100) * 200, rand(100) * 200, markersize = 4) + center!(scene) + +end +function testfunc_2() + + scene = Scene(resolution = (500, 500)); + x = [0, 1, 2, 0]; + y = [0, 0, 1, 2]; + z = [0, 2, 0, 1]; + color = [:red, :green, :blue, :yellow]; + i = [0, 0, 0, 1]; + j = [1, 2, 3, 2]; + k = [2, 3, 1, 3]; + + indices = [1, 2, 3, 1, 3, 4, 1, 4, 2, 2, 3, 4]; + mesh(x, y, z, indices, color = color); + r = linspace(-0.5, 2.5, 4); + axis(r, r, r); + center!(scene); + +end +function testfunc_3() + + scene = Scene(resolution = (500, 500)) + Makie.Makie.volume(rand(32, 32, 32), algorithm = :iso) + center!(scene) + +end +function testfunc_4() + + scene = Scene(resolution = (500, 500)) + heatmap(rand(32, 32)) + center!(scene) + +end +function testfunc_5() + + scene = Scene(resolution = (500, 500)) + r = linspace(-10, 10, 512) + z = ((x, y)-> sin(x) + cos(y)).(r, r') + Makie.contour(r, r, z, levels = 5, color = ColorBrewer.palette("RdYlBu", 5)) + center!(scene) + +end +function testfunc_6() + + scene = Scene(resolution = (500, 500)) + vx = -1:0.1:1; + vy = -1:0.1:1; + + f(x, y) = (sin(x*10) + cos(y*10)) / 4 + psurf = surface(vx, vy, f) + + pos = lift_node(psurf[:x], psurf[:y], psurf[:z]) do x, y, z + vec(Point3f0.(x, y', z .+ 0.5)) + end + pscat = scatter(pos) + plines = lines(view(pos, 1:2:length(pos))) + center!(scene) + @theme theme = begin + markersize = to_markersize2d(0.01) + strokecolor = to_color(:white) + strokewidth = to_float(0.01) + end + # this pushes all the values from theme to the plot + push!(pscat, theme) + pscat[:glow_color] = to_node(RGBA(0, 0, 0, 0.4), x->to_color((), x)) + # apply it to the scene + custom_theme(scene) + # From now everything will be plotted with new theme + psurf = surface(vx, 1:0.1:2, psurf[:z]) + center!(scene) + +end +function testfunc_7() + + scene = Scene(resolution = (500, 500)) + sv = scatter(rand(Point3f0, 100)) + similar(sv, rand(10), rand(10), rand(10), color = :black, markersize = 0.4) + scene + +end +function testfunc_8() + + scene = Scene(resolution = (500, 500)) + large_sphere = HyperSphere(Point3f0(0), 1f0) + positions = decompose(Point3f0, large_sphere) + colS = [Colors.RGBA{Float32}(rand(), rand(), rand(), 1.) for i = 1:length(positions)] + sizesS = [rand(Vec3f0) .* 0.5f0 for i = 1:length(positions)] + meshscatter(positions, color = colS, markersize = sizesS) + center!(scene) + +end +function testfunc_9() + + scene = Scene(resolution = (500, 500)) + y = [ + -0.997669 + -0.979084 + -0.942261 + -0.887885 + -0.81697 + -0.730836 + -0.631088 + -0.519584 + -0.398401 + -0.269797 + -0.136167 + 0.0 + 0.136167 + 0.269797 + 0.398401 + 0.519584 + 0.631088 + 0.730836 + 0.81697 + 0.887885 + 0.942261 + 0.979084 + 0.997669 + ] + contour(linspace(-0.99, 0.99, 23), y, rand(23, 23), levels = 10) + center!(scene) + +end +function testfunc_10() + + scene = Scene(resolution = (500, 500)) + # define points/edges + perturbfactor = 4e1 + N = 3; nbfacese = 30; radius = 0.02 + large_sphere = HyperSphere(Point3f0(0), 1f0) + positions = decompose(Point3f0, large_sphere, 30) + np = length(positions) + pts = [positions[k][l] for k = 1:length(positions), l = 1:3] + pts = vcat(pts, 1.1 * pts + randn(size(pts)) / perturbfactor) # light position influence ? + edges = hcat(collect(1:np), collect(1:np) + np) + ne = size(edges, 1); np = size(pts, 1) + # define markers meshes + meshC = GeometryTypes.GLNormalMesh(GeometryTypes.Cylinder{3, Float32}( + GeometryTypes.Point3f0(0., 0., 0.), + GeometryTypes.Point3f0(0., 0, 1.), + Float32(1)), nbfacese) + + meshS = GeometryTypes.GLNormalMesh(large_sphere, 20) + # define colors, markersizes and rotations + pG = [GeometryTypes.Point3f0(pts[k, 1], pts[k, 2], pts[k, 3]) for k = 1:np] + lengthsC = sqrt.(sum((pts[edges[:,1], :] .- pts[edges[:, 2], :]) .^ 2, 2)) + sizesC = [GeometryTypes.Vec3f0(radius, radius, lengthsC[i]) for i = 1:ne] + sizesC = [Vec3f0(1., 1., 1.) for i = 1:ne] + colorsp = [Colors.RGBA{Float32}(rand(), rand(), rand(), 1.) for i = 1:np] + colorsC = [(colorsp[edges[i, 1]] + colorsp[edges[i, 2]]) / 2. for i = 1:ne] + sizesC = [Vec3f0(radius, radius, lengthsC[i]) for i = 1:ne] + Qlist = zeros(ne, 4) + for k = 1:ne + ct = GeometryTypes.Cylinder{3, Float32}( + GeometryTypes.Point3f0(pts[edges[k, 1], 1], pts[edges[k, 1], 2], pts[edges[k, 1], 3]), + GeometryTypes.Point3f0(pts[edges[k, 2], 1], pts[edges[k, 2], 2], pts[edges[k, 2], 3]), + Float32(1)) + Q = GeometryTypes.rotation(ct) + r = 0.5 * sqrt(1 + Q[1, 1] + Q[2, 2] + Q[3, 3]); Qlist[k, 4] = r + Qlist[k, 1] = (Q[3, 2] - Q[2, 3]) / (4 * r) + Qlist[k, 2] = (Q[1, 3] - Q[3, 1]) / (4 * r) + Qlist[k, 3] = (Q[2, 1] - Q[1, 2]) / (4 * r) + end + rotationsC = AbstractVector[Vec4f0(Qlist[i, 1], Qlist[i, 2], Qlist[i, 3], Qlist[i, 4]) for i = 1:ne] + # plot + hm = Makie.meshscatter(pG[edges[:, 1]], color = colorsC, marker = meshC, + markersize = sizesC, rotations = rotationsC) + hp = Makie.meshscatter(pG, color = colorsp, marker = meshS, markersize = radius) + + r = linspace(-1.3, 1.3, 4); Makie.axis(r, r, r) + center!(scene) + +end +function testfunc_11() + + scene = Scene(resolution = (500, 500)) + large_sphere = HyperSphere(Point3f0(0), 1f0) + positions = decompose(Point3f0, large_sphere) + linepos = view(positions, rand(1:length(positions), 1000)) + lines(linepos, linewidth = 0.1, color = :black) + scatter(positions, strokewidth = 0.02, strokecolor = :white, color = RGBA(0.9, 0.2, 0.4, 0.6)) + r = linspace(-1.5, 1.5, 5) + axis(r, r, r) + scene + +end +function testfunc_12() + + scene = Scene(resolution = (500, 500)) + large_sphere = HyperSphere(Point3f0(0), 1f0) + positions = decompose(Point3f0, large_sphere) + meshscatter(positions, color = RGBA(0.9, 0.2, 0.4, 1)) + scene + +end +function testfunc_13() + + scene = Scene(resolution = (500, 500)) + + r = linspace(-2, 2, 40) + surf_func(i) = [Float32(xy_data(x*i, y*i)) for x = r, y = r] + z = surf_func(20) + surf = surface(r, r, z) + + wf = wireframe(r, r, surf[:z] .+ 1.0, + linewidth = 2f0, color = lift_node(x-> x[5], surf[:colormap]) + ) + xy = linspace(-2.1, 2.1, 4) + axis(xy, xy, linspace(0, 2, 4)) + center!(scene) + + io = VideoStream(scene) + for i in linspace(0, 60, 100) + surf[:z] = surf_func(i) + recordframe!(io) + end + scene + + +end +function testfunc_14() + + scene = Scene(resolution = (500, 500)) + + N = 40 + r = linspace(-2, 2, 40) + surf_func(i) = [Float32(xy_data(x*i, y*i)) for x = r, y = r] + surface( + r, r, surf_func(10), + color = GLVisualize.loadasset("doge.png") + ) + center!(scene) + scene + +end +function testfunc_15() + + scene = Scene(resolution = (500, 500)) + x = GLVisualize.loadasset("cat.obj") + Makie.mesh(x.vertices, x.faces, color = :black) + pos = map(x.vertices, x.normals) do p, n + p => p .+ (normalize(n) .* 0.05f0) + end + linesegment(pos) + scene + + +end +function testfunc_16() + + scene = Scene(resolution = (500, 500)) + mesh(GLVisualize.loadasset("cat.obj")) + r = linspace(-0.1, 1, 4) + center!(scene) + scene + +end +function testfunc_17() + + scene = Scene(resolution = (500, 500)) + cat = load(assetpath("cat.obj"), GLNormalUVMesh) + Makie.mesh(cat, color = loadasset("diffusemap.tga")) + center!(scene) + +end +function testfunc_18() + + scene = Scene(resolution = (500, 500)) + Makie.mesh(Sphere(Point3f0(0), 1f0)) + center!(scene) + scene + + +end +function testfunc_19() + + scene = Scene(resolution = (500, 500)) + wireframe(GLVisualize.loadasset("cat.obj")) + center!(scene) + scene + +end +function testfunc_20() + + scene = Scene(resolution = (500, 500)) + wireframe(Sphere(Point3f0(0), 1f0)) + center!(scene) + scene + +end +function testfunc_21() + + scene = Scene(resolution = (500, 500)) + heatmap(rand(32, 32)) + center!(scene) + +end +function testfunc_22() + + scene = Scene(resolution = (500, 500), color = :black) + stars = 100_000 + scatter((rand(Point3f0, stars) .- 0.5) .* 10, + glowwidth = 0.005, glow_color = :white, color = RGBA(0.8, 0.9, 0.95, 0.4), + markersize = rand(linspace(0.0001, 0.01, 100), stars) + ) + scene + +end +function testfunc_23() + + scene = Scene(resolution = (500, 500)) + Makie.volume(rand(32, 32, 32), algorithm = :iso) + center!(scene) + +end +function testfunc_24() + + scene = Scene(resolution = (500, 500)) + scatter(Point3f0[(1,0,0), (0,1,0), (0,0,1)], marker=[:x, :circle, :cross]) + axis(scene, linspace(0, 1, 4), linspace(0, 1, 4), linspace(0, 1, 4)) + center!(scene); + + +end +function testfunc_25() + + scene = Scene(resolution = (500, 500)) + + x = map([:dot, :dash, :dashdot], [2, 3, 4]) do ls, lw + linesegment(linspace(1, 5, 100), rand(100), rand(100), linestyle = ls, linewidth = lw) + end + push!(x, scatter(linspace(1, 5, 100), rand(100), rand(100))) + center!(scene) + l = Makie.legend(x, ["attribute $i" for i in 1:4]) + l[:position] = (0, 1) + l[:backgroundcolor] = RGBA(0.95, 0.95, 0.95) + l[:strokecolor] = RGB(0.8, 0.8, 0.8) + l[:gap] = 30 + l[:textsize] = 19 + l[:linepattern] = Point2f0[(0,-0.2), (0.5, 0.2), (0.5, 0.2), (1.0, -0.2)] + l[:scatterpattern] = decompose(Point2f0, Circle(Point2f0(0.5, 0), 0.3f0), 9) + l[:markersize] = 2f0 + scene + +end +function testfunc_26() + + scene = Scene(resolution = (500, 500)) + cmap = collect(linspace(to_color(:red), to_color(:blue), 20)) + l = Makie.legend(cmap, 1:4) + l[:position] = (1.0,1.0) + l[:textcolor] = :blue + l[:strokecolor] = :black + l[:strokewidth] = 1 + l[:textsize] = 15 + l[:textgap] = 5 + scene +end