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]

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

[gravatar]

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

[gravatar]

@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]

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]

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]

"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]

row, col is another one!

[gravatar]

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]

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]

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]

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

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

[gravatar]

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]

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]

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>.