/*
 * Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
 * http://paperjs.org/
 *
 * Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey
 * http://scratchdisk.com/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/**
 * @name Rectangle
 *
 * @class A Rectangle specifies an area that is enclosed by it's top-left
 * point (x, y), its width, and its height. It should not be confused with a
 * rectangular path, it is not an item.
 */
var Rectangle = Base.extend(/** @lends Rectangle# */{
    _class: 'Rectangle',
    // Tell Base.read that the Rectangle constructor supports reading with index
    _readIndex: true,
    // Enforce creation of beans, as bean getters have hidden parameters.
    // See  #getPoint() below.
    beans: true,

    /**
     * Creates a Rectangle object.
     *
     * @name Rectangle#initialize
     * @param {Point} point the top-left point of the rectangle
     * @param {Size} size the size of the rectangle
     */
    /**
     * Creates a Rectangle object.
     *
     * @name Rectangle#initialize
     * @param {Object} object an object containing properties to be set on the
     *        rectangle.
     *
     * @example // Create a rectangle between {x: 20, y: 20} and {x: 80, y:80}
     * var rectangle = new Rectangle({
     *     point: [20, 20],
     *     size: [60, 60]
     * });
     *
     * @example // Create a rectangle between {x: 20, y: 20} and {x: 80, y:80}
     * var rectangle = new Rectangle({
     *     from: [20, 20],
     *     to: [80, 80]
     * });
     */
    /**
     * Creates a rectangle object.
     *
     * @name Rectangle#initialize
     * @param {Number} x the left coordinate
     * @param {Number} y the top coordinate
     * @param {Number} width
     * @param {Number} height
     */
    /**
     * Creates a rectangle object from the passed points. These do not
     * necessarily need to be the top left and bottom right corners, the
     * constructor figures out how to fit a rectangle between them.
     *
     * @name Rectangle#initialize
     * @param {Point} from The first point defining the rectangle
     * @param {Point} to The second point defining the rectangle
     */
    /**
     * Creates a new rectangle object from the passed rectangle object.
     *
     * @name Rectangle#initialize
     * @param {Rectangle} rt
     */
    initialize: function Rectangle(arg0, arg1, arg2, arg3) {
        var type = typeof arg0,
            read = 0;
        if (type === 'number') {
            // new Rectangle(x, y, width, height)
            this.x = arg0;
            this.y = arg1;
            this.width = arg2;
            this.height = arg3;
            read = 4;
        } else if (type === 'undefined' || arg0 === null) {
            // new Rectangle(), new Rectangle(null)
            this.x = this.y = this.width = this.height = 0;
            read = arg0 === null ? 1 : 0;
        } else if (arguments.length === 1) {
            // This can either be an array, or an object literal.
            if (Array.isArray(arg0)) {
                this.x = arg0[0];
                this.y = arg0[1];
                this.width = arg0[2];
                this.height = arg0[3];
                read = 1;
            } else if (arg0.x !== undefined || arg0.width !== undefined) {
                // Another rectangle or a simple object literal
                // describing one. Use duck typing, and 0 as defaults.
                this.x = arg0.x || 0;
                this.y = arg0.y || 0;
                this.width = arg0.width || 0;
                this.height = arg0.height || 0;
                read = 1;
            } else if (arg0.from === undefined && arg0.to === undefined) {
                // Use #_set to support whatever property the rectangle can
                // take, but handle from/to separately below.
                this.x = this.y = this.width = this.height = 0;
                this._set(arg0);
                read = 1;
            }
        }
        if (!read) {
            // Read a point argument and look at the next value to see whether
            // it's a size or a point, then read accordingly.
            // We're supporting both reading from a normal arguments list and
            // covering the Rectangle({ from: , to: }) constructor, through
            // Point.readNamed().
            var point = Point.readNamed(arguments, 'from'),
                next = Base.peek(arguments);
            this.x = point.x;
            this.y = point.y;
            if (next && next.x !== undefined || Base.hasNamed(arguments, 'to')) {
                // new Rectangle(from, to)
                // Read above why we can use readNamed() to cover both cases.
                var to = Point.readNamed(arguments, 'to');
                this.width = to.x - point.x;
                this.height = to.y - point.y;
                // Check if horizontal or vertical order needs to be reversed.
                if (this.width < 0) {
                    this.x = to.x;
                    this.width = -this.width;
                }
                if (this.height < 0) {
                    this.y = to.y;
                    this.height = -this.height;
                }
            } else {
                // new Rectangle(point, size)
                var size = Size.read(arguments);
                this.width = size.width;
                this.height = size.height;
            }
            read = arguments.__index;
        }
        if (this.__read)
            this.__read = read;
    },

    /**
     * The x position of the rectangle.
     *
     * @name Rectangle#x
     * @type Number
     */

    /**
     * The y position of the rectangle.
     *
     * @name Rectangle#y
     * @type Number
     */

    /**
     * The width of the rectangle.
     *
     * @name Rectangle#width
     * @type Number
     */

    /**
     * The height of the rectangle.
     *
     * @name Rectangle#height
     * @type Number
     */

    /**
     * @ignore
     */
    set: function(x, y, width, height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
        return this;
    },

    /**
     * Returns a copy of the rectangle.
     */
    clone: function() {
        return new Rectangle(this.x, this.y, this.width, this.height);
    },

    /**
     * Checks whether the coordinates and size of the rectangle are equal to
     * that of the supplied rectangle.
     *
     * @param {Rectangle} rect
     * @return {Boolean} {@true if the rectangles are equal}
     */
    equals: function(rect) {
        var rt = Base.isPlainValue(rect)
                ? Rectangle.read(arguments)
                : rect;
        return rt === this
                || rt && this.x === rt.x && this.y === rt.y
                    && this.width === rt.width && this.height === rt.height
                || false;
    },

    /**
     * @return {String} a string representation of this rectangle
     */
    toString: function() {
        var f = Formatter.instance;
        return '{ x: ' + f.number(this.x)
                + ', y: ' + f.number(this.y)
                + ', width: ' + f.number(this.width)
                + ', height: ' + f.number(this.height)
                + ' }';
    },

    _serialize: function(options) {
        var f = options.formatter;
        // See Point#_serialize()
        return [f.number(this.x),
                f.number(this.y),
                f.number(this.width),
                f.number(this.height)];
    },

    /**
     * The top-left point of the rectangle
     *
     * @type Point
     * @bean
     */
    getPoint: function(_dontLink) {
        var ctor = _dontLink ? Point : LinkedPoint;
        return new ctor(this.x, this.y, this, 'setPoint');
    },

    setPoint: function(/* point */) {
        var point = Point.read(arguments);
        this.x = point.x;
        this.y = point.y;
    },


    /**
     * The size of the rectangle
     *
     * @type Size
     * @bean
     */
    getSize: function(_dontLink) {
        var ctor = _dontLink ? Size : LinkedSize;
        return new ctor(this.width, this.height, this, 'setSize');
    },

    setSize: function(/* size */) {
        var size = Size.read(arguments);
        // Keep track of how dimensions were specified through this._fix*
        // attributes.
        // _fixX / Y can either be 0 (l), 0.5 (center) or 1 (r), and is used as
        // direct factors to calculate the x / y adujstments from the size
        // differences.
        // _fixW / H is either 0 (off) or 1 (on), and is used to protect
        // widht / height values against changes.
        if (this._fixX)
            this.x += (this.width - size.width) * this._fixX;
        if (this._fixY)
            this.y += (this.height - size.height) * this._fixY;
        this.width = size.width;
        this.height = size.height;
        this._fixW = 1;
        this._fixH = 1;
    },

    /**
     * {@grouptitle Side Positions}
     *
     * The position of the left hand side of the rectangle. Note that this
     * doesn't move the whole rectangle; the right hand side stays where it was.
     *
     * @type Number
     * @bean
     */
    getLeft: function() {
        return this.x;
    },

    setLeft: function(left) {
        if (!this._fixW)
            this.width -= left - this.x;
        this.x = left;
        this._fixX = 0;
    },

    /**
     * The top coordinate of the rectangle. Note that this doesn't move the
     * whole rectangle: the bottom won't move.
     *
     * @type Number
     * @bean
     */
    getTop: function() {
        return this.y;
    },

    setTop: function(top) {
        if (!this._fixH)
            this.height -= top - this.y;
        this.y = top;
        this._fixY = 0;
    },

    /**
     * The position of the right hand side of the rectangle. Note that this
     * doesn't move the whole rectangle; the left hand side stays where it was.
     *
     * @type Number
     * @bean
     */
    getRight: function() {
        return this.x + this.width;
    },

    setRight: function(right) {
        // Turn _fixW off if we specify two _fixX values
        if (this._fixX !== undefined && this._fixX !== 1)
            this._fixW = 0;
        if (this._fixW)
            this.x = right - this.width;
        else
            this.width = right - this.x;
        this._fixX = 1;
    },

    /**
     * The bottom coordinate of the rectangle. Note that this doesn't move the
     * whole rectangle: the top won't move.
     *
     * @type Number
     * @bean
     */
    getBottom: function() {
        return this.y + this.height;
    },

    setBottom: function(bottom) {
        // Turn _fixH off if we specify two _fixY values
        if (this._fixY !== undefined && this._fixY !== 1)
            this._fixH = 0;
        if (this._fixH)
            this.y = bottom - this.height;
        else
            this.height = bottom - this.y;
        this._fixY = 1;
    },

    /**
     * The center-x coordinate of the rectangle.
     *
     * @type Number
     * @bean
     * @ignore
     */
    getCenterX: function() {
        return this.x + this.width * 0.5;
    },

    setCenterX: function(x) {
        this.x = x - this.width * 0.5;
        this._fixX = 0.5;
    },

    /**
     * The center-y coordinate of the rectangle.
     *
     * @type Number
     * @bean
     * @ignore
     */
    getCenterY: function() {
        return this.y + this.height * 0.5;
    },

    setCenterY: function(y) {
        this.y = y - this.height * 0.5;
        this._fixY = 0.5;
    },

    /**
     * {@grouptitle Corner and Center Point Positions}
     *
     * The center point of the rectangle.
     *
     * @type Point
     * @bean
     */
    getCenter: function(_dontLink) {
        var ctor = _dontLink ? Point : LinkedPoint;
        return new ctor(this.getCenterX(), this.getCenterY(), this, 'setCenter');
    },

    setCenter: function(/* point */) {
        var point = Point.read(arguments);
        this.setCenterX(point.x);
        this.setCenterY(point.y);
        // A special setter where we allow chaining, because it comes in handy
        // in a couple of places in core.
        return this;
    },

    /**
     * The top-left point of the rectangle.
     *
     * @name Rectangle#topLeft
     * @type Point
     */

    /**
     * The top-right point of the rectangle.
     *
     * @name Rectangle#topRight
     * @type Point
     */

    /**
     * The bottom-left point of the rectangle.
     *
     * @name Rectangle#bottomLeft
     * @type Point
     */

    /**
     * The bottom-right point of the rectangle.
     *
     * @name Rectangle#bottomRight
     * @type Point
     */

    /**
     * The left-center point of the rectangle.
     *
     * @name Rectangle#leftCenter
     * @type Point
     */

    /**
     * The top-center point of the rectangle.
     *
     * @name Rectangle#topCenter
     * @type Point
     */

    /**
     * The right-center point of the rectangle.
     *
     * @name Rectangle#rightCenter
     * @type Point
     */

    /**
     * The bottom-center point of the rectangle.
     *
     * @name Rectangle#bottomCenter
     * @type Point
     */

     /**
      * The area of the rectangle in square points.
      *
      * @type Number
      * @bean
      */
    getArea: function() {
        return this.width * this.height;
    },

    /**
     * @return {Boolean} {@true if the rectangle is empty}
     */
    isEmpty: function() {
        return this.width === 0 || this.height === 0;
    },

    /**
     * {@grouptitle Geometric Tests}
     *
     * Tests if the specified point is inside the boundary of the rectangle.
     *
     * @name Rectangle#contains
     * @function
     * @param {Point} point the specified point
     * @return {Boolean} {@true if the point is inside the rectangle's boundary}
     *
     * @example {@paperscript}
     * // Checking whether the mouse position falls within the bounding
     * // rectangle of an item:
     *
     * // Create a circle shaped path at {x: 80, y: 50}
     * // with a radius of 30.
     * var circle = new Path.Circle(new Point(80, 50), 30);
     * circle.fillColor = 'red';
     *
     * function onMouseMove(event) {
     *     // Check whether the mouse position intersects with the
     *     // bounding box of the item:
     *     if (circle.bounds.contains(event.point)) {
     *         // If it intersects, fill it with green:
     *         circle.fillColor = 'green';
     *     } else {
     *         // If it doesn't intersect, fill it with red:
     *         circle.fillColor = 'red';
     *     }
     * }
     */
    /**
     * Tests if the interior of the rectangle entirely contains the specified
     * rectangle.
     *
     * @name Rectangle#contains
     * @function
     * @param {Rectangle} rect The specified rectangle
     * @return {Boolean} {@true if the rectangle entirely contains the specified
     *                   rectangle}
     *
     * @example {@paperscript}
     * // Checking whether the bounding box of one item is contained within
     * // that of another item:
     *
     * // All newly created paths will inherit these styles:
     * project.currentStyle = {
     *     fillColor: 'green',
     *     strokeColor: 'black'
     * };
     *
     * // Create a circle shaped path at {x: 80, y: 50}
     * // with a radius of 45.
     * var largeCircle = new Path.Circle(new Point(80, 50), 45);
     *
     * // Create a smaller circle shaped path in the same position
     * // with a radius of 30.
     * var circle = new Path.Circle(new Point(80, 50), 30);
     *
     * function onMouseMove(event) {
     *     // Move the circle to the position of the mouse:
     *     circle.position = event.point;
     *
     *     // Check whether the bounding box of the smaller circle
     *     // is contained within the bounding box of the larger item:
     *     if (largeCircle.bounds.contains(circle.bounds)) {
     *         // If it does, fill it with green:
     *         circle.fillColor = 'green';
     *         largeCircle.fillColor = 'green';
     *     } else {
     *         // If doesn't, fill it with red:
     *         circle.fillColor = 'red';
     *         largeCircle.fillColor = 'red';
     *     }
     * }
     */
    contains: function(arg) {
        // Detect rectangles either by checking for 'width' on the passed object
        // or by looking at the amount of elements in the arguments list,
        // or the passed array:
        return arg && arg.width !== undefined
                || (Array.isArray(arg) ? arg : arguments).length == 4
                ? this._containsRectangle(Rectangle.read(arguments))
                : this._containsPoint(Point.read(arguments));
    },

    _containsPoint: function(point) {
        var x = point.x,
            y = point.y;
        return x >= this.x && y >= this.y
                && x <= this.x + this.width
                && y <= this.y + this.height;
    },

    _containsRectangle: function(rect) {
        var x = rect.x,
            y = rect.y;
        return x >= this.x && y >= this.y
                && x + rect.width <= this.x + this.width
                && y + rect.height <= this.y + this.height;
    },

    /**
     * Tests if the interior of this rectangle intersects the interior of
     * another rectangle. Rectangles just touching each other are considered
     * as non-intersecting.
     *
     * @param {Rectangle} rect the specified rectangle
     * @return {Boolean} {@true if the rectangle and the specified rectangle
     *                   intersect each other}
     *
     * @example {@paperscript}
     * // Checking whether the bounding box of one item intersects with
     * // that of another item:
     *
     * // All newly created paths will inherit these styles:
     * project.currentStyle = {
     *     fillColor: 'green',
     *     strokeColor: 'black'
     * };
     *
     * // Create a circle shaped path at {x: 80, y: 50}
     * // with a radius of 45.
     * var largeCircle = new Path.Circle(new Point(80, 50), 45);
     *
     * // Create a smaller circle shaped path in the same position
     * // with a radius of 30.
     * var circle = new Path.Circle(new Point(80, 50), 30);
     *
     * function onMouseMove(event) {
     *     // Move the circle to the position of the mouse:
     *     circle.position = event.point;
     *
     *     // Check whether the bounding box of the two circle
     *     // shaped paths intersect:
     *     if (largeCircle.bounds.intersects(circle.bounds)) {
     *         // If it does, fill it with green:
     *         circle.fillColor = 'green';
     *         largeCircle.fillColor = 'green';
     *     } else {
     *         // If doesn't, fill it with red:
     *         circle.fillColor = 'red';
     *         largeCircle.fillColor = 'red';
     *     }
     * }
     */
    intersects: function(/* rect */) {
        var rect = Rectangle.read(arguments);
        return rect.x + rect.width > this.x
                && rect.y + rect.height > this.y
                && rect.x < this.x + this.width
                && rect.y < this.y + this.height;
    },

    touches: function(/* rect */) {
        var rect = Rectangle.read(arguments);
        return rect.x + rect.width >= this.x
                && rect.y + rect.height >= this.y
                && rect.x <= this.x + this.width
                && rect.y <= this.y + this.height;
    },

    /**
     * {@grouptitle Boolean Operations}
     *
     * Returns a new rectangle representing the intersection of this rectangle
     * with the specified rectangle.
     *
     * @param {Rectangle} rect The rectangle to be intersected with this
     *                         rectangle
     * @return {Rectangle} the largest rectangle contained in both the specified
     *                     rectangle and in this rectangle
     *
     * @example {@paperscript}
     * // Intersecting two rectangles and visualizing the result using rectangle
     * // shaped paths:
     *
     * // Create two rectangles that overlap each other
     * var size = new Size(50, 50);
     * var rectangle1 = new Rectangle(new Point(25, 15), size);
     * var rectangle2 = new Rectangle(new Point(50, 40), size);
     *
     * // The rectangle that represents the intersection of the
     * // two rectangles:
     * var intersected = rectangle1.intersect(rectangle2);
     *
     * // To visualize the intersecting of the rectangles, we will
     * // create rectangle shaped paths using the Path.Rectangle
     * // constructor.
     *
     * // Have all newly created paths inherit a black stroke:
     * project.currentStyle.strokeColor = 'black';
     *
     * // Create two rectangle shaped paths using the abstract rectangles
     * // we created before:
     * new Path.Rectangle(rectangle1);
     * new Path.Rectangle(rectangle2);
     *
     * // Create a path that represents the intersected rectangle,
     * // and fill it with red:
     * var intersectionPath = new Path.Rectangle(intersected);
     * intersectionPath.fillColor = 'red';
     */
    intersect: function(/* rect */) {
        var rect = Rectangle.read(arguments),
            x1 = Math.max(this.x, rect.x),
            y1 = Math.max(this.y, rect.y),
            x2 = Math.min(this.x + this.width, rect.x + rect.width),
            y2 = Math.min(this.y + this.height, rect.y + rect.height);
        return new Rectangle(x1, y1, x2 - x1, y2 - y1);
    },

    /**
     * Returns a new rectangle representing the union of this rectangle with the
     * specified rectangle.
     *
     * @param {Rectangle} rect the rectangle to be combined with this rectangle
     * @return {Rectangle} the smallest rectangle containing both the specified
     *                     rectangle and this rectangle.
     */
    unite: function(/* rect */) {
        var rect = Rectangle.read(arguments),
            x1 = Math.min(this.x, rect.x),
            y1 = Math.min(this.y, rect.y),
            x2 = Math.max(this.x + this.width, rect.x + rect.width),
            y2 = Math.max(this.y + this.height, rect.y + rect.height);
        return new Rectangle(x1, y1, x2 - x1, y2 - y1);
    },

    /**
     * Adds a point to this rectangle. The resulting rectangle is the
     * smallest rectangle that contains both the original rectangle and the
     * specified point.
     *
     * After adding a point, a call to {@link #contains(point)} with the added
     * point as an argument does not necessarily return {@code true}.
     * The {@link Rectangle#contains(point)} method does not return {@code true}
     * for points on the right or bottom edges of a rectangle. Therefore, if the
     * added point falls on the left or bottom edge of the enlarged rectangle,
     * {@link Rectangle#contains(point)} returns {@code false} for that point.
     *
     * @param {Point} point
     */
    include: function(/* point */) {
        var point = Point.read(arguments);
        var x1 = Math.min(this.x, point.x),
            y1 = Math.min(this.y, point.y),
            x2 = Math.max(this.x + this.width, point.x),
            y2 = Math.max(this.y + this.height, point.y);
        return new Rectangle(x1, y1, x2 - x1, y2 - y1);
    },

    /**
     * Expands the rectangle by the specified amount in horizontal and
     * vertical directions.
     *
     * @name Rectangle#expand
     * @function
     * @param {Number|Size|Point} amount the amount to expand the rectangle in
     * both directions
     */
    /**
     * Expands the rectangle by the specified amounts in horizontal and
     * vertical directions.
     *
     * @name Rectangle#expand
     * @function
     * @param {Number} hor the amount to expand the rectangle in horizontal
     * direction
     * @param {Number} ver the amount to expand the rectangle in horizontal
     * direction
     */
    expand: function(/* amount */) {
        var amount = Size.read(arguments),
            hor = amount.width,
            ver = amount.height;
        return new Rectangle(this.x - hor / 2, this.y - ver / 2,
                this.width + hor, this.height + ver);
    },

    /**
     * Scales the rectangle by the specified amount from its center.
     *
     * @name Rectangle#scale
     * @function
     * @param {Number} amount
     */
    /**
     * Scales the rectangle in horizontal direction by the specified
     * {@code hor} amount and in vertical direction by the specified {@code ver}
     * amount from its center.
     *
     * @name Rectangle#scale
     * @function
     * @param {Number} hor
     * @param {Number} ver
     */
    scale: function(hor, ver) {
        return this.expand(this.width * hor - this.width,
                this.height * (ver === undefined ? hor : ver) - this.height);
    }
}, Base.each([
        ['Top', 'Left'], ['Top', 'Right'],
        ['Bottom', 'Left'], ['Bottom', 'Right'],
        ['Left', 'Center'], ['Top', 'Center'],
        ['Right', 'Center'], ['Bottom', 'Center']
    ],
    function(parts, index) {
        var part = parts.join('');
        // find out if the first of the pair is an x or y property,
        // by checking the first character for [R]ight or [L]eft;
        var xFirst = /^[RL]/.test(part);
        // Rename Center to CenterX or CenterY:
        if (index >= 4)
            parts[1] += xFirst ? 'Y' : 'X';
        var x = parts[xFirst ? 0 : 1],
            y = parts[xFirst ? 1 : 0],
            getX = 'get' + x,
            getY = 'get' + y,
            setX = 'set' + x,
            setY = 'set' + y,
            get = 'get' + part,
            set = 'set' + part;
        this[get] = function(_dontLink) {
            var ctor = _dontLink ? Point : LinkedPoint;
            return new ctor(this[getX](), this[getY](), this, set);
        };
        this[set] = function(/* point */) {
            var point = Point.read(arguments);
            this[setX](point.x);
            this[setY](point.y);
        };
    }, {
        // Enforce creation of beans, as bean getters have hidden parameters
        // See _dontLink argument above.
        beans: true
    }
));

