Module: StackingOrder

Defined in:
lib/stacking_order.rb,
lib/stacking_order/version.rb

Overview

Calculates how to print entries in a grid so that stack-cut pages reassemble into sequential order.

Constant Summary collapse

VERSION =
'1.2.0'

Class Method Summary collapse

Class Method Details

.apply_two_sided_flip(result, rows, columns) ⇒ Object



84
85
86
87
88
89
90
# File 'lib/stacking_order.rb', line 84

def apply_two_sided_flip(result, rows, columns)
  cells_per_page = rows * columns
  result.each_slice(cells_per_page).with_index.flat_map do |page, page_index|
    padded_page = pad_page(page, cells_per_page)
    page_index.odd? ? flip_page_rows(padded_page, rows, columns) : padded_page
  end
end

.flip_page_rows(page, rows, columns) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/stacking_order.rb', line 98

def flip_page_rows(page, rows, columns)
  if rows == 1
    row_slice = page.slice(0, columns) || []
    return row_slice.reverse
  end

  flipped = []
  rows.times do |row_index|
    source_row_index = rows - 1 - row_index
    row_slice = page.slice(source_row_index * columns, columns) || []
    flipped.concat(row_slice)
  end
  flipped
end

.order(entries:, rows:, columns:, two_sided_flipped: false) ⇒ Array<Integer, nil>

Public API for calculating the stacking order for printing entries on pages with a grid layout. See README for details.

Parameters:

  • entries (Integer)

    Total number of entries to print

  • rows (Integer)

    Number of rows in the grid on each page

  • columns (Integer)

    Number of columns in the grid on each page

Returns:

  • (Array<Integer, nil>)

    The order in which to print entries (with nil for empty cells)



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/stacking_order.rb', line 17

def order(entries:, rows:, columns:, two_sided_flipped: false)
  validate_arguments!(entries, rows, columns)

  return [] if entries.zero?

  cells_per_page = rows * columns
  num_pages = (entries.to_f / cells_per_page).ceil
  # In two-sided printing, every physical sheet of paper carries a front
  # and a back PDF page. An odd page count leaves the last paper with
  # content only on the front, producing cut folios whose backs are blank.
  num_pages += 1 if two_sided_flipped && num_pages.odd?

  result = []

  num_pages.times do |page_index|
    cells_per_page.times do |cell_index|
      entry_number = (cell_index * num_pages) + page_index + 1
      result << (entry_number <= entries ? entry_number : nil)
    end
  end

  if two_sided_flipped
    result = apply_two_sided_flip(result, rows, columns)
  end

  result.pop while result.last.nil?
  result
end

.pad_page(page, cells_per_page) ⇒ Object



92
93
94
95
96
# File 'lib/stacking_order.rb', line 92

def pad_page(page, cells_per_page)
  return page if page.length == cells_per_page

  page + Array.new(cells_per_page - page.length)
end

.visualize(entries:, rows:, columns:, two_sided_flipped: false, io: $stdout) ⇒ Object

Utility method that prints the layout for the provided configuration, showing the entries on each page and the resulting stacks after cutting. Useful for debugging or CLI demos.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/stacking_order.rb', line 49

def visualize(entries:, rows:, columns:, two_sided_flipped: false, io: $stdout)
  result = order(entries: entries, rows: rows, columns: columns, two_sided_flipped: two_sided_flipped)
  cells_per_page = rows * columns
  num_pages = (entries.to_f / cells_per_page).ceil

  io.puts
  io.puts('=' * 60)
  io.puts("Visualizing stacking order for #{entries} entries, #{rows} row(s), #{columns} column(s)")
  io.puts('=' * 60)
  io.puts("Result: #{result.inspect}")
  io.puts("\nPages layout:")

  result.each_slice(cells_per_page).with_index do |page, page_num|
    io.puts("\nPage #{page_num + 1}:")
    page.each_slice(columns).with_index do |row_values, row_num|
      formatted = row_values.map { |value| value ? format('%3d', value) : 'nil' }.join(' | ')
      io.puts("  Row #{row_num + 1}: #{formatted}")
    end
  end

  io.puts("\nAfter cutting and stacking:")
  cells_per_page.times do |cell_idx|
    row_idx = cell_idx / columns
    col_idx = cell_idx % columns
    stack = []

    num_pages.times do |page_idx|
      pos = (page_idx * cells_per_page) + cell_idx
      stack << (pos < result.length ? result[pos] : nil)
    end

    io.puts("  Position [#{row_idx + 1},#{col_idx + 1}] stack (bottom→top): #{stack.compact.join(', ')}")
  end
end