// Test template for Rects and so on wobjobby dooda. //!TEMPLATE_H //$Header: c:\RCSdir\kernel/rect.ctt 1.20 1995/07/07 11:35:00 Jason Exp $ #ifndef INC_RECT #define INC_RECT #include "camtypes.h" #include "coord.h" //!INCLUDE_EXTRA_HDRS /*********************************************************************************************** > Class Author: Simon M Date: 11/5/93 Purpose: This class is used to represent a rectangular region. It uses a union to enable the client to access the rectangle as two Coords (lo and hi) or as four LONGs (lox, loy, hix, hiy). An anonymous union is used, which is a defined C++ feature and so portable. The lo coordinates are inclusive, whilst the hi coordinates are exclusive. In the document space, lo.y <= hi.y and lo.x <= hi.x (the inequalities are not strict because rectangles can have zero width and/or height - such rectangles are considered perfectly valid). The inclusivity can be explained as follows: A point at (lo.x, lo.y) is inside the rectangle A point at (hi.x, hi.y) is outside the rectangle Any rectangle that does not obey the inequalites lo.y <= hi.y and lo.x <= hi.x is deemed to be 'invalid'. Invalid rectangles have special properties. They will cause errors when used in some operations, but not in others. The general rule is that if the rectangle is used as an entity, then the operation will succeed (taking account of the 'invalidity' of the rectangle). If, however, the actual coordinates of the invalid rectangle must be used/changed in the operation, then an error (usually an assertion failure) will occur. For example, using invalid rectangles in Unions and Intersections is ok, because the invalid rectangle is ignored, and the 'other' rectangle is returned as the result. This means that if both rectangles are invalid, then an invalid rectangle is returned. Conversely, trying to use operations like Translate(), Inflate(), Width() or Height() on invalid rectangles is considered to be an error, and will cause an assertion failure. See the individual function descriptions for more details. Errors: - SeeAlso: Coord SeeAlso: Rect SeeAlso: DocRect SeeAlso: WorkRect SeeAlso: OSRect ***********************************************************************************************/ class CAMAPI { public: union { struct { Coord lo,hi; }; struct { LONG lox,loy,hix,hiy; }; }; // constructors (); (LONG LowX, LONG LowY, LONG HighX, LONG HighY); (const Coord& Low, ULONG Width, ULONG Height); (const Coord& Low, const Coord& High); (const & ); // Copy constructor & operator=(const & );// Assignment operator LONG Width() const; LONG Height() const; Coord LowCorner() const; Coord HighCorner() const; Coord Centre() const; // General operations type stuff BOOL IsIntersectedWith(const &) const; Intersection(const &) const; Union(const &) const; int SplitRect(const & R, * SubRects); BOOL ContainsCoord(const Coord&) const; BOOL ContainsRectCoord(const Coord&) const; BOOL ContainsRect(const &) const; BOOL IsAdjacent(const &, MILLIPOINT Fuzzy) const; void MakeEmpty(); BOOL IsEmpty() const; BOOL IsValid() const; void Inflate(LONG XInc, LONG YInc); void Inflate(LONG XInc); void Translate(LONG XOfs, LONG YOfs); void IncludePoint(const Coord&); // Overloaded operators int operator==(const &) const; int operator!=(const &) const; //!INCLUDE_EXTRA_DECLS }; /********************************************************************************************* > void ::Translate(LONG XOfs, LONG YOfs) Author: Tim Browse Created: 17/5/93 Inputs: (XOfs, YOfs) - the offset to translate the rectangle by. Outputs: - Returns: N/A. Purpose: Translate a rectangle by given offset. Errors: An assertion failure if the rectangle is invalid. **********************************************************************************************/ inline void ::Translate(LONG XOfs, LONG YOfs) { // Detect an invalid rectangle ENSURE(IsValid(), "::Translate() was called on an \ninvalid rectangle."); lo.x += XOfs; lo.y += YOfs; hi.x += XOfs; hi.y += YOfs; } /********************************************************************************************* > int operator==(const & R) const Author: Tim Browse Created: 17/5/93 Inputs: R - the rectangle to compare against. Outputs: - Returns: TRUE if R is describes the same rectangle as the object. Purpose: Test for equality of two rectangles. As all invalid rectangles have the same results when used for Union/Intersection, any two invalid rectangles are considered equal. Friend: Errors: None. **********************************************************************************************/ inline int ::operator==(const & R) const { // Invalid rectangles are equal if ((!IsValid()) && (!R.IsValid())) return TRUE; // Could use structure compare? Would it be portable? Probably not... return ((lo.x == R.lo.x) && (lo.y == R.lo.y) && (hi.x == R.hi.x) && (hi.y == R.hi.y)); } /********************************************************************************************* > int operator!=(const & R) const Author: Tim Browse Created: 17/5/93 Inputs: R - the rectangle to compare against. Outputs: - Returns: TRUE if R does not describe the same rectangle as the object. Purpose: Test for inequality of two rectangles. As all invalid rectangles have the same results when used for Union/Intersection, any two invalid rectangles are considered equal. Friend: Errors: None. **********************************************************************************************/ inline int ::operator!=(const & R) const { // Invalid rectangles are equal if ((!IsValid()) && (!R.IsValid())) return FALSE; return ((lo.x != R.lo.x) || (lo.y != R.lo.y) || (hi.x != R.hi.x) || (hi.y != R.hi.y)); } /********************************************************************************************* > void ::Inflate(LONG XInc, LONG YInc) Author: Tim Browse Created: 17/5/93 Inputs: XInc, YInc - the amount to inflate (or deflate) the rectangle by. Outputs: - Returns: N/A. Purpose: Inflate a rectangle by given amounts. Negative values will deflate the rectangle. Errors: An assertion failure if the rectangle is invalid. **********************************************************************************************/ inline void ::Inflate(LONG XInc, LONG YInc) { // Detect an invalid rectangle ENSURE(IsValid(), "::Inflate(LONG, LONG) was called on an \ninvalid rectangle."); lo.x -= XInc; lo.y -= YInc; hi.x += XInc; hi.y += YInc; } /********************************************************************************************* > void ::Inflate(LONG Inc) Author: Tim Browse Created: 17/5/93 Inputs: Inc - the amount to inflate (or deflate) the rectangle by. Outputs: - Returns: N/A. Purpose: Inflate a rectangle by given amount. Negative values will deflate the rectangle. Errors: An assertion failure if the rectangle is invalid. **********************************************************************************************/ inline void ::Inflate(LONG Inc) { // Detect an invalid rectangle ENSURE(IsValid(), "::Inflate(LONG) was called on an \ninvalid rectangle."); lo.x -= Inc; lo.y -= Inc; hi.x += Inc; hi.y += Inc; } /********************************************************************************************* > ::() Author: Simon M Created: 13/5/93 Inputs: None Outputs: - Returns: - Purpose: To construct an empty . Errors: **********************************************************************************************/ inline ::() { // An empty rectangle hi.x = hi.y = lo.x = lo.y = 0; } /********************************************************************************************* > ::(LONG LowX, LONG LowY, LONG HighX, LONG HighY) Author: Simon M Created: 13/5/93 Inputs: LowX : Lower X coord of rectangle (inclusive) HighX: Higher X coord of rectangle (exclusive) LowY : Lower Y coord of rectangle (inclusive) HighY: Higher Y coord of rectangle (exclusive) Outputs: - Returns: - Purpose: To construct a with an inclusive lower left hand corner position of (Left, Lower) and an exclusive upper right hand corner position of (Right, Upper). Errors: An assertion failure will occur if the lower left hand coordinates are not lower than and to the left of the upper right coordinate. **********************************************************************************************/ inline ::(LONG LowX, LONG LowY, LONG HighX, LONG HighY) { // Defensive programming, detect an invalid rectangle ENSURE((LowX <= HighX) && (LowY <= HighY), "::(LONG, LONG, LONG, LONG) was\n passed invalid coordinates"); lo.x = LowX; lo.y = LowY; hi.x = HighX; hi.y = HighY; } /********************************************************************************************* > ::(const Coord& Low, const Coord& High) Author: Simon M Created: 13/5/93 Inputs: Low : Coordinates of the lower left hand corner (inclusive) High: Coordinates of the upper right hand corner (exclusive) Outputs: - Returns: - Purpose: To construct a rectangle with an inclusive lower left hand corner position of Low and an exclusive upper right hand corner position of High. Errors: An assertion failure will occur if the lower left hand coordinates are not lower than and to the left of the upper right coordinates. **********************************************************************************************/ inline ::(const Coord& Low, const Coord& High) { // Defensive programming, detect an invalid rectangle ENSURE((Low.x <= High.x) && (Low.y <= High.y), "::(Coord, Coord) was\n passed invalid coordinates"); lo = Low; hi = High; } /********************************************************************************************* > ::(const Coord& Low, ULONG Width, ULONG Height) Author: Tim Browse Created: 17/5/93 Inputs: Low: Coordinates of the inclusive lower left hand corner. Width, Height : Desired dimensions of the rectangle. Outputs: - Returns: - Purpose: To construct a rectangle with an inclusive lower left hand corner position of Low and a width and height as specified. Errors: None. **********************************************************************************************/ inline ::(const Coord& Low, ULONG Width, ULONG Height) { lo = Low; hi.x = lox + Width; hi.y = loy + Height; } /********************************************************************************************* > ::(const & R) Author: Simon M Created: 13/5/93 Inputs: R: The copy of the Outputs: - Returns: - Purpose: Copy constructor **********************************************************************************************/ inline ::(const & R) { lo = R.lo; hi = R.hi; } /********************************************************************************************* > & ::operator=(const & ) Author: Simon M Created: 13/5/93 Inputs: : to copy Outputs: - Returns: Reference to this Purpose: Equals operator **********************************************************************************************/ inline & ::operator=(const & ) { lo = .lo; hi = .hi; return *this; } /********************************************************************************************* > LONG ::Width() const Author: Simon M Created: 13/5/93 Inputs: - Outputs: - Returns: The width of the Purpose: To find the width of the **********************************************************************************************/ inline LONG ::Width() const { // Detect an invalid rectangle ENSURE(IsValid(), "::Width() was called on\nan invalid rectangle."); return (hi.x - lo.x); } /********************************************************************************************* > LONG ::Height() const Author: Simon M Created: 13/5/93 Inputs: - Outputs: - Returns: The height of the Purpose: To find the height of the **********************************************************************************************/ inline LONG ::Height() const { // Detect an invalid rectangle ENSURE(IsValid(), "::Height() was called on\nan invalid rectangle."); return(hi.y - lo.y); } /********************************************************************************************* > Coord ::LowCorner() const Author: Simon M Created: 13/5/93 Inputs: - Outputs: - Returns: The inclusive lower left hand coordinates of the Purpose: To find the lower left hand coordinates of the Errors: Assertion failure if the rectangle is invalid. **********************************************************************************************/ inline Coord ::LowCorner() const { // Detect an invalid rectangle ENSURE(IsValid(), "::LowCorner() was called on\nan invalid rectangle."); return(lo); } /********************************************************************************************* > Coord ::HighCorner() const Author: Simon M Created: 13/5/93 Inputs: - Outputs: - Returns: The exclusive upper right hand coordinates of the Purpose: To find the upper right hand coordinates of the Errors: Assertion failure if the rectangle is invalid. **********************************************************************************************/ inline Coord ::HighCorner() const { // Detect an invalid rectangle ENSURE(IsValid(), "::HighCorner() was called on\nan invalid rectangle."); return(hi); } /********************************************************************************************* > Coord ::Centre() const Author: Markn Created: 19/5/99 Inputs: - Outputs: - Returns: The centre coord of this Purpose: To find the centre of the It calculates Coord(lox+(width/2),loy+(height/2)) Errors: Assertion failure if the rectangle is invalid. **********************************************************************************************/ inline Coord ::Centre() const { // Detect an invalid rectangle ENSURE(IsValid(), "::Centre() was called on\nan invalid rectangle."); return Coord(lo.x + Width() / 2, lo.y + Height() / 2); } /********************************************************************************************* > BOOL ::IsIntersectedWith(const & ) const Author: Tim Browse Created: 17/5/93 Inputs: - Outputs: - Returns: TRUE if the rectangles intersect, FALSE otherwise. Purpose: To check for rectangle intersection Errors: **********************************************************************************************/ inline BOOL ::IsIntersectedWith(const & R) const { // Detect an invalid rectangle if ((!IsValid()) || (!R.IsValid())) return FALSE; return ((hi.x > R.lo.x) && (lo.x < R.hi.x) && (hi.y > R.lo.y) && (lo.y < R.hi.y)); } /********************************************************************************************* > BOOL ::ContainsCoord(const Coord& Point) Author: Tim Browse Created: 17/5/93 Inputs: - Outputs: - Returns: TRUE if the coordinate is within the rectangle, FALSE otherwise. Purpose: To check for coordinate containment. Errors: Assertion failure if the rectangle is invalid. SeeAlso: ContainsRectCoord; ContainsRect **********************************************************************************************/ inline BOOL ::ContainsCoord(const Coord& Point) const { // Check for an an empty rectangle if (IsEmpty()) return FALSE; // Detect an invalid rectangle ENSURE(IsValid(), "::ContainsCoord() was called on\nan invalid rectangle."); return ((Point.x >= lo.x) && (Point.x < hi.x) && (Point.y >= lo.y) && (Point.y < hi.y)); } /********************************************************************************************* > BOOL ::ContainsRectCoord(const Coord& Point) Author: Tim Browse Created: 17/5/93 Inputs: - Outputs: - Returns: TRUE if the coordinate is within the rectangle, FALSE otherwise. Purpose: To check for coordinate containment. This will work for coordinates which have been extracted from other rectangles (i.e. the top right corner is considered inclusive for this operation, not exclusive). Errors: Assertion failure if the rectangle is invalid. SeeAlso: ContainsCoord; ContainsRect **********************************************************************************************/ inline BOOL ::ContainsRectCoord(const Coord& Point) const { // Check for an an empty rectangle if (IsEmpty()) return FALSE; // Detect an invalid rectangle ENSURE(IsValid(), "::ContainsRectCoord() was called on\nan invalid rectangle."); return ((Point.x >= lo.x) && (Point.x <= hi.x) && (Point.y >= lo.y) && (Point.y <= hi.y)); } /********************************************************************************************* > BOOL ::ContainsRect(const & Rect) Author: Tim Browse Created: 17/5/93 Inputs: - Outputs: - Returns: TRUE if the rectangle 'Rect' is within the rectangle, FALSE otherwise. Purpose: To check for rectangle containment. Errors: Assertion failure if the rectangle is invalid. SeeAlso: ContainsRectCoord; ContainsCoord **********************************************************************************************/ inline BOOL ::ContainsRect(const & Rect) const { // Check for an an empty rectangle if (IsEmpty()) return FALSE; // Detect an invalid rectangle ENSURE(IsValid(), "::ContainsRect() was called on\nan invalid rectangle."); return ((Rect.lo.x >= lo.x) && (Rect.hi.x <= hi.x) && (Rect.lo.y >= lo.y) && (Rect.hi.y <= hi.y)); } /******************************************************************************************** > void ::MakeEmpty() Author: Tim Created: 03/03/94 Purpose: Make the rectangle an empty one (all coordinates are set to 0). SeeAlso: ::MakeEmpty ********************************************************************************************/ inline void ::MakeEmpty() { lo.x = lo.y = hi.x = hi.y = 0; } /********************************************************************************************* > BOOL ::IsEmpty() const Author: Tim Browse Created: 17/5/93 Inputs: - Outputs: - Returns: TRUE if the rectangle is empty. Purpose: To check for empty rectangle. Errors: **********************************************************************************************/ inline BOOL ::IsEmpty() const { return ((lo.x == hi.x) || (lo.y == hi.y)); } /********************************************************************************************* > BOOL ::IsValid() const Author: Tim Browse Created: 9/6/93 Inputs: - Outputs: - Returns: TRUE if the rectangle is valid. Purpose: To check for a valid rectangle. Errors: **********************************************************************************************/ inline BOOL ::IsValid() const { return ((lo.x <= hi.x) && (lo.y <= hi.y)); } #endif //!TEMPLATE_CPP #include "camtypes.h" #include "rect.h" #include "ensure.h" DECLARE_SOURCE("$Revision: 1.20 $"); /* $Log: rect.ctt $ Revision 1.20 1995/07/07 11:35:00 Jason Fixed ABS macro definitions to be safe Revision 1.19 1994/06/28 16:57:47 Rik Added some code that shows how the hell the IsAdjacent function works Revision 1.18 1994/03/03 11:38:12 Tim Added a MakeEmpty function. Revision 1.17 1994/01/20 17:09:23 Simon Default constructor now creates an empty recatngle Revision 1.16 1994/01/12 17:30:17 Rik Improved region merging and region joining Revision 1.15 1993/11/05 14:56:03 Rik Blobs work, fixed DocRect probs, selecting blobs work Revision 1.14 1993/08/12 11:17:48 Will Global CheckIn for new Coord system. Revision 1.13 1993/07/29 11:10:02 Will Fixed problem with fuzzy adjacent test. Revision 1.12 1993/07/21 21:08:29 Will Split rect function was splitting the wrong rect. Revision 1.11 1993/07/19 17:02:42 Will Added some comments. Added Fuzzy limit passed during IsAdjacent(). Revision 1.10 1993/07/15 17:04:10 Will Added SplitRect function for BG rendering. Revision 1.9 1993/07/14 17:34:25 Will Added IsAdjacent function for region merging. Revision 1.8 1993/07/12 12:58:11 Tim Added a ContainsRect function. Revision 1.7 1993/06/18 11:06:28 Tim Made rectangle classes usable by tools by using the CAMAPI macro in the class declaration. Revision 1.6 1993/06/15 16:49:28 Tim Altered Intersection and Union to return objects rather than object refs. Revision 1.5 1993/06/10 10:29:35 Tim Improved links for ADC generated help file. Revision 1.4 1993/06/09 16:40:02 Tim Moved class comment from .cpp file to .h file to conform to style guide. Revision 1.3 1993/06/09 13:52:40 Tim Revised use and meaning of empty/invalid rectangles. Expanded comment headers to explain this. Revision 1.2 1993/06/08 14:12:06 Tim Added comments about non-re-entrancy of Union and Intersection. */ /********************************************************************************************** > BOOL ::IsAdjacent() const Author: Will Created: 14/7/93 Inputs: The Fuzzy limit in MILLIPOINTS. Outputs: - Returns: TRUE if the rectangles are adjacent within a Fuzzy limit. Purpose: To check for adjacent rectangles. Errors: **********************************************************************************************/ BOOL ::IsAdjacent(const & Rect, MILLIPOINT Fuzzy) const { // Check for an an empty rectangle if (IsEmpty()) return FALSE; // Detect an invalid rectangle ENSURE(IsValid(), "::IsAdjacent() was called on\nan invalid rectangle."); #if 0 // This is the expansion of the rampant return statement below // Near top or bottom BOOL NearBot = (ABS(Rect.hi.y - lo.y)) <= Fuzzy; BOOL NearTop = (ABS(Rect.lo.y - hi.y)) <= Fuzzy; // Near left or right BOOL NearLeft = (ABS(Rect.hi.x - lo.x)) <= Fuzzy; BOOL NearRight = (ABS(Rect.lo.x - hi.x)) <= Fuzzy; // Overlaps BOOL OverLeft = (ABS(Rect.lo.x - lo.x)) <= Fuzzy; BOOL OverRight = (ABS(Rect.hi.x - hi.x)) <= Fuzzy; BOOL OverTop = (ABS(Rect.hi.y - hi.y)) <= Fuzzy; BOOL OverBot = (ABS(Rect.lo.y - lo.y)) <= Fuzzy; // Adjacent to the top or bottom? BOOL TopOrBot = (NearTop || NearBot) && OverLeft && OverRight; BOOL LeftOrRight = (NearLeft || NearRight) && OverTop && OverBot; return (TopOrBot || LeftOrRight); #endif // This is explained more carefully in the section above that is excluded from the build return (( ((ABS(Rect.lo.x - lo.x))<=Fuzzy) && ((ABS(Rect.hi.x - hi.x))<=Fuzzy) && (((ABS(Rect.hi.y - lo.y))<=Fuzzy) || ((ABS(Rect.lo.y - hi.y))<=Fuzzy)) ) || ( ((ABS(Rect.lo.y - lo.y))<=Fuzzy) && ((ABS(Rect.hi.y - hi.y))<=Fuzzy) && (((ABS(Rect.hi.x - lo.x))<=Fuzzy) || ((ABS(Rect.lo.x - hi.x))<=Fuzzy)) )); } /********************************************************************************************* > ::Union(const & R) Author: Tim Browse Created: 17/5/93 Inputs: R - the rectangle to join with the object. Outputs: - Returns: The simple union (bounding box). Purpose: Compute union of two rectangles. If one of the rectangles involved is invalid, the other is returned as the result. In the case of both being invalid, one of the invalid rectangles is returned as the result. It is undefined which rectangle is returned in this case. Errors: None. **********************************************************************************************/ ::Union(const & R) const { // Special cases for invalid rectangles... if (!IsValid() || IsEmpty()) return R; if (!R.IsValid() || R.IsEmpty()) return *this; // Return the result U; U.lo.x = min(lo.x, R.lo.x); U.lo.y = min(lo.y, R.lo.y); U.hi.x = max(hi.x, R.hi.x); U.hi.y = max(hi.y, R.hi.y); return U; } /********************************************************************************************* > ::Intersection(const & R) Author: Tim Browse Created: 17/5/93 Inputs: R - the rectangle to intersect with. Outputs: - Returns: The intersection. Purpose: Compute intersection of two rectangles. If one of the rectangles involved is invalid, the other is returned as the result. In the case of both being invalid, one of the invalid rectangles is returned as the result. It is undefined which rectangle is returned in this case. Errors: None. **********************************************************************************************/ ::Intersection(const & R) const { // Special cases for invalid rectangles... if (!IsValid()) return R; if (!R.IsValid()) return *this; // Special cases when empty rectangles if (IsEmpty()) return *this; if (R.IsEmpty()) return R; // Return the result I; I.lo.x = max(lo.x, R.lo.x); I.lo.y = max(lo.y, R.lo.y); I.hi.x = min(hi.x, R.hi.x); I.hi.y = min(hi.y, R.hi.y); return I; } /********************************************************************************************* > int ::SplitRect(const & R, * SubRects) Author: Will Created: 14/7/93 Inputs: R is the rectangle to intersect with, SubRects is an array of to fill. Outputs: Puts any sub-rects into SubRects. Returns: The number of subrectangles found. 0 for no intersection. Purpose: Splits a rectangle into sub-rectangles which all exclude the interecting rectangle R. The number of sub-rects generated depends on how the rectangles intersect. _______________________ ___________________ | | | | | *this A | | *this A | |_ _ _ ________ _ _ _ | |_ _ _ _ _ ________|__________ | | | | | | | | | B | R | C | | B | | | |_ _ _ |________| _ _ _ | |__________|________| R | | | | | | D Index=4 | | Index=2 | |_______________________| |___________________| Errors: **********************************************************************************************/ int ::SplitRect(const & R, * SubRects) { if (!this->IsIntersectedWith(R)) return 0; // No intersection int Index = 0; if (R.hi.y < hi.y) SubRects[Index++] = (lo.x, R.hi.y, hi.x, hi.y); // Sub-Rect A if (R.lo.y > lo.y) SubRects[Index++] = (lo.x, lo.y, hi.x, R.lo.y); // Sub-Rect D LONG Ry0 = max(R.lo.y, lo.y); LONG Ry1 = min(R.hi.y, hi.y); if (R.lo.x > lo.x) SubRects[Index++] = (lo.x, Ry0, R.lo.x, Ry1); // Sub-Rect B if (R.hi.x < hi.x) SubRects[Index++] = (R.hi.x, Ry0, hi.x, Ry1); // Sub-Rect C return Index; } /*********************************************************************************************** > void ::IncludePoint(const Coord& Point) Author: Tim Browse Created: 27/5/93 Inputs: Point - coordinate to include into this rectangle. Outputs: - Returns: - Purpose: Expand a rectangle so that it includes the given point. Errors: An assertion failure if the rectangle is invalid. SeeAlso: - ***********************************************************************************************/ void ::IncludePoint(const Coord& Point) { // Detect an invalid rectangle ENSURE(IsValid(), "::IncludePoint() was called on an \ninvalid rectangle."); // Extend lower corner to include point if necessary lo.x = min(lo.x, Point.x); lo.y = min(lo.y, Point.y); // Extend upper corner to include point if necessary // (remember upper corner is exclusive) hi.x = max(hi.x, Point.x + 1L); hi.y = max(hi.y, Point.y + 1L); } //!INCLUDE_EXTRA_DEFNS //!TEMPLATE_END