Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ jobs:
run: |
pip install ford

# Install ffmpeg for animation generation
sudo apt-get update
sudo apt-get install -y ffmpeg

# Install fpm (Fortran Package Manager) - same version as CI
wget https://github.com/fortran-lang/fpm/releases/download/v0.12.0/fpm-0.12.0-linux-x86_64-gcc-12
chmod +x fpm-0.12.0-linux-x86_64-gcc-12
Expand All @@ -55,6 +59,10 @@ jobs:
# Run all examples to generate outputs
make example

# Build and run special examples (including animation)
chmod +x scripts/compile_special_examples.sh
./scripts/compile_special_examples.sh || true

# Copy generated outputs to media directory (preserving structure)
for dir in output/example/fortran/*/; do
example_name=$(basename "$dir")
Expand Down
16 changes: 16 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ build:
# Build and run the examples
example: create_build_dirs
fpm run --example $(FPM_FLAGS_TEST) $(ARGS)
# Build and run special examples that need manual compilation
@if [ -z "$(ARGS)" ]; then \
./scripts/compile_special_examples.sh 2>/dev/null || true; \
fi

# Build and run the apps for debugging
debug:
Expand Down Expand Up @@ -89,7 +93,19 @@ doc:
ford README.md
# Copy example media files to doc build directory for proper linking
mkdir -p build/doc/media/examples
# Copy from doc/media if it exists (GitHub Actions workflow populates this)
if [ -d doc/media/examples ]; then cp -r doc/media/examples/* build/doc/media/examples/ 2>/dev/null || true; fi
# Also copy directly from output directory if available (for local builds)
for dir in output/example/fortran/*/; do \
if [ -d "$$dir" ]; then \
example_name=$$(basename "$$dir"); \
mkdir -p "build/doc/media/examples/$$example_name"; \
cp "$$dir"*.png "build/doc/media/examples/$$example_name/" 2>/dev/null || true; \
cp "$$dir"*.txt "build/doc/media/examples/$$example_name/" 2>/dev/null || true; \
cp "$$dir"*.pdf "build/doc/media/examples/$$example_name/" 2>/dev/null || true; \
cp "$$dir"*.mp4 "build/doc/media/examples/$$example_name/" 2>/dev/null || true; \
fi; \
done

# Generate coverage report
coverage:
Expand Down
102 changes: 56 additions & 46 deletions doc/example/animation.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Animation

# Animation

Create animated plots with enhanced FFmpeg pipe reliability and comprehensive error recovery.
Create animated plots with enhanced FFmpeg pipe reliability and GitHub Pages integration.

## Quick Start

Expand All @@ -18,14 +18,38 @@ integer :: status
! Create animation
anim = FuncAnimation(update_func, frames=60, interval=50, fig=fig)

! Save with enhanced error handling
call anim%save("animation.mp4", fps=24, status=status)
! Save to GitHub Pages structure
call create_output_directory()
call save_animation(anim, "output/example/fortran/animation/animation.mp4", 24, status)
if (status /= 0) print *, "Enhanced error recovery available"
```

## Enhanced FFmpeg Integration (Issue #186)
## GitHub Pages Integration

**Download Animation**: [animation.mp4](../media/examples/animation/animation.mp4)

The animation example generates MP4 files in a structured directory:

```
output/example/fortran/animation/animation.mp4
```

This path integrates seamlessly with GitHub Pages documentation, making animated examples directly accessible via web links.

### Output Structure

```fortran
! Create directory and save animation
call create_output_directory()
call save_animation(anim, "output/example/fortran/animation/animation.mp4", 24, status)
```

**Result**: Animation saved to `output/example/fortran/animation/animation.mp4` for GitHub Pages access.

## Enhanced FFmpeg Integration

**Latest Improvements:**
- GitHub Pages MP4 download integration
- Enhanced pipe reliability with robust error recovery
- Cross-platform file validation and error diagnostics
- Improved Windows binary pipe handling
Expand Down Expand Up @@ -74,9 +98,10 @@ program save_animation_demo
! Create animation with figure reference
anim = FuncAnimation(update_wave, frames=NFRAMES, interval=50, fig=fig)

! Save as MP4 video with 24 fps
! Save as MP4 video with 24 fps to GitHub Pages structure
print *, "Saving animation as MP4..."
call save_animation_with_error_handling(anim, "animation.mp4", 24)
call create_output_directory()
call save_animation_with_error_handling(anim, "output/example/fortran/animation/animation.mp4", 24)

contains

Expand All @@ -100,7 +125,7 @@ contains
integer, intent(in) :: fps
integer :: status

call anim%save(filename, fps, status)
call save_animation(anim, filename, fps, status)

select case (status)
case (0)
Expand All @@ -121,6 +146,17 @@ contains
print *, "✗ Unknown error (status:", status, ") - check system diagnostics"
end select
end subroutine save_animation_with_error_handling

subroutine create_output_directory()
integer :: mkdir_status

! Create directory structure for GitHub Pages integration
call execute_command_line("mkdir -p output/example/fortran/animation", &
exitstat=mkdir_status)
if (mkdir_status /= 0) then
print *, "Warning: Could not create output directory structure"
end if
end subroutine create_output_directory

end program save_animation_demo
```
Expand Down Expand Up @@ -176,37 +212,13 @@ ffprobe -v error -show_format animation.mp4
file animation.mp4 # Should show: ISO Media, MP4 v2
```

## Example Code Structure

```fortran
! Initialize animation
call anim%init(fps=30, duration=5.0)

