I am helping a friend with a blog regarding hardball doubles squash, its rules and the like.

For a variety of reasons I wanted to see if I could produce an 3-D isometric view of the court along with a number of other details. It took me quite some time; nearly driving me nuts. But the process was in the end entertaining and prehaps educational. So, I thought I’d use the endeavour as the subject of one of my own blog posts. (I don’t currently expect the isometric images to ever be used on my friends blog.)

I initially thought I would find an example of something similar in SVG. Then handcode an SVG image of what I wanted based on that SVG’s markup. But fairly quickly gave up on that idea. Decided to see if I could do it using Python and matplotlib.

Isometric Calculations

It was hard to find good explanations of how to convert 3-D points to those in a 2-D isometric image. Most articles suggested I use an isometric grid to draw what I wanted. Others suggested drawing apps that I could perhaps use. Not much help.

I found one that discussed the transformations involved and showed the transformation matrix for one particular view. Tried to do the matrix multiplication but got nowhere (I may now know why, having recalled a similar problem when trying to rotate my spirograph images. Will need to check that out.)

$$ \begin{bmatrix}\mathbf {c} _{x}\\\mathbf {c} _{y}\\\mathbf {c} _{z}\\\end{bmatrix} = \begin{bmatrix}1&0&0\\0&{\cos \alpha }&{\sin \alpha }\\0&{-\sin \alpha }&{\cos \alpha }\\\end{bmatrix} \begin{bmatrix}{\cos \beta }&0&{-\sin \beta }\\0&1&0\\{\sin \beta }&0&{\cos \beta }\\\end{bmatrix} \begin{bmatrix}\mathbf {a} _{x}\\\mathbf {a} _{y}\\\mathbf {a} _{z}\\\end{bmatrix} $$

I was trying \(\alpha = 30° \mathsf{\text{and }} \beta = 120°\).

I eventually found a post on stackoverflow that got me started. As I write this, I am realizing that what I got in that post is just what I was trying using the above array transforms. Anyway, I decided to go with the following from the post.

$$u = x*\cos(\alpha) + y*\cos(\alpha+120°) + z*\cos(\alpha-120°)$$ $$v = x*\sin(\alpha) + y*\sin(\alpha+120°) + z*\sin(\alpha-120°)$$ $$\mathsf{\text{where }} \alpha = 30 $$

The Floor

The floor of a hardball doubles squash court is 25 feet wide by 45 feet long.

I figured I should start with an isometric version of the floor. I basically put the back left corner at (0, 0, 0) and the front right corner at (25, 50, 0). We will simply be doing a rotation transform (well maybe a skew transform) on the floor in the xy plane.

Started a new module, isometric.py. I wrote a small function to generate the x and y values. Then I created an array of all the values for the floor. And, ran all of those through the function to generate a new array of the transformed values.

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

iso_alpha = 30
iso_beta = 120


def toIsometric2D(x, y, z, alpha=iso_alpha, beta=iso_beta):
    # u = (x - z) / (2**.5)
    # v=(x + 2 * y + z) / (6**.5)
    r_alpha = math.radians(alpha)
    r_beta = math.radians(beta)
    u = x*math.cos(r_alpha) + y*math.cos(r_alpha + r_beta) + z*math.cos(r_alpha - r_beta)
    v = x*math.sin(r_alpha) + y*math.sin(r_alpha + r_beta) + z*math.sin(r_alpha - r_beta)
    return (u, v)

# floor: (0, 0, 0) -> (25, 0, 0) -> (25, 45, 0) -> (0, 45, 0)
crt_points = [[0, 0, 0], [25, 0, 0], [25, 45, 0], [0, 45, 0]]

iso_1 = []
for c_pt in crt_points:
    tmp = toIsometric2D(*c_pt, _iso_angle)
    iso_1.append(tmp)

fig, ax = plt.subplots(figsize=(8.0, 6.4), frameon=False)
for spine in ax.spines.values():
  spine.set_visible(False)
ax.tick_params(bottom=False, labelbottom=False, left=False, labelleft=False)

# floor
for i in range(4):
   ln_to = (i + 1) % 4
   ax.plot([iso_1[i][0], iso_1[ln_to][0]], [iso_1[i][1], iso_1[ln_to][1]], c='k')

plot.show()

And after executing the module, I got the following. Which seems to look like what I was expecting.

isometric view of the floor of a hardball doubles court
Isometric view of the floor of a hardball doubles court

Right Wall

Okay, let’s look at adding some 3-D to this 2-D isometric image. I’ll start with the right wall. Note: it is recommended the walls of the court be a minimum of 24 feet high. The actual playable area will be less. But the respective committee wants to make sure it is possible to execute high lobs to the back of the court.

crt_points = [[0, 0, 0], [25, 0, 0], [25, 45, 0], [0, 45, 0],
              [25, 0, 0], [25, 0, 24], [25, 45, 24], [25, 45, 0]
             ]

... ...

# side wall
for i in range(4, 7):
  ln_to = ((i + 1) % 4) + 4
  ax.plot([iso_1[i][0], iso_1[ln_to][0]], [iso_1[i][1], iso_1[ln_to][1]], c='g')

I am painting the side wall in green. You will see why below.

attempt at isometric view of the floor and right side wall of a hardball doubles court
Attempt to add right side wall to isometric view

Well, not exactly want we want. The wall is going downwards.

I started looking at calculating the wall points manually. Got it done for the right wall. Then started thinking a bit. If the wall is going downwards, perhaps I need to change something in the formula I am using to get the transformed y value. Since it was the 2-D representation of the z value that was wrong, I decided to change
\(v = x*\sin(\alpha) + y*\sin(\alpha+120°) + z*\sin(\alpha-120°)\)
to
\(v = x*\sin(\alpha) + y*\sin(\alpha+120°) - z*\sin(\alpha-120°)\).

I made the change to the function. And ran the module again.

2nd attempt at isometric view of the floor and right side wall of a hardball doubles court
2nd attempt to add right side wall to isometric view

Visually the ratio of the height of the wall (24 feet) to the floor width (25 feet) looks correct. I will change the wall plot colour back to black.

And, let’s quickly add the front wall.

crt_points = [[0, 0, 0], [25, 0, 0], [25, 45, 0], [0, 45, 0],
              [25, 0, 0], [25, 0, 24], [25, 45, 24], [25, 45, 0],
              [25, 45, 0], [25, 45, 24], [0, 45, 24], [0, 45, 0]
             ]
isometric view of the floor and walls of a hardball doubles court
Isometric view of the floor and walls of a hardball doubles court

That looks like progress.

Done

I still have to add the various in/out lines, the service lines, etc. to the image. But this post seems to me to be a nice length, so I think I will call it a day. I will add those lines so critical to the rules of the game in the next post.

Until then, I hope you can find something of interest to try programming.

Resources