15

It's not that this doesn't make sense, but it just works out awkward 99% of the time.

Often in 2D graphics rectangles are initialized, stored and manipulated as a pair of points. In no particular language,

class Rect:
   p1, p2: point

It makes more sense to define a rectangle as two x values and two y values, like this:

class Rect
   xleft, xright: int
   ytop, ybottom: int

With two points, if at some place in the source code you want to make use of the y value of the top, you'd have to say rect.p1.y (hmmm, stop and think, is it p1 or p2) but with the four values as plain data members, it's clear and direct: rect.ytop (no thinking required!) The use of two points means that in dealing with the vertical, you have to tangle the horizontal; there's an extraneous relation between indepenent elements.

How did this two-point idea come about and why does it persists? Does it have some benefit over bare x and y coordinates?

ADDED NOTE: This question is in the context of X-Y aligned rectangles, such as in windows managers and GUI toolkits, not in the context of arbitrary shapes in drawing and painting app.

DarenW
  • 4,433
  • 3
  • 22
  • 43
  • 8
    Have you written an substantial amount of code using Rects? –  Nov 30 '10 at 21:03
  • 6
    A rectangle is defined by two points, so representing them as two points makes a lot of sense. – Adam Crossland Nov 30 '10 at 21:38
  • 2
    A rectangle is more naturally defined as a range of x values and a range of y values. – DarenW Nov 30 '10 at 22:22
  • 2
    Great question! I never thought about it myself, but you make an interesting argument! FWIW, Windows has a [RECT](http://msdn.microsoft.com/en-us/library/dd162897.aspx) like you describe as well (top, left, bottom, right) – Dean Harding Nov 30 '10 at 22:41
  • Hm ... how the class is implemented is of little consequence to me as long it is fast enough and readable. What is important is that all of the constructors and methods that I could wish for are available to me. – Job Dec 01 '10 at 02:14
  • 3
    How do you define a rectangle with two points? Is it assumed to have no rotation? – Nick T Dec 01 '10 at 04:33
  • @Nick T: Though I dumbly didn't state it explicitly, I meant for only straight X-Y aligned rectangles, such as in a windows manager or for plotting data, not full arbitrary shapes that one might twirl around and create drawings with. – DarenW Dec 01 '10 at 18:42
  • @Dean Harding: Well then, Windows it is all the way! Goodbye Linux and the others! :P – DarenW Dec 01 '10 at 18:43
  • I wonder if it might be informative to have a short list of several graphics APIs, GUI toolkits etc and which type of rectangle structs they use? – DarenW Dec 01 '10 at 18:44

16 Answers16

9

I always liked defining a rectangle as a point + width and height, where the point is the upper-left corner of the rectangle.

class Rect {
  float x, y;
  float width, height;
}

And then add whatever methods you need to fetch the other metrics. Like the Java version

Martin Wickman
  • 13,305
  • 3
  • 31
  • 66
  • 4
    Is the top y+height, y-height or just y? – Cameron MacFarland Dec 01 '10 at 06:21
  • 2
    @Cameron MacFarland: That depends on the application's coordinate system, which is no concern of a lowly rectangle. – Jon Purdy Dec 01 '10 at 08:22
  • 2
    @Martin Wickman: What is the advantage over using 2 points? – Kramii Dec 01 '10 at 12:28
  • @Kramii: One advantage is that only have to translate one point if you are moving the whole rect. Btw, you can always calculate the "missing point" if you need it (cpu/memory tradeoff). – Martin Wickman Dec 01 '10 at 12:41
  • This too appears in real life. I find it tricky as one of the most common things I do with rectangles is test if a point is contained within. Drawing is also common. In both cases addition needs to be performed, which bothers high performance clock cycle counters like me. – DarenW Dec 01 '10 at 18:46
  • @Jon: But that's the point of the original question. Why have a rectangle format that allows for ambiguous definitions for left,top,right,bottom when instead you can just store those values. – Cameron MacFarland Dec 01 '10 at 23:36
8

Have you considered that it is less error prone?

If you use (Point1, Point2) it is then very clear what you are specifying. If you provide 2 points, then the only possible error is that the user has mixed up their x and y when constructing the points as the order of the points doesn't matter.

If you supply 4 integers, then if someone isn't paying attention, they may supply (x1,x2,y1,y2) when you wanted (x1,y1,x2,y2) or vice versa. Also, some APIs such as WCF's Rect structure define a rectangle as (x, y, width, height) which could then cause confusion over what (1, 2, 3, 4) means. Is that (x, y, w, h) or (x1, y1, x2, y2) or (x1, x2, y1, y2)?

All in all, (Point1, Point2) seems a bit safer to me.

MIA
  • 5,264
  • 19
  • 29
  • 3
    What about something like Rect(xrange(x1,x2), yrange(y1,y2))? That seems the ultimate in API usage safety and elegance. – DarenW Dec 01 '10 at 18:49
7

Actually, a rectagle isn't defined by 2 points. A rectangle can only be defined by two points if it is parallel to the axes.

There are several ways to represent rectangles that are parallel to the axes:

  1. Two diagonally opposite points
  2. One corner point, height and width
  3. Centre point, half height and width (uncommon, but sometimes useful).
  4. As two X coordinates and two Y coordinates

For (1), many libraries use a convention to determine which two points are used - topLeft and bottomRight, for example.

The choice of representation may be driven by the original purpose of the rectangle definition, but I imagine that it often arbitrary. The representations are equivalent in the information that they carry. They do, however, differ in the ease with which properties of the rectangle may be calculated and the the convenience with which operations can be performed on the reectangle.

Benefits of definition (1) over others include:

  • Consistency of API with other polygons, lines etc.
  • topLeft, bottomRight can be passed to any method that accepts points
  • Methods of Point class can be called on topLeft, bottomRight
  • Most properties can be derived easily, eg. bottomLeft, topRight, width, height, centre, diagonal length, etc.
Kramii
  • 14,029
  • 5
  • 44
  • 64
6

Well p1: Point and p2: Point are each going to have two int coordinates in them anyway, so doesn't your class amount to the same thing?

And if you store those two points as first-class Point objects, don't you get a little more utility from them? In most graphical coordinate systems that I know of, points are subclassed in this way to create a hierarchy of objects: point -> circle -> ellipse and so on.

So if you make an object that doesn't use the Point class, you have divorced that object from the rest of the class hierarchy.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
  • 1
    The advantage I see of OP's representation is if you want to know the lower y value of a rectangle, you know it's "ybottom", where as with p1/p2 you need to figure out which one is lower. This is unless you guarantee p1 will be the lower values, anyway. – Jason Viers Nov 30 '10 at 21:08
  • 1
    While it's true the two different structs both boil down to four coordinates, the two-points version introduces an extraneous level that gathers one x and one y, with no particular rationale to which x goes with which y. I do not see this extra level as providing any utility whatsoever, after many years of graphics programming. – DarenW Nov 30 '10 at 22:27
  • 1
    @Jason: Good point. With the `ytop`/`ybottom` approach, though, there would also need to be a guarantee somewhere that `ybottom` is actually below `ytop`. – Dr. Wily's Apprentice Nov 30 '10 at 22:55
  • Or call 'em y1 and y2, and use min(y1,y2) and max(y1,y2) - of course that would be clumsier even than access through two points p1, p2. – DarenW Dec 01 '10 at 18:47
  • the naming of top/bottom buys you nothing as nothing prevents bottomx < topx, unless you code for that specifically. I think it would just add confusion. – lkg Oct 21 '16 at 20:34
5

This is why I like Delphi's TRect. It's defined as a variant record (union struct in C-speak) that can be interpreted either as a TopLeft and a BottomRight point, or Top, Left, Bottom and Right integers, whichever is more convenient at the moment.

Mason Wheeler
  • 82,151
  • 24
  • 234
  • 309
4

Surely if you define your rectangle as:

class Rect
{
    Point bottomLeft;
    Point topRight;
}

then you know straight away which point is which.

Even better would be to add extra properties that allowed you to manipulate the rectangle in which ever ways you needed for your application. These would simply update the underlying data structure.

By adding a transformation to the shape you can orient your rectangle any way you'd like. You'd still need an axis aligned bounding box for quick accept/reject checks :)

