plotcli-py 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. CLAUDE.md +51 -0
  2. LICENSE +21 -0
  3. PKG-INFO +358 -0
  4. README.md +340 -0
  5. main.py +6 -0
  6. plotcli-original/.Rbuildignore +18 -0
  7. plotcli-original/.github/workflows/deploy_docs.yml +43 -0
  8. plotcli-original/.gitignore +46 -0
  9. plotcli-original/DESCRIPTION +25 -0
  10. plotcli-original/NAMESPACE +60 -0
  11. plotcli-original/NEWS.md +112 -0
  12. plotcli-original/R/ascii_escape.r +13 -0
  13. plotcli-original/R/canvas.r +586 -0
  14. plotcli-original/R/class_functions.r +114 -0
  15. plotcli-original/R/geom_registry.r +1376 -0
  16. plotcli-original/R/ggplotcli.r +234 -0
  17. plotcli-original/R/ggplotcli_helpers.r +1099 -0
  18. plotcli-original/R/helper_functions.r +351 -0
  19. plotcli-original/R/plotcli.r +963 -0
  20. plotcli-original/R/plotcli_grid.r +1 -0
  21. plotcli-original/R/plotcli_wrappers.r +416 -0
  22. plotcli-original/R/zzz.r +15 -0
  23. plotcli-original/README.md +192 -0
  24. plotcli-original/docs/ascii.png +0 -0
  25. plotcli-original/docs/bar.png +0 -0
  26. plotcli-original/docs/block.png +0 -0
  27. plotcli-original/docs/boxplot.png +0 -0
  28. plotcli-original/docs/density.png +0 -0
  29. plotcli-original/docs/facet.png +0 -0
  30. plotcli-original/docs/facet_grid.png +0 -0
  31. plotcli-original/docs/generate_png.sh +137 -0
  32. plotcli-original/docs/heatmap.png +0 -0
  33. plotcli-original/docs/histogram.png +0 -0
  34. plotcli-original/docs/line.png +0 -0
  35. plotcli-original/docs/noborder.png +0 -0
  36. plotcli-original/docs/scatter.png +0 -0
  37. plotcli-original/docs/showcase.R +182 -0
  38. plotcli-original/inst/doc/ggplotcli.R +231 -0
  39. plotcli-original/inst/doc/ggplotcli.Rmd +329 -0
  40. plotcli-original/inst/doc/ggplotcli.html +1078 -0
  41. plotcli-original/inst/doc/plotcli_class.R +98 -0
  42. plotcli-original/inst/doc/plotcli_class.Rmd +121 -0
  43. plotcli-original/inst/doc/plotcli_class.html +564 -0
  44. plotcli-original/inst/doc/plotcli_wrappers.R +35 -0
  45. plotcli-original/inst/doc/plotcli_wrappers.Rmd +62 -0
  46. plotcli-original/inst/doc/plotcli_wrappers.html +546 -0
  47. plotcli-original/man/AsciiCanvas.Rd +116 -0
  48. plotcli-original/man/BlockCanvas.Rd +132 -0
  49. plotcli-original/man/BrailleCanvas.Rd +146 -0
  50. plotcli-original/man/Canvas.Rd +492 -0
  51. plotcli-original/man/GeomRegistry.Rd +9 -0
  52. plotcli-original/man/add_legend_to_output.Rd +12 -0
  53. plotcli-original/man/braille_dot_bit.Rd +29 -0
  54. plotcli-original/man/braille_set_dot.Rd +21 -0
  55. plotcli-original/man/bresenham.Rd +27 -0
  56. plotcli-original/man/build_plot_output.Rd +28 -0
  57. plotcli-original/man/build_plot_output_v2.Rd +41 -0
  58. plotcli-original/man/cat_plot_matrix.Rd +17 -0
  59. plotcli-original/man/cbind.plotcli.Rd +19 -0
  60. plotcli-original/man/cbind_plots.Rd +17 -0
  61. plotcli-original/man/color_to_term.Rd +18 -0
  62. plotcli-original/man/create_canvas.Rd +21 -0
  63. plotcli-original/man/create_panel_scales.Rd +29 -0
  64. plotcli-original/man/create_scales.Rd +27 -0
  65. plotcli-original/man/dot-geom_registry.Rd +16 -0
  66. plotcli-original/man/draw_border.Rd +12 -0
  67. plotcli-original/man/draw_grid.Rd +12 -0
  68. plotcli-original/man/extract_legend_info.Rd +12 -0
  69. plotcli-original/man/extract_plot_labels.Rd +12 -0
  70. plotcli-original/man/extract_plot_style.Rd +12 -0
  71. plotcli-original/man/format_axis_label.Rd +18 -0
  72. plotcli-original/man/format_four_chars.Rd +21 -0
  73. plotcli-original/man/geom_area_handler.Rd +12 -0
  74. plotcli-original/man/geom_bar_handler.Rd +12 -0
  75. plotcli-original/man/geom_boxplot_handler.Rd +13 -0
  76. plotcli-original/man/geom_density_handler.Rd +12 -0
  77. plotcli-original/man/geom_histogram_handler.Rd +12 -0
  78. plotcli-original/man/geom_hline_handler.Rd +12 -0
  79. plotcli-original/man/geom_line_handler.Rd +12 -0
  80. plotcli-original/man/geom_path_handler.Rd +12 -0
  81. plotcli-original/man/geom_point_handler.Rd +12 -0
  82. plotcli-original/man/geom_rect_handler.Rd +12 -0
  83. plotcli-original/man/geom_segment_handler.Rd +12 -0
  84. plotcli-original/man/geom_smooth_handler.Rd +12 -0
  85. plotcli-original/man/geom_text_handler.Rd +12 -0
  86. plotcli-original/man/geom_vline_handler.Rd +12 -0
  87. plotcli-original/man/get_color_hue.Rd +18 -0
  88. plotcli-original/man/get_data_subset.Rd +23 -0
  89. plotcli-original/man/get_facet_info.Rd +18 -0
  90. plotcli-original/man/get_geom_handler.Rd +17 -0
  91. plotcli-original/man/get_term_colors.Rd +21 -0
  92. plotcli-original/man/ggplotcli.Rd +83 -0
  93. plotcli-original/man/init_color_mapping.Rd +15 -0
  94. plotcli-original/man/is_braille.Rd +20 -0
  95. plotcli-original/man/is_geom_registered.Rd +17 -0
  96. plotcli-original/man/list_registered_geoms.Rd +14 -0
  97. plotcli-original/man/make_colored.Rd +23 -0
  98. plotcli-original/man/make_unique_names.Rd +20 -0
  99. plotcli-original/man/normalize_data.Rd +27 -0
  100. plotcli-original/man/pclib.Rd +48 -0
  101. plotcli-original/man/pclibx.Rd +46 -0
  102. plotcli-original/man/pclid.Rd +44 -0
  103. plotcli-original/man/pclih.Rd +50 -0
  104. plotcli-original/man/pclil.Rd +48 -0
  105. plotcli-original/man/pclis.Rd +48 -0
  106. plotcli-original/man/pixel_to_braille.Rd +23 -0
  107. plotcli-original/man/plotcli.Rd +598 -0
  108. plotcli-original/man/plotcli_bar.Rd +48 -0
  109. plotcli-original/man/plotcli_box.Rd +46 -0
  110. plotcli-original/man/plotcli_density.Rd +44 -0
  111. plotcli-original/man/plotcli_histogram.Rd +50 -0
  112. plotcli-original/man/plotcli_line.Rd +48 -0
  113. plotcli-original/man/plotcli_options.Rd +18 -0
  114. plotcli-original/man/plotcli_scatter.Rd +48 -0
  115. plotcli-original/man/plus-.plotcli.Rd +19 -0
  116. plotcli-original/man/rbind.plotcli.Rd +19 -0
  117. plotcli-original/man/rbind_plots.Rd +17 -0
  118. plotcli-original/man/register_geom.Rd +21 -0
  119. plotcli-original/man/remove_color_codes.Rd +21 -0
  120. plotcli-original/man/render_faceted_plot.Rd +25 -0
  121. plotcli-original/man/render_single_panel.Rd +12 -0
  122. plotcli-original/man/safe_aes_name.Rd +18 -0
  123. plotcli-original/tests/testthat/test-new-geoms.R +136 -0
  124. plotcli-original/tests/testthat/test-plotcli.R +69 -0
  125. plotcli-original/tests/testthat.R +4 -0
  126. plotcli-original/vignettes/ggplotcli.Rmd +329 -0
  127. plotcli-original/vignettes/plotcli_class.R +98 -0
  128. plotcli-original/vignettes/plotcli_class.Rmd +121 -0
  129. plotcli-original/vignettes/plotcli_wrappers.R +35 -0
  130. plotcli-original/vignettes/plotcli_wrappers.Rmd +62 -0
  131. plotcli.egg-info/PKG-INFO +11 -0
  132. plotcli.egg-info/SOURCES.txt +7 -0
  133. plotcli.egg-info/dependency_links.txt +1 -0
  134. plotcli.egg-info/entry_points.txt +3 -0
  135. plotcli.egg-info/top_level.txt +1 -0
  136. plotcli.py +978 -0
  137. plotcli_py-0.1.0.dist-info/METADATA +358 -0
  138. plotcli_py-0.1.0.dist-info/RECORD +143 -0
  139. plotcli_py-0.1.0.dist-info/WHEEL +4 -0
  140. plotcli_py-0.1.0.dist-info/entry_points.txt +2 -0
  141. plotcli_py-0.1.0.dist-info/licenses/LICENSE +21 -0
  142. pyproject.toml +31 -0
  143. uv.lock +8 -0
