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.
- CLAUDE.md +51 -0
- LICENSE +21 -0
- PKG-INFO +358 -0
- README.md +340 -0
- main.py +6 -0
- plotcli-original/.Rbuildignore +18 -0
- plotcli-original/.github/workflows/deploy_docs.yml +43 -0
- plotcli-original/.gitignore +46 -0
- plotcli-original/DESCRIPTION +25 -0
- plotcli-original/NAMESPACE +60 -0
- plotcli-original/NEWS.md +112 -0
- plotcli-original/R/ascii_escape.r +13 -0
- plotcli-original/R/canvas.r +586 -0
- plotcli-original/R/class_functions.r +114 -0
- plotcli-original/R/geom_registry.r +1376 -0
- plotcli-original/R/ggplotcli.r +234 -0
- plotcli-original/R/ggplotcli_helpers.r +1099 -0
- plotcli-original/R/helper_functions.r +351 -0
- plotcli-original/R/plotcli.r +963 -0
- plotcli-original/R/plotcli_grid.r +1 -0
- plotcli-original/R/plotcli_wrappers.r +416 -0
- plotcli-original/R/zzz.r +15 -0
- plotcli-original/README.md +192 -0
- plotcli-original/docs/ascii.png +0 -0
- plotcli-original/docs/bar.png +0 -0
- plotcli-original/docs/block.png +0 -0
- plotcli-original/docs/boxplot.png +0 -0
- plotcli-original/docs/density.png +0 -0
- plotcli-original/docs/facet.png +0 -0
- plotcli-original/docs/facet_grid.png +0 -0
- plotcli-original/docs/generate_png.sh +137 -0
- plotcli-original/docs/heatmap.png +0 -0
- plotcli-original/docs/histogram.png +0 -0
- plotcli-original/docs/line.png +0 -0
- plotcli-original/docs/noborder.png +0 -0
- plotcli-original/docs/scatter.png +0 -0
- plotcli-original/docs/showcase.R +182 -0
- plotcli-original/inst/doc/ggplotcli.R +231 -0
- plotcli-original/inst/doc/ggplotcli.Rmd +329 -0
- plotcli-original/inst/doc/ggplotcli.html +1078 -0
- plotcli-original/inst/doc/plotcli_class.R +98 -0
- plotcli-original/inst/doc/plotcli_class.Rmd +121 -0
- plotcli-original/inst/doc/plotcli_class.html +564 -0
- plotcli-original/inst/doc/plotcli_wrappers.R +35 -0
- plotcli-original/inst/doc/plotcli_wrappers.Rmd +62 -0
- plotcli-original/inst/doc/plotcli_wrappers.html +546 -0
- plotcli-original/man/AsciiCanvas.Rd +116 -0
- plotcli-original/man/BlockCanvas.Rd +132 -0
- plotcli-original/man/BrailleCanvas.Rd +146 -0
- plotcli-original/man/Canvas.Rd +492 -0
- plotcli-original/man/GeomRegistry.Rd +9 -0
- plotcli-original/man/add_legend_to_output.Rd +12 -0
- plotcli-original/man/braille_dot_bit.Rd +29 -0
- plotcli-original/man/braille_set_dot.Rd +21 -0
- plotcli-original/man/bresenham.Rd +27 -0
- plotcli-original/man/build_plot_output.Rd +28 -0
- plotcli-original/man/build_plot_output_v2.Rd +41 -0
- plotcli-original/man/cat_plot_matrix.Rd +17 -0
- plotcli-original/man/cbind.plotcli.Rd +19 -0
- plotcli-original/man/cbind_plots.Rd +17 -0
- plotcli-original/man/color_to_term.Rd +18 -0
- plotcli-original/man/create_canvas.Rd +21 -0
- plotcli-original/man/create_panel_scales.Rd +29 -0
- plotcli-original/man/create_scales.Rd +27 -0
- plotcli-original/man/dot-geom_registry.Rd +16 -0
- plotcli-original/man/draw_border.Rd +12 -0
- plotcli-original/man/draw_grid.Rd +12 -0
- plotcli-original/man/extract_legend_info.Rd +12 -0
- plotcli-original/man/extract_plot_labels.Rd +12 -0
- plotcli-original/man/extract_plot_style.Rd +12 -0
- plotcli-original/man/format_axis_label.Rd +18 -0
- plotcli-original/man/format_four_chars.Rd +21 -0
- plotcli-original/man/geom_area_handler.Rd +12 -0
- plotcli-original/man/geom_bar_handler.Rd +12 -0
- plotcli-original/man/geom_boxplot_handler.Rd +13 -0
- plotcli-original/man/geom_density_handler.Rd +12 -0
- plotcli-original/man/geom_histogram_handler.Rd +12 -0
- plotcli-original/man/geom_hline_handler.Rd +12 -0
- plotcli-original/man/geom_line_handler.Rd +12 -0
- plotcli-original/man/geom_path_handler.Rd +12 -0
- plotcli-original/man/geom_point_handler.Rd +12 -0
- plotcli-original/man/geom_rect_handler.Rd +12 -0
- plotcli-original/man/geom_segment_handler.Rd +12 -0
- plotcli-original/man/geom_smooth_handler.Rd +12 -0
- plotcli-original/man/geom_text_handler.Rd +12 -0
- plotcli-original/man/geom_vline_handler.Rd +12 -0
- plotcli-original/man/get_color_hue.Rd +18 -0
- plotcli-original/man/get_data_subset.Rd +23 -0
- plotcli-original/man/get_facet_info.Rd +18 -0
- plotcli-original/man/get_geom_handler.Rd +17 -0
- plotcli-original/man/get_term_colors.Rd +21 -0
- plotcli-original/man/ggplotcli.Rd +83 -0
- plotcli-original/man/init_color_mapping.Rd +15 -0
- plotcli-original/man/is_braille.Rd +20 -0
- plotcli-original/man/is_geom_registered.Rd +17 -0
- plotcli-original/man/list_registered_geoms.Rd +14 -0
- plotcli-original/man/make_colored.Rd +23 -0
- plotcli-original/man/make_unique_names.Rd +20 -0
- plotcli-original/man/normalize_data.Rd +27 -0
- plotcli-original/man/pclib.Rd +48 -0
- plotcli-original/man/pclibx.Rd +46 -0
- plotcli-original/man/pclid.Rd +44 -0
- plotcli-original/man/pclih.Rd +50 -0
- plotcli-original/man/pclil.Rd +48 -0
- plotcli-original/man/pclis.Rd +48 -0
- plotcli-original/man/pixel_to_braille.Rd +23 -0
- plotcli-original/man/plotcli.Rd +598 -0
- plotcli-original/man/plotcli_bar.Rd +48 -0
- plotcli-original/man/plotcli_box.Rd +46 -0
- plotcli-original/man/plotcli_density.Rd +44 -0
- plotcli-original/man/plotcli_histogram.Rd +50 -0
- plotcli-original/man/plotcli_line.Rd +48 -0
- plotcli-original/man/plotcli_options.Rd +18 -0
- plotcli-original/man/plotcli_scatter.Rd +48 -0
- plotcli-original/man/plus-.plotcli.Rd +19 -0
- plotcli-original/man/rbind.plotcli.Rd +19 -0
- plotcli-original/man/rbind_plots.Rd +17 -0
- plotcli-original/man/register_geom.Rd +21 -0
- plotcli-original/man/remove_color_codes.Rd +21 -0
- plotcli-original/man/render_faceted_plot.Rd +25 -0
- plotcli-original/man/render_single_panel.Rd +12 -0
- plotcli-original/man/safe_aes_name.Rd +18 -0
- plotcli-original/tests/testthat/test-new-geoms.R +136 -0
- plotcli-original/tests/testthat/test-plotcli.R +69 -0
- plotcli-original/tests/testthat.R +4 -0
- plotcli-original/vignettes/ggplotcli.Rmd +329 -0
- plotcli-original/vignettes/plotcli_class.R +98 -0
- plotcli-original/vignettes/plotcli_class.Rmd +121 -0
- plotcli-original/vignettes/plotcli_wrappers.R +35 -0
- plotcli-original/vignettes/plotcli_wrappers.Rmd +62 -0
- plotcli.egg-info/PKG-INFO +11 -0
- plotcli.egg-info/SOURCES.txt +7 -0
- plotcli.egg-info/dependency_links.txt +1 -0
- plotcli.egg-info/entry_points.txt +3 -0
- plotcli.egg-info/top_level.txt +1 -0
- plotcli.py +978 -0
- plotcli_py-0.1.0.dist-info/METADATA +358 -0
- plotcli_py-0.1.0.dist-info/RECORD +143 -0
- plotcli_py-0.1.0.dist-info/WHEEL +4 -0
- plotcli_py-0.1.0.dist-info/entry_points.txt +2 -0
- plotcli_py-0.1.0.dist-info/licenses/LICENSE +21 -0
- pyproject.toml +31 -0
- 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
|
+
)
|