Call for a Rectangle<T>


#1

hi jules, what u think about having a Rectangle class like the point class which have its internal coordinates in flating point ? i’m using an app which holds a set of spatial coordinates (in mt. in “double” type) and have to be treated like any other rectangle (converting spatial coordinates to screen coordinates back and forth), as i do with spatial points. now having points in floating point and rectangles in integers is making me writing a lot of wrapping/conversion functions and also i’m loosing the possibilities to use RectangleLists also rewriting a lot of common functions.
i know this could lead to a lot of conversions when using floats rectangle in paint method, but what u think is the best approach ?
i thought about having a Rectangle, Point, Line so u can reuse the same algoritms (unions, intersects, lists and so on), but dunnow if this is worth it… would like to hear your opinion !


#2

Yeah, most of the projects i’ve been working on recently have made heavy use of ‘FloatRect’ - a class i made by copying the Rectangle source and using “Find and Replace…” to change most of the ‘int’ to ‘float’. I had to tweak a few functions that did purely integer operations, but it worked pretty much right away [in fact, i started using it too quickly - it still has the augmented function name ‘getFloatersection()’ which i haven’t been arsed to revert to a name that actually makes sense due to it being functional enough]

I was going to request that at least a FloatRectangle be implemented in juce, to save me from having to dig out the files for each new project that needs them. Seeing someone else make such a request makes me feel it’s worth chiming up about.


#3

Yes, this is stuff I always think when I’m playing with the rectangle class. The best way to do it would be to make a different Rectangle class which is templated, and typedef Rectangle to be an int version of it, so that all the existing code still works.


#4
/*
  ==============================================================================

   This file is part of the EJUCE library
   which is based on Raw Material Software ltd. JUCE

   EJUCE can be redistributed and/or modified under the terms of the
   GNU General Public License, as published by the Free Software Foundation;
   either version 2 of the License, or (at your option) any later version.

   EJUCE is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with EJUCE if not, visit www.gnu.org/licenses or write to the
   Free Software Foundation, Inc., 59 Temple Place, Suite 330,
   Boston, MA 02111-1307 USA

   @author	Asnaghi Lucio

  ==============================================================================
*/

#ifndef __EJUCE_RECTANGLE_HEADERFILE_H__
#define __EJUCE_RECTANGLE_HEADERFILE_H__


