Advertisement
matthewrmata

Strike Zone Minimum Distance Algorithms

Nov 1st, 2015
176
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
R 35.44 KB | None | 0 0
  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;
  51.     ball_radius <- 0.125;
  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.         }
  373.         if (dist_sz < ball_radius){
  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.             }
  410.             if (dist_sz < ball_radius){
  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.             }
  486.             if (dist_sz < ball_radius){
  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;
  516.     ball_radius <- 0.125;
  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.         }
  752.         if (dist_sz < ball_radius){
  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.             }
  789.             if (dist_sz < ball_radius){
  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.             }
  827.             if (dist_sz < ball_radius){
  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;
  854.     ball_radius <- 0.125;
  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.    
  895.     if (pz < ball_radius){
  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;
  920.     ball_radius <- 0.125;
  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.         }
  1109.         if (dist_sz < ball_radius){
  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.             }
  1146.             if (dist_sz < ball_radius){
  1147.                 OUT <- list(dist_sz = max(0,dist_sz-ball_radius), stage = 2, zone = zone, px = px, pz = pz);
  1148.             return(OUT);
  1149.             }
  1150.         }
  1151.     }
  1152.    
  1153.     OUT <- list(dist_sz = max(0,dist_sz-ball_radius), stage = stage, zone = zone, px = px, pz = pz);
  1154.     return(OUT);
  1155. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement