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

/**
 * @name Path
 *
 * @class The path item represents a path in a Paper.js project.
 *
 * @extends PathItem
 */
// DOCS: Explain that path matrix is always applied with each transformation.
var Path = PathItem.extend(/** @lends Path# */{
	_class: 'Path',
	_serializeFields: {
		segments: [],
		closed: false
	},

	/**
	 * Creates a new path item and places it at the top of the active layer.
	 *
	 * @name Path#initialize
	 * @param {Segment[]} [segments] An array of segments (or points to be
	 * converted to segments) that will be added to the path
	 * @return {Path} the newly created path
	 *
	 * @example
	 * // Create an empty path and add segments to it:
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 * path.add(new Point(30, 30));
	 * path.add(new Point(100, 100));
	 *
	 * @example
	 * // Create a path with two segments:
	 * var segments = [new Point(30, 30), new Point(100, 100)];
	 * var path = new Path(segments);
	 * path.strokeColor = 'black';
	 */
	/**
	 * Creates a new path item from an object description and places it at the
	 * top of the active layer.
	 *
	 * @name Path#initialize
	 * @param {Object} object an object literal containing properties to
	 * be set on the path
	 * @return {Path} the newly created path
	 *
	 * @example {@paperscript}
	 * var path = new Path({
	 * 	segments: [[20, 20], [80, 80], [140, 20]],
	 * 	fillColor: 'black',
	 * 	closed: true
	 * });
	 *
	 * @example {@paperscript}
	 * var path = new Path({
	 * 	segments: [[20, 20], [80, 80], [140, 20]],
	 * 	strokeColor: 'red',
	 * 	strokeWidth: 20,
	 * 	strokeCap: 'round',
	 * 	selected: true
	 * });
	 */
	/**
	 * Creates a new path item from SVG path-data and places it at the top of
	 * the active layer.
	 *
	 * @name Path#initialize
	 * @param {String} pathData the SVG path-data that describes the geometry
	 * of this path.
	 * @return {Path} the newly created path
	 *
	 * @example {@paperscript}
	 * var pathData = 'M100,50c0,27.614-22.386,50-50,50S0,77.614,0,50S22.386,0,50,0S100,22.386,100,50';
	 * var path = new Path(pathData);
	 * path.fillColor = 'red';
	 */
	initialize: function Path(arg) {
		this._closed = false;
		this._segments = [];
		// arg can either be an object literal containing properties to be set
		// on the path, a list of segments to be set, or the first of multiple
		// arguments describing separate segments.
		// If it is an array, it can also be a description of a point, so
		// check its first entry for object as well.
		// But first see if segments are directly passed at all. If not, try
		// _set(arg).
		var segments = Array.isArray(arg)
			? typeof arg[0] === 'object'
				? arg
				: arguments
			// See if it behaves like a segment or a point, but filter out
			// rectangles, as accepted by some Path.Constructor constructors.
			: arg && (arg.size === undefined && (arg.x !== undefined
					|| arg.point !== undefined))
				? arguments
				: null;
		// Always call setSegments() to initialize a few related variables.
		this.setSegments(segments || []);
		if (!segments && typeof arg === 'string') {
			this.setPathData(arg);
			// Erase for _initialize() call below.
			arg = null;
		}
		// Only pass on arg as props if it wasn't consumed for segments already.
		this._initialize(!segments && arg);
	},

	_equals: function(item) {
		return Base.equals(this._segments, item._segments);
	},

	clone: function(insert) {
		var copy = this._clone(new Path({
			segments: this._segments,
			insert: false
		}), insert);
		// Speed up things a little by copy over values that don't need checking
		copy._closed = this._closed;
		if (this._clockwise !== undefined)
			copy._clockwise = this._clockwise;
		return copy;
	},

	_changed: function _changed(flags) {
		_changed.base.call(this, flags);
		if (flags & /*#=*/ ChangeFlag.GEOMETRY) {
			// Delete cached native Path
			delete (this._compound ? this._parent : this)._currentPath;
			delete this._length;
			// Clockwise state becomes undefined as soon as geometry changes.
			delete this._clockwise;
			// Curves are no longer valid
			if (this._curves) {
				for (var i = 0, l = this._curves.length; i < l; i++)
					this._curves[i]._changed(/*#=*/ Change.GEOMETRY);
			}
		} else if (flags & /*#=*/ ChangeFlag.STROKE) {
			// TODO: We could preserve the purely geometric bounds that are not
			// affected by stroke: _bounds.bounds and _bounds.handleBounds
			delete this._bounds;
		}
	},

	/**
	 * The segments contained within the path.
	 *
	 * @type Segment[]
	 * @bean
	 */
	getSegments: function() {
		return this._segments;
	},

	setSegments: function(segments) {
		var fullySelected = this.isFullySelected();
		this._segments.length = 0;
		this._selectedSegmentState = 0;
		// Calculate new curves next time we call getCurves()
		delete this._curves;
		this._add(Segment.readAll(segments));
		if (fullySelected)
			this.setFullySelected(true);
	},

	/**
	 * The first Segment contained within the path.
	 *
	 * @type Segment
	 * @bean
	 */
	getFirstSegment: function() {
		return this._segments[0];
	},

	/**
	 * The last Segment contained within the path.
	 *
	 * @type Segment
	 * @bean
	 */
	getLastSegment: function() {
		return this._segments[this._segments.length - 1];
	},

	/**
	 * The curves contained within the path.
	 *
	 * @type Curve[]
	 * @bean
	 */
	getCurves: function() {
		var curves = this._curves,
			segments = this._segments;
		if (!curves) {
			var length = this._countCurves();
			curves = this._curves = new Array(length);
			for (var i = 0; i < length; i++)
				curves[i] = new Curve(this, segments[i],
					// Use first segment for segment2 of closing curve
					segments[i + 1] || segments[0]);
		}
		return curves;
	},

	/**
	 * The first Curve contained within the path.
	 *
	 * @type Curve
	 * @bean
	 */
	getFirstCurve: function() {
		return this.getCurves()[0];
	},

	/**
	 * The last Curve contained within the path.
	 *
	 * @type Curve
	 * @bean
	 */
	getLastCurve: function() {
		var curves = this.getCurves();
		return curves[curves.length - 1];
	},

	/**
	 * The segments contained within the path, described as SVG style path data.
	 *
	 * @type String
	 * @bean
	 */
	getPathData: function(/* precision */) {
		var segments = this._segments,
			precision = arguments[0],
			f = Formatter.instance,
			parts = [];

		// TODO: Add support for H/V and/or relative commands, where appropriate
		// and resulting in shorter strings
		function addCurve(seg1, seg2, skipLine) {
			var point1 = seg1._point,
				point2 = seg2._point,
				handle1 = seg1._handleOut,
				handle2 = seg2._handleIn;
			if (handle1.isZero() && handle2.isZero()) {
				if (!skipLine) {
					// L = absolute lineto: moving to a point with drawing
					parts.push('L' + f.point(point2, precision));
				}
			} else {
				// c = relative curveto: handle1, handle2 + end - start,
				// end - start
				var end = point2.subtract(point1);
				parts.push('c' + f.point(handle1, precision)
						+ ' ' + f.point(end.add(handle2), precision)
						+ ' ' + f.point(end, precision));
			}
		}

		if (segments.length === 0)
			return '';
		parts.push('M' + f.point(segments[0]._point));
		for (var i = 0, l = segments.length  - 1; i < l; i++)
			addCurve(segments[i], segments[i + 1], false);
		if (this._closed) {
			addCurve(segments[segments.length - 1], segments[0], true);
			parts.push('z');
		}
		return parts.join('');
	},

	/**
	 * Specifies whether the path is closed. If it is closed, Paper.js connects
	 * the first and last segments.
	 *
	 * @type Boolean
	 * @bean
	 *
	 * @example {@paperscript}
	 * var myPath = new Path();
	 * myPath.strokeColor = 'black';
	 * myPath.add(new Point(50, 75));
	 * myPath.add(new Point(100, 25));
	 * myPath.add(new Point(150, 75));
	 *
	 * // Close the path:
	 * myPath.closed = true;
	 */
	isClosed: function() {
		return this._closed;
	},

	setClosed: function(closed) {
		// On-the-fly conversion to boolean:
		if (this._closed != (closed = !!closed)) {
			this._closed = closed;
			// Update _curves length
			if (this._curves) {
				var length = this._curves.length = this._countCurves();
				// If we were closing this path, we need to add a new curve now
				if (closed)
					this._curves[length - 1] = new Curve(this,
						this._segments[length - 1], this._segments[0]);
			}
			this._changed(/*#=*/ Change.GEOMETRY);
		}
	},

	// TODO: Consider adding getSubPath(a, b), returning a part of the current
	// path, with the added benefit that b can be < a, and closed looping is
	// taken into account.

	isEmpty: function() {
		return this._segments.length === 0;
	},

	isPolygon: function() {
		for (var i = 0, l = this._segments.length; i < l; i++) {
			if (!this._segments[i].isLinear())
				return false;
		}
		return true;
	},

	_applyMatrix: function(matrix) {
		var coords = new Array(6);
		for (var i = 0, l = this._segments.length; i < l; i++)
			this._segments[i]._transformCoordinates(matrix, coords, true);
		return true;
	},

	/**
	 * Private method that adds a segment to the segment list. It assumes that
	 * the passed object is a segment already and does not perform any checks.
	 * If a curves list was requested, it will kept in sync with the segments
	 * list automatically.
	 */
	_add: function(segs, index) {
		// Local short-cuts:
		var segments = this._segments,
			curves = this._curves,
			amount = segs.length,
			append = index == null,
			index = append ? segments.length : index;
		// Scan through segments to add first, convert if necessary and set
		// _path and _index references on them.
		for (var i = 0; i < amount; i++) {
			var segment = segs[i];
			// If the segments belong to another path already, clone them before
			// adding:
			if (segment._path)
				segment = segs[i] = segment.clone();
			segment._path = this;
			segment._index = index + i;
			// If parts of this segment are selected, adjust the internal
			// _selectedSegmentState now
			if (segment._selectionState)
				this._updateSelection(segment, 0, segment._selectionState);
		}
		if (append) {
			// Append them all at the end by using push
			segments.push.apply(segments, segs);
		} else {
			// Insert somewhere else
			segments.splice.apply(segments, [index, 0].concat(segs));
			// Adjust the indices of the segments above.
			for (var i = index + amount, l = segments.length; i < l; i++)
				segments[i]._index = i;
		}
		// Keep the curves list in sync all the time in case it as requested
		// already.
		if (curves || segs._curves) {
			if (!curves)
				curves = this._curves = [];
			// We need to step one index down from the inserted segment to
			// get its curve, except for the first segment.
			var from = index > 0 ? index - 1 : index,
				start = from,
				to = Math.min(from + amount, this._countCurves());
			if (segs._curves) {
				// Reuse removed curves.
				curves.splice.apply(curves, [from, 0].concat(segs._curves));
				start += segs._curves.length;
			}
			// Insert new curves, but do not initialize their segments yet,
			// since #_adjustCurves() handles all that for us.
			for (var i = start; i < to; i++)
				curves.splice(i, 0, new Curve(this, null, null));
			// Adjust segments for the curves before and after the removed ones
			this._adjustCurves(from, to);
		}
		this._changed(/*#=*/ Change.GEOMETRY);
		return segs;
	},

	/**
	 * Adjusts segments of curves before and after inserted / removed segments.
	 */
	_adjustCurves: function(from, to) {
		var segments = this._segments,
			curves = this._curves,
			curve;
		for (var i = from; i < to; i++) {
			curve = curves[i];
			curve._path = this;
			curve._segment1 = segments[i];
			curve._segment2 = segments[i + 1] || segments[0];
		}
		// If it's the first segment, correct the last segment of closed
		// paths too:
		if (curve = curves[this._closed && from === 0 ? segments.length - 1
				: from - 1])
			curve._segment2 = segments[from] || segments[0];
		// Fix the segment after the modified range, if it exists
		if (curve = curves[to])
			curve._segment1 = segments[to];
	},

	/**
	 * Returns the amount of curves this path item is supposed to have, based
	 * on its amount of #segments and #closed state.
	 */
	_countCurves: function() {
		var length = this._segments.length;
		// Reduce length by one if it's an open path:
		return !this._closed && length > 0 ? length - 1 : length;
	},

	// DOCS: find a way to document the variable segment parameters of Path#add
	/**
	 * Adds one or more segments to the end of the {@link #segments} array of
	 * this path.
	 *
	 * @param {Segment|Point} segment the segment or point to be added.
	 * @return {Segment} the added segment. This is not necessarily the same
	 * object, e.g. if the segment to be added already belongs to another path.
	 *
	 * @example {@paperscript}
	 * // Adding segments to a path using point objects:
	 * var path = new Path({
	 * 	strokeColor: 'black'
	 * });
	 *
	 * // Add a segment at {x: 30, y: 75}
	 * path.add(new Point(30, 75));
	 *
	 * // Add two segments in one go at {x: 100, y: 20}
	 * // and {x: 170, y: 75}:
	 * path.add(new Point(100, 20), new Point(170, 75));
	 *
	 * @example {@paperscript}
	 * // Adding segments to a path using arrays containing number pairs:
	 * var path = new Path({
	 * 	strokeColor: 'black'
	 * });
	 *
	 * // Add a segment at {x: 30, y: 75}
	 * path.add([30, 75]);
	 *
	 * // Add two segments in one go at {x: 100, y: 20}
	 * // and {x: 170, y: 75}:
	 * path.add([100, 20], [170, 75]);
	 *
	 * @example {@paperscript}
	 * // Adding segments to a path using objects:
	 * var path = new Path({
	 * 	strokeColor: 'black'
	 * });
	 *
	 * // Add a segment at {x: 30, y: 75}
	 * path.add({x: 30, y: 75});
	 *
	 * // Add two segments in one go at {x: 100, y: 20}
	 * // and {x: 170, y: 75}:
	 * path.add({x: 100, y: 20}, {x: 170, y: 75});
	 *
	 * @example {@paperscript}
	 * // Adding a segment with handles to a path:
	 * var path = new Path({
	 * 	strokeColor: 'black'
	 * });
	 *
	 * path.add(new Point(30, 75));
	 *
	 * // Add a segment with handles:
	 * var point = new Point(100, 20);
	 * var handleIn = new Point(-50, 0);
	 * var handleOut = new Point(50, 0);
	 * var added = path.add(new Segment(point, handleIn, handleOut));
	 *
	 * // Select the added segment, so we can see its handles:
	 * added.selected = true;
	 *
	 * path.add(new Point(170, 75));
	 */
	add: function(segment1 /*, segment2, ... */) {
		return arguments.length > 1 && typeof segment1 !== 'number'
			// addSegments
			? this._add(Segment.readAll(arguments))
			// addSegment
			: this._add([ Segment.read(arguments) ])[0];
	},

	/**
	 * Inserts one or more segments at a given index in the list of this path's
	 * segments.
	 *
	 * @param {Number} index the index at which to insert the segment.
	 * @param {Segment|Point} segment the segment or point to be inserted.
	 * @return {Segment} the added segment. This is not necessarily the same
	 * object, e.g. if the segment to be added already belongs to another path.
	 *
	 * @example {@paperscript}
	 * // Inserting a segment:
	 * var myPath = new Path();
	 * myPath.strokeColor = 'black';
	 * myPath.add(new Point(50, 75));
	 * myPath.add(new Point(150, 75));
	 *
	 * // Insert a new segment into myPath at index 1:
	 * myPath.insert(1, new Point(100, 25));
	 *
	 * // Select the segment which we just inserted:
	 * myPath.segments[1].selected = true;
	 *
	 * @example {@paperscript}
	 * // Inserting multiple segments:
	 * var myPath = new Path();
	 * myPath.strokeColor = 'black';
	 * myPath.add(new Point(50, 75));
	 * myPath.add(new Point(150, 75));
	 *
	 * // Insert two segments into myPath at index 1:
	 * myPath.insert(1, [80, 25], [120, 25]);
	 *
	 * // Select the segments which we just inserted:
	 * myPath.segments[1].selected = true;
	 * myPath.segments[2].selected = true;
	 */
	insert: function(index, segment1 /*, segment2, ... */) {
		return arguments.length > 2 && typeof segment1 !== 'number'
			// insertSegments
			? this._add(Segment.readAll(arguments, 1), index)
			// insertSegment
			: this._add([ Segment.read(arguments, 1) ], index)[0];
	},

	addSegment: function(/* segment */) {
		return this._add([ Segment.read(arguments) ])[0];
	},

	insertSegment: function(index /*, segment */) {
		return this._add([ Segment.read(arguments, 1) ], index)[0];
	},

	/**
	 * Adds an array of segments (or types that can be converted to segments)
	 * to the end of the {@link #segments} array.
	 *
	 * @param {Segment[]} segments
	 * @return {Segment[]} an array of the added segments. These segments are
	 * not necessarily the same objects, e.g. if the segment to be added already
	 * belongs to another path.
	 *
	 * @example {@paperscript}
	 * // Adding an array of Point objects:
	 * var path = new Path({
	 * 	strokeColor: 'black'
	 * });
	 * var points = [new Point(30, 50), new Point(170, 50)];
	 * path.addSegments(points);
	 *
	 * @example {@paperscript}
	 * // Adding an array of [x, y] arrays:
	 * var path = new Path({
	 * 	strokeColor: 'black'
	 * });
	 * var array = [[30, 75], [100, 20], [170, 75]];
	 * path.addSegments(array);
	 *
	 * @example {@paperscript}
	 * // Adding segments from one path to another:
	 *
	 * var path = new Path({
	 * 	strokeColor: 'black'
	 * });
	 * path.addSegments([[30, 75], [100, 20], [170, 75]]);
	 *
	 * var path2 = new Path();
	 * path2.strokeColor = 'red';
	 *
	 * // Add the second and third segments of path to path2:
	 * path2.add(path.segments[1], path.segments[2]);
	 *
	 * // Move path2 30pt to the right:
	 * path2.position.x += 30;
	 */
	addSegments: function(segments) {
		return this._add(Segment.readAll(segments));
	},

	/**
	 * Inserts an array of segments at a given index in the path's
	 * {@link #segments} array.
	 *
	 * @param {Number} index the index at which to insert the segments.
	 * @param {Segment[]} segments the segments to be inserted.
	 * @return {Segment[]} an array of the added segments. These segments are
	 * not necessarily the same objects, e.g. if the segment to be added already
	 * belongs to another path.
	 */
	insertSegments: function(index, segments) {
		return this._add(Segment.readAll(segments), index);
	},

	/**
	 * Removes the segment at the specified index of the path's
	 * {@link #segments} array.
	 *
	 * @param {Number} index the index of the segment to be removed
	 * @return {Segment} the removed segment
	 *
	 * @example {@paperscript}
	 * // Removing a segment from a path:
	 *
	 * // Create a circle shaped path at { x: 80, y: 50 }
	 * // with a radius of 35:
	 * var path = new Path.Circle({
	 * 	center: new Point(80, 50),
	 * 	radius: 35,
	 * 	strokeColor: 'black'
	 * });
	 *
	 * // Remove its second segment:
	 * path.removeSegment(1);
	 *
	 * // Select the path, so we can see its segments:
	 * path.selected = true;
	 */
	removeSegment: function(index) {
		return this.removeSegments(index, index + 1)[0] || null;
	},

	/**
	 * Removes all segments from the path's {@link #segments} array.
	 *
	 * @name Path#removeSegments
	 * @alias Path#clear
	 * @function
	 * @return {Segment[]} an array containing the removed segments
	 */
	/**
	 * Removes the segments from the specified {@code from} index to the
	 * {@code to} index from the path's {@link #segments} array.
	 *
	 * @param {Number} from the beginning index, inclusive
	 * @param {Number} [to=segments.length] the ending index, exclusive
	 * @return {Segment[]} an array containing the removed segments
	 *
	 * @example {@paperscript}
	 * // Removing segments from a path:
	 *
	 * // Create a circle shaped path at { x: 80, y: 50 }
	 * // with a radius of 35:
	 * var path = new Path.Circle({
	 * 	center: new Point(80, 50),
	 * 	radius: 35,
	 * 	strokeColor: 'black'
	 * });
	 *
	 * // Remove the segments from index 1 till index 2:
	 * path.removeSegments(1, 2);
	 *
	 * // Select the path, so we can see its segments:
	 * path.selected = true;
	 */
	removeSegments: function(from, to/*, includeCurves */) {
		from = from || 0;
		to = Base.pick(to, this._segments.length);
		var segments = this._segments,
			curves = this._curves,
			count = segments.length, // segment count before removal
			removed = segments.splice(from, to - from),
			amount = removed.length;
		if (!amount)
			return removed;
		// Update selection state accordingly
		for (var i = 0; i < amount; i++) {
			var segment = removed[i];
			if (segment._selectionState)
				this._updateSelection(segment, segment._selectionState, 0);
			// Clear the indices and path references of the removed segments
			delete segment._index;
			delete segment._path;
		}
		// Adjust the indices of the segments above.
		for (var i = from, l = segments.length; i < l; i++)
			segments[i]._index = i;
		// Keep curves in sync
		if (curves) {
			// If we're removing the last segment, remove the last curve (the
			// one to the left of the segment, not to the right, as normally).
			// Also take into account closed paths, which have one curve more
			// than segments.
			var index = from > 0 && to === count + (this._closed ? 1 : 0)
					? from - 1
					: from,
				curves = curves.splice(index, amount);
			// Return the removed curves as well, if we're asked to include
			// them, but exclude the first curve, since that's shared with the
			// previous segment and does not connect the returned segments.
			if (arguments[2])
				removed._curves = curves.slice(1);
			// Adjust segments for the curves before and after the removed ones
			this._adjustCurves(index, index);
		}
		this._changed(/*#=*/ Change.GEOMETRY);
		return removed;
	},

	// DOCS Path#clear()
	clear: '#removeSegments',

	/**
	 * Specifies whether an path is selected and will also return {@code true}
	 * if the path is partially selected, i.e. one or more of its segments is
	 * selected.
	 *
	 * Paper.js draws the visual outlines of selected items on top of your
	 * project. This can be useful for debugging, as it allows you to see the
	 * construction of paths, position of path curves, individual segment points
	 * and bounding boxes of symbol and raster items.
	 *
	 * @type Boolean
	 * @bean
	 * @see Project#selectedItems
	 * @see Segment#selected
	 * @see Point#selected
	 *
	 * @example {@paperscript}
	 * // Selecting an item:
	 * var path = new Path.Circle({
	 * 	center: new Size(80, 50),
	 * 	radius: 35
	 * });
	 * path.selected = true; // Select the path
	 *
	 * @example {@paperscript}
	 * // A path is selected, if one or more of its segments is selected:
	 * var path = new Path.Circle({
	 * 	center: new Size(80, 50),
	 * 	radius: 35
	 * });
	 *
	 * // Select the second segment of the path:
	 * path.segments[1].selected = true;
	 *
	 * // If the path is selected (which it is), set its fill color to red:
	 * if (path.selected) {
	 * 	path.fillColor = 'red';
	 * }
	 *
	 */
	/**
	 * Specifies whether the path and all its segments are selected. Cannot be 
	 * {@code true} on an empty path.
	 *
	 * @type Boolean
	 * @bean
	 *
	 * @example {@paperscript}
	 * // A path is fully selected, if all of its segments are selected:
	 * var path = new Path.Circle({
	 * 	center: new Size(80, 50),
	 * 	radius: 35
	 * });
	 * path.fullySelected = true;
	 *
	 * var path2 = new Path.Circle({
	 * 	center: new Size(180, 50),
	 * 	radius: 35
	 * });
	 *
	 * // Deselect the second segment of the second path:
	 * path2.segments[1].selected = false;
	 *
	 * // If the path is fully selected (which it is),
	 * // set its fill color to red:
	 * if (path.fullySelected) {
	 * 	path.fillColor = 'red';
	 * }
	 *
	 * // If the second path is fully selected (which it isn't, since we just
	 * // deselected its second segment),
	 * // set its fill color to red:
	 * if (path2.fullySelected) {
	 * 	path2.fillColor = 'red';
	 * }
	 */
	isFullySelected: function() {
		var length = this._segments.length;
		return this._selected && length > 0 && this._selectedSegmentState
				=== length * /*#=*/ SelectionState.POINT;
	},

	setFullySelected: function(selected) {
		// No need to call _selectSegments() when selected is false, since
		// #setSelected() does that for us
		if (selected)
			this._selectSegments(true);
		this.setSelected(selected);
	},

	setSelected: function setSelected(selected) {
		// Deselect all segments when path is marked as not selected
		if (!selected)
			this._selectSegments(false);
		// No need to pass true for noChildren since Path has none anyway.
		setSelected.base.call(this, selected);
	},

	_selectSegments: function(selected) {
		var length = this._segments.length;
		this._selectedSegmentState = selected
				? length * /*#=*/ SelectionState.POINT : 0;
		for (var i = 0; i < length; i++)
			this._segments[i]._selectionState = selected
					? /*#=*/ SelectionState.POINT : 0;
	},

	_updateSelection: function(segment, oldState, newState) {
		segment._selectionState = newState;
		var total = this._selectedSegmentState += newState - oldState;
		// Set this path as selected in case we have selected segments. Do not
		// unselect if we're down to 0, as the path itself can still remain
		// selected even when empty.
		if (total > 0)
			this.setSelected(true);
	},

	/**
	 * Converts the curves in a path to straight lines with an even distribution
	 * of points. The distance between the produced segments is as close as
	 * possible to the value specified by the {@code maxDistance} parameter.
	 *
	 * @param {Number} maxDistance the maximum distance between the points
	 *
	 * @example {@paperscript}
	 * // Flattening a circle shaped path:
	 *
	 * // Create a circle shaped path at { x: 80, y: 50 }
	 * // with a radius of 35:
	 * var path = new Path.Circle({
	 * 	center: new Size(80, 50),
	 * 	radius: 35
	 * });
	 *
	 * // Select the path, so we can inspect its segments:
	 * path.selected = true;
	 *
	 * // Create a copy of the path and move it 150 points to the right:
	 * var copy = path.clone();
	 * copy.position.x += 150;
	 *
	 * // Convert its curves to points, with a max distance of 20:
	 * copy.flatten(20);
	 */
	flatten: function(maxDistance) {
		var flattener = new PathFlattener(this),
			pos = 0,
			// Adapt step = maxDistance so the points distribute evenly.
			step = flattener.length / Math.ceil(flattener.length / maxDistance),
			// Add/remove half of step to end, so imprecisions are ok too.
			// For closed paths, remove it, because we don't want to add last
			// segment again
			end = flattener.length + (this._closed ? -step : step) / 2;
		// Iterate over path and evaluate and add points at given offsets
		var segments = [];
		while (pos <= end) {
			segments.push(new Segment(flattener.evaluate(pos, 0)));
			pos += step;
		}
		this.setSegments(segments);
	},

	/**
	 * Smooths a path by simplifying it. The {@link Path#segments} array is
	 * analyzed and replaced by a more optimal set of segments, reducing memory
	 * usage and speeding up drawing.
	 *
	 * @param {Number} [tolerance=2.5]
	 *
	 * @example {@paperscript height=300}
	 * // Click and drag below to draw to draw a line, when you release the
	 * // mouse, the is made smooth using path.simplify():
	 *
	 * var path;
	 * function onMouseDown(event) {
	 * 	// If we already made a path before, deselect it:
	 * 	if (path) {
	 * 		path.selected = false;
	 * 	}
	 * 
	 * 	// Create a new path and add the position of the mouse
	 * 	// as its first segment. Select it, so we can see the
	 * 	// segment points:
	 * 	path = new Path({
	 * 		segments: [event.point],
	 * 		strokeColor: 'black',
	 * 		selected: true
	 * 	});
	 * }
	 * 
	 * function onMouseDrag(event) {
	 * 	// On every drag event, add a segment to the path
	 * 	// at the position of the mouse:
	 * 	path.add(event.point);
	 * }
	 * 
	 * function onMouseUp(event) {
	 * 	// When the mouse is released, simplify the path:
	 * 	path.simplify();
	 * 	path.selected = true;
	 * }
	 */
	simplify: function(tolerance) {
		if (this._segments.length > 2) {
			var fitter = new PathFitter(this, tolerance || 2.5);
			this.setSegments(fitter.fit());
		}
	},

	// TODO: reduceSegments([flatness])

	/**
	 * Splits the path at the given offset. After splitting, the path will be
	 * open. If the path was open already, splitting will result in two paths.
	 * 
	 * @name Path#split
	 * @function
	 * @param {Number} offset the offset at which to split the path
	 * as a number between 0 and {@link Path#length}
	 * @return {Path} the newly created path after splitting, if any
	 * 
	 * @example {@paperscript} // Splitting an open path
	 * var path = new Path();
	 * path.strokeColor = 'black';
	 * path.add(20, 20);
	 * 
	 * // Add an arc through {x: 90, y: 80} to {x: 160, y: 20}
	 * path.arcTo([90, 80], [160, 20]);
	 * 
	 * // Split the path at 30% of its length:
	 * var path2 = path.split(path.length * 0.3);
	 * path2.strokeColor = 'red';
	 * 
	 * // Move the newly created path 40px to the right:
	 * path2.position.x += 40;
	 * 
	 * @example {@paperscript} // Splitting a closed path
	 * var path = new Path.Rectangle({
	 * 	from: [20, 20],
	 * 	to: [80, 80],
	 * 	strokeColor: 'black'
	 * });
	 * 
	 * // Split the path at 60% of its length:
	 * path.split(path.length * 0.6);
	 * 
	 * // Move the first segment, to show where the path
	 * // was split:
	 * path.firstSegment.point.x += 20;
	 * 
	 * // Select the first segment:
	 * path.firstSegment.selected = true;
	 */
	/**
	 * Splits the path at the given curve location. After splitting, the path
	 * will be open. If the path was open already, splitting will result in two
	 * paths.
	 * 
	 * @name Path#split
	 * @function
	 * @param {CurveLocation} location the curve location at which to split
	 * the path
	 * @return {Path} the newly created path after splitting, if any
	 * 
	 * @example {@paperscript}
	 * var path = new Path.Circle({
	 * 	center: view.center,
	 * 	radius: 40,
	 * 	strokeColor: 'black'
	 * });
	 * 
	 * var pointOnCircle = view.center + {
	 * 	length: 40,
	 * 	angle: 30
	 * };
	 * 
	 * var curveLocation = path.getNearestLocation(pointOnCircle);
	 * 
	 * path.split(curveLocation);
	 * path.lastSegment.selected = true;
	 */
	/**
	 * Splits the path at the given curve index and parameter. After splitting,
	 * the path will be open. If the path was open already, splitting will
	 * result in two paths.
	 * 
	 * @example {@paperscript} // Splitting an open path
	 * // Draw a V shaped path:
	 * var path = new Path([20, 20], [50, 80], [80, 20]);
	 * path.strokeColor = 'black';
	 * 
	 * // Split the path half-way down its second curve:
	 * var path2 = path.split(1, 0.5);
	 * 
	 * // Give the resulting path a red stroke-color
	 * // and move it 20px to the right:
	 * path2.strokeColor = 'red';
	 * path2.position.x += 20;
	 * 
	 * @example {@paperscript} // Splitting a closed path
	 * var path = new Path.Rectangle({
	 * 	from: [20, 20],
	 * 	to: [80, 80],
	 * 	strokeColor: 'black'
	 * });
	 * 
	 * // Split the path half-way down its second curve:
	 * path.split(2, 0.5);
	 * 
	 * // Move the first segment, to show where the path
	 * // was split:
	 * path.firstSegment.point.x += 20;
	 * 
	 * // Select the first segment:
	 * path.firstSegment.selected = true;
	 * 
	 * @param {Number} index the index of the curve in the {@link Path#curves}
	 * array at which to split
	 * @param {Number} parameter the parameter at which the curve will be split
	 * @return {Path} the newly created path after splitting, if any
	 */
	split: function(index, parameter) {
		if (parameter === null)
			return;
		if (arguments.length === 1) {
			var arg = index;
			// split(offset), convert offset to location
			if (typeof arg === 'number')
				arg = this.getLocationAt(arg);
			// split(location)
			index = arg.index;
			parameter = arg.parameter;
		}
		if (parameter >= 1) {
			// t == 1 is the same as t == 0 and index ++
			index++;
			parameter--;
		}
		var curves = this.getCurves();
		if (index >= 0 && index < curves.length) {
			// Only divide curves if we're not on an existing segment already.
			if (parameter > 0) {
				// Divide the curve with the index at given parameter.
				// Increase because dividing adds more segments to the path.
				curves[index++].divide(parameter, true);
			}
			// Create the new path with the segments to the right of given
			// parameter, which are removed from the current path. Pass true
			// for includeCurves, since we want to preserve and move them to
			// the new path through _add(), allowing us to have CurveLocation
			// keep the connection to the new path through moved curves. 
			var segs = this.removeSegments(index, this._segments.length, true),
				path;
			if (this._closed) {
				// If the path is closed, open it and move the segments round,
				// otherwise create two paths.
				this.setClosed(false);
				// Just have path point to this. The moving around of segments
				// will happen below.
				path = this;
			} else if (index > 0) {
				// Pass true for _preserve, in case of CompoundPath, to avoid 
				// reversing of path direction, which would mess with segs!
				// Use _clone to copy over all other attributes, including style
				path = this._clone(new Path().insertAbove(this, true));
			}
			path._add(segs, 0);
			// Add dividing segment again. In case of a closed path, that's the
			// beginning segment again at the end, since we opened it.
			this.addSegment(segs[0]);
			return path;
		}
		return null;
	},

	/**
	 * Specifies whether the path is oriented clock-wise.
	 *
	 * @type Boolean
	 * @bean
	 */
	isClockwise: function() {
		if (this._clockwise !== undefined)
			return this._clockwise;
		return Path.isClockwise(this._segments);
	},

	setClockwise: function(clockwise) {
		// Only revers the path if its clockwise orientation is not the same
		// as what it is now demanded to be.
		// On-the-fly conversion to boolean:
		if (this.isClockwise() != (clockwise = !!clockwise))
			this.reverse();
		// Reverse only flips _clockwise state if it was already set, so let's
		// always set this here now.
		this._clockwise = clockwise;
	},

	/**
	 * Reverses the orientation of the path, by reversing all its segments.
	 */
	reverse: function() {
		this._segments.reverse();
		// Reverse the handles:
		for (var i = 0, l = this._segments.length; i < l; i++) {
			var segment = this._segments[i];
			var handleIn = segment._handleIn;
			segment._handleIn = segment._handleOut;
			segment._handleOut = handleIn;
			segment._index = i;
		}
		// Clear curves since it all has changed.
		delete this._curves;
		// Flip clockwise state if it's defined
		if (this._clockwise !== undefined)
			this._clockwise = !this._clockwise;
	},

	// DOCS: document Path#join in more detail.
	/**
	 * Joins the path with the specified path, which will be removed in the
	 * process.
	 *
	 * @param {Path} path
	 *
	 * @example {@paperscript}
	 * // Joining two paths:
	 * var path = new Path({
	 * 	segments: [[30, 25], [30, 75]],
	 * 	strokeColor: 'black'
	 * });
	 * 
	 * var path2 = new Path({
	 * 	segments: [[200, 25], [200, 75]],
	 * 	strokeColor: 'black'
	 * });
	 * 
	 * // Join the paths:
	 * path.join(path2);
	 *
	 * @example {@paperscript}
	 * // Joining two paths that share a point at the start or end of their
	 * // segments array:
	 * var path = new Path({
	 * 	segments: [[30, 25], [30, 75]],
	 * 	strokeColor: 'black'
	 * });
	 * 
	 * var path2 = new Path({
	 * 	segments: [[30, 25], [80, 25]],
	 * 	strokeColor: 'black'
	 * });
	 * 
	 * // Join the paths:
	 * path.join(path2);
	 * 
	 * // After joining, path with have 3 segments, since it
	 * // shared its first segment point with the first
	 * // segment point of path2.
	 * 
	 * // Select the path to show that they have joined:
	 * path.selected = true;
	 *
	 * @example {@paperscript}
	 * // Joining two paths that connect at two points:
	 * var path = new Path({
	 * 	segments: [[30, 25], [80, 25], [80, 75]],
	 * 	strokeColor: 'black'
	 * });
	 * 
	 * var path2 = new Path({
	 * 	segments: [[30, 25], [30, 75], [80, 75]],
	 * 	strokeColor: 'black'
	 * });
	 * 
	 * // Join the paths:
	 * path.join(path2);
	 * 
	 * // Because the paths were joined at two points, the path is closed
	 * // and has 4 segments.
	 * 
	 * // Select the path to show that they have joined:
	 * path.selected = true;
	 */
	join: function(path) {
		if (path) {
			var segments = path._segments,
				last1 = this.getLastSegment(),
				last2 = path.getLastSegment();
			if (last1._point.equals(last2._point))
				path.reverse();
			var first1,
				first2 = path.getFirstSegment();
			if (last1._point.equals(first2._point)) {
				last1.setHandleOut(first2._handleOut);
				this._add(segments.slice(1));
			} else {
				first1 = this.getFirstSegment();
				if (first1._point.equals(first2._point))
					path.reverse();
				last2 = path.getLastSegment();
				if (first1._point.equals(last2._point)) {
					first1.setHandleIn(last2._handleIn);
					// Prepend all segments from path except the last one
					this._add(segments.slice(0, segments.length - 1), 0);
				} else {
					this._add(segments.slice());
				}
			}
			if (path.closed)
				this._add([segments[0]]);
			path.remove();
			// Close if they touch in both places. Fetch the segments again
			// since they may have changed.
			first1 = this.getFirstSegment();
			last1 = this.getLastSegment();
			if (last1._point.equals(first1._point)) {
				first1.setHandleIn(last1._handleIn);
				last1.remove();
				this.setClosed(true);
			}
			this._changed(/*#=*/ Change.GEOMETRY);
			return true;
		}
		return false;
	},

	/**
	 * The approximate length of the path in points.
	 *
	 * @type Number
	 * @bean
	 */
	getLength: function() {
		if (this._length == null) {
			var curves = this.getCurves();
			this._length = 0;
			for (var i = 0, l = curves.length; i < l; i++)
				this._length += curves[i].getLength();
		}
		return this._length;
	},

	/**
	 * The area of the path in square points. Self-intersecting paths can
	 * contain sub-areas that cancel each other out.
	 *
	 * @type Number
	 * @bean
	 */
	getArea: function() {
		var curves = this.getCurves();
		var area = 0;
		for (var i = 0, l = curves.length; i < l; i++)
			area += curves[i].getArea();
		return area;
	},

	_getOffset: function(location) {
		var index = location && location.getIndex();
		if (index != null) {
			var curves = this.getCurves(),
				offset = 0;
			for (var i = 0; i < index; i++)
				offset += curves[i].getLength();
			var curve = curves[index];
			return offset + curve.getLength(0, location.getParameter());
		}
		return null;
	},

	/**
	 * Returns the curve location of the specified point if it lies on the
	 * path, {@code null} otherwise.
	 * @param {Point} point the point on the path.
	 * @return {CurveLocation} the curve location of the specified point.
	 */
	getLocationOf: function(point) {
		point = Point.read(arguments);
		var curves = this.getCurves();
		for (var i = 0, l = curves.length; i < l; i++) {
			var loc = curves[i].getLocationOf(point);
			if (loc)
				return loc;
		}
		return null;
	},

	// DOCS: document Path#getLocationAt
	/**
	 * {@grouptitle Positions on Paths and Curves}
	 *
	 * @param {Number} offset
	 * @param {Boolean} [isParameter=false]
	 * @return {CurveLocation}
	 */
	getLocationAt: function(offset, isParameter) {
		var curves = this.getCurves(),
			length = 0;
		if (isParameter) {
			// offset consists of curve index and curve parameter, before and
			// after the fractional digit.
			var index = ~~offset; // = Math.floor()
			return curves[index].getLocationAt(offset - index, true);
		}
		for (var i = 0, l = curves.length; i < l; i++) {
			var start = length,
				curve = curves[i];
			length += curve.getLength();
			if (length >= offset) {
				// Found the segment within which the length lies
				return curve.getLocationAt(offset - start);
			}
		}
		// It may be that through impreciseness of getLength, that the end
		// of the curves was missed:
		if (offset <= this.getLength())
			return new CurveLocation(curves[curves.length - 1], 1);
		return null;
	},

	/**
	 * Calculates the point on the path at the given offset.
	 *
	 * @param {Number} offset
	 * @param {Boolean} [isParameter=false]
	 * @return {Point} the point at the given offset
	 *
	 * @example {@paperscript height=150}
	 * // Finding the point on a path at a given offset:
	 *
	 * // Create an arc shaped path:
	 * var path = new Path({
	 * 	strokeColor: 'black'
	 * });
	 * 
	 * path.add(new Point(40, 100));
	 * path.arcTo(new Point(150, 100));
	 * 
	 * // We're going to be working with a third of the length
	 * // of the path as the offset:
	 * var offset = path.length / 3;
	 * 
	 * // Find the point on the path:
	 * var point = path.getPointAt(offset);
	 * 
	 * // Create a small circle shaped path at the point:
	 * var circle = new Path.Circle({
	 * 	center: point,
	 * 	radius: 3,
	 * 	fillColor: 'red'
	 * });
	 *
	 * @example {@paperscript height=150}
	 * // Iterating over the length of a path:
	 *
	 * // Create an arc shaped path:
	 * var path = new Path({
	 * 	strokeColor: 'black'
	 * });
	 *
	 * path.add(new Point(40, 100));
	 * path.arcTo(new Point(150, 100));
	 *
	 * var amount = 5;
	 * var length = path.length;
	 * for (var i = 0; i < amount + 1; i++) {
	 * 	var offset = i / amount * length;
	 *
	 * 	// Find the point on the path at the given offset:
	 * 	var point = path.getPointAt(offset);
	 *
	 * 	// Create a small circle shaped path at the point:
	 * 	var circle = new Path.Circle({
	 * 		center: point,
	 * 		radius: 3,
	 * 		fillColor: 'red'
	 * 	});
	 * }
	 */
	getPointAt: function(offset, isParameter) {
		var loc = this.getLocationAt(offset, isParameter);
		return loc && loc.getPoint();
	},

	/**
	 * Calculates the tangent to the path at the given offset as a vector point.
	 *
	 * @param {Number} offset
	 * @param {Boolean} [isParameter=false]
	 * @return {Point} the tangent vector at the given offset
	 *
	 * @example {@paperscript height=150}
	 * // Working with the tangent vector at a given offset:
	 *
	 * // Create an arc shaped path:
	 * var path = new Path({
	 * 	strokeColor: 'black'
	 * });
	 *
	 * path.add(new Point(40, 100));
	 * path.arcTo(new Point(150, 100));
	 *
	 * // We're going to be working with a third of the length
	 * // of the path as the offset:
	 * var offset = path.length / 3;
	 *
	 * // Find the point on the path:
	 * var point = path.getPointAt(offset);
	 *
	 * // Find the tangent vector at the given offset:
	 * var tangent = path.getTangentAt(offset);
	 *
	 * // Make the tangent vector 60pt long:
	 * tangent.length = 60;
	 *
	 * var line = new Path({
	 * 	segments: [point, point + tangent],
	 * 	strokeColor: 'red'
	 * })
	 *
	 * @example {@paperscript height=200}
	 * // Iterating over the length of a path:
	 *
	 * // Create an arc shaped path:
	 * var path = new Path({
	 * 	strokeColor: 'black'
	 * });
	 *
	 * path.add(new Point(40, 100));
	 * path.arcTo(new Point(150, 100));
	 *
	 * var amount = 6;
	 * var length = path.length;
	 * for (var i = 0; i < amount + 1; i++) {
	 * 	var offset = i / amount * length;
	 *
	 * 	// Find the point on the path at the given offset:
	 * 	var point = path.getPointAt(offset);
	 *
	 * 	// Find the normal vector on the path at the given offset:
	 * 	var tangent = path.getTangentAt(offset);
	 *
	 * 	// Make the tangent vector 60pt long:
	 * 	tangent.length = 60;
	 *
	 * 	var line = new Path({
	 * 		segments: [point, point + tangent],
	 * 		strokeColor: 'red'
	 * 	})
	 * }
	 */
	getTangentAt: function(offset, isParameter) {
		var loc = this.getLocationAt(offset, isParameter);
		return loc && loc.getTangent();
	},

	/**
	 * Calculates the normal to the path at the given offset as a vector point.
	 *
	 * @param {Number} offset
	 * @param {Boolean} [isParameter=false]
	 * @return {Point} the normal vector at the given offset
	 *
	 * @example {@paperscript height=150}
	 * // Working with the normal vector at a given offset:
	 *
	 * // Create an arc shaped path:
	 * var path = new Path({
	 * 	strokeColor: 'black'
	 * });
	 *
	 * path.add(new Point(40, 100));
	 * path.arcTo(new Point(150, 100));
	 *
	 * // We're going to be working with a third of the length
	 * // of the path as the offset:
	 * var offset = path.length / 3;
	 *
	 * // Find the point on the path:
	 * var point = path.getPointAt(offset);
	 *
	 * // Find the normal vector at the given offset:
	 * var normal = path.getNormalAt(offset);
	 *
	 * // Make the normal vector 30pt long:
	 * normal.length = 30;
	 *
	 * var line = new Path({
	 * 	segments: [point, point + normal],
	 * 	strokeColor: 'red'
	 * });
	 *
	 * @example {@paperscript height=200}
	 * // Iterating over the length of a path:
	 *
	 * // Create an arc shaped path:
	 * var path = new Path({
	 * 	strokeColor: 'black'
	 * });
	 *
	 * path.add(new Point(40, 100));
	 * path.arcTo(new Point(150, 100));
	 *
	 * var amount = 10;
	 * var length = path.length;
	 * for (var i = 0; i < amount + 1; i++) {
	 * 	var offset = i / amount * length;
	 *
	 * 	// Find the point on the path at the given offset:
	 * 	var point = path.getPointAt(offset);
	 *
	 * 	// Find the normal vector on the path at the given offset:
	 * 	var normal = path.getNormalAt(offset);
	 *
	 * 	// Make the normal vector 30pt long:
	 * 	normal.length = 30;
	 *
	 * 	var line = new Path({
	 * 		segments: [point, point + normal],
	 * 		strokeColor: 'red'
	 * 	});
	 * }
	 */
	getNormalAt: function(offset, isParameter) {
		var loc = this.getLocationAt(offset, isParameter);
		return loc && loc.getNormal();
	},

	/**
	 * Returns the nearest location on the path to the specified point.
	 *
	 * @function
	 * @param point {Point} the point for which we search the nearest location
	 * @return {CurveLocation} the location on the path that's the closest to
	 * the specified point
	 */
	getNearestLocation: function(point) {
		point = Point.read(arguments);
		var curves = this.getCurves(),
			minDist = Infinity,
			minLoc = null;
		for (var i = 0, l = curves.length; i < l; i++) {
			var loc = curves[i].getNearestLocation(point);
			if (loc._distance < minDist) {
				minDist = loc._distance;
				minLoc = loc;
			}
		}
		return minLoc;
	},

	/**
	 * Returns the nearest point on the path to the specified point.
	 *
	 * @function
	 * @param point {Point} the point for which we search the nearest point
	 * @return {Point} the point on the path that's the closest to the specified
	 * point
	 * 
	 * @example {@paperscript height=200}
	 * var star = new Path.Star({
	 * 	center: view.center,
	 * 	points: 10,
	 * 	radius1: 30,
	 * 	radius2: 60,
	 * 	strokeColor: 'black'
	 * });
	 * 
	 * var circle = new Path.Circle({
	 * 	center: view.center,
	 * 	radius: 3,
	 * 	fillColor: 'red'
	 * });
	 * 
	 * function onMouseMove(event) {
	 * 	// Get the nearest point from the mouse position
	 * 	// to the star shaped path:
	 * 	var nearestPoint = star.getNearestPoint(event.point);
	 * 
	 * 	// Move the red circle to the nearest point:
	 * 	circle.position = nearestPoint;
	 * }
	 */
	getNearestPoint: function(point) {
		// We need to use point to avoid minification issues and prevent method
		// from turning into a bean (by removal of the point argument).
		point = Point.read(arguments);
		return this.getNearestLocation(point).getPoint();
	},

	getStyle: function() {
		// If this path is part of a CompoundPath, use the paren't style instead
		var parent = this._parent;
		return (parent && parent._type === 'compound-path'
				? parent : this)._style;
	},

	// DOCS: toShape

	toShape: function(insert) {
		if (!this._closed)
			return null;

		var segments = this._segments,
			type,
			size,
			radius,
			topCenter;

		function isColinear(i, j) {
			return segments[i].isColinear(segments[j]);
		}

		function isOrthogonal(i) {
			return segments[i].isOrthogonal();
		}

		function isArc(i) {
			return segments[i].isArc();
		}

		function getDistance(i, j) {
			return segments[i]._point.getDistance(segments[j]._point);
		}

		// See if actually have any curves in the path. Differentiate
		// between straight objects (line, polyline, rect, and  polygon) and
		// objects with curves(circle, ellipse, roundedRectangle).
		if (this.isPolygon() && segments.length === 4
				&& isColinear(0, 2) && isColinear(1, 3) && isOrthogonal(1)) {
			type = Shape.Rectangle;
			size = new Size(getDistance(0, 3), getDistance(0, 1));
			topCenter = segments[1]._point.add(segments[2]._point).divide(2);
		} else if (segments.length === 8 && isArc(0) && isArc(2) && isArc(4)
				&& isArc(6) && isColinear(1, 5) && isColinear(3, 7)) {
			// It's a rounded rectangle.
			type = Shape.Rectangle;
			size = new Size(getDistance(1, 6), getDistance(0, 3));
			// Subtract side lengths from total width and divide by 2 to get the
			// corner radius size.
			radius = size.subtract(new Size(getDistance(0, 7),
					getDistance(1, 2))).divide(2);
			topCenter = segments[3]._point.add(segments[4]._point).divide(2);
		} else if (segments.length === 4
				&& isArc(0) && isArc(1) && isArc(2) && isArc(3)) {
			// If the distance between (point0 and point2) and (point1
			// and point3) are equal, then it is a circle
			if (Numerical.isZero(getDistance(0, 2) - getDistance(1, 3))) {
				type = Shape.Circle;
				radius = getDistance(0, 2) / 2;
			} else {
				type = Shape.Ellipse;
				radius = new Size(getDistance(2, 0) / 2, getDistance(3, 1) / 2);
			}
			topCenter = segments[1]._point;
		}

		if (type) {
			var center = this.getPosition(true),
				shape = new type({
					center: center,
					size: size,
					radius: radius,
					insert: false
				});
			// Determine and apply the shape's angle of rotation.
			shape.rotate(topCenter.subtract(center).getAngle() + 90);
			shape.setStyle(this._style);
			// Insert is true by default.
			if (insert || insert === undefined)
				shape.insertAbove(this);
			return shape;
		}
		return null;
	},

	_getWinding: function(point) {
		var closed = this._closed;
		// If the path is not closed, we should not bail out in case it has a
		// fill color!
		if (!closed && !this.hasFill()
				// We need to call the internal _getBounds, to get non-
				// transformed bounds.
				|| !this._getBounds('getRoughBounds')._containsPoint(point))
			return 0;
		// Use the crossing number algorithm, by counting the crossings of the
		// beam in right y-direction with the shape, and see if it's an odd
		// number, meaning the starting point is inside the shape.
		// http://en.wikipedia.org/wiki/Point_in_polygon
		var curves = this.getCurves(),
			segments = this._segments,
			winding = 0,
			// Reuse arrays for root-finding, give garbage collector a break
			roots1 = [],
			roots2 = [],
			last = (closed
					? curves[curves.length - 1]
					// Create a straight closing line for open paths, just like
					// how filling open paths works.
					: new Curve(segments[segments.length - 1]._point,
						segments[0]._point)).getValues(),
			previous = last;
		for (var i = 0, l = curves.length; i < l; i++) {
			var curve = curves[i].getValues(),
				x = curve[0],
				y = curve[1];
			// Filter out curves with 0-length (all 4 points in the same place):
			if (!(x === curve[2] && y === curve[3] && x === curve[4]
					&& y === curve[5] && x === curve[6] && y === curve[7])) {
				winding += Curve._getWinding(curve, previous, point.x, point.y,
						roots1, roots2);
				previous = curve;
			}
		}
		if (!closed) {
			winding += Curve._getWinding(last, previous, point.x, point.y,
					roots1, roots2);
		}
		return winding;
	},

	_hitTest: function(point, options) {
		var style = this.getStyle(),
			segments = this._segments,
			closed = this._closed,
			tolerance = options.tolerance,
			radius = 0, join, cap, miterLimit,
			that = this,
			area, loc, res;

		if (options.stroke) {
			radius = style.getStrokeWidth() / 2;
			if (radius > 0) {
				join = style.getStrokeJoin();
				cap = style.getStrokeCap();
				miterLimit = radius * style.getMiterLimit();
			} else {
				join = cap = 'round';
			}
			// Add some tolerance, so even when no stroke is set, will match for
			// stroke locations.
			radius += tolerance;
		}

		function checkPoint(seg, pt, name) {
			if (point.getDistance(pt) < tolerance)
				return new HitResult(name, that, { segment: seg, point: pt });
		}

		function checkSegmentPoints(seg, ends) {
			var pt = seg._point;
			// Note, when checking for ends, we don't also check for handles,
			// since this will happen afterwards in a separate loop, see below.
			return (ends || options.segments) && checkPoint(seg, pt, 'segment')
				|| (!ends && options.handles) && (
					checkPoint(seg, pt.add(seg._handleIn), 'handle-in') ||
					checkPoint(seg, pt.add(seg._handleOut), 'handle-out'));
		}

		// Code to check stroke join / cap areas

		function addAreaPoint(point) {
			area.push(point);
		}

		// In order to be able to reuse crossings counting code, we describe
		// each line as a curve values array.
		function getAreaCurve(index) {
			var p1 = area[index],
				p2 = area[(index + 1) % area.length];
			return [p1.x, p1.y, p1.x, p1.y, p2.x, p2.y, p2.x ,p2.y];
		}

		function isInArea(point) {
			var length = area.length,
				previous = getAreaCurve(length - 1),
				roots1 = [],
				roots2 = [],
				winding = 0;
			for (var i = 0; i < length; i++) {
				var curve = getAreaCurve(i);
				winding += Curve._getWinding(curve, previous, point.x, point.y,
						roots1, roots2);
				previous = curve;
			}
			return !!winding;
		}

		function checkSegmentStroke(segment) {
			// Handle joins / caps that are not round specificelly, by
			// hit-testing their polygon areas.
			if (join !== 'round' || cap !== 'round') {
				area = [];
				if (closed || segment._index > 0
						&& segment._index < segments.length - 1) {
					// It's a join. See that it's not a round one (one of
					// the handles has to be zero too for this!)
					if (join !== 'round' && (segment._handleIn.isZero() 
							|| segment._handleOut.isZero()))
						Path._addSquareJoin(segment, join, radius, miterLimit,
								addAreaPoint, true);
				} else if (cap !== 'round') {
					// It's a cap
					Path._addSquareCap(segment, cap, radius, addAreaPoint, true);
				}
				// See if the above produced an area to check for
				if (area.length > 0)
					return isInArea(point);
			}
			// Fallback scenario is a round join / cap, but make sure we
			// didn't check for areas already.
			return point.getDistance(segment._point) <= radius;
		}

		// If we're asked to query for segments, ends or handles, do all that
		// before stroke or fill.
		if (options.ends && !options.segments && !closed) {
			if (res = checkSegmentPoints(segments[0], true)
					|| checkSegmentPoints(segments[segments.length - 1], true))
				return res;
		} else if (options.segments || options.handles) {
			for (var i = 0, l = segments.length; i < l; i++) {
				if (res = checkSegmentPoints(segments[i]))
					return res;
			}
		}
		// If we're querying for stroke, perform that before fill
		if (radius > 0) {
			loc = this.getNearestLocation(point);
			if (loc) {
				// Now see if we're on a segment, and if so, check for its
				// stroke join / cap first. If not, do a normal radius check
				// for round strokes.
				var parameter = loc.getParameter();
				if (parameter === 0 || parameter === 1) {
					if (!checkSegmentStroke(loc.getSegment()))
						loc = null;
				} else  if (loc._distance > radius) {
					loc = null;
				}
			}
			// If we have miter joins, we may not be done yet, since they can be
			// longer than the radius. Check for each segment within reach now.
			if (!loc && join === 'miter') {
				for (var i = 0, l = segments.length; i < l; i++) {
					var segment = segments[i];
					if (point.getDistance(segment._point) <= miterLimit
							&& checkSegmentStroke(segment)) {
						loc = segment.getLocation();
						break;
					}
				}
			}
		}
		// Don't process loc yet, as we also need to query for stroke after fill
		// in some cases. Simply skip fill query if we already have a matching
		// stroke.
		return !loc && options.fill && this.hasFill() && this._contains(point)
				? new HitResult('fill', this)
				: loc
					// TODO: Do we need to transform loc back to the coordinate
					// system of the DOM level on which the inquiry was started?
					? new HitResult('stroke', this, { location: loc })
					: null;
	}

	// TODO: intersects(item)
	// TODO: contains(item)
	// TODO: intersect(item)
	// TODO: unite(item)
	// TODO: exclude(item)
}, new function() { // Scope for drawing

	// Note that in the code below we're often accessing _x and _y on point
	// objects that were read from segments. This is because the SegmentPoint
	// class overrides the plain x / y properties with getter / setters and
	// stores the values in these private properties internally. To avoid
	// calling of getter functions all the time we directly access these private
	// properties here. The distinction between normal Point objects and
	// SegmentPoint objects maybe seem a bit tedious but is worth the benefit in
	// performance.

	function drawHandles(ctx, segments, matrix, size) {
		var half = size / 2;

		function drawHandle(index) {
			var hX = coords[index],
				hY = coords[index + 1];
			if (pX != hX || pY != hY) {
				ctx.beginPath();
				ctx.moveTo(pX, pY);
				ctx.lineTo(hX, hY);
				ctx.stroke();
				ctx.beginPath();
				ctx.arc(hX, hY, half, 0, Math.PI * 2, true);
				ctx.fill();
			}
		}

		var coords = new Array(6);
		for (var i = 0, l = segments.length; i < l; i++) {
			var segment = segments[i];
			segment._transformCoordinates(matrix, coords, false);
			var state = segment._selectionState,
				selected = state & /*#=*/ SelectionState.POINT,
				pX = coords[0],
				pY = coords[1];
			if (selected || (state & /*#=*/ SelectionState.HANDLE_IN))
				drawHandle(2);
			if (selected || (state & /*#=*/ SelectionState.HANDLE_OUT))
				drawHandle(4);
			// Draw a rectangle at segment.point:
			ctx.save();
			ctx.beginPath();
			ctx.rect(pX - half, pY - half, size, size);
			ctx.fill();
			// If the point is not selected, draw a white square that is 1 px
			// smaller on all sides:
			if (!selected) {
				ctx.beginPath();
				ctx.rect(pX - half + 1, pY - half + 1, size - 2, size - 2);
				ctx.fillStyle = '#ffffff';
				ctx.fill();
			}
			ctx.restore();
		}
	}

	function drawSegments(ctx, path, matrix) {
		var segments = path._segments,
			length = segments.length,
			coords = new Array(6),
			first = true,
			curX, curY,
			prevX, prevY,
			inX, inY,
			outX, outY;

		function drawSegment(i) {
			var segment = segments[i];
			// Optimise code when no matrix is provided by accessing semgent
			// points hand handles directly, since this is the default when
			// drawing paths. Matrix is only used for drawing selections.
			if (matrix) {
				segment._transformCoordinates(matrix, coords, false);
				curX = coords[0];
				curY = coords[1];
			} else {
				var point = segment._point;
				curX = point._x;
				curY = point._y;
			}
			if (first) {
				ctx.moveTo(curX, curY);
				first = false;
			} else {
				if (matrix) {
					inX = coords[2];
					inY = coords[3];
				} else {
					var handle = segment._handleIn;
					inX = curX + handle._x;
					inY = curY + handle._y;
				}
				if (inX == curX && inY == curY && outX == prevX && outY == prevY) {
					ctx.lineTo(curX, curY);
				} else {
					ctx.bezierCurveTo(outX, outY, inX, inY, curX, curY);
				}
			}
			prevX = curX;
			prevY = curY;
			if (matrix) {
				outX = coords[4];
				outY = coords[5];
			} else {
				var handle = segment._handleOut;
				outX = prevX + handle._x;
				outY = prevY + handle._y;
			}
		}

		for (var i = 0; i < length; i++)
			drawSegment(i);
		// Close path by drawing first segment again
		if (path._closed && length > 1)
			drawSegment(0);
	}

	return {
		_draw: function(ctx, param) {
			var clip = param.clip,
				// Also mark this Path as _compound so _changed() knows about it
				compound = this._compound = param.compound;
			if (!compound)
				ctx.beginPath();

			var style = this.getStyle(),
				hasFill = style.hasFill(),
				hasStroke = style.hasStroke(),
				dashArray = style.getDashArray(),
				// dashLength is only set if we can't draw dashes natively
				dashLength = !paper.support.nativeDash && hasStroke
						&& dashArray && dashArray.length;

			function getOffset(i) {
				// Negative modulo is necessary since we're stepping back 
				// in the dash sequence first.
				return dashArray[((i % dashLength) + dashLength) % dashLength];
			}

			if (this._currentPath) {
				ctx.currentPath = this._currentPath;
			} else if (hasFill || hasStroke && !dashLength || compound || clip){
				// Prepare the canvas path if we have any situation that
				// requires it to be defined.
				drawSegments(ctx, this);
				if (this._closed)
					ctx.closePath();
				// CompoundPath collects its own _currentPath
				if (!compound)
					this._currentPath = ctx.currentPath;
			}

			if (!clip && !compound && (hasFill || hasStroke)) {
				// If the path is part of a compound path or doesn't have a fill
				// or stroke, there is no need to continue.
				this._setStyles(ctx);
				if (hasFill) {
					ctx.fill(style.getWindingRule());
					// If shadowColor is defined, clear it after fill, so it
					// won't be applied to both fill and stroke. If the path is
					// only stroked, we don't have to clear it.
					ctx.shadowColor = 'rgba(0,0,0,0)';
				}
				if (hasStroke) {
					if (dashLength) {
						// We cannot use the path created by drawSegments above
						// Use CurveFlatteners to draw dashed paths:
						// NOTE: We don't cache this path in another currentPath
						// since browsers that support currentPath also support
						// native dashes.
						ctx.beginPath();
						var flattener = new PathFlattener(this),
							length = flattener.length,
							from = -style.getDashOffset(), to,
							i = 0;
						from = from % length;
						// Step backwards in the dash sequence first until the
						// from parameter is below 0.
						while (from > 0) {
							from -= getOffset(i--) + getOffset(i--);
						}
						while (from < length) {
							to = from + getOffset(i++);
							if (from > 0 || to > 0)
								flattener.drawPart(ctx,
										Math.max(from, 0), Math.max(to, 0));
							from = to + getOffset(i++);
						}
					}
					ctx.stroke();
				}
			}
		},

		_drawSelected: function(ctx, matrix) {
			ctx.beginPath();
			drawSegments(ctx, this, matrix);
			// Now stroke it and draw its handles:
			ctx.stroke();
			drawHandles(ctx, this._segments, matrix,
					this._project.options.handleSize || 4);
		}
	};
}, new function() { // Path Smoothing

	/**
	 * Solves a tri-diagonal system for one of coordinates (x or y) of first
	 * bezier control points.
	 *
	 * @param rhs right hand side vector.
	 * @return Solution vector.
	 */
	function getFirstControlPoints(rhs) {
		var n = rhs.length,
			x = [], // Solution vector.
			tmp = [], // Temporary workspace.
			b = 2;
		x[0] = rhs[0] / b;
		// Decomposition and forward substitution.
		for (var i = 1; i < n; i++) {
			tmp[i] = 1 / b;
			b = (i < n - 1 ? 4 : 2) - tmp[i];
			x[i] = (rhs[i] - x[i - 1]) / b;
		}
		// Back-substitution.
		for (var i = 1; i < n; i++) {
			x[n - i - 1] -= tmp[n - i] * x[n - i];
		}
		return x;
	}

	return {
		// Note: Documentation for smooth() is in PathItem
		smooth: function() {
			// This code is based on the work by Oleg V. Polikarpotchkin,
			// http://ov-p.spaces.live.com/blog/cns!39D56F0C7A08D703!147.entry
			// It was extended to support closed paths by averaging overlapping
			// beginnings and ends. The result of this approach is very close to
			// Polikarpotchkin's closed curve solution, but reuses the same
			// algorithm as for open paths, and is probably executing faster as
			// well, so it is preferred.
			var segments = this._segments,
				size = segments.length,
				closed = this._closed,
				n = size,
				// Add overlapping ends for averaging handles in closed paths
				overlap = 0;
			if (size <= 2)
				return;
			if (closed) {
				// Overlap up to 4 points since averaging beziers affect the 4
				// neighboring points
				overlap = Math.min(size, 4);
				n += Math.min(size, overlap) * 2;
			}
			var knots = [];
			for (var i = 0; i < size; i++)
				knots[i + overlap] = segments[i]._point;
			if (closed) {
				// If we're averaging, add the 4 last points again at the
				// beginning, and the 4 first ones at the end.
				for (var i = 0; i < overlap; i++) {
					knots[i] = segments[i + size - overlap]._point;
					knots[i + size + overlap] = segments[i]._point;
				}
			} else {
				n--;
			}
			// Calculate first Bezier control points
			// Right hand side vector
			var rhs = [];

			// Set right hand side X values
			for (var i = 1; i < n - 1; i++)
				rhs[i] = 4 * knots[i]._x + 2 * knots[i + 1]._x;
			rhs[0] = knots[0]._x + 2 * knots[1]._x;
			rhs[n - 1] = 3 * knots[n - 1]._x;
			// Get first control points X-values
			var x = getFirstControlPoints(rhs);

			// Set right hand side Y values
			for (var i = 1; i < n - 1; i++)
				rhs[i] = 4 * knots[i]._y + 2 * knots[i + 1]._y;
			rhs[0] = knots[0]._y + 2 * knots[1]._y;
			rhs[n - 1] = 3 * knots[n - 1]._y;
			// Get first control points Y-values
			var y = getFirstControlPoints(rhs);

			if (closed) {
				// Do the actual averaging simply by linearly fading between the
				// overlapping values.
				for (var i = 0, j = size; i < overlap; i++, j++) {
					var f1 = i / overlap,
						f2 = 1 - f1,
						ie = i + overlap,
						je = j + overlap;
					// Beginning
					x[j] = x[i] * f1 + x[j] * f2;
					y[j] = y[i] * f1 + y[j] * f2;
					// End
					x[je] = x[ie] * f2 + x[je] * f1;
					y[je] = y[ie] * f2 + y[je] * f1;
				}
				n--;
			}
			var handleIn = null;
			// Now set the calculated handles
			for (var i = overlap; i <= n - overlap; i++) {
				var segment = segments[i - overlap];
				if (handleIn)
					segment.setHandleIn(handleIn.subtract(segment._point));
				if (i < n) {
					segment.setHandleOut(
							new Point(x[i], y[i]).subtract(segment._point));
					handleIn = i < n - 1
							? new Point(
								2 * knots[i + 1]._x - x[i + 1],
								2 * knots[i + 1]._y - y[i + 1])
							: new Point(
								(knots[n]._x + x[n - 1]) / 2,
								(knots[n]._y + y[n - 1]) / 2);
				}
			}
			if (closed && handleIn) {
				var segment = this._segments[0];
				segment.setHandleIn(handleIn.subtract(segment._point));
			}
		}
	};
}, new function() { // PostScript-style drawing commands
	/**
	 * Helper method that returns the current segment and checks if a moveTo()
	 * command is required first.
	 */
	function getCurrentSegment(that) {
		var segments = that._segments;
		if (segments.length == 0)
			throw new Error('Use a moveTo() command first');
		return segments[segments.length - 1];
	}

	return {
		// Note: Documentation for these methods is found in PathItem, as they
		// are considered abstract methods of PathItem and need to be defined in
		// all implementing classes.
		moveTo: function(/* point */) {
			// moveTo should only be called at the beginning of paths. But it 
			// can ce called again if there is nothing drawn yet, in which case
			// the first segment gets readjusted.
			if (this._segments.length === 1)
				this.removeSegment(0);
			// Let's not be picky about calling moveTo() when not at the
			// beginning of a path, just bail out:
			if (!this._segments.length)
				this._add([ new Segment(Point.read(arguments)) ]);
		},

		moveBy: function(/* point */) {
			throw new Error('moveBy() is unsupported on Path items.');
		},

		lineTo: function(/* point */) {
			// Let's not be picky about calling moveTo() first:
			this._add([ new Segment(Point.read(arguments)) ]);
		},

		cubicCurveTo: function(/* handle1, handle2, to */) {
			var handle1 = Point.read(arguments),
				handle2 = Point.read(arguments),
				to = Point.read(arguments),
				// First modify the current segment:
				current = getCurrentSegment(this);
			// Convert to relative values:
			current.setHandleOut(handle1.subtract(current._point));
			// And add the new segment, with handleIn set to c2
			this._add([ new Segment(to, handle2.subtract(to)) ]);
		},

		quadraticCurveTo: function(/* handle, to */) {
			var handle = Point.read(arguments),
				to = Point.read(arguments),
				current = getCurrentSegment(this)._point;
			// This is exact:
			// If we have the three quad points: A E D,
			// and the cubic is A B C D,
			// B = E + 1/3 (A - E)
			// C = E + 1/3 (D - E)
			this.cubicCurveTo(
				handle.add(current.subtract(handle).multiply(1 / 3)),
				handle.add(to.subtract(handle).multiply(1 / 3)),
				to
			);
		},

		curveTo: function(/* through, to, parameter */) {
			var through = Point.read(arguments),
				to = Point.read(arguments),
				t = Base.pick(Base.read(arguments), 0.5),
				t1 = 1 - t,
				current = getCurrentSegment(this)._point,
				// handle = (through - (1 - t)^2 * current - t^2 * to) /
				// (2 * (1 - t) * t)
				handle = through.subtract(current.multiply(t1 * t1))
					.subtract(to.multiply(t * t)).divide(2 * t * t1);
			if (handle.isNaN())
				throw new Error(
					'Cannot put a curve through points with parameter = ' + t);
			this.quadraticCurveTo(handle, to);
		},

		arcTo: function(/* to, clockwise | through, to */) {
			// Get the start point:
			var current = getCurrentSegment(this),
				from = current._point,
				through,
				to = Point.read(arguments),
				// Peek at next value to see if it's clockwise,
				// with true as default value.
				clockwise = Base.pick(Base.peek(arguments), true);
			if (typeof clockwise === 'boolean') {
				// arcTo(to, clockwise)
				var middle = from.add(to).divide(2),
				through = middle.add(middle.subtract(from).rotate(
						clockwise ? -90 : 90));
			} else {
				// arcTo(through, to)
				through = to;
				to = Point.read(arguments);
			}
			// Construct the two perpendicular middle lines to (from, through)
			// and (through, to), and intersect them to get the center
			var l1 = new Line(from.add(through).divide(2),
						through.subtract(from).rotate(90), true),
				l2 = new Line(through.add(to).divide(2),
						to.subtract(through).rotate(90), true),
				center = l1.intersect(l2, true),
				line = new Line(from, to),
				throughSide = line.getSide(through);
			if (!center) {
				// If the two lines are colinear, there cannot be an arc as the
				// circle is infinitely big and has no center point. If side is
				// 0, the connecting arc line of this huge circle is a line
				// between the two points, so we can use #lineTo instead.
				// Otherwise we bail out:
				if (!throughSide)
					return this.lineTo(to);
				throw new Error('Cannot put an arc through the given points: '
					+ [from, through, to]);
			}
			var vector = from.subtract(center),
				extent = vector.getDirectedAngle(to.subtract(center)),
				centerSide = line.getSide(center);
			if (centerSide == 0) {
				// If the center is lying on the line, we might have gotten the
				// wrong sign for extent above. Use the sign of the side of the
				// through point.
				extent = throughSide * Math.abs(extent);
			} else if (throughSide == centerSide) {
				// If the center is on the same side of the line (from, to) as
				// the through point, we're extending bellow 180 degrees and
				// need to adapt extent.
				extent -= 360 * (extent < 0 ? -1 : 1);
			}
			var ext = Math.abs(extent),
				count =  ext >= 360 ? 4 : Math.ceil(ext / 90),
				inc = extent / count,
				half = inc * Math.PI / 360,
				z = 4 / 3 * Math.sin(half) / (1 + Math.cos(half)),
				segments = [];
			for (var i = 0; i <= count; i++) {
				// Explicitely use to point for last segment, since depending
				// on values the calculation adds imprecision:
				var pt = i < count ? center.add(vector) : to;
				var out = i < count ? vector.rotate(90).multiply(z) : null;
				if (i == 0) {
					// Modify startSegment
					current.setHandleOut(out);
				} else {
					// Add new Segment
					segments.push(
						new Segment(pt, vector.rotate(-90).multiply(z), out));
				}
				vector = vector.rotate(inc);
			}
			// Add all segments at once at the end for higher performance
			this._add(segments);
		},

		lineBy: function(/* to */) {
			var to = Point.read(arguments),
				current = getCurrentSegment(this)._point;
			this.lineTo(current.add(to));
		},

		curveBy: function(/* through, to, parameter */) {
			var through = Point.read(arguments),
				to = Point.read(arguments),
				parameter = Base.read(arguments),
				current = getCurrentSegment(this)._point;
			this.curveTo(current.add(through), current.add(to), parameter);
		},

		cubicCurveBy: function(/* handle1, handle2, to */) {
			var handle1 = Point.read(arguments),
				handle2 = Point.read(arguments),
				to = Point.read(arguments),
				current = getCurrentSegment(this)._point;
			this.cubicCurveTo(current.add(handle1), current.add(handle2),
					current.add(to));
		},

		quadraticCurveBy: function(/* handle, to */) {
			var handle = Point.read(arguments),
				to = Point.read(arguments),
				current = getCurrentSegment(this)._point;
			this.quadraticCurveTo(current.add(handle), current.add(to));
		},

		arcBy: function(/* to, clockwise | through, to */) {
			var current = getCurrentSegment(this)._point,
				point = current.add(Point.read(arguments)),
				// Peek at next value to see if it's clockwise, with true as
				// default value.
				clockwise = Base.pick(Base.peek(arguments), true);
			if (typeof clockwise === 'boolean') {
				this.arcTo(point, clockwise);
			} else {
				this.arcTo(point, current.add(Point.read(arguments)));
			}
		},

		closePath: function() {
			var first = this.getFirstSegment(),
				last = this.getLastSegment();
			if (first._point.equals(last._point)) {
				first.setHandleIn(last._handleIn);
				last.remove();
			}
			this.setClosed(true);
		}
	};
}, {  // A dedicated scope for the tricky bounds calculations
	// We define all the different getBounds functions as static methods on Path
	// and have #_getBounds directly access these. All static bounds functions
	// below have the same first four parameters: segments, closed, style,
	// matrix, so they can be called from #_getBounds() and also be used in
	// Curve. But not all of them use all these parameters, and some define
	// additional ones after.

	_getBounds: function(getter, matrix) {
		// See #draw() for an explanation of why we can access _style
		// properties directly here:
		return Path[getter](this._segments, this._closed, this.getStyle(),
				matrix);
	},

// Mess with indentation in order to get more line-space below:
statics: {
	/**
	 * Determines whether the segments describe a path in clockwise or counter-
	 * clockwise orientation.
	 *
	 * @private
	 */
	isClockwise: function(segments) {
		var sum = 0;
		// Method derived from:
		// http://stackoverflow.com/questions/1165647
		// We treat the curve points and handles as the outline of a polygon of
		// which we determine the orientation using the method of calculating
		// the sum over the edges. This will work even with non-convex polygons,
		// telling you whether it's mostly clockwise
		// TODO: Check if this works correctly for all open paths.
		for (var i = 0, l = segments.length; i < l; i++) {
			var v = Curve.getValues(
					segments[i], segments[i + 1 < l ? i + 1 : 0]);
			for (var j = 2; j < 8; j += 2)
				sum += (v[j - 2] - v[j]) * (v[j + 1] + v[j - 1]);
		}
		return sum > 0;
	},

	/**
	 * Returns the bounding rectangle of the item excluding stroke width.
	 *
	 * @private
	 */
	getBounds: function(segments, closed, style, matrix, strokePadding) {
		var first = segments[0];
		// If there are no segments, return "empty" rectangle, just like groups,
		// since #bounds is assumed to never return null.
		if (!first)
			return new Rectangle();
		var coords = new Array(6),
			// Make coordinates for first segment available in prevCoords.
			prevCoords = first._transformCoordinates(matrix, new Array(6), false),
			min = prevCoords.slice(0, 2), // Start with values of first point
			max = min.slice(), // clone
			roots = new Array(2);

		function processSegment(segment) {
			segment._transformCoordinates(matrix, coords, false);
			for (var i = 0; i < 2; i++) {
				Curve._addBounds(
					prevCoords[i], // prev.point
					prevCoords[i + 4], // prev.handleOut
					coords[i + 2], // segment.handleIn
					coords[i], // segment.point,
					i, strokePadding ? strokePadding[i] : 0, min, max, roots);
			}
			// Swap coordinate buffers.
			var tmp = prevCoords;
			prevCoords = coords;
			coords = tmp;
		}

		for (var i = 1, l = segments.length; i < l; i++)
			processSegment(segments[i]);
		if (closed)
			processSegment(first);
		return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]);
	},

	/**
	 * Returns the bounding rectangle of the item including stroke width.
	 *
	 * @private
	 */
	getStrokeBounds: function(segments, closed, style, matrix) {
		/**
		 * Returns the horizontal and vertical padding that a transformed round
		 * stroke adds to the bounding box, by calculating the dimensions of a
		 * rotated ellipse.
		 */
		function getPenPadding(radius, matrix) {
			if (!matrix)
				return [radius, radius];
			// If a matrix is provided, we need to rotate the stroke circle
			// and calculate the bounding box of the resulting rotated elipse:
			// Get rotated hor and ver vectors, and determine rotation angle
			// and elipse values from them:
			var mx = matrix.shiftless(),
				hor = mx.transform(new Point(radius, 0)),
				ver = mx.transform(new Point(0, radius)),
				phi = hor.getAngleInRadians(),
				a = hor.getLength(),
				b = ver.getLength();
			// Formula for rotated ellipses:
			// x = cx + a*cos(t)*cos(phi) - b*sin(t)*sin(phi)
			// y = cy + b*sin(t)*cos(phi) + a*cos(t)*sin(phi)
			// Derivates (by Wolfram Alpha):
			// derivative of x = cx + a*cos(t)*cos(phi) - b*sin(t)*sin(phi)
			// dx/dt = a sin(t) cos(phi) + b cos(t) sin(phi) = 0
			// derivative of y = cy + b*sin(t)*cos(phi) + a*cos(t)*sin(phi)
			// dy/dt = b cos(t) cos(phi) - a sin(t) sin(phi) = 0
			// This can be simplified to:
			// tan(t) = -b * tan(phi) / a // x
			// tan(t) =  b * cot(phi) / a // y
			// Solving for t gives:
			// t = pi * n - arctan(b * tan(phi) / a) // x
			// t = pi * n + arctan(b * cot(phi) / a)
			//   = pi * n + arctan(b / tan(phi) / a) // y
			var sin = Math.sin(phi),
				cos = Math.cos(phi),
				tan = Math.tan(phi),
				tx = -Math.atan(b * tan / a),
				ty = Math.atan(b / (tan * a));
			// Due to symetry, we don't need to cycle through pi * n solutions:
			return [Math.abs(a * Math.cos(tx) * cos - b * Math.sin(tx) * sin),
					Math.abs(b * Math.sin(ty) * cos + a * Math.cos(ty) * sin)];
		}

		// TODO: Find a way to reuse 'bounds' cache instead?
		if (!style.hasStroke())
			return Path.getBounds(segments, closed, style, matrix);
		var length = segments.length - (closed ? 0 : 1),
			radius = style.getStrokeWidth() / 2,
			padding = getPenPadding(radius, matrix),
			bounds = Path.getBounds(segments, closed, style, matrix, padding),
			join = style.getStrokeJoin(),
			cap = style.getStrokeCap(),
			miterLimit = radius * style.getMiterLimit();
		// Create a rectangle of padding size, used for union with bounds
		// further down
		var joinBounds = new Rectangle(new Size(padding).multiply(2));

		function add(point) {
			bounds = bounds.include(matrix
				? matrix._transformPoint(point, point) : point);
		}

		function addJoin(segment, join) {
			// When both handles are set in a segment, the join setting is
			// ignored and round is always used.
			if (join === 'round' || !segment._handleIn.isZero()
					&& !segment._handleOut.isZero()) {
				bounds = bounds.unite(joinBounds.setCenter(matrix
					? matrix._transformPoint(segment._point) : segment._point));
			} else {
				Path._addSquareJoin(segment, join, radius, miterLimit, add);
			}
		}

		function addCap(segment, cap) {
			switch (cap) {
			case 'round':
				addJoin(segment, cap);
				break;
			case 'butt':
			case 'square':
				Path._addSquareCap(segment, cap, radius, add); 
				break;
			}
		}

		for (var i = 1; i < length; i++)
			addJoin(segments[i], join);
		if (closed) {
			addJoin(segments[0], join);
		} else if (length > 0) {
			addCap(segments[0], cap);
			addCap(segments[segments.length - 1], cap);
		}
		return bounds;
	},

	_addSquareJoin: function(segment, join, radius, miterLimit, addPoint, area) {
		// Treat bevel and miter in one go, since they share a lot of code.
		var curve2 = segment.getCurve(),
			curve1 = curve2.getPrevious(),
			point = curve2.getPointAt(0, true),
			normal1 = curve1.getNormalAt(1, true),
			normal2 = curve2.getNormalAt(0, true),
			step = normal1.getDirectedAngle(normal2) < 0 ? -radius : radius;
		normal1.setLength(step);
		normal2.setLength(step);
		if (area) {
			addPoint(point);
			addPoint(point.add(normal1));
		}
		if (join === 'miter') {
			// Intersect the two lines
			var corner = new Line(
					point.add(normal1),
					new Point(-normal1.y, normal1.x), true
				).intersect(new Line(
					point.add(normal2),
					new Point(-normal2.y, normal2.x), true
				), true);
			// See if we actually get a bevel point and if its distance is below
			// the miterLimit. If not, make a normal bevel.
			if (corner && point.getDistance(corner) <= miterLimit) {
				addPoint(corner);
				if (!area)
					return;
			}
		}
		// Produce a normal bevel
		if (!area)
			addPoint(point.add(normal1));
		addPoint(point.add(normal2));
	},

	_addSquareCap: function(segment, cap, radius, addPoint, area) {
		// Calculate the corner points of butt and square caps
		var point = segment._point,
			loc = segment.getLocation(),
			normal = loc.getNormal().normalize(radius);
		if (area) {
			addPoint(point.subtract(normal));
			addPoint(point.add(normal));
		}
		// For square caps, we need to step away from point in the direction of
		// the tangent, which is the rotated normal.
		// Checking loc.getParameter() for 0 is to see whether this is the first
		// or the last segment of the open path, in order to determine in which
		// direction to move the point.
		if (cap === 'square')
			point = point.add(normal.rotate(loc.getParameter() == 0 ? -90 : 90));
		addPoint(point.add(normal));
		addPoint(point.subtract(normal));
	},

	/**
	 * Returns the bounding rectangle of the item including handles.
	 *
	 * @private
	 */
	getHandleBounds: function(segments, closed, style, matrix, strokePadding,
			joinPadding) {
		var coords = new Array(6),
			x1 = Infinity,
			x2 = -x1,
			y1 = x1,
			y2 = x2;
		strokePadding = strokePadding / 2 || 0;
		joinPadding = joinPadding / 2 || 0;
		for (var i = 0, l = segments.length; i < l; i++) {
			var segment = segments[i];
			segment._transformCoordinates(matrix, coords, false);
			for (var j = 0; j < 6; j += 2) {
				// Use different padding for points or handles
				var padding = j == 0 ? joinPadding : strokePadding,
					x = coords[j],
					y = coords[j + 1],
					xn = x - padding,
					xx = x + padding,
					yn = y - padding,
					yx = y + padding;
				if (xn < x1) x1 = xn;
				if (xx > x2) x2 = xx;
				if (yn < y1) y1 = yn;
				if (yx > y2) y2 = yx;
			}
		}
		return new Rectangle(x1, y1, x2 - x1, y2 - y1);
	},

	/**
	 * Returns the rough bounding rectangle of the item that is sure to include
	 * all of the drawing, including stroke width.
	 *
	 * @private
	 */
	getRoughBounds: function(segments, closed, style, matrix) {
		// Delegate to handleBounds, but pass on radius values for stroke and
		// joins. Hanlde miter joins specially, by passing the largets radius
		// possible.
		var strokeWidth = style.getStrokeColor() ? style.getStrokeWidth() : 0,
			joinWidth = strokeWidth;
		if (strokeWidth === 0) {
			strokeWidth = /*#=*/ Numerical.TOLERANCE;
		} else {
			if (style.getStrokeJoin() === 'miter')
				joinWidth = strokeWidth * style.getMiterLimit();
			if (style.getStrokeCap() === 'square')
				joinWidth = Math.max(joinWidth, strokeWidth * Math.sqrt(2));
		}
		return Path.getHandleBounds(segments, closed, style, matrix,
				strokeWidth, joinWidth);
	}
}});