However, if your model allows rectangles in any orientation without applying a transformation then "bottom left" and "top right" have no meaning, which leads back to "p1" and "p2" (or something equivalent).

ChrisF
  • 38,878
  • 11
  • 125
  • 168
  • So what happens when you rotate the rectangle by 90 degrees? Are you now tracking two different points than you were initially, or is your "topRight" now to the left of "bottomLeft"? – Inaimathi Nov 30 '10 at 23:24
  • @Inaimathi - if you add a transformation matrix you can keep the rectangle orientated with the axes. However, it does depend on your application. – ChrisF Nov 30 '10 at 23:54
2

i think it makes more sense for a rectangle to be represented by an x and y extent and a point; you could even make the location point the center of the rectangle so it would be independent of rotation

but it was probably easiest to code it as two points!

Steven A. Lowe
  • 33,808
  • 2
  • 84
  • 151
2

I don't like it because we have thrown out a potential degree of freedom, which essentially allows for an arbitrary rotation. A general 2D rectangle has five unknowns (degrees of freedom). We could specify them as the coordinates of a point, the lengths of the two sides that form a vertex with this point, and the angle from the horizontal of the first line (the other being assumed to have an angle 90 degrees greater). An infinite number of other possibilities could also be used, but there are five independent quantities that must be specified. Some choices will lead to easier algebra than others, depending upon what is done with them.