! Generate frames
do i = 1, n_frames
! Update data
call update_data(t)

! Plot frame
call fig%clear()
call fig%add_plot(x, y)

! Add frame
call anim%add_frame(fig)
end do

! Save video
call anim%save('animation.mp4')
```

## Windows-Specific Examples

### Handling Paths with Spaces
### Cross-Platform Paths
```fortran
! Windows paths with spaces work automatically
character(len=256) :: output_file
output_file = "C:\Users\User Name\Documents\animation.mp4"
call anim%save(output_file, fps=24, status=status)
! GitHub Pages structure works on all platforms
call save_animation(anim, "output/example/fortran/animation/animation.mp4", 24, status)
```

### Error Handling for Windows
Expand All @@ -221,19 +233,17 @@ else if (status == -1) then
end if
```

### Directory Creation
```fortran
! Create output directory on Windows
character(len=256) :: output_dir
output_dir = "animations"
if (is_windows()) then
call system('mkdir "' // trim(output_dir) // '" 2>NUL')
else
call system('mkdir -p "' // trim(output_dir) // '"')
end if
```

## Output
## Accessing Animation Files

**Local Development**:
- Generated file: `output/example/fortran/animation/animation.mp4`
- Use any video player to view locally

**GitHub Pages**:
- Download link: `[animation.mp4](../media/examples/animation/animation.mp4)`
- Direct browser access from documentation
- Integrated with example documentation workflow

## Troubleshooting (Enhanced)

Expand Down
18 changes: 15 additions & 3 deletions example/fortran/animation/save_animation_demo.f90
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ program save_animation_demo
! Create animation with figure reference
anim = FuncAnimation(update_wave, frames=NFRAMES, interval=50, fig=fig)

! Save as MP4 video with 24 fps
! Save as MP4 video with 24 fps to GitHub Pages structure
print *, "Saving animation as MP4..."
call save_animation_with_error_handling(anim, "animation.mp4", 24)
call create_output_directory()
call save_animation_with_error_handling(anim, "output/example/fortran/animation/animation.mp4", 24)

contains

Expand All @@ -55,7 +56,7 @@ subroutine save_animation_with_error_handling(anim, filename, fps)
integer, intent(in) :: fps
integer :: status

call anim%save(filename, fps, status)
call save_animation(anim, filename, fps, status)

select case (status)
case (0)
Expand All @@ -79,4 +80,15 @@ subroutine save_animation_with_error_handling(anim, filename, fps)
end select
end subroutine save_animation_with_error_handling

subroutine create_output_directory()
integer :: mkdir_status

! Create directory structure for GitHub Pages integration
call execute_command_line("mkdir -p output/example/fortran/animation", &
exitstat=mkdir_status)
if (mkdir_status /= 0) then
print *, "Warning: Could not create output directory structure"
end if
end subroutine create_output_directory

