Rectangle & Array suggestions


#1

Hi,

I have a couple of little suggestions for the next juce version.

I noticed that one of the requirements for a juce::Array ElementType is that it should be int-constructible.

This needlessly forces Array clients to instrument their types with a bogus int constructor (just like juce::Rectangle does).

I guess that the reason of this is because we want the operator [] and remove method to return null pointers on some cases.

This behavior can be kept by default constructing ElementType instead of int-constructing it. (default constructing a pointer type zero-initialises its value, this is what the standard C++ library containers do).

I’m thus proposing the following patch:

--- juce_Array.h
+++ juce_Array.patched.h
@@ -226,11 +226,11 @@
     */
     inline ElementType operator[] (const int index) const throw()
     {
         lock.enter();
         const ElementType result = (index >= 0 && index < numUsed) ? this->elements [index]
-                                                                   : (ElementType) 0;
+                                                                   : ElementType();
         lock.exit();
 
         return result;
     }
 
@@ -749,11 +749,11 @@
             return removed;
         }
         else
         {
             lock.exit();
-            return ElementType (0);
+            return ElementType();
         }
     }
 
     /** Removes an item from the array.

I also have a few syntactic sugar requests regarding the juce::Rectangle interface:

  • Remove the now useless Rectangle(int) constructor
  • Add a Rectangle(int width, int height) constructor
  • Make the translate and expand methods return a non-const reference to this.
  • Add two new methods: shrink(int dw, int dh) and shrinked(int dw, int dh), as shorthands to expand(-dw, -dh) and expanded(-dw, -dh).

These modifications would allow lines like this to compile:

graphics.drawRect(Rectangle(getWidth(), getHeight()).shrink(borderWidth, borderHeight));

Please let me know if all this makes sense to you :slight_smile:

Thanks.

--- juce_Rectangle.h	Fri Apr 27 15:51:04 2007
+++ juce_Rectangle.patched.h	Thu May 31 11:17:41 2007
@@ -52,22 +52,17 @@
     Rectangle();
 
     /** Creates a copy of another rectangle. */
     Rectangle (const Rectangle& other);
 
+    /** Creates a rectangle with a given size at position (0, 0). */
+    Rectangle (const int width, const int height);
+
     /** Creates a rectangle with a given position and size. */
     Rectangle (const int x, const int y,
                const int width, const int height);
 
-    /** Creates a rectangle of zero size.
-
-        (this constructor is just here to allow the class to be used in an Array).
-
-        The default co-ordinates will be (0, 0, 0, 0).
-    */
-    Rectangle (int dummy);
-
     /** Destructor. */
     ~Rectangle();
 
     //==============================================================================
     /** Returns the x co-ordinate of the rectangle's left-hand-side. */
@@ -105,30 +100,56 @@
 
     /** Changes all the rectangle's co-ordinates. */
     void setBounds (const int newX, const int newY,
                     const int newWidth, const int newHeight) throw();
 
-    /** Moves the rectangle's position by adding amount to its x and y co-ordinates. */
-    void translate (const int deltaX,
-                    const int deltaY) throw();
+    /** Moves the rectangle's position by adding amount to its x and y co-ordinates.
+
+        Returns this Rectangle to allow method daisy-chaining.
+
+    */
+    Rectangle& translate (const int deltaX,
+                          const int deltaY) throw();
 
     /** Returns a rectangle which is the same as this one moved by a given amount. */
     const Rectangle translated (const int deltaX,
                                 const int 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).
+
+        Returns this Rectangle to allow method daisy-chaining.
     */
