Skip to content

Commit

Permalink
start of the @Frames macro (#338)
Browse files Browse the repository at this point in the history
*@Frames macro with len and `stop`
* support for startof and endof (object or frame)
  • Loading branch information
Wikunia authored Aug 9, 2021
1 parent 3f2cad0 commit 9d6b289
Show file tree
Hide file tree
Showing 11 changed files with 269 additions and 17 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Javis.jl - Changelog

## Unreleased
## Unreleased
- added `@Frames` macro for full power mode of defining frames
- bugfix in `@JLayer` when dimensions are not defined explicitly

## v0.6.1 (7th of August 2021)
Expand Down
7 changes: 5 additions & 2 deletions examples/follow_path.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@ Background(1:220, ground)

# let the points appear one by one
objects = [
Object(frame_start:200, (args...) -> circle(O, 10, :fill), points[i]) for
(frame_start, i) in zip(1:2:(2 * npoints), 1:npoints)
Object(
@Frames(i == 1 ? 1 : prev_start() + 2, stop = 200),
(args...) -> circle(O, 10, :fill),
points[i],
) for i in 1:npoints
]

# easiest to move canvas to draw at origin
Expand Down
12 changes: 7 additions & 5 deletions src/Javis.jl
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,7 @@ function preprocess_frames!(objects::Vector{<:AbstractObject})
)
end

if isempty(CURRENT_OBJECT)
push!(CURRENT_OBJECT, objects[1])
else
CURRENT_OBJECT[1] = objects[1]
end
set_current_object(objects[1])
return frames
end

Expand Down Expand Up @@ -322,8 +318,12 @@ function render(
@error "Currently, only gif and mp4 creation is supported. Not a $ext."
end

# check if livestream is used and livestream if that's the case
_livestream(streamconfig, framerate, video.width, video.height, pathname)

# clear all CURRENT_* constants to not accidentally use a previous video when creating a new one
empty_CURRENT_constants()

# even if liveview = false, show the rendered gif in the cell output
if isdefined(Main, :IJulia) && Main.IJulia.inited
display(MIME("text/html"), """<img src="$(pathname)">""")
Expand Down Expand Up @@ -694,6 +694,8 @@ export rev
export scaleto
export act!
export anim_translate, anim_rotate, anim_rotate_around, anim_scale

export @Frames, prev_start, prev_end, startof, endof
export JBox, JCircle, JEllipse, JLine, JPoly, JRect, JStar, @JShape

# custom override of luxor extensions
Expand Down
3 changes: 3 additions & 0 deletions src/structs/Action.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ mutable struct Action <: AbstractAction
defs::Dict{Symbol,Any}
end

const CURRENT_ACTION = Array{Action,1}()
const PREVIOUS_ACTION = Array{Action,1}()

"""
Action([frames], [Animation], func::Function; keep=true)
Expand Down
99 changes: 98 additions & 1 deletion src/structs/Frames.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mutable struct Frames{T}
end

Base.convert(::Type{Frames}, x) = Frames(nothing, x)
Base.convert(::Type{Frames}, x::Frames) = x
Base.convert(::Type{Frames}, x::UnitRange) = Frames(x, x)

Base.copy(f::Frames) = Frames(f.frames, f.user)
Expand Down Expand Up @@ -65,7 +66,7 @@ function get_frames(parent, elem, frames::Symbol, last_frames::UnitRange; is_fir
end

"""
get_frames(parent, elem, relative::RFrames, last_frames::UnitRang; is_first=falsee)
get_frames(parent, elem, relative::RFrames, last_frames::UnitRange; is_first=falsee)
Return the frames based on a relative frames [`RFrames`](@ref) object and the `last_frames`.
"""
Expand Down Expand Up @@ -95,3 +96,99 @@ function get_frames(parent, elem, glob::GFrames, last_frames::UnitRange; is_firs
end
return glob.frames
end

"""
function get_frames(parent, elem, func_frames::Function, last_frames::UnitRange; is_first = false)
Return the frames based on a specified function. The function `func_frames` is simply evaluated
"""
function get_frames(
parent,
elem,
func_frames::Function,
last_frames::UnitRange;
is_first = false,
)
return func_frames()
end

"""
prev_start()
The start frame of the previous object or for an action the start frame of the parental object.
Can be used to provide frame ranges like:
```
@Frames(prev_start(), 10)
```
"""
function prev_start()
if CURRENT_OBJECT_ACTION_TYPE[1] == :Object
PREVIOUS_OBJECT[1].frames.frames[1]
else
PREVIOUS_ACTION[1].frames.frames[1]
end
end

"""
prev_end()
The end frame of the previous object or for an action the end frame of the parental object.
Can be used to provide frame ranges like:
```
@Frames(prev_end()-10, 10)
```
"""
function prev_end()
if CURRENT_OBJECT_ACTION_TYPE[1] == :Object
PREVIOUS_OBJECT[1].frames.frames[end]
else
PREVIOUS_ACTION[1].frames.frames[end]
end
end

startof(oa::Union{AbstractAction,AbstractObject}) = oa.frames.frames[1]
endof(oa::Union{AbstractAction,AbstractObject}) = oa.frames.frames[end]

"""
@Frames(start, len)
@Frames(start, stop=)
Can be used to define frames using functions like [`prev_start`](@ref) or [`prev_end`](@ref)
# Example
```
red_circ = Object(1:90, (args...)->circ("red"))
blue_circ = Object(@Frames(prev_start()+20, 70), (args...)->circ("blue"))
blue_circ = Object(@Frames(prev_start()+20, stop=90), (args...)->circ("blue"))
```
is the same as
```
red_circ = Object(1:90, (args...)->circ("red"))
blue_circ = Object(21:90, (args...)->circ("blue"))
blue_circ = Object(41:90, (args...)->circ("blue"))
```
"""
macro Frames(start, in_args...)
args = []
kwargs = Pair{Symbol,Any}[]
kwarg_symbols = Symbol[]
for el in in_args
if Meta.isexpr(el, :(=))
push!(kwargs, Pair(el.args...))
push!(kwarg_symbols, el.args[1])
else
push!(args, el)
end
end
stop_idx = findfirst(==(:stop), kwarg_symbols)
if stop_idx !== nothing
stop = kwargs[stop_idx][2]
return esc(quote
Javis.Frames(nothing, () -> ($start):($stop))
end)
elseif isempty(kwarg_symbols)
esc(quote
Javis.Frames(nothing, () -> ($start):($start + $(args[1]) - 1))
end)
end
end
5 changes: 4 additions & 1 deletion src/structs/Object.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ end
holds the current object in an array to be declared as a constant
The current object can be accessed using CURRENT_OBJECT[1]
"""

const CURRENT_OBJECT = Array{Object,1}()


const PREVIOUS_OBJECT = Array{Object,1}()
const CURRENT_OBJECT_ACTION_TYPE = Array{Symbol,1}()

Object(func::Function, args...; kwargs...) = Object(:same, func, args...; kwargs...)

Object(frames, func::Function; kwargs...) = Object(frames, func, O; kwargs...)
Expand Down
113 changes: 113 additions & 0 deletions src/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ function compute_frames!(
is_first = true
counter = 1
for elem in elements
# update (CURRENT/PREVIOUS)_(OBJECT/ACTION) and CURRENT_OBJECT_ACTION_TYPE
if elem isa Object
set_current_object(elem)
empty!(CURRENT_ACTION)
elseif elem isa Action
set_current_action(elem)
end
set_current_action_type(elem)

if last_frames === nothing && get_frames(elem) === nothing
throw(ArgumentError("Frames need to be defined explicitly in the initial
Object/Background or Action."))
Expand Down Expand Up @@ -138,3 +147,107 @@ function get_polypoint_at(points, t; pdist = polydistances(points))
)
return overshootpoint
end

"""
set_previous_object(object::Object)
Set the `object` as `PREVIOUS_OBJECT`
"""
function set_previous_object(object::Object)
if isempty(PREVIOUS_OBJECT)
push!(PREVIOUS_OBJECT, object)
else
PREVIOUS_OBJECT[1] = object
end
end

"""
set_previous_action(action::Action)
Set the `action` as `PREVIOUS_ACTION`
"""
function set_previous_action(action::Action)
if isempty(PREVIOUS_ACTION)
push!(PREVIOUS_ACTION, action)
else
PREVIOUS_ACTION[1] = action
end
end

"""
set_current_object(object::Object)
Set the `object` as `CURRENT_OBJECT` and update `PREVIOUS_OBJECT`/`PREVIOUS_ACTION`
"""
function set_current_object(object::Object)
update_previous_object_or_action()

if isempty(CURRENT_OBJECT)
push!(CURRENT_OBJECT, object)
else
CURRENT_OBJECT[1] = object
end
end

"""
set_current_action(action::Action)
Set the `action` as `CURRENT_ACTION` and update `PREVIOUS_OBJECT`/`PREVIOUS_ACTION`
"""
function set_current_action(action::Action)
update_previous_object_or_action()

if isempty(CURRENT_ACTION)
push!(CURRENT_ACTION, action)
else
CURRENT_ACTION[1] = action
end
end

"""
update_previous_object_or_action()
Update the `PREVIOUS_OBJECT` or `PREVIOUS_ACTION` depending on whether the
last element was an object or an action. This is still saved in `CURRENT_OBJECT_ACTION_TYPE`.
"""
function update_previous_object_or_action()
if !isempty(CURRENT_OBJECT_ACTION_TYPE)
if CURRENT_OBJECT_ACTION_TYPE[1] == :Object
!isempty(CURRENT_OBJECT) && set_previous_object(CURRENT_OBJECT[1])
else
!isempty(CURRENT_ACTION) && set_previous_action(CURRENT_ACTION[1])
end
end
end

"""
set_current_action_type(t)
Set `CURRENT_OBJECT_ACTION_TYPE` to `:Object` or `:Action` depending
on the type of `t`.
"""
function set_current_action_type(t)
type = :Object
if t isa AbstractAction
type = :Action
end
if isempty(CURRENT_OBJECT_ACTION_TYPE)
push!(CURRENT_OBJECT_ACTION_TYPE, type)
else
CURRENT_OBJECT_ACTION_TYPE[1] = type
end
end

"""
empty_CURRENT_constants()
empty all `CURRENT_` constants like `CURRENT_OBJECT`
"""
function empty_CURRENT_constants()
empty!(CURRENT_VIDEO)
empty!(CURRENT_OBJECT)
empty!(CURRENT_ACTION)
empty!(PREVIOUS_OBJECT)
empty!(PREVIOUS_ACTION)
empty!(CURRENT_OBJECT_ACTION_TYPE)
end
12 changes: 7 additions & 5 deletions test/animations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,12 @@ end
red_ball = Object(RFrames(-24:0), (args...) -> circ(O, "red"), p1)
act!(red_ball, Action(anim_rotate_around(from_rot, to_rot, O)))

blue_ball = Object(1:25, (args...) -> circ(O, "blue"), p2)
blue_ball = Object(@Frames(prev_start(), 25), (args...) -> circ(O, "blue"), p2)
act!(blue_ball, Action(anim_rotate_around(to_rot, from_rot, red_ball)))
path_red =
Object(1:25, (video, args...) -> path!(path_of_red, get_position(red_ball), "red"))
path_red = Object(
@Frames(prev_start(), stop = prev_start() + 25 - 1),
(video, args...) -> path!(path_of_red, get_position(red_ball), "red"),
)
path_blue =
Object(:same, (video, args...) -> path!(path_of_blue, pos(blue_ball), "blue"))
string = Object(1:25, (args...) -> rad(pos(red_ball), pos(blue_ball), "black"))
Expand All @@ -133,7 +135,7 @@ end
video = Video(500, 500)
back = Object(1:25, ground, in_global_layer = true)
act!(back, Action(anim_rotate/ 2, π / 2)))
act!(back, Action(1:1, anim_translate(Point(25, 25))))
act!(back, Action(@Frames(prev_start(), stop = 1), anim_translate(Point(25, 25))))

Object(latex_title)
red_ball = Object(RFrames(-24:0), (args...) -> circ_ret_trans(O, "red"), p1)
Expand Down Expand Up @@ -220,7 +222,7 @@ end
p = Point(100, 0)

Background(1:10, ground_black_on_white)
circ = Object((args...) -> circ_ret_trans(), p)
circ = Object(@Frames(1, stop = prev_end()), (args...) -> circ_ret_trans(), p)
act!(circ, Action(anim_rotate_around(0.0, 2π, O)))
Object((args...) -> line(Point(-200, 0), Point(-200, -10 * ang(circ)), :stroke))

Expand Down
Binary file added test/stream_local.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions test/unit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,24 @@
@test Javis.get_frames(objects[2]) == 31:50
@test Javis.get_frames(objects[2].actions[1]) == 1:10
@test Javis.get_frames(objects[2].actions[2]) == 11:20


demo = Video(500, 500)
back = Background(1:50, (args...) -> 1)
obj1 = Object(1:10, (args...) -> 1)
a1 = Action(1:10, anim_scale(1, 2))
a2 = Action(@Frames(startof(a1) + 5, 5), anim_scale(1, 2))
act!(obj1, a1)
act!(obj1, a2)
obj2 = Object(@Frames(prev_start(), 10), (args...) -> 1)
obj3 = Object(@Frames(startof(obj2) + 1, 10), (args...) -> 1)

Javis.preprocess_frames!(demo.objects)
@test Javis.get_frames(obj1) == 1:10
@test Javis.get_frames(obj2) == 1:10
@test Javis.get_frames(obj3) == 2:11
@test Javis.get_frames(obj1.actions[1]) == 1:10
@test Javis.get_frames(obj1.actions[2]) == 6:10
end

@testset "anim_" begin
Expand Down
Loading

0 comments on commit 9d6b289

Please sign in to comment.