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,963 @@
1
+ #' plotcli R6 Class
2
+ #'
3
+ #' This class provides a set of methods to create and customize command-line plots using R6.
4
+ #' It supports various plot types, such as scatter, line, bar, and box plots, and allows
5
+ #' customization of plot elements, such as title, axis labels, ticks, and legend.
6
+ #'
7
+ #' @section Usage:
8
+ #' \preformatted{
9
+ #' plotcli <- plotcli$new()
10
+ #' plotcli$add_data(data)
11
+ #' plotcli$print_plot()
12
+ #' }
13
+ #' @field plot_width The width of the plot
14
+ #' @field plot_height The height of the plot
15
+ #' @field plot_canvas The canvas for drawing the plot
16
+ #' @field plot_matrix The matrix containing the entire plot, including borders, labels, and title
17
+ #' @field data A list containing the data sets to be plotted
18
+ #' @field title The title of the plot
19
+ #' @field x_label The label for the x-axis
20
+ #' @field y_label The label for the y-axis
21
+ #' @field ylim The limits for the y-axis
22
+ #' @field xlim The limits for the x-axis
23
+ #' @field x_min The minimum value of the x-axis
24
+ #' @field x_max The maximum value of the x-axis
25
+ #' @field y_min The minimum value of the y-axis
26
+ #' @field y_max The maximum value of the y-axis
27
+ #' @field plot_matrix_canvas_row_start The starting row of the plot canvas within the plot matrix
28
+ #' @field plot_matrix_canvas_col_start The starting column of the plot canvas within the plot matrix
29
+ #' @field is_boxplot A logical value indicating if the plot is a boxplot
30
+ #' @field draw_legend A logical value indicating if the legend should be drawn
31
+ #'
32
+ #' @section Methods:
33
+ #' \describe{
34
+ #' \item{initialize()}{Initializes the PlotCLI object with parameters.}
35
+ #' \item{initialize_plot_matrix()}{Initializes the plot matrix with the plot canvas.}
36
+ #' \item{print()}{Default print method for PlotCLI object.}
37
+ #' \item{add_row()}{Adds a single row to the plot matrix.}
38
+ #' \item{add_col()}{Adds a single column to the plot matrix.}
39
+ #' \item{add_borders()}{Adds borders around the plot canvas.}
40
+ #' \item{add_row_col_index()}{Adds row and column index to the plot matrix.}
41
+ #' \item{add_title()}{Adds a title to the plot matrix.}
42
+ #' \item{add_y_ticks()}{Adds y-axis tick labels to the plot matrix.}
43
+ #' \item{add_y_label()}{Adds a y-axis label to the plot matrix.}
44
+ #' \item{add_x_ticks()}{Adds x-axis tick labels to the plot matrix.}
45
+ #' \item{add_x_label()}{Adds an x-axis label to the plot matrix.}
46
+ #' \item{add_legend()}{Adds a legend to the plot matrix.}
47
+ #' \item{add_data()}{Adds data to the object.}
48
+ #' \item{get_min_max()}{Gets minimum and maximum values for x and y.}
49
+ #' \item{remove_out_of_range_data()}{Removes out of range data points if xlim and ylim were given.}
50
+ #' \item{draw_scatter_plot()}{Draws a scatter plot on the plot canvas.}
51
+ #' \item{draw_line_plot()}{Draws a line plot on the plot canvas.}
52
+ #' \item{draw_barplot()}{Draws a bar plot on the plot canvas.}
53
+ #' \item{draw_barplot_braille()}{Draws a bar plot with braille characters on the plot canvas.}
54
+ #' \item{draw_boxplot()}{Draws a box plot on the plot canvas.}
55
+ #' \item{print_plot()}{Assembles all plot elements and prints the plot to the console.}
56
+ #' }
57
+ #' @export
58
+ #'
59
+ #' @examples
60
+ #' # Create a new plotcli object
61
+ #' plotcli <- plotcli$new()
62
+ #'
63
+ #' # Add data for a scatter plot
64
+ #' plotcli$add_data(list(x = 1:10, y = rnorm(10), type = "scatter", color = "red"))
65
+ #'
66
+ #' # Print the plot
67
+ #' plotcli$print_plot()
68
+ #'
69
+ plotcli <- R6Class("plotcli",
70
+ public = list(
71
+ plot_width = getOption("plotcli.plot_width", 80),
72
+ plot_height = getOption("plotcli.plot_height", 40),
73
+ plot_canvas = NULL,
74
+ plot_matrix = NULL,
75
+ data = NULL,
76
+ title = NULL,
77
+ x_label = "x",
78
+ y_label = "y",
79
+ ylim = NULL,
80
+ xlim = NULL,
81
+ x_min = NULL,
82
+ x_max = NULL,
83
+ y_min = NULL,
84
+ y_max = NULL,
85
+ # keeping track of the location of the canvas inside the entire plot matrix
86
+ plot_matrix_canvas_row_start = NULL,
87
+ plot_matrix_canvas_col_start = NULL,
88
+ is_boxplot = FALSE,
89
+ draw_legend = TRUE,
90
+
91
+ #' @description Initialize object
92
+ #' @param plot_width integer, width of the plot canvas
93
+ #' @param plot_height integer, height of the plot canvas
94
+ #' @param title character, title of the plot
95
+ #' @param x_label character, label for the x-axis
96
+ #' @param y_label character, label for the y-axis
97
+ #' @param xlim numeric vector, limits for the x-axis
98
+ #' @param ylim numeric vector, limits for the y-axis
99
+ #' @param is_boxplot logical, whether the plot is a boxplot
100
+ #' @param draw_legend logical, whether to draw the legend
101
+ initialize = function(
102
+ plot_width = 60,
103
+ plot_height = 20,
104
+ x_label = "x",
105
+ y_label = "y",
106
+ ylim = NULL,
107
+ xlim = NULL,
108
+ title = NULL,
109
+ is_boxplot = FALSE,
110
+ draw_legend = TRUE
111
+ ) {
112
+ self$plot_canvas <- matrix(" ", nrow = plot_height, ncol = plot_width)
113
+ self$x_label <- x_label
114
+ self$y_label <- y_label
115
+ self$ylim <- ylim
116
+ self$xlim <- xlim
117
+ self$title <- title
118
+ self$plot_width <- plot_width
119
+ self$plot_height <- plot_height
120
+ self$is_boxplot <- is_boxplot
121
+ self$draw_legend <- draw_legend
122
+ },
123
+
124
+ #' This function initializes the plot matrix based on the plot canvas.
125
+ #' @description Initialize the plot matrix
126
+ #' @param plot_width The width of the plot
127
+ #' @param plot_height The height of the plot
128
+ #' @return A plot matrix object
129
+ initialize_plot_matrix = function() {
130
+ self$plot_matrix <- self$plot_canvas
131
+ self$plot_matrix_canvas_row_start <- 1
132
+ self$plot_matrix_canvas_col_start <- 1
133
+ },
134
+
135
+ #' @description Default print method for plotcli object
136
+ #' @param ... Additional arguments passed to the print method
137
+ #' @return The plotcli object, invisibly
138
+ print = function(...) {
139
+ self$print_plot()
140
+ invisible(self)
141
+ },
142
+
143
+ #' @description Add a single row to the plot matrix
144
+ #' @param bottom logical, if TRUE, add row to the bottom of the matrix, otherwise add to the top (default: FALSE)
145
+ add_row = function(bottom = FALSE) {
146
+ plot_matrix <- self$plot_matrix
147
+ if (bottom) {
148
+ plot_matrix <- rbind(plot_matrix, " ")
149
+ } else {
150
+ plot_matrix <- rbind(" ", plot_matrix)
151
+ }
152
+
153
+ # update the canvas location
154
+ self$plot_matrix_canvas_row_start <- self$plot_matrix_canvas_row_start + 1
155
+
156
+ self$plot_matrix <- plot_matrix
157
+ },
158
+
159
+ #' @description Add a single column to the plot matrix
160
+ add_col = function() {
161
+ plot_matrix <- self$plot_matrix
162
+ plot_matrix <- cbind(" ", plot_matrix)
163
+
164
+ # update the canvas location
165
+ self$plot_matrix_canvas_col_start <- self$plot_matrix_canvas_col_start + 1
166
+
167
+ self$plot_matrix <- plot_matrix
168
+ },
169
+
170
+ #' @description Add borders to the plot matrix
171
+ add_borders = function() {
172
+
173
+ plot_matrix <- self$plot_matrix
174
+
175
+ # Add horizontal borders
176
+ plot_matrix <- rbind(
177
+ c(top_left_corner_char, rep(horiz_border_char, ncol(plot_matrix)), top_right_corner_char),
178
+ cbind(vert_border_char, plot_matrix, vert_border_char),
179
+ c(bottom_left_corner_char, rep(horiz_border_char, ncol(plot_matrix)), bottom_right_corner_char)
180
+ )
181
+
182
+ # update the canvas location
183
+ self$plot_matrix_canvas_row_start <- self$plot_matrix_canvas_row_start + 1
184
+ self$plot_matrix_canvas_col_start <- self$plot_matrix_canvas_col_start + 1
185
+
186
+ self$plot_matrix <- plot_matrix
187
+ },
188
+
189
+ #' @description Add row and column index to the plot matrix
190
+ add_row_col_index = function() {
191
+ plot_matrix <- self$plot_matrix
192
+
193
+ plot_matrix <- rbind(
194
+ c(
195
+ rep(" ", times = self$plot_matrix_canvas_col_start - 1),
196
+ # only take the last character of the number
197
+ substr(1:ncol(self$plot_canvas), nchar(1:ncol(self$plot_canvas)), nchar(1:ncol(self$plot_canvas))),
198
+ rep(" ", times = ncol(plot_matrix) - (self$plot_matrix_canvas_col_start + ncol(self$plot_canvas) - 1))
199
+ ),
200
+ plot_matrix
201
+ )
202
+
203
+ plot_matrix <- cbind(
204
+ c(
205
+ rep(" ", times = self$plot_matrix_canvas_row_start),
206
+ substr(1:nrow(self$plot_canvas), nchar(1:nrow(self$plot_canvas)), nchar(1:nrow(self$plot_canvas))),
207
+ rep(" ", times = nrow(plot_matrix) - (self$plot_matrix_canvas_row_start + nrow(self$plot_canvas)))
208
+ ),
209
+ plot_matrix
210
+ )
211
+
212
+ # update the canvas location
213
+ self$plot_matrix_canvas_row_start <- self$plot_matrix_canvas_row_start + 1
214
+ self$plot_matrix_canvas_col_start <- self$plot_matrix_canvas_col_start + 1
215
+
216
+ self$plot_matrix <- plot_matrix
217
+ },
218
+
219
+ #' Add title to the plot matrix
220
+ #'
221
+ #' @param title character, title of the plot
222
+ add_title = function() {
223
+ if (!is.null(self$title)) {
224
+ title <- self$title
225
+ plot_matrix <- self$plot_matrix
226
+ title_vec <- strsplit(title, "")[[1]]
227
+ nchar_title <- length(title_vec)
228
+ title_col_start <- self$plot_matrix_canvas_col_start + floor((ncol(self$plot_canvas) - nchar_title) / 2)
229
+ if (title_col_start < self$plot_matrix_canvas_col_start) title_col_start <- self$plot_matrix_canvas_col_start
230
+ title_col_end <- title_col_start + nchar_title - 1
231
+ plot_matrix <- rbind(rep(" ", ncol(plot_matrix)), plot_matrix)
232
+ plot_matrix[1, title_col_start:title_col_end] <- title_vec
233
+
234
+ # update the canvas location
235
+ self$plot_matrix_canvas_row_start <- self$plot_matrix_canvas_row_start + 1
236
+
237
+ self$plot_matrix <- plot_matrix
238
+ }
239
+ },
240
+
241
+ #' Add y-ticks label to the plot matrix
242
+ #'
243
+ #' @param n_ticks numeric, number of ticks
244
+ add_y_ticks = function(n_ticks = 5) {
245
+ if (n_ticks < 2) stop("n_ticks must be at least 2")
246
+ plot_matrix <- self$plot_matrix
247
+ y_min <- self$y_min
248
+ y_max <- self$y_max
249
+
250
+ offset <- 1
251
+
252
+ # hence the function format_four_chars
253
+ char_length <- 4
254
+
255
+ # initialize matrix for the y ticks
256
+ y_tick_matrix <- matrix(" ", nrow = nrow(plot_matrix), ncol = offset + char_length)
257
+
258
+ # now we need want to place the y ticks at the appropriate places within our y tick matrix and space
259
+ # them out evenly
260
+ y_ticks <- seq(y_max, y_min, length.out = n_ticks)
261
+
262
+ # Place the y ticks into the y tick matrix at the appropriate spots
263
+ y_tick_positions <- round(seq(1, nrow(self$plot_canvas), length.out = n_ticks)) + self$plot_matrix_canvas_row_start - 1
264
+
265
+ for (i in 1:length(y_ticks)) {
266
+ y_tick_matrix[y_tick_positions[i], 1:(offset + char_length)] <- c(unlist(stringr::str_split(format_four_chars(y_ticks[i]), pattern = "")), rep(" ", times = offset))
267
+ }
268
+
269
+ # Combine the y tick matrix with the plot matrix
270
+ plot_matrix <- cbind(y_tick_matrix, plot_matrix)
271
+
272
+ # update the canvas location
273
+ self$plot_matrix_canvas_col_start <- self$plot_matrix_canvas_col_start + offset + char_length
274
+
275
+ self$plot_matrix <- plot_matrix
276
+ },
277
+
278
+ #' Add y-axis label to the plot matrix
279
+ #'
280
+ #' @description Add a y-axis label to the plot matrix
281
+ #' @param y_label character, the y-axis label to be added
282
+ add_y_label = function(y_label = self$y_label) {
283
+ if(!is.null(y_label)) {
284
+ plot_matrix <- self$plot_matrix
285
+ y_min <- self$y_min
286
+ y_max <- self$y_max
287
+
288
+ offset <- 3
289
+
290
+ # hence the function format_four_chars
291
+ char_length <- nchar(y_label)
292
+
293
+ # initialize matrix for the y label
294
+ y_label_matrix <- matrix(" ", nrow = nrow(plot_matrix), ncol = offset + char_length)
295
+
296
+ # Place the y label into the y label matrix at the appropriate spot
297
+ y_label_position <- floor(nrow(self$plot_canvas) / 2) + self$plot_matrix_canvas_row_start - 1
298
+
299
+ y_label_matrix[y_label_position, 1:(offset + char_length)] <- c(unlist(stringr::str_split(y_label, "")), rep(" ", times = offset))
300
+
301
+ # Combine the y label matrix with the plot matrix
302
+ plot_matrix <- cbind(y_label_matrix, plot_matrix)
303
+
304
+ # update the canvas location
305
+ self$plot_matrix_canvas_col_start <- self$plot_matrix_canvas_col_start + offset + char_length
306
+
307
+ self$plot_matrix <- plot_matrix
308
+ }
309
+ },
310
+
311
+ #' Add x-ticks label to the plot matrix
312
+ #'
313
+ #' @param n_ticks numeric, number of ticks
314
+ add_x_ticks = function(n_ticks = 5) {
315
+ if (n_ticks < 2) stop("n_ticks must be at least 2")
316
+ plot_matrix <- self$plot_matrix
317
+
318
+ x_min <- self$x_min
319
+ x_max <- self$x_max
320
+
321
+ offset <- 1
322
+
323
+ # hence the function format_four_chars
324
+ char_length <- 4
325
+
326
+ # initialize matrix for the x ticks
327
+ x_tick_matrix <- matrix(" ", nrow = 1 + offset, ncol = ncol(plot_matrix))
328
+
329
+ if (self$is_boxplot) {
330
+ x_ticks <- unlist(lapply(self$data, function(dat) dat$name))
331
+ x_tick_positions <- unlist(lapply(self$data, function(dat) dat$x_position)) + self$plot_matrix_canvas_col_start - 1 - (nchar(x_ticks) %/% 2)
332
+ } else {
333
+ # now we need want to place the x ticks at the appropriate places within our x tick matrix and space
334
+ # them out evenly
335
+ x_ticks <- seq(x_min, x_max, length.out = n_ticks)
336
+
337
+ # Place the x ticks into the x tick matrix at the appropriate spots
338
+ x_tick_positions <- round(seq(1, ncol(self$plot_canvas), length.out = n_ticks), digits = 0) + self$plot_matrix_canvas_col_start - char_length
339
+
340
+ # center the x ticks
341
+ x_tick_positions[2:(length(x_ticks) - 1)] <- x_tick_positions[2:(length(x_ticks) - 1)] + floor(char_length / 2)
342
+
343
+ # first element needs to start earlier
344
+ x_tick_positions[1] <- x_tick_positions[1] + char_length - 1
345
+ }
346
+
347
+ for (i in 1:length(x_ticks)) {
348
+ if (self$is_boxplot) {
349
+ x_tick_matrix[1 + offset, x_tick_positions[i]:(x_tick_positions[i] + nchar(x_ticks[i]) - 1)] <- unlist(stringr::str_split(x_ticks[i], pattern = ""))
350
+ } else {
351
+ x_tick_matrix[1 + offset, x_tick_positions[i]:(x_tick_positions[i] + char_length - 1)] <- unlist(stringr::str_split(format_four_chars(x_ticks[i]), pattern = ""))
352
+ }
353
+ }
354
+
355
+
356
+ # Combine the x tick matrix with the plot matrix
357
+ plot_matrix <- rbind(plot_matrix, x_tick_matrix)
358
+
359
+ self$plot_matrix <- plot_matrix
360
+ },
361
+
362
+ #' Add x-axis label to the plot matrix
363
+ #'
364
+ #' @description Add x-axis label to the plot matrix
365
+ #' @param x_label x label
366
+ add_x_label = function(x_label = self$x_label) {
367
+ if(!is.null(self$x_label)) {
368
+ plot_matrix <- self$plot_matrix
369
+ x_label <- self$x_label
370
+
371
+ offset <- 1
372
+
373
+ # initialize matrix for the x label
374
+ x_label_matrix <- matrix(" ", nrow = 1 + offset, ncol = ncol(plot_matrix))
375
+
376
+ # Place the x label into the x label matrix at the center
377
+ x_label_position <- floor(ncol(self$plot_canvas) / 2) - floor(nchar(x_label) / 2) + self$plot_matrix_canvas_col_start - 1
378
+
379
+ x_label_matrix[1 + offset, x_label_position:(x_label_position + nchar(x_label) - 1)] <- unlist(stringr::str_split(x_label, pattern = ""))
380
+
381
+ # Combine the x label matrix with the plot matrix
382
+ plot_matrix <- rbind(plot_matrix, x_label_matrix)
383
+
384
+ self$plot_matrix <- plot_matrix
385
+ }
386
+ },
387
+
388
+ #' Add legend to the plot matrix
389
+ #'
390
+ #' @description Add legend to the plot matrix
391
+ add_legend = function() {
392
+ if (!self$draw_legend) return()
393
+ plot_matrix <- self$plot_matrix
394
+
395
+ legend_names <- unlist(lapply(self$data, function(dat) dat$name))
396
+ n_legend <- length(legend_names)
397
+
398
+ # Center the legend based on nrow of plot_matrix
399
+ offset <- 3
400
+ legend_start <- floor(nrow(self$plot_canvas) / 2) + self$plot_matrix_canvas_row_start - 1 - floor(n_legend / 2)
401
+ legend_rows <- legend_start:(legend_start + n_legend - 1)
402
+
403
+ # Initialize matrix for the legend
404
+ legend_matrix <- matrix(" ", nrow = nrow(plot_matrix), ncol = n_legend + offset)
405
+
406
+ # Place the legend names into the legend matrix at the appropriate spots
407
+ for (i in 1:n_legend) {
408
+ legend_matrix[legend_rows[i], (offset + 1)] <- make_colored(legend_names[i], self$data[[i]]$color)
409
+ }
410
+
411
+ # Combine the legend matrix with the plot matrix
412
+ plot_matrix <- cbind(plot_matrix, legend_matrix)
413
+
414
+ self$plot_matrix <- plot_matrix
415
+ },
416
+
417
+ #' Add data to the object.
418
+ #'
419
+ #' @param data list, list with elements: x, y, type, color, braille, name
420
+ add_data = function(data) {
421
+ # check if we have a boxplot
422
+ if (
423
+ (data$type != "boxplot" & self$is_boxplot) |
424
+ (data$type == "boxplot" & length(self$data) > 0 & !self$is_boxplot)
425
+ ) {
426
+ stop("boxplots cannot be combined with other types at the moment")
427
+ }
428
+
429
+ # if there is no name, give it a name based on the order (like data_1
430
+ if (is.null(data$name)) {
431
+ data$name <- paste("data", length(self$data) + 1, sep = "_")
432
+ } else {
433
+ # Ensure unique names
434
+ existing_names <- unlist(lapply(self$data, function(dat) dat$name))
435
+ all_names <- c(existing_names, data$name)
436
+ unique_names <- make_unique_names(all_names)
437
+ data$name <- unique_names[length(unique_names)]
438
+ }
439
+
440
+ # add matrix_colored to the data
441
+ data$matrix_colored <- list()
442
+ if (is.null(data$braille)) data$braille <- getOption("plotcli.braille", TRUE)
443
+
444
+ if (is.null(self$data)) {
445
+ self$data <- list(data)
446
+ } else {
447
+ self$data <- append(self$data, list(data))
448
+ }
449
+
450
+ if (data$type == "boxplot") {
451
+ self$is_boxplot <- TRUE
452
+ }
453
+ },
454
+
455
+ #' Get minimum and maximum values for x and y
456
+ #'
457
+ #' @description Calculate the minimum and maximum values for x and y
458
+ get_min_max = function() {
459
+ cushion_percentage <- 0.00
460
+
461
+ self$x_min <- min(unlist(lapply(self$data, function(dat) dat$x)))
462
+ self$x_max <- max(unlist(lapply(self$data, function(dat) dat$x)))
463
+ self$y_min <- min(unlist(lapply(self$data, function(dat) dat$y)))
464
+ self$y_max <- max(unlist(lapply(self$data, function(dat) dat$y)))
465
+
466
+ if (is.null(self$xlim)) {
467
+ x_range <- self$x_max - self$x_min
468
+ if (x_range == 0) {
469
+ expand <- if (self$x_min == 0) 1 else abs(self$x_min) * 0.05
470
+ self$x_min <- self$x_min - expand
471
+ self$x_max <- self$x_max + expand
472
+ } else {
473
+ cushion_x <- x_range * cushion_percentage
474
+ self$x_min <- self$x_min - cushion_x
475
+ self$x_max <- self$x_max + cushion_x
476
+ }
477
+ } else {
478
+ self$x_min <- self$xlim[1]
479
+ self$x_max <- self$xlim[2]
480
+ }
481
+
482
+ if (is.null(self$ylim)) {
483
+ y_range <- self$y_max - self$y_min
484
+ if (y_range == 0) {
485
+ expand <- if (self$y_min == 0) 1 else abs(self$y_min) * 0.05
486
+ self$y_min <- self$y_min - expand
487
+ self$y_max <- self$y_max + expand
488
+ } else {
489
+ cushion_y <- y_range * cushion_percentage
490
+ self$y_min <- self$y_min - cushion_y
491
+ self$y_max <- self$y_max + cushion_y
492
+ }
493
+ } else {
494
+ self$y_min <- self$ylim[1]
495
+ self$y_max <- self$ylim[2]
496
+ }
497
+ },
498
+
499
+ #' Function to remove out of range data points if xlim and ylim were given
500
+ #'
501
+ #' @description Remove data points that are outside the specified xlim and ylim
502
+ remove_out_of_range_data = function() {
503
+ for (i in 1:length(self$data)) {
504
+ in_x <- self$data[[i]]$x >= self$x_min & self$data[[i]]$x <= self$x_max
505
+ in_y <- self$data[[i]]$y >= self$y_min & self$data[[i]]$y <= self$y_max
506
+ in_both <- in_x & in_y
507
+
508
+ self$data[[i]]$x <- self$data[[i]]$x[in_both]
509
+ self$data[[i]]$y <- self$data[[i]]$y[in_both]
510
+ }
511
+ },
512
+
513
+ #' Draw a scatter plot to the plot canvas.
514
+ #'
515
+ #' @description Draw a scatter plot of the specified data set on the plot canvas.
516
+ #' @param set_idx numeric, the data element index to be drawn
517
+ draw_scatter_plot = function(set_idx) {
518
+ plot_canvas <- self$plot_canvas
519
+
520
+ # Calculate the overall minimum and maximum values for all data sets
521
+ x_min <- self$x_min
522
+ x_max <- self$x_max
523
+ y_min <- self$y_min
524
+ y_max <- self$y_max
525
+
526
+ x <- self$data[[set_idx]]$x
527
+ y <- self$data[[set_idx]]$y
528
+
529
+ braille <- self$data[[set_idx]]$braille
530
+
531
+ if (braille) {
532
+ # Calculate Braille resolution (2x horizontal, 4x vertical)
533
+ braille_width <- ncol(plot_canvas) * 2
534
+ braille_height <- nrow(plot_canvas) * 4
535
+
536
+ # Normalize x and y data points to fit within the Braille plot area
537
+ x_norm <- round(normalize_data(x, x_min, x_max, braille_width))
538
+ # Invert y so that higher values are at the top (lower pixel y)
539
+ y_norm <- braille_height - round(normalize_data(y, y_min, y_max, braille_height)) + 1
540
+
541
+ # Draw data points using Braille characters
542
+ for (i in 1:length(x)) {
543
+ # Convert pixel coordinates to Braille cell and dot position
544
+ pos <- pixel_to_braille(x_norm[i], y_norm[i], nrow(plot_canvas), ncol(plot_canvas))
545
+
546
+ # Get current character and set the dot
547
+ current_char <- plot_canvas[pos$cell_row, pos$cell_col]
548
+ plot_canvas[pos$cell_row, pos$cell_col] <- braille_set_dot(
549
+ current_char, pos$dot_row, pos$dot_col
550
+ )
551
+
552
+ # color tracking
553
+ self$data[[set_idx]]$matrix_colored[[length(self$data[[set_idx]]$matrix_colored) + 1]] <-
554
+ list(row = pos$cell_row, col = pos$cell_col)
555
+ }
556
+ } else {
557
+ # Normalize x and y data points to fit within the plot area
558
+ x_norm <- round(normalize_data(x, x_min, x_max, ncol(plot_canvas)))
559
+ y_norm <- round(normalize_data(y, y_min, y_max, nrow(plot_canvas)))
560
+
561
+ # Draw data points using asterisk
562
+ for (i in 1:length(x)) {
563
+ ascii_row <- nrow(plot_canvas) - y_norm[i] + 1
564
+ ascii_col <- x_norm[i]
565
+
566
+ # Clamp to valid range
567
+ ascii_row <- max(1, min(ascii_row, nrow(plot_canvas)))
568
+ ascii_col <- max(1, min(ascii_col, ncol(plot_canvas)))
569
+
570
+ self$data[[set_idx]]$matrix_colored[[length(self$data[[set_idx]]$matrix_colored) + 1]] <-
571
+ list(row = ascii_row, col = ascii_col)
572
+ plot_canvas[ascii_row, ascii_col] <- "*"
573
+ }
574
+ }
575
+
576
+ self$plot_canvas <- plot_canvas
577
+ },
578
+
579
+ #' Draw a line plot to the plot canvas.
580
+ #'
581
+ #' @param set_idx numeric, the data element index to be drawn
582
+ draw_line_plot = function(set_idx) {
583
+ plot_canvas <- self$plot_canvas
584
+
585
+ x_min <- self$x_min
586
+ x_max <- self$x_max
587
+ y_min <- self$y_min
588
+ y_max <- self$y_max
589
+
590
+ x <- self$data[[set_idx]]$x
591
+ y <- self$data[[set_idx]]$y
592
+
593
+ braille <- self$data[[set_idx]]$braille
594
+
595
+ if (braille) {
596
+ # Calculate Braille resolution (2x horizontal, 4x vertical)
597
+ braille_width <- ncol(plot_canvas) * 2
598
+ braille_height <- nrow(plot_canvas) * 4
599
+
600
+ # Normalize to pixel coordinates
601
+ x_norm <- round(normalize_data(x, x_min, x_max, braille_width))
602
+ # Invert y so that higher values are at the top (lower pixel y)
603
+ y_norm <- braille_height - round(normalize_data(y, y_min, y_max, braille_height)) + 1
604
+
605
+ # Draw lines between consecutive points at pixel resolution
606
+ for (i in 1:(length(x) - 1)) {
607
+ # Get all pixel points along the line using Bresenham
608
+ pixel_points <- bresenham(x_norm[i], y_norm[i], x_norm[i + 1], y_norm[i + 1])
609
+
610
+ for (pixel in pixel_points) {
611
+ px <- pixel[1]
612
+ py <- pixel[2]
613
+
614
+ # Convert pixel to Braille cell and dot
615
+ pos <- pixel_to_braille(px, py, nrow(plot_canvas), ncol(plot_canvas))
616
+
617
+ # Set the dot
618
+ current_char <- plot_canvas[pos$cell_row, pos$cell_col]
619
+ plot_canvas[pos$cell_row, pos$cell_col] <- braille_set_dot(
620
+ current_char, pos$dot_row, pos$dot_col
621
+ )
622
+
623
+ # color tracking
624
+ self$data[[set_idx]]$matrix_colored[[length(self$data[[set_idx]]$matrix_colored) + 1]] <-
625
+ list(row = pos$cell_row, col = pos$cell_col)
626
+ }
627
+ }
628
+ } else {
629
+ # ASCII mode - draw at character resolution
630
+ x_norm <- round(normalize_data(x, x_min, x_max, ncol(plot_canvas)))
631
+ y_norm <- round(normalize_data(y, y_min, y_max, nrow(plot_canvas)))
632
+
633
+ for (i in 1:(length(x) - 1)) {
634
+ # Invert y for screen coordinates
635
+ y1 <- nrow(plot_canvas) - y_norm[i] + 1
636
+ y2 <- nrow(plot_canvas) - y_norm[i + 1] + 1
637
+
638
+ points <- bresenham(x_norm[i], y1, x_norm[i + 1], y2)
639
+
640
+ for (point in points) {
641
+ row <- point[2]
642
+ col <- point[1]
643
+
644
+ # Clamp to valid range
645
+ row <- max(1, min(row, nrow(plot_canvas)))
646
+ col <- max(1, min(col, ncol(plot_canvas)))
647
+
648
+ # color tracking
649
+ self$data[[set_idx]]$matrix_colored[[length(self$data[[set_idx]]$matrix_colored) + 1]] <-
650
+ list(row = row, col = col)
651
+ plot_canvas[row, col] <- "*"
652
+ }
653
+ }
654
+ }
655
+
656
+ self$plot_canvas <- plot_canvas
657
+ },
658
+
659
+ #' Draw a barplot to the plot canvas.
660
+ #'
661
+ #' @param set_idx numeric, the data element index to be drawn
662
+ draw_barplot = function(set_idx) {
663
+ plot_canvas <- self$plot_canvas
664
+
665
+ x_min <- self$x_min
666
+ x_max <- self$x_max
667
+ y_min <- self$y_min
668
+ y_max <- self$y_max
669
+
670
+ x <- self$data[[set_idx]]$x
671
+ y <- self$data[[set_idx]]$y
672
+
673
+ # Normalize x and y data points to fit within the plot area
674
+ x_norm <- round(normalize_data(x, x_min, x_max, ncol(plot_canvas)))
675
+ y_norm <- round(normalize_data(y, y_min, y_max, nrow(plot_canvas)))
676
+
677
+ for (i in 1:length(x)) {
678
+ for (j in 1:y_norm[i]) {
679
+ plot_canvas[nrow(plot_canvas) - j + 1, x_norm[i]] <- full_block_char
680
+ self$data[[set_idx]]$matrix_colored[[length(self$data[[set_idx]]$matrix_colored) + 1]] <-
681
+ list(row = nrow(plot_canvas) - j + 1, col = x_norm[i])
682
+ }
683
+ }
684
+
685
+ self$plot_canvas <- plot_canvas
686
+ },
687
+
688
+ #' Draw a barplot to the plot canvas with braille characters.
689
+ #'
690
+ #' @param set_idx numeric, the data element index to be drawn
691
+ draw_barplot_braille = function(set_idx) {
692
+ plot_canvas <- self$plot_canvas
693
+
694
+ x_min <- self$x_min
695
+ x_max <- self$x_max
696
+ y_min <- self$y_min
697
+ y_max <- self$y_max
698
+
699
+ x <- self$data[[set_idx]]$x
700
+ y <- self$data[[set_idx]]$y
701
+
702
+ braille_height <- nrow(plot_canvas) * 4
703
+
704
+ # Normalize x and y data points to fit within the plot area
705
+ x_norm <- round(normalize_data(x, x_min, x_max, ncol(plot_canvas)))
706
+ y_norm <- round(normalize_data(y, y_min, y_max, braille_height))
707
+
708
+ for (i in 1:length(x)) {
709
+ # For each bar, fill from bottom up
710
+ # py goes from braille_height (bottom) up to (braille_height - y_norm[i] + 1)
711
+ bar_top_py <- braille_height - y_norm[i] + 1
712
+
713
+ for (py in braille_height:bar_top_py) {
714
+ # For bars, we fill both columns of the Braille cell for a solid look
715
+ # Use the x_norm position, and fill both dot columns
716
+ pos <- pixel_to_braille(x_norm[i] * 2 - 1, py, nrow(plot_canvas), ncol(plot_canvas))
717
+
718
+ # Set both left and right dots in this row for a fuller bar
719
+ current_char <- plot_canvas[pos$cell_row, pos$cell_col]
720
+ current_char <- braille_set_dot(current_char, pos$dot_row, 0) # left column
721
+ current_char <- braille_set_dot(current_char, pos$dot_row, 1) # right column
722
+ plot_canvas[pos$cell_row, pos$cell_col] <- current_char
723
+
724
+ self$data[[set_idx]]$matrix_colored[[length(self$data[[set_idx]]$matrix_colored) + 1]] <-
725
+ list(row = pos$cell_row, col = pos$cell_col)
726
+ }
727
+ }
728
+
729
+ self$plot_canvas <- plot_canvas
730
+ },
731
+
732
+
733
+
734
+ #' Draw a boxplot to the plot canvas.
735
+ #'
736
+ #' @param set_idx numeric, the data element index to be drawn
737
+ draw_boxplot = function(set_idx) {
738
+ plot_canvas <- self$plot_canvas
739
+
740
+ # x_center <- median(1:ncol(plot_canvas))
741
+ x_center <- self$data[[set_idx]]$x_position
742
+ y <- self$data[[set_idx]]$y
743
+
744
+ y_min <- self$y_min
745
+ y_max <- self$y_max
746
+
747
+ # Calculate box plot statistics on the original y values
748
+ box_stats <- boxplot.stats(y)
749
+
750
+ # Normalize the box plot statistics to fit within the plot area
751
+ stats_norm <- round(normalize_data(box_stats$stats, y_min, y_max, nrow(plot_canvas)))
752
+
753
+ # Draw the box plot using the normalized statistics
754
+ box_top <- nrow(plot_canvas) - stats_norm[5] + 1
755
+ box_bottom <- nrow(plot_canvas) - stats_norm[1] + 1
756
+ box_left <- x_center - 4 # Adjust the left position to create a wider box
757
+ box_right <- x_center + 4 # Adjust the right position to create a wider box
758
+
759
+ # Draw vertical lines
760
+ plot_canvas[box_top:(stats_norm[5] - stats_norm[4] + box_top), x_center] <- box_vert_char
761
+ plot_canvas[box_bottom:(box_bottom - (stats_norm[2] - stats_norm[1])), x_center] <- box_vert_char
762
+
763
+ self$data[[set_idx]]$matrix_colored[[length(self$data[[set_idx]]$matrix_colored) + 1]] <-
764
+ list(row = box_bottom:(box_bottom - (stats_norm[2] - stats_norm[1])), col = x_center)
765
+ self$data[[set_idx]]$matrix_colored[[length(self$data[[set_idx]]$matrix_colored) + 1]] <-
766
+ list(row = box_top:(stats_norm[5] - stats_norm[4] + box_top), col = x_center)
767
+
768
+
769
+ # Draw horizontal lines
770
+ for (i in 1:5) {
771
+ plot_canvas[nrow(plot_canvas) - stats_norm[i] + 1, (box_left + 1):(box_right - 1)] <- box_horiz_char
772
+ # Add colored matrix elements for horizontal lines
773
+ for (col_idx in (box_left + 1):(box_right - 1)) {
774
+ self$data[[set_idx]]$matrix_colored[[length(self$data[[set_idx]]$matrix_colored) + 1]] <- list(row = nrow(plot_canvas) - stats_norm[i] + 1, col = col_idx)
775
+ }
776
+ }
777
+
778
+ # Draw corners
779
+ plot_canvas[nrow(plot_canvas) - stats_norm[2] + 1, box_left] <- box_top_left_corner_char
780
+ plot_canvas[nrow(plot_canvas) - stats_norm[2] + 1, box_right] <- box_top_right_corner_char
781
+ plot_canvas[nrow(plot_canvas) - stats_norm[4] + 1, box_left] <- box_bottom_left_corner_char
782
+ plot_canvas[nrow(plot_canvas) - stats_norm[4] + 1, box_right] <- box_bottom_right_corner_char
783
+
784
+ plot_canvas[nrow(plot_canvas) - stats_norm[2] + 1, box_left] <- box_bottom_left_corner_char
785
+ plot_canvas[nrow(plot_canvas) - stats_norm[2] + 1, box_right] <- box_bottom_right_corner_char
786
+ plot_canvas[nrow(plot_canvas) - stats_norm[4] + 1, box_left] <- box_top_left_corner_char
787
+ plot_canvas[nrow(plot_canvas) - stats_norm[4] + 1, box_right] <- box_top_right_corner_char
788
+
789
+ # Add colored matrix elements for corners
790
+ self$data[[set_idx]]$matrix_colored[[length(self$data[[set_idx]]$matrix_colored) + 1]] <- list(row = nrow(plot_canvas) - stats_norm[2] + 1, col = box_left)
791
+ self$data[[set_idx]]$matrix_colored[[length(self$data[[set_idx]]$matrix_colored) + 1]] <- list(row = nrow(plot_canvas) - stats_norm[2] + 1, col = box_right)
792
+ self$data[[set_idx]]$matrix_colored[[length(self$data[[set_idx]]$matrix_colored) + 1]] <- list(row = nrow(plot_canvas) - stats_norm[4] + 1, col = box_left)
793
+ self$data[[set_idx]]$matrix_colored[[length(self$data[[set_idx]]$matrix_colored) + 1]] <- list(row = nrow(plot_canvas) - stats_norm[4] + 1, col = box_right)
794
+
795
+ for (i in (stats_norm[4] - 2):(stats_norm[2])) {
796
+ plot_canvas[nrow(plot_canvas) - i + 0, box_left] <- box_vert_char
797
+ plot_canvas[nrow(plot_canvas) - i + 0, box_right] <- box_vert_char
798
+ # Add colored matrix elements for vertical lines
799
+ self$data[[set_idx]]$matrix_colored[[length(self$data[[set_idx]]$matrix_colored) + 1]] <- list(row = nrow(plot_canvas) - i + 0, col = box_left)
800
+ self$data[[set_idx]]$matrix_colored[[length(self$data[[set_idx]]$matrix_colored) + 1]] <- list(row = nrow(plot_canvas) - i + 0, col = box_right)
801
+ }
802
+
803
+ # Draw outliers
804
+ outliers <- box_stats$out
805
+ if (length(outliers) > 0) {
806
+ outliers_norm <- round(normalize_data(outliers, y_min, y_max, nrow(plot_canvas)))
807
+ for (i in 1:length(outliers)) {
808
+ outlier_row <- nrow(plot_canvas) - outliers_norm[i] + 1
809
+ plot_canvas[outlier_row, x_center] <- "*"
810
+ self$data[[set_idx]]$matrix_colored[[length(self$data[[set_idx]]$matrix_colored) + 1]] <- list(row = outlier_row, col = x_center)
811
+ }
812
+ }
813
+
814
+ # Update the plot canvas
815
+ self$plot_canvas <- plot_canvas
816
+ },
817
+
818
+ #' Draw colors to the canvas
819
+ #'
820
+ #' @description In the draw_ functions we have been keeping track of the locations of the colored matrix elements.
821
+ #' These are now being colored.
822
+ draw_colors = function() {
823
+ plot_canvas <- self$plot_canvas
824
+ # get the colors in
825
+ for (set_idx in 1:length(self$data)) {
826
+ for (i in 1:length(self$data[[set_idx]]$matrix_colored)) {
827
+ this_row <- self$data[[set_idx]]$matrix_colored[[i]]$row
828
+ this_col <- self$data[[set_idx]]$matrix_colored[[i]]$col
829
+ plot_canvas[this_row, this_col] <- make_colored(plot_canvas[this_row, this_col], self$data[[set_idx]]$color)
830
+ }
831
+ }
832
+
833
+ self$plot_canvas <- plot_canvas
834
+ },
835
+
836
+ #' Draw the different plots types from all data elements to the canvas
837
+ #'
838
+ #' @description This function iterates through all data elements and calls the appropriate draw_ function
839
+ #' based on the plot type (scatter, line, boxplot, or barplot).
840
+ draw_plot = function() {
841
+ for (set_idx in 1:length(self$data)) {
842
+ if (self$data[[set_idx]]$type == "scatter") {
843
+ self$draw_scatter_plot(set_idx)
844
+ } else if (self$data[[set_idx]]$type == "line") {
845
+ self$draw_line_plot(set_idx)
846
+ } else if (self$data[[set_idx]]$type == "boxplot") {
847
+ self$draw_boxplot(set_idx)
848
+ } else if (self$data[[set_idx]]$type == "barplot") {
849
+
850
+ if(self$data[[set_idx]]$braille) {
851
+ self$draw_barplot_braille(set_idx)
852
+ } else {
853
+ self$draw_barplot(set_idx)
854
+ }
855
+ }
856
+ }
857
+ },
858
+
859
+ #' Make plot matrix: assembles all plot elements (canvas + borders + title + axes + legend)
860
+ #'
861
+ #' @description This function assembles all plot elements (canvas + borders + title + axes + legend)
862
+ #' and creates the final plot matrix.
863
+ make_plot_matrix = function() {
864
+ self$get_min_max()
865
+ self$remove_out_of_range_data()
866
+
867
+ # Calculate x-axis positions for boxplots
868
+ boxplot_count <- sum(sapply(self$data, function(dat) dat$type == "boxplot"))
869
+ if (boxplot_count > 0) {
870
+ boxplot_positions <- seq(from = 1, to = ncol(self$plot_canvas), length.out = boxplot_count + 1)
871
+ boxplot_positions <- round(boxplot_positions[-1] - diff(boxplot_positions) / 2)
872
+ boxplot_idx <- 1
873
+ for (set_idx in 1:length(self$data)) {
874
+ if (self$data[[set_idx]]$type == "boxplot") {
875
+ self$data[[set_idx]]$x_position <- boxplot_positions[boxplot_idx]
876
+ boxplot_idx <- boxplot_idx + 1
877
+ }
878
+ }
879
+ }
880
+
881
+ self$draw_plot()
882
+ self$draw_colors()
883
+
884
+ x_min <- self$x_min
885
+ x_max <- self$x_max
886
+ y_min <- self$y_min
887
+ y_max <- self$y_max
888
+
889
+ # Call the functions to add borders, title, x/y labels, and ticks, and legend
890
+ self$initialize_plot_matrix()
891
+ self$add_borders()
892
+ # NOTE: this function is for debugging: prints row and column index around the canvas
893
+ # self$add_row_col_index()
894
+ self$add_title()
895
+ self$add_y_ticks()
896
+ self$add_y_label()
897
+ self$add_x_ticks()
898
+ self$add_x_label()
899
+ if(self$draw_legend) self$add_legend()
900
+ self$add_col()
901
+ self$add_col()
902
+ self$add_row()
903
+ self$add_row(bottom = TRUE)
904
+ },
905
+
906
+ #' Export plot matrix
907
+ #'
908
+ #' @description This function exports the plot matrix.
909
+ #' @return The plot matrix.
910
+ export_plot_matrix = function() {
911
+ self$make_plot_matrix()
912
+ return(self$plot_matrix)
913
+ },
914
+
915
+ #' Main plotting function: assembles all plot elements (canvas + borders + title + axes + legend) and prints the plot
916
+ #' by 'cat'ing the plot matrix to the console.
917
+ #'
918
+ #' @description This function assembles all plot elements (canvas + borders + title + axes + legend) and
919
+ #' prints the final plot by 'cat'ing the plot matrix to the console.
920
+ print_plot = function() {
921
+ self$make_plot_matrix()
922
+ cat_plot_matrix(self$plot_matrix)
923
+ },
924
+
925
+ #' Merge two plotcli objects
926
+ #'
927
+ #' This method combines the data from two plotcli objects into a single plotcli object.
928
+ #' It takes the maximum of the plot_width and plot_height, combines the titles, and sets
929
+ #' the xlim and ylim to the minimum and maximum values of both objects.
930
+ #'
931
+ #' @param other A plotcli object to be merged with the current object.
932
+ #'
933
+ #' @return A new plotcli object containing the combined data from both objects.
934
+ #'
935
+ merge = function(other) {
936
+ if (!inherits(other, "plotcli")) {
937
+ stop("Can only merge with another plotcli object")
938
+ }
939
+
940
+ # Combine data from both objects
941
+ combined_data <- c(self$data, other$data)
942
+
943
+ # Create a new plotcli object with combined data
944
+ new_plotcli <- plotcli$new(
945
+ plot_width = max(self$plot_width, other$plot_width),
946
+ plot_height = max(self$plot_height, other$plot_height),
947
+ x_label = self$x_label,
948
+ y_label = self$y_label,
949
+ ylim = if (!is.null(self$ylim) && !is.null(other$ylim)) c(min(self$ylim[1], other$ylim[1]), max(self$ylim[2], other$ylim[2])) else NULL,
950
+ xlim = if (!is.null(self$xlim) && !is.null(other$xlim)) c(min(self$xlim[1], other$xlim[1]), max(self$xlim[2], other$xlim[2])) else NULL,
951
+ title = if (!is.null(self$title) && !is.null(other$title)) paste(self$title, other$title, sep = " & ") else NULL,
952
+ draw_legend = TRUE
953
+ )
954
+
955
+ # Add combined data to the new plotcli object
956
+ for (data in combined_data) {
957
+ new_plotcli$add_data(data)
958
+ }
959
+
960
+ return(new_plotcli)
961
+ }
962
+ )
963
+ )