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
3 changes: 2 additions & 1 deletion BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
- [x] #333: Fix - Circles seem not centered with line plot in marker_demo.html (COMPLETED)
- [x] #332: Fix - Dashed and dash-dotted look funny on line_styles.html (COMPLETED)
- [x] #330: Fix - Old plot not cleared in second figure (figure() call) in contour_demo.html (COMPLETED)
- [ ] #355: Fix - First plot is empty (likely from figure CLEAR logic regression)
- [ ] #328: Fix - One legend entry too much in basic_plots.html second plot
- [ ] #327: Fix - MP4 link not showing on animation.html
- [ ] #336: Fix - Streamplot and stateful streamplot example redundant
Expand All @@ -24,8 +23,10 @@
- [ ] #350: Refactor - improve documentation comments in raster drawing module
- [ ] #357: Docs - standardize colormap documentation across examples
- [ ] #358: Refactor - consolidate ASCII output formatting in example docs
- [ ] #360: Refactor - split fortplot_raster.f90 to comply with file size limits

## DOING (Current Work)
- [ ] #355: Fix - First plot is empty (likely from figure CLEAR logic regression)

## BLOCKED (Infrastructure Issues)

Expand Down
2 changes: 1 addition & 1 deletion src/fortplot_animation_core.f90
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module fortplot_animation_core
use iso_c_binding, only: c_char, c_int, c_null_char
use fortplot_animation_constants
use fortplot_figure_core, only: figure_t, plot_data_t
use fortplot_rendering, only: savefig
! savefig is part of figure_t, not rendering module
use fortplot_pipe, only: open_ffmpeg_pipe, write_png_to_pipe, close_ffmpeg_pipe
use fortplot_utils, only: initialize_backend, ensure_directory_exists
use fortplot_logging, only: log_error, log_info, log_warning
Expand Down
145 changes: 145 additions & 0 deletions src/fortplot_contour_algorithms.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
module fortplot_contour_algorithms
!! Contour plotting algorithms module
!!
!! This module implements the marching squares algorithm and related
!! functions for contour line extraction and rendering.

use, intrinsic :: iso_fortran_env, only: wp => real64
implicit none

private
public :: calculate_marching_squares_config
public :: get_contour_lines
public :: interpolate_edge_crossings
public :: apply_marching_squares_lookup

contains

subroutine calculate_marching_squares_config(z1, z2, z3, z4, level, config)
!! Calculate marching squares configuration for a cell
real(wp), intent(in) :: z1, z2, z3, z4, level
integer, intent(out) :: config

config = 0
if (z1 >= level) config = config + 1
if (z2 >= level) config = config + 2
if (z3 >= level) config = config + 4
if (z4 >= level) config = config + 8
end subroutine calculate_marching_squares_config

subroutine get_contour_lines(config, x1, y1, x2, y2, x3, y3, x4, y4, &
z1, z2, z3, z4, level, line_points, num_lines)
!! Extract contour lines from a cell using marching squares
integer, intent(in) :: config
real(wp), intent(in) :: x1, y1, x2, y2, x3, y3, x4, y4
real(wp), intent(in) :: z1, z2, z3, z4, level
real(wp), intent(out) :: line_points(8)
integer, intent(out) :: num_lines

real(wp) :: xa, ya, xb, yb, xc, yc, xd, yd

call interpolate_edge_crossings(x1, y1, x2, y2, x3, y3, x4, y4, &
z1, z2, z3, z4, level, xa, ya, xb, yb, xc, yc, xd, yd)
call apply_marching_squares_lookup(config, xa, ya, xb, yb, xc, yc, xd, yd, &
line_points, num_lines)
end subroutine get_contour_lines

subroutine interpolate_edge_crossings(x1, y1, x2, y2, x3, y3, x4, y4, &
z1, z2, z3, z4, level, xa, ya, xb, yb, xc, yc, xd, yd)
!! Interpolate the positions where contour crosses cell edges
real(wp), intent(in) :: x1, y1, x2, y2, x3, y3, x4, y4
real(wp), intent(in) :: z1, z2, z3, z4, level
real(wp), intent(out) :: xa, ya, xb, yb, xc, yc, xd, yd

real(wp) :: t

! Edge 1-2 (bottom)
if (abs(z2 - z1) > epsilon(1.0_wp)) then
t = (level - z1) / (z2 - z1)
xa = x1 + t * (x2 - x1)
ya = y1 + t * (y2 - y1)
else
xa = 0.5_wp * (x1 + x2)
ya = 0.5_wp * (y1 + y2)
end if

! Edge 2-3 (right)
if (abs(z3 - z2) > epsilon(1.0_wp)) then
t = (level - z2) / (z3 - z2)
xb = x2 + t * (x3 - x2)
yb = y2 + t * (y3 - y2)
else
xb = 0.5_wp * (x2 + x3)
yb = 0.5_wp * (y2 + y3)
end if

! Edge 3-4 (top)
if (abs(z4 - z3) > epsilon(1.0_wp)) then
t = (level - z3) / (z4 - z3)
xc = x3 + t * (x4 - x3)
yc = y3 + t * (y4 - y3)
else
xc = 0.5_wp * (x3 + x4)
yc = 0.5_wp * (y3 + y4)
end if

! Edge 4-1 (left)
if (abs(z1 - z4) > epsilon(1.0_wp)) then
t = (level - z4) / (z1 - z4)
xd = x4 + t * (x1 - x4)
yd = y4 + t * (y1 - y4)
else
xd = 0.5_wp * (x4 + x1)
yd = 0.5_wp * (y4 + y1)
end if
end subroutine interpolate_edge_crossings

