Okay, that so called “triangle” really isn’t. When you take into account clearing for boasts it, in fact, becomes an irregular pentagon. I wanted to see what that would look like in an isometric view.

The first thing I had to sort was the reflection points for the boasts. I wanted the point where, given angle of incidence equals angle of reflection, the ball would hit one of the front wall nicks. Any reflection point closer to the front wall would definitely hit the front wall (well if high enough). Any closer to the back wall would not likely make it directly to the front wall (I am going to ignore the spin of the ball on or after impact).

Boast Reflection Points

I didn’t want to start messing around in my isometric modules, so I started a new one to sort out where those points would be given any ball location when being struck. Specifically, angle_incidence.py.

Now the way to determine the reflection point, is to reflect the ball position on the opposite side of the target wall. Then draw a line from that point to the target front wall nick. Where that line intersects the target wall is the proper reflection point. Something like this. Do note that some of those imports may not be used, I just copied the lot from one of the isometric modules.

import math
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
from matplotlib import patches
import numpy as np  

fig_wd, fig_ht = 8.0, 6.4

fig, ax = plt.subplots(figsize=(fig_wd, fig_ht), frameon=False)

# ball
p_ball = [8, 15, 8.5]

ax.set_aspect('equal')

ax.plot([0, 25], [0, 0], c='k')
ax.plot([25, 25], [0, 45], c='k')
ax.plot([25, 0], [45, 45], c='k')
ax.plot([0, 0], [45, 0], c='k')

ax.scatter(p_ball[0], p_ball[1], c='b')

ax.scatter(-p_ball[0], p_ball[1], c='b')
ax.plot([-p_ball[0], 25], [p_ball[1], 45], c='k')

plt.show()
figure showing how to find reflection point on left wall for boast intended to hit the front right nick
Find Reflection Point for Boast to Front Right Nick

As you can see, in this case that reflection point is the y-intercept of the equation for the line running from the reflected ball to the front right nick. And, I have previously written a function that takes two points and returns the slope and intercept for the equation of the line between them.

def get_ln_parameters(x1, y1, x2, y2):
  slp = (y1 - y2) / (x1 - x2)
  y_int = y1 - (x1 * slp)
  return slp, y_int

# jump to relevant new code
# get line equation parameters and plot reflection point
rc_slp, rc_int = get_ln_parameters(-p_ball[0], p_ball[1], 25, 45)
ax.scatter(0, rc_int, c='b')
# plot line from ball to reflection point
ax.plot([p_ball[0], 0], [p_ball[1], rc_int], c='k', alpha=0.5, ls='--')

I changed the line style and opacity so that it is easier to see the plotted point at the reflection point. And have added a line from the ball to that point so that you can perhaps see that the angles in and out are the same. Strangly enough, in my test case, those angles are very close to 45°. At 45° the slope would be 1.0.

slope: 0.9090909090909091, intercept: 22.272727272727273
figure showing the reflection point on left wall for boast intended to hit the front right nick
Reflection Point for Boast to Front Right Nick

Now a boast off the right wall to the front left nick. In this case we will need the relevant line equation to determine the reflection point. Still truly simple

ax.text(2, rc_int, f"(0, {rc_int:.2f})")

rx_ball = 25 + (25 - p_ball[0])
ax.scatter(rx_ball, p_ball[1], c='b')
ax.plot([rx_ball, 0], [p_ball[1], 45], c='k', alpha=0.5, ls="--")

lc_slp, lc_int = get_ln_parameters(rx_ball, p_ball[1], 0, 45)
r_pt = (lc_slp * 25) + lc_int
ax.scatter(25, r_pt, c='b')

ax.plot([p_ball[0], 25], [p_ball[1], r_pt], c='k', alpha=0.5, ls='--')

ax.text(15, r_pt, f"(25, {r_pt:.2f})")
figure showing the reflection point on right wall for boast intended to hit the front left nick
Reflection Point for Boast to Front Left Nick

Now, because I want to be able to use the above in my drawings for any ball location, I am going to put it in a function, get_reflect(b_pos). And, as most of the plotting was just for development and this post, that will all disappear. It will return the y-value for the two reflection points, (left wall, right wall).

def get_reflect(b_pos):
  rc_slp, rc_int = get_ln_parameters(-b_pos[0], b_pos[1], 25, 45)
  rx_ball = 25 + (25 - b_pos[0])
  lc_slp, lc_int = get_ln_parameters(rx_ball, b_pos[1], 0, 45)
  rr_pt = (lc_slp * 25) + lc_int
  return (rc_int, rr_pt)

And let’s test that it works. To get a clearer picture of ball position, I am adding some of the court lines. As well as lines and dimensions specifying the ball’s position.

... ... 

# let's just show the court
for spine in ax.spines.values():
  spine.set_visible(False)
ax.tick_params(bottom=False, labelbottom=False, left=False, labelleft=False)

... ...

rl, rr = get_reflect(p_ball[0:2])

ax.plot([p_ball[0], 0], [p_ball[1], rl], c='b', alpha=0.5, ls='--')
ax.plot([0, 25], [rl, 45], c='b', alpha=0.5, ls='--')
ax.text(2, rc_int, f"(0, {rl:.2f})")

... ...

ax.plot([0, 25], [15, 15], c='r')
ax.plot([12.5, 12.5], [15, 0], c='r')

l_center = (0, 15)
r_center = (25, 15)
a_wd = 4.5
strt = -90
fini = 0
e1 = patches.Arc(l_center, a_wd, a_wd, angle=0, theta1=strt, theta2=fini, color='r')
ax.add_patch(e1)
strt = 180
fini = 270
e2 = patches.Arc(r_center, a_wd, a_wd, angle=0, theta1=strt, theta2=fini, color='r')
ax.add_patch(e2)

ax.plot([20, 20], [p_ball[1], 0], c='k', ls=':')
ax.plot([20, 0], [p_ball[1], p_ball[1]], c='k', ls=':')
ax.text(p_ball[0]/3, p_ball[1]-1.5, f"{p_ball[0]}'")
ax.text(p_ball[0]+0.5, p_ball[1]/2, f"{p_ball[1]}'")
figure showing the reflection points generated when testing the new function
Reflection Points for Boasts as Returned by Function

And the numbers and look match the previous image. Just for fun, let’s try a different ball location.

figure showing the reflection points for ball 5 feet from right side and 10 from back wall
Reflection Points for Boasts for Ball at Position Shown

Done

I was planning to look at the irregualar pentagonal plane in this post. But, I think this post is plenty long enough. Though it was a rather easy coding job. The bulk of the code I wrote was to generate the images for this post. That said, t’was a good bit of fun.

Until next time, may your code come easily and the bugs seldomly.