Spaces:
Running
Running
Update app.R
Browse files
app.R
CHANGED
|
@@ -2941,6 +2941,37 @@ create_pitcher_pdf <- function(game_df, pitcher_name, output_file, pitch_colors)
|
|
| 2941 |
invisible(output_file)
|
| 2942 |
}
|
| 2943 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2944 |
# =====================================================================
|
| 2945 |
# ====================== UMPIRE CODE ================================
|
| 2946 |
# =====================================================================
|
|
@@ -3013,6 +3044,30 @@ umpire_create_report_pdf <- function(data,
|
|
| 3013 |
y = c( 0.150, 0.150, 0.300, 0.500, 0.300)
|
| 3014 |
)
|
| 3015 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3016 |
base_zone <- function() {
|
| 3017 |
ggplot() +
|
| 3018 |
geom_rect(data = buffer_zone_rect,
|
|
@@ -3039,85 +3094,82 @@ umpire_create_report_pdf <- function(data,
|
|
| 3039 |
)
|
| 3040 |
}
|
| 3041 |
|
| 3042 |
-
|
| 3043 |
-
|
|
|
|
|
|
|
|
|
|
| 3044 |
filter(BatterSide == side_label,
|
| 3045 |
PitchCall %in% c("BallCalled", "BallinDirt"),
|
| 3046 |
PlateLocSide > -0.83083, PlateLocSide < 0.83083,
|
| 3047 |
PlateLocHeight < 3.37750, PlateLocHeight > 1.5)
|
| 3048 |
base_zone() +
|
| 3049 |
geom_point(data = pts, aes(x = PlateLocSide, y = PlateLocHeight),
|
| 3050 |
-
pch = 21, fill = "#006F71", color = "black", size =
|
|
|
|
|
|
|
| 3051 |
ggtitle(title_label)
|
| 3052 |
}
|
| 3053 |
|
| 3054 |
-
umpire_create_strike_plot <- function(
|
| 3055 |
-
pts <-
|
| 3056 |
filter(BatterSide == side_label, PitchCall == "StrikeCalled") %>%
|
| 3057 |
filter(PlateLocSide < -0.83083 | PlateLocSide > 0.83083 |
|
| 3058 |
PlateLocHeight > 3.37750 | PlateLocHeight < 1.5)
|
| 3059 |
base_zone() +
|
| 3060 |
geom_point(data = pts, aes(x = PlateLocSide, y = PlateLocHeight),
|
| 3061 |
-
pch = 21, fill = "#006F71", color = "black", size =
|
|
|
|
|
|
|
| 3062 |
ggtitle(title_label)
|
| 3063 |
}
|
| 3064 |
|
| 3065 |
-
umpire_create_ball_plot_team <- function(
|
| 3066 |
-
pts <-
|
| 3067 |
filter(if (is_ccu) BatterTeam == "COA_CHA" else BatterTeam != "COA_CHA",
|
| 3068 |
PitchCall %in% c("BallCalled", "BallinDirt"),
|
| 3069 |
PlateLocSide > -0.83083, PlateLocSide < 0.83083,
|
| 3070 |
PlateLocHeight < 3.37750, PlateLocHeight > 1.5)
|
| 3071 |
base_zone() +
|
| 3072 |
geom_point(data = pts, aes(x = PlateLocSide, y = PlateLocHeight),
|
| 3073 |
-
pch = 21, fill = "#006F71", color = "black", size =
|
|
|
|
|
|
|
| 3074 |
ggtitle(title_label)
|
| 3075 |
}
|
| 3076 |
|
| 3077 |
-
umpire_create_strike_plot_team <- function(
|
| 3078 |
-
pts <-
|
| 3079 |
filter(if (is_ccu) BatterTeam == "COA_CHA" else BatterTeam != "COA_CHA",
|
| 3080 |
PitchCall == "StrikeCalled") %>%
|
| 3081 |
filter(PlateLocSide < -0.83083 | PlateLocSide > 0.83083 |
|
| 3082 |
PlateLocHeight > 3.37750 | PlateLocHeight < 1.5)
|
| 3083 |
base_zone() +
|
| 3084 |
geom_point(data = pts, aes(x = PlateLocSide, y = PlateLocHeight),
|
| 3085 |
-
pch = 21, fill = "#006F71", color = "black", size =
|
|
|
|
|
|
|
| 3086 |
ggtitle(title_label)
|
| 3087 |
}
|
| 3088 |
|
| 3089 |
-
|
| 3090 |
-
|
| 3091 |
-
|
| 3092 |
-
|
| 3093 |
-
|
| 3094 |
-
|
| 3095 |
-
|
| 3096 |
-
|
| 3097 |
-
|
| 3098 |
-
|
| 3099 |
-
|
| 3100 |
-
|
| 3101 |
-
|
| 3102 |
-
|
| 3103 |
-
|
| 3104 |
-
|
| 3105 |
-
|
| 3106 |
-
|
| 3107 |
-
PlateLocHeight < 3.37750, PlateLocHeight > 1.5),
|
| 3108 |
-
data %>% filter(BatterSide == "Left", PitchCall == "StrikeCalled") %>%
|
| 3109 |
-
filter(PlateLocSide < -0.83083 | PlateLocSide > 0.83083 |
|
| 3110 |
-
PlateLocHeight > 3.37750 | PlateLocHeight < 1.5),
|
| 3111 |
-
data %>% filter(BatterSide == "Right", PitchCall == "StrikeCalled") %>%
|
| 3112 |
-
filter(PlateLocSide < -0.83083 | PlateLocSide > 0.83083 |
|
| 3113 |
-
PlateLocHeight > 3.37750 | PlateLocHeight < 1.5)
|
| 3114 |
-
) %>%
|
| 3115 |
-
mutate(
|
| 3116 |
-
Side = paste0(sprintf("%.0f", abs(PlateLocSide * 12)), '"'),
|
| 3117 |
-
Height = paste0(sprintf("%.0f", PlateLocHeight * 12), '"')
|
| 3118 |
-
) %>%
|
| 3119 |
-
arrange(PitchNo) %>%
|
| 3120 |
-
select(dplyr::any_of(c("PitchNo","Inning","Top/Bottom","TopBottom",
|
| 3121 |
"Batter","PitchCall","Side","Height","BatterTeam")))
|
| 3122 |
|
| 3123 |
green <- "#006F71"
|
|
@@ -3150,67 +3202,67 @@ umpire_create_report_pdf <- function(data,
|
|
| 3150 |
check.names = FALSE
|
| 3151 |
)
|
| 3152 |
|
| 3153 |
-
draw_header <- function() {
|
| 3154 |
-
|
| 3155 |
-
|
| 3156 |
-
|
| 3157 |
-
|
| 3158 |
-
|
| 3159 |
-
|
| 3160 |
-
|
| 3161 |
-
|
| 3162 |
-
|
| 3163 |
-
|
| 3164 |
-
|
| 3165 |
-
|
| 3166 |
-
|
| 3167 |
-
|
| 3168 |
-
|
| 3169 |
-
|
| 3170 |
-
|
| 3171 |
-
|
| 3172 |
-
|
| 3173 |
-
|
| 3174 |
-
|
| 3175 |
-
|
| 3176 |
-
|
| 3177 |
-
|
| 3178 |
-
|
| 3179 |
-
|
|
|
|
| 3180 |
)
|
| 3181 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3182 |
)
|
| 3183 |
-
|
| 3184 |
-
|
| 3185 |
-
## --- EMBEDDED LOGO LINKS ---
|
| 3186 |
-
left_logo_url <- "https://i.imgur.com/zjTu3JS.png"
|
| 3187 |
-
right_logo_url <- "https://i.ibb.co/Q3kFXXd9/8acd1b8a-7920-403a-8e9d-86742634effb.png"
|
| 3188 |
-
|
| 3189 |
-
draw_logo_url(left_logo_url, 0.05, "left")
|
| 3190 |
-
draw_logo_url(right_logo_url, 0.95, "right")
|
| 3191 |
-
|
| 3192 |
-
## --- HEADER TEXT ---
|
| 3193 |
-
grid.text(
|
| 3194 |
-
title_text,
|
| 3195 |
-
y = 0.975,
|
| 3196 |
-
gp = gpar(fontsize = 16, fontface = "bold")
|
| 3197 |
-
)
|
| 3198 |
-
|
| 3199 |
-
grid.text(
|
| 3200 |
-
subhead_text,
|
| 3201 |
-
y = 0.948,
|
| 3202 |
-
gp = gpar(fontsize = 12, fontface = "bold")
|
| 3203 |
-
)
|
| 3204 |
-
|
| 3205 |
-
if (!is.na(game_date)) {
|
| 3206 |
grid.text(
|
| 3207 |
-
|
| 3208 |
-
y = 0.
|
| 3209 |
-
gp = gpar(fontsize =
|
| 3210 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3211 |
}
|
| 3212 |
-
|
| 3213 |
-
|
| 3214 |
grDevices::pdf(output_file, width = 8.5, height = 11)
|
| 3215 |
|
| 3216 |
grid.newpage()
|
|
@@ -3231,8 +3283,8 @@ draw_header <- function() {
|
|
| 3231 |
popViewport()
|
| 3232 |
|
| 3233 |
grid.newpage()
|
| 3234 |
-
|
| 3235 |
-
|
| 3236 |
x = 0.5, y = 0.02, gp = grid::gpar(cex = 0.75, col = "grey50"))
|
| 3237 |
|
| 3238 |
pushViewport(viewport(x = 0.28, y = 0.81, width = 0.47, height = 0.27)); print(plot_ball_ccu, newpage = FALSE); popViewport()
|
|
@@ -3240,25 +3292,25 @@ draw_header <- function() {
|
|
| 3240 |
pushViewport(viewport(x = 0.28, y = 0.60, width = 0.47, height = 0.27)); print(plot_strike_ccu, newpage = FALSE); popViewport()
|
| 3241 |
pushViewport(viewport(x = 0.72, y = 0.60, width = 0.47, height = 0.27)); print(plot_strike_opp, newpage = FALSE); popViewport()
|
| 3242 |
|
| 3243 |
-
if (nrow(
|
| 3244 |
-
# First page can fit
|
| 3245 |
first_page_rows <- 15
|
| 3246 |
remaining_page_rows <- rows_per_page # 30 rows per full page
|
| 3247 |
|
| 3248 |
-
if (nrow(
|
| 3249 |
# Fits on current page
|
| 3250 |
pushViewport(viewport(x = 0.5, y = 0.30, width = 0.92, height = 0.50))
|
| 3251 |
-
grid.table(
|
| 3252 |
popViewport()
|
| 3253 |
} else {
|
| 3254 |
# First batch on current page
|
| 3255 |
-
first_chunk <-
|
| 3256 |
pushViewport(viewport(x = 0.5, y = 0.30, width = 0.92, height = 0.50))
|
| 3257 |
grid.table(first_chunk, rows = NULL, theme = ttheme_green_small)
|
| 3258 |
popViewport()
|
| 3259 |
|
| 3260 |
# Remaining rows paginated onto new pages
|
| 3261 |
-
remaining <-
|
| 3262 |
remaining_chunks <- split(remaining, ceiling(seq_len(nrow(remaining)) / remaining_page_rows))
|
| 3263 |
|
| 3264 |
for (i in seq_along(remaining_chunks)) {
|
|
@@ -3276,6 +3328,7 @@ draw_header <- function() {
|
|
| 3276 |
invisible(output_file)
|
| 3277 |
}
|
| 3278 |
|
|
|
|
| 3279 |
# Advanced Pitcher Functions
|
| 3280 |
`%||%` <- function(a, b) if (!is.null(a)) a else b
|
| 3281 |
|
|
|
|
| 2941 |
invisible(output_file)
|
| 2942 |
}
|
| 2943 |
|
| 2944 |
+
|
| 2945 |
+
## --- EMBEDDED LOGO LINKS ---
|
| 2946 |
+
left_logo_url <- "https://i.imgur.com/zjTu3JS.png"
|
| 2947 |
+
right_logo_url <- "https://i.ibb.co/Q3kFXXd9/8acd1b8a-7920-403a-8e9d-86742634effb.png"
|
| 2948 |
+
|
| 2949 |
+
draw_logo_url(left_logo_url, 0.05, "left")
|
| 2950 |
+
draw_logo_url(right_logo_url, 0.95, "right")
|
| 2951 |
+
|
| 2952 |
+
## --- HEADER TEXT ---
|
| 2953 |
+
grid.text(
|
| 2954 |
+
title_text,
|
| 2955 |
+
y = 0.975,
|
| 2956 |
+
gp = gpar(fontsize = 16, fontface = "bold")
|
| 2957 |
+
)
|
| 2958 |
+
|
| 2959 |
+
grid.text(
|
| 2960 |
+
subhead_text,
|
| 2961 |
+
y = 0.948,
|
| 2962 |
+
gp = gpar(fontsize = 12, fontface = "bold")
|
| 2963 |
+
)
|
| 2964 |
+
|
| 2965 |
+
if (!is.na(game_date)) {
|
| 2966 |
+
grid.text(
|
| 2967 |
+
game_date,
|
| 2968 |
+
y = 0.925,
|
| 2969 |
+
gp = gpar(fontsize = 9)
|
| 2970 |
+
)
|
| 2971 |
+
}
|
| 2972 |
+
}
|
| 2973 |
+
|
| 2974 |
+
|
| 2975 |
# =====================================================================
|
| 2976 |
# ====================== UMPIRE CODE ================================
|
| 2977 |
# =====================================================================
|
|
|
|
| 3044 |
y = c( 0.150, 0.150, 0.300, 0.500, 0.300)
|
| 3045 |
)
|
| 3046 |
|
| 3047 |
+
# ---------------------------------------------------------------
|
| 3048 |
+
# BUILD MISSED CALLS TABLE EARLY (before plots) so we can number them
|
| 3049 |
+
# ---------------------------------------------------------------
|
| 3050 |
+
MissedCalls <- dplyr::bind_rows(
|
| 3051 |
+
data %>% filter(PitchCall %in% c("BallCalled", "BallinDirt"),
|
| 3052 |
+
PlateLocSide > -0.83083, PlateLocSide < 0.83083,
|
| 3053 |
+
PlateLocHeight < 3.37750, PlateLocHeight > 1.5),
|
| 3054 |
+
data %>% filter(PitchCall == "StrikeCalled") %>%
|
| 3055 |
+
filter(PlateLocSide < -0.83083 | PlateLocSide > 0.83083 |
|
| 3056 |
+
PlateLocHeight > 3.37750 | PlateLocHeight < 1.5)
|
| 3057 |
+
) %>%
|
| 3058 |
+
arrange(PitchNo) %>%
|
| 3059 |
+
mutate(CallNo = row_number()) %>%
|
| 3060 |
+
mutate(
|
| 3061 |
+
Side = paste0(sprintf("%.0f", abs(PlateLocSide * 12)), '"'),
|
| 3062 |
+
Height = paste0(sprintf("%.0f", PlateLocHeight * 12), '"')
|
| 3063 |
+
)
|
| 3064 |
+
|
| 3065 |
+
# Keep PlateLocSide/PlateLocHeight/BatterSide/BatterTeam available for plot filtering
|
| 3066 |
+
# but select display columns for the table at the end
|
| 3067 |
+
|
| 3068 |
+
# ---------------------------------------------------------------
|
| 3069 |
+
# BASE ZONE HELPER
|
| 3070 |
+
# ---------------------------------------------------------------
|
| 3071 |
base_zone <- function() {
|
| 3072 |
ggplot() +
|
| 3073 |
geom_rect(data = buffer_zone_rect,
|
|
|
|
| 3094 |
)
|
| 3095 |
}
|
| 3096 |
|
| 3097 |
+
# ---------------------------------------------------------------
|
| 3098 |
+
# PLOT HELPERS — now use MissedCalls with CallNo labels
|
| 3099 |
+
# ---------------------------------------------------------------
|
| 3100 |
+
umpire_create_ball_plot <- function(mc, side_label, title_label) {
|
| 3101 |
+
pts <- mc %>%
|
| 3102 |
filter(BatterSide == side_label,
|
| 3103 |
PitchCall %in% c("BallCalled", "BallinDirt"),
|
| 3104 |
PlateLocSide > -0.83083, PlateLocSide < 0.83083,
|
| 3105 |
PlateLocHeight < 3.37750, PlateLocHeight > 1.5)
|
| 3106 |
base_zone() +
|
| 3107 |
geom_point(data = pts, aes(x = PlateLocSide, y = PlateLocHeight),
|
| 3108 |
+
pch = 21, fill = "#006F71", color = "black", size = 5) +
|
| 3109 |
+
geom_text(data = pts, aes(x = PlateLocSide, y = PlateLocHeight, label = CallNo),
|
| 3110 |
+
color = "white", size = 2.2, fontface = "bold") +
|
| 3111 |
ggtitle(title_label)
|
| 3112 |
}
|
| 3113 |
|
| 3114 |
+
umpire_create_strike_plot <- function(mc, side_label, title_label) {
|
| 3115 |
+
pts <- mc %>%
|
| 3116 |
filter(BatterSide == side_label, PitchCall == "StrikeCalled") %>%
|
| 3117 |
filter(PlateLocSide < -0.83083 | PlateLocSide > 0.83083 |
|
| 3118 |
PlateLocHeight > 3.37750 | PlateLocHeight < 1.5)
|
| 3119 |
base_zone() +
|
| 3120 |
geom_point(data = pts, aes(x = PlateLocSide, y = PlateLocHeight),
|
| 3121 |
+
pch = 21, fill = "#006F71", color = "black", size = 5) +
|
| 3122 |
+
geom_text(data = pts, aes(x = PlateLocSide, y = PlateLocHeight, label = CallNo),
|
| 3123 |
+
color = "white", size = 2.2, fontface = "bold") +
|
| 3124 |
ggtitle(title_label)
|
| 3125 |
}
|
| 3126 |
|
| 3127 |
+
umpire_create_ball_plot_team <- function(mc, is_ccu, title_label) {
|
| 3128 |
+
pts <- mc %>%
|
| 3129 |
filter(if (is_ccu) BatterTeam == "COA_CHA" else BatterTeam != "COA_CHA",
|
| 3130 |
PitchCall %in% c("BallCalled", "BallinDirt"),
|
| 3131 |
PlateLocSide > -0.83083, PlateLocSide < 0.83083,
|
| 3132 |
PlateLocHeight < 3.37750, PlateLocHeight > 1.5)
|
| 3133 |
base_zone() +
|
| 3134 |
geom_point(data = pts, aes(x = PlateLocSide, y = PlateLocHeight),
|
| 3135 |
+
pch = 21, fill = "#006F71", color = "black", size = 5) +
|
| 3136 |
+
geom_text(data = pts, aes(x = PlateLocSide, y = PlateLocHeight, label = CallNo),
|
| 3137 |
+
color = "white", size = 2.2, fontface = "bold") +
|
| 3138 |
ggtitle(title_label)
|
| 3139 |
}
|
| 3140 |
|
| 3141 |
+
umpire_create_strike_plot_team <- function(mc, is_ccu, title_label) {
|
| 3142 |
+
pts <- mc %>%
|
| 3143 |
filter(if (is_ccu) BatterTeam == "COA_CHA" else BatterTeam != "COA_CHA",
|
| 3144 |
PitchCall == "StrikeCalled") %>%
|
| 3145 |
filter(PlateLocSide < -0.83083 | PlateLocSide > 0.83083 |
|
| 3146 |
PlateLocHeight > 3.37750 | PlateLocHeight < 1.5)
|
| 3147 |
base_zone() +
|
| 3148 |
geom_point(data = pts, aes(x = PlateLocSide, y = PlateLocHeight),
|
| 3149 |
+
pch = 21, fill = "#006F71", color = "black", size = 5) +
|
| 3150 |
+
geom_text(data = pts, aes(x = PlateLocSide, y = PlateLocHeight, label = CallNo),
|
| 3151 |
+
color = "white", size = 2.2, fontface = "bold") +
|
| 3152 |
ggtitle(title_label)
|
| 3153 |
}
|
| 3154 |
|
| 3155 |
+
# ---------------------------------------------------------------
|
| 3156 |
+
# CREATE ALL PLOTS (pass MissedCalls instead of data)
|
| 3157 |
+
# ---------------------------------------------------------------
|
| 3158 |
+
plot_ball_lhb <- umpire_create_ball_plot(MissedCalls, "Left", "Ball Called v LHB")
|
| 3159 |
+
plot_ball_rhb <- umpire_create_ball_plot(MissedCalls, "Right", "Ball Called v RHB")
|
| 3160 |
+
plot_strike_lhb <- umpire_create_strike_plot(MissedCalls, "Left", "Strike Called v LHB")
|
| 3161 |
+
plot_strike_rhb <- umpire_create_strike_plot(MissedCalls, "Right", "Strike Called v RHB")
|
| 3162 |
+
|
| 3163 |
+
plot_ball_ccu <- umpire_create_ball_plot_team(MissedCalls, TRUE, "Ball Called v CCU Hitters")
|
| 3164 |
+
plot_ball_opp <- umpire_create_ball_plot_team(MissedCalls, FALSE, "Ball Called v Opp Hitters")
|
| 3165 |
+
plot_strike_ccu <- umpire_create_strike_plot_team(MissedCalls, TRUE, "Strike Called v CCU Hitters")
|
| 3166 |
+
plot_strike_opp <- umpire_create_strike_plot_team(MissedCalls, FALSE, "Strike Called v Opp Hitters")
|
| 3167 |
+
|
| 3168 |
+
# ---------------------------------------------------------------
|
| 3169 |
+
# PREPARE DISPLAY TABLE (select only display columns, CallNo first)
|
| 3170 |
+
# ---------------------------------------------------------------
|
| 3171 |
+
MissedCallsDisplay <- MissedCalls %>%
|
| 3172 |
+
select(dplyr::any_of(c("CallNo","PitchNo","Inning","Top/Bottom","TopBottom",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3173 |
"Batter","PitchCall","Side","Height","BatterTeam")))
|
| 3174 |
|
| 3175 |
green <- "#006F71"
|
|
|
|
| 3202 |
check.names = FALSE
|
| 3203 |
)
|
| 3204 |
|
| 3205 |
+
draw_header <- function() {
|
| 3206 |
+
|
| 3207 |
+
suppressPackageStartupMessages({
|
| 3208 |
+
library(grid)
|
| 3209 |
+
library(magick)
|
| 3210 |
+
})
|
| 3211 |
+
|
| 3212 |
+
draw_logo_url <- function(url, x, just) {
|
| 3213 |
+
img <- try(
|
| 3214 |
+
magick::image_read(url),
|
| 3215 |
+
silent = TRUE
|
| 3216 |
+
)
|
| 3217 |
+
|
| 3218 |
+
if (inherits(img, "try-error")) return(NULL)
|
| 3219 |
+
|
| 3220 |
+
img <- magick::image_resize(img, "x130")
|
| 3221 |
+
|
| 3222 |
+
grid.draw(
|
| 3223 |
+
grid::rasterGrob(
|
| 3224 |
+
as.raster(img),
|
| 3225 |
+
interpolate = TRUE,
|
| 3226 |
+
vp = viewport(
|
| 3227 |
+
x = x,
|
| 3228 |
+
y = 0.96,
|
| 3229 |
+
width = 0.13,
|
| 3230 |
+
height = 0.08,
|
| 3231 |
+
just = c(just, "center")
|
| 3232 |
+
)
|
| 3233 |
)
|
| 3234 |
)
|
| 3235 |
+
}
|
| 3236 |
+
|
| 3237 |
+
## --- EMBEDDED LOGO LINKS ---
|
| 3238 |
+
left_logo_url <- "https://i.imgur.com/zjTu3JS.png"
|
| 3239 |
+
right_logo_url <- "https://i.ibb.co/Q3kFXXd9/8acd1b8a-7920-403a-8e9d-86742634effb.png"
|
| 3240 |
+
|
| 3241 |
+
draw_logo_url(left_logo_url, 0.05, "left")
|
| 3242 |
+
draw_logo_url(right_logo_url, 0.95, "right")
|
| 3243 |
+
|
| 3244 |
+
## --- HEADER TEXT ---
|
| 3245 |
+
grid.text(
|
| 3246 |
+
title_text,
|
| 3247 |
+
y = 0.975,
|
| 3248 |
+
gp = gpar(fontsize = 16, fontface = "bold")
|
| 3249 |
)
|
| 3250 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3251 |
grid.text(
|
| 3252 |
+
subhead_text,
|
| 3253 |
+
y = 0.948,
|
| 3254 |
+
gp = gpar(fontsize = 12, fontface = "bold")
|
| 3255 |
)
|
| 3256 |
+
|
| 3257 |
+
if (!is.na(game_date)) {
|
| 3258 |
+
grid.text(
|
| 3259 |
+
game_date,
|
| 3260 |
+
y = 0.925,
|
| 3261 |
+
gp = gpar(fontsize = 9)
|
| 3262 |
+
)
|
| 3263 |
+
}
|
| 3264 |
}
|
| 3265 |
+
|
|
|
|
| 3266 |
grDevices::pdf(output_file, width = 8.5, height = 11)
|
| 3267 |
|
| 3268 |
grid.newpage()
|
|
|
|
| 3283 |
popViewport()
|
| 3284 |
|
| 3285 |
grid.newpage()
|
| 3286 |
+
|
| 3287 |
+
grid::grid.text("Data: TrackMan | Report Generated: Coastal Carolina Baseball Analytics",
|
| 3288 |
x = 0.5, y = 0.02, gp = grid::gpar(cex = 0.75, col = "grey50"))
|
| 3289 |
|
| 3290 |
pushViewport(viewport(x = 0.28, y = 0.81, width = 0.47, height = 0.27)); print(plot_ball_ccu, newpage = FALSE); popViewport()
|
|
|
|
| 3292 |
pushViewport(viewport(x = 0.28, y = 0.60, width = 0.47, height = 0.27)); print(plot_strike_ccu, newpage = FALSE); popViewport()
|
| 3293 |
pushViewport(viewport(x = 0.72, y = 0.60, width = 0.47, height = 0.27)); print(plot_strike_opp, newpage = FALSE); popViewport()
|
| 3294 |
|
| 3295 |
+
if (nrow(MissedCallsDisplay) > 0) {
|
| 3296 |
+
# First page can fit 15 rows in the bottom half
|
| 3297 |
first_page_rows <- 15
|
| 3298 |
remaining_page_rows <- rows_per_page # 30 rows per full page
|
| 3299 |
|
| 3300 |
+
if (nrow(MissedCallsDisplay) <= first_page_rows) {
|
| 3301 |
# Fits on current page
|
| 3302 |
pushViewport(viewport(x = 0.5, y = 0.30, width = 0.92, height = 0.50))
|
| 3303 |
+
grid.table(MissedCallsDisplay, rows = NULL, theme = ttheme_green_small)
|
| 3304 |
popViewport()
|
| 3305 |
} else {
|
| 3306 |
# First batch on current page
|
| 3307 |
+
first_chunk <- MissedCallsDisplay[1:first_page_rows, , drop = FALSE]
|
| 3308 |
pushViewport(viewport(x = 0.5, y = 0.30, width = 0.92, height = 0.50))
|
| 3309 |
grid.table(first_chunk, rows = NULL, theme = ttheme_green_small)
|
| 3310 |
popViewport()
|
| 3311 |
|
| 3312 |
# Remaining rows paginated onto new pages
|
| 3313 |
+
remaining <- MissedCallsDisplay[(first_page_rows + 1):nrow(MissedCallsDisplay), , drop = FALSE]
|
| 3314 |
remaining_chunks <- split(remaining, ceiling(seq_len(nrow(remaining)) / remaining_page_rows))
|
| 3315 |
|
| 3316 |
for (i in seq_along(remaining_chunks)) {
|
|
|
|
| 3328 |
invisible(output_file)
|
| 3329 |
}
|
| 3330 |
|
| 3331 |
+
|
| 3332 |
# Advanced Pitcher Functions
|
| 3333 |
`%||%` <- function(a, b) if (!is.null(a)) a else b
|
| 3334 |
|