Things that bug me about two-dimensional programming

Monday 13 February 2006

I’m doing a lot of coding these days involving XY coordinates, and there’s a handful of little annoyances. They’re no one’s fault, I just want to vent.

First, it’s natural to say “x and y”, and it’s natural to say “height and width”, but x corresponds to width, and y to height, so I often make mistakes that switch the two:

ht, wd = foox, fooy   # This is wrong.

The same goes for loops over x and y. The natural order to visit the points in a grid is the raster order: finish a row, then go on to the next row. But that means having the first loop be over y rather than x:

for y in range(lowy, hiy):
    for x in range(lowx, hix):
        do_something(x, y)

For this last, there’s a solution: create a generator that makes x,y pairs in a single loop:

def xyrange(startx, endx, starty, endy):
    """ Generate the pairs (x, y) in a rectangle.
    """
    for y in range(starty, endy):
        for x in range(startx, endx):
            yield x,y

Then this function is the only place that needs the inside-out y x ugliness, and you can use a single loop everywhere else:

for x, y in xyrange(lowx, hix, lowy, hiy):
    do_something(x, y)

This has the advantage that you can break out of the loop cleanly when you find a point you are looking for. It has the disadvantage that you can’t do an action at the end of each row.

Update: Richard Schwartz noticed that I originally had said,

First, it’s natural to say “x and y”, and it’s natural to say “height and width”, but x corresponds to height, and y to width, so I often make mistakes that switch the two.

which makes that sentence itself an error of the sort it describes, making it an unintentionally self-referential sentence!

Comments

[gravatar]
Pete Lyons 8:34 AM on 13 Feb 2006

Y is High-t, I repeat this little mantra all the time when working with coordinates.

[gravatar]
andrew 8:54 AM on 13 Feb 2006

I know nothing about py, but can you pass in an optional callback (pointer, delegate, address) that will get called after each row?

[gravatar]
Bill Mill 9:33 AM on 13 Feb 2006

@andrew: yes, you certainly could allow the generator to take an optional function reference if you wanted to. It even seems to me like it wouldn't be that ugly. Just call it end_of_row or something descriptive...

[gravatar]
Ned Batchelder 10:42 AM on 13 Feb 2006

You could, but likely the thing you want to do at the end of row involves your local variables, and now you have to package them up, and the whole thing becomes more cumbersome than just putting in two loops.

Or, in the loop, you could check if x is equal to lowx, and then do beginning of row processing...

[gravatar]
Damien Katz 11:48 AM on 13 Feb 2006

Couldn't you return a third value in the tuple that's a flag to indicate last row?

[gravatar]
Michael Chermside 1:55 PM on 13 Feb 2006

Interesting. I never say "height and width", always "width and height". And I specifically recall being fairly young and wondering which was the natural order of these. I compared to "x and y" and went based on that.

[gravatar]
Marius Gedminas 2:29 PM on 13 Feb 2006

"Width, height" is the natural order to me as well. On the other hand, I say "row, column", which makes it easy to think about indexing two-dimensional arrays (e.g., map[row][col]).

[gravatar]
Ned Batchelder 3:01 PM on 13 Feb 2006

row, col is another one!

[gravatar]
Ayende Rahien 4:13 PM on 13 Feb 2006

def xyrange(startx, endx, starty, endy):
""" Generate the pairs (x, y) in a rectangle.
"""
for y in range(starty, endy):
for x in range(startx, endx):
yield x,y, x == endx

That should give you the ability to check for the end of line

[gravatar]
Alexander Belchenko 4:18 PM on 13 Feb 2006

In my last project where I need to draw on two types of displays (normal and rotated on 90 degrees) I leave width and height and start to using sizex and sizey.

[gravatar]
James 6:44 PM on 13 Feb 2006

From the title I thought you would propose a way to program in 3d. Using 3d goggles I suppose or perhaps a holographic projector in order to do your programming. However, I don't even want visualize a 3d diff file. :)

[gravatar]
Jonathon Duerig 11:20 PM on 13 Feb 2006

My first thought is that you were going to discuss Befunge, where the language is two-dimensional:

http://esoteric.voxelperfect.net/wiki/Befunge

[gravatar]
Shane Hathaway 3:40 PM on 15 Feb 2006

What about the fact that the Y axis is upside down? That's the main thing that mixes me up. In math, graphs are drawn with Y increasing as you move upward. But in computer graphics, Y increases as you move downward. (I take this as evidence that computer scientists don't always listen to mathematicians.)

Also, precisely what part of a pixel do does a coordinate refer to? When you say (0,0), does that mean the center of the top left pixel or the top left of the top left pixel? This matters if the lines are antialiased.

[gravatar]
Haukur Hreinsson 9:44 PM on 18 Feb 2006

Ah, the pixel thing. Didn't you get the memo?

ftp://ftp.alvyray.com/Acrobat/6_Pixel.pdf

I have an additional 2D programming annoyance. You tend to write two lines where one does something with say x, width, and cos, and the other with y, height, and sin. Then there may be a few things that go in both lines. And of course you write the second by copying and modifying the first. The fun starts when you forget one of the required changes. And even if you don't, it still feels wrong.

I get way too hung up on stuff like that.

[gravatar]
Ed Davies 8:03 AM on 15 Jun 2006

Try doing graphics for geographical positions (i.e., maps). Latitude and longitude but easting and northing.

Add a comment:

Ignore this:
Leave this empty:
Name is required. Either email or web are required. Email won't be displayed and I won't spam you. Your web site won't be indexed by search engines.
Don't put anything here:
Leave this empty:
URLs auto-link and some tags are allowed: <a><b><i><p><br><pre>.