-    void expand (const int deltaX,
-                 const int deltaY) throw();
+    Rectangle& expand (const int deltaX,
+                       const int 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 Rectangle expanded (const int deltaX,
+                              const int deltaY) const throw();
+
+    /** Shrinks the rectangle by a given amount.
+
+        Effectively, its new size is the one resulting from a call to expand(-deltaX, -deltaY).
+
+        Returns this Rectangle to allow method daisy-chaining.
+
+        @see expand
+    */
+    Rectangle& shrink (const int deltaX,
+                       const int deltaY) throw();
+
+    /** Returns a rectangle that is smaller than this one by a given amount.
+
+        Effectively, the rectangle returned is the one returned by a call to expanded(-deltaX, -deltaY).
+    
+        @see expanded
+    */
+    const Rectangle shrinked (const int deltaX,
                               const int deltaY) const throw();
 
     //==============================================================================
     /** Returns true if the two rectangles are identical. */
     bool operator== (const Rectangle& other) const throw();
--- juce_Rectangle.cpp	Fri Apr 27 15:51:22 2007
+++ juce_Rectangle.patched.cpp	Thu May 31 11:19:53 2007
@@ -44,17 +44,16 @@
     w (0),
     h (0)
 {
 }
 
-Rectangle::Rectangle (int)
+Rectangle::Rectangle (const int w_, const int h_)
   : x (0),
     y (0),
-    w (0),
-    h (0)
-{
-}
+    w (w_),
+    h (h_)
+{}
 
 Rectangle::Rectangle (const int x_, const int y_,
                       const int w_, const int h_)
   : x (x_),
     y (y_),
@@ -104,41 +103,57 @@
 {
     w = w_;
     h = h_;
 }
 
-void Rectangle::translate (const int dx,
-                           const int dy) throw()
+Rectangle& Rectangle::translate (const int dx,
+                                 const int dy) throw()
 {
     x += dx;
     y += dy;
+
+    return *this;
 }
 
 const Rectangle Rectangle::translated (const int dx,
                                        const int dy) const throw()
 {
     return Rectangle (x + dx, y + dy, w, h);
 }
 
-void Rectangle::expand (const int deltaX,
-                        const int deltaY) throw()
+Rectangle& Rectangle::expand (const int deltaX,
+                              const int deltaY) throw()
 {
     const int nw = jmax (0, w + deltaX + deltaX);
     const int nh = jmax (0, h + deltaY + deltaY);
 
     setBounds (x - deltaX,
                y - deltaY,
                nw, nh);
+
+    return *this;
 }
 
 const Rectangle Rectangle::expanded (const int deltaX,
                                      const int deltaY) const throw()
 {
     return Rectangle (x - deltaX,
                       y - deltaY,
                       w + deltaX + deltaX,
                       h + deltaY + deltaY);
+}
+
+Rectangle& Rectangle::shrink (const int deltaX,
+                              const int deltaY) throw()
+{
+    return expand(-deltaX, -deltaY);
+}
+
+const Rectangle Rectangle::shrinked (const int deltaX,
+                                     const int deltaY) const throw()
+{
+    return expanded(-deltaX, -deltaY);
 }
 
 bool Rectangle::operator== (const Rectangle& other) const throw()
 {
     return x == other.x

#2

:oops: Sorry, typo.

I meant:


#3

I’m fairly sure I only added the int-constructor because I couldn’t get it to work on all compilers using a default constructor. That was some time ago though, so maybe I’ll have another go, maybe it was an old gcc version I had trouble with… can’t really remember now.

Those are good requests for the Rectangle class - although I’m not too sure about allowing daisy-chaining like that. I’d rather encourage code like this:

instead of

But more importantly, it protects you from subtle mistakes like:

when you actually meant to type “translated”.


#4

Ok. But this generates an extra temporary. Probably not worth worrying about in most cases, anyway.

Yes. Good point.

Seems like there’s a temporary method qualifier missing in the C++ langage :slight_smile:

// Only resolved on temporary instances.
Rectangle& Rectangle :: translate () temporary;

// Resolved on named instances.
void Rectangle :: translate ();

void blah ()
{
  Rectangle r;
  doSomething(r.translate(5, 1)); // Error.
  doSomething(Rectangle(16, 64).translate(6, 9)); // OK.
}

That would make everyone happy :slight_smile:


#5