end program save_animation_demo
30 changes: 30 additions & 0 deletions scripts/compile_special_examples.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash
# Build special examples that require manual compilation

# Exit on error
set -e

echo "Building special examples..."

# Find the build directory with module files
BUILD_DIR=$(find build -name "fortplot.mod" -type f 2>/dev/null | head -1 | xargs dirname)
LIB_DIR=$(find build -name "libfortplot.a" -type f 2>/dev/null | head -1 | xargs dirname)

if [ -z "$BUILD_DIR" ] || [ -z "$LIB_DIR" ]; then
echo "Error: fortplot library not built. Run 'make build' first."
exit 1
fi

# Build animation example
echo "Building animation example..."
mkdir -p output/example/fortran/animation
gfortran -I "$BUILD_DIR" -o save_animation_demo_temp \
example/fortran/animation/save_animation_demo.f90 \
"$LIB_DIR/libfortplot.a" -lm

# Run animation example to generate MP4
echo "Generating animation..."
./save_animation_demo_temp
rm -f save_animation_demo_temp

echo "Special examples built successfully!"
22 changes: 22 additions & 0 deletions src/fortplot_animation.f90
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ module fortplot_animation
public :: animate_interface
public :: save_animation

! Register the save implementation on module initialization
logical, save :: impl_registered = .false.

contains

! Wrapper to maintain backward compatibility for save method
Expand All @@ -21,7 +24,26 @@ subroutine save_animation(anim, filename, fps, status)
integer, intent(in), optional :: fps
integer, intent(out), optional :: status

call register_save_implementation()
call save_animation_full(anim, filename, fps, status)
end subroutine save_animation

! Type-bound procedure for animation save method implementation
subroutine animation_save_impl(anim, filename, fps, status)
class(animation_t), intent(inout) :: anim
character(len=*), intent(in) :: filename
integer, intent(in), optional :: fps
integer, intent(out), optional :: status

call save_animation_full(anim, filename, fps, status)
end subroutine animation_save_impl

! Register the save implementation pointer
subroutine register_save_implementation()
if (.not. impl_registered) then
save_animation_impl => animation_save_impl
impl_registered = .true.
end if
end subroutine register_save_implementation

end module fortplot_animation
44 changes: 44 additions & 0 deletions src/fortplot_animation_core.f90
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,27 @@ end subroutine animate_interface
procedure :: set_save_frames
procedure :: save_frame_sequence
procedure :: set_figure
procedure :: save
end type animation_t

! Animation save interface to avoid circular dependency
abstract interface
subroutine save_animation_interface(anim, filename, fps, status)
import :: animation_t
class(animation_t), intent(inout) :: anim
character(len=*), intent(in) :: filename
integer, intent(in), optional :: fps
integer, intent(out), optional :: status
end subroutine save_animation_interface
end interface

! External save implementation procedure pointer
procedure(save_animation_interface), pointer :: save_animation_impl => null()

public :: FuncAnimation
public :: animate_interface
public :: save_animation_interface
public :: save_animation_impl

contains

Expand Down Expand Up @@ -147,6 +164,33 @@ subroutine cpu_time_delay(seconds)
end do
end subroutine cpu_time_delay

subroutine save(self, filename, fps, status)
!! Save animation to video file - delegates to full pipeline implementation
class(animation_t), intent(inout) :: self
character(len=*), intent(in) :: filename
integer, intent(in), optional :: fps
integer, intent(out), optional :: status

! Attempt to register implementation if not done
call try_register_save_implementation()

if (.not. associated(save_animation_impl)) then
if (present(status)) status = -1
call log_error_with_remediation("Animation save implementation not initialized", &
"Import fortplot_animation module to register the save implementation")
return
end if

! Call the facade save_animation wrapper to avoid circular dependency
call save_animation_impl(self, filename, fps, status)
end subroutine save

subroutine try_register_save_implementation()
!! Attempt to register save implementation (will be set by facade module)
!! This is a no-op if the facade module hasn't been imported
continue
end subroutine try_register_save_implementation

subroutine log_error_with_remediation(error_msg, remediation_msg)
character(len=*), intent(in) :: error_msg, remediation_msg

Expand Down
Loading
Loading