LoginLogin
Might make SBS readonly: thread

[SB3] Subtlety With The Coordinate System (Related to how GBOX uses X1, Y1, X2, Y2 rather than X Y W H)

Root / Programming Questions / [.]

randoCreated:
Hey all! Lately I’ve been a little confused about something that really should be simple, something that should probably send me back to like a basic geometry class or something. I’m simply trying to draw and do math with a rectangle, defined by an X, Y, width and height. When I draw a rectangle at any point with a width/height of 16, the drawn rectangle ends up being 17x17. To combat this in drawing, I just subtracted 1 from the end coordinates. For example, this is how my code would’ve changed:
GBOX X, Y, X + W, Y + H
GBOX X, Y, X + W - 1, Y + H - 1
I did this because the old version seems to count the starting pixel as part of the rectangle, but not as part of the size of the rectangle. This worked to solve the graphics side of things, however when I started adding math the problem was no longer completely solved because it seems like in any arithmetic operation I’d need to subtract 1 from the size again. Otherwise, whatever I’m doing with the rectangle in terms of physics would not match up exactly with what the rectangle looks like on screen. There seems to be a simple solution: in my function to define a rectangle I would just push on the input size minus 1, rather than the input size unchanged. In that way, I would never have to subtract 1 from the size to get my desired results. However, I don’t think this would account for the whole picture. I also want to do math that will inevitably use half of the rectangle’s size, because for example I will want to find the center point of this rectangle. My intended size for this particular rectangle is 16x16, and subtracting 1 from the input of 16 causes the system to actually draw a 16x16 rectangle. But half of 15, the newly stored size, is different from half of 16, so if I were to preemptively subtract 1 for the sake of drawing functions and addition/subtraction, I would have to add 1 back in for multiplication/division. Maybe this is something to do with how SmileBASIC works, or perhaps more likely this is a subtlety with math that I don’t fully understand yet. In either case, I’m looking for more insight. What way does the math actually work? What would be the correct action to take in order to make a realistic physics system while also keeping a safe distance from any spaghetti code? Right now I’m thinking I’ll just subtract 1 from the size any time I’m using purely addition or subtraction, so multiplication and division don’t get muddy. Is that a smart way to go, and does that properly capture the bigger picture of what’s going on?

When it comes to creating a physics engine, you definitely want to store the W & H as an intrinsic property of the object, as well as position X & Y, and derive the screen projected coordinates from those. It's a little odd to think about, but you're subtracting 1 from W/H for the same reason you subtract 1 from the length of an array in a for loop: zero indexing. On calculating the center point, I think some confusion comes from the fact that you're trying to find a pixel at the center of an image with even side lengths. There isn't one, so you have to choose between rounding up (to offset 8) or down (to offset 7). The true center of the 16x16 is at offset 7.5. It's a little clearer on, say, a 9x9 rect where the center pixel is. Also when projecting fp objects it's important to note that SB will always truncate to an integer. If the object starts with an offset 0.99 and moves to offset -0.99, the distance traveled is nearly 2 pixels, but the position is truncated towards integer offset 0 in both cases. Basically switching between floor() and ceil() depending on whether you are positive or negative. So you should always apply rounding yourself to the coordinates before passing to graphics commands. Usually FLOOR() which complements the zero-index coordinate system. So I think the main lesson is that you subtract 1 from W/H because that's how you convert from a length to an offset. Think about how you'd calculate the endpoints of a 1x1 object, how treating the W/H as offsets nets you a 2x2 object instead.

I think the basic confusion you're having comes from this: you're thinking of these coordinates as points instead of pixels. For example, if you were to plot the points (0, 0), (0, 1), (1, 0), (1, 1) on graph paper, they would form a square that's 1 unit wide and 1 unit tall. However, a pixel isn't a point, it's more like filling in an entire square on your graph paper. In other words, a single pixel is already 1 unit wide and 1 unit tall. So, when you do `GBOX 0, 0, 1, 1`, you're filling in the pixels at (0, 0), (0, 1), (1, 0), (1, 1). That's two pixels across, and two pixels down, so your square is 2 units wide and 2 units tall.

I think these thoughts make a lot of sense. Here’s kind of what I’ve come up with in my brain and how I’m thinking about it. I am in fact thinking about these numbers as points, rather than entire pixels. So the way I do the math, each point is located at the top left corner of that pixel on the screen if that makes sense. However, of course drawing won’t match with what the math does because it will draw all the way from that corner to the bottom right corner of the pixel. I’ve also since discovered that arithmetic operations do follow the same patterns as multiplication and division where I don’t have to subtract 1 (which will actually cause issues), not the same as what the graphics functions are doing. I just had a slight issue before in my math having to do with like infinities and dividing by 0 and whatnot. This is why I hate love AABB swept (genuinely when I get it to work I LOVE it). So long story short, yeah I think it’s just the way the graphics functions and pixels work. Thank you guys for your input and helping me to understand this! I’ll keep you posted if I am further confused.