//==============================================================================
/**
    A rectangle, specified using integer co-ordinates.

    @see RectangleList, Path, Line, Point
*/
template<class T> class Rect
{
public:
    //==============================================================================
    /** Creates a rectangle of zero size.

        The default co-ordinates will be (0, 0, 0, 0).
    */
    Rect ()
      : x (0),
        y (0),
        w (0),
        h (0)
    {
    }

    /** Creates a copy of another rectangle. */
    Rect (const Rect<T>& other)
      : x (other.x),
        y (other.y),
        w (other.w),
        h (other.h)
    {
    }

    /** Creates a rectangle with a given position and size. */
    Rect (const T x_, const T y_,
          const T width_, const T height_)
      : x (x_),
        y (y_),
        w (width_),
        h (height_)
    {
    }

    /** Destructor. */
    ~Rect()
    {
    }

    //==============================================================================
    /** Returns the x co-ordinate of the rectangle's left-hand-side. */
    inline T getX() const throw()                         { return x; }

    /** Returns the y co-ordinate of the rectangle's top edge. */
    inline T getY() const throw()                         { return y; }

    /** Returns the width of the rectangle. */
    inline T getWidth() const throw()                     { return w; }

    /** Returns the height of the rectangle. */
    inline T getHeight() const throw()                    { return h; }

    /** Returns the x co-ordinate of the rectangle's right-hand-side. */
    inline T getRight() const throw()                     { return x + w; }

    /** Returns the y co-ordinate of the rectangle's bottom edge. */
    inline T getBottom() const throw()                    { return y + h; }

    /** Returns the x co-ordinate of the rectangle's centre. */
    inline T getCentreX() const throw()                   { return x + (w / (T)2); }

    /** Returns the y co-ordinate of the rectangle's centre. */
    inline T getCentreY() const throw()                   { return y + (h / (T)2); }

    /** Returns true if the rectangle's width and height are both zero or less */
    bool isEmpty() const throw();

    /** Changes the position of the rectangle's top-left corner (leaving its size unchanged). */
    void setPosition (const T x, const T y) throw();

    /** Changes the rectangle's size, leaving the position of its top-left corner unchanged. */
    void setSize (const T w, const T h) throw();

    /** Changes all the rectangle's co-ordinates. */
    void setBounds (const T newX, const T newY,
                    const T newWidth, const T newHeight) throw();

    /** Moves the rectangle's position by adding amount to its x and y co-ordinates. */
    void translate (const T deltaX,
                    const T deltaY) throw();

    /** Returns a rectangle which is the same as this one moved by a given amount. */
    const Rect<T> translated (const T deltaX,
                              const T deltaY) const throw();

    /** Expands the rectangle by a given amount.

        Effectively, its new size is (x - deltaX, y - deltaY, w + deltaX * 2, h + deltaY * 2).
    */
    void expand (const T deltaX,
                 const T deltaY) throw();

    /** Returns a rectangle that is larger than this one by a given amount.

        Effectively, the rectangle returned is (x - deltaX, y - deltaY, w + deltaX * 2, h + deltaY * 2).
    */
    const Rect<T> expanded (const T deltaX,
                            const T deltaY) const throw();

    //==============================================================================
    /** Returns true if the two rectangles are identical. */
    bool operator== (const Rect<T>& other) const throw();

    /** Returns true if the two rectangles are not identical. */
    bool operator!= (const Rect<T>& other) const throw();

    /** Returns true if this co-ordinate is inside the rectangle. */
    bool contains (const T x, const T y) const throw();

    /** Returns true if this other rectangle is completely inside this one. */
    bool contains (const Rect<T>& other) const throw();

    /** Returns true if any part of another rectangle overlaps this one. */
    bool intersects (const Rect<T>& other) const throw();

    /** Returns the region that is the overlap between this and another rectangle.

        If the two rectangles don'T overlap, the rectangle returned will be empty.
    */
    const Rect<T> getIntersection (const Rect<T>& other) const throw();

    /** Clips a rectangle so that it lies only within this one.

        This is a non-static version of intersectRectangles().

        Returns false if the two regions didn'T overlap.
    */
    bool intersectRectangle (T& x, T& y, T& w, T& h) const throw();

    /** Returns the smallest rectangle that contains both this one and the one
        passed-in.
    */
    const Rect<T> getUnion (const Rect<T>& other) const throw();

    /** If this rectangle merged with another one results in a simple rectangle, this
        will set this rectangle to the result, and return true.

        Returns false and does nothing to this rectangle if the two rectangles don'T overlap,
        or if they form a complex region.
    */
    bool enlargeIfAdjacent (const Rect<T>& other) throw();

    /** If after removing another rectangle from this one the result is a simple rectangle,
        this will set this object's bounds to be the result, and return true.

        Returns false and does nothing to this rectangle if the two rectangles don'T overlap,
        or if removing the other one would form a complex region.
    */
    bool reduceIfPartlyContainedIn (const Rect<T>& other) throw();

    //==============================================================================
    /** Static utility to intersect two sets of rectangular co-ordinates.

        Returns false if the two regions didn'T overlap.

        @see intersectRectangle
    */
    static bool intersectRectangles (T& x1, T& y1, T& w1, T& h1,
                                     T x2, T y2, T w2, T h2) throw();

    //==============================================================================
    /** Creates a string describing this rectangle.

        The string will be of the form "x y width height", e.g. "100 100 400 200".

        Coupled with the fromString() method, this is very handy for things like
        storing rectangles (particularly component positions) in XML attributes.

        @see fromString
    */
    // const String toString() const throw();

    /** Parses a string containing a rectangle's details.

        The string should contain 4 integer tokens, in the form "x y width height". They
        can be comma or whitespace separated.

        This method is intended to go with the toString() method, to form an easy way
        of saving/loading rectangles as strings.

        @see toString
    */
    // static const Rect fromString (const String& stringVersion);

    //==============================================================================
    // juce_UseDebuggingNewOperator

private:
    T x, y, w, h;
};


#endif   // __MAJESTY_RECTANGLE_HEADERFILE_H__
/*
  ==============================================================================

   This file is part of the EJUCE library
   which is based on Raw Material Software ltd. JUCE

   EJUCE can be redistributed and/or modified under the terms of the
   GNU General Public License, as published by the Free Software Foundation;
   either version 2 of the License, or (at your option) any later version.

   EJUCE is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with EJUCE if not, visit www.gnu.org/licenses or write to the
   Free Software Foundation, Inc., 59 Temple Place, Suite 330,
   Boston, MA 02111-1307 USA

   @author	Asnaghi Lucio

  ==============================================================================
*/

#include "ejuce_Rect.h"


//==============================================================================
template <class T>
bool Rect<T>::isEmpty() const throw()
{
    return w <= 0 || h <= 0;
}

template <class T>
void Rect<T>::setBounds (const T x_,
                         const T y_,
                         const T w_,
                         const T h_) throw()
{
    x = x_;
    y = y_;
    w = w_;
    h = h_;
}

template <class T>
void Rect<T>::setPosition (const T x_,
                           const T y_) throw()
{
    x = x_;
    y = y_;
}

template <class T>
void Rect<T>::setSize (const T w_,
                       const T h_) throw()
{
    w = w_;
    h = h_;
}

template <class T>
void Rect<T>::translate (const T dx,
                         const T dy) throw()
{
    x += dx;
    y += dy;
}

template <class T>
const Rect<T> Rect<T>::translated (const T dx,
                                   const T dy) const throw()
{
    return Rect<T> (x + dx, y + dy, w, h);
}

template <class T>
void Rect<T>::expand (const T deltaX,
                      const T deltaY) throw()
{
    const T nw = jmax ((T)0, w + deltaX + deltaX);
    const T nh = jmax ((T)0, h + deltaY + deltaY);

    setBounds (x - deltaX,
               y - deltaY,
               nw, nh);
}

template <class T>
const Rect<T> Rect<T>::expanded (const T deltaX,
                                 const T deltaY) const throw()
{
    return Rect<T> (x - deltaX,
                    y - deltaY,
                    w + deltaX + deltaX,
                    h + deltaY + deltaY);
}

template <class T>
bool Rect<T>::operator== (const Rect& other) const throw()
{
    return x == other.x
        && y == other.y
        && w == other.w
        && h == other.h;
}

template <class T>
bool Rect<T>::operator!= (const Rect& other) const throw()
{
    return x != other.x
        || y != other.y
        || w != other.w
        || h != other.h;
}

template <class T>
bool Rect<T>::contains (const T px,
                        const T py) const throw()
{
    return px >= x
        && py >= y
        && px < x + w
        && py < y + h;
}

template <class T>
bool Rect<T>::contains (const Rect<T>& other) const throw()
{
    return x <= other.x
        && y <= other.y
        && x + w >= other.x + other.w
        && y + h >= other.y + other.h;
}

template <class T>
bool Rect<T>::intersects (const Rect<T>& other) const throw()
{
    return x + w > other.x
        && y + h > other.y
        && x < other.x + other.w
        && y < other.y + other.h
        && w > 0
        && h > 0;
}

template <class T>
const Rect<T> Rect<T>::getIntersection (const Rect<T>& other) const throw()
{
    const T nx = jmax (x, other.x);
    const T ny = jmax (y, other.y);
    const T nw = jmin (x + w, other.x + other.w) - nx;
    const T nh = jmin (y + h, other.y + other.h) - ny;

    if (nw >= 0 && nh >= 0)
        return Rect<T> (nx, ny, nw, nh);
    else
        return Rect<T>();
}

