Skip to content

Commit

Permalink
Let us follow a path (#163)
Browse files Browse the repository at this point in the history
* Let us follow a path
  • Loading branch information
Wikunia authored Sep 17, 2020
1 parent 8cb7b34 commit 68405a5
Show file tree
Hide file tree
Showing 13 changed files with 154 additions and 3 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
- Use [VideoIO](https://github.com/JuliaIO/VideoIO.jl) for faster rendering without temporary images
- Ability to draw text in an animated way
- Ability to morph with `fill` or `stroke` and using `SubAction` to specify changes in color
- An object described by an action can follow a path (a vector of points). See `follow_path`

## 0.1.5 (14th of September 2020)
- Bugfix in svg parser when a layer gets both transformed and scaled
Expand Down
8 changes: 6 additions & 2 deletions src/Javis.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ Create a video with a certain `width` and `height` in pixel.
This also sets `CURRENT_VIDEO`.
"""
function Video(width, height)
# some luxor functions need a drawing ;)
Drawing()
video = Video(width, height, Dict{Symbol,Any}())
if isempty(CURRENT_VIDEO)
push!(CURRENT_VIDEO, video)
Expand Down Expand Up @@ -164,13 +166,15 @@ A SubAction should not be created by hand but instead by using one of the constr
- `transitions::Vector{Transition}`: A list of transitions like [`Translation`](@ref)
- `internal_transitions::Vector{InternalTransition}`:
A list of internal transitions which store the current transition for a specific frame.
- `defs::Dict{Symbol, Any}` any kind of definitions that are relevant for the subaction.
"""
mutable struct SubAction <: AbstractAction
frames::Frames
anim::Animation
func::Function
transitions::Vector{Transition}
internal_transitions::Vector{InternalTransition}
defs::Dict{Symbol,Any}
end

SubAction(transitions::Transition...) = SubAction(:same, transitions...)
Expand Down Expand Up @@ -281,7 +285,7 @@ SubAction(frames, trans::Transition...) =


SubAction(frames, anim::Animation, func::Function, transitions::Transition...) =
SubAction(frames, anim, func::Function, collect(transitions), [])
SubAction(frames, anim, func::Function, collect(transitions), [], Dict{Symbol,Any}())

"""
ActionSetting
Expand Down Expand Up @@ -1428,7 +1432,7 @@ export Video, Action, BackgroundAction, SubAction, Rel
export Line, Translation, Rotation, Transformation, Scaling
export val, pos, ang, get_value, get_position, get_angle
export projection, morph
export appear, disappear, rotate_around
export appear, disappear, rotate_around, follow_path
export rev
export scaleto

Expand Down
60 changes: 60 additions & 0 deletions src/subaction_animations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,63 @@ function _sethue(video, action, subaction, rel_frame)
color = get_interpolation(subaction, rel_frame)
Luxor.sethue(color)
end

"""
follow_path(points::Vector{Point}; closed=true)
Can be applied inside a subaction such that the object defined in the parent action follows a path.
It takes a vector of points which can be created as an example by calling `circle(O, 50)` <- notice that the action is set to `:none` the default.
# Example
```julia
SubAction(1:150, follow_path(star(O, 300)))
```
# Arguments
- `points::Vector{Point}` - the vector of points the object should follow
# Keywords
- `closed::Bool` default: true, sets whether the path is a closed path as for example when
using a circle, ellipse or any polygon. For a bezier path it should be set to false.
"""
function follow_path(points::Vector{Point}; closed = true)
(video, action, subaction, rel_frame) ->
_follow_path(video, action, subaction, rel_frame, points; closed = closed)
end

function _follow_path(video, action, subaction, rel_frame, points; closed = closed)
t = get_interpolation(subaction, rel_frame)
# if not closed it should be always between 0 and 1
if !closed
t = clamp(t, 0.0, 1.0)
end
# if t is discrete and not 0.0 take the last point or first if closed
if rel_frame != 1 && isapprox_discrete(t)
if closed
translate(points[1])
else
translate(points[end])
end
return
end
# get only the fractional part to be between 0 and 1
t -= floor(t)
if rel_frame == 1
# compute the distances only once for performance reasons
subaction.defs[:p_dist] = polydistances(points, closed = closed)
end
if isapprox(t, 0.0, atol = 1e-4)
translate(points[1])
return
end
pdist = subaction.defs[:p_dist]
ind, surplus = nearestindex(pdist, t * pdist[end])

nextind = mod1(ind + 1, length(points))
overshootpoint = between(
points[ind],
points[nextind],
surplus / distance(points[ind], points[nextind]),
)
translate(overshootpoint)
end
4 changes: 4 additions & 0 deletions src/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,7 @@ function get_interpolation(action::AbstractAction, frame)
end
return at(action.anim, t)
end

function isapprox_discrete(val; atol = 1e-4)
return isapprox(val, round(val); atol = atol)
end
83 changes: 83 additions & 0 deletions test/animations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,89 @@ end
end
end

@testset "Following a path" begin
video = Video(800, 600)

anim = Animation([0, 1], [0.0, 2.0], [sineio()])

color_anim = Animation(
[0, 0.5, 1], # must go from 0 to 1
[Lab(colorant"red"), Lab(colorant"cyan"), Lab(colorant"red")],
[sineio(), sineio()],
)

actions = [
Action(
frame_start:(frame_start + 149),
(args...) -> star(O, 20, 5, 0.5, 0, :fill);
subactions = [
SubAction(1:150, anim, follow_path(star(O, 100))),
SubAction(1:150, color_anim, sethue()),
],
) for frame_start in 1:7:22
]

javis(
video,
[BackgroundAction(1:180, ground), actions...],
tempdirectory = "images",
pathname = "",
)

@test_reference "refs/followPath10.png" load("images/0000000010.png")
@test_reference "refs/followPath30.png" load("images/0000000030.png")
@test_reference "refs/followPath100.png" load("images/0000000100.png")
@test_reference "refs/followPath160.png" load("images/0000000160.png")

# Following along a bezier path
function simple_bezier()
P1 = Point(-300, 0)
CP1 = Point(-200, -200)
CP2 = Point(200, -200)
P2 = Point(300, 0)

beziersegment = BezierPathSegment(P1, CP1, CP2, P2)
beziertopoly(beziersegment)
end

video = Video(800, 600)

anim = Animation([0, 1], [0.0, 1.0], [sineio()])

color_anim = Animation(
[0, 0.5, 1], # must go from 0 to 1
[Lab(colorant"red"), Lab(colorant"cyan"), Lab(colorant"red")],
[sineio(), sineio()],
)

actions = [
Action(
frame_start:(frame_start + 149),
(args...) -> star(O, 20, 5, 0.5, 0, :fill);
subactions = [
SubAction(1:150, anim, follow_path(simple_bezier(); closed = false)),
SubAction(1:150, color_anim, sethue()),
],
) for frame_start in 1:7:22
]

javis(
video,
[BackgroundAction(1:180, ground), actions...],
tempdirectory = "images",
pathname = "",
)

@test_reference "refs/followPathBezier10.png" load("images/0000000010.png")
@test_reference "refs/followPathBezier30.png" load("images/0000000030.png")
@test_reference "refs/followPathBezier100.png" load("images/0000000100.png")
@test_reference "refs/followPathBezier160.png" load("images/0000000160.png")

for i in 1:180
rm("images/$(lpad(i, 10, "0")).png")
end
end

@testset "test default kwargs" begin
video = Video(500, 500)
pathname = javis(video, [Action(1:10, ground), Action(1:10, morph(astar, acirc))])
Expand Down
Binary file added test/refs/followPath10.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 test/refs/followPath100.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 test/refs/followPath160.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 test/refs/followPath30.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 test/refs/followPathBezier10.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 test/refs/followPathBezier100.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 test/refs/followPathBezier160.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 test/refs/followPathBezier30.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 68405a5

Please sign in to comment.