Skip to content

Latest commit

 

History

14 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fourier-draw

fourier-draw is a Python tool for turning simple drawings into Fourier epicycle animations.

It can take a hand-drawn image or SVG, extract path geometry, compute Fourier coefficients, and render an animation showing rotating vectors tracing the drawing.

The project supports both:

  • single-curve Fourier animation
  • multi-stroke Fourier animation for disconnected SVG paths

Features

  • Convert .png, .jpg, .jpeg, or .svg inputs into usable SVG paths
  • Sample SVG paths into complex-valued curves
  • Compute Fourier coefficients from sampled drawing paths
  • Render epicycle animations with Matplotlib
  • Optionally render polished math-style animations with Manim
  • Support disconnected SVG paths as separate strokes
  • Run the full pipeline with one command

Installation

Clone the repository:

git clone https://github.com/KaiyueZou/fourier-draw.git
cd fourier-draw

Create and activate a virtual environment:

python3 -m venv .venv
source .venv/bin/activate

Install the package in editable mode:

python -m pip install --upgrade pip setuptools wheel
python -m pip install -e ".[dev]"

For raster image vectorization, install Potrace:

brew install potrace

For MP4 export with Matplotlib, install FFmpeg:

brew install ffmpeg

For Manim support, install the optional Manim dependencies:

brew install cairo pkg-config pango ffmpeg
python -m pip install -e ".[dev,manim]"

Check that the command-line tool works:

fourier-draw hello

Expected output:

fourier-draw is installed and working.

Quick start

Single-curve animation

fourier-draw run examples/input/test_k.png \
  --output examples/output/test_k_animation.mp4 \
  --terms 100 \
  --n-samples 2000 \
  --frames 600 \
  --fps 30

Multi-stroke animation

For an SVG containing multiple separate path elements:

fourier-draw run-strokes examples/input/test_kz_two_strokes.svg \
  --output examples/output/test_kz_animation.mp4 \
  --terms 60 \
  --samples-per-path 1000 \
  --frames-per-stroke 180 \
  --pause-frames 30 \
  --fps 30

Clean drawing-only animation

Hide the epicycle circles and vectors:

fourier-draw run-strokes examples/input/test_kz_two_strokes.svg \
  --output examples/output/test_kz_clean.mp4 \
  --terms 60 \
  --samples-per-path 1000 \
  --frames-per-stroke 180 \
  --pause-frames 30 \
  --fps 30 \
  --no-circles \
  --no-vectors

Manim backend

fourier-draw run-strokes examples/input/test_kz_two_strokes.svg \
  --output examples/output/test_kz_manim.mp4 \
  --backend manim \
  --terms 30 \
  --samples-per-path 1000 \
  --run-time-per-stroke 3 \
  --pause-time 0.5 \
  --quality low

Step-by-step workflow

The one-command pipeline is convenient, but the full workflow can also be run step by step.

1. Vectorize or validate input

fourier-draw vectorize examples/input/test_k.png \
  --output examples/output/test_k.svg

For SVG input, this command validates and copies the SVG:

fourier-draw vectorize examples/input/test_kz_two_strokes.svg \
  --output examples/output/test_kz_two_strokes.svg

2. Sample SVG paths

Single-curve sampling:

fourier-draw sample examples/output/test_k.svg \
  --output examples/output/test_k_points.npy \
  --n-samples 2000

Multi-stroke sampling:

fourier-draw sample-strokes examples/output/test_kz_two_strokes.svg \
  --output examples/output/test_kz_strokes_points.npz \
  --samples-per-path 1000

3. Compute Fourier coefficients

Single-curve coefficients:

fourier-draw coeffs examples/output/test_k_points.npy \
  --output examples/output/test_k_coeffs.npz \
  --terms 100

Multi-stroke coefficients:

fourier-draw coeffs-strokes examples/output/test_kz_strokes_points.npz \
  --output examples/output/test_kz_strokes_coeffs.npz \
  --terms 60

4. Animate

Single-curve Matplotlib animation:

