GUI & 2D drawing rectangle
struct rect { ivec2 start, end; };
24%
struct rect { ivec2 pos, size; };
76%
Poll ended at .

pos + size wins by 3:1 on Mastodon. But a poll in a private Discord had start + end win by 3:1.

I use start + end in my code.

I did a survey of a bunch of popular systems to see what they use:

macOS Core Graphics CGRect: pos + size
Cairo cairo_rectangle_int_t: pos + size
GTK GdkRectangle: pos + size
dotnet System.Drawing.Rectangle: pos + size
xlib XDrawRectangle: pos + size
nanovg: pos + size
HTML5 canvas: pos + size
Unity OnGUI: pos + size

Blend2D BLBox: start + end
Skia SkRect: start + end
Classic Mac QuickDraw Rect: start + end
Win32 RECT: start + end
BeOS and Haiku BRect: start + end
Delphi & Lazarus TRect: start + end
anti-grain geometry rect_base: start + end
UE5 FRect: start + end
dear imgui: start + end
Qt QRect: start + end
Direct2D D2D_RECT_F: start + end

@cancel That's expected for mastodon, it's mostly populated by web devs. What's the central topic of the private discord? It has got to be some low-level dev thing, right?
@neauoire yeah mostly game engine programming and platform handling and developer tools and that sort of stuff
@cancel I think forming the painting boundaries(start, end) from the start is a much better tactic, but it's not something people are exposed to outside of game dev.

@neauoire it seems like the only places where pos + size are normal is web dev, gtk, and macos core graphics (for some reason)

The start + end ones cover a lot of areas, from pretty new libraries (blend2d) to ancient ones like quickdraw and 16-bit windows (RECT is older than win32.)

start + end seems better to me because it requires fewer instructions to do boundary testing on the mouse cursor, rectangle intersection, union, etc. with pos + size you're always having to keep adding and removing the size to do stuff. better to just do it once and keep the rectangle around to re-use.

in the systems that use pos + size like Cairo/gtk and nanovg, further in their drawing/pipeline implementation they always end up adding the size back to pos to do the drawing or calculate stuff. and if you look upwards in the stack you often see code that has to split a start + end rect back to pos + size before handing it off to Cairo again.

Blend2D also has a BLRect type, which is a pos + size rect, and if you follow its path in Blend2D it ends up getting turned into a BLBox (start + end) later in its pipeline, after the user code has submitted it.

@cancel @neauoire one values the programmers time and sense of comfort, the other values the computer's sense of time and workload.
@floatvoid @neauoire I’m not sure that pos + size is quicker or faster. The size your code wants to make something is usually not stored solely and directly in the rect that you give to the drawing or hit detection system. So you treat it as an output, and then there’s not really any difference between the two types in usability. It’s also easy to add a function that takes a pos and size and returns a start+end rect.
@cancel ivec2 min, max;
@sigrid yeah that would count as start, end, and is more descriptive about the coordinates.
@sigrid @cancel I do this too and appreciate conceiving of a rectangle as a 2D range.