Omega Centauri
  • 237
  • 1
  • 2
  • 5
    It's important to realize that a "standard" rectangle structure is not a rigorously-defined mathematical rectangle, but a simplified version that's always parallel to the X and Y axes, because it was created to be used for one very specific thing: defining rectangular regions for a window manager, which are (almost) always parallel to the X and Y axes. – Mason Wheeler Dec 01 '10 at 00:28
  • +1, the left/right/top/bottom structure is presorted, and therefore has less information – Javier Dec 01 '10 at 03:46
  • Good point, about x-y aligned rectangles. I had in mind that, and also extents as used in 3D modeling, and some other things, but all x-y (maybe also -z) aligned. – DarenW Dec 09 '10 at 03:47
1

Isnt that exactly the same thing as 2 points? How is this awkward... most drawing routines require points, not separate x/y components.

GrandmasterB
  • 37,990
  • 7
  • 78
  • 131
1

Defining rectangles as point pairs allows you to reuse the point as a vertex for another shape. Just a thought...

RobotHumans
  • 906
  • 8
  • 11
  • But it seems like playing with half a deck, to keep only two points to define a four-cornered shape. If you happen to need top-left, cool, but if you need top-right, you have to do some fancy data grabbing, relatively speaking. – DarenW Nov 30 '10 at 22:30
  • if there is an accessor that is defined as point A X point B Y and point B X point A Y it makes it lighter in memory/on disk I think to store half as many vertexes and just point to them(depending on the maximum grid size). I get where you're going with your range thought, but you aren't playing with half the deck, you just aren't storing duplicate data....and the other points to be made are what are the memory constraints? what are the re-implementation implications retooling rects in all interconnected code. – RobotHumans Nov 30 '10 at 22:40
  • and since you don't have to do any "work" to get the first two vertexes out of memory, it's probably faster – RobotHumans Nov 30 '10 at 22:41
1

I believe it is mainly to establish uniformity between all shape primitives.