@@ -0,0 +1,586 @@
1
+ #' Canvas Classes for Terminal Plotting
2
+ #'
3
+ #' Abstract canvas system that provides different rendering backends:
4
+ #' - AsciiCanvas: Basic ASCII characters (*, -, |, +)
5
+ #' - BrailleCanvas: Unicode Braille patterns (2x4 dots per cell = 8x resolution)
6
+ #' - BlockCanvas: Unicode block elements (half-blocks for 2x vertical resolution)
7
+ #'
8
+ #' @name Canvas
9
+ NULL
10
+
11
+ #' Base Canvas Class
12
+ #'
13
+ #' Abstract base class for all canvas types. Provides the interface
14
+ #' that all canvas implementations must follow.
15
+ #'
16
+ #' @export
17
+ Canvas <- R6Class("Canvas",
18
+ public = list(
19
+ #' @field width Character width of the canvas
20
+ width = NULL,
21
+ #' @field height Character height of the canvas
22
+ height = NULL,
23
+ #' @field pixel_width Pixel width (may be higher than char width for Braille/Block)
24
+ pixel_width = NULL,
25
+ #' @field pixel_height Pixel height (may be higher than char height for Braille/Block)
26
+ pixel_height = NULL,
27
+ #' @field matrix Character matrix for rendering
28
+ matrix = NULL,
29
+ #' @field color_matrix Parallel matrix tracking colors per cell
30
+ color_matrix = NULL,
31
+ #' @field x_mult Horizontal multiplier (pixels per character)
32
+ x_mult = 1,
33
+ #' @field y_mult Vertical multiplier (pixels per character)
34
+ y_mult = 1,
35
+
36
+ #' @description Initialize the canvas
37
+ #' @param width Character width
38
+ #' @param height Character height
39
+ initialize = function(width, height) {
40
+ self$width <- width
41
+ self$height <- height
42
+ self$pixel_width <- width * self$x_mult
43
+ self$pixel_height <- height * self$y_mult
44
+ self$matrix <- matrix(" ", nrow = height, ncol = width)
45
+ self$color_matrix <- matrix(NA_character_, nrow = height, ncol = width)
46
+ },
47
+
48
+ #' @description Set a pixel at the given coordinates
49
+
50
+ #' @param x X coordinate (1-based, in pixel space)
51
+ #' @param y Y coordinate (1-based, in pixel space, 1 = top)
52
+ #' @param color Optional color name
53
+ set_pixel = function(x, y, color = NULL) {
54
+ stop("set_pixel must be implemented by subclass")
55
+ },
56
+
57
+ #' @description Draw a line between two points
58
+ #' @param x0 Start X coordinate
59
+ #' @param y0 Start Y coordinate
60
+ #' @param x1 End X coordinate
61
+ #' @param y1 End Y coordinate
62
+ #' @param color Optional color name
63
+ draw_line = function(x0, y0, x1, y1, color = NULL) {
64
+ # Use Bresenham's algorithm at pixel resolution
65
+ points <- bresenham(
66
+ round(x0), round(y0),
67
+ round(x1), round(y1)
68
+ )
69
+ for (pt in points) {
70
+ self$set_pixel(pt[1], pt[2], color)
71
+ }
72
+ },
73
+
74
+ #' @description Draw multiple connected line segments
75
+ #' @param xs Vector of X coordinates
76
+ #' @param ys Vector of Y coordinates
77
+ #' @param color Optional color name
78
+ draw_polyline = function(xs, ys, color = NULL) {
79
+ if (length(xs) != length(ys)) stop("xs and ys must have same length")
80
+ if (length(xs) < 2) return()
81
+
82
+ for (i in 1:(length(xs) - 1)) {
83
+ self$draw_line(xs[i], ys[i], xs[i + 1], ys[i + 1], color)
84
+ }
85
+ },
86
+
87
+ #' @description Draw points (scatter)
88
+ #' @param xs Vector of X coordinates
89
+ #' @param ys Vector of Y coordinates
90
+ #' @param color Optional color name
91
+ draw_points = function(xs, ys, color = NULL) {
92
+ if (length(xs) != length(ys)) stop("xs and ys must have same length")
93
+
94
+ for (i in seq_along(xs)) {
95
+ self$set_pixel(round(xs[i]), round(ys[i]), color)
96
+ }
97
+ },
98
+
99
+ #' @description Fill a rectangle
100
+ #' @param x0 Left X coordinate
101
+ #' @param y0 Top Y coordinate
102
+ #' @param x1 Right X coordinate
103
+ #' @param y1 Bottom Y coordinate
104
+ #' @param color Optional color name
105
+ fill_rect = function(x0, y0, x1, y1, color = NULL) {
106
+ x0 <- round(x0); x1 <- round(x1)
107
+ y0 <- round(y0); y1 <- round(y1)
108
+
109
+ for (x in x0:x1) {
110
+ for (y in y0:y1) {
111
+ self$set_pixel(x, y, color)
112
+ }
113
+ }
114
+ },
115
+
116
+ #' @description Fill a vertical bar from bottom up to a height
117
+ #' @param x X coordinate (center of bar in pixel space)
118
+ #' @param height Height in pixels from bottom
119
+ #' @param bar_width Width of bar in pixels (default 2)
120
+ #' @param color Optional color name
121
+ fill_bar = function(x, height, bar_width = 2, color = NULL) {
122
+ x <- round(x)
123
+ height <- round(height)
124
+ half_width <- floor(bar_width / 2)
125
+
126
+ for (dx in (-half_width):(half_width - 1 + bar_width %% 2)) {
127
+ for (y in (self$pixel_height - height + 1):self$pixel_height) {
128
+ self$set_pixel(x + dx, y, color)
129
+ }
130
+ }
131
+ },
132
+
133
+ #' @description Place text at a position
134
+ #' @param x X coordinate (character position)
135
+ #' @param y Y coordinate (character position)
136
+ #' @param text Text string to place
137
+ #' @param color Optional color name
138
+ draw_text = function(x, y, text, color = NULL) {
139
+ chars <- strsplit(text, "")[[1]]
140
+ for (i in seq_along(chars)) {
141
+ col <- x + i - 1
142
+ if (col >= 1 && col <= self$width && y >= 1 && y <= self$height) {
143
+ self$matrix[y, col] <- chars[i]
144
+ if (!is.null(color)) {
145
+ self$color_matrix[y, col] <- color
146
+ }
147
+ }
148
+ }
149
+ },
150
+
151
+ #' @description Apply colors to the matrix
152
+ #' @return Matrix with ANSI color codes applied
153
+ apply_colors = function() {
154
+ result <- self$matrix
155
+ for (i in 1:nrow(result)) {
156
+ for (j in 1:ncol(result)) {
157
+ if (!is.na(self$color_matrix[i, j])) {
158
+ result[i, j] <- make_colored(result[i, j], self$color_matrix[i, j])
159
+ }
160
+ }
161
+ }
162
+ return(result)
163
+ },
164
+
165
+ #' @description Get the rendered matrix (with colors)
166
+ #' @return Character matrix
167
+ render = function() {
168
+ self$apply_colors()
169
+ },
170
+
171
+ #' @description Print the canvas to console
172
+ print = function() {
173
+ rendered <- self$render()
174
+ cat("\n")
175
+ for (i in 1:nrow(rendered)) {
176
+ cat(paste(rendered[i, ], collapse = ""), "\n")
177
+ }
178
+ invisible(self)
179
+ },
180
+
181
+ #' @description Clear the canvas
182
+ clear = function() {
183
+ self$matrix <- matrix(" ", nrow = self$height, ncol = self$width)
184
+ self$color_matrix <- matrix(NA_character_, nrow = self$height, ncol = self$width)
185
+ },
186
+
187
+ #' @description Draw a rectangle outline
188
+ #' @param x0 Left X coordinate (pixel space)
189
+ #' @param y0 Top Y coordinate (pixel space)
190
+ #' @param x1 Right X coordinate (pixel space)
191
+ #' @param y1 Bottom Y coordinate (pixel space)
192
+ #' @param color Optional color name
193
+ draw_rect = function(x0, y0, x1, y1, color = NULL) {
194
+ x0 <- round(x0); x1 <- round(x1)
195
+ y0 <- round(y0); y1 <- round(y1)
196
+
197
+ # Draw four sides
198
+ self$draw_line(x0, y0, x1, y0, color) # Top
199
+ self$draw_line(x0, y1, x1, y1, color) # Bottom
200
+ self$draw_line(x0, y0, x0, y1, color) # Left
201
+ self$draw_line(x1, y0, x1, y1, color) # Right
202
+ },
203
+
204
+ #' @description Fill an area between a polyline and the bottom
205
+ #' @param xs Vector of X coordinates
206
+ #' @param ys Vector of Y coordinates
207
+ #' @param color Optional color name
208
+ fill_area = function(xs, ys, color = NULL) {
209
+ if (length(xs) != length(ys)) stop("xs and ys must have same length")
210
+ if (length(xs) < 2) return()
211
+
212
+ # For each x, find the y value and fill from there to bottom
213
+ for (i in seq_along(xs)) {
214
+ x <- round(xs[i])
215
+ y <- round(ys[i])
216
+
217
+ if (x >= 1 && x <= self$pixel_width) {
218
+ # Fill from y to bottom (pixel_height)
219
+ for (py in y:self$pixel_height) {
220
+ self$set_pixel(x, py, color)
221
+ }
222
+ }
223
+ }
224
+ },
225
+
226
+ #' @description Draw a segment (line with optional arrowhead)
227
+ #' @param x0 Start X coordinate
228
+ #' @param y0 Start Y coordinate
229
+ #' @param x1 End X coordinate
230
+ #' @param y1 End Y coordinate
231
+ #' @param arrow_end Add arrowhead at end (default FALSE)
232
+ #' @param color Optional color name
233
+ draw_segment = function(x0, y0, x1, y1, arrow_end = FALSE, color = NULL) {
234
+ # Draw the main line
235
+ self$draw_line(x0, y0, x1, y1, color)
236
+
237
+ # Add arrowhead if requested
238
+ if (arrow_end) {
239
+ # Calculate arrow direction
240
+ dx <- x1 - x0
241
+ dy <- y1 - y0
242
+ len <- sqrt(dx^2 + dy^2)
243
+
244
+ if (len > 0) {
245
+ # Normalize and scale for arrow size
246
+ arrow_len <- min(len * 0.2, 4) # Arrow is 20% of line or max 4 pixels
247
+ dx <- dx / len * arrow_len
248
+ dy <- dy / len * arrow_len
249
+
250
+ # Arrow wings at 30 degrees
251
+ wing_x1 <- x1 - dx + dy * 0.5
252
+ wing_y1 <- y1 - dy - dx * 0.5
253
+ wing_x2 <- x1 - dx - dy * 0.5
254
+ wing_y2 <- y1 - dy + dx * 0.5
255
+
256
+ self$draw_line(x1, y1, wing_x1, wing_y1, color)
257
+ self$draw_line(x1, y1, wing_x2, wing_y2, color)
258
+ }
259
+ }
260
+ },
261
+
262
+ #' @description Draw a horizontal line
263
+ #' @param y Y coordinate
264
+ #' @param x0 Start X (default 1)
265
+ #' @param x1 End X (default pixel_width)
266
+ #' @param color Optional color name
267
+ draw_hline = function(y, x0 = 1, x1 = NULL, color = NULL) {
268
+ if (is.null(x1)) x1 <- self$pixel_width
269
+ self$draw_line(x0, y, x1, y, color)
270
+ },
271
+
272
+ #' @description Draw a vertical line
273
+ #' @param x X coordinate
274
+ #' @param y0 Start Y (default 1)
275
+ #' @param y1 End Y (default pixel_height)
276
+ #' @param color Optional color name
277
+ draw_vline = function(x, y0 = 1, y1 = NULL, color = NULL) {
278
+ if (is.null(y1)) y1 <- self$pixel_height
279
+ self$draw_line(x, y0, x, y1, color)
280
+ },
281
+
282
+ #' @description Draw a circle outline
283
+ #' @param cx Center X coordinate
284
+ #' @param cy Center Y coordinate
285
+ #' @param r Radius in pixels
286
+ #' @param color Optional color name
287
+ draw_circle = function(cx, cy, r, color = NULL) {
288
+ # Midpoint circle algorithm
289
+ x <- r
290
+ y <- 0
291
+ err <- 0
292
+
293
+ while (x >= y) {
294
+ self$set_pixel(cx + x, cy + y, color)
295
+ self$set_pixel(cx + y, cy + x, color)
296
+ self$set_pixel(cx - y, cy + x, color)
297
+ self$set_pixel(cx - x, cy + y, color)
298
+ self$set_pixel(cx - x, cy - y, color)
299
+ self$set_pixel(cx - y, cy - x, color)
300
+ self$set_pixel(cx + y, cy - x, color)
301
+ self$set_pixel(cx + x, cy - y, color)
302
+
303
+ y <- y + 1
304
+ err <- err + 1 + 2 * y
305
+
306
+ if (2 * (err - x) + 1 > 0) {
307
+ x <- x - 1
308
+ err <- err + 1 - 2 * x
309
+ }
310
+ }
311
+ },
312
+
313
+ #' @description Fill a circle
314
+ #' @param cx Center X coordinate
315
+ #' @param cy Center Y coordinate
316
+ #' @param r Radius in pixels
317
+ #' @param color Optional color name
318
+ fill_circle = function(cx, cy, r, color = NULL) {
319
+ for (y in (cy - r):(cy + r)) {
320
+ for (x in (cx - r):(cx + r)) {
321
+ if ((x - cx)^2 + (y - cy)^2 <= r^2) {
322
+ self$set_pixel(x, y, color)
323
+ }
324
+ }
325
+ }
326
+ },
327
+
328
+ #' @description Draw a polygon outline
329
+ #' @param xs Vector of X coordinates
330
+ #' @param ys Vector of Y coordinates
331
+ #' @param closed Whether to close the polygon (default TRUE)
332
+ #' @param color Optional color name
333
+ draw_polygon = function(xs, ys, closed = TRUE, color = NULL) {
334
+ if (length(xs) != length(ys)) stop("xs and ys must have same length")
335
+ if (length(xs) < 2) return()
336
+
337
+ # Draw all edges
338
+ for (i in 1:(length(xs) - 1)) {
339
+ self$draw_line(xs[i], ys[i], xs[i + 1], ys[i + 1], color)
340
+ }
341
+
342
+ # Close the polygon if requested
343
+ if (closed && length(xs) >= 3) {
344
+ self$draw_line(xs[length(xs)], ys[length(ys)], xs[1], ys[1], color)
345
+ }
346
+ }
347
+ )
348
+ )
349
+
350
+
351
+ #' ASCII Canvas Class
352
+ #'
353
+ #' Simple canvas using basic ASCII characters.
354
+ #' Resolution: 1x1 (one pixel per character)
355
+ #'
356
+ #' @export
357
+ AsciiCanvas <- R6Class("AsciiCanvas",
358
+ inherit = Canvas,
359
+ public = list(
360
+ #' @field point_char Character used for points
361
+ point_char = "*",
362
+
363
+ #' @description Initialize ASCII canvas
364
+ #' @param width Character width
365
+ #' @param height Character height
366
+ #' @param point_char Character to use for points (default "*")
367
+ initialize = function(width, height, point_char = "*") {
368
+ self$x_mult <- 1
369
+ self$y_mult <- 1
370
+ self$point_char <- point_char
371
+ super$initialize(width, height)
372
+ },
373
+
374
+ #' @description Set a pixel
375
+ #' @param x X coordinate (1-based)
376
+ #' @param y Y coordinate (1-based, 1 = top)
377
+ #' @param color Optional color name
378
+ set_pixel = function(x, y, color = NULL) {
379
+ x <- round(x)
380
+ y <- round(y)
381
+
382
+ if (x < 1 || x > self$width || y < 1 || y > self$height) return()
383
+
384
+ self$matrix[y, x] <- self$point_char
385
+ if (!is.null(color)) {
386
+ self$color_matrix[y, x] <- color
387
+ }
388
+ }
389
+ )
390
+ )
391
+
392
+
393
+ #' Braille Canvas Class
394
+ #'
395
+ #' High-resolution canvas using Unicode Braille patterns.
396
+ #' Resolution: 2x4 (2 horizontal, 4 vertical dots per character = 8x resolution)
397
+ #'
398
+ #' Braille dot layout (dot numbers and bit values):
399
+ #' \preformatted{
400
+ #' Col 0 Col 1 Bit values
401
+ #' Row 0: 1 4 0x01 0x08
402
+ #' Row 1: 2 5 0x02 0x10
403
+ #' Row 2: 3 6 0x04 0x20
404
+ #' Row 3: 7 8 0x40 0x80
405
+ #' }
406
+ #'
407
+ #' @export
408
+ BrailleCanvas <- R6Class("BrailleCanvas",
409
+ inherit = Canvas,
410
+ public = list(
411
+ #' @field braille_base Unicode code point for empty Braille character
412
+ braille_base = 0x2800L,
413
+
414
+ #' @description Initialize Braille canvas
415
+ #' @param width Character width
416
+ #' @param height Character height
417
+ initialize = function(width, height) {
418
+ self$x_mult <- 2
419
+ self$y_mult <- 4
420
+ super$initialize(width, height)
421
+ },
422
+
423
+ #' @description Get the bit value for a dot position
424
+ #' @param dot_row Row within Braille cell (0-3)
425
+ #' @param dot_col Column within Braille cell (0-1)
426
+ #' @return Bit value
427
+ get_dot_bit = function(dot_row, dot_col) {
428
+ if (dot_col == 0) {
429
+ if (dot_row < 3) {
430
+ return(bitwShiftL(1L, dot_row))
431
+ } else {
432
+ return(0x40L)
433
+ }
434
+ } else {
435
+ if (dot_row < 3) {
436
+ return(bitwShiftL(1L, dot_row + 3))
437
+ } else {
438
+ return(0x80L)
439
+ }
440
+ }
441
+ },
442
+
443
+ #' @description Set a pixel in Braille space
444
+ #' @param x X coordinate (1-based, in pixel space: 1 to width*2)
445
+ #' @param y Y coordinate (1-based, in pixel space: 1 to height*4, 1 = top)
446
+ #' @param color Optional color name
447
+ set_pixel = function(x, y, color = NULL) {
448
+ x <- round(x)
449
+ y <- round(y)
450
+
451
+ # Clamp to valid range
452
+ if (x < 1) x <- 1
453
+ if (x > self$pixel_width) x <- self$pixel_width
454
+ if (y < 1) y <- 1
455
+ if (y > self$pixel_height) y <- self$pixel_height
456
+
457
+ # Convert pixel coords to cell coords
458
+ cell_col <- ((x - 1) %/% 2) + 1
459
+ cell_row <- ((y - 1) %/% 4) + 1
460
+
461
+ # Get dot position within cell
462
+ dot_col <- (x - 1) %% 2
463
+ dot_row <- (y - 1) %% 4
464
+
465
+ # Get current character
466
+ current_char <- self$matrix[cell_row, cell_col]
467
+ if (current_char == " " || !is_braille(current_char)) {
468
+ current_code <- self$braille_base
469
+ } else {
470
+ current_code <- utf8ToInt(current_char)
471
+ }
472
+
473
+ # Set the dot bit
474
+ dot_bit <- self$get_dot_bit(dot_row, dot_col)
475
+ new_code <- bitwOr(current_code, dot_bit)
476
+
477
+ self$matrix[cell_row, cell_col] <- intToUtf8(new_code)
478
+
479
+ if (!is.null(color)) {
480
+ self$color_matrix[cell_row, cell_col] <- color
481
+ }
482
+ }
483
+ )
484
+ )
485
+
486
+
487
+ #' Block Canvas Class
488
+ #'
489
+ #' Canvas using Unicode block elements for 2x vertical resolution.
490
+ #' Uses half-block characters: upper half (U+2580), lower half (U+2584), full block (U+2588).
491
+ #'
492
+ #' Resolution: 1x2 (1 horizontal, 2 vertical pixels per character)
493
+ #'
494
+ #' @export
495
+ BlockCanvas <- R6Class("BlockCanvas",
496
+ inherit = Canvas,
497
+ public = list(
498
+ #' @field upper_block Upper half block character
499
+ upper_block = "\u2580", # ▀
500
+ #' @field lower_block Lower half block character
501
+ lower_block = "\u2584", # ▄
502
+ #' @field full_block Full block character
503
+ full_block = "\u2588", # █
504
+ #' @field pixel_state Matrix tracking which half-pixels are set (0=none, 1=upper, 2=lower, 3=both)
505
+ pixel_state = NULL,
506
+
507
+ #' @description Initialize Block canvas
508
+ #' @param width Character width
509
+ #' @param height Character height
510
+ initialize = function(width, height) {
511
+ self$x_mult <- 1
512
+ self$y_mult <- 2
513
+ super$initialize(width, height)
514
+ self$pixel_state <- matrix(0L, nrow = height, ncol = width)
515
+ },
516
+
517
+ #' @description Set a pixel in Block space
518
+ #' @param x X coordinate (1-based, in pixel space: 1 to width)
519
+ #' @param y Y coordinate (1-based, in pixel space: 1 to height*2, 1 = top)
520
+ #' @param color Optional color name
521
+ set_pixel = function(x, y, color = NULL) {
522
+ x <- round(x)
523
+ y <- round(y)
524
+
525
+ # Clamp to valid range
526
+ if (x < 1) x <- 1
527
+ if (x > self$pixel_width) x <- self$pixel_width
528
+ if (y < 1) y <- 1
529
+ if (y > self$pixel_height) y <- self$pixel_height
530
+
531
+ # Convert pixel coords to cell coords
532
+ cell_col <- x
533
+ cell_row <- ((y - 1) %/% 2) + 1
534
+
535
+ # Determine if upper or lower half
536
+ is_lower <- ((y - 1) %% 2) == 1
537
+
538
+ # Update pixel state
539
+ current_state <- self$pixel_state[cell_row, cell_col]
540
+ if (is_lower) {
541
+ new_state <- bitwOr(current_state, 2L) # Set lower bit
542
+ } else {
543
+ new_state <- bitwOr(current_state, 1L) # Set upper bit
544
+ }
545
+ self$pixel_state[cell_row, cell_col] <- new_state
546
+
547
+ # Update character based on state
548
+ char <- switch(as.character(new_state),
549
+ "0" = " ",
550
+ "1" = self$upper_block,
551
+ "2" = self$lower_block,
552
+ "3" = self$full_block,
553
+ " "
554
+ )
555
+ self$matrix[cell_row, cell_col] <- char
556
+
557
+ if (!is.null(color)) {
558
+ self$color_matrix[cell_row, cell_col] <- color
559
+ }
560
+ },
561
+
562
+ #' @description Clear the canvas
563
+ clear = function() {
564
+ super$clear()
565
+ self$pixel_state <- matrix(0L, nrow = self$height, ncol = self$width)
566
+ }
567
+ )
568
+ )
569
+
570
+
571
+ #' Create a canvas of the specified type
572
+ #'
573
+ #' @param width Character width
574
+ #' @param height Character height
575
+ #' @param type Canvas type: "ascii", "braille", or "block"
576
+ #' @return A Canvas object
577
+ #' @export
578
+ create_canvas <- function(width, height, type = "braille") {
579
+ switch(type,
580
+ "ascii" = AsciiCanvas$new(width, height),
581
+ "braille" = BrailleCanvas$new(width, height),
582
+ "block" = BlockCanvas$new(width, height),
583
+ stop("Unknown canvas type: ", type)
584
+ )
585
+ }
586
+
@@ -0,0 +1,114 @@
1
+ #' Combine plot matrices horizontally
2
+ #'
3
+ #' This function combines multiple plot matrices horizontally, centering them vertically.
4
+ #'
5
+ #' @param ... A list of plot matrices to be combined.
6
+ #' @return A combined plot matrix.
7
+ #' @export
8
+ #'
9
+ cbind_plots = function(...) {
10
+ plots <- list(...)
11
+ if (any(sapply(plots, function(plot) !inherits(plot, "plotcli")))) {
12
+ stop("All input objects must be of class 'plotcli'")
13
+ }
14
+ max_rows <- max(sapply(plots, function(plot) nrow(plot$plot_matrix)))
15
+
16
+ # Initialize the combined plot matrix
17
+ combined_matrix <- matrix(" ", nrow = max_rows, ncol = 0)
18
+
19
+ for (i in seq_along(plots)) {
20
+ plot <- plots[[i]]
21
+ # Center the plot matrix vertically
22
+ row_offset <- floor((max_rows - nrow(plot$plot_matrix)) / 2)
23
+ centered_matrix <- matrix(" ", nrow = max_rows, ncol = ncol(plot$plot_matrix))
24
+ centered_matrix[(1 + row_offset):(nrow(plot$plot_matrix) + row_offset), ] <- plot$plot_matrix
25
+
26
+ # Remove the last endline in the first plot_matrix
27
+ if (i == 1) {
28
+ centered_matrix <- centered_matrix[, -ncol(centered_matrix)]
29
+ }
30
+
31
+ # Combine the centered plot matrix with the combined matrix
32
+ combined_matrix <- cbind(combined_matrix, centered_matrix)
33
+ }
34
+
35
+ return(combined_matrix)
36
+ }
37
+
38
+ #' Combine plot matrices vertically
39
+ #'
40
+ #' This function combines multiple plot matrices vertically, centering them horizontally.
41
+ #'
42
+ #' @param ... A list of plot matrices to be combined.
43
+ #' @return A combined plot matrix.
44
+ #' @export
45
+ #'
46
+ rbind_plots = function(...) {
47
+ plots <- list(...)
48
+ if (any(sapply(plots, function(plot) !inherits(plot, "plotcli")))) {
49
+ stop("All input objects must be of class 'plotcli'")
50
+ }
51
+ max_cols <- max(sapply(plots, function(plot) ncol(plot$plot_matrix)))
52
+
53
+ # Initialize the combined plot matrix
54
+ combined_matrix <- matrix(" ", nrow = 0, ncol = max_cols)
55
+
56
+ for (plot in plots) {
57
+ # Center the plot matrix horizontally
58
+ col_offset <- floor((max_cols - ncol(plot$plot_matrix)) / 2)
59
+ centered_matrix <- matrix(" ", nrow = nrow(plot$plot_matrix), ncol = max_cols)
60
+ centered_matrix[, (1 + col_offset):(ncol(plot$plot_matrix) + col_offset)] <- plot$plot_matrix
61
+
62
+ # Combine the centered plot matrix with the combined matrix
63
+ combined_matrix <- rbind(combined_matrix, centered_matrix)
64
+ }
65
+
66
+ return(combined_matrix)
67
+ }
68
+
69
+
70
+ #' Overload the "+" operator for plotcli objects
71
+ #'
72
+ #' This function overloads the "+" operator to merge two plotcli objects.
73
+ #'
74
+ #' @param plot1 A plotcli object to be merged.
75
+ #' @param plot2 A plotcli object to be merged.
76
+ #' @return A new plotcli object containing the combined data from both objects.
77
+ #' @export
78
+ `+.plotcli` <- function(plot1, plot2) {
79
+ return(plot1$merge(plot2))
80
+ }
81
+
82
+ #' Generic function for combining plotcli objects horizontally
83
+ #'
84
+ #' @param ... plotcli objects to be combined.
85
+ #' @param deparse.level The deparsing level for the arguments.
86
+ #' @return A combined plot matrix.
87
+ #' @export
88
+ cbind.plotcli <- function(..., deparse.level = 1) {
89
+ plots <- list(...)
90
+ if (length(plots) != 2) {
91
+ stop("cbind.plotcli only supports combining exactly two plotcli objects.")
92
+ }
93
+ plot1 <- plots[[1]]
94
+ plot2 <- plots[[2]]
95
+ combined_matrix <- cbind_plots(plot1, plot2)
96
+ return(combined_matrix)
97
+ }
98
+
99
+ #' Generic function for combining plotcli objects vertically
100
+ #'
101
+ #' @param ... plotcli objects to be combined.
102
+ #' @param deparse.level The deparsing level for the arguments.
103
+ #' @return A combined plot matrix.
104
+ #' @export
105
+ rbind.plotcli <- function(..., deparse.level = 1) {
106
+ plots <- list(...)
107
+ if (length(plots) != 2) {
108
+ stop("rbind.plotcli only supports combining exactly two plotcli objects.")
109
+ }
110
+ plot1 <- plots[[1]]
111
+ plot2 <- plots[[2]]
112
+ combined_matrix <- rbind_plots(plot1, plot2)
113
+ return(combined_matrix)
114
+ }