# Strike Zone Minimum Distance Algorithms

Nov 1st, 2015
153
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
1. #########
2. # ZONES #
3. #########
4.
5. # The zones in the below algorithms are as follows:
6.
7. # Stage 1 and 2 (and 3 for the rectangular zones) where "5" is the strike zone:
8.
9. #############
10. # 1 # 2 # 3 #
11. #############
12. # 4 # 5 # 6 #
13. #############
14. # 7 # 8 # 9 #
15. #############
16.
17. # Stage 3 for the rule book strike zone, where "17" is directly behind the strike zone:
18.
19. ##########################
20. # 10 # 11 # 12 # 13 # 14 #
21. ##########################
22. # 15 # 16 # 17 # 18 # 19 #
23. ##########################
24. # 20 # 21 # 22 # 23 # 24 #
25. ##########################
26.
27. # In Stage 3, the areas above, below, and in the triangular part of the strike zone take 12, 17, and 22 and map them to 25, 26, and
28. # 27, respectively.
29.
30. ############################
31. # 3D Rule Book Strike Zone #
32. ############################
33.
34. dist_3D_to_SZ <- function(Pfx){
35.
36.     # This function finds the minimum distance from the pitch to the strike zone in three dimensions
37.     # Pfx is a list containing ax, ay, az, vx0, vy0, vz0, x0, y0, z0, sz_top, and sz_bot
38.
39.     # Set various locations in x, y, and z
40.     sz_front_y <- 17/12;
41.     sz_middle_y <- 17/24;
42.     sz_back_y <- 0;
43.     mitt_y <- -55/24;
44.     ground_z <- 0.125;
45.     sz_left_x <- -17/24;
46.     sz_right_x <- 17/24;
47.     sz_top_z <- Pfx\$sz_top;
48.     sz_bottom_z <- Pfx\$sz_bot;
49.     sz_middle_x <- (sz_left_x + sz_right_x)/2;
50.     sz_middle_z <- (sz_top_z + sz_bottom_z)/2;
52.
53.     # Find the time to various distances in y
54.
55.     # STAGE 1: Time to the front of home plate
56.     t_to_front <- (-Pfx\$vy0 - sqrt(Pfx\$vy0^2 - 2*Pfx\$ay*(Pfx\$y0 - sz_front_y)))/Pfx\$ay;
57.
58.     # STAGE 2: Time to the midpoint of home plate
59.     t_to_middle <- (-Pfx\$vy0 - sqrt(Pfx\$vy0^2 - 2*Pfx\$ay*(Pfx\$y0 - sz_middle_y)))/Pfx\$ay;
60.
61.     # STAGE 3: Time to the back of home plate
62.     t_to_back <- (-Pfx\$vy0 - sqrt(Pfx\$vy0^2 - 2*Pfx\$ay*(Pfx\$y0 - sz_back_y)))/Pfx\$ay;
63.
64.     # Time to catcher's mitt
65.     t_to_mitt <- (-Pfx\$vy0 - sqrt(Pfx\$vy0^2 - 2*Pfx\$ay*(Pfx\$y0 - mitt_y)))/Pfx\$ay;
66.
67.     # Time to ball hitting ground
68.     disc_z <- Pfx\$vz0^2 - 2*Pfx\$az*(Pfx\$z0 - ground_z);
69.     if (disc_z > 0){
70.         t_to_ground <- (-Pfx\$vz0 - sqrt(disc_z))/Pfx\$az;
71.     } else {
72.         t_to_ground <- t_to_mitt + 1;
73.     }
74.
75.     # Find the location at the front of the strike zone
76.     px <- x_eval(Pfx,t_to_front);
77.     pz <- z_eval(Pfx,t_to_front);
78.     pvx <- vx_eval(Pfx,t_to_front);
79.     pvz <- vz_eval(Pfx,t_to_front);
80.
81.
82.     # Find the number of stages before the ball hits the ground OR the catcher's mitt
83.     if(t_to_ground > t_to_middle){
84.         STAGES <- 3;
85.     } else if (t_to_ground > t_to_front){
86.         STAGES <- 2;
87.     } else {
88.         STAGES <- 1;
89.     }
90.
91.     # Allocate space for times when the pitch switches zones
92.     ZONE_TIMES_12 <- array(0,dim=8);
93.     ZONE_TIMES_3 <- array(0,dim=12);
94.
95.     # Left Vertical
96.     disc_LV <- Pfx\$vx0^2 - 2*Pfx\$ax*(Pfx\$x0 - sz_left_x);
97.     if (disc_LV > 0  && Pfx\$ax != 0) {
98.         ZONE_TIMES_12[1] <- (-Pfx\$vx0 + sqrt(disc_LV))/Pfx\$ax;
99.         ZONE_TIMES_12[2] <- (-Pfx\$vx0 - sqrt(disc_LV))/Pfx\$ax;
100.     } else if (Pfx\$ax == 0) {
101.         ZONE_TIMES_12[1] <- -(Pfx\$x0-sz_left_x)/Pfx\$vx0;
102.     }
103.
104.     # Right Vertical
105.     disc_RV <- Pfx\$vx0^2 - 2*Pfx\$ax*(Pfx\$x0 - sz_right_x);
106.     if (disc_RV > 0 && Pfx\$ax != 0) {
107.         ZONE_TIMES_12[3] <- (-Pfx\$vx0 + sqrt(disc_RV))/Pfx\$ax;
108.         ZONE_TIMES_12[4] <- (-Pfx\$vx0 - sqrt(disc_RV))/Pfx\$ax;
109.     } else if (Pfx\$ax == 0) {
110.         ZONE_TIMES_12[3] <- -(Pfx\$x0-sz_right_x)/Pfx\$vx0;
111.     }
112.
113.     # Top Horizontal
114.     disc_TH <- Pfx\$vz0^2 - 2*Pfx\$az*(Pfx\$z0 - sz_top_z);
115.     if (disc_TH > 0 && Pfx\$az != 0) {
116.         ZONE_TIMES_12[5] <- (-Pfx\$vz0 + sqrt(disc_TH))/Pfx\$az;
117.         ZONE_TIMES_12[6] <- (-Pfx\$vz0 - sqrt(disc_TH))/Pfx\$az;
118.         ZONE_TIMES_3[9] <- ZONE_TIMES_12[5];
119.         ZONE_TIMES_3[10] <- ZONE_TIMES_12[6];
120.     } else if (Pfx\$az == 0) {
121.         ZONE_TIMES_12[5] <- -(Pfx\$z0-sz_top_z)/Pfx\$vz0;
122.         ZONE_TIMES_3[9] <- ZONE_TIMES_12[5];
123.     }
124.
125.     # Bottom Horizontal
126.     disc_BH <- Pfx\$vz0^2 - 2*Pfx\$az*(Pfx\$z0 - sz_bottom_z);
127.     if (disc_BH > 0 && Pfx\$az != 0) {
128.         ZONE_TIMES_12[7] <- (-Pfx\$vz0 + sqrt(disc_BH))/Pfx\$az;
129.         ZONE_TIMES_12[8] <- (-Pfx\$vz0 - sqrt(disc_BH))/Pfx\$az;
130.         ZONE_TIMES_3[11] <- ZONE_TIMES_12[7];
131.         ZONE_TIMES_3[12] <- ZONE_TIMES_12[8];
132.     } else if (Pfx\$az == 0) {
133.         ZONE_TIMES_12[7] <- -(Pfx\$z0-sz_bottom_z)/Pfx\$vz0;
134.     }
135.
136.     # Left End
137.     a <- 0.5*(Pfx\$ax - Pfx\$ay);
138.     b <- Pfx\$vx0 - Pfx\$vy0;
139.     c <- Pfx\$x0 + (17/12) - Pfx\$y0;
140.     disc_LE <- b^2 - 4*a*c;
141.     if (disc_LE > 0){
142.         ZONE_TIMES_3[1] <- (-b + sqrt(disc_LE))/(2*a);
143.         ZONE_TIMES_3[2] <- (-b - sqrt(disc_LE))/(2*a);
144.     }
145.
146.     # Left Middle
147.     a <- 0.5*(Pfx\$ax - Pfx\$ay);
148.     b <- Pfx\$vx0 - Pfx\$vy0;
149.     c <- Pfx\$x0 - Pfx\$y0;
150.     disc_LM <- b^2 - 4*a*c;
151.     if (disc_LM > 0){
152.         ZONE_TIMES_3[3] <- (-b + sqrt(disc_LM))/(2*a);
153.         ZONE_TIMES_3[4] <- (-b - sqrt(disc_LM))/(2*a);
154.     }
155.
156.     # Right Middle
157.     a <- 0.5*(Pfx\$ax + Pfx\$ay);
158.     b <- Pfx\$vx0 + Pfx\$vy0;
159.     c <- Pfx\$x0 + Pfx\$y0;
160.     disc_RM <- b^2 - 4*a*c;
161.     if (disc_LM > 0){
162.         ZONE_TIMES_3[5] <- (-b + sqrt(disc_RM))/(2*a);
163.         ZONE_TIMES_3[6] <- (-b - sqrt(disc_RM))/(2*a);
164.     }
165.
166.     # Right End
167.     a <- 0.5*(Pfx\$ax + Pfx\$ay);
168.     b <- Pfx\$vx0 + Pfx\$vy0;
169.     c <- Pfx\$x0 - (17/12) + Pfx\$y0;
170.     disc_RE <- b^2 - 4*a*c;
171.     if (disc_RE > 0){
172.         ZONE_TIMES_3[7] <- (-b + sqrt(disc_RE))/(2*a);
173.         ZONE_TIMES_3[8] <- (-b - sqrt(disc_RE))/(2*a);
174.     }
175.
176.     # Allocate an array for moves between the zones
177.     ZONE_MOVE_1 <- array(0,dim=8);
178.     ZONE_MOVE_2 <- array(0,dim=8);
179.     ZONE_MOVE_3 <- array(0,dim=12);
180.
181.     ZONE_TIMES_1 <- array(0,dim=8);
182.     ZONE_TIMES_2 <- array(0,dim=8);
183.
184.     # Check for times within range of 50 feet to the middle of the strike zone
185.     for(i in 1:8){
186.         if(ZONE_TIMES_12[i] > 0 && ZONE_TIMES_12[i] < t_to_front) {
187.             ZONE_MOVE_1[i] <- 1;
188.             ZONE_TIMES_1[i] <- ZONE_TIMES_12[i];
189.         }
190.         if(ZONE_TIMES_12[i] > t_to_front && ZONE_TIMES_12[i] < t_to_middle) {
191.             ZONE_MOVE_2[i] <- 1;
192.             ZONE_TIMES_2[i] <- ZONE_TIMES_12[i];
193.         }
194.     }
195.
196.     for(i in 1:12){
197.         if(ZONE_TIMES_3[i] > t_to_middle && ZONE_TIMES_3[i] < t_to_mitt) {
198.             ZONE_MOVE_3[i] <- 1;
199.         }
200.     }
201.
202.     # Sort the times for zone switches and their indices
203.     ZONE_TIMES_1_SORT <- sort(ZONE_TIMES_1,decreasing=FALSE,index.return=TRUE);
204.     ZONE_TIMES_2_SORT <- sort(ZONE_TIMES_2,decreasing=FALSE,index.return=TRUE);
205.     ZONE_TIMES_3_SORT <- sort(ZONE_TIMES_3,decreasing=FALSE,index.return=TRUE);
206.
207.     MOVES_1 <- sum(ZONE_MOVE_1);
208.     MOVES_2 <- sum(ZONE_MOVE_2);
209.     MOVES_3 <- sum(ZONE_MOVE_3);
210.     ZONES_1 <- array(0,dim=(MOVES_1+1));
211.     ZONES_2 <- array(0,dim=(MOVES_2+1));
212.     ZONES_3 <- array(0,dim=(MOVES_3+1));
213.     ZONES_TRI <- array(0,dim=(MOVES_3+1));
214.     SWITCH_1 <- array(0,dim=(MOVES_1+2));
215.     SWITCH_2 <- array(0,dim=(MOVES_2+2));
216.     SWITCH_3 <- array(0,dim=(MOVES_3+2));
217.
218.     # Find the zone in which the pitch starts
219.     ZONES_1[1] <- zone_check(Pfx,sz_left_x,sz_right_x,sz_top_z,sz_bottom_z);
220.     SWITCH_1[1] <- 0;
221.
222.     k <- 1;
223.
224.     for(i in ZONE_TIMES_1_SORT\$ix){
225.         if(ZONE_MOVE_1[i] == 1){
226.             k <- k+1;
227.             if(i <= 4){
228.                 vx <- vx_eval(Pfx,ZONE_TIMES_1[i]);
229.                 if(vx > 0){
230.                     ZONES_1[k] <- ZONES_1[k-1] + 1;
231.                 } else {
232.                     ZONES_1[k] <- ZONES_1[k-1] - 1;
233.                 }
234.             } else {
235.                 vz <- vz_eval(Pfx,ZONE_TIMES_1[i]);
236.                 if(vz > 0){
237.                     ZONES_1[k] <- ZONES_1[k-1] - 3;
238.                 } else {
239.                     ZONES_1[k] <- ZONES_1[k-1] + 3;
240.                 }
241.             }
242.             SWITCH_1[k] <- ZONE_TIMES_1[i];
243.         }
244.     }
245.
246.     SWITCH_1[MOVES_1+2] <- t_to_front;
247.
248.     ZONES_2[1] <- ZONES_1[MOVES_1+1];
249.     SWITCH_2[1] <- t_to_front;
250.
251.     k <- 1;
252.
253.     for(i in ZONE_TIMES_2_SORT\$ix){
254.         if(ZONE_MOVE_2[i] == 1){
255.             k <- k+1;
256.             if(i <= 4){
257.                 vx <- vx_eval(Pfx,ZONE_TIMES_2[i]);
258.                 if(vx > 0){
259.                     ZONES_2[k] <- ZONES_2[k-1] + 1;
260.                 } else {
261.                     ZONES_2[k] <- ZONES_2[k-1] - 1;
262.                 }
263.             } else {
264.                 vz <- vz_eval(Pfx,ZONE_TIMES_2[i]);
265.                 if(vz > 0){
266.                     ZONES_2[k] <- ZONES_2[k-1] - 3;
267.                 } else {
268.                     ZONES_2[k] <- ZONES_2[k-1] + 3;
269.                 }
270.             }
271.             SWITCH_2[k] <- ZONE_TIMES_2[i];
272.         }
273.     }
274.
275.     SWITCH_2[MOVES_2+2] <- t_to_middle;
276.
277.     # Re-map the zones from the front end of the strike zone to the back end
278.     ZONE_TEMP <- ZONES_2[MOVES_2+1];
279.     if (ZONE_TEMP == 1){
280.         ZONES_3[1] <- 10;
281.     } else if (ZONE_TEMP == 2){
282.         ZONES_3[1] <- 12;
283.     } else if (ZONE_TEMP == 3){
284.         ZONES_3[1] <- 14;
285.     } else if (ZONE_TEMP == 4){
286.         ZONES_3[1] <- 15;
287.     } else if (ZONE_TEMP == 5){
288.         ZONES_3[1] <- 17;
289.     } else if (ZONE_TEMP == 6){
290.         ZONES_3[1] <- 19;
291.     } else if (ZONE_TEMP == 7){
292.         ZONES_3[1] <- 20;
293.     } else if (ZONE_TEMP == 8){
294.         ZONES_3[1] <- 22;
295.     } else if (ZONE_TEMP == 9){
296.         ZONES_3[1] <- 24;
297.     }
298.
299.     ZONES_TRI[1] <- 1;
300.
301.     SWITCH_3[1] <- t_to_middle;
302.
303.     k <- 1;
304.
305.     for(i in ZONE_TIMES_3_SORT\$ix){
306.         if(ZONE_MOVE_3[i] == 1){
307.             k <- k+1;
308.             if (ZONE_TIMES_3[i] < t_to_back && ZONE_TIMES_3[i] > t_to_middle) {
309.                 ZONES_TRI[k] <- 1;
310.             }
311.             if (i <= 4){
312.                 vx <- vx_eval(Pfx,ZONE_TIMES_3[i]);
313.                 vy <- vy_eval(Pfx,ZONE_TIMES_3[i]);
314.                 if (abs(vx) > abs(vy) && vx < 0) {
315.                     ZONES_3[k] <- ZONES_3[k-1] - 1;
316.                 } else {
317.                     ZONES_3[k] <- ZONES_3[k-1] + 1;
318.                 }
319.             } else if (i <= 8) {
320.                 vx <- vx_eval(Pfx,ZONE_TIMES_3[i]);
321.                 vy <- vy_eval(Pfx,ZONE_TIMES_3[i]);
322.                 if (abs(vx) > abs(vy) && vx > 0) {
323.                     ZONES_3[k] <- ZONES_3[k-1] + 1;
324.                 } else {
325.                     ZONES_3[k] <- ZONES_3[k-1] - 1;
326.                 }
327.             } else {
328.                 vz <- vz_eval(Pfx,ZONE_TIMES_3[i]);
329.                 if (vz > 0){
330.                     ZONES_3[k] <- ZONES_3[k-1] - 5;
331.                 } else {
332.                     ZONES_3[k] <- ZONES_3[k-1] + 5;
333.                 }
334.             }
335.             SWITCH_3[k] <- ZONE_TIMES_3[i];
336.         }
337.     }
338.
339.     SWITCH_3[MOVES_3+2] <- t_to_mitt;
340.
341.     MOVE_CTR <- 1;
342.     dist_sz <- 55;
343.
344.     # Find the distances over STAGE 1
345.     while(MOVE_CTR <= (MOVES_1+1) && SWITCH_1[MOVE_CTR] < t_to_ground){
346.         t_0 <- SWITCH_1[MOVE_CTR];
347.         t_1 <- min(SWITCH_1[MOVE_CTR+1],t_to_ground);
348.         if(ZONES_1[MOVE_CTR] == 1){
349.             dist_sz_temp <- min_corner(Pfx,sz_left_x,sz_front_y,sz_top_z,t_0,t_1);
350.         } else if(ZONES_1[MOVE_CTR] == 2){
351.             dist_sz_temp <- min_edge_yz(Pfx,sz_front_y,sz_top_z,t_0,t_1);
352.         } else if(ZONES_1[MOVE_CTR] == 3){
353.             dist_sz_temp <- min_corner(Pfx,sz_right_x,sz_front_y,sz_top_z,t_0,t_1);
354.         } else if(ZONES_1[MOVE_CTR] == 4){
355.             dist_sz_temp <- min_edge_xy(Pfx,sz_left_x,sz_front_y,t_0,t_1);
356.         } else if(ZONES_1[MOVE_CTR] == 5){
357.             dist_sz_temp <- min_face_y(Pfx,sz_front_y,t_0,t_1);
358.         } else if(ZONES_1[MOVE_CTR] == 6){
359.             dist_sz_temp <- min_edge_xy(Pfx,sz_right_x,sz_front_y,t_0,t_1);
360.         } else if(ZONES_1[MOVE_CTR] == 7){
361.             dist_sz_temp <- min_corner(Pfx,sz_left_x,sz_front_y,sz_bottom_z,t_0,t_1);
362.         } else if(ZONES_1[MOVE_CTR] == 8){
363.             dist_sz_temp <- min_edge_yz(Pfx,sz_front_y,sz_bottom_z,t_0,t_1);
364.         } else if(ZONES_1[MOVE_CTR] == 9){
365.             dist_sz_temp <- min_corner(Pfx,sz_right_x,sz_front_y,sz_bottom_z,t_0,t_1);
366.         }
367.         MOVE_CTR <- MOVE_CTR + 1;
368.         if (dist_sz_temp < dist_sz){
369.             dist_sz <- dist_sz_temp;
370.             stage <- 1;
371.             zone <- ZONES_1[MOVE_CTR-1];
372.         }
374.             OUT <- list(dist_sz = max(dist_sz-ball_radius,0), stage = stage, zone = zone, px = px, pz = pz, pvx = pvx, pvz = pvz);
375.             return(OUT);
376.         }
377.     }
378.
379.     # Find the distances over STAGE 2
380.     if (STAGES > 1){
381.         MOVE_CTR <- 1;
382.         while(MOVE_CTR <= (MOVES_2+1) && SWITCH_2[MOVE_CTR] < t_to_ground){
383.             t_1 <- SWITCH_2[MOVE_CTR];
384.             t_2 <- min(SWITCH_2[MOVE_CTR+1],t_to_ground);
385.             if(ZONES_2[MOVE_CTR] == 1){
386.                 dist_sz_temp <- min_edge_xz(Pfx,sz_left_x,sz_top_z,t_1,t_2);
387.             } else if(ZONES_2[MOVE_CTR] == 2){
388.                 dist_sz_temp <- min_face_z(Pfx,sz_top_z,t_1,t_2);
389.             } else if(ZONES_2[MOVE_CTR] == 3){
390.                 dist_sz_temp <- min_edge_xz(Pfx,sz_right_x,sz_top_z,t_1,t_2);
391.             } else if(ZONES_2[MOVE_CTR] == 4){
392.                 dist_sz_temp <- min_face_x(Pfx,sz_left_x,t_1,t_2);
393.             } else if(ZONES_2[MOVE_CTR] == 5){
394.                 dist_sz_temp <- 0;
395.             } else if(ZONES_2[MOVE_CTR] == 6){
396.                 dist_sz_temp <- min_face_x(Pfx,sz_right_x,t_1,t_2);
397.             } else if(ZONES_2[MOVE_CTR] == 7){
398.                 dist_sz_temp <- min_edge_xz(Pfx,sz_left_x,sz_bottom_z,t_1,t_2);
399.             } else if(ZONES_2[MOVE_CTR] == 8){
400.                 dist_sz_temp <- min_face_z(Pfx,sz_bottom_z,t_1,t_2);
401.             } else if(ZONES_2[MOVE_CTR] == 9){
402.                 dist_sz_temp <- min_edge_xz(Pfx,sz_right_x,sz_bottom_z,t_1,t_2);
403.             }
404.             MOVE_CTR <- MOVE_CTR + 1;
405.             if (dist_sz_temp < dist_sz){
406.                 dist_sz <- dist_sz_temp;
407.                 stage <- 2;
408.                 zone <- ZONES_2[MOVE_CTR-1];
409.             }
411.                 OUT <- list(dist_sz = max(dist_sz-ball_radius,0), stage = stage, zone = zone, px = px, pz = pz, pvx = pvx, pvz = pvz);
412.             return(OUT);
413.             }
414.         }
415.     }
416.
417.     tri_flag <- 0;
418.     # Find the distances over STAGE 3
419.     if (STAGES > 2){
420.         MOVE_CTR <- 1;
421.         while (MOVE_CTR <= (MOVES_3+1) && SWITCH_3[MOVE_CTR] < t_to_ground){
422.             t_2 <- SWITCH_3[MOVE_CTR];
423.             t_3 <- min(SWITCH_3[MOVE_CTR+1],t_to_ground);
424.             if(ZONES_3[MOVE_CTR] == 10){
425.                 dist_sz_temp <- min_corner(Pfx,sz_left_x,sz_middle_y,sz_top_z,t_2,t_3);
426.             } else if (ZONES_3[MOVE_CTR] == 11) {
427.                 dist_sz_temp <- min_diag_edge_xy(Pfx,sz_left_x,sz_middle_y,sz_top_z,sz_middle_x,sz_back_y,t_2,t_3);
428.             } else if (ZONES_3[MOVE_CTR] == 12) {
429.                 if (ZONES_TRI[MOVE_CTR] == 1) {
430.                     dist_sz_temp <- min_face_z(Pfx,sz_top_z,t_2,t_3);
431.                     tri_flag <- 1;
432.                 } else {
433.                     dist_sz_temp <- min_corner(Pfx,sz_middle_x,sz_back_y,sz_top_z,t_2,t_3);
434.                 }
435.             } else if (ZONES_3[MOVE_CTR] == 13) {
436.                 dist_sz_temp <- min_diag_edge_xy(Pfx,sz_right_x,sz_middle_y,sz_top_z,sz_middle_x,sz_back_y,t_2,t_3);
437.             } else if (ZONES_3[MOVE_CTR] == 14) {
438.                 dist_sz_temp <- min_corner(Pfx,sz_right_x,sz_middle_y,sz_top_z,t_2,t_3);
439.             } else if (ZONES_3[MOVE_CTR] == 15) {
440.                 dist_sz_temp <- min_edge_xy(Pfx,sz_left_x,sz_middle_y,t_2,t_3);
441.             } else if (ZONES_3[MOVE_CTR] == 16) {
442.                 dist_sz_temp <- min_diag_face_xy(Pfx,1,1,0,sz_middle_x,sz_back_y,sz_bottom_z,t_2,t_3);
443.             } else if (ZONES_3[MOVE_CTR] == 17) {
444.                 if (ZONES_TRI[MOVE_CTR] == 1){
445.                     dist_sz_temp <- 0;
446.                     tri_flag <- 1;
447.                 } else {
448.                     dist_sz_temp <- min_edge_xy(Pfx,sz_middle_x,sz_back_y,t_2,t_3);
449.                 }
450.             } else if (ZONES_3[MOVE_CTR] == 18) {
451.                 dist_sz_temp <- min_diag_face_xy(Pfx,-1,1,0,sz_middle_x,sz_back_y,sz_bottom_z,t_2,t_3);
452.             } else if (ZONES_3[MOVE_CTR] == 19) {
453.                 dist_sz_temp <- min_edge_xy(Pfx,sz_right_x,sz_middle_y,t_2,t_3);
454.             } else if (ZONES_3[MOVE_CTR] == 20) {
455.                 dist_sz_temp <- min_corner(Pfx,sz_left_x,sz_middle_y,sz_bottom_z,t_2,t_3);
456.             } else if (ZONES_3[MOVE_CTR] == 21) {
457.                 dist_sz_temp <- min_diag_edge_xy(Pfx,sz_left_x,sz_middle_y,sz_bottom_z,sz_middle_x,sz_back_y,t_2,t_3);
458.             } else if (ZONES_3[MOVE_CTR] == 22) {
459.                 if (ZONES_TRI[MOVE_CTR] == 1) {
460.                     dist_sz_temp <- min_face_z(Pfx,sz_bottom_z,t_2,t_3);
461.                     tri_flag <- 1;
462.                 } else {
463.                     dist_sz_temp <- min_corner(Pfx,sz_middle_x,sz_back_y,sz_bottom_z,t_2,t_3);
464.                 }
465.             } else if (ZONES_3[MOVE_CTR] == 23) {
466.                 dist_sz_temp <- min_diag_edge_xy(Pfx,sz_right_x,sz_middle_y,sz_bottom_z,sz_middle_x,sz_back_y,t_2,t_3);
467.             } else if (ZONES_3[MOVE_CTR] == 24) {
468.                 dist_sz_temp <- min_corner(Pfx,sz_right_x,sz_middle_y,sz_bottom_z,t_2,t_3);
469.             }
470.             MOVE_CTR <- MOVE_CTR + 1;
471.             if (dist_sz_temp < dist_sz){
472.                 dist_sz <- dist_sz_temp;
473.                 stage <- 3;
474.                 zone <- ZONES_3[MOVE_CTR-1];
475.                 if (tri_flag == 1){
476.                     if (zone == 12) {
477.                         zone <- 25;
478.                     } else if (zone == 17) {
479.                         zone <- 26;
480.                     } else {
481.                         zone <- 27;
482.                     }
483.                     tri_flag <- 0;
484.                 }
485.             }
487.                 OUT <- list(dist_sz = max(dist_sz-ball_radius,0), stage = stage, zone = zone, px = px, pz = pz, pvx = pvx, pvz = pvz);
488.                 return(OUT);
489.             }
490.         }
491.     }
492.
493.     OUT <- list(dist_sz = max(dist_sz-ball_radius,0), stage = stage, zone = zone, px = px, pz = pz, pvx = pvx, pvz = pvz);
494.     return(OUT);
495. }
496.
497. ########################
498. # 3D Strike Zone (Box) #
499. ########################
500.
501. dist_3D_to_SZ_box <- function(Pfx){
502.
503.     # This function finds the minimum distance from the pitch to a strike zone box in three dimensions
504.     # Pfx is a list containing ax, ay, az, vx0, vy0, vz0, x0, y0, z0, sz_top, and sz_bot
505.
506.     # Set various locations in x, y, and z
507.     sz_front_y <- 17/12;
508.     sz_back_y <- 17/24;
509.     plate_front <- 17/12;
510.     mitt_y <- -55/24;
511.     ground_z <- 0.125;
512.     sz_left_x <- -17/24;
513.     sz_right_x <- 17/24;
514.     sz_top_z <- Pfx\$sz_top;
515.     sz_bottom_z <- Pfx\$sz_bot;
517.
518.     # Find the time to various distances in y
519.
520.     # STAGE 1: Time to the front of home plate
521.     t_to_front <- (-Pfx\$vy0 - sqrt(Pfx\$vy0^2 - 2*Pfx\$ay*(Pfx\$y0 - sz_front_y)))/Pfx\$ay;
522.
523.     # Time to the front of home plate
524.     t_to_plate <- (-Pfx\$vy0 - sqrt(Pfx\$vy0^2 - 2*Pfx\$ay*(Pfx\$y0 - plate_front)))/Pfx\$ay;
525.
526.     # STAGE 2: Time to the back of home plate
527.     t_to_back <- (-Pfx\$vy0 - sqrt(Pfx\$vy0^2 - 2*Pfx\$ay*(Pfx\$y0 - sz_back_y)))/Pfx\$ay;
528.
529.     # Stage 3: Time to catcher's mitt
530.     t_to_mitt <- (-Pfx\$vy0 - sqrt(Pfx\$vy0^2 - 2*Pfx\$ay*(Pfx\$y0 - mitt_y)))/Pfx\$ay;
531.
532.     # Time to ball hitting ground
533.     disc_z <- Pfx\$vz0^2 - 2*Pfx\$az*(Pfx\$z0 - ground_z);
534.     if (disc_z > 0){
535.         t_to_ground <- (-Pfx\$vz0 - sqrt(disc_z))/Pfx\$az;
536.     } else {
537.         t_to_ground <- t_to_mitt + 1;
538.     }
539.
540.     # Find the location at the front of the strike zone
541.     px <- x_eval(Pfx,t_to_plate);
542.     pz <- z_eval(Pfx,t_to_plate);
543.
544.     # Find the number of stages before the ball hits the ground OR the catcher's mitt
545.     if(t_to_ground > t_to_back){
546.         STAGES <- 3;
547.     } else if (t_to_ground > t_to_front){
548.         STAGES <- 2;
549.     } else {
550.         STAGES <- 1;
551.     }
552.
553.     # Allocate space for times when the pitch switches zones
554.     ZONE_TIMES <- array(0,dim=8);
555.
556.     # Left Vertical
557.     disc_LV <- Pfx\$vx0^2 - 2*Pfx\$ax*(Pfx\$x0 - sz_left_x);
558.     if (disc_LV > 0  && Pfx\$ax != 0) {
559.         ZONE_TIMES[1] <- (-Pfx\$vx0 + sqrt(disc_LV))/Pfx\$ax;
560.         ZONE_TIMES[2] <- (-Pfx\$vx0 - sqrt(disc_LV))/Pfx\$ax;
561.     } else if (Pfx\$ax == 0) {
562.         ZONE_TIMES[1] <- -(Pfx\$x0-sz_left_x)/Pfx\$vx0;
563.     }
564.
565.     # Right Vertical
566.     disc_RV <- Pfx\$vx0^2 - 2*Pfx\$ax*(Pfx\$x0 - sz_right_x);
567.     if (disc_RV > 0 && Pfx\$ax != 0) {
568.         ZONE_TIMES[3] <- (-Pfx\$vx0 + sqrt(disc_RV))/Pfx\$ax;
569.         ZONE_TIMES[4] <- (-Pfx\$vx0 - sqrt(disc_RV))/Pfx\$ax;
570.     } else if (Pfx\$ax == 0) {
571.         ZONE_TIMES[3] <- -(Pfx\$x0-sz_right_x)/Pfx\$vx0;
572.     }
573.
574.     # Top Horizontal
575.     disc_TH <- Pfx\$vz0^2 - 2*Pfx\$az*(Pfx\$z0 - sz_top_z);
576.     if (disc_TH > 0 && Pfx\$az != 0) {
577.         ZONE_TIMES[5] <- (-Pfx\$vz0 + sqrt(disc_TH))/Pfx\$az;
578.         ZONE_TIMES[6] <- (-Pfx\$vz0 - sqrt(disc_TH))/Pfx\$az;
579.     } else if (Pfx\$az == 0) {
580.         ZONE_TIMES[5] <- -(Pfx\$z0-sz_top_z)/Pfx\$vz0;
581.     }
582.
583.     # Bottom Horizontal
584.     disc_BH <- Pfx\$vz0^2 - 2*Pfx\$az*(Pfx\$z0 - sz_bottom_z);
585.     if (disc_BH > 0 && Pfx\$az != 0) {
586.         ZONE_TIMES[7] <- (-Pfx\$vz0 + sqrt(disc_BH))/Pfx\$az;
587.         ZONE_TIMES[8] <- (-Pfx\$vz0 - sqrt(disc_BH))/Pfx\$az;
588.     } else if (Pfx\$az == 0) {
589.         ZONE_TIMES[7] <- -(Pfx\$z0-sz_bottom_z)/Pfx\$vz0;
590.     }
591.
592.     # Allocate an array for moves between the zones
593.     ZONE_MOVE_1 <- array(0,dim=8);
594.     ZONE_MOVE_2 <- array(0,dim=8);
595.     ZONE_MOVE_3 <- array(0,dim=8);
596.
597.     ZONE_TIMES_1 <- array(0,dim=8);
598.     ZONE_TIMES_2 <- array(0,dim=8);
599.     ZONE_TIMES_3 <- array(0,dim=8);
600.
601.     # Check for times within range of 50 feet to the middle of the strike zone
602.     for(i in 1:8){
603.         if(ZONE_TIMES[i] > 0 && ZONE_TIMES[i] < t_to_front) {
604.             ZONE_MOVE_1[i] <- 1;
605.             ZONE_TIMES_1[i] <- ZONE_TIMES[i];
606.         }
607.         if(ZONE_TIMES[i] > t_to_front && ZONE_TIMES[i] < t_to_back) {
608.             ZONE_MOVE_2[i] <- 1;
609.             ZONE_TIMES_2[i] <- ZONE_TIMES[i];
610.         }
611.         if(ZONE_TIMES[i] > t_to_back && ZONE_TIMES[i] < t_to_mitt) {
612.             ZONE_MOVE_3[i] <- 1;
613.             ZONE_TIMES_3[i] <- ZONE_TIMES[i];
614.         }
615.     }
616.
617.     # Sort the times for zone switches and their indices
618.     ZONE_TIMES_1_SORT <- sort(ZONE_TIMES_1,decreasing=FALSE,index.return=TRUE);
619.     ZONE_TIMES_2_SORT <- sort(ZONE_TIMES_2,decreasing=FALSE,index.return=TRUE);
620.     ZONE_TIMES_3_SORT <- sort(ZONE_TIMES_3,decreasing=FALSE,index.return=TRUE);
621.
622.     MOVES_1 <- sum(ZONE_MOVE_1);
623.     MOVES_2 <- sum(ZONE_MOVE_2);
624.     MOVES_3 <- sum(ZONE_MOVE_3);
625.     ZONES_1 <- array(0,dim=(MOVES_1+1));
626.     ZONES_2 <- array(0,dim=(MOVES_2+1));
627.     ZONES_3 <- array(0,dim=(MOVES_3+1));
628.     SWITCH_1 <- array(0,dim=(MOVES_1+2));
629.     SWITCH_2 <- array(0,dim=(MOVES_2+2));
630.     SWITCH_3 <- array(0,dim=(MOVES_3+2));
631.
632.     # Find the zone in which the pitch starts
633.     ZONES_1[1] <- zone_check(Pfx,sz_left_x,sz_right_x,sz_top_z,sz_bottom_z);
634.     SWITCH_1[1] <- 0;
635.
636.     k <- 1;
637.
638.     for(i in ZONE_TIMES_1_SORT\$ix){
639.         if(ZONE_MOVE_1[i] == 1){
640.             k <- k+1;
641.             if(i <= 4){
642.                 vx <- vx_eval(Pfx,ZONE_TIMES_1[i]);
643.                 if(vx > 0){
644.                     ZONES_1[k] <- ZONES_1[k-1] + 1;
645.                 } else {
646.                     ZONES_1[k] <- ZONES_1[k-1] - 1;
647.                 }
648.             } else {
649.                 vz <- vz_eval(Pfx,ZONE_TIMES_1[i]);
650.                 if(vz > 0){
651.                     ZONES_1[k] <- ZONES_1[k-1] - 3;
652.                 } else {
653.                     ZONES_1[k] <- ZONES_1[k-1] + 3;
654.                 }
655.             }
656.             SWITCH_1[k] <- ZONE_TIMES_1[i];
657.         }
658.     }
659.
660.     SWITCH_1[MOVES_1+2] <- t_to_front;
661.
662.     ZONES_2[1] <- ZONES_1[MOVES_1+1];
663.     SWITCH_2[1] <- t_to_front;
664.
665.     k <- 1;
666.
667.     for(i in ZONE_TIMES_2_SORT\$ix){
668.         if(ZONE_MOVE_2[i] == 1){
669.             k <- k+1;
670.             if(i <= 4){
671.                 vx <- vx_eval(Pfx,ZONE_TIMES_2[i]);
672.                 if(vx > 0){
673.                     ZONES_2[k] <- ZONES_2[k-1] + 1;
674.                 } else {
675.                     ZONES_2[k] <- ZONES_2[k-1] - 1;
676.                 }
677.             } else {
678.                 vz <- vz_eval(Pfx,ZONE_TIMES_1[i]);
679.                 if(vz > 0){
680.                     ZONES_2[k] <- ZONES_2[k-1] - 3;
681.                 } else {
682.                     ZONES_2[k] <- ZONES_2[k-1] + 3;
683.                 }
684.             }
685.             SWITCH_2[k] <- ZONE_TIMES_2[i];
686.         }
687.     }
688.
689.     SWITCH_2[MOVES_2+2] <- t_to_back;
690.
691.     ZONES_3[1] <- ZONES_2[MOVES_2+1];
692.     SWITCH_3[1] <- t_to_back;
693.
694.     k <- 1;
695.
696.     for(i in ZONE_TIMES_3_SORT\$ix){
697.         if(ZONE_MOVE_3[i] == 1){
698.             k <- k+1;
699.             if(i <= 4){
700.                 vx <- vx_eval(Pfx,ZONE_TIMES_3[i]);
701.                 if(vx > 0){
702.                     ZONES_3[k] <- ZONES_3[k-1] + 1;
703.                 } else {
704.                     ZONES_3[k] <- ZONES_3[k-1] - 1;
705.                 }
706.             } else {
707.                 vz <- vz_eval(Pfx,ZONE_TIMES_3[i]);
708.                 if(vz > 0){
709.                     ZONES_3[k] <- ZONES_3[k-1] - 3;
710.                 } else {
711.                     ZONES_3[k] <- ZONES_3[k-1] + 3;
712.                 }
713.             }
714.             SWITCH_3[k] <- ZONE_TIMES_3[i];
715.         }
716.     }
717.
718.     SWITCH_3[MOVES_3+2] <- t_to_mitt;
719.
720.     MOVE_CTR <- 1;
721.     dist_sz <- 55;
722.
723.     # Find the distances over STAGE 1
724.     while(MOVE_CTR <= (MOVES_1+1) && SWITCH_1[MOVE_CTR] < t_to_ground){
725.         t_0 <- SWITCH_1[MOVE_CTR];
726.         t_1 <- min(SWITCH_1[MOVE_CTR+1],t_to_ground);
727.         if(ZONES_1[MOVE_CTR] == 1){
728.             dist_sz_temp <- min_corner(Pfx,sz_left_x,sz_front_y,sz_top_z,t_0,t_1);
729.         } else if(ZONES_1[MOVE_CTR] == 2){
730.             dist_sz_temp <- min_edge_yz(Pfx,sz_front_y,sz_top_z,t_0,t_1);
731.         } else if(ZONES_1[MOVE_CTR] == 3){
732.             dist_sz_temp <- min_corner(Pfx,sz_right_x,sz_front_y,sz_top_z,t_0,t_1);
733.         } else if(ZONES_1[MOVE_CTR] == 4){
734.             dist_sz_temp <- min_edge_xy(Pfx,sz_left_x,sz_front_y,t_0,t_1);
735.         } else if(ZONES_1[MOVE_CTR] == 5){
736.             dist_sz_temp <- min_face_y(Pfx,sz_front_y,t_0,t_1);
737.         } else if(ZONES_1[MOVE_CTR] == 6){
738.             dist_sz_temp <- min_edge_xy(Pfx,sz_right_x,sz_front_y,t_0,t_1);
739.         } else if(ZONES_1[MOVE_CTR] == 7){
740.             dist_sz_temp <- min_corner(Pfx,sz_left_x,sz_front_y,sz_bottom_z,t_0,t_1);
741.         } else if(ZONES_1[MOVE_CTR] == 8){
742.             dist_sz_temp <- min_edge_yz(Pfx,sz_front_y,sz_bottom_z,t_0,t_1);
743.         } else if(ZONES_1[MOVE_CTR] == 9){
744.             dist_sz_temp <- min_corner(Pfx,sz_right_x,sz_front_y,sz_bottom_z,t_0,t_1);
745.         }
746.         MOVE_CTR <- MOVE_CTR + 1;
747.         if (dist_sz_temp < dist_sz){
748.             dist_sz <- dist_sz_temp;
749.             stage <- 1;
750.             zone <- ZONES_1[MOVE_CTR-1];
751.         }
753.             OUT <- list(dist_sz = max(dist_sz-ball_radius,0), stage = stage, zone = zone, px = px, pz = pz);
754.             return(OUT);
755.         }
756.     }
757.
758.     # Find the distances over STAGE 2
759.     if (STAGES > 1){
760.         MOVE_CTR <- 1;
761.         while(MOVE_CTR <= (MOVES_2+1) && SWITCH_2[MOVE_CTR] < t_to_ground){
762.             t_1 <- SWITCH_2[MOVE_CTR];
763.             t_2 <- min(SWITCH_2[MOVE_CTR+1],t_to_ground);
764.             if(ZONES_2[MOVE_CTR] == 1){
765.                 dist_sz_temp <- min_edge_xz(Pfx,sz_left_x,sz_top_z,t_1,t_2);
766.             } else if(ZONES_2[MOVE_CTR] == 2){
767.                 dist_sz_temp <- min_face_z(Pfx,sz_top_z,t_1,t_2);
768.             } else if(ZONES_2[MOVE_CTR] == 3){
769.                 dist_sz_temp <- min_edge_xz(Pfx,sz_right_x,sz_top_z,t_1,t_2);
770.             } else if(ZONES_2[MOVE_CTR] == 4){
771.                 dist_sz_temp <- min_face_x(Pfx,sz_left_x,t_1,t_2);
772.             } else if(ZONES_2[MOVE_CTR] == 5){
773.                 dist_sz_temp <- 0;
774.             } else if(ZONES_2[MOVE_CTR] == 6){
775.                 dist_sz_temp <- min_face_x(Pfx,sz_right_x,t_1,t_2);
776.             } else if(ZONES_2[MOVE_CTR] == 7){
777.                 dist_sz_temp <- min_edge_xz(Pfx,sz_left_x,sz_bottom_z,t_1,t_2);
778.             } else if(ZONES_2[MOVE_CTR] == 8){
779.                 dist_sz_temp <- min_face_z(Pfx,sz_bottom_z,t_1,t_2);
780.             } else if(ZONES_2[MOVE_CTR] == 9){
781.                 dist_sz_temp <- min_edge_xz(Pfx,sz_right_x,sz_bottom_z,t_1,t_2);
782.             }
783.             MOVE_CTR <- MOVE_CTR + 1;
784.             if (dist_sz_temp < dist_sz){
785.                 dist_sz <- dist_sz_temp;
786.                 stage <- 2;
787.                 zone <- ZONES_2[MOVE_CTR-1];
788.             }
790.                 OUT <- list(dist_sz = max(dist_sz-ball_radius,0), stage = stage, zone = zone, px = px, pz = pz);
791.             return(OUT);
792.             }
793.         }
794.     }
795.
796.     # Find the distances over STAGE 3
797.     if (STAGES > 2){
798.         MOVE_CTR <- 1;
799.         while(MOVE_CTR <= (MOVES_3+1) && SWITCH_3[MOVE_CTR] < t_to_ground){
800.             t_2 <- SWITCH_3[MOVE_CTR];
801.             t_3 <- min(SWITCH_3[MOVE_CTR+1],t_to_ground);
802.             if(ZONES_3[MOVE_CTR] == 1){
803.                 dist_sz_temp <- min_corner(Pfx,sz_left_x,sz_back_y,sz_top_z,t_2,t_3);
804.             } else if(ZONES_3[MOVE_CTR] == 2){
805.                 dist_sz_temp <- min_edge_yz(Pfx,sz_back_y,sz_top_z,t_2,t_3);
806.             } else if(ZONES_3[MOVE_CTR] == 3){
807.                 dist_sz_temp <- min_corner(Pfx,sz_right_x,sz_back_y,sz_top_z,t_2,t_3);
808.             } else if(ZONES_3[MOVE_CTR] == 4){
809.                 dist_sz_temp <- min_edge_xy(Pfx,sz_left_x,sz_back_y,t_2,t_3);
810.             } else if(ZONES_3[MOVE_CTR] == 5){
811.                 dist_sz_temp <- min_face_y(Pfx,sz_back_y,t_2,t_3);
812.             } else if(ZONES_3[MOVE_CTR] == 6){
813.                 dist_sz_temp <- min_edge_xy(Pfx,sz_right_x,sz_back_y,t_2,t_3);
814.             } else if(ZONES_3[MOVE_CTR] == 7){
815.                 dist_sz_temp <- min_corner(Pfx,sz_left_x,sz_back_y,sz_bottom_z,t_2,t_3);
816.             } else if(ZONES_3[MOVE_CTR] == 8){
817.                 dist_sz_temp <- min_edge_yz(Pfx,sz_back_y,sz_bottom_z,t_2,t_3);
818.             } else if(ZONES_3[MOVE_CTR] == 9){
819.                 dist_sz_temp <- min_corner(Pfx,sz_right_x,sz_back_y,sz_bottom_z,t_2,t_3);
820.             }
821.             MOVE_CTR <- MOVE_CTR + 1;
822.             if (dist_sz_temp < dist_sz){
823.                 dist_sz <- dist_sz_temp;
824.                 stage <- 3;
825.                 zone <- ZONES_3[MOVE_CTR-1];
826.             }
828.                 OUT <- list(dist_sz = max(dist_sz-ball_radius,0), stage = stage, zone = zone, px = px, pz = pz);
829.                 return(OUT);
830.             }
831.         }
832.     }
833.
834.     OUT <- list(dist_sz = max(dist_sz-ball_radius,0), stage = stage, zone = zone, px = px, pz = pz);
835.     return(OUT);
836. }
837.
838. ##################
839. # 2D Strike Zone #
840. ##################
841.
842. dist_2D_to_SZ <- function(Pfx){
843.
844.     # This function finds the minimum distance from the pitch to the 2D strike zone
845.     # Pfx is a list containing ax, ay, az, vx0, vy0, vz0, x0, y0, z0, sz_top, and sz_bot
846.
847.     # Set various locations in x, y, and z
848.     sz_front_y <- 17/12;
849.     plate_front <- 17/12;
850.     sz_left_x <- -17/24;
851.     sz_right_x <- 17/24;
852.     sz_top_z <- Pfx\$sz_top;
853.     sz_bottom_z <- Pfx\$sz_bot;
855.
856.     # Time to the front of the strike zone
857.     t_to_front <- (-Pfx\$vy0 - sqrt(Pfx\$vy0^2 - 2*Pfx\$ay*(Pfx\$y0 - sz_front_y)))/Pfx\$ay;
858.
859.     # Time to the front of home plate
860.     t_to_plate <- (-Pfx\$vy0 - sqrt(Pfx\$vy0^2 - 2*Pfx\$ay*(Pfx\$y0 - plate_front)))/Pfx\$ay;
861.
862.     # Find the location/velocity at the front of the strike zone
863.     px <- x_eval(Pfx,t_to_front);
864.     pz <- z_eval(Pfx,t_to_front);
865.     vx <- vx_eval(Pfx,t_to_front);
866.     vz <- vz_eval(Pfx,t_to_front);
867.
868.     px_plate <- x_eval(Pfx,t_to_plate);
869.     pz_plate <- z_eval(Pfx,t_to_plate);
870.
871.     # Find the zone for the pitch at the front of the strike zone
872.     ZONE <- zone_check_2D(px,pz,vx,vz,sz_left_x,sz_right_x,sz_top_z,sz_bottom_z);
873.
874.     # Find the distance to the front of the strike zone
875.     if(ZONE == 1){
876.         dist_sz <- sqrt((sz_left_x - px)^2 + (sz_top_z - pz)^2);
877.     } else if(ZONE == 2){
878.         dist_sz <- abs(sz_top_z - pz);
879.     } else if(ZONE == 3){
880.         dist_sz <- sqrt((sz_right_x - px)^2 + (sz_top_z - pz)^2);
881.     } else if(ZONE == 4){
882.         dist_sz <- abs(sz_left_x - px);
883.     } else if(ZONE == 5){
884.         dist_sz <- 0;
885.     } else if(ZONE == 6){
886.         dist_sz <- abs(sz_right_x - px);
887.     } else if(ZONE == 7){
888.         dist_sz <- sqrt((sz_left_x - px)^2 + (sz_bottom_z - pz)^2);
889.     } else if(ZONE == 8){
890.         dist_sz <- abs(sz_bottom_z - pz);
891.     } else if(ZONE == 9){
892.         dist_sz <- sqrt((sz_right_x - px)^2 + (sz_bottom_z - pz)^2);
893.     }
894.
896.         dist_sz <- 99999;
897.     }
898.
899.     OUT <- list(dist_sz = max(dist_sz-ball_radius,0), zone = ZONE, px = px_plate, pz = pz_plate, vx = vx, vz = vz);
900.     return(OUT);
901. }
902.
903. ###########################
904. # 3D Strike Zone (Tunnel) #
905. ###########################
906.
907. dist_3D_to_SZ_tunnel <- function(Pfx){
908.
909.     # This function finds the minimum distance from the pitch to the strike zone (as a tunnel to the catcher's mitt) in three dimensions
910.     # Pfx is a list containing ax, ay, az, vx0, vy0, vz0, x0, y0, z0, sz_top, and sz_bot
911.
912.     # Set various locations in x, y, and z
913.     sz_front_y <- 17/12;
914.     mitt_y <- -55/24;
915.     ground_z <- 0.125;
916.     sz_left_x <- -17/24;
917.     sz_right_x <- 17/24;
918.     sz_top_z <- Pfx\$sz_top;
919.     sz_bottom_z <- Pfx\$sz_bot;
921.
922.     # Find the time to various distances in y
923.
924.     # STAGE 1: Time to the front of home plate
925.     t_to_front <- (-Pfx\$vy0 - sqrt(Pfx\$vy0^2 - 2*Pfx\$ay*(Pfx\$y0 - sz_front_y)))/Pfx\$ay;
926.
927.     # Time to catcher's mitt
928.     t_to_mitt <- (-Pfx\$vy0 - sqrt(Pfx\$vy0^2 - 2*Pfx\$ay*(Pfx\$y0 - mitt_y)))/Pfx\$ay;
929.
930.     # Time to ball hitting ground
931.     disc_z <- Pfx\$vz0^2 - 2*Pfx\$az*(Pfx\$z0 - ground_z);
932.     if (disc_z > 0){
933.         t_to_ground <- (-Pfx\$vz0 - sqrt(disc_z))/Pfx\$az;
934.     } else {
935.         t_to_ground <- t_to_mitt + 1;
936.     }
937.
938.     # Find the location at the front of the strike zone
939.     px <- x_eval(Pfx,t_to_mitt);
940.     pz <- z_eval(Pfx,t_to_mitt);
941.
942.     # Find the number of stages before the ball hits the ground OR the catcher's mitt
943.     if(t_to_ground > t_to_front){
944.         STAGES <- 2;
945.     } else {
946.         STAGES <- 1;
947.     }
948.
949.     # Allocate space for times when the pitch switches zones
950.     ZONE_TIMES <- array(0,dim=8);
951.
952.     # Left Vertical
953.     disc_LV <- Pfx\$vx0^2 - 2*Pfx\$ax*(Pfx\$x0 - sz_left_x);
954.     if (disc_LV > 0  && Pfx\$ax != 0) {
955.         ZONE_TIMES[1] <- (-Pfx\$vx0 + sqrt(disc_LV))/Pfx\$ax;
956.         ZONE_TIMES[2] <- (-Pfx\$vx0 - sqrt(disc_LV))/Pfx\$ax;
957.     } else if (Pfx\$ax == 0) {
958.         ZONE_TIMES[1] <- -(Pfx\$x0-sz_left_x)/Pfx\$vx0;
959.     }
960.
961.     # Right Vertical
962.     disc_RV <- Pfx\$vx0^2 - 2*Pfx\$ax*(Pfx\$x0 - sz_right_x);
963.     if (disc_RV > 0 && Pfx\$ax != 0) {
964.         ZONE_TIMES[3] <- (-Pfx\$vx0 + sqrt(disc_RV))/Pfx\$ax;
965.         ZONE_TIMES[4] <- (-Pfx\$vx0 - sqrt(disc_RV))/Pfx\$ax;
966.     } else if (Pfx\$ax == 0) {
967.         ZONE_TIMES[3] <- -(Pfx\$x0-sz_right_x)/Pfx\$vx0;
968.     }
969.
970.     # Top Horizontal
971.     disc_TH <- Pfx\$vz0^2 - 2*Pfx\$az*(Pfx\$z0 - sz_top_z);
972.     if (disc_TH > 0 && Pfx\$az != 0) {
973.         ZONE_TIMES[5] <- (-Pfx\$vz0 + sqrt(disc_TH))/Pfx\$az;
974.         ZONE_TIMES[6] <- (-Pfx\$vz0 - sqrt(disc_TH))/Pfx\$az;
975.     } else if (Pfx\$az == 0) {
976.         ZONE_TIMES[5] <- -(Pfx\$z0-sz_top_z)/Pfx\$vz0;
977.     }
978.
979.     # Bottom Horizontal
980.     disc_BH <- Pfx\$vz0^2 - 2*Pfx\$az*(Pfx\$z0 - sz_bottom_z);
981.     if (disc_BH > 0 && Pfx\$az != 0) {
982.         ZONE_TIMES[7] <- (-Pfx\$vz0 + sqrt(disc_BH))/Pfx\$az;
983.         ZONE_TIMES[8] <- (-Pfx\$vz0 - sqrt(disc_BH))/Pfx\$az;
984.     } else if (Pfx\$az == 0) {
985.         ZONE_TIMES[7] <- -(Pfx\$z0-sz_bottom_z)/Pfx\$vz0;
986.     }
987.
988.     # Allocate arrays for moves between the zones
989.     ZONE_MOVE_1 <- array(0,dim=8);
990.     ZONE_MOVE_2 <- array(0,dim=8);
991.
992.     ZONE_TIMES_1 <- array(0,dim=8);
993.     ZONE_TIMES_2 <- array(0,dim=8);
994.
995.     # Check for times within range of 50 feet to the middle of the strike zone
996.     for(i in 1:8){
997.         if(ZONE_TIMES[i] > 0 && ZONE_TIMES[i] < t_to_front) {
998.             ZONE_MOVE_1[i] <- 1;
999.             ZONE_TIMES_1[i] <- ZONE_TIMES[i];
1000.         }
1001.         if(ZONE_TIMES[i] > t_to_front && ZONE_TIMES[i] < t_to_mitt) {
1002.             ZONE_MOVE_2[i] <- 1;
1003.             ZONE_TIMES_2[i] <- ZONE_TIMES[i];
1004.         }
1005.     }
1006.
1007.     # Sort the times for zone switches and their indices
1008.     ZONE_TIMES_1_SORT <- sort(ZONE_TIMES_1,decreasing=FALSE,index.return=TRUE);
1009.     ZONE_TIMES_2_SORT <- sort(ZONE_TIMES_2,decreasing=FALSE,index.return=TRUE);
1010.
1011.     MOVES_1 <- sum(ZONE_MOVE_1);
1012.     MOVES_2 <- sum(ZONE_MOVE_2);
1013.     ZONES_1 <- array(0,dim=(MOVES_1+1));
1014.     ZONES_2 <- array(0,dim=(MOVES_2+1));
1015.     SWITCH_1 <- array(0,dim=(MOVES_1+2));
1016.     SWITCH_2 <- array(0,dim=(MOVES_2+2));
1017.
1018.     # Find the zone in which the pitch starts
1019.     ZONES_1[1] <- zone_check(Pfx,sz_left_x,sz_right_x,sz_top_z,sz_bottom_z);
1020.     SWITCH_1[1] <- 0;
1021.
1022.     k <- 1;
1023.
1024.     for(i in ZONE_TIMES_1_SORT\$ix){
1025.         if(ZONE_MOVE_1[i] == 1){
1026.             k <- k+1;
1027.             if(i <= 4){
1028.                 vx <- vx_eval(Pfx,ZONE_TIMES_1[i]);
1029.                 if(vx > 0){
1030.                     ZONES_1[k] <- ZONES_1[k-1] + 1;
1031.                 } else {
1032.                     ZONES_1[k] <- ZONES_1[k-1] - 1;
1033.                 }
1034.             } else {
1035.                 vz <- vz_eval(Pfx,ZONE_TIMES_1[i]);
1036.                 if(vz > 0){
1037.                     ZONES_1[k] <- ZONES_1[k-1] - 3;
1038.                 } else {
1039.                     ZONES_1[k] <- ZONES_1[k-1] + 3;
1040.                 }
1041.             }
1042.             SWITCH_1[k] <- ZONE_TIMES_1[i];
1043.         }
1044.     }
1045.
1046.     SWITCH_1[MOVES_1+2] <- t_to_front;
1047.
1048.     ZONES_2[1] <- ZONES_1[MOVES_1+1];
1049.     SWITCH_2[1] <- t_to_front;
1050.
1051.     k <- 1;
1052.
1053.     for(i in ZONE_TIMES_2_SORT\$ix){
1054.         if(ZONE_MOVE_2[i] == 1){
1055.             k <- k+1;
1056.             if(i <= 4){
1057.                 vx <- vx_eval(Pfx,ZONE_TIMES_2[i]);
1058.                 if(vx > 0){
1059.                     ZONES_2[k] <- ZONES_2[k-1] + 1;
1060.                 } else {
1061.                     ZONES_2[k] <- ZONES_2[k-1] - 1;
1062.                 }
1063.             } else {
1064.                 vz <- vz_eval(Pfx,ZONE_TIMES_2[i]);
1065.                 if(vz > 0){
1066.                     ZONES_2[k] <- ZONES_2[k-1] - 3;
1067.                 } else {
1068.                     ZONES_2[k] <- ZONES_2[k-1] + 3;
1069.                 }
1070.             }
1071.             SWITCH_2[k] <- ZONE_TIMES_2[i];
1072.         }
1073.     }
1074.
1075.     SWITCH_2[MOVES_2+2] <- t_to_mitt;
1076.
1077.     MOVE_CTR <- 1;
1078.     dist_sz <- 55;
1079.
1080.     # Find the distances over STAGE 1
1081.     while(MOVE_CTR <= (MOVES_1+1) && SWITCH_1[MOVE_CTR] < t_to_ground){
1082.         t_0 <- SWITCH_1[MOVE_CTR];
1083.         t_1 <- min(SWITCH_1[MOVE_CTR+1],t_to_ground);
1084.         if(ZONES_1[MOVE_CTR] == 1){
1085.             dist_sz_temp <- min_corner(Pfx,sz_left_x,sz_front_y,sz_top_z,t_0,t_1);
1086.         } else if(ZONES_1[MOVE_CTR] == 2){
1087.             dist_sz_temp <- min_edge_yz(Pfx,sz_front_y,sz_top_z,t_0,t_1);
1088.         } else if(ZONES_1[MOVE_CTR] == 3){
1089.             dist_sz_temp <- min_corner(Pfx,sz_right_x,sz_front_y,sz_top_z,t_0,t_1);
1090.         } else if(ZONES_1[MOVE_CTR] == 4){
1091.             dist_sz_temp <- min_edge_xy(Pfx,sz_left_x,sz_front_y,t_0,t_1);
1092.         } else if(ZONES_1[MOVE_CTR] == 5){
1093.             dist_sz_temp <- min_face_y(Pfx,sz_front_y,t_0,t_1);
1094.         } else if(ZONES_1[MOVE_CTR] == 6){
1095.             dist_sz_temp <- min_edge_xy(Pfx,sz_right_x,sz_front_y,t_0,t_1);
1096.         } else if(ZONES_1[MOVE_CTR] == 7){
1097.             dist_sz_temp <- min_corner(Pfx,sz_left_x,sz_front_y,sz_bottom_z,t_0,t_1);
1098.         } else if(ZONES_1[MOVE_CTR] == 8){
1099.             dist_sz_temp <- min_edge_yz(Pfx,sz_front_y,sz_bottom_z,t_0,t_1);
1100.         } else if(ZONES_1[MOVE_CTR] == 9){
1101.             dist_sz_temp <- min_corner(Pfx,sz_right_x,sz_front_y,sz_bottom_z,t_0,t_1);
1102.         }
1103.         MOVE_CTR <- MOVE_CTR + 1;
1104.         if (dist_sz_temp < dist_sz){
1105.             dist_sz <- dist_sz_temp;
1106.             stage <- 1;
1107.             zone <- ZONES_1[MOVE_CTR-1];
1108.         }
1110.             OUT <- list(dist_sz = max(0,dist_sz-ball_radius), stage = 1, zone = zone, px = px, pz = pz);
1111.             return(OUT);
1112.         }
1113.     }
1114.
1115.     # Find the distances over STAGE 2
1116.     if (STAGES > 1){
1117.         MOVE_CTR <- 1;
1118.         while(MOVE_CTR <= (MOVES_2+1) && SWITCH_2[MOVE_CTR] < t_to_ground){
1119.             t_1 <- SWITCH_2[MOVE_CTR];
1120.             t_2 <- min(SWITCH_2[MOVE_CTR+1],t_to_ground);
1121.             if(ZONES_2[MOVE_CTR] == 1){
1122.                 dist_sz_temp <- min_edge_xz(Pfx,sz_left_x,sz_top_z,t_1,t_2);
1123.             } else if(ZONES_2[MOVE_CTR] == 2){
1124.                 dist_sz_temp <- min_face_z(Pfx,sz_top_z,t_1,t_2);
1125.             } else if(ZONES_2[MOVE_CTR] == 3){
1126.                 dist_sz_temp <- min_edge_xz(Pfx,sz_right_x,sz_top_z,t_1,t_2);
1127.             } else if(ZONES_2[MOVE_CTR] == 4){
1128.                 dist_sz_temp <- min_face_x(Pfx,sz_left_x,t_1,t_2);
1129.             } else if(ZONES_2[MOVE_CTR] == 5){
1130.                 dist_sz_temp <- 0;
1131.             } else if(ZONES_2[MOVE_CTR] == 6){
1132.                 dist_sz_temp <- min_face_x(Pfx,sz_right_x,t_1,t_2);
1133.             } else if(ZONES_2[MOVE_CTR] == 7){
1134.                 dist_sz_temp <- min_edge_xz(Pfx,sz_left_x,sz_bottom_z,t_1,t_2);
1135.             } else if(ZONES_2[MOVE_CTR] == 8){
1136.                 dist_sz_temp <- min_face_z(Pfx,sz_bottom_z,t_1,t_2);
1137.             } else if(ZONES_2[MOVE_CTR] == 9){
1138.                 dist_sz_temp <- min_edge_xz(Pfx,sz_right_x,sz_bottom_z,t_1,t_2);
1139.             }
1140.             MOVE_CTR <- MOVE_CTR + 1;
1141.             if (dist_sz_temp < dist_sz){
1142.                 dist_sz <- dist_sz_temp;
1143.                 stage <- 2;
1144.                 zone <- ZONES_2[MOVE_CTR-1];
1145.             }