#' provide correct justification for text labeling, depending on the chosen angle
#' @param angle integer value of the text angle, using the same rules as in ggplot2. Positive values for counterclockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Negative values for clockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc.
#' @param pos where text is? Either "top", "right", "bottom" or "left" of the elements to justify from.
#' @param kind: kind of text? Either "axis" or "text". In the first case, the pos argument refers to the axis position, and in the second to annotated text (using ggplot2::annotate() or ggplot2::geom_text()).
#' @returns a list containing: $angle: the submitted angle (value potentially reduced to fit the [-360 ; 360] interval, e.g., 460 -> 100, without impact on the final angle displayed); $pos: the selected position (argument pos); $kind: the selected kind of text (argument kind); $hjust: the horizontal justification; $vjust: the vertical justification.
#' - justification behave differently on plot, depending whether it is used for annotayed text or for axis labelling. Indeed the latter has labelling constrained.
#' - Of note, a bug in ggplot2: vjust sometimes does not work, i.e., the same justification result is obtained whatever the value used. This is the case with angle = 90, pos = "top", kind = "axis". While everything is fine with angle = 90, pos = "bottom", kind = "axis". At least, everything seems fine for kind = "axis" and pos = c("left", "bottom").
#' @export
ggjust<-function(
angle,
pos,
kind="axis"){
# DEBUGGING
# angle = 45 ; pos = "left" ; kind = "axis"
# package name
package.name<-"ggcute"
# end package name
# function name
function.name<-base::paste0(base::as.list(base::match.call(expand.dots=FALSE))[[1]],"()")# function name with "()" paste, which split into a vector of three: c("::()", "package()", "function()") if "package::function()" is used.
if(function.name[1]=="::()"){
function.name<-function.name[3]
}
arg.names<-base::names(base::formals(fun=base::sys.function(base::sys.parent(n=2))))# names of all the arguments
arg.user.setting<-base::as.list(base::match.call(expand.dots=FALSE))[-1]# list of the argument settings (excluding default values not provided by the user)
if(base::any(tempo)){# normally no NA for missing() output
tempo.cat<-base::paste0("ERROR IN ",function.name," OF THE ",package.name," PACKAGE\nFOLLOWING ARGUMENT",base::ifelse(base::sum(tempo,na.rm=TRUE)>1,"S HAVE","HAS")," NO DEFAULT VALUE AND REQUIRE ONE:\n",base::paste0(mandat.args,collapse="\n"))
base::stop(base::paste0("\n\n================\n\n",tempo.cat,"\n\n================\n\n"),call.=FALSE)# == in stop() to be able to add several messages between ==
}
# end arg with no default values
# argument checking with arg_check()
# argument checking
argum.check<-NULL#
text.check<-NULL#
checked.arg.names<-NULL# for function debbuging: used by r_debugging_tools
# source("C:/Users/yhan/Documents/Git_projects/debugging_tools_for_r_dev/r_debugging_tools.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using arg_check()
tempo.arg<-base::names(arg.user.setting)# values provided by the user
tempo.log<-base::suppressWarnings(base::sapply(base::lapply(base::lapply(tempo.arg,FUN=base::get,envir=base::sys.nframe(),inherits=FALSE),FUN=base::is.na),FUN=base::any))&base::lapply(base::lapply(tempo.arg,FUN=base::get,envir=base::sys.nframe(),inherits=FALSE),FUN=base::length)==1L# no argument provided by the user can be just NA
if(base::any(tempo.log)==TRUE){# normally no NA because is.na() used here
tempo.cat<-base::paste0("ERROR IN ",function.name," OF THE ",package.name," PACKAGE\n",base::ifelse(base::sum(tempo.log,na.rm=TRUE)>1,"THESE ARGUMENTS","THIS ARGUMENT")," CANNOT JUST BE NA:",base::paste0(tempo.arg[tempo.log],collapse="\n"))
base::stop(base::paste0("\n\n================\n\n",tempo.cat,"\n\n================\n\n"),call.=FALSE)# == in stop() to be able to add several messages between ==
if(base::any(tempo.log)==TRUE){# normally no NA with is.null()
tempo.cat<-base::paste0("ERROR IN ",function.name," OF THE ",package.name," PACKAGE:\n",base::ifelse(base::sum(tempo.log,na.rm=TRUE)>1,"THESE ARGUMENTS\n","THIS ARGUMENT\n"),base::paste0(tempo.arg[tempo.log],collapse="\n"),"\nCANNOT BE NULL")
base::stop(base::paste0("\n\n================\n\n",tempo.cat,"\n\n================\n\n"),call.=FALSE)# == in stop() to be able to add several messages between ==
}
# end management of NULL arguments
# code that protects set.seed() in the global environment
# end code that protects set.seed() in the global environment
# warning initiation
# end warning initiation
# end second round of checking and data preparation
# main code
# to get angle between -360 and 360
while(angle>360){
angle<-angle-360
}
while(angle<-360){
angle<-angle+360
}
# end to get angle between -360 and 360
# justifications
if(pos%in%c("bottom","top")){
# code below is for if(pos == "bottom"){
if(any(sapply(FUN=all.equal,c(-360,-180,0,180,360),angle)==TRUE)){# equivalent of angle == -360 | angle == -180 | angle == 0 | angle == 180 | angle == 360 but deals with floats