@cancel More of an aside (I'm not recommending it at the API level) but one representation that's often forgotten is center and radius (half-extent) per axis. The center/radius representation of intervals is nice for overlap checking since an interval is just a 1-dimensional disk/ball. So for 2-dimensional rects:

abs(a.cx - b.cx) <= a.rx + b.rx && abs(a.cy - b.cy) <= a.ry + b.ry

Mentioned here, for example: https://fgiesen.wordpress.com/2011/10/16/checking-for-interval-overlap/

Checking for interval overlap

This’ll be a short post, based on a short exchange I recently had with a colleague at work (hey Per!). It’s about a few tricks for checking interval overlaps that aren’t as well-k…

The ryg blog
@cancel One slightly annoying thing is that you have to work in "half-units", e.g. the center of the interval [0..1] is at 1/2. So for integers/grids it's more of an internal representation than something you'd expose.
@pervognsen this might actually come in handy for me later! thanks
@cancel @pervognsen I was going to say exactly this, center and half_extents is good apart from that gotcha for pixel coords.
@skylark13 @pervognsen multiplying by 2 doesn't seem too bad

@cancel @pervognsen Each has their ideal use case...

pos, size and min, max are good if you're drawing pixels. Which one you like most largely depends if you prefer having -1 in some cases or +1 in others. min, max could be better because you never have to check for zero size (max = pos+size-1).

For bounds like in culling, center, half_extents is convenient.

@skylark13 @pervognsen hmm why do you have to add or subtract 1?

@cancel @pervognsen

min,max is inclusive
pos,size is exclusive

Let's take for example with integer pixel coords:
min=4
max=6

You would expect a loop over this interval to hit pixels 4,5,6
So it would be for(x=min; x<=max; ++x) {...}

That's equivalent to
pos=4
size=3 (there are 3 pixels in the interval)
And a loop would be for(x=pos; x<pos+size; ++x) {...}

So min=pos, max=pos+size-1
Or pos=min, size=max-min+1

Hope this explanation makes sense...

@skylark13 @pervognsen

i wouldn't expect that to hit pixel 6. i write my loops like,

for (x = min.x; x < max.x; x++) { ..}

@cancel @pervognsen max is max, not size... you're using it as if it were a size.

If you think just of the name, min is the first pixel you loop over, and max is the last one, inclusively. As an analogy, if I say I can pay a maximum of 50$ for something, and someone sells that thing for precisely 50$, then I can buy it, it doesn't have to be 49.99$

So if I say the min pixel is 4 and the max pixel is 6, then I expect to loop over 4,5,6. But yeah, I'm particular about terminology hehehe

@skylark13 @pervognsen I don't see why it has to be that way? max is the max value of the end of the rectangle, in this case, if you want to call it max. in my original toot i called it start,end.
@skylark13 @pervognsen it's not being treated as a size because it's not relative to the start position.

@cancel @pervognsen

You're treating it as exclusive. It's fine, as long as that's your convention. I thought my analogy with money was a good one. For me, the term max implies that it is inclusive.

@skylark13 @pervognsen is there an advantage to the inclusive convention? having to add or subtract 1 at various times seems troublesome

@cancel @pervognsen

Depends on the case, I don't use it everywhere but in some cases it's simpler. Processing tiles where you need the last tile of the previous interval for the next one, and don't want overlap, for example.

@skylark13 @cancel @pervognsen wouldn't you need to add 1 to prevent overlap with max being inclusive?
while with [start,end) you can use end as the start of the next tile and they would implicitly not overlap

@maliusarth @cancel @pervognsen yeah, you're right.

I realize I was being pedantic about terminology, which is not useful at all. You can use what you want as long as it's clear to you and you handle the boundary conditions. It's just different words...

@cancel I’m evil so: int x0, y0, x1, y1 or ivec2 a, b
@bd all of the existing stuff i surveyed used x0, y0, x1, y1 to mean start, end
@cancel i know what you mean, though I have trouble conceptualizing a rectangle as something with a beginning and end, so in my mind they are just two sets of coordinates
@bd @cancel for my part I find it most useful to be able to either dynamically resize a rectangle relative to its origin (an ingame dialogue box appearing or disappearing), or to move a static sized rect around the screen by animating the origin only.

@cancel the downside of using two corners is the ambiguity of if the second corner is included. The mathematically“right” answer puts pixel centers at half coordinates. But that’s not intuitive. People want to specify the stop pixel.. so (0,0) - (1,1) is a 2x2 rectangle. But that’s means you need to add 1 to get the width.

Using pixel edge coordinates + size solves all these problems and also appears to work right even if user assumes pixel center coordinates.

@jkaniarz huh, that's not how I imagine it. I always imagine the two corners way as each coordinate representing the top left of the pixel. so (0,0) (1,1) is a 1x1 rectangle. (0,0) (0,0) is a 0x0 rectangle.
@cancel I tend to use center position and half-size because I'm chaos incarnate and like rotating things.