subroutine apply_marching_squares_lookup(config, xa, ya, xb, yb, xc, yc, xd, yd, line_points, num_lines)
!! Apply marching squares lookup table to get line segments
integer, intent(in) :: config
real(wp), intent(in) :: xa, ya, xb, yb, xc, yc, xd, yd
real(wp), intent(out) :: line_points(8)
integer, intent(out) :: num_lines

num_lines = 0

select case (config)
case (0, 15)
! No contour or all inside - no lines
num_lines = 0
case (1, 14)
! Corner 1 isolated
line_points(1:4) = [xa, ya, xd, yd]
num_lines = 1
case (2, 13)
! Corner 2 isolated
line_points(1:4) = [xa, ya, xb, yb]
num_lines = 1
case (3, 12)
! Corners 1 and 2
line_points(1:4) = [xd, yd, xb, yb]
num_lines = 1
case (4, 11)
! Corner 3 isolated
line_points(1:4) = [xb, yb, xc, yc]
num_lines = 1
case (5)
! Corners 1 and 3 - saddle point (choose connection)
line_points(1:8) = [xa, ya, xd, yd, xb, yb, xc, yc]
num_lines = 2
case (10)
! Corners 2 and 4 - saddle point (choose connection)
line_points(1:8) = [xa, ya, xb, yb, xc, yc, xd, yd]
num_lines = 2
case (6, 9)
! Corners 2 and 3
line_points(1:4) = [xa, ya, xc, yc]
num_lines = 1
case (7, 8)
! Corners 1, 2 and 3
line_points(1:4) = [xd, yd, xc, yc]
num_lines = 1
end select
end subroutine apply_marching_squares_lookup

end module fortplot_contour_algorithms
54 changes: 13 additions & 41 deletions src/fortplot_fast_io.f90
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ module fortplot_fast_io

subroutine fast_savefig(fig, filename, use_memory_override)
!! Fast savefig that can use memory backend when appropriate
use fortplot_figure_base, only: figure_t
use fortplot_rendering, only: render_figure
use fortplot_figure_core, only: figure_t
! render_figure is a type-bound procedure, not needed in import
use fortplot_utils, only: get_backend_from_filename

class(figure_t), intent(inout) :: fig
Expand All @@ -57,38 +57,10 @@ subroutine fast_savefig(fig, filename, use_memory_override)

call cpu_time(start_time)

if (use_memory) then
! Use memory backend for fast operation
mem_backend => get_memory_backend()

! Render the figure
call render_figure(fig)

! Get rendered data from backend
backend_type = get_backend_from_filename(filename)

! Convert rendered figure to byte buffer
call figure_to_buffer(fig, backend_type, buffer_data, buffer_size)

if (buffer_size > 0) then
! Save to memory backend
call mem_backend%save(filename, buffer_data(1:buffer_size), backend_type)

call log_debug("Fast I/O: Saved to memory backend: " // trim(filename))
memory_saves = memory_saves + 1
else
! Fallback to disk if buffer creation failed
call savefig_disk(fig, filename)
disk_saves = disk_saves + 1
end if

if (allocated(buffer_data)) deallocate(buffer_data)

else
! Use standard disk-based savefig
call savefig_disk(fig, filename)
disk_saves = disk_saves + 1
end if
! For now, always use disk-based savefig since render_figure is private
! Memory backend optimization needs to be reworked after refactoring
call savefig_disk(fig, filename)
disk_saves = disk_saves + 1

call cpu_time(end_time)

Expand All @@ -102,19 +74,19 @@ end subroutine fast_savefig

subroutine savefig_disk(fig, filename)
!! Standard disk-based savefig (wrapper for original)
use fortplot_rendering, only: savefig
use fortplot_figure_base, only: figure_t
! savefig is a type-bound procedure, not needed in import
use fortplot_figure_core, only: figure_t

class(figure_t), intent(inout) :: fig
character(len=*), intent(in) :: filename

call savefig(fig, filename)
call fig%savefig(filename)

end subroutine savefig_disk

subroutine figure_to_buffer(fig, backend_type, buffer_data, buffer_size)
!! Convert rendered figure to byte buffer
use fortplot_figure_base, only: figure_t
use fortplot_figure_core, only: figure_t

class(figure_t), intent(inout) :: fig
character(len=*), intent(in) :: backend_type
Expand Down Expand Up @@ -142,7 +114,7 @@ end subroutine figure_to_buffer

subroutine extract_png_buffer(fig, buffer_data, buffer_size)
!! Extract PNG data from figure backend
use fortplot_figure_base, only: figure_t
use fortplot_figure_core, only: figure_t

class(figure_t), intent(inout) :: fig
integer(int8), dimension(:), allocatable, intent(out) :: buffer_data
Expand All @@ -163,7 +135,7 @@ end subroutine extract_png_buffer

subroutine extract_pdf_buffer(fig, buffer_data, buffer_size)
!! Extract PDF data from figure backend
use fortplot_figure_base, only: figure_t
use fortplot_figure_core, only: figure_t

class(figure_t), intent(inout) :: fig
integer(int8), dimension(:), allocatable, intent(out) :: buffer_data
Expand All @@ -186,7 +158,7 @@ end subroutine extract_pdf_buffer

subroutine extract_ascii_buffer(fig, buffer_data, buffer_size)
!! Extract ASCII data from figure backend
use fortplot_figure_base, only: figure_t
use fortplot_figure_core, only: figure_t

class(figure_t), intent(inout) :: fig
integer(int8), dimension(:), allocatable, intent(out) :: buffer_data
Expand Down
Loading
Loading