/**
 * @name LinkedRectangle
 *
 * @class An internal version of Rectangle that notifies its owner of each
 * change through setting itself again on the setter that corresponds to the
 * getter that produced this LinkedRectangle.
 * Note: This prototype is not exported.
 *
 * @private
 */
var LinkedRectangle = Rectangle.extend({
    // Have LinkedRectangle appear as a normal Rectangle in debugging
    initialize: function Rectangle(x, y, width, height, owner, setter) {
        this.set(x, y, width, height, true);
        this._owner = owner;
        this._setter = setter;
    },

    set: function(x, y, width, height, _dontNotify) {
        this._x = x;
        this._y = y;
        this._width = width;
        this._height = height;
        if (!_dontNotify)
            this._owner[this._setter](this);
        return this;
    }
}, new function() {
    var proto = Rectangle.prototype;

    return Base.each(['x', 'y', 'width', 'height'], function(key) {
        var part = Base.capitalize(key);
        var internal = '_' + key;
        this['get' + part] = function() {
            return this[internal];
        };

        this['set' + part] = function(value) {
            this[internal] = value;
            // Check if this setter is called from another one which sets
            // _dontNotify, as it will notify itself
            if (!this._dontNotify)
                this._owner[this._setter](this);
        };
    }, Base.each(['Point', 'Size', 'Center',
            'Left', 'Top', 'Right', 'Bottom', 'CenterX', 'CenterY',
            'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight',
            'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'],
        function(key) {
            var name = 'set' + key;
            this[name] = function(/* value */) {
                // Make sure the above setters of x, y, width, height do not
                // each notify the owner, as we're going to take care of this
                // afterwards here, only once per change.
                this._dontNotify = true;
                proto[name].apply(this, arguments);
                this._dontNotify = false;
                this._owner[this._setter](this);
            };
        }, /** @lends Rectangle# */{
            /**
             * {@grouptitle Item Bounds}
             *
             * Specifies whether an item's bounds are selected and will also
             * mark the item as selected.
             *
             * Paper.js draws the visual bounds of selected items on top of your
             * project. This can be useful for debugging.
             *
             * @type Boolean
             * @default false
             * @bean
             */
            isSelected: function() {
                return this._owner._boundsSelected;
            },

            setSelected: function(selected) {
                var owner = this._owner;
                if (owner.setSelected) {
                    owner._boundsSelected = selected;
                    // Update the owner's selected state too, so the bounds
                    // actually get drawn. When deselecting, take a path's
                    // _selectedSegmentState into account too, since it will
                    // have to remain selected even when bounds are deselected
                    owner.setSelected(selected || owner._selectedSegmentState > 0);
                }
            }
        })
    );
});