fourier-draw animate examples/output/test_k_coeffs.npz \
  --output examples/output/test_k_animation.mp4 \
  --frames 600 \
  --fps 30

Single-curve Manim animation:

fourier-draw animate-manim examples/output/test_k_coeffs.npz \
  --output examples/output/test_k_manim.mp4 \
  --run-time 8 \
  --fps 30 \
  --quality low

Multi-stroke Matplotlib animation:

fourier-draw animate-strokes examples/output/test_kz_strokes_coeffs.npz \
  --output examples/output/test_kz_strokes_animation.mp4 \
  --frames-per-stroke 180 \
  --pause-frames 30 \
  --fps 30

Multi-stroke Manim animation:

fourier-draw animate-strokes-manim examples/output/test_kz_strokes_coeffs.npz \
  --output examples/output/test_kz_strokes_manim.mp4 \
  --run-time-per-stroke 3 \
  --pause-time 0.5 \
  --fps 30 \
  --quality low

Input types

fourier-draw supports:

  • .png
  • .jpg
  • .jpeg
  • .svg

For the first version, the raster vectorization pipeline works best for:

dark handwriting or line art on a light background

For light drawing on dark background, use:

--invert

Example:

fourier-draw run input.png \
  --output output.mp4 \
  --invert

Centerline paths vs contour paths

There are two common types of SVG path input.

Centerline SVG paths

These are paths where the SVG path itself represents the pen trajectory, with visual thickness supplied by stroke-width.

Example:

<path d="M 0 0 L 100 100" stroke="black" stroke-width="10" fill="none" />

fourier-draw samples the centerline geometry.

This is best for handwriting-style animation.

Contour SVG paths

When raster images are vectorized with Potrace, the resulting SVG usually represents the outline or contour of the foreground shape.

This is best for logos, silhouettes, and filled shapes.

Single-curve vs multi-stroke mode

Single-curve mode

Single-curve mode concatenates SVG paths into one curve.

Use it for:

  • closed shapes
  • logos
  • one continuous drawing
  • debugging

Commands:

fourier-draw run ...
fourier-draw sample ...
fourier-draw coeffs ...
fourier-draw animate ...

Multi-stroke mode

Multi-stroke mode gives each SVG path its own Fourier series.

Use it for:

  • disconnected letters
  • separate handwriting strokes
  • dots and crossbars
  • multi-path SVG drawings

Commands:

fourier-draw run-strokes ...
fourier-draw sample-strokes ...
fourier-draw coeffs-strokes ...
fourier-draw animate-strokes ...

Python API

from fourier_draw import run_single_pipeline, run_strokes_pipeline

run_single_pipeline(
    input_path="examples/input/test_k.png",
    output_path="examples/output/test_k_animation.mp4",
    terms=100,
    n_samples=2000,
)

run_strokes_pipeline(
    input_path="examples/input/test_kz_two_strokes.svg",
    output_path="examples/output/test_kz_animation.mp4",
    terms=60,
    samples_per_path=1000,
)

Development

Install development dependencies:

python -m pip install -e ".[dev]"

Run tests:

pytest

Install Manim extras:

python -m pip install -e ".[dev,manim]"

Known limitations

  • Raster vectorization currently uses Potrace and usually extracts contours, not handwriting centerlines.
  • Multi-stroke mode treats each SVG <path> element as one stroke. It does not yet split subpaths inside one path.
  • Automatic grouping of paths into letters is not yet supported.
  • Manim rendering can be slower than Matplotlib, especially with many Fourier terms.
  • Very noisy raster images may require preprocessing before vectorization.
  • The package currently works best for simple line art, handwriting, logos, and symbolic shapes.

Roadmap

Possible future features:

  • centerline tracing / skeletonization for raster handwriting
  • automatic path grouping
  • better stroke ordering
  • SVG preview utilities
  • richer color/style customization
  • web demo
  • direct GIF optimization
  • support for 3D Fourier curves

License

MIT License.

About

Turn simple drawings into Fourier epicycle animations.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages