diff --git a/.github/workflows/format_check.yml b/.github/workflows/format_check.yml new file mode 100644 index 000000000..d0841520a --- /dev/null +++ b/.github/workflows/format_check.yml @@ -0,0 +1,40 @@ +name: format-check + +on: + push: + branches: + - 'master' + tags: '*' + pull_request: + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + julia-version: [1.5.0] + julia-arch: [x86] + os: [ubuntu-latest] + steps: + - uses: julia-actions/setup-julia@latest + with: + version: ${{ matrix.julia-version }} + + - uses: actions/checkout@master + - name: Install JuliaFormatter and format + run: | + julia -e 'using Pkg; Pkg.add("JuliaFormatter")' + julia -e 'using JuliaFormatter; format(".", annotate_untyped_fields_with_any=false, always_for_in=true, whitespace_in_kwargs=true, whitespace_ops_in_indices=true)' + - name: Format check + run: | + julia -e ' + out = Cmd(`git diff --name-only`) |> read |> String + if out == "" + exit(0) + else + @error "Some files have not been formatted !!!" + write(stdout, out) + exit(1) + end' + + diff --git a/CHANGELOG.md b/CHANGELOG.md index b240b624d..f7aa4f715 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Javis.jl - Changelog +## Unreleased v0.2 +- Show progress of rendering using [ProgressMeter.jl](https://github.com/timholy/ProgressMeter.jl) + +## Unreleased +- Ability to scale an object with `Scaling`. Works similar to `Translation` and `Rotation` +- Added JuliaFormatter GitHub Action + ## 0.1.2 (24th of August 2020) - Added capabilities for generating `.mp4` files - Updated testing scheme for `Javis.jl` diff --git a/Project.toml b/Project.toml index 961db926d..ac7442f37 100644 --- a/Project.toml +++ b/Project.toml @@ -8,6 +8,7 @@ FFMPEG = "c87230d0-a227-11e9-1b43-d7ebe4e7570a" LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" LightXML = "9c8b4983-aa76-5018-a973-4c85ecc9e179" Luxor = "ae8d54c2-7ccd-5906-9d76-62fc9837b5bc" +ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [compat] @@ -15,4 +16,5 @@ FFMPEG = "0.3, 0.4" LaTeXStrings = "1.1" LightXML = "0.9" Luxor = "2" +ProgressMeter = "1" julia = "1.4" diff --git a/TODO.md b/TODO.md index 1597bf054..f8bf4871b 100644 --- a/TODO.md +++ b/TODO.md @@ -8,8 +8,6 @@ graph TD B(#102) C(#55) - B --> A - C --> A ``` - [ ] 102 OPEN Ability to see animation live without creating pngs enhancement 2020-08-18 16:46:49 +0000 UTC @@ -37,7 +35,7 @@ B-->A --- -- [ ] 103 OPEN Transformation: Scale enhancement 2020-08-18 16:54:28 +0000 UTC +- [X] 103 OPEN Transformation: Scale enhancement 2020-08-18 16:54:28 +0000 UTC - [ ] 98 OPEN Hijack Plotting Library for Object Positioning enhancement, question 2020-08-17 19:32:37 +0000 UTC @@ -76,7 +74,7 @@ graph TD - [ ] 64 OPEN Javis Templates Low Priority, To the Moon!, documentation, … 2020-08-09 18:31:06 +0000 UTC -- [ ] 56 OPEN ProgressMeter enhancement 2020-08-09 22:21:42 +0000 UTC +- [x] 56 OPEN ProgressMeter enhancement 2020-08-09 22:21:42 +0000 UTC - [ ] 42 OPEN Using Animations.jl enhancement, question 2020-08-05 13:42:17 +0000 UTC diff --git a/docs/make.jl b/docs/make.jl index 4bf8dc252..5c0520d08 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -2,30 +2,28 @@ using Javis using Documenter makedocs(; - modules=[Javis], - authors="Ole Kröger and contributors", - repo="https://github.com/Wikunia/Javis.jl/blob/{commit}{path}#L{line}", - sitename="Javis.jl", - format=Documenter.HTML(; - prettyurls=get(ENV, "CI", "false") == "true", - canonical="https://Wikunia.github.io/Javis.jl", - assets=String[], + modules = [Javis], + authors = "Ole Kröger and contributors", + repo = "https://github.com/Wikunia/Javis.jl/blob/{commit}{path}#L{line}", + sitename = "Javis.jl", + format = Documenter.HTML(; + prettyurls = get(ENV, "CI", "false") == "true", + canonical = "https://Wikunia.github.io/Javis.jl", + assets = String[], ), - pages=[ + pages = [ "Home" => "index.md", - "Tutorials" => [ - "tutorials.md", + "Tutorials" => [ + "tutorials.md", "tutorials/tutorial_1.md", "tutorials/tutorial_2.md", "tutorials/tutorial_3.md", "tutorials/tutorial_4.md", - ], + ], "Mission" => "mission.md", "References" => "references.md", "Contributing" => "contributing.md", ], ) -deploydocs(; - repo="github.com/Wikunia/Javis.jl", -) +deploydocs(; repo = "github.com/Wikunia/Javis.jl") diff --git a/docs/src/tutorials/tutorial_4.md b/docs/src/tutorials/tutorial_4.md index f86d31f99..08aef86e5 100644 --- a/docs/src/tutorials/tutorial_4.md +++ b/docs/src/tutorials/tutorial_4.md @@ -63,7 +63,7 @@ Although one could use standard an `Action` to achieve the same functionality, ` The `subactions` keyword uses a list of [`SubAction`](@ref) structs which are defined in a similar fashion as `Action` with `frames` and a `function` but are in some sense simpler than the `Action`. -A function of a `SubAction` is normally either [`appear`](@ref) or [`disappear`](@ref) at the moment or one of two transformations: [`Translation`](@ref) and [`Rotation`](@ref). +A function of a `SubAction` is normally either [`appear`](@ref) or [`disappear`](@ref) at the moment or one of these transformations: [`Translation`](@ref), [`Rotation`](@ref) and [`Scaling`](@ref). In theory you can define your own but that is way outside of this tutorial. diff --git a/pull_request_template.md b/pull_request_template.md index 6bd53c3a4..03148d6e3 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -6,12 +6,13 @@ If you are contributing to `Javis.jl`, please make sure you are able to check of - [ ] Did I update the `TODO.md` (if applicable)? - [ ] Did I make sure to only change the part of the file where I introduced a new change/feature? - [ ] Did I cover all corner cases to be close to 100% test coverage (if applicable)? +- [ ] Did I properly add Javis dependencies to the `Project.toml` + set an upper bound of the dependency (if applicable)? - [ ] Did I properly add test dependencies to the `test` directory (if applicable)? - [ ] Did I check relevant tutorials that may be affected by changes in this PR? - [ ] Did I clearly articulate why this PR was made the way it was and how it was made? **Link to relevant issue(s)** - +Closes # **How did you address these issues with this PR? What methods did you use?** diff --git a/src/Javis.jl b/src/Javis.jl index cc3637d61..fb453067f 100644 --- a/src/Javis.jl +++ b/src/Javis.jl @@ -5,6 +5,7 @@ using LaTeXStrings using LightXML import Luxor import Luxor: Point, @layer +using ProgressMeter using Random const FRAMES_SYMBOL = [:same] @@ -219,6 +220,10 @@ The current settings of an [`Action`](@ref) which are saved in `action.current_s - `mul_opacity::Float64`: the current multiplier for opacity. The actual opacity is then: `mul_opacity * opacity` - `fontsize::Float64` the current font size +- `current_scale::Tuple{Float64, Float64}`: the current scale +- `desired_scale::Tuple{Float64, Float64}`: the new desired scale +- `mul_scale::Float64`: the multiplier for the new desired scale. + The actual new scale is then: `mul_scale * desired_scale` """ mutable struct ActionSetting line_width::Float64 @@ -226,9 +231,16 @@ mutable struct ActionSetting opacity::Float64 mul_opacity::Float64 # the multiplier of opacity is between 0 and 1 fontsize::Float64 + # scale has three fields instead of just the normal two + # current scale + # desired scale and scale multiplier => `desired_scale*mul_scale` is the new desired scale + # the scale change needs to be computed using `current_scale` and the desired scale + current_scale::Tuple{Float64,Float64} + desired_scale::Tuple{Float64,Float64} + mul_scale::Float64 # the multiplier of scale is between 0 and 1 end -ActionSetting() = ActionSetting(1.0, 1.0, 1.0, 1.0, 10.0) +ActionSetting() = ActionSetting(1.0, 1.0, 1.0, 1.0, 10.0, (1.0, 1.0), (1.0, 1.0), 1.0) """ update_ActionSetting!(as::ActionSetting, by::ActionSetting) @@ -241,6 +253,9 @@ function update_ActionSetting!(as::ActionSetting, by::ActionSetting) as.opacity = by.opacity as.mul_opacity = by.mul_opacity as.fontsize = by.fontsize + as.current_scale = by.current_scale + as.desired_scale = by.desired_scale + as.mul_scale = by.mul_scale end """ @@ -417,6 +432,10 @@ mutable struct InternalRotation <: InternalTransition center::Point end +mutable struct InternalScaling <: InternalTransition + scale::Tuple{Float64,Float64} +end + """ Translation <: Transition @@ -487,6 +506,52 @@ Rotation as a transition from `from` to `to` (in radians) around the origin. """ Rotation(from, to) = Rotation(from, to, O) +""" + Scaling <: Transition + +Stores the scaling similar to [`Translation`](@ref) with `from` and `to`. + +# Example +- Can be called with different constructors like: +``` +Scaling(10) -> Scaling(CURRENT_SCALING, (10.0, 10.0)) +Scaling(10, :my-scale) -> Scaling((10.0, 10.0), :my_scale) +Scaling(10, 2) -> Scaling((10.0, 10.0), (2.0, 2.0)) +Scaling(10, (1,2)) -> Scaling((10.0, 10.0), (1.0, 2.0)) +``` + +# Fields +- `from::Union{Tuple{Float64, Float64}, Symbol}`: The start scaling or a link to it +- `to::Union{Tuple{Float64, Float64}, Symbol}`: The end scaling or a link to it +- `compute_from_once::Bool`: Saves whether the from is computed for the first frame or + every frame. Is true if from is `:_current_scale`. +""" +mutable struct Scaling <: Transition + from::Union{Tuple{Float64,Float64},Symbol} + to::Union{Tuple{Float64,Float64},Symbol} + compute_from_once::Bool +end + +Scaling(to::Tuple) = Scaling(:_current_scale, to, true) +Scaling(to::Real) = Scaling(:_current_scale, convert(Float64, to), true) +Scaling(to::Symbol) = Scaling(:_current_scale, to, true) + +function Scaling(from::Real, to::Real, compute_from_once = false) + from_flt = convert(Float64, from) + to_flt = convert(Float64, to) + Scaling((from_flt, from_flt), (to_flt, to_flt), compute_from_once) +end + +function Scaling(from::Real, to, compute_from_once = false) + flt = convert(Float64, from) + Scaling((flt, flt), to, compute_from_once) +end + +function Scaling(from, to::Real, compute_from_once = false) + flt = convert(Float64, to) + Scaling(from, (flt, flt), compute_from_once) +end + """ Line @@ -687,6 +752,37 @@ function compute_transition!( internal_translation.by = from + t * (to - from) end +""" + compute_transition!(internal_translation::InternalScaling, translation::Scaling, + video, action::AbstractAction, frame) + +Computes the scaling transformation for the `action`. +If the `scaling` is given directly it uses the frame number for interpolation. +If `scaling` includes symbols, the current definition of that symbol is looked up +and used for computation. +""" +function compute_transition!( + internal_scale::InternalScaling, + scale::Scaling, + video, + action::AbstractAction, + frame, +) + t = (frame - first(get_frames(action))) / (length(get_frames(action)) - 1) + # makes sense to only allow 0 ≤ t ≤ 1 + t = min(1.0, t) + from, to = scale.from, scale.to + + if !scale.compute_from_once || frame == first(get_frames(action)) + from isa Symbol && (from = get_scale(from)) + if scale.compute_from_once + scale.from = from + end + end + to isa Symbol && (to = get_scale(to)) + internal_scale.scale = from .+ t .* (to .- from) +end + """ perform_transformation(action::AbstractAction) @@ -717,6 +813,15 @@ function perform_transformation(trans::InternalRotation) rotate(trans.angle) end +""" + perform_transformation(trans::InternalScaling) + +Scale as described in `trans`. +""" +function perform_transformation(trans::InternalScaling) + scaleto(trans.scale...) +end + """ get_value(s::Symbol) @@ -728,6 +833,14 @@ and [`get_angle`](@ref). - `Any`: the value stored by a previous action. """ function get_value(s::Symbol) + is_internal = first(string(s)) == '_' + if is_internal + internal_sym = Symbol(string(s)[2:end]) + if hasfield(ActionSetting, internal_sym) + return getfield(get_current_setting(), internal_sym) + end + end + defs = CURRENT_VIDEO[1].defs if haskey(defs, s) return defs[s] @@ -763,6 +876,29 @@ get_position(s::Symbol) = get_position(val(s)) """ pos(x) = get_position(x) +# As it is just the number tuple -> return it +get_scale(x::Tuple{<:Number,<:Number}) = x + +# If just the number -> return it as a tuple +get_scale(x::Number) = (x, x) + +""" + get_scale(s::Symbol) + +Get access to the scaling that got saved in `s` by a previous action. + +# Returns +- `Scaling`: the scale stored by a previous action. +""" +get_scale(s::Symbol) = get_scale(val(s)) + +""" + scl(x) + +`scl` is just a short-hand for [`get_scale`](@ref) +""" +scl(x) = get_scale(x) + get_angle(t::Transformation) = t.angle @@ -812,6 +948,8 @@ function create_internal_transitions!(action::AbstractAction) push!(action.internal_transitions, InternalTranslation(O)) elseif trans isa Rotation push!(action.internal_transitions, InternalRotation(0.0, O)) + elseif trans isa Scaling + push!(action.internal_transitions, InternalScaling((1.0, 1.0))) end end end @@ -911,10 +1049,10 @@ function javis( else CURRENT_ACTION[1] = actions[1] end - background_settings = ActionSetting() filecounter = 1 - for frame in frames + @showprogress 1 "Rendering frames..." for frame in frames + background_settings = ActionSetting() Drawing( video.width, video.height, @@ -1039,6 +1177,7 @@ end Set the default action values - line_width and calls `Luxor.setline`. - opacity and calls `Luxor.opacity`. +- scale and calls `Luxor.scale`. """ function set_action_defaults!(action) cs = action.current_setting @@ -1046,10 +1185,21 @@ function set_action_defaults!(action) Luxor.setline(current_line_width) current_opacity = cs.opacity * cs.mul_opacity Luxor.setopacity(current_opacity) + + desired_scale = cs.desired_scale .* cs.mul_scale + scaleto(desired_scale...) end -const LUXOR_DONT_EXPORT = - [:boundingbox, :Boxmaptile, :Sequence, :setline, :setopacity, :fontsize, :get_fontsize] +const LUXOR_DONT_EXPORT = [ + :boundingbox, + :Boxmaptile, + :Sequence, + :setline, + :setopacity, + :fontsize, + :get_fontsize, + :scale, +] # Export each function from Luxor for func in names(Luxor; imported = true) @@ -1061,12 +1211,12 @@ end export javis, latex export Video, Action, BackgroundAction, SubAction, Rel -export Line, Translation, Rotation, Transformation +export Line, Translation, Rotation, Transformation, Scaling export val, pos, ang, get_value, get_position, get_angle export projection, morph export appear, disappear # custom override of luxor extensions -export setline, setopacity, fontsize, get_fontsize +export setline, setopacity, fontsize, get_fontsize, scale end diff --git a/src/backgrounds.jl b/src/backgrounds.jl index c82088101..5b666ef0a 100644 --- a/src/backgrounds.jl +++ b/src/backgrounds.jl @@ -23,11 +23,7 @@ Example call of this function within an `Action`. """ function draw_grid(; direction::AbstractString = "TR", line_gap = 25) return (video, action, frame) -> - _draw_grid(video, - action, - frame; - direction = direction, - line_gap = line_gap) + _draw_grid(video, action, frame; direction = direction, line_gap = line_gap) end function _draw_grid( @@ -49,7 +45,7 @@ function _draw_grid( if direction[1] == 'T' # Bottom to top for vertical grid lines - for x_point = min_width:line_gap:max_width + for x_point in min_width:line_gap:max_width start_point = Point(x_point, max_height) finish_point = Point(x_point, min_height) end_point = start_point + step * (finish_point - start_point) @@ -57,7 +53,7 @@ function _draw_grid( end else # Top to bottom motion for vertical grid lines - for x_point = min_width:line_gap:max_width + for x_point in min_width:line_gap:max_width start_point = Point(x_point, min_height) finish_point = Point(x_point, max_height) end_point = start_point + step * (finish_point - start_point) @@ -67,7 +63,7 @@ function _draw_grid( if direction[2] == 'R' # Left to right motion for horizontal grid lines - for y_point = min_height:line_gap:max_height + for y_point in min_height:line_gap:max_height start_point = Point(min_width, y_point) finish_point = Point(max_width, y_point) end_point = start_point + step * (finish_point - start_point) @@ -75,7 +71,7 @@ function _draw_grid( end else # Right to left motion for horizontal grid lines - for y_point = min_height:line_gap:max_height + for y_point in min_height:line_gap:max_height start_point = Point(max_width, y_point) finish_point = Point(min_width, y_point) end_point = start_point + step * (finish_point - start_point) @@ -118,12 +114,12 @@ One will need to define their own path for `tempdirectory` and `pathname`. """ function zero_lines(; direction::AbstractString = "TR", line_thickness = 10) - return (video, action, frame) -> - _zero_lines(video, - action, - frame; - direction = direction, - line_thickness = line_thickness, + return (video, action, frame) -> _zero_lines( + video, + action, + frame; + direction = direction, + line_thickness = line_thickness, ) end @@ -147,21 +143,21 @@ function _zero_lines( step = (frame - first(get_frames(action))) / (length(get_frames(action)) - 1) if direction[1] == 'B' - # Top to bottom motion for vertical line + # Top to bottom motion for vertical line start_point_y = Point(0, min_height) end_point_y = start_point_y + step * (Point(0, max_height) - start_point_y) else - # Bottom to top motion for vertical line + # Bottom to top motion for vertical line start_point_y = Point(0, max_height) end_point_y = start_point_y + step * (Point(0, min_height) - start_point_y) end if direction[2] == 'R' - # Left to right motion for horizontal line + # Left to right motion for horizontal line start_point_x = Point(min_width, 0) end_point_x = start_point_x + step * (Point(max_width, 0) - start_point_x) else - # Right to left motion for horizontal line + # Right to left motion for horizontal line start_point_x = Point(max_width, 0) end_point_x = start_point_x + step * (Point(min_width, 0) - start_point_x) end diff --git a/src/luxor_overrides.jl b/src/luxor_overrides.jl index 712398300..9dad313fa 100644 --- a/src/luxor_overrides.jl +++ b/src/luxor_overrides.jl @@ -16,14 +16,14 @@ line(O, Point(10, 10)) - `linewidth`: the line width in pixel """ function setline(linewidth) - cs = get_current_setting() - cs.line_width = linewidth + cs = get_current_setting() + cs.line_width = linewidth current_line_width = cs.line_width * cs.mul_line_width Luxor.setline(current_line_width) end """ - setopacity(linewidth) + setopacity(opacity) Set the opacity and multiply it with the current multiplier which is i.e. set by [`appear`](@ref) and [`disappear`](@ref). @@ -40,19 +40,109 @@ circle(O, 20, :fill) - `opacity`: the opacity between 0.0 and 1.0 """ function setopacity(opacity) - cs = get_current_setting() - cs.opacity = opacity + cs = get_current_setting() + cs.opacity = opacity current_opacity = cs.opacity * cs.mul_opacity Luxor.setopacity(current_opacity) end +""" + fontsize(fsize) + +Same as `Luxor.fontsize`: Sets the current font size. + +# Example +``` +fontsize(12) +text("Hello World!") +``` + +# Arguments: +- `fsize`: the new font size +""" function fontsize(fsize) - cs = get_current_setting() - cs.fontsize = fsize + cs = get_current_setting() + cs.fontsize = fsize Luxor.fontsize(fsize) end +""" + get_fontsize(fsize) + +Same as `Luxor.get_fontsize` but works with every version of Luxor that is supported by Javis. + +# Example +``` +fontsize(12) +fsize = get_fontsize() +text("Hello World! \$fsize") +``` + +# Returns +- `Float64`: the current font size +""" function get_fontsize() - cs = get_current_setting() + cs = get_current_setting() return cs.fontsize end + +""" + scale(scl) + +Set the scale and multiply it with the current multiplier +which is i.e. set by [`appear`](@ref) and [`disappear`](@ref). + +Normal behavior without any animation is the same as `Luxor.scale`. + +# Example +``` +scale(0.5) +circle(O, 20, :fill) # the radius would be 10 because of the scaling +``` + +# Arguments: +- `scl`: the new default scale +""" +function scale(scl::Number) + scale(scl, scl) +end + +""" + scale(scl_x, scl_y) + +Same as [`scale`](@ref) but the x scale and y scale can be changed independently. + +# Arguments: +- `scl_x`: scale in x direction +- `scl_y`: scale in y direction +""" +function scale(scl_x, scl_y) + cs = get_current_setting() + cs.desired_scale = (scl_x, scl_y) + current_scale = cs.desired_scale .* cs.mul_scale + Luxor.scale(current_scale...) + cs.current_scale = cs.current_scale .* current_scale + # println("cs.current_scale: $(cs.current_scale)") +end + +""" + scaleto(x, y) + +Scale to a specific scaling instead of multiplying it with the current scale. +For scaling on top of the current scale have a look at [`scale`](@ref). +""" +function scaleto(x, y) + cs = get_current_setting() + cs.desired_scale = (x, y) + scaling = (x, y) ./ cs.current_scale + # we divided by 0 but clearly we want to scale to 0 + # -> we want scaling to be 0 not Inf + if x ≈ 0 + scaling = (0.0, scaling[2]) + end + if y ≈ 0 + scaling = (scaling[1], 0.0) + end + Luxor.scale(scaling...) + cs.current_scale = (x, y) +end diff --git a/src/morphs.jl b/src/morphs.jl index a41fd9cd7..afc0716e4 100644 --- a/src/morphs.jl +++ b/src/morphs.jl @@ -25,7 +25,7 @@ function match_num_point!(poly_1::Vector{Point}, poly_2::Vector{Point}) end # the difference of the length of points - diff = l2-l1 + diff = l2 - l1 points_per_edge = div(diff, l1) # how many extra points do we need @@ -39,20 +39,20 @@ function match_num_point!(poly_1::Vector{Point}, poly_2::Vector{Point}) # p1 is the current point in the original polygon p1 = poly_1_orig[i] # p2 is the next point (which is the first of the polygon in the last iteration) - if i+1 > l1 + if i + 1 > l1 p2 = poly_1_orig[1] else - p2 = poly_1_orig[i+1] + p2 = poly_1_orig[i + 1] end # if we need 5 points and have only 4 edges we add 2 points for the first edge rem = 0 if i <= points_per_edge_extra rem = 1 end - for j in 1:points_per_edge+rem + for j in 1:(points_per_edge + rem) # create the interpolated point between p1 and p2 - t = j/(points_per_edge+rem+1) - new_point = p1+t*(p2-p1) + t = j / (points_per_edge + rem + 1) + new_point = p1 + t * (p2 - p1) insert!(poly_1, index, new_point) index += 1 end @@ -137,8 +137,8 @@ function save_morph_polygons!(action::Action, from_func::Function, to_func::Func for i in 1:length(from_poly) overall_distance = 0.0 - for j = 1:length(from_poly) - p1 = from_poly[(j+i-1) % length(from_poly) + 1] + for j in 1:length(from_poly) + p1 = from_poly[(j + i - 1) % length(from_poly) + 1] p2 = to_poly[j] overall_distance += distance(p1, p2) end @@ -149,7 +149,7 @@ function save_morph_polygons!(action::Action, from_func::Function, to_func::Func end new_from_poly = copy(from_poly) for i in 1:length(from_poly) - new_from_poly[i] = from_poly[(i+smallest_i-1) % length(from_poly) + 1] + new_from_poly[i] = from_poly[(i + smallest_i - 1) % length(from_poly) + 1] end action.opts[:from_poly] = new_from_poly @@ -162,8 +162,7 @@ end Internal version of [`morph`](@ref) but described there. """ -function _morph(video::Video, action::Action, frame, - from_func::Function, to_func::Function) +function _morph(video::Video, action::Action, frame, from_func::Function, to_func::Function) # computation of the polygons and the best way to morph in the first frame if frame == first(get_frames(action)) save_morph_polygons!(action, from_func, to_func) @@ -175,12 +174,12 @@ function _morph(video::Video, action::Action, frame, points = action.opts[:points] # compute the interpolation variable `t` for the current frame - t = (frame-first(get_frames(action)))/(length(get_frames(action))-1) + t = (frame - first(get_frames(action))) / (length(get_frames(action)) - 1) for (i, p1, p2) in zip(1:length(from_poly), from_poly, to_poly) - new_point = p1+t*(p2-p1) + new_point = p1 + t * (p2 - p1) points[i] = new_point end - poly(points, :stroke; close=true) + poly(points, :stroke; close = true) end diff --git a/src/subaction_animations.jl b/src/subaction_animations.jl index 6bee3ebff..30fcf64d4 100644 --- a/src/subaction_animations.jl +++ b/src/subaction_animations.jl @@ -28,16 +28,22 @@ end function _appear(video, action, subaction, rel_frame, symbol::Val{:fade_line_width}) # t is between 0 and 1 - t = (rel_frame - first(get_frames(subaction)) + 1)/length(get_frames(subaction)) + t = (rel_frame - first(get_frames(subaction))) / (length(get_frames(subaction)) - 1) action.current_setting.mul_line_width = t end function _appear(video, action, subaction, rel_frame, symbol::Val{:fade}) # t is between 0 and 1 - t = (rel_frame - first(get_frames(subaction)) + 1)/length(get_frames(subaction)) + t = (rel_frame - first(get_frames(subaction))) / (length(get_frames(subaction)) - 1) action.current_setting.mul_opacity = t end +function _appear(video, action, subaction, rel_frame, symbol::Val{:scale}) + # t is between 0 and 1 + t = (rel_frame - first(get_frames(subaction))) / (length(get_frames(subaction)) - 1) + action.current_setting.mul_scale = t +end + """ disappear(s::Symbol) @@ -68,12 +74,18 @@ end function _disappear(video, action, subaction, rel_frame, symbol::Val{:fade_line_width}) # t is between 0 and 1 - t = (rel_frame - first(get_frames(subaction)) + 1)/length(get_frames(subaction)) - action.current_setting.mul_line_width = 1-t + t = (rel_frame - first(get_frames(subaction))) / (length(get_frames(subaction)) - 1) + action.current_setting.mul_line_width = 1 - t end function _disappear(video, action, subaction, rel_frame, symbol::Val{:fade}) # t is between 0 and 1 - t = (rel_frame - first(get_frames(subaction)) + 1)/length(get_frames(subaction)) - action.current_setting.mul_opacity = 1-t + t = (rel_frame - first(get_frames(subaction))) / (length(get_frames(subaction)) - 1) + action.current_setting.mul_opacity = 1 - t +end + +function _disappear(video, action, subaction, rel_frame, symbol::Val{:scale}) + # t is between 0 and 1 + t = (rel_frame - first(get_frames(subaction))) / (length(get_frames(subaction)) - 1) + action.current_setting.mul_scale = 1 - t end diff --git a/src/svg2luxor.jl b/src/svg2luxor.jl index 8a8ba0291..47934675b 100644 --- a/src/svg2luxor.jl +++ b/src/svg2luxor.jl @@ -91,8 +91,9 @@ function draw_obj(::Val{:path}, o, defs) l_pt, c_pt = path_quadratic(c_pt, parse.(Float64, split(args))...) elseif command == 'T' # the control point is a reflection based on the current and last point - control_pt = l_pt+2*(c_pt-l_pt) - l_pt, c_pt = path_quadratic(c_pt, control_pt..., parse.(Float64, split(args))...) + control_pt = l_pt + 2 * (c_pt - l_pt) + l_pt, c_pt = + path_quadratic(c_pt, control_pt..., parse.(Float64, split(args))...) elseif command == 'L' new_pt = Point(parse.(Float64, split(args))...) line(new_pt) @@ -124,8 +125,8 @@ end Moving to the specified point """ -function path_move(x,y) - p = Point(x,y) +function path_move(x, y) + p = Point(x, y) move(p) p end @@ -135,12 +136,12 @@ end Drawing a quadratic bezier curve by computing a cubic one as that is supported by Luxor """ -function path_quadratic(c_pt::Point, x,y, xe, ye) - e_pt = Point(xe,ye) - qc = Point(x,y) +function path_quadratic(c_pt::Point, x, y, xe, ye) + e_pt = Point(xe, ye) + qc = Point(x, y) # compute the control points of a cubic bezier curve - c1 = c_pt+2/3*(qc-c_pt) - c2 = e_pt+2/3*(qc-e_pt) + c1 = c_pt + 2 / 3 * (qc - c_pt) + c2 = e_pt + 2 / 3 * (qc - e_pt) curve(c1, c2, e_pt) return qc, e_pt end @@ -170,16 +171,19 @@ function set_attr(::Val{:transform}, transform_str) if transform_str !== nothing m = match(r"(.+)\((.+)\)", transform_str) type = Symbol(m.captures[1]) - set_transform(Val{type}(), parse.(Float64, strip.(split(m.captures[2],r"[, ]")))...) + set_transform( + Val{type}(), + parse.(Float64, strip.(split(m.captures[2], r"[, ]")))..., + ) end end -set_attr(::Val{:x}, x) = translate(parse(Float64,x),0) -set_attr(::Val{:y}, y) = translate(0,parse(Float64,y)) +set_attr(::Val{:x}, x) = translate(parse(Float64, x), 0) +set_attr(::Val{:y}, y) = translate(0, parse(Float64, y)) set_attr(::Val{Symbol("stroke-width")}, sw) = setline(parse(Float64, sw)) -set_transform(::Val{:translate}, x,y) = translate(x,y) -set_transform(::Val{:scale}, x,y=x) = scale(x,y) +set_transform(::Val{:translate}, x, y) = translate(x, y) +set_transform(::Val{:scale}, x, y = x) = scale(x, y) """ set_transform(::Val{:matrix}, args...) @@ -189,17 +193,17 @@ Multiply the new matrix with the current matrix and set it. function set_transform(::Val{:matrix}, args...) old = cairotojuliamatrix(getmatrix()) update = cairotojuliamatrix(collect(args)) - new = old*update + new = old * update # only the first two rows are considered - setmatrix(vec(new[1:2,:])) + setmatrix(vec(new[1:2, :])) end #= General fallbacks =# -draw_obj(::Union{Val{:title}, Val{:defs}}, o, defs) = nothing +draw_obj(::Union{Val{:title},Val{:defs}}, o, defs) = nothing # no support for colors at this point -set_attr(t::Union{Val{:stroke}, Val{:fill}, Val{Symbol("aria-hidden")}}, args...) = nothing +set_attr(t::Union{Val{:stroke},Val{:fill},Val{Symbol("aria-hidden")}}, args...) = nothing draw_obj(t, o, defs) = @warn "Can't draw $t" set_transform(t, args...) = @warn "Can't transform $t" @@ -218,23 +222,23 @@ function pathsvg(svg) fsize = get_current_setting().fontsize xdoc = parse_string(svg) xroot = root(xdoc) - def_element = get_elements_by_tagname(xroot, "defs")[1] + def_element = get_elements_by_tagname(xroot, "defs")[1] # create a dict for all the definitions - defs = Dict{String, Any}() + defs = Dict{String,Any}() for def in collect(child_elements(def_element)) defs[attribute(def, "id")] = def end x, y, width, height = parse.(Float64, split(attribute(xroot, "viewBox"))) # remove ex in the end - ex_width = parse(Float64, attribute(xroot, "width")[1:end-2]) - ex_height = parse(Float64, attribute(xroot, "height")[1:end-2]) + ex_width = parse(Float64, attribute(xroot, "width")[1:(end - 2)]) + ex_height = parse(Float64, attribute(xroot, "height")[1:(end - 2)]) # everything capsulated in a layer @layer begin # unit ex: half of font size # such that we can scale half of a font size (size of a lower letter) # with the corresponding height of the svg canvas # and the ex_height given in it's description - scale((fsize/2)/(height/ex_height)) + scale((fsize / 2) / (height / ex_height)) translate(-x, -y) for child in collect(child_elements(xroot)) diff --git a/src/util.jl b/src/util.jl index 6f88cbbb6..ddf055e61 100644 --- a/src/util.jl +++ b/src/util.jl @@ -3,7 +3,7 @@ Set action.frames.frames to the computed frames for each action in actions. """ -function compute_frames!(actions::Vector{AA}) where AA <: AbstractAction +function compute_frames!(actions::Vector{AA}) where {AA<:AbstractAction} last_frames = nothing for action in actions if last_frames === nothing && get_frames(action) === nothing diff --git a/test/animations.jl b/test/animations.jl index f92a7482b..29d651a78 100644 --- a/test/animations.jl +++ b/test/animations.jl @@ -82,7 +82,7 @@ end @test_reference "refs/dancing_circles_16.png" load("images/0000000016.png") @test isfile("dancing.gif") - for i = 1:25 + for i in 1:25 rm("images/$(lpad(i, 10, "0")).png") end rm("images/palette.bmp") @@ -178,7 +178,7 @@ end ) @test_reference "refs/dancing_circles_16_rot_trans.png" load("images/0000000016.png") - for i = 1:25 + for i in 1:25 rm("images/$(lpad(i, 10, "0")).png") end end @@ -224,7 +224,7 @@ end ) @test_reference "refs/dancing_circles_16_rot_trans.png" load("images/0000000016.png") - for i = 1:25 + for i in 1:25 rm("images/$(lpad(i, 10, "0")).png") end end @@ -252,7 +252,7 @@ end @test_reference "refs/grid_drawing_br.png" load("images/0000000018.png") @test_reference "refs/grid_drawing_tl.png" load("images/0000000028.png") @test_reference "refs/grid_drawing_tr.png" load("images/0000000038.png") - for i = 1:40 + for i in 1:40 rm("images/$(lpad(i, 10, "0")).png") end end @@ -274,7 +274,7 @@ end ) @test_reference "refs/circle_angle.png" load("images/0000000008.png") - for i = 1:10 + for i in 1:10 rm("images/$(lpad(i, 10, "0")).png") end end @@ -302,7 +302,7 @@ acirc(args...) = circle(Point(100, 100), 30) @test_reference "refs/star2circle5.png" load("images/0000000005.png") @test_reference "refs/star2circle15.png" load("images/0000000015.png") - for i = 1:20 + for i in 1:20 rm("images/$(lpad(i, 10, "0")).png") end end @@ -356,7 +356,39 @@ end @test_reference "refs/nicholas15.png" load("images/0000000015.png") @test_reference "refs/nicholas25.png" load("images/0000000025.png") @test_reference "refs/nicholas35.png" load("images/0000000035.png") - for i = 1:50 + for i in 1:50 + rm("images/$(lpad(i, 10, "0")).png") + end +end + +@testset "Squeezing a circle using scale" begin + demo = Video(500, 500) + javis( + demo, + [ + BackgroundAction(1:100, ground), + Action(:start_scale, (args...) -> (1.0, 1.0)), + Action( + (args...) -> circ(); + subactions = [ + SubAction(1:25, Scaling((1.0, 1.5))), + SubAction(Rel(25), Scaling((2.0, 1.0))), + SubAction(Rel(25), Scaling(:start_scale)), + SubAction(Rel(25), Scaling(2.0)), + ], + ), + Action((args...) -> circ(Point(-100, 0))), + ], + tempdirectory = "images", + pathname = "", + ) + + @test_reference "refs/squeeze15.png" load("images/0000000015.png") + @test_reference "refs/squeeze25.png" load("images/0000000025.png") + @test_reference "refs/squeeze35.png" load("images/0000000035.png") + @test_reference "refs/squeeze65.png" load("images/0000000065.png") + @test_reference "refs/squeeze85.png" load("images/0000000085.png") + for i in 1:100 rm("images/$(lpad(i, 10, "0")).png") end end @@ -407,7 +439,60 @@ end # test that the last frame is completely white @test sum(load("images/0000000050.png")) == RGB{Float64}(500 * 500, 500 * 500, 500 * 500) - for i = 1:50 + for i in 1:50 + rm("images/$(lpad(i, 10, "0")).png") + end +end + +@testset "Scaling circle" begin + video = Video(500, 500) + javis( + video, + [ + BackgroundAction(1:50, ground), + Action(:set_scale, (args...) -> 2), + Action( + (args...) -> circ(), + subactions = [ + SubAction(1:15, Scaling(0.0, :set_scale)), + SubAction(36:50, Scaling(0.0)), + ], + ), + ], + tempdirectory = "images", + pathname = "", + ) + + @test_reference "refs/scalingCircle07.png" load("images/0000000007.png") + @test_reference "refs/scalingCircle25.png" load("images/0000000025.png") + @test_reference "refs/scalingCircle42.png" load("images/0000000042.png") + + # test using appear and disappear + video = Video(500, 500) + javis( + video, + [ + BackgroundAction(1:50, ground), + BackgroundAction(1:50, (args...) -> scale(2)), + Action( + (args...) -> circ(), + subactions = [ + SubAction(1:15, appear(:scale)), + SubAction(36:50, disappear(:scale)), + ], + ), + ], + tempdirectory = "images", + pathname = "", + ) + + @test_reference "refs/scalingCircle07.png" load("images/0000000007.png") + @test_reference "refs/scalingCircle25.png" load("images/0000000025.png") + @test_reference "refs/scalingCircle42.png" load("images/0000000042.png") + # test that the last frame is completely white + @test sum(load("images/0000000050.png")) == + RGB{Float64}(500 * 500, 500 * 500, 500 * 500) + for i in 1:50 rm("images/$(lpad(i, 10, "0")).png") end end diff --git a/test/refs/circlerSquare07opacity.png b/test/refs/circlerSquare07opacity.png index 968e0ea02..dbbdb63ea 100644 Binary files a/test/refs/circlerSquare07opacity.png and b/test/refs/circlerSquare07opacity.png differ diff --git a/test/refs/circlerSquare42opacity.png b/test/refs/circlerSquare42opacity.png index 40f4ba0f9..2f2c1f37b 100644 Binary files a/test/refs/circlerSquare42opacity.png and b/test/refs/circlerSquare42opacity.png differ diff --git a/test/refs/nicholas15.png b/test/refs/nicholas15.png index 1788a1acb..ec3e2be8e 100644 Binary files a/test/refs/nicholas15.png and b/test/refs/nicholas15.png differ diff --git a/test/refs/nicholas35.png b/test/refs/nicholas35.png index 1788a1acb..1afa6e326 100644 Binary files a/test/refs/nicholas35.png and b/test/refs/nicholas35.png differ diff --git a/test/refs/scalingCircle07.png b/test/refs/scalingCircle07.png new file mode 100644 index 000000000..bf7c68264 Binary files /dev/null and b/test/refs/scalingCircle07.png differ diff --git a/test/refs/scalingCircle25.png b/test/refs/scalingCircle25.png new file mode 100644 index 000000000..bf4d21d88 Binary files /dev/null and b/test/refs/scalingCircle25.png differ diff --git a/test/refs/scalingCircle42.png b/test/refs/scalingCircle42.png new file mode 100644 index 000000000..ae7059a9e Binary files /dev/null and b/test/refs/scalingCircle42.png differ diff --git a/test/refs/squeeze15.png b/test/refs/squeeze15.png new file mode 100644 index 000000000..d699315b8 Binary files /dev/null and b/test/refs/squeeze15.png differ diff --git a/test/refs/squeeze25.png b/test/refs/squeeze25.png new file mode 100644 index 000000000..dd371317c Binary files /dev/null and b/test/refs/squeeze25.png differ diff --git a/test/refs/squeeze35.png b/test/refs/squeeze35.png new file mode 100644 index 000000000..b3864f225 Binary files /dev/null and b/test/refs/squeeze35.png differ diff --git a/test/refs/squeeze65.png b/test/refs/squeeze65.png new file mode 100644 index 000000000..24a5efb4c Binary files /dev/null and b/test/refs/squeeze65.png differ diff --git a/test/refs/squeeze85.png b/test/refs/squeeze85.png new file mode 100644 index 000000000..c5599c87f Binary files /dev/null and b/test/refs/squeeze85.png differ diff --git a/test/svg.jl b/test/svg.jl index 4cb5b8d94..dda97d96f 100644 --- a/test/svg.jl +++ b/test/svg.jl @@ -14,11 +14,18 @@ end video = Video(400, 200) - javis(video, [ - BackgroundAction(1:1, latex_ground), - Action((args...)->latex(L"\mathcal{O}(\log{n})")), # default fontsize 50 - Action((args...)->latex_blend(L"\mathcal{O}\left(\frac{\log{x}}{2}\right)", 20)), - ], tempdirectory="images", pathname="") + javis( + video, + [ + BackgroundAction(1:1, latex_ground), + Action((args...) -> latex(L"\mathcal{O}(\log{n})")), # default fontsize 50 + Action( + (args...) -> latex_blend(L"\mathcal{O}\left(\frac{\log{x}}{2}\right)", 20), + ), + ], + tempdirectory = "images", + pathname = "", + ) @test_reference "refs/ologn.png" load("images/0000000001.png") rm("images/0000000001.png") end @@ -36,10 +43,15 @@ end end video = Video(400, 200) - javis(video, [ - BackgroundAction(1:1, latex_ground), - Action((args...)->foreground(L"\mathcal{O}(\log{n})")), - ], tempdirectory="images", pathname="") + javis( + video, + [ + BackgroundAction(1:1, latex_ground), + Action((args...) -> foreground(L"\mathcal{O}(\log{n})")), + ], + tempdirectory = "images", + pathname = "", + ) @test_reference "refs/ologn_circ.png" load("images/0000000001.png") rm("images/0000000001.png") end @@ -60,10 +72,15 @@ end end video = Video(400, 200) - javis(video, [ - BackgroundAction(1:1, latex_ground), - Action((args...)->foreground(L"\mathcal{O}(\log{n})")), - ], tempdirectory="images", pathname="") + javis( + video, + [ + BackgroundAction(1:1, latex_ground), + Action((args...) -> foreground(L"\mathcal{O}(\log{n})")), + ], + tempdirectory = "images", + pathname = "", + ) @test_reference "refs/ologn_circ.png" load("images/0000000001.png") rm("images/0000000001.png") end diff --git a/test/unit.jl b/test/unit.jl index c209a33ef..5cc94900d 100644 --- a/test/unit.jl +++ b/test/unit.jl @@ -1,119 +1,156 @@ @testset "unit tests" begin -@testset "projection" begin - x0 = Line(Point(0, 10), O) - p = Point(10, 20) - @test projection(p, x0) == Point(0, 20) + @testset "projection" begin + x0 = Line(Point(0, 10), O) + p = Point(10, 20) + @test projection(p, x0) == Point(0, 20) - y0 = Line(Point(10, 0), O) - @test projection(p, y0) == Point(10, 0) -end + y0 = Line(Point(10, 0), O) + @test projection(p, y0) == Point(10, 0) + end -@testset "translation" begin - video = Video(500, 500) - # dummy action doesn't need a real function - action = Action(1:100, ()->1, Translation(Point(1,1), Point(100, 100))) - # needs internal translation as well - push!(action.internal_transitions, Javis.InternalTranslation(O)) - Javis.compute_transformation!(action, video, 1) - @test action.internal_transitions[1].by == Point(1,1) - Javis.compute_transformation!(action, video, 50) - @test action.internal_transitions[1].by == Point(50,50) - Javis.compute_transformation!(action, video, 100) - @test action.internal_transitions[1].by == Point(100,100) -end + @testset "translation" begin + video = Video(500, 500) + # dummy action doesn't need a real function + action = Action(1:100, () -> 1, Translation(Point(1, 1), Point(100, 100))) + # needs internal translation as well + push!(action.internal_transitions, Javis.InternalTranslation(O)) + Javis.compute_transformation!(action, video, 1) + @test action.internal_transitions[1].by == Point(1, 1) + Javis.compute_transformation!(action, video, 50) + @test action.internal_transitions[1].by == Point(50, 50) + Javis.compute_transformation!(action, video, 100) + @test action.internal_transitions[1].by == Point(100, 100) + end -@testset "Relative frames" begin - video = Video(500, 500) - action = Action(Rel(10), (args...)->1, Translation(Point(1,1), Point(100, 100))) - # dummy action doesn't need a real function - test_file = javis(video, [ - Action(1:100, (args...)->1, Translation(Point(1,1), Point(100, 100))), - action - ]) - @test Javis.get_frames(action) == 101:110 - rm(test_file) -end + @testset "scaling" begin + video = Video(500, 500) + # dummy action doesn't need a real function + action = Action(0:100, () -> 1, Scaling(0.0, 1.0)) + # needs internal scaling as well + push!(action.internal_transitions, Javis.InternalScaling((0, 0))) + Javis.compute_transformation!(action, video, 0) + @test action.internal_transitions[1].scale == (0.0, 0.0) + Javis.compute_transformation!(action, video, 50) + @test action.internal_transitions[1].scale == (0.5, 0.5) + Javis.compute_transformation!(action, video, 100) + @test action.internal_transitions[1].scale == (1.0, 1.0) + end -@testset "translation from origin" begin - video = Video(500, 500) - # dummy action doesn't need a real function - action = Action(1:100, ()->1, Translation(Point(99, 99))) - # needs internal translation as well - push!(action.internal_transitions, Javis.InternalTranslation(O)) + @testset "Relative frames" begin + video = Video(500, 500) + action = Action(Rel(10), (args...) -> 1, Translation(Point(1, 1), Point(100, 100))) + # dummy action doesn't need a real function + test_file = javis( + video, + [ + Action(1:100, (args...) -> 1, Translation(Point(1, 1), Point(100, 100))), + action, + ], + ) + @test Javis.get_frames(action) == 101:110 + rm(test_file) + end - Javis.compute_transformation!(action, video, 1) - @test action.internal_transitions[1].by == O - Javis.compute_transformation!(action, video, 50) - @test action.internal_transitions[1].by == Point(49,49) - Javis.compute_transformation!(action, video, 100) - @test action.internal_transitions[1].by == Point(99,99) + @testset "translation from origin" begin + video = Video(500, 500) + # dummy action doesn't need a real function + action = Action(1:100, () -> 1, Translation(Point(99, 99))) + # needs internal translation as well + push!(action.internal_transitions, Javis.InternalTranslation(O)) - video = Video(500, 500) - # dummy action doesn't need a real function - action = Action(1:100, ()->1, Translation(99, 99)) - # needs internal translation as well - push!(action.internal_transitions, Javis.InternalTranslation(O)) + Javis.compute_transformation!(action, video, 1) + @test action.internal_transitions[1].by == O + Javis.compute_transformation!(action, video, 50) + @test action.internal_transitions[1].by == Point(49, 49) + Javis.compute_transformation!(action, video, 100) + @test action.internal_transitions[1].by == Point(99, 99) - Javis.compute_transformation!(action, video, 1) - @test action.internal_transitions[1].by == O - Javis.compute_transformation!(action, video, 50) - @test action.internal_transitions[1].by == Point(49,49) - Javis.compute_transformation!(action, video, 100) - @test action.internal_transitions[1].by == Point(99,99) -end + video = Video(500, 500) + # dummy action doesn't need a real function + action = Action(1:100, () -> 1, Translation(99, 99)) + # needs internal translation as well + push!(action.internal_transitions, Javis.InternalTranslation(O)) -@testset "rotations" begin - video = Video(500, 500) - # dummy action doesn't need a real function - action = Action(1:100, ()->1, Rotation(2π)) - # needs internal translation as well - push!(action.internal_transitions, Javis.InternalRotation(0.0, O)) + Javis.compute_transformation!(action, video, 1) + @test action.internal_transitions[1].by == O + Javis.compute_transformation!(action, video, 50) + @test action.internal_transitions[1].by == Point(49, 49) + Javis.compute_transformation!(action, video, 100) + @test action.internal_transitions[1].by == Point(99, 99) + end - Javis.compute_transformation!(action, video, 1) - @test action.internal_transitions[1].angle == 0.0 - Javis.compute_transformation!(action, video, 100) - @test action.internal_transitions[1].angle == 2π + @testset "rotations" begin + video = Video(500, 500) + # dummy action doesn't need a real function + action = Action(1:100, () -> 1, Rotation(2π)) + # needs internal translation as well + push!(action.internal_transitions, Javis.InternalRotation(0.0, O)) - video = Video(500, 500) - # dummy action doesn't need a real function - action = Action(1:100, ()->1, Rotation(2π, Point(2.0, 5.0))) - # needs internal translation as well - push!(action.internal_transitions, Javis.InternalRotation(0.0, O)) + Javis.compute_transformation!(action, video, 1) + @test action.internal_transitions[1].angle == 0.0 + Javis.compute_transformation!(action, video, 100) + @test action.internal_transitions[1].angle == 2π - Javis.compute_transformation!(action, video, 1) - @test action.internal_transitions[1].angle == 0.0 - @test action.internal_transitions[1].center == Point(2.0, 5.0) - Javis.compute_transformation!(action, video, 100) - @test action.internal_transitions[1].angle == 2π - @test action.internal_transitions[1].center == Point(2.0, 5.0) -end + video = Video(500, 500) + # dummy action doesn't need a real function + action = Action(1:100, () -> 1, Rotation(2π, Point(2.0, 5.0))) + # needs internal translation as well + push!(action.internal_transitions, Javis.InternalRotation(0.0, O)) -@testset "Frames errors" begin - video = Video(500, 500) - # throws because the frames of the first action need to be defined explicitly - @test_throws ArgumentError javis(video, [Action((args...)->1, Translation(Point(1,1), Point(100, 100)))]) - # throws because the frames of the first action need to be defined explicitly - @test_throws ArgumentError javis(video, [Action(Rel(10), (args...)->1, Translation(Point(1,1), Point(100, 100)))]) - # throws because :some is not supported as Symbol for `frames` - @test_throws ArgumentError javis(video, [ - Action(1:100, (args...)->1, Translation(Point(1,1), Point(100, 100))), - Action(:some, :id, (args...)->1, Translation(Point(1,1), Point(100, 100))) - ]) -end + Javis.compute_transformation!(action, video, 1) + @test action.internal_transitions[1].angle == 0.0 + @test action.internal_transitions[1].center == Point(2.0, 5.0) + Javis.compute_transformation!(action, video, 100) + @test action.internal_transitions[1].angle == 2π + @test action.internal_transitions[1].center == Point(2.0, 5.0) + end -@testset "Unspecified symbol error" begin - video = Video(500, 500) - # throws because the frames of the first action need to be defined explicitly - @test_throws ErrorException javis(video, [ - Action(1:100, (args...)->1), - Action(1:100, (args...)->line(O, pos(:non_existent), :stroke)) - ]) + @testset "Frames errors" begin + video = Video(500, 500) + # throws because the frames of the first action need to be defined explicitly + @test_throws ArgumentError javis( + video, + [Action((args...) -> 1, Translation(Point(1, 1), Point(100, 100)))], + ) + # throws because the frames of the first action need to be defined explicitly + @test_throws ArgumentError javis( + video, + [Action(Rel(10), (args...) -> 1, Translation(Point(1, 1), Point(100, 100)))], + ) + # throws because :some is not supported as Symbol for `frames` + @test_throws ArgumentError javis( + video, + [ + Action(1:100, (args...) -> 1, Translation(Point(1, 1), Point(100, 100))), + Action( + :some, + :id, + (args...) -> 1, + Translation(Point(1, 1), Point(100, 100)), + ), + ], + ) + end - video = Video(500, 500) - # throws because the frames of the first action need to be defined explicitly - @test_throws ErrorException javis(video, [ - Action(1:100, (args...)->1), - Action(1:100, (args...)->line(O, ang(:non_existent), :stroke)) - ]) -end + @testset "Unspecified symbol error" begin + video = Video(500, 500) + # throws because the frames of the first action need to be defined explicitly + @test_throws ErrorException javis( + video, + [ + Action(1:100, (args...) -> 1), + Action(1:100, (args...) -> line(O, pos(:non_existent), :stroke)), + ], + ) + + video = Video(500, 500) + # throws because the frames of the first action need to be defined explicitly + @test_throws ErrorException javis( + video, + [ + Action(1:100, (args...) -> 1), + Action(1:100, (args...) -> line(O, ang(:non_existent), :stroke)), + ], + ) + end end