% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/morph_pixel.R
\name{pixel_morph_animate}
\alias{pixel_morph_animate}
\title{Pixel-level image morphing (animation)}
\usage{
pixel_morph_animate(
  imgA,
  imgB,
  n_frames = 16L,
  fps = 10L,
  format = c("gif", "webp", "mp4"),
  outfile = NULL,
  show = interactive(),
  mode = c("color_walk", "exact", "recursive", "color_match"),
  lap_method = "jv",
  maximize = FALSE,
  quantize_bits = 5L,
  downscale_steps = 0L,
  alpha = 1,
  beta = 0,
  patch_size = 1L,
  upscale = 1
)
}
\arguments{
\item{imgA}{Source image (file path or magick image object)}

\item{imgB}{Target image (file path or magick image object)}

\item{n_frames}{Integer number of animation frames (default: 16)}

\item{fps}{Frames per second for playback (default: 10)}

\item{format}{Output format: "gif", "webp", or "mp4"}

\item{outfile}{Optional output file path}

\item{show}{Logical, display animation in viewer (default: interactive())}

\item{mode}{Assignment algorithm: "color_walk" (default), "exact",
"recursive", or "color_match" (spatially match pixels of identical
quantized colour, filling the remainder by identity)}

\item{lap_method}{LAP solver method (default: "jv")}

\item{maximize}{Logical, maximize instead of minimize cost (default: FALSE)}

\item{quantize_bits}{Color quantization for "color_walk" mode (default: 5)}

\item{downscale_steps}{Number of 2x reductions before computing assignment (default: 0)}

\item{alpha}{Weight for color distance in cost function (default: 1)}

\item{beta}{Weight for spatial distance in cost function (default: 0)}

\item{patch_size}{Tile size for tiled modes (default: 1)}

\item{upscale}{Post-rendering upscaling factor (default: 1)}
}
\value{
Invisibly returns a list with animation object and metadata:
\item{animation}{magick animation object}
\item{width}{Image width in pixels}
\item{height}{Image height in pixels}
\item{assignment}{Integer vector of 1-based assignment indices (R convention)}
\item{n_pixels}{Total number of pixels}
\item{mode}{Mode used for matching}
\item{upscale}{Upscaling factor applied}
}
\description{
Creates an animated morph by computing optimal pixel assignment from image A
to image B, then rendering intermediate frames showing the transport.
}
\details{
\subsection{Assignment vs Rendering Semantics}{

\strong{CRITICAL:} This function has two separate phases with different semantics:

\strong{Phase 1 - Assignment Computation:}

The assignment is computed by minimizing:
\preformatted{
  cost(i,j) = alpha * color_distance(A[i], B[j]) + 
              beta * spatial_distance(pos_i, pos_j)
}

This means B's COLORS influence which pixels from A map to which positions.

\strong{Phase 2 - Rendering (Transport-Only):}

The renderer uses ONLY A's colors:
\itemize{
\item Intermediate frames: A's pixels move along paths with motion blur
\item Final frame: A's pixels at their assigned positions (sharp, no blur)
\item B's colors NEVER appear in the output
}

\strong{Result:} You get A's colors rearranged to match B's geometry/layout.
}

\subsection{What This Means}{
\itemize{
\item B influences WHERE pixels go (via similarity in cost function)
\item B does NOT determine WHAT COLORS appear in output
\item Final image has A's palette arranged to mimic B's structure
}
}

\subsection{Parameter Guidance}{

\strong{For pure spatial rearrangement (ignore B's colors in assignment):}
\preformatted{
  pixel_morph_animate(A, B, alpha = 0, beta = 1)
}

\strong{For color-similarity matching (default):}
\preformatted{
  pixel_morph_animate(A, B, alpha = 1, beta = 0)
}

\strong{For hybrid (color + spatial):}
\preformatted{
  pixel_morph_animate(A, B, alpha = 1, beta = 0.2)
}
}

\subsection{Permutation Guarantees}{

Assignment is guaranteed to be a bijection (permutation) ONLY when:
\itemize{
\item \code{downscale_steps = 0} (no resolution changes)
\item \code{mode = "exact"} with \code{patch_size = 1}
}

With downscaling or tiled modes, assignment may have:
\itemize{
\item \strong{Overlaps:} Multiple source pixels map to same destination (last write wins)
\item \strong{Holes:} Some destinations never filled (remain transparent)
}

A warning is issued if overlaps/holes are detected in the final frame.
}
}
\examples{
if (requireNamespace("magick", quietly = TRUE)) {
  imgA <- system.file("extdata/icons/circleA_40.png", package = "couplr")
  imgB <- system.file("extdata/icons/circleB_40.png", package = "couplr")
  if (nzchar(imgA) && nzchar(imgB)) {
    outfile <- tempfile(fileext = ".gif")
    pixel_morph_animate(imgA, imgB, outfile = outfile, n_frames = 4, show = FALSE)
  }
}

}