Sure you can define rectangle in many different ways, but how do you define a triangle, or a star, or a circle in a way that can use similar data structures?

All polygons can be defined by their points, with a short amount of logic to determine what to do with the points.

Graphics libraries primarily operate on these polygons in terms of vertices and edges, so points and the lines between them, all the calculations work on these two features, well that and facets, but that itself is just a function of the edges.

Orbling
  • 5,696
  • 1
  • 32
  • 38
1

In two dimensions, storing a rectangle as two points is clearer than defining a particular corner and a width and height - consider negative width or height, or the calculations required to determine each option from the other.

Performing rotations on a rectangle defined by points is also much simpler than one defined with a point plus width and height.

I'd expect encapsulation to make this differentiation unimportant as a user of the class.

A rectangle should be defined as three points to be well defined in 3 dimensions. I'm not entirely sure of the requirement for defining a rectangle in 4 or more dimensions.

Armand
  • 6,508
  • 4
  • 37
  • 53
1

It's completely arbitrary. You need four pieces of information to draw a rectangle. The library designer(s) decided to represent it with two points (each with an x-y coordinate), but could easily have done it with x/y/w/h or top/bottom/left/right.

I suppose the OP's real question is: why was this particular choice made?

Barry Brown
  • 4,095
  • 4
  • 25
  • 27
1

The choice of parameters are only important to the low-level designers / coders.

High-level users only need to think about:

  • IsPointInRect
  • Area
  • Intersection (or Clipping)
  • HasOverlap (same as Intersection.Area > 0)
  • Union (becomes a list of rectangles)
  • Subtraction (a list of rectangles that represent the same point set that is in rect A but not in rect B)
  • Transform
    • Shifts in X and Y
    • Rotation (0, 90, 180, 270)
    • Scaling in X and Y (see note)
  • Simple syntax for properties Xmin, Xmax, Ymin, Ymax, Width, Height so that the user does not need to know the exact choice of parameters.

Note: In order to minimize loss of precision during scaling transform, it is sometimes appropriate to implement a second Rect class that uses floating-point coordinates, so that intermediate results can be stored accurately in a sequence of transforms and only rounded to integer in the last step.

rwong
  • 16,695
  • 3
  • 33
  • 81
0

As @Steven says, I think it should be in terms of one (x,y) point, and a (w,h) size vector. That's because it's easy to fall into an ambiguity. Suppose you have the following filled-in rectangle starting at point (0,0).

  012
0 XXX
1 XXX
2 XXX

Clearly it's width,height are (3,3), but what is it's second point? Is it (2,2) or (3,3)?

This ambiguity can cause all kinds of problems.

I learned the hard way years ago that it's better to think of graphic coordinates as the lines between the pixels, not as the lines the pixels are on. That way there's no ambiguity.

Mike Dunlavey
  • 12,815
  • 2
  • 35
  • 58
  • 2
    The original QuickDraw routines on the Mac (the ones from the 1980s) used the mathematical model that points were infinitely small. The points on the screen lay between the pixels. So a line drawn from (3,5) to (10,5) had a length of 7 and occupied 7 pixels. In today's coordinates, that line would have a length of 8. – Barry Brown Dec 01 '10 at 02:34
  • @Barry: That makes sense, since it used XOR a lot, and if you're stringing lines together, you want them to connect without a missing pixel where they meet. I actually published a siggraph paper on filling polygons, considering both coordinate systems. – Mike Dunlavey Dec 01 '10 at 03:11
0
Pa(x,y)*-----------------------------------*Pb(x,y)
       |                                   |
       |                                   |
       |                                   |
       |                                   |
       |                                   |
       |                                   |
Pc(x,y)*-----------------------------------*Pd(x,y)

We can define both Pb & Pc thus:

Pb(Pd(x),Pa(y))

and

Pc(Pa(x),Pd(y))

So there is no need to define all four points due to symmetry

Darknight
  • 12,209
  • 1
  • 38
  • 58