off = offense("gun_2x2")
off = [pl.moved(w=-17.0) if pl.key == "X" else pl.moved(w=17.0) if pl.key == "Z" else pl for pl in off]
off = [pl.moved(d=-5.0, w=1.5) if pl.key == "RB" else pl for pl in off]
hmd = [Player(k, pos, "def", d, w, label=lab) for k, pos, d, w, lab in [
("E1", "DE", 1.0, -3.5, None), ("N", "NT", 1.0, 0.5, None), ("E2", "DE", 1.0, 4.0, None),
("W", "WILL", 12.0, -8.0, None), ("M", "MIKE", 12.0, 0.0, None), ("D", "DB", 12.0, 8.0, None),
("C1", "CB", 18.0, -15.0, None), ("C2", "CB", 18.0, 15.0, None),
("S1", "FS", 36.0, -5.0, None), ("S2", "SS", 36.0, 5.0, None), ("G", "NB", 40.0, 0.0, "$")]]
hmp = Play(off, hmd, los=62, to_go=None)
hmp.dropback(3.0)
SPOT = 50.0 # two yards deep in the end zone (the goal line is 48 yards away)
T_THROW = 4.3
# receivers: the diamond around the jumper (absolute (d, w) from the line of scrimmage)
for key, end, spd in [("Y", (SPOT, 0.5), 8.8), # jumper
("H", (SPOT - 3.0, 0.0), 8.6), # front tipper
("Z", (SPOT + 3.0, 0.5), 9.2), # back tipper / trailer
("X", (SPOT - 1.5, -3.0), 9.0), # side
("RB", (SPOT - 1.5, 3.5), 8.8)]: # side
hmp.route(key, [(end[0] - 2.0, end[1] * 1.3), end], absolute=True, speed=spd)
# defense: DBs at or above the jump point, a deeper goalie, underneath players to the front of the pile
for key, to, delay, spd in [("S1", (SPOT + 1.0, -1.6), 0.4, 4.5), ("S2", (SPOT + 1.0, 2.4), 0.4, 4.5),
("C1", (SPOT + 2.0, -4.6), 0.0, 7.0), ("C2", (SPOT + 1.5, 5.2), 0.0, 7.0),
("G", (SPOT + 5.5, 0.0), 0.6, 4.5), ("W", (SPOT - 4.0, -3.5), 0.2, 6.6),
("M", (SPOT - 5.5, 0.5), 0.2, 6.6), ("D", (SPOT - 4.6, 4.4), 0.2, 6.6)]:
hmp.drop(key, to, delay=delay, speed=spd)
for key in ("E1", "N", "E2"):
hmp.rush(key, speed=3.6)
hmp.pass_to("Y", t=T_THROW)
# when does the ball arrive? (first moment it reaches the jumper)
_ts = np.arange(T_THROW, T_THROW + 6.0, 0.05)
T_ARR = float(next(t for t in _ts if np.linalg.norm(hmp.ball_position(t) - hmp.position("Y", t)) < 0.6))
if PDF:
# print: the whole play at the snap (left), then two close-ups of the landing spot (right)
from gridiron import draw_frame
trk = hmp.to_tracking(t_after=0.8)
trk["_id"] = trk.displayName
tmap = trk.groupby("frameId")["time"].first()
fig = plt.figure(figsize=(6.6, 5.3))
axL = fig.add_axes([0.0, 0.0, 0.45, 0.93])
no_numbers(hmp.draw(ax=axL, window=(-10, 60), lateral=(-19.5, 19.5), title=False))
axL.set_title("The plan, at the snap", fontsize=8.5, color=style.INK, loc="left")
for t, (x0, y0), ttl in [(T_ARR - 0.6, (0.47, 0.5), f"+{T_ARR - 0.6:.1f} s: the crowd sets up, ball coming down"),
(T_ARR, (0.47, 0.0), f"+{T_ARR:.1f} s: the ball arrives at the jumper")]:
axr = fig.add_axes([x0, y0, 0.53, 0.43])
fr = Field(ax=axr, orient="vertical", los=hmp.los, window=(38, 60), lateral=(hmp.ball_y - 12.5, hmp.ball_y + 12.5))
no_numbers(fr)
fid = int((tmap - t).abs().idxmin())
near = trk[trk.x >= hmp.los + 39.0] # only what is inside the close-up
draw_frame(fr, near[near.frameId == fid], near[(near.frameId <= fid) & (near.frameId >= fid - 10)],
fr.pts_per_yard())
# the ball: where it is now, with a dotted trace of the last half-second of its flight
trace = [tuple(map(float, fr.xy(*hmp.to_abs(*hmp.ball_position(tt))))) for tt in np.linspace(t - 0.5, t, 6)
if hmp.ball_position(tt)[0] >= 38.5]
if len(trace) > 1:
axr.plot(*zip(*trace), color=style.BALL, lw=1.2, ls=(0, (1.5, 1.5)), zorder=11)
bd, bw = hmp.ball_position(t)
if t == T_ARR:
bd, bw = bd + 1.1, bw + 0.9 # at the jumper's raised hands, so his letter stays readable
bx, by = map(float, fr.xy(*hmp.to_abs(bd, bw)))
axr.add_patch(Ellipse((bx, by), 0.75, 1.15, fc=style.BALL, ec="white", lw=0.8, zorder=12))
if t == T_ARR:
for (d_, w_), (pd0, pw0), txt in [((SPOT, -8.6), (SPOT, -0.5), "jumper (Y)"),
((SPOT + 5.5, -8.6), (SPOT + 5.5, -0.9), "goalie ($)"),
((SPOT + 3.0, 8.6), (SPOT + 3.0, 1.4), "back tipper (Z)"),
((SPOT - 3.0, 8.6), (SPOT - 3.0, 1.0), "front tipper (H)")]:
xx, yy = map(float, fr.xy(*hmp.to_abs(d_, w_)))
lx, ly = map(float, fr.xy(*hmp.to_abs(pd0, pw0)))
axr.plot([xx, lx], [yy, ly], color=style.MUTED, lw=0.6, zorder=8)
axr.text(xx, yy, txt, fontsize=6.2, color=style.INK_2, ha="center", va="center", zorder=9,
bbox=dict(boxstyle="round,pad=0.15", fc=style.SURFACE, ec="none", alpha=0.95))
axr.set_title(ttl, fontsize=8, color=style.INK, loc="left")
plt.show()
else:
show_play(hmp, times=[0.0, 2.5, T_THROW, T_ARR])