Just for fun, let's draw a tennis court. Using dimensions from Wikipedia:
Tennis is played on a rectangular flat surface, usually of grass, clay, concrete (hard court) or a synthetic suspended court. The court is 23.78 meters (78 feet) long, 10.97 meters (36 feet) wide. Its width is 8.23 meters (27 feet) for singles matches and 10.97 meters (36 feet) for doubles matches. The service line is 6.40 meters (21 feet) from the net. Additional clear space around the court is needed in order for players to reach overrun balls for a total of 18.3 meters (60 feet) wide and 36.7 meters (120 feet) long. A net is stretched across the full width of the court, parallel with the baselines, dividing it into two equal ends. The net is 1.07 meters (3 feet 6 inches) high at the posts, and 0.914 meters (3 feet) high in the center.
This little exercise really drove home D3's data joins.
Every line on the court is an SVG line
element. I started off writing each line individually, but that meant writing a lot of code twice.
Then I thought about using loops, but JavaScript's loops are cumbersome, and it didn't feel right anyway. Lines on the court are predictable, so I should be able to describe them as data arrays, right?
Yup. Each kind of line became a selection. For example:
// baselines
court.selectAll('line.baseline')
.data([0, ch / 2, ch])
.enter().append('line')
.attr('class', 'baseline')
.attr('x1', 0)
.attr('x2', x(cw))
.attr('y1', y)
.attr('y2', y);
For baselines (plus the net), each element in the data array is the Y position, or the distance from one end of the court (ch
is court height, cw
is court width). Each array item is joined to an existing or just-created line. The rest is just description. Rinse and repeat for sidelines, service boxes and center marks.
I used D3's linear scales to convert real distance to pixels, so I could think in feet and let D3 do the math.
At some point, I'd love to find some data on shot placement -- where Roger Federer puts his serve, for example.