Skip to content

Commit

Permalink
Merge branch 'master' into ff/transformation-origin
Browse files Browse the repository at this point in the history
  • Loading branch information
ffreyer committed Oct 15, 2024
2 parents 64e0ed2 + e284765 commit 1d7a095
Show file tree
Hide file tree
Showing 13 changed files with 457 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## [Unreleased]

- Added `origin` to transformation so that the reference point of `rotate!()` and `scale!()` can be modified [#4472](https://github.com/MakieOrg/Makie.jl/pull/4472)
- Improved performance of `record` by avoiding unnecessary copying in common cases [#4475](https://github.com/MakieOrg/Makie.jl/pull/4475).

## [0.21.14] - 2024-10-11

Expand Down
Binary file added assets/cursor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/cursor_pressed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
254 changes: 254 additions & 0 deletions docs/fake_interaction.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
module FakeInteraction

using Makie
using GLMakie
using Makie.Animations

export interaction_record
export MouseTo
export LeftClick
export LeftDown
export LeftUp
export Lazy
export Wait
export relative_pos

@recipe(Cursor) do scene
Theme(
color = :black,
strokecolor = :white,
strokewidth = 1,
width = 10,
notch = 2,
shaftwidth = 2.5,
shaftlength = 4,
headlength = 12,
multiplier = 1,
)
end

function Makie.plot!(p::Cursor)
poly = lift(p.width, p.notch, p.shaftwidth, p.shaftlength, p.headlength) do w, draw, wshaft, lshaft, lhead
ps = Point2f[
(0, 0),
(-w/2, -lhead),
(-wshaft/2, -lhead+draw),
(-wshaft/2, -lhead-lshaft),
(wshaft/2, -lhead-lshaft),
(wshaft/2, -lhead+draw),
(w/2, -lhead),
]

angle = asin((-w/2) / (-lhead))

Makie.Polygon(map(ps) do point
Makie.Mat2f(cos(angle), sin(angle), -sin(angle), cos(angle)) * point
end)
end

scatter!(p, p[1], marker = poly, markersize = p.multiplier, color = p.color, strokecolor = p.strokecolor, strokewidth = p.strokewidth,
glowcolor = (:black, 0.10), glowwidth = 2, transform_marker = true)

return p
end

struct Lazy
f::Function
end

struct MouseTo{T}
target::T
duration::Union{Nothing,Float64}
end

MouseTo(target) = MouseTo(target, nothing)

function mousepositions_frame(m::MouseTo, startpos, t)

dur = duration(m, startpos)

keyframe_from = Animations.Keyframe(0.0, Point2f(startpos))
keyframe_to = Animations.Keyframe(dur, Point2f(m.target))

pos = Animations.interpolate(saccadic(2), t, keyframe_from, keyframe_to)
[pos]
end
function mousepositions_end(m::MouseTo, startpos)
[m.target]
end


duration(mouseto::MouseTo, prev_position) = mouseto.duration === nothing ? automatic_duration(mouseto, prev_position) : mouseto.duration
function automatic_duration(mouseto::MouseTo, prev_position)
dist = sqrt(+(((mouseto.target .- prev_position) .^ 2)...))
0.6 + dist / 1000 * 0.5
end

struct Wait
duration::Float64
end

duration(w::Wait, prev_position) = w.duration

struct LeftClick end

duration(::LeftClick, _) = 0.15
mouseevents_start(l::LeftClick) = [Makie.MouseButtonEvent(Mouse.left, Mouse.press)]
mouseevents_end(l::LeftClick) = [Makie.MouseButtonEvent(Mouse.left, Mouse.release)]

struct LeftDown end

duration(::LeftDown, _) = 0.0
mouseevents_start(l::LeftDown) = [Makie.MouseButtonEvent(Mouse.left, Mouse.press)]

struct LeftUp end

duration(::LeftUp, _) = 0.0
mouseevents_start(l::LeftUp) = [Makie.MouseButtonEvent(Mouse.left, Mouse.release)]

mouseevents_start(obj) = []
mouseevents_end(obj) = []
mouseevents_frame(obj, t) = []
mousepositions_start(obj, startpos) = []
mousepositions_end(obj, startpos) = []
mousepositions_frame(obj, startpos, t) = []

function alpha_blend(fg::Makie.RGBA, bg::Makie.RGB)
r = (fg.r * fg.alpha + bg.r * (1 - fg.alpha))
g = (fg.g * fg.alpha + bg.g * (1 - fg.alpha))
b = (fg.b * fg.alpha + bg.b * (1 - fg.alpha))
return RGBf(r, g, b)
end


function recordframe_with_cursor_overlay!(io, cursor_pos, viewport, cursor_img, cursor_tip_frac)
glnative = Makie.colorbuffer(io.screen, Makie.GLNative)
# Make no copy if already Matrix{RGB{N0f8}}
# There may be a 1px padding for odd dimensions
xdim, ydim = size(glnative)
copy!(view(io.buffer, 1:xdim, 1:ydim), glnative)

render_cursor!(io.buffer, (xdim, ydim), cursor_pos, viewport, cursor_img, cursor_tip_frac)

write(io.io, io.buffer)
return
end

function render_cursor!(buffer, sz, cursor_pos, viewport, cursor_img, cursor_tip_frac)
cursor_loc_idx = round.(Int, cursor_pos ./ viewport.widths .* sz) .- round.(Int, (1, -1) .* (cursor_tip_frac .* size(cursor_img)))
for idx in CartesianIndices(cursor_img)
image_idx = Tuple(idx) .* (1, -1) .+ cursor_loc_idx
if all((1, 1) .<= image_idx .<= sz)
px = buffer[image_idx...]
cursor_px = cursor_img[idx]
buffer[image_idx...] = alpha_blend(cursor_px, px)
end
end
return
end

function interaction_record(func, figlike, filepath, events::AbstractVector; fps = 60, px_per_unit = 2, update = true, kwargs...)
content_scene = Makie.get_scene(figlike)
sz = content_scene.viewport[].widths
# composite_scene = Scene(; camera = campixel!, size = sz)
# scr = display(GLMakie.Screen(), composite_scene)
# img = Observable(zeros(RGBAf, sz...))
# image!(composite_scene, 0..sz[1], 0..sz[2], img)
cursor_position = Observable(sz ./ 2)
content_scene.events.mouseposition[] = tuple(cursor_position[]...)
# curs = cursor!(composite_scene, cursor_position)
if update
Makie.update_state_before_display!(figlike)
end

if isempty(events)
error("Event list is empty")
end

cursor_img = Makie.FileIO.load(joinpath(@__DIR__, "..", "assets", "cursor.png"))'
cursor_pressed_img = Makie.FileIO.load(joinpath(@__DIR__, "..", "assets", "cursor_pressed.png"))'
cursor_tip_frac = (0.3, 0.15)

record(content_scene, filepath; framerate = fps, px_per_unit, kwargs...) do io
t = 0.0
t_event = 0.0
current_duration = 0.0

i_event = 1
i_frame = 1
event_startposition = Point2f(content_scene.events.mouseposition[])

while i_event <= length(events)
event = events[i_event]
if event isa Lazy
event = event.f(figlike)
end

t_event += current_duration # from previous
event_startposition = Point2f(content_scene.events.mouseposition[])
current_duration = duration(event, event_startposition)

mouseevents = mouseevents_start(event)
for mouseevent in mouseevents
content_scene.events.mousebutton[] = mouseevent
end
mousepositions = mousepositions_start(event, event_startposition)
for mouseposition in mousepositions
content_scene.events.mouseposition[] = tuple(mouseposition...)
cursor_position[] = mouseposition
end

while t < t_event + current_duration
mouseevents = mouseevents_frame(event, t - t_event)
for mouseevent in mouseevents
content_scene.events.mousebutton[] = mouseevent
end
mousepositions = mousepositions_frame(event, event_startposition, t - t_event)
for mouseposition in mousepositions
content_scene.events.mouseposition[] = tuple(mouseposition...)
cursor_position[] = mouseposition
end

func(i_frame, t)
# img[] = rotr90(Makie.colorbuffer(figlike, update = false))
# if content_scene.events.mousebutton[].action === Makie.Mouse.press
# curs.multiplier = 0.8
# else
# curs.multiplier = 1.0
# end

mouse_pressed = content_scene.events.mousebutton[].action === Makie.Mouse.press

recordframe_with_cursor_overlay!(
io,
content_scene.events.mouseposition[],
content_scene.viewport[],
mouse_pressed ? cursor_pressed_img : cursor_img,
cursor_tip_frac
)
i_frame += 1
t = i_frame / fps
end

mouseevents = mouseevents_end(event)
for mouseevent in mouseevents
content_scene.events.mousebutton[] = mouseevent
end
mousepositions = mousepositions_end(event, event_startposition)
for mouseposition in mousepositions
content_scene.events.mouseposition[] = tuple(mouseposition...)
cursor_position[] = mouseposition
end

i_event += 1
end
return
end
return
end

interaction_record(figlike, filepath, events::AbstractVector; kwargs...) = interaction_record((args...,) -> nothing, figlike, filepath, events; kwargs...)

relative_pos(block, rel) = Point2f(block.layoutobservables.computedbbox[].origin .+ rel .* block.layoutobservables.computedbbox[].widths)

end
1 change: 1 addition & 0 deletions docs/makedocs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ end
include("figure_block.jl")
include("attrdocs_block.jl")
include("shortdocs_block.jl")
include("fake_interaction.jl")

docs_url = "docs.makie.org"
repo = "github.com/MakieOrg/Makie.jl.git"
Expand Down
Binary file added docs/src/assets/beeswarm_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/src/assets/geomakie_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 13 additions & 5 deletions docs/src/ecosystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,42 @@

These packages and sites are maintained by third parties. If you install packages, keep an eye on version conflicts or downgrades as the Makie ecosystem is developing quickly so things break occasionally.

## AlgebraOfGraphics.jl
## [AlgebraOfGraphics.jl](https://github.com/MakieOrg/AlgebraOfGraphics.jl)

Grammar-of-graphics style plotting, inspired by ggplot2.

```@raw html
<img src="./assets/aog_example.png" style="max-height: 30vh;">
```

## Beautiful Makie
## [Beautiful Makie](https://beautiful.makie.org/dev/)

This third-party gallery contains many advanced examples.

```@raw html
<img src="./assets/beautifulmakie_example.png" style="max-height: 30vh;">
```

## GraphMakie.jl
## [GraphMakie.jl](https://github.com/MakieOrg/GraphMakie.jl)

Graphs with two- and three-dimensional layout algorithms.

```@raw html
<img src="./assets/graphmakie.png" style="max-height: 30vh;">
```

## GeoMakie.jl
## [GeoMakie.jl](https://github.com/MakieOrg/GeoMakie.jl)

Geographic plotting utilities including projections.

```@raw html
<img src="./assets/geomakie_example.png" style="max-height: 30vh;">
```
```

## [SwarmMakie.jl](https://github.com/MakieOrg/SwarmMakie.jl)

Beeswarm plots for Makie.jl!

```@raw html
<img src="./assets/beeswarm_example.png" style="max-height: 30vh;">
```
37 changes: 36 additions & 1 deletion docs/src/reference/blocks/button.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Button

```@figure backend=GLMakie
```@example button
using GLMakie
GLMakie.activate!() # hide
fig = Figure()
Expand All @@ -24,8 +26,41 @@ barplot!(counts, color = cgrad(:Spectral)[LinRange(0, 1, 5)])
ylims!(ax, 0, 20)
fig
nothing # hide
```

```@setup button
using ..FakeInteraction
events = [
Wait(0.5),
Lazy() do fig
MouseTo(relative_pos(buttons[1], (0.3, 0.3)))
end,
LeftClick(),
Wait(0.2),
LeftClick(),
Wait(0.2),
LeftClick(),
Wait(0.4),
Lazy() do fig
MouseTo(relative_pos(buttons[4], (0.7, 0.2)))
end,
Wait(0.2),
LeftClick(),
Wait(0.2),
LeftClick(),
Wait(0.2),
LeftClick(),
Wait(0.5)
]
interaction_record(fig, "button_example.mp4", events)
```

```@raw html
<video autoplay loop muted playsinline src="./button_example.mp4" width="600"/>
```
## Attributes

```@attrdocs
Expand Down
Loading

0 comments on commit 1d7a095

Please sign in to comment.