tests/testthat/test-wiggle.R

test_that("wiggle() inserts 2 extra frames per selected frame", {
  images <- make_film(n = 3L)
  result <- suppressMessages(wiggle(images = images, degrees = 3, frames = 2L))
  expect_equal(length(result), 5L)
})

test_that("wiggle() labels include +/- degree annotations", {
  images <- make_film(n = 3L, labels = c("A", "B", "C"))
  result <- suppressMessages(wiggle(images = images, degrees = 5, frames = 2L))
  expect_equal(
    attr(result, "labels"),
    c("A", "B", "B [wiggled +5\u00b0]", "B [wiggled -5\u00b0]", "C")
  )
})

test_that("wiggle() handles multiple selected frames with correct offset", {
  images <- make_film(n = 3L)
  result <- suppressMessages(wiggle(images = images, degrees = 3, frames = c(1L, 2L)))
  expect_equal(length(result), 7L)
})

test_that("wiggle() frames = NULL applies to all frames", {
  images <- make_film(n = 2L)
  result <- suppressMessages(wiggle(images = images, degrees = 3))
  expect_equal(length(result), 6L)
})

test_that("wiggle() handles first frame", {
  images <- make_film(n = 3L)
  result <- suppressMessages(wiggle(images = images, degrees = 3, frames = 1L))
  expect_equal(length(result), 5L)
})

test_that("wiggle() handles last frame", {
  images <- make_film(n = 3L)
  result <- suppressMessages(wiggle(images = images, degrees = 3, frames = 3L))
  expect_equal(length(result), 5L)
})

test_that("wiggle() errors on negative degrees", {
  images <- make_film(n = 3L)
  expect_error(wiggle(images = images, degrees = -3))
})

# ── regression: canvas size and fill colour ───────────────────────────────────

test_that("wiggle() preserves the original canvas size for all frames", {
  # Regression: image_rotate() expands the canvas; wiggle() must crop it back.
  # Before the fix, wiggled frames were ~11x11 instead of 10x10.
  images <- make_film(n = 3L)
  result <- suppressMessages(wiggle(images = images, degrees = 5, frames = 1:2))
  info <- magick::image_info(result)
  expect_true(all(info$width  == 10L))
  expect_true(all(info$height == 10L))
})

test_that("wiggle() fills rotation corners with the frame's own background colour", {
  # Regression: image_rotate() was using a stale ImageMagick fill colour
  # instead of the frame's background, producing visible artefacts.
  bg_color <- "#aabbcc"
  frame  <- magick::image_blank(20L, 20L, color = bg_color)
  images <- Reduce(c, replicate(3L, frame, simplify = FALSE))
  result <- suppressMessages(wiggle(images = images, degrees = 5, frames = 2L))
  # Wiggled frames sit at positions 3 (+5°) and 4 (-5°) after the fix.
  expected_rgb <- c(0xaaL, 0xbbL, 0xccL)
  for (pos in c(3L, 4L)) {
    corner <- as.vector(as.integer(magick::image_data(
      magick::image_crop(result[pos], "1x1+0+0"), channels = "rgb"
    )))
    expect_equal(corner, expected_rgb,
                 label = sprintf("corner of wiggled frame %d", pos))
  }
})

test_that("wiggle() messages the frame sequence", {
  images <- make_film(n = 2L)
  withr::with_options(list(stopmotion.verbose = TRUE), {
    expect_message(
      wiggle(images = images, degrees = 3),
      regexp = "Frame sequence after wiggle"
    )
  })
})

Try the stopmotion package in your browser

Any scripts or data that you put into this service are public.

stopmotion documentation built on March 24, 2026, 5:06 p.m.