|
| 1 | +module UnicodePlot |
| 2 | + class DensityCanvas < Canvas |
| 3 | + DENSITY_SIGNS = [" ", "░", "▒", "▓", "█"].freeze |
| 4 | + |
| 5 | + MIN_WIDTH = 5 |
| 6 | + MIN_HEIGHT = 5 |
| 7 | + |
| 8 | + X_PIXEL_PER_CHAR = 1 |
| 9 | + Y_PIXEL_PER_CHAR = 2 |
| 10 | + |
| 11 | + def initialize(width, height, **kw) |
| 12 | + width = [width, MIN_WIDTH].max |
| 13 | + height = [height, MIN_HEIGHT].max |
| 14 | + @max_density = 1 |
| 15 | + super(width, height, |
| 16 | + width * X_PIXEL_PER_CHAR, |
| 17 | + height * Y_PIXEL_PER_CHAR, |
| 18 | + 0, |
| 19 | + x_pixel_per_char: X_PIXEL_PER_CHAR, |
| 20 | + y_pixel_per_char: Y_PIXEL_PER_CHAR, |
| 21 | + **kw) |
| 22 | + end |
| 23 | + |
| 24 | + def pixel!(pixel_x, pixel_y, color) |
| 25 | + unless 0 <= pixel_x && pixel_x <= pixel_width && |
| 26 | + 0 <= pixel_y && pixel_y <= pixel_height |
| 27 | + return color |
| 28 | + end |
| 29 | + |
| 30 | + pixel_x -= 1 unless pixel_x < pixel_width |
| 31 | + pixel_y -= 1 unless pixel_y < pixel_height |
| 32 | + |
| 33 | + char_x = (pixel_x.fdiv(pixel_width) * width).floor |
| 34 | + char_y = (pixel_y.fdiv(pixel_height) * height).floor |
| 35 | + |
| 36 | + index = index_at(char_x, char_y) |
| 37 | + @grid[index] += 1 |
| 38 | + @max_density = [@max_density, @grid[index]].max |
| 39 | + @colors[index] |= COLOR_ENCODE[color] |
| 40 | + color |
| 41 | + end |
| 42 | + |
| 43 | + def print_row(out, row_index) |
| 44 | + unless 0 <= row_index && row_index < height |
| 45 | + raise ArgumentError, "row_index out of bounds" |
| 46 | + end |
| 47 | + y = row_index |
| 48 | + den_sign_count = DENSITY_SIGNS.length |
| 49 | + val_scale = (den_sign_count - 1).fdiv(@max_density) |
| 50 | + (0 ... width).each do |x| |
| 51 | + den_index = (char_at(x, y) * val_scale).round |
| 52 | + print_color(out, color_at(x, y), DENSITY_SIGNS[den_index]) |
| 53 | + end |
| 54 | + end |
| 55 | + end |
| 56 | +end |
0 commit comments