template <class T>
bool Rect<T>::intersectRectangle (T& x1, T& y1, T& w1, T& h1) const throw()
{
    const T maxX = jmax (x1, x);
    w1 = jmin (x1 + w1, x + w) - maxX;

    if (w1 > 0)
    {
        const T maxY = jmax (y1, y);
        h1 = jmin (y1 + h1, y + h) - maxY;

        if (h1 > 0)
        {
            x1 = maxX;
            y1 = maxY;

            return true;
        }
    }

    return false;
}

template <class T>
bool Rect<T>::intersectRectangles (T& x1, T& y1,T& w1, T& h1,
                                T x2, T y2, T w2, T h2) throw()
{
    const T x = jmax (x1, x2);
    w1 = jmin (x1 + w1, x2 + w2) - x;

    if (w1 > 0)
    {
        const T y = jmax (y1, y2);
        h1 = jmin (y1 + h1, y2 + h2) - y;

        if (h1 > 0)
        {
            x1 = x;
            y1 = y;

            return true;
        }
    }

    return false;
}

template <class T>
const Rect<T> Rect<T>::getUnion (const Rect<T>& other) const throw()
{
    const T newX = jmin (x, other.x);
    const T newY = jmin (y, other.y);

    return Rect<T> (newX, newY,
                    jmax (x + w, other.x + other.w) - newX,
                    jmax (y + h, other.y + other.h) - newY);
}

template <class T>
bool Rect<T>::enlargeIfAdjacent (const Rect<T>& other) throw()
{
    if (x == other.x && getRight() == other.getRight()
        && (other.getBottom() >= y && other.y <= getBottom()))
    {
        const T newY = jmin (y, other.y);
        h = jmax (getBottom(), other.getBottom()) - newY;
        y = newY;
        return true;
    }
    else if (y == other.y && getBottom() == other.getBottom()
              && (other.getRight() >= x && other.x <= getRight()))
    {
        const T newX = jmin (x, other.x);
        w = jmax (getRight(), other.getRight()) - newX;
        x = newX;
        return true;
    }

    return false;
}

template <class T>
bool Rect<T>::reduceIfPartlyContainedIn (const Rect<T>& other) throw()
{
    int inside = 0;
    const T otherR = other.getRight();

    if (x >= other.x && x < otherR)
        inside = 1;

    const T otherB = other.getBottom();

    if (y >= other.y && y < otherB)
        inside |= 2;

    const T r = x + w;

    if (r >= other.x && r < otherR)
        inside |= 4;

    const T b = y + h;

    if (b >= other.y && b < otherB)
        inside |= 8;

    switch (inside)
    {
    case 1 + 2 + 8:
        w = r - otherR;
        x = otherR;
        return true;

    case 1 + 2 + 4:
        h = b - otherB;
        y = otherB;
        return true;

    case 2 + 4 + 8:
        w = other.x - x;
        return true;

    case 1 + 4 + 8:
        h = other.y - y;
        return true;
    }

    return false;
}

/* @XXX - tricky to templatize !
const String Rect::toString() const throw()
{
    String s;
    s.preallocateStorage (16);

    s << x << T(' ')
      << y << T(' ')
      << w << T(' ')
      << h;

    return s;
}

const Rect Rect::fromString (const String& stringVersion)
{
    StringArray toks;
    toks.addTokens (stringVersion.trim(), T(",; \t\r\n"), 0);

    return Rect (toks[0].trim().getDoubleValue(),
                 toks[1].trim().getDoubleValue(),
                 toks[2].trim().getDoubleValue(),
                 toks[3].trim().getDoubleValue());
}
*/

something like this ? (btw working!)
the only problem is how to deal between passing a “Rect intRect” to a function that needs a “Rect dblRect” without needs to write “myFunction (Rect (dblRect.getX(), dblRect.getY()…” and be so verbose…


#5

Yeah, that’s the idea. Too busy to look at this at the moment, but will do eventually…


#6

In the header you should also typedef an internal ‘type’ or whatnot name to the template type so that it can be tested out using higher template tests (you have no idea how useful they can be until you start using them). You should also specify all set things in the class to use that typedef instead of the template as it helps in most IDE’s intellisense.


#7