From be97725e7aedfe9f906d2502370c19091fdc69bd Mon Sep 17 00:00:00 2001 From: SimonDanisch Date: Tue, 13 Feb 2024 13:46:34 +0100 Subject: [PATCH] move gappy & friends to Makie --- GLMakie/src/glshaders/lines.jl | 40 +++++---------------------------- WGLMakie/src/lines.jl | 41 +++++++++------------------------- src/utilities/utilities.jl | 30 +++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 65 deletions(-) diff --git a/GLMakie/src/glshaders/lines.jl b/GLMakie/src/glshaders/lines.jl index 0413f88cf13..342f9017ced 100644 --- a/GLMakie/src/glshaders/lines.jl +++ b/GLMakie/src/glshaders/lines.jl @@ -29,36 +29,6 @@ function intensity_convert_tex(intensity::VecOrSignal{T}, verts) where T Texture(intensity) end end -#TODO NaNMath.min/max? -dist(a, b) = abs(a-b) -mindist(x, a, b) = min(dist(a, x), dist(b, x)) -function gappy(x, ps) - n = length(ps) - x <= first(ps) && return first(ps) - x - for j=1:(n-1) - p0 = ps[j] - p1 = ps[min(j+1, n)] - if p0 <= x && p1 >= x - return mindist(x, p0, p1) * (isodd(j) ? 1 : -1) - end - end - return last(ps) - x -end -function ticks(points, resolution) - # This is used to map a vector of `points` to a signed distance field. The - # points mark transition between "on" and "off" section of the pattern. - - # The output should be periodic so the signed distance field value - # representing points[1] should be equal to the one representing points[end]. - # => range(..., length = resolution+1)[1:end-1] - - # points[end] should still represent the full length of the pattern though, - # so we need rescaling by ((resolution + 1) / resolution) - - scaled = ((resolution + 1) / resolution) .* points - r = range(first(scaled), stop=last(scaled), length=resolution+1)[1:end-1] - return Float16[-gappy(x, scaled) for x = r] -end @nospecialize function draw_lines(screen, position::Union{VectorTypes{T}, MatTypes{T}}, data::Dict) where T<:Point @@ -110,10 +80,10 @@ function draw_lines(screen, position::Union{VectorTypes{T}, MatTypes{T}}, data:: if !isa(to_value(pattern), Vector) error("Pattern needs to be a Vector of floats. Found: $(typeof(pattern))") end - tex = GLAbstraction.Texture(map(pt -> ticks(pt, 100), pattern), x_repeat = :repeat) + tex = GLAbstraction.Texture(lift(Makie.linestyle_to_sdf, pattern); x_repeat=:repeat) data[:pattern] = tex end - data[:pattern_length] = map(pt -> Float32(last(pt) - first(pt)), pattern) + data[:pattern_length] = lift(pt -> Float32(last(pt) - first(pt)), pattern) end data[:intensity] = intensity_convert(intensity, vertex) @@ -123,7 +93,7 @@ end function draw_linesegments(screen, positions::VectorTypes{T}, data::Dict) where T <: Point @gen_defaults! data begin vertex = positions => GLBuffer - color = default(RGBA, s, 1) => GLBuffer + color = nothing => GLBuffer color_map = nothing => Texture color_norm = nothing thickness = 2f0 => GLBuffer @@ -151,9 +121,9 @@ function draw_linesegments(screen, positions::VectorTypes{T}, data::Dict) where if !isa(to_value(pattern), Vector) error("Pattern needs to be a Vector of floats. Found: $(typeof(pattern))") end - tex = GLAbstraction.Texture(map(pt -> ticks(pt, 100), pattern), x_repeat = :repeat) + tex = GLAbstraction.Texture(lift(Makie.linestyle_to_sdf, pattern); x_repeat=:repeat) data[:pattern] = tex - data[:pattern_length] = map(pt -> Float32(last(pt) - first(pt)), pattern) + data[:pattern_length] = lift(pt -> Float32(last(pt) - first(pt)), pattern) end robj = assemble_shader(data) return robj diff --git a/WGLMakie/src/lines.jl b/WGLMakie/src/lines.jl index 16b13eb6e27..4a446c94c03 100644 --- a/WGLMakie/src/lines.jl +++ b/WGLMakie/src/lines.jl @@ -1,24 +1,4 @@ # Same as GLMakie, see GLMakie/shaders/lines.jl -# TODO: maybe move to Makie? -dist(a, b) = abs(a-b) -mindist(x, a, b) = min(dist(a, x), dist(b, x)) -function gappy(x, ps) - n = length(ps) - x <= first(ps) && return first(ps) - x - for j=1:(n-1) - p0 = ps[j] - p1 = ps[min(j+1, n)] - if p0 <= x && p1 >= x - return mindist(x, p0, p1) * (isodd(j) ? 1 : -1) - end - end - return last(ps) - x -end -function ticks(points, resolution) - scaled = ((resolution + 1) / resolution) .* points - r = range(first(scaled), stop=last(scaled), length=resolution+1)[1:end-1] - return Float16[-gappy(x, scaled) for x = r] -end function serialize_three(scene::Scene, plot::Union{Lines, LineSegments}) Makie.@converted_attribute plot (linewidth, linestyle) @@ -28,13 +8,14 @@ function serialize_three(scene::Scene, plot::Union{Lines, LineSegments}) :depth_shift => plot.depth_shift, :picking => false ) + if isnothing(to_value(linestyle)) uniforms[:pattern] = false uniforms[:pattern_length] = 1f0 else # TODO: pixel per unit - uniforms[:pattern] = Sampler(map(pt -> ticks(pt, 100), linestyle), x_repeat = :repeat) - uniforms[:pattern_length] = map(pt -> Float32(last(pt) - first(pt)), linestyle) + uniforms[:pattern] = Sampler(lift(Makie.linestyle_to_sdf, plot, linestyle); x_repeat=:repeat) + uniforms[:pattern_length] = lift(ls -> Float32(last(ls) - first(ls)), linestyle) end color = plot.calculated_colors @@ -55,18 +36,18 @@ function serialize_three(scene::Scene, plot::Union{Lines, LineSegments}) indices = Observable(Int[]) points_transformed = lift(plot, transform_func_obs(plot), plot[1], plot.space) do tf, ps, space - output = apply_transform(tf, ps, space) + transformed_points = apply_transform(tf, ps, space) # TODO: Do this in javascript? - if isempty(output) + if isempty(transformed_points) empty!(indices[]) notify(indices) - return output + return transformed_points else - sizehint!(empty!(indices[]), length(output) + 2) + sizehint!(empty!(indices[]), length(transformed_points) + 2) was_nan = true - for i in eachindex(output) + for i in eachindex(transformed_points) # dublicate first and last element of line selection - if isnan(output[i]) + if isnan(transformed_points[i]) if !was_nan push!(indices[], i-1) # end of line dublication end @@ -78,10 +59,10 @@ function serialize_three(scene::Scene, plot::Union{Lines, LineSegments}) push!(indices[], i) end - push!(indices[], length(output)) + push!(indices[], length(transformed_points)) notify(indices) - return output[indices[]] + return transformed_points[indices[]] end end positions = lift(serialize_buffer_attribute, plot, points_transformed) diff --git a/src/utilities/utilities.jl b/src/utilities/utilities.jl index 450b2125854..30e8408c9f3 100644 --- a/src/utilities/utilities.jl +++ b/src/utilities/utilities.jl @@ -470,3 +470,33 @@ function available_plotting_methods() end return meths end + +mindist(x, a, b) = min(abs(a - x), abs(b - x)) +function gappy(x, ps) + n = length(ps) + x <= first(ps) && return first(ps) - x + for j in 1:(n - 1) + p0 = ps[j] + p1 = ps[min(j + 1, n)] + if p0 <= x && p1 >= x + return mindist(x, p0, p1) * (isodd(j) ? 1 : -1) + end + end + return last(ps) - x +end + + +# This is used to map a vector of `points` to a signed distance field. The +# points mark transition between "on" and "off" section of the pattern. + +# The output should be periodic so the signed distance field value +# representing points[1] should be equal to the one representing points[end]. +# => range(..., length = resolution+1)[1:end-1] + +# points[end] should still represent the full length of the pattern though, +# so we need rescaling by ((resolution + 1) / resolution) +function linestyle_to_sdf(linestyle::AbstractVector{<:Real}, resolution::Real=100) + scaled = ((resolution + 1) / resolution) .* linestyle + r = range(first(scaled); stop=last(scaled), length=resolution + 1)[1:(end - 1)] + return Float16[-gappy(x, scaled) for x in r] +end