Skip to content

Commit

Permalink
Merge branch 'master' into ff/frame_time_budget
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonDanisch authored Jun 21, 2024
2 parents 6cbc60e + 225d0ae commit 02854a2
Show file tree
Hide file tree
Showing 42 changed files with 1,106 additions and 798 deletions.
16 changes: 13 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,17 @@

## [Unreleased]

- CairoMakie: Added argument `pdf_version` to restrict the PDF version when saving a figure as a PDF [#3845](https://github.com/MakieOrg/Makie.jl/pull/3845).
- Improved accuracy of framerate settings in GLMakie [#3954](https://github.com/MakieOrg/Makie.jl/pull/3954)
- Fixes for Menu and DataInspector [#3975](https://github.com/MakieOrg/Makie.jl/pull/3975)
- Add line-loop detection and rendering to GLMakie and WGLMakie [#3907](https://github.com/MakieOrg/Makie.jl/pull/3907)

## [0.21.3] - 2024-06-17

- Fix stack overflows when using `markerspace = :data` with `scatter` [#3960](https://github.com/MakieOrg/Makie.jl/issues/3960).
- CairoMakie: Fix broken SVGs when using non-interpolated image primitives, for example Colorbars, with recent Cairo versions [#3967](https://github.com/MakieOrg/Makie.jl/pull/3967).
- CairoMakie: Add argument `pdf_version` to restrict the PDF version when saving a figure as a PDF [#3845](https://github.com/MakieOrg/Makie.jl/pull/3845).
- CairoMakie: Fix incorrect scaling factor for SVGs with Cairo_jll 1.18 [#3964](https://github.com/MakieOrg/Makie.jl/pull/3964).
- Fixed use of Textbox from Bonito [#3924](https://github.com/MakieOrg/Makie.jl/pull/3924)

## [0.21.2] - 2024-05-22

Expand Down Expand Up @@ -508,8 +517,9 @@ All other changes are collected [in this PR](https://github.com/MakieOrg/Makie.j
- Fixed rendering of `heatmap`s with one or more reversed ranges in CairoMakie, as in `heatmap(1:10, 10:-1:1, rand(10, 10))` [#1100](https://github.com/MakieOrg/Makie.jl/pull/1100).
- Fixed volume slice recipe and added docs for it [#1123](https://github.com/MakieOrg/Makie.jl/pull/1123).

[Unreleased]: https://github.com/MakieOrg/Makie.jl/compare/v0.21.2...HEAD
[0.21.2]: https://github.com/MakieOrg/Makie.jl/compare/v0.21.0...v0.21.2
[Unreleased]: https://github.com/MakieOrg/Makie.jl/compare/v0.21.3...HEAD
[0.21.3]: https://github.com/MakieOrg/Makie.jl/compare/v0.21.2...v0.21.3
[0.21.2]: https://github.com/MakieOrg/Makie.jl/compare/v0.21.1...v0.21.2
[0.21.1]: https://github.com/MakieOrg/Makie.jl/compare/v0.21.0...v0.21.1
[0.21.0]: https://github.com/MakieOrg/Makie.jl/compare/v0.20.10...v0.21.0
[0.20.10]: https://github.com/MakieOrg/Makie.jl/compare/v0.20.9...v0.20.10
Expand Down
6 changes: 4 additions & 2 deletions CairoMakie/Project.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
name = "CairoMakie"
uuid = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
author = ["Simon Danisch <sdanisch@gmail.com>"]
version = "0.12.2"
version = "0.12.3"

[deps]
CRC32c = "8bf52ea8-c179-5cab-976a-9e18b702a9bc"
Cairo = "159f3aea-2a34-519c-b102-8c37f9878175"
Cairo_jll = "83423d85-b0ee-5818-9007-b63ccbeb887a"
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
FreeType = "b38be410-82b0-50bf-ab77-7b57e271db43"
Expand All @@ -17,12 +18,13 @@ PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
[compat]
CRC32c = "1.0, 1.6"
Cairo = "1.0.4"
Cairo_jll = "1.18.0"
Colors = "0.10, 0.11, 0.12"
FileIO = "1.1"
FreeType = "3, 4.0"
GeometryBasics = "0.4.11"
LinearAlgebra = "1.0, 1.6"
Makie = "=0.21.2"
Makie = "=0.21.3"
PrecompileTools = "1.0"
julia = "1.3"

Expand Down
9 changes: 6 additions & 3 deletions CairoMakie/src/infrastructure.jl
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ end
# instead of the whole Scene
# - Recognize when a screen is an image surface, and set scale to render the plot
# at the scale of the device pixel
function draw_plot_as_image(scene::Scene, screen::Screen, primitive::Plot, scale::Number = 1)
function draw_plot_as_image(scene::Scene, screen::Screen{RT}, primitive::Plot, scale::Number = 1) where RT
# you can provide `p.rasterize = scale::Int` or `p.rasterize = true`, both of which are numbers

# Extract scene width in device indepentent units
Expand All @@ -163,8 +163,11 @@ function draw_plot_as_image(scene::Scene, screen::Screen, primitive::Plot, scale
# Cairo.scale(screen.context, w / scr.surface.width, h / scr.surface.height)
Cairo.set_source_surface(screen.context, scr.surface, 0, 0)
p = Cairo.get_source(scr.context)
# this is needed to avoid blurry edges
Cairo.pattern_set_extend(p, Cairo.EXTEND_PAD)
if RT !== SVG
# this is needed to avoid blurry edges in png renderings, however since Cairo 1.18 this
# setting seems to create broken SVGs
Cairo.pattern_set_extend(p, Cairo.EXTEND_PAD)
end
# Set filter doesn't work!?
Cairo.pattern_set_filter(p, Cairo.FILTER_BILINEAR)
Cairo.fill(screen.context)
Expand Down
21 changes: 17 additions & 4 deletions CairoMakie/src/primitives.jl
Original file line number Diff line number Diff line change
Expand Up @@ -202,17 +202,23 @@ project_command(c::ClosePath, scene, space, model) = c

function draw_single(primitive::Lines, ctx, positions)
n = length(positions)
start = positions[begin]

@inbounds for i in 1:n
p = positions[i]
# only take action for non-NaNs
if !isnan(p)
# new line segment at beginning or if previously NaN
if i == 1 || isnan(positions[i-1])
Cairo.move_to(ctx, p...)
start = p
else
Cairo.line_to(ctx, p...)
# complete line segment at end or if next point is NaN
if i == n || isnan(positions[i+1])
if p start
Cairo.close_path(ctx)
end
Cairo.stroke(ctx)
end
end
Expand Down Expand Up @@ -298,7 +304,8 @@ function draw_multi(primitive::Lines, ctx, positions, colors::AbstractArray, lin
prev_position = positions[begin]
prev_nan = isnan(prev_position)
prev_continued = false

start = positions[begin]

if !prev_nan
# first is not nan, move_to
Cairo.move_to(ctx, positions[begin]...)
Expand All @@ -315,6 +322,7 @@ function draw_multi(primitive::Lines, ctx, positions, colors::AbstractArray, lin
# this is nan
if prev_continued
# and this is prev_continued, so set source and stroke to finish previous line
(prev_position start) && Cairo.close_path(ctx)
Cairo.set_line_width(ctx, this_linewidth)
!isnothing(dash) && Cairo.set_dash(ctx, dash .* this_linewidth)
Cairo.set_source_rgba(ctx, red(prev_color), green(prev_color), blue(prev_color), alpha(prev_color))
Expand All @@ -328,6 +336,7 @@ function draw_multi(primitive::Lines, ctx, positions, colors::AbstractArray, lin
if !this_nan
# but this is not nan, so move to this position
Cairo.move_to(ctx, this_position...)
start = this_position
else
# and this is also nan, do nothing
end
Expand All @@ -342,6 +351,7 @@ function draw_multi(primitive::Lines, ctx, positions, colors::AbstractArray, lin

if i == lastindex(positions)
# this is the last element so stroke this
(this_position start) && Cairo.close_path(ctx)
Cairo.set_line_width(ctx, this_linewidth)
!isnothing(dash) && Cairo.set_dash(ctx, dash .* this_linewidth)
Cairo.set_source_rgba(ctx, red(this_color), green(this_color), blue(this_color), alpha(this_color))
Expand Down Expand Up @@ -782,7 +792,7 @@ premultiplied_rgba(a::AbstractArray{<:Color}) = RGBA.(a)
premultiplied_rgba(r::RGBA) = RGBA(r.r * r.alpha, r.g * r.alpha, r.b * r.alpha, r.alpha)
premultiplied_rgba(c::Colorant) = premultiplied_rgba(RGBA(c))

function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Union{Heatmap, Image}))
function draw_atomic(scene::Scene, screen::Screen{RT}, @nospecialize(primitive::Union{Heatmap, Image})) where RT
ctx = screen.context
image = primitive[3][]
xs, ys = primitive[1][], primitive[2][]
Expand Down Expand Up @@ -858,8 +868,11 @@ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Unio
Cairo.scale(ctx, w / s.width, h / s.height)
Cairo.set_source_surface(ctx, s, 0, 0)
p = Cairo.get_source(ctx)
# this is needed to avoid blurry edges
Cairo.pattern_set_extend(p, Cairo.EXTEND_PAD)
if RT !== SVG
# this is needed to avoid blurry edges in png renderings, however since Cairo 1.18 this
# setting seems to create broken SVGs
Cairo.pattern_set_extend(p, Cairo.EXTEND_PAD)
end
filt = interpolate ? Cairo.FILTER_BILINEAR : Cairo.FILTER_NEAREST
Cairo.pattern_set_filter(p, filt)
Cairo.fill(ctx)
Expand Down
10 changes: 7 additions & 3 deletions CairoMakie/src/screen.jl
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,17 @@ struct ScreenConfig
end
end

css_px_per_unit(pt_per_unit) = pt_per_unit / 0.75

function device_scaling_factor(rendertype, sc::ScreenConfig)
isv = is_vector_backend(convert(RenderType, rendertype))
return isv ? sc.pt_per_unit : sc.px_per_unit
rt = convert(RenderType, rendertype)
isv = is_vector_backend(rt)
# from version 1.18 on, Cairo saves SVGs without the pt unit specified, so they are actually in CSS px now
return rt === SVG ? css_px_per_unit(sc.pt_per_unit) : isv ? sc.pt_per_unit : sc.px_per_unit
end

function device_scaling_factor(surface::Cairo.CairoSurface, sc::ScreenConfig)
return is_vector_backend(surface) ? sc.pt_per_unit : sc.px_per_unit
return device_scaling_factor(get_render_type(surface), sc)
end

const LAST_INLINE = Ref{Union{Makie.Automatic,Bool}}(Makie.automatic)
Expand Down
4 changes: 2 additions & 2 deletions GLMakie/Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "GLMakie"
uuid = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
version = "0.10.2"
version = "0.10.3"

[deps]
ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
Expand Down Expand Up @@ -30,7 +30,7 @@ FreeTypeAbstraction = "0.10"
GLFW = "3.3"
GeometryBasics = "0.4.11"
LinearAlgebra = "1.0, 1.6"
Makie = "=0.21.2"
Makie = "=0.21.3"
Markdown = "1.0, 1.6"
MeshIO = "0.4"
ModernGL = "1"
Expand Down
2 changes: 1 addition & 1 deletion GLMakie/assets/shader/line_segment.geom
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ void main(void)
// Set invalid / ignored outputs
f_truncation = vec2(-1e12); // no truncated joint
f_pattern_overwrite = vec4(-1e12, 1.0, 1e12, 1.0); // no joints to overwrite
f_extrusion = vec2(0.5); // no joints needing extrusion
f_extrusion = vec2(0.0); // no joints needing extrusion
f_linepoints = vec4(-1e12);
f_miter_vecs = vec4(-1);

Expand Down
38 changes: 24 additions & 14 deletions GLMakie/assets/shader/lines.geom
Original file line number Diff line number Diff line change
Expand Up @@ -196,22 +196,32 @@ void main(void)
return;
}

// We mark each of the four vertices as valid or not. Vertices can be
// marked invalid on input (eg, if they contain NaN). We also mark them
// invalid if they repeat in the index buffer. This allows us to render to
// the very ends of a polyline without clumsy buffering the position data on the
// CPU side by repeating the first and last points via the index buffer. It
// just requires a little care further down to avoid degenerate normals.
// We mark vertices based on their role in a line segment:
// 0: the vertex is skipped/invalid (i.e. NaN)
// 1: the vertex is valid (part of a plain line segment)
// 2: the vertex is either ..
// a loop target if the previous or next vertex is marked 0
// or a normal valid vertex otherwise
// isvalid[0] and [3] are used to discern whether a line segment is part
// of a continuing line (valid) or a line start/end (invalid). A line only
// ends if the previous / next vertex is invalid
// isvalid[1] and [2] are used to discern whether a line segment should be
// discarded. This should happen if either vertex is invalid or if one of
// the vertices is a loop target.
// A loop target is an extra vertex placed before/after the shared vertex to
// guide joint generation. Consider for example a closed triangle A B C A.
// To cleanly close the loop both A's need to create a joint as if we had
// c A B C A b, but without drawing the c-A and A-b segments. c and b would
// be loop targets, matching C and B in position, but only being valid in
// isvalid[0] and [3], not as a drawn segment in isvalid[1] and [2].
bool isvalid[4] = bool[](
g_valid_vertex[0] == 1 && g_id[0].y != g_id[1].y,
g_valid_vertex[1] == 1,
g_valid_vertex[2] == 1,
g_valid_vertex[3] == 1 && g_id[2].y != g_id[3].y
(g_valid_vertex[0] > 0) && g_id[0].y != g_id[1].y,
(g_valid_vertex[1] > 0) && !((g_valid_vertex[0] == 0) && (g_valid_vertex[1] == 2)),
(g_valid_vertex[2] > 0) && !((g_valid_vertex[2] == 2) && (g_valid_vertex[3] == 0)),
(g_valid_vertex[3] > 0) && g_id[2].y != g_id[3].y
);

if(!isvalid[1] || !isvalid[2]){
// If one of the central vertices is invalid or there is a break in the
// line, we don't emit anything.
return;
}

Expand Down Expand Up @@ -407,8 +417,8 @@ void main(void)
// if joint skipped elongate to new length
// if normal joint elongate a lot to let discard/truncation handle joint
f_extrusion = vec2(
!isvalid[0] ? min(AA_RADIUS, halfwidth) : (adjustment[0] == 0.0 ? 1e12 : halfwidth * abs(extrusion[0][0])),
!isvalid[3] ? min(AA_RADIUS, halfwidth) : (adjustment[1] == 0.0 ? 1e12 : halfwidth * abs(extrusion[1][0]))
!isvalid[0] ? 0.0 : (adjustment[0] == 0.0 ? 1e12 : halfwidth * abs(extrusion[0][0])),
!isvalid[3] ? 0.0 : (adjustment[1] == 0.0 ? 1e12 : halfwidth * abs(extrusion[1][0]))
);

// used to compute width sdf
Expand Down
92 changes: 84 additions & 8 deletions GLMakie/src/glshaders/lines.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,85 @@ gl_color_type_annotation(::Real) = "float"
gl_color_type_annotation(::Makie.RGB) = "vec3"
gl_color_type_annotation(::Makie.RGBA) = "vec4"

function generate_indices(positions)
valid_obs = Observable(Float32[]) # why does this need to be a float?

indices_obs = const_lift(positions) do ps
valid = valid_obs[]
resize!(valid, length(ps))

indices = Cuint[]
sizehint!(indices, length(ps)+2)

# This loop identifies sections of line points A B C D E F bounded by
# the start/end of the list ps or by NaN and generates indices for them:
# if A == F (loop): E A B C D E F B 0
# if A != F (no loop): 0 A B C D E F 0
# where 0 is NaN
# It marks vertices as invalid (0) if they are NaN, valid (1) if they
# are part of a continous line section, or as ghost edges (2) used to
# cleanly close a loop. The shader detects successive vertices with
# 1-2-0 and 0-2-1 validity to avoid drawing ghost segments (E-A from
# 0-E-A-B and F-B from E-F-B-0 which would dublicate E-F and A-B)

last_start_pos = eltype(ps)(NaN)
last_start_idx = -1

for (i, p) in enumerate(ps)
not_nan = isfinite(p)
valid[i] = not_nan

if not_nan
if last_start_idx == -1
# place nan before section of line vertices
# (or dublicate ps[1])
push!(indices, i-1)
last_start_idx = length(indices) + 1
last_start_pos = p
end
# add line vertex
push!(indices, i)

# case loop (loop index set, loop contains at least 3 segments, start == end)
elseif (last_start_idx != -1) && (length(indices) - last_start_idx > 2) &&
(ps[max(1, i-1)] last_start_pos)

# add ghost vertices before an after the loop to cleanly connect line
indices[last_start_idx-1] = max(1, i-2)
push!(indices, indices[last_start_idx+1], i)
# mark the ghost vertices
valid[i-2] = 2
valid[indices[last_start_idx+1]] = 2
# not in loop anymore
last_start_idx = -1

# non-looping line end
elseif (last_start_idx != -1) # effective "last index not NaN"
push!(indices, i)
last_start_idx = -1
# else: we don't need to push repeated NaNs
end
end

# treat ps[end+1] as NaN to correctly finish the line
if (last_start_idx != -1) && (length(indices) - last_start_idx > 2) &&
(ps[end] last_start_pos)

indices[last_start_idx-1] = length(ps) - 1
push!(indices, indices[last_start_idx+1])
valid[end-1] = 2
valid[indices[last_start_idx+1]] = 2
elseif last_start_idx != -1
push!(indices, length(ps))
end

notify(valid_obs)
return indices .- Cuint(1)
end

return indices_obs, valid_obs
end

@nospecialize
function draw_lines(screen, position::Union{VectorTypes{T}, MatTypes{T}}, data::Dict) where T<:Point
p_vec = if isa(position, GPUArray)
Expand All @@ -37,11 +116,13 @@ function draw_lines(screen, position::Union{VectorTypes{T}, MatTypes{T}}, data::
const_lift(vec, position)
end

indices, valid_vertex = generate_indices(p_vec)

color_type = gl_color_type_annotation(data[:color])
resolution = data[:resolution]

@gen_defaults! data begin
total_length::Int32 = const_lift(x-> Int32(length(x)), position)
total_length::Int32 = const_lift(x -> Int32(length(x) - 2), indices)
vertex = p_vec => GLBuffer
color = nothing => GLBuffer
color_map = nothing => Texture
Expand All @@ -53,10 +134,7 @@ function draw_lines(screen, position::Union{VectorTypes{T}, MatTypes{T}}, data::
# Duplicate the vertex indices on the ends of the line, as our geometry
# shader in `layout(lines_adjacency)` mode requires each rendered
# segment to have neighbouring vertices.
indices = const_lift(p_vec) do p
len0 = length(p) - 1
return isempty(p) ? Cuint[] : Cuint[0; 0:len0; len0]
end => to_index_buffer
indices = indices => to_index_buffer
transparency = false
fast = false
shader = GLVisualizeShader(
Expand All @@ -70,9 +148,7 @@ function draw_lines(screen, position::Union{VectorTypes{T}, MatTypes{T}}, data::
)
)
gl_primitive = GL_LINE_STRIP_ADJACENCY
valid_vertex = const_lift(p_vec) do points
map(p-> Float32(all(isfinite, p)), points)
end => GLBuffer
valid_vertex = valid_vertex => GLBuffer
lastlen = const_lift(sumlengths, p_vec, resolution) => GLBuffer
pattern_length = 1f0 # we divide by pattern_length a lot.
debug = false
Expand Down
2 changes: 1 addition & 1 deletion MakieCore/Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "MakieCore"
uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b"
authors = ["Simon Danisch"]
version = "0.8.2"
version = "0.8.3"

[deps]
ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
Expand Down
Loading

0 comments on commit 02854a2

Please sign in to comment.