diff --git a/dist/paper.d.ts b/dist/paper.d.ts
index 40a14ad113..0d556135c3 100644
--- a/dist/paper.d.ts
+++ b/dist/paper.d.ts
@@ -15,87 +15,85 @@
*/
declare namespace paper {
-
- /**
+ /**
* All properties and functions that expect color values in the form
* of instances of Color objects, also accept named colors and hex values as
* strings which are then converted to instances of
* {@link Color} internally.
*/
- class Color {
- /**
+ class Color {
+ /**
* The type of the color as a string.
*/
- type: string
+ type: string;
- /**
+ /**
* The color components that define the color, including the alpha value
* if defined.
*/
- readonly components: number[]
+ readonly components: number[];
- /**
+ /**
* The color's alpha value as a number between `0` and `1`.
* All colors of the different subclasses support alpha values.
*/
- alpha: number
+ alpha: number;
- /**
+ /**
* The amount of red in the color as a value between `0` and `1`.
*/
- red: number
+ red: number;
- /**
+ /**
* The amount of green in the color as a value between `0` and `1`.
*/
- green: number
+ green: number;
- /**
+ /**
* The amount of blue in the color as a value between `0` and `1`.
*/
- blue: number
+ blue: number;
- /**
+ /**
* The amount of gray in the color as a value between `0` and `1`.
*/
- gray: number
+ gray: number;
- /**
+ /**
* The hue of the color as a value in degrees between `0` and `360`.
*/
- hue: number
+ hue: number;
- /**
+ /**
* The saturation of the color as a value between `0` and `1`.
*/
- saturation: number
+ saturation: number;
- /**
+ /**
* The brightness of the color as a value between `0` and `1`.
*/
- brightness: number
+ brightness: number;
- /**
+ /**
* The lightness of the color as a value between `0` and `1`.
- *
+ *
* Note that all other components are shared with HSB.
*/
- lightness: number
+ lightness: number;
- /**
+ /**
* The gradient object describing the type of gradient and the stops.
*/
- gradient: Gradient
+ gradient: Gradient;
- /**
+ /**
* The highlight point of the gradient.
*/
- highlight: Point
-
+ highlight: Point;
- /**
+ /**
* Creates a RGB Color object.
- *
+ *
* @param red - the amount of red in the color as a value between
* `0` and `1`
* @param green - the amount of green in the color as a value
@@ -105,19 +103,19 @@ declare namespace paper {
* @param alpha - the alpha of the color as a value between `0`
* and `1`
*/
- constructor(red: number, green: number, blue: number, alpha?: number)
+ constructor(red: number, green: number, blue: number, alpha?: number);
- /**
+ /**
* Creates a gray Color object.
- *
+ *
* @param gray - the amount of gray in the color as a value
* between `0` and `1`
* @param alpha - the alpha of the color as a value between `0`
* and `1`
*/
- constructor(gray: number, alpha?: number)
+ constructor(gray: number, alpha?: number);
- /**
+ /**
* Creates a Color object from a CSS string. All common CSS color string
* formats are supported:
* - Named colors (e.g. `'red'`, `'fuchsia'`, …)
@@ -125,20 +123,25 @@ declare namespace paper {
* - RGB strings (`'rgb(255, 128, 0)'`, `'rgba(255, 128, 0, 0.5)'`, …)
* - HSL strings (`'hsl(180deg, 20%, 50%)'`,
* `'hsla(3.14rad, 20%, 50%, 0.5)'`, …)
- *
+ *
* @param color - the color's CSS string representation
*/
- constructor(color: string)
+ constructor(color: string);
- /**
+ /**
* Creates a gradient Color object.
*/
- constructor(gradient: Gradient, origin: Point, destination: Point, highlight?: Point)
+ constructor(
+ gradient: Gradient,
+ origin: Point,
+ destination: Point,
+ highlight?: Point
+ );
- /**
+ /**
* Creates a HSB, HSL or gradient Color object from the properties of
* the provided object:
- *
+ *
* @option hsb.hue {Number} the hue of the color as a value in degrees
* between `0` and `360`
* @option hsb.saturation {Number} the saturation of the color as a
@@ -164,265 +167,262 @@ declare namespace paper {
* the gradient, as an alternative to providing a gradient object
* @option gradient.radial {Boolean} controls whether the gradient is
* radial, as an alternative to providing a gradient object
- *
+ *
* @param object - an object describing the components and
* properties of the color
*/
- constructor(object: object)
+ constructor(object: object);
- /**
+ /**
* Sets the color to the passed values. Note that any sequence of
* parameters that is supported by the various {@link Color}
* constructors also work for calls of `set()`.
*/
- set(...values: any[]): Color
+ set(...values: any[]): Color;
- /**
+ /**
* Converts the color to another type.
- *
+ *
* @param type - the color type to convert to. Possible values:
* {@values 'rgb', 'gray', 'hsb', 'hsl'}
- *
+ *
* @return the converted color as a new instance
*/
- convert(type: string): Color
+ convert(type: string): Color;
- /**
+ /**
* Checks if the color has an alpha value.
- *
+ *
* @return true if the color has an alpha value
*/
- hasAlpha(): boolean
+ hasAlpha(): boolean;
- /**
+ /**
* Checks if the component color values of the color are the
* same as those of the supplied one.
- *
+ *
* @param color - the color to compare with
- *
+ *
* @return true if the colors are the same
*/
- equals(color: Color): boolean
+ equals(color: Color): boolean;
- /**
+ /**
* @return a copy of the color object
*/
- clone(): Color
+ clone(): Color;
- /**
+ /**
* @return a string representation of the color
*/
- toString(): string
+ toString(): string;
- /**
+ /**
* Returns the color as a CSS string.
- *
+ *
* @param hex - whether to return the color in hexadecimal
* representation or as a CSS RGB / RGBA string.
- *
+ *
* @return a CSS string representation of the color
*/
- toCSS(hex: boolean): string
+ toCSS(hex: boolean): string;
- /**
+ /**
* Transform the gradient color by the specified matrix.
- *
+ *
* @param matrix - the matrix to transform the gradient color by
*/
- transform(matrix: Matrix): void
+ transform(matrix: Matrix): void;
- /**
+ /**
* Returns a color object with random {@link #red}, {@link #green}
* and {@link #blue} values between `0` and `1`.
- *
+ *
* @return the newly created color object
*/
- static random(): Color
+ static random(): Color;
- /**
+ /**
* Returns the addition of the supplied value to both coordinates of
* the color as a new color.
* The object itself is not modified!
- *
+ *
* @param number - the number to add
- *
+ *
* @return the addition of the color and the value as a new
* color
*/
- add(number: number): Color
+ add(number: number): Color;
- /**
+ /**
* Returns the addition of the supplied color to the color as a new
* color.
* The object itself is not modified!
- *
+ *
* @param color - the color to add
- *
+ *
* @return the addition of the two colors as a new color
*/
- add(color: Color): Color
+ add(color: Color): Color;
- /**
+ /**
* Returns the subtraction of the supplied value to both coordinates of
* the color as a new color.
* The object itself is not modified!
- *
+ *
* @param number - the number to subtract
- *
+ *
* @return the subtraction of the color and the value as a new
* color
*/
- subtract(number: number): Color
+ subtract(number: number): Color;
- /**
+ /**
* Returns the subtraction of the supplied color to the color as a new
* color.
* The object itself is not modified!
- *
+ *
* @param color - the color to subtract
- *
+ *
* @return the subtraction of the two colors as a new color
*/
- subtract(color: Color): Color
+ subtract(color: Color): Color;
- /**
+ /**
* Returns the multiplication of the supplied value to both coordinates
* of the color as a new color.
* The object itself is not modified!
- *
+ *
* @param number - the number to multiply
- *
+ *
* @return the multiplication of the color and the value as a
* new color
*/
- multiply(number: number): Color
+ multiply(number: number): Color;
- /**
+ /**
* Returns the multiplication of the supplied color to the color as a
* new color.
* The object itself is not modified!
- *
+ *
* @param color - the color to multiply
- *
+ *
* @return the multiplication of the two colors as a new color
*/
- multiply(color: Color): Color
+ multiply(color: Color): Color;
- /**
+ /**
* Returns the division of the supplied value to both coordinates of
* the color as a new color.
* The object itself is not modified!
- *
+ *
* @param number - the number to divide
- *
+ *
* @return the division of the color and the value as a new
* color
*/
- divide(number: number): Color
+ divide(number: number): Color;
- /**
+ /**
* Returns the division of the supplied color to the color as a new
* color.
* The object itself is not modified!
- *
+ *
* @param color - the color to divide
- *
+ *
* @return the division of the two colors as a new color
*/
- divide(color: Color): Color
-
+ divide(color: Color): Color;
}
- /**
+ /**
* A compound path is a complex path that is made up of one or more
* simple sub-paths. It can have the `nonzero` fill rule, or the `evenodd` rule
* applied. Both rules use mathematical equations to determine if any region is
* outside or inside the final shape. The `evenodd` rule is more predictable:
* Every other region within a such a compound path is a hole, regardless of
* path direction.
- *
+ *
* All the paths in a compound path take on the style of the compound path and
* can be accessed through its {@link Item#children} list.
*/
class CompoundPath extends PathItem {
- /**
+ /**
* Specifies whether the compound-path is fully closed, meaning all its
* contained sub-paths are closed path.
- *
+ *
* @see Path#closed
*/
- closed: boolean
+ closed: boolean;
- /**
+ /**
* The first Segment contained within the compound-path, a short-cut to
* calling {@link Path#firstSegment} on {@link Item#firstChild}.
*/
- readonly firstSegment: Segment
+ readonly firstSegment: Segment;
- /**
+ /**
* The last Segment contained within the compound-path, a short-cut to
* calling {@link Path#lastSegment} on {@link Item#lastChild}.
*/
- readonly lastSegment: Segment
+ readonly lastSegment: Segment;
- /**
+ /**
* All the curves contained within the compound-path, from all its child
* {@link Path} items.
*/
- readonly curves: Curve[]
+ readonly curves: Curve[];
- /**
+ /**
* The first Curve contained within the compound-path, a short-cut to
* calling {@link Path#firstCurve} on {@link Item#firstChild}.
*/
- readonly firstCurve: Curve
+ readonly firstCurve: Curve;
- /**
+ /**
* The last Curve contained within the compound-path, a short-cut to
* calling {@link Path#lastCurve} on {@link Item#lastChild}.
*/
- readonly lastCurve: Curve
+ readonly lastCurve: Curve;
- /**
+ /**
* The area that the compound-path's geometry is covering, calculated by
* getting the {@link Path#area} of each sub-path and it adding up.
* Note that self-intersecting paths and sub-paths of different orientation
* can result in areas that cancel each other out.
*/
- readonly area: number
+ readonly area: number;
- /**
+ /**
* The total length of all sub-paths in this compound-path, calculated by
* getting the {@link Path#length} of each sub-path and it adding up.
*/
- readonly length: number
+ readonly length: number;
-
- /**
+ /**
* Creates a new compound path item from SVG path-data and places it at the
* top of the active layer.
- *
+ *
* @param pathData - the SVG path-data that describes the geometry
* of this path
*/
- constructor(pathData: string)
+ constructor(pathData: string);
- /**
+ /**
* Creates a new compound path item from an object description and places it
* at the top of the active layer.
- *
+ *
* @param object - an object containing properties to be set on the
* path
*/
- constructor(object: object)
-
+ constructor(object: object);
}
- /**
+ /**
* The Curve object represents the parts of a path that are connected by
* two following {@link Segment} objects. The curves of a path can be accessed
* through its {@link Path#curves} array.
- *
+ *
* While a segment describe the anchor point and its incoming and outgoing
* handles, a Curve object describes the curve passing between two such
* segments. Curves and segments represent two different ways of looking at the
@@ -430,570 +430,573 @@ declare namespace paper {
* convenient ways to work with parts of the path, finding lengths, positions or
* tangents at given offsets.
*/
- class Curve {
- /**
+ class Curve {
+ /**
* The first anchor point of the curve.
*/
- point1: Point
+ point1: Point;
- /**
+ /**
* The second anchor point of the curve.
*/
- point2: Point
+ point2: Point;
- /**
+ /**
* The handle point that describes the tangent in the first anchor point.
*/
- handle1: Point
+ handle1: Point;
- /**
+ /**
* The handle point that describes the tangent in the second anchor point.
*/
- handle2: Point
+ handle2: Point;
- /**
+ /**
* The first segment of the curve.
*/
- readonly segment1: Segment
+ readonly segment1: Segment;
- /**
+ /**
* The second segment of the curve.
*/
- readonly segment2: Segment
+ readonly segment2: Segment;
- /**
+ /**
* The path that the curve belongs to.
*/
- readonly path: Path
+ readonly path: Path;
- /**
+ /**
* The index of the curve in the {@link Path#curves} array.
*/
- readonly index: number
+ readonly index: number;
- /**
+ /**
* The next curve in the {@link Path#curves} array that the curve
* belongs to.
*/
- readonly next: Curve
+ readonly next: Curve;
- /**
+ /**
* The previous curve in the {@link Path#curves} array that the curve
* belongs to.
*/
- readonly previous: Curve
+ readonly previous: Curve;
- /**
+ /**
* Specifies whether the points and handles of the curve are selected.
*/
- selected: boolean
+ selected: boolean;
- /**
+ /**
* An array of 8 float values, describing this curve's geometry in four
* absolute x/y pairs (point1, handle1, handle2, point2). This format is
* used internally for efficient processing of curve geometries, e.g. when
* calculating intersections or bounds.
- *
+ *
* Note that the handles are converted to absolute coordinates.
*/
- readonly values: number[]
+ readonly values: number[];
- /**
+ /**
* An array of 4 point objects, describing this curve's geometry in absolute
* coordinates (point1, handle1, handle2, point2).
- *
+ *
* Note that the handles are converted to absolute coordinates.
*/
- readonly points: Point[]
+ readonly points: Point[];
- /**
+ /**
* The approximated length of the curve.
*/
- readonly length: number
+ readonly length: number;
- /**
+ /**
* The area that the curve's geometry is covering.
*/
- readonly area: number
+ readonly area: number;
- /**
+ /**
* The bounding rectangle of the curve excluding stroke width.
*/
- bounds: Rectangle
+ bounds: Rectangle;
- /**
+ /**
* The bounding rectangle of the curve including stroke width.
*/
- strokeBounds: Rectangle
+ strokeBounds: Rectangle;
- /**
+ /**
* The bounding rectangle of the curve including handles.
*/
- handleBounds: Rectangle
-
+ handleBounds: Rectangle;
- /**
+ /**
* Creates a new curve object.
*/
- constructor(segment1: Segment, segment2: Segment)
+ constructor(segment1: Segment, segment2: Segment);
- /**
+ /**
* Creates a new curve object.
*/
- constructor(point1: Point, handle1: Point, handle2: Point, point2: Point)
+ constructor(
+ point1: Point,
+ handle1: Point,
+ handle2: Point,
+ point2: Point
+ );
- /**
+ /**
* Returns a copy of the curve.
*/
- clone(): Curve
+ clone(): Curve;
- /**
+ /**
* @return a string representation of the curve
*/
- toString(): string
+ toString(): string;
- /**
+ /**
* Determines the type of cubic Bézier curve via discriminant
* classification, as well as the curve-time parameters of the associated
* points of inflection, loops, cusps, etc.
- *
+ *
* @return the curve classification information as an object, see
* options
*/
- classify(): object
+ classify(): object;
- /**
+ /**
* Removes the curve from the path that it belongs to, by removing its
* second segment and merging its handle with the first segment.
- *
+ *
* @return true if the curve was removed
*/
- remove(): boolean
+ remove(): boolean;
- /**
+ /**
* Checks if the this is the first curve in the {@link Path#curves} array.
- *
+ *
* @return true if this is the first curve
*/
- isFirst(): boolean
+ isFirst(): boolean;
- /**
+ /**
* Checks if the this is the last curve in the {@link Path#curves} array.
- *
+ *
* @return true if this is the last curve
*/
- isLast(): boolean
+ isLast(): boolean;
- /**
+ /**
* Creates a new curve as a sub-curve from this curve, its range defined by
* the given curve-time parameters. If `from` is larger than `to`, then
* the resulting curve will have its direction reversed.
- *
+ *
* @param from - the curve-time parameter at which the sub-curve
* starts
* @param to - the curve-time parameter at which the sub-curve
* ends
- *
+ *
* @return the newly create sub-curve
*/
- getPart(from: number, to: number): Curve
+ getPart(from: number, to: number): Curve;
- /**
+ /**
* Divides the curve into two curves at the given offset or location. The
* curve itself is modified and becomes the first part, the second part is
* returned as a new curve. If the curve belongs to a path item, a new
* segment is inserted into the path at the given location, and the second
* part becomes a part of the path as well.
- *
+ *
* @see #divideAtTime(time)
- *
+ *
* @param location - the offset or location on the
* curve at which to divide
- *
+ *
* @return the second part of the divided curve if the location is
* valid, {code null} otherwise
*/
- divideAt(location: number | CurveLocation): Curve
+ divideAt(location: number | CurveLocation): Curve;
- /**
+ /**
* Divides the curve into two curves at the given curve-time parameter. The
* curve itself is modified and becomes the first part, the second part is
* returned as a new curve. If the modified curve belongs to a path item,
* the second part is also added to the path.
- *
+ *
* @see #divideAt(offset)
- *
+ *
* @param time - the curve-time parameter on the curve at which to
* divide
- *
+ *
* @return the second part of the divided curve, if the offset is
* within the valid range, {code null} otherwise.
*/
- divideAtTime(time: number): Curve
+ divideAtTime(time: number): Curve;
- /**
+ /**
* Splits the path this curve belongs to at the given offset. After
* splitting, the path will be open. If the path was open already, splitting
* will result in two paths.
- *
+ *
* @see Path#splitAt(offset)
- *
+ *
* @param location - the offset or location on the
* curve at which to split
- *
+ *
* @return the newly created path after splitting, if any
*/
- splitAt(location: number | CurveLocation): Path
+ splitAt(location: number | CurveLocation): Path;
- /**
+ /**
* Splits the path this curve belongs to at the given offset. After
* splitting, the path will be open. If the path was open already, splitting
* will result in two paths.
- *
+ *
* @see Path#splitAt(offset)
- *
+ *
* @param time - the curve-time parameter on the curve at which to
* split
- *
+ *
* @return the newly created path after splitting, if any
*/
- splitAtTime(time: number): Path
+ splitAtTime(time: number): Path;
- /**
+ /**
* Returns a reversed version of the curve, without modifying the curve
* itself.
- *
+ *
* @return a reversed version of the curve
*/
- reversed(): Curve
+ reversed(): Curve;
- /**
+ /**
* Clears the curve's handles by setting their coordinates to zero,
* turning the curve into a straight line.
*/
- clearHandles(): void
+ clearHandles(): void;
- /**
+ /**
* Checks if this curve has any curve handles set.
- *
+ *
* @see Curve#handle1
* @see Curve#handle2
* @see Segment#hasHandles()
* @see Path#hasHandles()
- *
+ *
* @return true if the curve has handles set
*/
- hasHandles(): boolean
+ hasHandles(): boolean;
- /**
+ /**
* Checks if this curve has any length.
- *
+ *
* @param epsilon - the epsilon against which to compare the
* curve's length
- *
+ *
* @return true if the curve is longer than the given epsilon
*/
- hasLength(epsilon?: number): boolean
+ hasLength(epsilon?: number): boolean;
- /**
+ /**
* Checks if this curve appears as a straight line. This can mean that
* it has no handles defined, or that the handles run collinear with the
* line that connects the curve's start and end point, not falling
* outside of the line.
- *
+ *
* @return true if the curve is straight
*/
- isStraight(): boolean
+ isStraight(): boolean;
- /**
+ /**
* Checks if this curve is parametrically linear, meaning that it is
* straight and its handles are positioned at 1/3 and 2/3 of the total
* length of the curve.
- *
+ *
* @return true if the curve is parametrically linear
*/
- isLinear(): boolean
+ isLinear(): boolean;
- /**
+ /**
* Checks if the the two curves describe straight lines that are
* collinear, meaning they run in parallel.
- *
+ *
* @param curve - the other curve to check against
- *
+ *
* @return true if the two lines are collinear
*/
- isCollinear(curve: Curve): boolean
+ isCollinear(curve: Curve): boolean;
- /**
+ /**
* Checks if the curve is a straight horizontal line.
- *
+ *
* @return true if the line is horizontal
*/
- isHorizontal(): boolean
+ isHorizontal(): boolean;
- /**
+ /**
* Checks if the curve is a straight vertical line.
- *
+ *
* @return true if the line is vertical
*/
- isVertical(): boolean
+ isVertical(): boolean;
- /**
+ /**
* Calculates the curve location at the specified offset on the curve.
- *
+ *
* @param offset - the offset on the curve
- *
+ *
* @return the curve location at the specified the offset
*/
- getLocationAt(offset: number): CurveLocation
+ getLocationAt(offset: number): CurveLocation;
- /**
+ /**
* Calculates the curve location at the specified curve-time parameter on
* the curve.
- *
+ *
* @param time - the curve-time parameter on the curve
- *
+ *
* @return the curve location at the specified the location
*/
- getLocationAtTime(time: number): CurveLocation
+ getLocationAtTime(time: number): CurveLocation;
- /**
+ /**
* Calculates the curve-time parameter of the specified offset on the path,
* relative to the provided start parameter. If offset is a negative value,
* the parameter is searched to the left of the start parameter. If no start
* parameter is provided, a default of `0` for positive values of `offset`
* and `1` for negative values of `offset`.
- *
+ *
* @param offset - the offset at which to find the curve-time, in
* curve length units
* @param start - the curve-time in relation to which the offset is
* determined
- *
+ *
* @return the curve-time parameter at the specified location
*/
- getTimeAt(offset: number, start?: number): number
+ getTimeAt(offset: number, start?: number): number;
- /**
+ /**
* Calculates the curve-time parameters where the curve is tangential to
* provided tangent. Note that tangents at the start or end are included.
- *
+ *
* @param tangent - the tangent to which the curve must be tangential
- *
+ *
* @return at most two curve-time parameters, where the curve is
* tangential to the given tangent
*/
- getTimesWithTangent(tangent: Point): number[]
+ getTimesWithTangent(tangent: Point): number[];
- /**
+ /**
* Calculates the curve offset at the specified curve-time parameter on
* the curve.
- *
+ *
* @param time - the curve-time parameter on the curve
- *
+ *
* @return the curve offset at the specified the location
*/
- getOffsetAtTime(time: number): number
+ getOffsetAtTime(time: number): number;
- /**
+ /**
* Returns the curve location of the specified point if it lies on the
* curve, `null` otherwise.
- *
+ *
* @param point - the point on the curve
- *
+ *
* @return the curve location of the specified point
*/
- getLocationOf(point: Point): CurveLocation
+ getLocationOf(point: Point): CurveLocation;
- /**
+ /**
* Returns the length of the path from its beginning up to up to the
* specified point if it lies on the path, `null` otherwise.
- *
+ *
* @param point - the point on the path
- *
+ *
* @return the length of the path up to the specified point
*/
- getOffsetOf(point: Point): number
+ getOffsetOf(point: Point): number;
- /**
+ /**
* Returns the curve-time parameter of the specified point if it lies on the
* curve, `null` otherwise.
* Note that if there is more than one possible solution in a
* self-intersecting curve, the first found result is returned.
- *
+ *
* @param point - the point on the curve
- *
+ *
* @return the curve-time parameter of the specified point
*/
- getTimeOf(point: Point): number
+ getTimeOf(point: Point): number;
- /**
+ /**
* Returns the nearest location on the curve to the specified point.
- *
+ *
* @param point - the point for which we search the nearest location
- *
+ *
* @return the location on the curve that's the closest to
* the specified point
*/
- getNearestLocation(point: Point): CurveLocation
+ getNearestLocation(point: Point): CurveLocation;
- /**
+ /**
* Returns the nearest point on the curve to the specified point.
- *
+ *
* @param point - the point for which we search the nearest point
- *
+ *
* @return the point on the curve that's the closest to the
* specified point
*/
- getNearestPoint(point: Point): Point
+ getNearestPoint(point: Point): Point;
- /**
+ /**
* Calculates the point on the curve at the given location.
- *
+ *
* @param location - the offset or location on the
* curve
- *
+ *
* @return the point on the curve at the given location
*/
- getPointAt(location: number | CurveLocation): Point
+ getPointAt(location: number | CurveLocation): Point;
- /**
+ /**
* Calculates the normalized tangent vector of the curve at the given
* location.
- *
+ *
* @param location - the offset or location on the
* curve
- *
+ *
* @return the normalized tangent of the curve at the given location
*/
- getTangentAt(location: number | CurveLocation): Point
+ getTangentAt(location: number | CurveLocation): Point;
- /**
+ /**
* Calculates the normal vector of the curve at the given location.
- *
+ *
* @param location - the offset or location on the
* curve
- *
+ *
* @return the normal of the curve at the given location
*/
- getNormalAt(location: number | CurveLocation): Point
+ getNormalAt(location: number | CurveLocation): Point;
- /**
+ /**
* Calculates the weighted tangent vector of the curve at the given
* location, its length reflecting the curve velocity at that location.
- *
+ *
* @param location - the offset or location on the
* curve
- *
+ *
* @return the weighted tangent of the curve at the given location
*/
- getWeightedTangentAt(location: number | CurveLocation): Point
+ getWeightedTangentAt(location: number | CurveLocation): Point;
- /**
+ /**
* Calculates the weighted normal vector of the curve at the given location,
* its length reflecting the curve velocity at that location.
- *
+ *
* @param location - the offset or location on the
* curve
- *
+ *
* @return the weighted normal of the curve at the given location
*/
- getWeightedNormalAt(location: number | CurveLocation): Point
+ getWeightedNormalAt(location: number | CurveLocation): Point;
- /**
+ /**
* Calculates the curvature of the curve at the given location. Curvatures
* indicate how sharply a curve changes direction. A straight line has zero
* curvature, where as a circle has a constant curvature. The curve's radius
* at the given location is the reciprocal value of its curvature.
- *
+ *
* @param location - the offset or location on the
* curve
- *
+ *
* @return the curvature of the curve at the given location
*/
- getCurvatureAt(location: number | CurveLocation): number
+ getCurvatureAt(location: number | CurveLocation): number;
- /**
+ /**
* Calculates the point on the curve at the given location.
- *
+ *
* @param time - the curve-time parameter on the curve
- *
+ *
* @return the point on the curve at the given location
*/
- getPointAtTime(time: number): Point
+ getPointAtTime(time: number): Point;
- /**
+ /**
* Calculates the normalized tangent vector of the curve at the given
* location.
- *
+ *
* @param time - the curve-time parameter on the curve
- *
+ *
* @return the normalized tangent of the curve at the given location
*/
- getTangentAtTime(time: number): Point
+ getTangentAtTime(time: number): Point;
- /**
+ /**
* Calculates the normal vector of the curve at the given location.
- *
+ *
* @param time - the curve-time parameter on the curve
- *
+ *
* @return the normal of the curve at the given location
*/
- getNormalAtTime(time: number): Point
+ getNormalAtTime(time: number): Point;
- /**
+ /**
* Calculates the weighted tangent vector of the curve at the given
* location, its length reflecting the curve velocity at that location.
- *
+ *
* @param time - the curve-time parameter on the curve
- *
+ *
* @return the weighted tangent of the curve at the given location
*/
- getWeightedTangentAtTime(time: number): Point
+ getWeightedTangentAtTime(time: number): Point;
- /**
+ /**
* Calculates the weighted normal vector of the curve at the given location,
* its length reflecting the curve velocity at that location.
- *
+ *
* @param time - the curve-time parameter on the curve
- *
+ *
* @return the weighted normal of the curve at the given location
*/
- getWeightedNormalAtTime(time: number): Point
+ getWeightedNormalAtTime(time: number): Point;
- /**
+ /**
* Calculates the curvature of the curve at the given location. Curvatures
* indicate how sharply a curve changes direction. A straight line has zero
* curvature, where as a circle has a constant curvature. The curve's radius
* at the given location is the reciprocal value of its curvature.
- *
+ *
* @param time - the curve-time parameter on the curve
- *
+ *
* @return the curvature of the curve at the given location
*/
- getCurvatureAtTime(time: number): number
+ getCurvatureAtTime(time: number): number;
- /**
+ /**
* Returns all intersections between two {@link Curve} objects as an
* array of {@link CurveLocation} objects.
- *
+ *
* @param curve - the other curve to find the intersections with
* (if the curve itself or `null` is passed, the self intersection
* of the curve is returned, if it exists)
- *
+ *
* @return the locations of all intersections between
* the curves
*/
- getIntersections(curve: Curve): CurveLocation[]
-
+ getIntersections(curve: Curve): CurveLocation[];
}
- /**
+ /**
* CurveLocation objects describe a location on {@link Curve} objects, as
* defined by the curve-time {@link #time}, a value between `0` (beginning
* of the curve) and `1` (end of the curve). If the curve is part of a
* {@link Path} item, its {@link #index} inside the {@link Path#curves}
* array is also provided.
- *
+ *
* The class is in use in many places, such as
* {@link Path#getLocationAt},
* {@link Path#getLocationOf},
@@ -1001,326 +1004,314 @@ declare namespace paper {
* {@link PathItem#getIntersections},
* etc.
*/
- class CurveLocation {
- /**
+ class CurveLocation {
+ /**
* The segment of the curve which is closer to the described location.
*/
- readonly segment: Segment
+ readonly segment: Segment;
- /**
+ /**
* The curve that this location belongs to.
*/
- readonly curve: Curve
+ readonly curve: Curve;
- /**
+ /**
* The path that this locations is situated on.
*/
- readonly path: Path
+ readonly path: Path;
- /**
+ /**
* The index of the {@link #curve} within the {@link Path#curves} list, if
* it is part of a {@link Path} item.
*/
- readonly index: number
+ readonly index: number;
- /**
+ /**
* The curve-time parameter, as used by various bezier curve calculations.
* It is value between `0` (beginning of the curve) and `1` (end of the
* curve).
*/
- readonly time: number
+ readonly time: number;
- /**
+ /**
* The point which is defined by the {@link #curve} and
* {@link #time}.
*/
- readonly point: Point
+ readonly point: Point;
- /**
+ /**
* The length of the path from its beginning up to the location described
* by this object. If the curve is not part of a path, then the length
* within the curve is returned instead.
*/
- readonly offset: number
+ readonly offset: number;
- /**
+ /**
* The length of the curve from its beginning up to the location described
* by this object.
*/
- readonly curveOffset: number
+ readonly curveOffset: number;
- /**
+ /**
* The curve location on the intersecting curve, if this location is the
* result of a call to {@link PathItem#getIntersections} /
* {@link Curve#getIntersections}.
*/
- readonly intersection: CurveLocation
+ readonly intersection: CurveLocation;
- /**
+ /**
* The tangential vector to the {@link #curve} at the given location.
*/
- readonly tangent: Point
+ readonly tangent: Point;
- /**
+ /**
* The normal vector to the {@link #curve} at the given location.
*/
- readonly normal: Point
+ readonly normal: Point;
- /**
+ /**
* The curvature of the {@link #curve} at the given location.
*/
- readonly curvature: number
+ readonly curvature: number;
- /**
+ /**
* The distance from the queried point to the returned location.
- *
+ *
* @see Curve#getNearestLocation(point)
* @see PathItem#getNearestLocation(point)
*/
- readonly distance: number
-
+ readonly distance: number;
- /**
+ /**
* Creates a new CurveLocation object.
*/
- constructor(curve: Curve, time: number, point?: Point)
+ constructor(curve: Curve, time: number, point?: Point);
- /**
+ /**
* Checks whether tow CurveLocation objects are describing the same location
* on a path, by applying the same tolerances as elsewhere when dealing with
* curve-time parameters.
- *
+ *
* @return true if the locations are equal
*/
- equals(location: CurveLocation): boolean
+ equals(location: CurveLocation): boolean;
- /**
+ /**
* @return a string representation of the curve location
*/
- toString(): string
+ toString(): string;
- /**
+ /**
* Checks if the location is an intersection with another curve and is
* merely touching the other curve, as opposed to crossing it.
- *
+ *
* @see #isCrossing()
- *
+ *
* @return true if the location is an intersection that is
* merely touching another curve
*/
- isTouching(): boolean
+ isTouching(): boolean;
- /**
+ /**
* Checks if the location is an intersection with another curve and is
* crossing the other curve, as opposed to just touching it.
- *
+ *
* @see #isTouching()
- *
+ *
* @return true if the location is an intersection that is
* crossing another curve
*/
- isCrossing(): boolean
+ isCrossing(): boolean;
- /**
+ /**
* Checks if the location is an intersection with another curve and is
* part of an overlap between the two involved paths.
- *
+ *
* @see #isCrossing()
* @see #isTouching()
- *
+ *
* @return true if the location is an intersection that is
* part of an overlap between the two involved paths
*/
- hasOverlap(): boolean
-
+ hasOverlap(): boolean;
}
- /**
+ /**
* The Event object is the base class for any of the other event types,
* such as {@link MouseEvent}, {@link ToolEvent} and {@link KeyEvent}.
*/
- class Event {
- /**
+ class Event {
+ /**
* The time at which the event was created, in milliseconds since the epoch.
*/
- readonly timeStamp: number
+ readonly timeStamp: number;
- /**
+ /**
* The current state of the keyboard modifiers.
- *
+ *
* @see Key.modifiers
*/
- readonly modifiers: any
+ readonly modifiers: any;
-
- /**
+ /**
* Cancels the event if it is cancelable, without stopping further
* propagation of the event.
*/
- preventDefault(): void
+ preventDefault(): void;
- /**
+ /**
* Prevents further propagation of the current event.
*/
- stopPropagation(): void
+ stopPropagation(): void;
- /**
+ /**
* Cancels the event if it is cancelable, and stops stopping further
* propagation of the event. This is has the same effect as calling both
* {@link #stopPropagation} and {@link #preventDefault}.
- *
+ *
* Any handler can also return `false` to indicate that `stop()` should be
* called right after.
*/
- stop(): void
-
+ stop(): void;
}
- /**
+ /**
* The Gradient object.
*/
- class Gradient {
- /**
+ class Gradient {
+ /**
* The gradient stops on the gradient ramp.
*/
- stops: GradientStop[]
+ stops: GradientStop[];
- /**
+ /**
* Specifies whether the gradient is radial or linear.
*/
- radial: boolean
-
+ radial: boolean;
- /**
+ /**
* @return a copy of the gradient
*/
- clone(): Gradient
+ clone(): Gradient;
- /**
+ /**
* Checks whether the gradient is equal to the supplied gradient.
- *
+ *
* @return true if they are equal
*/
- equals(gradient: Gradient): boolean
-
+ equals(gradient: Gradient): boolean;
}
- /**
+ /**
* The GradientStop object.
*/
- class GradientStop {
- /**
+ class GradientStop {
+ /**
* The ramp-point of the gradient stop as a value between `0` and `1`.
*/
- offset: number
+ offset: number;
- /**
+ /**
* The color of the gradient stop.
*/
- color: Color
-
+ color: Color;
- /**
+ /**
* Creates a GradientStop object.
- *
+ *
* @param color - the color of the stop
* @param offset - the position of the stop on the gradient
* ramp as a value between `0` and `1`; `null` or `undefined` for automatic
* assignment.
*/
- constructor(color?: Color, offset?: number)
+ constructor(color?: Color, offset?: number);
- /**
+ /**
* @return a copy of the gradient-stop
*/
- clone(): GradientStop
-
+ clone(): GradientStop;
}
- /**
+ /**
* A Group is a collection of items. When you transform a Group, its
* children are treated as a single unit without changing their relative
* positions.
*/
class Group extends Item {
- /**
+ /**
* Specifies whether the group item is to be clipped. When setting to
* `true`, the first child in the group is automatically defined as the
* clipping mask.
*/
- clipped: boolean
+ clipped: boolean;
-
- /**
+ /**
* Creates a new Group item and places it at the top of the active layer.
- *
+ *
* @param children - An array of children that will be added to the
* newly created group
*/
- constructor(children?: Item[])
+ constructor(children?: Item[]);
- /**
+ /**
* Creates a new Group item and places it at the top of the active layer.
- *
+ *
* @param object - an object containing the properties to be set on
* the group
*/
- constructor(object: object)
-
+ constructor(object: object);
}
- /**
+ /**
* A HitResult object contains information about the results of a hit
* test. It is returned by {@link Item#hitTest} and
* {@link Project#hitTest}.
*/
- class HitResult {
- /**
+ class HitResult {
+ /**
* Describes the type of the hit result. For example, if you hit a segment
* point, the type would be `'segment'`.
*/
- type: string
+ type: string;
- /**
+ /**
* If the HitResult has a {@link HitResult#type} of `'bounds'`, this
* property describes which corner of the bounding rectangle was hit.
*/
- name: string
+ name: string;
- /**
+ /**
* The item that was hit.
*/
- item: Item
+ item: Item;
- /**
+ /**
* If the HitResult has a type of 'curve' or 'stroke', this property gives
* more information about the exact position that was hit on the path.
*/
- location: CurveLocation
+ location: CurveLocation;
- /**
+ /**
* If the HitResult has a type of 'pixel', this property refers to the color
* of the pixel on the {@link Raster} that was hit.
*/
- color: Color | null
+ color: Color | null;
- /**
+ /**
* If the HitResult has a type of 'stroke', 'segment', 'handle-in' or
* 'handle-out', this property refers to the segment that was hit or that
* is closest to the hitResult.location on the curve.
*/
- segment: Segment
+ segment: Segment;
- /**
+ /**
* Describes the actual coordinates of the segment, handle or bounding box
* corner that was hit.
*/
- point: Point
-
-
+ point: Point;
}
- /**
+ /**
* The Item type allows you to access and modify the items in
* Paper.js projects. Its functionality is inherited by different project
* item types such as {@link Path}, {@link CompoundPath}, {@link Group},
@@ -1328,162 +1319,162 @@ declare namespace paper {
* is unique to their type, but share the underlying properties and functions
* that they inherit from Item.
*/
- class Item {
- /**
+ class Item {
+ /**
* The unique id of the item.
*/
- readonly id: number
+ readonly id: number;
- /**
+ /**
* The class name of the item as a string.
*/
- className: string
+ className: string;
- /**
+ /**
* The name of the item. If the item has a name, it can be accessed by name
* through its parent's children list.
*/
- name: string
+ name: string;
- /**
+ /**
* The path style of the item.
*/
- style: Style
+ style: Style;
- /**
+ /**
* Specifies whether the item is locked. When set to `true`, item
* interactions with the mouse are disabled.
*/
- locked: boolean
+ locked: boolean;
- /**
+ /**
* Specifies whether the item is visible. When set to `false`, the item
* won't be drawn.
*/
- visible: boolean
+ visible: boolean;
- /**
+ /**
* The blend mode with which the item is composited onto the canvas. Both
* the standard canvas compositing modes, as well as the new CSS blend modes
* are supported. If blend-modes cannot be rendered natively, they are
* emulated. Be aware that emulation can have an impact on performance.
*/
- blendMode: string
+ blendMode: string;
- /**
+ /**
* The opacity of the item as a value between `0` and `1`.
*/
- opacity: number
+ opacity: number;
- /**
+ /**
* Specifies whether the item is selected. This will also return `true` for
* {@link Group} items if they are partially selected, e.g. groups
* containing selected or partially selected paths.
- *
+ *
* 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.
- *
+ *
* @see Project#selectedItems
* @see Segment#selected
* @see Curve#selected
* @see Point#selected
*/
- selected: boolean
+ selected: boolean;
- /**
+ /**
* Specifies whether the item defines a clip mask. This can only be set on
* paths and compound paths, and only if the item is already contained
* within a clipping group.
*/
- clipMask: boolean
+ clipMask: boolean;
- /**
+ /**
* A plain javascript object which can be used to store
* arbitrary data on the item.
*/
- data: any
+ data: any;
- /**
+ /**
* The item's position within the parent item's coordinate system. By
* default, this is the {@link Rectangle#center} of the item's
* {@link #bounds} rectangle.
*/
- position: Point
+ position: Point;
- /**
+ /**
* The item's pivot point specified in the item coordinate system, defining
* the point around which all transformations are hinging. This is also the
* reference point for {@link #position}. By default, it is set to `null`,
* meaning the {@link Rectangle#center} of the item's {@link #bounds}
* rectangle is used as pivot.
*/
- pivot: Point
+ pivot: Point;
- /**
+ /**
* The bounding rectangle of the item excluding stroke width.
*/
- bounds: Rectangle
+ bounds: Rectangle;
- /**
+ /**
* The bounding rectangle of the item including stroke width.
*/
- strokeBounds: Rectangle
+ strokeBounds: Rectangle;
- /**
+ /**
* The bounding rectangle of the item including handles.
*/
- handleBounds: Rectangle
+ handleBounds: Rectangle;
- /**
+ /**
* The bounding rectangle of the item without any matrix transformations.
- *
+ *
* Typical use case would be drawing a frame around the object where you
* want to draw something of the same size, position, rotation, and scaling,
* like a selection frame.
*/
- internalBounds: Rectangle
+ internalBounds: Rectangle;
- /**
+ /**
* The current rotation angle of the item, as described by its
* {@link #matrix}.
* Please note that this only returns meaningful values for items with
* {@link #applyMatrix} set to `false`, meaning they do not directly bake
* transformations into their content.
*/
- rotation: number
+ rotation: number;
- /**
+ /**
* The current scale factor of the item, as described by its
* {@link #matrix}.
* Please note that this only returns meaningful values for items with
* {@link #applyMatrix} set to `false`, meaning they do not directly bake
* transformations into their content.
*/
- scaling: Point
+ scaling: Point;
- /**
+ /**
* The item's transformation matrix, defining position and dimensions in
* relation to its parent item in which it is contained.
*/
- matrix: Matrix
+ matrix: Matrix;
- /**
+ /**
* The item's global transformation matrix in relation to the global project
* coordinate space. Note that the view's transformations resulting from
* zooming and panning are not factored in.
*/
- readonly globalMatrix: Matrix
+ readonly globalMatrix: Matrix;
- /**
+ /**
* The item's global matrix in relation to the view coordinate space. This
* means that the view's transformations resulting from zooming and panning
* are factored in.
*/
- readonly viewMatrix: Matrix
+ readonly viewMatrix: Matrix;
- /**
+ /**
* Controls whether the transformations applied to the item (e.g. through
* {@link #transform}, {@link #rotate},
* {@link #scale}, etc.) are stored in its {@link #matrix} property,
@@ -1491,107 +1482,107 @@ declare namespace paper {
* on to the segments in {@link Path} items, the children of {@link Group}
* items, etc.).
*/
- applyMatrix: boolean
+ applyMatrix: boolean;
- /**
+ /**
* The project that this item belongs to.
*/
- readonly project: Project
+ readonly project: Project;
- /**
+ /**
* The view that this item belongs to.
*/
- readonly view: View
+ readonly view: View;
- /**
+ /**
* The layer that this item is contained within.
*/
- readonly layer: Layer
+ readonly layer: Layer;
- /**
+ /**
* The item that this item is contained within.
*/
- parent: Item
+ parent: Item;
- /**
+ /**
* The children items contained within this item. Items that define a
* {@link #name} can also be accessed by name.
- *
+ *
* Please note: The children array should not be modified directly
* using array functions. To remove single items from the children list, use
* {@link Item#remove}, to remove all items from the children list, use
* {@link Item#removeChildren}. To add items to the children list, use
* {@link Item#addChild} or {@link Item#insertChild}.
*/
- children: Item[]
+ children: Item[];
- /**
+ /**
* The first item contained within this item. This is a shortcut for
* accessing `item.children[0]`.
*/
- readonly firstChild: Item
+ readonly firstChild: Item;
- /**
+ /**
* The last item contained within this item.This is a shortcut for
* accessing `item.children[item.children.length - 1]`.
*/
- readonly lastChild: Item
+ readonly lastChild: Item;
- /**
+ /**
* The next item on the same level as this item.
*/
- readonly nextSibling: Item
+ readonly nextSibling: Item;
- /**
+ /**
* The previous item on the same level as this item.
*/
- readonly previousSibling: Item
+ readonly previousSibling: Item;
- /**
+ /**
* The index of this item within the list of its parent's children.
*/
- readonly index: number
+ readonly index: number;
- /**
+ /**
* The color of the stroke.
*/
- strokeColor: Color | null
+ strokeColor: Color | null;
- /**
+ /**
* The width of the stroke.
*/
- strokeWidth: number
+ strokeWidth: number;
- /**
+ /**
* The shape to be used at the beginning and end of open {@link Path} items,
* when they have a stroke.
*/
- strokeCap: string
+ strokeCap: string;
- /**
+ /**
* The shape to be used at the segments and corners of {@link Path} items
* when they have a stroke.
*/
- strokeJoin: string
+ strokeJoin: string;
- /**
+ /**
* The dash offset of the stroke.
*/
- dashOffset: number
+ dashOffset: number;
- /**
+ /**
* Specifies whether the stroke is to be drawn taking the current affine
* transformation into account (the default behavior), or whether it should
* appear as a non-scaling stroke.
*/
- strokeScaling: boolean
+ strokeScaling: boolean;
- /**
+ /**
* Specifies an array containing the dash and gap lengths of the stroke.
*/
- dashArray: number[]
+ dashArray: number[];
- /**
+ /**
* The miter limit of the stroke.
* When two line segments meet at a sharp angle and miter joins have been
* specified for {@link Item#strokeJoin}, it is possible for the miter to
@@ -1599,47 +1590,47 @@ declare namespace paper {
* miterLimit imposes a limit on the ratio of the miter length to the
* {@link Item#strokeWidth}.
*/
- miterLimit: number
+ miterLimit: number;
- /**
+ /**
* The fill color of the item.
*/
- fillColor: Color | null
+ fillColor: Color | null;
- /**
+ /**
* The fill-rule with which the shape gets filled. Please note that only
* modern browsers support fill-rules other than `'nonzero'`.
*/
- fillRule: string
+ fillRule: string;
- /**
+ /**
* The shadow color.
*/
- shadowColor: Color | null
+ shadowColor: Color | null;
- /**
+ /**
* The shadow's blur radius.
*/
- shadowBlur: number
+ shadowBlur: number;
- /**
+ /**
* The shadow's offset.
*/
- shadowOffset: Point
+ shadowOffset: Point;
- /**
+ /**
* The color the item is highlighted with when selected. If the item does
* not specify its own color, the color defined by its layer is used instead.
*/
- selectedColor: Color | null
+ selectedColor: Color | null;
- /**
+ /**
* Item level handler function to be called on each frame of an animation.
* The function receives an event object which contains information about
* the frame event:
- *
+ *
* @see View#onFrame
- *
+ *
* @option event.count {Number} the number of times the frame event was
* fired
* @option event.time {Number} the total amount of time passed since the
@@ -1647,81 +1638,81 @@ declare namespace paper {
* @option event.delta {Number} the time passed in seconds since the last
* frame event
*/
- onFrame: Function | null
+ onFrame: Function | null;
- /**
+ /**
* The function to be called when the mouse button is pushed down on the
* item. The function receives a {@link MouseEvent} object which contains
* information about the mouse event.
* Note that such mouse events bubble up the scene graph hierarchy and will
* reach the view, unless they are stopped with {@link
* Event#stopPropagation()} or by returning `false` from the handler.
- *
+ *
* @see View#onMouseDown
*/
- onMouseDown: Function | null
+ onMouseDown: Function | null;
- /**
+ /**
* The function to be called when the mouse position changes while the mouse
* is being dragged over the item. The function receives a {@link
* MouseEvent} object which contains information about the mouse event.
* Note that such mouse events bubble up the scene graph hierarchy and will
* reach the view, unless they are stopped with {@link
* Event#stopPropagation()} or by returning `false` from the handler.
- *
+ *
* @see View#onMouseDrag
*/
- onMouseDrag: Function | null
+ onMouseDrag: Function | null;
- /**
+ /**
* The function to be called when the mouse button is released over the item.
* The function receives a {@link MouseEvent} object which contains
* information about the mouse event.
* Note that such mouse events bubble up the scene graph hierarchy and will
* reach the view, unless they are stopped with {@link
* Event#stopPropagation()} or by returning `false` from the handler.
- *
+ *
* @see View#onMouseUp
*/
- onMouseUp: Function | null
+ onMouseUp: Function | null;
- /**
+ /**
* The function to be called when the mouse clicks on the item. The function
* receives a {@link MouseEvent} object which contains information about the
* mouse event.
* Note that such mouse events bubble up the scene graph hierarchy and will
* reach the view, unless they are stopped with {@link
* Event#stopPropagation()} or by returning `false` from the handler.
- *
+ *
* @see View#onClick
*/
- onClick: Function | null
+ onClick: Function | null;
- /**
+ /**
* The function to be called when the mouse double clicks on the item. The
* function receives a {@link MouseEvent} object which contains information
* about the mouse event.
* Note that such mouse events bubble up the scene graph hierarchy and will
* reach the view, unless they are stopped with {@link
* Event#stopPropagation()} or by returning `false` from the handler.
- *
+ *
* @see View#onDoubleClick
*/
- onDoubleClick: Function | null
+ onDoubleClick: Function | null;
- /**
+ /**
* The function to be called repeatedly while the mouse moves over the item.
* The function receives a {@link MouseEvent} object which contains
* information about the mouse event.
* Note that such mouse events bubble up the scene graph hierarchy and will
* reach the view, unless they are stopped with {@link
* Event#stopPropagation()} or by returning `false` from the handler.
- *
+ *
* @see View#onMouseMove
*/
- onMouseMove: Function | null
+ onMouseMove: Function | null;
- /**
+ /**
* The function to be called when the mouse moves over the item. This
* function will only be called again, once the mouse moved outside of the
* item first. The function receives a {@link MouseEvent} object which
@@ -1729,69 +1720,68 @@ declare namespace paper {
* Note that such mouse events bubble up the scene graph hierarchy and will
* reach the view, unless they are stopped with {@link
* Event#stopPropagation()} or by returning `false` from the handler.
- *
+ *
* @see View#onMouseEnter
*/
- onMouseEnter: Function | null
+ onMouseEnter: Function | null;
- /**
+ /**
* The function to be called when the mouse moves out of the item.
* The function receives a {@link MouseEvent} object which contains
* information about the mouse event.
* Note that such mouse events bubble up the scene graph hierarchy and will
* reach the view, unless they are stopped with {@link
* Event#stopPropagation()} or by returning `false` from the handler.
- *
+ *
* @see View#onMouseLeave
*/
- onMouseLeave: Function | null
-
+ onMouseLeave: Function | null;
- /**
+ /**
* Sets the properties of the passed object literal on this item to the
* values defined in the object literal, if the item has property of the
* given name (or a setter defined for it).
- *
+ *
* @return the item itself
*/
- set(props: object): this
+ set(props: object): this;
- /**
+ /**
* Clones the item within the same project and places the copy above the
* item.
- *
+ *
* @option [insert=true] specifies whether the copy should be
* inserted into the scene graph. When set to `true`, it is inserted
* above the original
* @option [deep=true] specifies whether the item's children should also be
* cloned
- *
+ *
* @return the newly cloned item
*/
- clone(options?: object): this
+ clone(options?: object): this;
- /**
+ /**
* Copies the content of the specified item over to this item.
- *
+ *
* @param source - the item to copy the content from
*/
- copyContent(source: Item): void
+ copyContent(source: Item): void;
- /**
+ /**
* Copies all attributes of the specified item over to this item. This
* includes its style, visibility, matrix, pivot, blend-mode, opacity,
* selection state, data, name, etc.
- *
+ *
* @param source - the item to copy the attributes from
* @param excludeMatrix - whether to exclude the transformation
* matrix when copying all attributes
*/
- copyAttributes(source: Item, excludeMatrix: boolean): void
+ copyAttributes(source: Item, excludeMatrix: boolean): void;
- /**
+ /**
* Rasterizes the item into a newly created Raster object. The item itself
* is not removed after rasterization.
- *
+ *
* @option [resolution=view.resolution] {Number} the desired resolution to
* be used when rasterizing, in pixels per inch (DPI). If not specified,
* the value of `view.resolution` is used by default.
@@ -1803,38 +1793,38 @@ declare namespace paper {
* @option [insert=true] {Boolean} specifies whether the raster should be
* inserted into the scene graph. When set to `true`, it is inserted
* above the rasterized item.
- *
+ *
* @param options - the rasterization options
- *
+ *
* @return the reused raster or the newly created raster item
*/
- rasterize(options?: object): Raster
+ rasterize(options?: object): Raster;
- /**
+ /**
* Checks whether the item's geometry contains the given point.
- *
+ *
* @param point - the point to check for
*/
- contains(point: Point): boolean
+ contains(point: Point): boolean;
- /**
+ /**
* @param rect - the rectangle to check against
*/
- isInside(rect: Rectangle): boolean
+ isInside(rect: Rectangle): boolean;
- /**
+ /**
* @param item - the item to check against
*/
- intersects(item: Item): boolean
+ intersects(item: Item): boolean;
- /**
+ /**
* Performs a hit-test on the item and its children (if it is a {@link
* Group} or {@link Layer}) at the location of the specified point,
* returning the first found hit.
- *
+ *
* The options object allows you to control the specifics of the hit-
* test and may contain a combination of the following values:
- *
+ *
* @option [options.tolerance={@link PaperScope#settings}.hitTolerance]
* {Number} the tolerance of the hit-test
* @option options.class {Function} only hit-test against a specific item
@@ -1865,88 +1855,88 @@ declare namespace paper {
* @option options.guides {Boolean} hit-test items that have {@link
* Item#guide} set to `true`
* @option options.selected {Boolean} only hit selected items
- *
+ *
* @param point - the point where the hit-test should be performed
* (in global coordinates system).
- *
+ *
* @return a hit result object describing what exactly was hit
* or `null` if nothing was hit
*/
- hitTest(point: Point, options?: object): HitResult
+ hitTest(point: Point, options?: object): HitResult;
- /**
+ /**
* Performs a hit-test on the item and its children (if it is a {@link
* Group} or {@link Layer}) at the location of the specified point,
* returning all found hits.
- *
+ *
* The options object allows you to control the specifics of the hit-
* test. See {@link #hitTest} for a list of all options.
- *
+ *
* @see #hitTest(point[, options]);
- *
+ *
* @param point - the point where the hit-test should be performed
* (in global coordinates system).
- *
+ *
* @return hit result objects for all hits, describing what
* exactly was hit or `null` if nothing was hit
*/
- hitTestAll(point: Point, options?: object): HitResult[]
+ hitTestAll(point: Point, options?: object): HitResult[];
- /**
+ /**
* Checks whether the item matches the criteria described by the given
* object, by iterating over all of its properties and matching against
* their values through {@link #matches}.
- *
+ *
* See {@link Project#getItems} for a selection of illustrated
* examples.
- *
+ *
* @see #getItems(options)
- *
+ *
* @param options - the criteria to match against
- *
+ *
* @return true if the item matches all the criteria
*/
- matches(options: object | Function): boolean
+ matches(options: object | Function): boolean;
- /**
+ /**
* Checks whether the item matches the given criteria. Extended matching is
* possible by providing a compare function or a regular expression.
* Matching points, colors only work as a comparison of the full object, not
* partial matching (e.g. only providing the x-coordinate to match all
* points with that x-value). Partial matching does work for
* {@link Item#data}.
- *
+ *
* See {@link Project#getItems} for a selection of illustrated
* examples.
- *
+ *
* @see #getItems(options)
- *
+ *
* @param name - the name of the state to match against
* @param compare - the value, function or regular expression to
* compare against
- *
+ *
* @return true if the item matches the state
*/
- matches(name: string, compare: object): boolean
+ matches(name: string, compare: object): boolean;
- /**
+ /**
* Fetch the descendants (children or children of children) of this item
* that match the properties in the specified object. Extended matching is
* possible by providing a compare function or regular expression. Matching
* points, colors only work as a comparison of the full object, not partial
* matching (e.g. only providing the x- coordinate to match all points with
* that x-value). Partial matching does work for {@link Item#data}.
- *
+ *
* Matching items against a rectangular area is also possible, by setting
* either `options.inside` or `options.overlapping` to a rectangle
* describing the area in which the items either have to be fully or partly
* contained.
- *
+ *
* See {@link Project#getItems} for a selection of illustrated
* examples.
- *
+ *
* @see #matches(options)
- *
+ *
* @option [options.recursive=true] {Boolean} whether to loop recursively
* through all children, or stop at the current level
* @option options.match {Function} a match function to be called for each
@@ -1959,14 +1949,14 @@ declare namespace paper {
* to be fully contained
* @option options.overlapping {Rectangle} the rectangle with which the
* items need to at least partly overlap
- *
+ *
* @param options - the criteria to match against
- *
+ *
* @return the list of matching descendant items
*/
- getItems(options: object | Function): Item[]
+ getItems(options: object | Function): Item[];
- /**
+ /**
* Fetch the first descendant (child or child of child) of this item
* that matches the properties in the specified object.
* Extended matching is possible by providing a compare function or
@@ -1976,44 +1966,44 @@ declare namespace paper {
* does work for {@link Item#data}.
* See {@link Project#getItems} for a selection of illustrated
* examples.
- *
+ *
* @see #getItems(options)
- *
+ *
* @param options - the criteria to match against
- *
+ *
* @return the first descendant item matching the given criteria
*/
- getItem(options: object | Function): Item
+ getItem(options: object | Function): Item;
- /**
+ /**
* Exports (serializes) the item with its content and child items to a JSON
* data string.
- *
+ *
* @option [options.asString=true] {Boolean} whether the JSON is returned as
* a `Object` or a `String`
* @option [options.precision=5] {Number} the amount of fractional digits in
* numbers used in JSON data
- *
+ *
* @param options - the serialization options
- *
+ *
* @return the exported JSON data
*/
- exportJSON(options?: object): string
+ exportJSON(options?: object): string;
- /**
+ /**
* Imports (deserializes) the stored JSON data into this item. If the data
* describes an item of the same class or a parent class of the item, the
* data is imported into the item itself. If not, the imported item is added
* to this item's {@link Item#children} list. Note that not all type of
* items can have children.
- *
+ *
* @param json - the JSON data to import from
*/
- importJSON(json: string): Item
+ importJSON(json: string): Item;
- /**
+ /**
* Exports the item with its content and child items as an SVG DOM.
- *
+ *
* @option [options.bounds='view'] {String|Rectangle} the bounds of the area
* to export, either as a string ({@values 'view', content'}), or a
* {@link Rectangle} object: `'view'` uses the view bounds,
@@ -2032,19 +2022,19 @@ declare namespace paper {
* @option [options.embedImages=true] {Boolean} whether raster images should
* be embedded as base64 data inlined in the xlink:href attribute, or
* kept as a link to their external URL.
- *
+ *
* @param options - the export options
- *
+ *
* @return the item converted to an SVG node or a
* `String` depending on `option.asString` value
*/
- exportSVG(options?: object): SVGElement | string
+ exportSVG(options?: object): SVGElement | string;
- /**
+ /**
* Converts the provided SVG content into Paper.js items and adds them to
* the this item's children list. Note that the item is not cleared first.
* You can call {@link Item#removeChildren} to do so.
- *
+ *
* @option [options.expandShapes=false] {Boolean} whether imported shape
* items should be expanded to path items
* @option options.onLoad {Function} the callback function to call once the
@@ -2059,506 +2049,506 @@ declare namespace paper {
* @option [options.applyMatrix={@link PaperScope#settings}.applyMatrix]
* {Boolean} whether the imported items should have their transformation
* matrices applied to their contents or not
- *
+ *
* @param svg - the SVG content to import, either as a SVG
* DOM node, a string containing SVG content, or a string describing the
* URL of the SVG file to fetch.
* @param options - the import options
- *
+ *
* @return the newly created Paper.js item containing the converted
* SVG content
*/
- importSVG(svg: SVGElement | string, options?: object): Item
+ importSVG(svg: SVGElement | string, options?: object): Item;
- /**
+ /**
* Imports the provided external SVG file, converts it into Paper.js items
* and adds them to the this item's children list. Note that the item is not
* cleared first. You can call {@link Item#removeChildren} to do so.
- *
+ *
* @param svg - the URL of the SVG file to fetch.
* @param onLoad - the callback function to call once the SVG
* content is loaded from the given URL receiving two arguments: the
* converted `item` and the original `svg` data as a string. Only
* required when loading from external files.
- *
+ *
* @return the newly created Paper.js item containing the converted
* SVG content
*/
- importSVG(svg: SVGElement | string, onLoad: Function): Item
+ importSVG(svg: SVGElement | string, onLoad: Function): Item;
- /**
+ /**
* Adds the specified item as a child of this item at the end of the its
* {@link #children} list. You can use this function for groups, compound
* paths and layers.
- *
+ *
* @param item - the item to be added as a child
- *
+ *
* @return the added item, or `null` if adding was not possible
*/
- addChild(item: Item): Item
+ addChild(item: Item): Item;
- /**
+ /**
* Inserts the specified item as a child of this item at the specified index
* in its {@link #children} list. You can use this function for groups,
* compound paths and layers.
- *
+ *
* @param index - the index at which to insert the item
* @param item - the item to be inserted as a child
- *
+ *
* @return the inserted item, or `null` if inserting was not possible
*/
- insertChild(index: number, item: Item): Item
+ insertChild(index: number, item: Item): Item;
- /**
+ /**
* Adds the specified items as children of this item at the end of the its
* children list. You can use this function for groups, compound paths and
* layers.
- *
+ *
* @param items - the items to be added as children
- *
+ *
* @return the added items, or `null` if adding was not possible
*/
- addChildren(items: Item[]): Item[]
+ addChildren(items: Item[]): Item[];
- /**
+ /**
* Inserts the specified items as children of this item at the specified
* index in its {@link #children} list. You can use this function for
* groups, compound paths and layers.
- *
+ *
* @param items - the items to be appended as children
- *
+ *
* @return the inserted items, or `null` if inserted was not
* possible
*/
- insertChildren(index: number, items: Item[]): Item[]
+ insertChildren(index: number, items: Item[]): Item[];
- /**
+ /**
* Inserts this item above the specified item.
- *
+ *
* @param item - the item above which it should be inserted
- *
+ *
* @return the inserted item, or `null` if inserting was not possible
*/
- insertAbove(item: Item): Item
+ insertAbove(item: Item): Item;
- /**
+ /**
* Inserts this item below the specified item.
- *
+ *
* @param item - the item below which it should be inserted
- *
+ *
* @return the inserted item, or `null` if inserting was not possible
*/
- insertBelow(item: Item): Item
+ insertBelow(item: Item): Item;
- /**
+ /**
* Sends this item to the back of all other items within the same parent.
*/
- sendToBack(): void
+ sendToBack(): void;
- /**
+ /**
* Brings this item to the front of all other items within the same parent.
*/
- bringToFront(): void
+ bringToFront(): void;
- /**
+ /**
* Adds it to the specified owner, which can be either a {@link Item} or a
* {@link Project}.
- *
+ *
* @param owner - the item or project to
* add the item to
- *
+ *
* @return the item itself, if it was successfully added
*/
- addTo(owner: Project | Layer | Group | CompoundPath): this
+ addTo(owner: Project | Layer | Group | CompoundPath): this;
- /**
+ /**
* Clones the item and adds it to the specified owner, which can be either
* a {@link Item} or a {@link Project}.
- *
+ *
* @param owner - the item or project to
* copy the item to
- *
+ *
* @return the new copy of the item, if it was successfully added
*/
- copyTo(owner: Project | Layer | Group | CompoundPath): this
+ copyTo(owner: Project | Layer | Group | CompoundPath): this;
- /**
+ /**
* If this is a group, layer or compound-path with only one child-item,
* the child-item is moved outside and the parent is erased. Otherwise, the
* item itself is returned unmodified.
- *
+ *
* @return the reduced item
*/
- reduce(options: any): Item
+ reduce(options: any): Item;
- /**
+ /**
* Removes the item and all its children from the project. The item is not
* destroyed and can be inserted again after removal.
- *
+ *
* @return true if the item was removed
*/
- remove(): boolean
+ remove(): boolean;
- /**
+ /**
* Replaces this item with the provided new item which will takes its place
* in the project hierarchy instead.
- *
+ *
* @param item - the item that will replace this item
- *
+ *
* @return true if the item was replaced
*/
- replaceWith(item: Item): boolean
+ replaceWith(item: Item): boolean;
- /**
+ /**
* Removes all of the item's {@link #children} (if any).
- *
+ *
* @return an array containing the removed items
*/
- removeChildren(): Item[]
+ removeChildren(): Item[];
- /**
+ /**
* Removes the children from the specified `start` index to and excluding
* the `end` index from the parent's {@link #children} array.
- *
+ *
* @param start - the beginning index, inclusive
* @param end - the ending index, exclusive
- *
+ *
* @return an array containing the removed items
*/
- removeChildren(start: number, end?: number): Item[]
+ removeChildren(start: number, end?: number): Item[];
- /**
+ /**
* Reverses the order of the item's children
*/
- reverseChildren(): void
+ reverseChildren(): void;
- /**
+ /**
* Specifies whether the item has any content or not. The meaning of what
* content is differs from type to type. For example, a {@link Group} with
* no children, a {@link TextItem} with no text content and a {@link Path}
* with no segments all are considered empty.
- *
+ *
* @param recursively - whether an item with children should be
* considered empty if all its descendants are empty
*/
- isEmpty(recursively?: boolean): boolean
+ isEmpty(recursively?: boolean): boolean;
- /**
+ /**
* Checks whether the item has a fill.
- *
+ *
* @return true if the item has a fill
*/
- hasFill(): boolean
+ hasFill(): boolean;
- /**
+ /**
* Checks whether the item has a stroke.
- *
+ *
* @return true if the item has a stroke
*/
- hasStroke(): boolean
+ hasStroke(): boolean;
- /**
+ /**
* Checks whether the item has a shadow.
- *
+ *
* @return true if the item has a shadow
*/
- hasShadow(): boolean
+ hasShadow(): boolean;
- /**
+ /**
* Checks if the item contains any children items.
- *
+ *
* @return true it has one or more children
*/
- hasChildren(): boolean
+ hasChildren(): boolean;
- /**
+ /**
* Checks whether the item and all its parents are inserted into scene graph
* or not.
- *
+ *
* @return true if the item is inserted into the scene graph
*/
- isInserted(): boolean
+ isInserted(): boolean;
- /**
+ /**
* Checks if this item is above the specified item in the stacking order
* of the project.
- *
+ *
* @param item - the item to check against
- *
+ *
* @return true if it is above the specified item
*/
- isAbove(item: Item): boolean
+ isAbove(item: Item): boolean;
- /**
+ /**
* Checks if the item is below the specified item in the stacking order of
* the project.
- *
+ *
* @param item - the item to check against
- *
+ *
* @return true if it is below the specified item
*/
- isBelow(item: Item): boolean
+ isBelow(item: Item): boolean;
- /**
+ /**
* Checks whether the specified item is the parent of the item.
- *
+ *
* @param item - the item to check against
- *
+ *
* @return true if it is the parent of the item
*/
- isParent(item: Item): boolean
+ isParent(item: Item): boolean;
- /**
+ /**
* Checks whether the specified item is a child of the item.
- *
+ *
* @param item - the item to check against
- *
+ *
* @return true it is a child of the item
*/
- isChild(item: Item): boolean
+ isChild(item: Item): boolean;
- /**
+ /**
* Checks if the item is contained within the specified item.
- *
+ *
* @param item - the item to check against
- *
+ *
* @return true if it is inside the specified item
*/
- isDescendant(item: Item): boolean
+ isDescendant(item: Item): boolean;
- /**
+ /**
* Checks if the item is an ancestor of the specified item.
- *
+ *
* @param item - the item to check against
- *
+ *
* @return true if the item is an ancestor of the specified
* item
*/
- isAncestor(item: Item): boolean
+ isAncestor(item: Item): boolean;
- /**
+ /**
* Checks if the item is an a sibling of the specified item.
- *
+ *
* @param item - the item to check against
- *
+ *
* @return true if the item is aa sibling of the specified item
*/
- isSibling(item: Item): boolean
+ isSibling(item: Item): boolean;
- /**
+ /**
* Checks whether the item is grouped with the specified item.
- *
+ *
* @return true if the items are grouped together
*/
- isGroupedWith(item: Item): boolean
+ isGroupedWith(item: Item): boolean;
- /**
+ /**
* Translates (moves) the item by the given offset views.
- *
+ *
* @param delta - the offset to translate the item by
*/
- translate(delta: Point): void
+ translate(delta: Point): void;
- /**
+ /**
* Rotates the item by a given angle around the given center point.
- *
+ *
* Angles are oriented clockwise and measured in degrees.
- *
+ *
* @see Matrix#rotate(angle[, center])
- *
+ *
* @param angle - the rotation angle
*/
- rotate(angle: number, center?: Point): void
+ rotate(angle: number, center?: Point): void;
- /**
+ /**
* Scales the item by the given value from its center point, or optionally
* from a supplied point.
- *
+ *
* @param scale - the scale factor
*/
- scale(scale: number, center?: Point): void
+ scale(scale: number, center?: Point): void;
- /**
+ /**
* Scales the item by the given values from its center point, or optionally
* from a supplied point.
- *
+ *
* @param hor - the horizontal scale factor
* @param ver - the vertical scale factor
*/
- scale(hor: number, ver: number, center?: Point): void
+ scale(hor: number, ver: number, center?: Point): void;
- /**
+ /**
* Shears the item by the given value from its center point, or optionally
* by a supplied point.
- *
+ *
* @see Matrix#shear(shear[, center])
- *
+ *
* @param shear - the horizontal and vertical shear factors as a point
*/
- shear(shear: Point, center?: Point): void
+ shear(shear: Point, center?: Point): void;
- /**
+ /**
* Shears the item by the given values from its center point, or optionally
* by a supplied point.
- *
+ *
* @see Matrix#shear(hor, ver[, center])
- *
+ *
* @param hor - the horizontal shear factor
* @param ver - the vertical shear factor
*/
- shear(hor: number, ver: number, center?: Point): void
+ shear(hor: number, ver: number, center?: Point): void;
- /**
+ /**
* Skews the item by the given angles from its center point, or optionally
* by a supplied point.
- *
+ *
* @see Matrix#shear(skew[, center])
- *
+ *
* @param skew - the horizontal and vertical skew angles in degrees
*/
- skew(skew: Point, center?: Point): void
+ skew(skew: Point, center?: Point): void;
- /**
+ /**
* Skews the item by the given angles from its center point, or optionally
* by a supplied point.
- *
+ *
* @see Matrix#shear(hor, ver[, center])
- *
+ *
* @param hor - the horizontal skew angle in degrees
* @param ver - the vertical sskew angle in degrees
*/
- skew(hor: number, ver: number, center?: Point): void
+ skew(hor: number, ver: number, center?: Point): void;
- /**
+ /**
* Transform the item.
- *
+ *
* @param matrix - the matrix by which the item shall be transformed
*/
- transform(matrix: Matrix): void
+ transform(matrix: Matrix): void;
- /**
+ /**
* Converts the specified point from global project coordinate space to the
* item's own local coordinate space.
- *
+ *
* @param point - the point to be transformed
- *
+ *
* @return the transformed point as a new instance
*/
- globalToLocal(point: Point): Point
+ globalToLocal(point: Point): Point;
- /**
+ /**
* Converts the specified point from the item's own local coordinate space
* to the global project coordinate space.
- *
+ *
* @param point - the point to be transformed
- *
+ *
* @return the transformed point as a new instance
*/
- localToGlobal(point: Point): Point
+ localToGlobal(point: Point): Point;
- /**
+ /**
* Converts the specified point from the parent's coordinate space to
* item's own local coordinate space.
- *
+ *
* @param point - the point to be transformed
- *
+ *
* @return the transformed point as a new instance
*/
- parentToLocal(point: Point): Point
+ parentToLocal(point: Point): Point;
- /**
+ /**
* Converts the specified point from the item's own local coordinate space
* to the parent's coordinate space.
- *
+ *
* @param point - the point to be transformed
- *
+ *
* @return the transformed point as a new instance
*/
- localToParent(point: Point): Point
+ localToParent(point: Point): Point;
- /**
+ /**
* Transform the item so that its {@link #bounds} fit within the specified
* rectangle, without changing its aspect ratio.
*/
- fitBounds(rectangle: Rectangle, fill?: boolean): void
+ fitBounds(rectangle: Rectangle, fill?: boolean): void;
- /**
+ /**
* Attaches an event handler to the item.
- *
+ *
* @param type - the type of event: {@values 'frame', mousedown',
* 'mouseup', 'mousedrag', 'click', 'doubleclick', 'mousemove',
* 'mouseenter', 'mouseleave'}
* @param function - the function to be called when the event
* occurs, receiving a {@link MouseEvent} or {@link Event} object as its
* sole argument
- *
+ *
* @return this item itself, so calls can be chained
*/
- on(type: string, callback: Function): this
+ on(type: string, callback: Function): this;
- /**
+ /**
* Attaches one or more event handlers to the item.
- *
+ *
* @param object - an object containing one or more of the following
* properties: {@values frame, mousedown, mouseup, mousedrag, click,
* doubleclick, mousemove, mouseenter, mouseleave}
- *
+ *
* @return this item itself, so calls can be chained
*/
- on(object: object): this
+ on(object: object): this;
- /**
+ /**
* Detach an event handler from the item.
- *
+ *
* @param type - the type of event: {@values 'frame', mousedown',
* 'mouseup', 'mousedrag', 'click', 'doubleclick', 'mousemove',
* 'mouseenter', 'mouseleave'}
* @param function - the function to be detached
- *
+ *
* @return this item itself, so calls can be chained
*/
- off(type: string, callback: Function): this
+ off(type: string, callback: Function): this;
- /**
+ /**
* Detach one or more event handlers to the item.
- *
+ *
* @param object - an object containing one or more of the following
* properties: {@values frame, mousedown, mouseup, mousedrag, click,
* doubleclick, mousemove, mouseenter, mouseleave}
- *
+ *
* @return this item itself, so calls can be chained
*/
- off(object: object): this
+ off(object: object): this;
- /**
+ /**
* Emit an event on the item.
- *
+ *
* @param type - the type of event: {@values 'frame', mousedown',
* 'mouseup', 'mousedrag', 'click', 'doubleclick', 'mousemove',
* 'mouseenter', 'mouseleave'}
* @param event - an object literal containing properties describing
* the event
- *
+ *
* @return true if the event had listeners
*/
- emit(type: string, event: object): boolean
+ emit(type: string, event: object): boolean;
- /**
+ /**
* Check if the item has one or more event handlers of the specified type.
- *
+ *
* @param type - the type of event: {@values 'frame', mousedown',
* 'mouseup', 'mousedrag', 'click', 'doubleclick', 'mousemove',
* 'mouseenter', 'mouseleave'}
- *
+ *
* @return true if the item has one or more event handlers of
* the specified type
*/
- responds(type: string): boolean
+ responds(type: string): boolean;
- /**
+ /**
* Removes the item when the events specified in the passed options object
* occur.
- *
+ *
* @option options.move {Boolean) remove the item when the next {@link
* Tool#onMouseMove} event is fired.
* @option options.drag {Boolena) remove the item when the next {@link
@@ -2568,31 +2558,31 @@ declare namespace paper {
* @option options.up {Boolean) remove the item when the next {@link
* Tool#onMouseUp} event is fired.
*/
- removeOn(options: object): void
+ removeOn(options: object): void;
- /**
+ /**
* Removes the item when the next {@link Tool#onMouseMove} event is fired.
*/
- removeOnMove(): void
+ removeOnMove(): void;
- /**
+ /**
* Removes the item when the next {@link Tool#onMouseDown} event is fired.
*/
- removeOnDown(): void
+ removeOnDown(): void;
- /**
+ /**
* Removes the item when the next {@link Tool#onMouseDrag} event is fired.
*/
- removeOnDrag(): void
+ removeOnDrag(): void;
- /**
+ /**
* Removes the item when the next {@link Tool#onMouseUp} event is fired.
*/
- removeOnUp(): void
+ removeOnUp(): void;
- /**
+ /**
* Tween item between two states.
- *
+ *
* @option options.duration {Number} the duration of the tweening
* @option [options.easing='linear'] {Function|String} an easing function or the type
* of the easing: {@values 'linear' 'easeInQuad' 'easeOutQuad'
@@ -2600,59 +2590,57 @@ declare namespace paper {
* 'easeInQuart' 'easeOutQuart' 'easeInOutQuart' 'easeInQuint'
* 'easeOutQuint' 'easeInOutQuint'}
* @option [options.start=true] {Boolean} whether to start tweening automatically
- *
+ *
* @param from - the state at the start of the tweening
* @param to - the state at the end of the tweening
* @param options - the options or the duration
*/
- tween(from: object, to: object, options: object | number): Tween
+ tween(from: object, to: object, options: object | number): Tween;
- /**
+ /**
* Tween item to a state.
- *
+ *
* @see Item#tween(from, to, options)
- *
+ *
* @param to - the state at the end of the tweening
* @param options - the options or the duration
*/
- tween(to: object, options: object | number): Tween
+ tween(to: object, options: object | number): Tween;
- /**
+ /**
* Tween item.
- *
+ *
* @see Item#tween(from, to, options)
- *
+ *
* @param options - the options or the duration
*/
- tween(options: object | number): Tween
+ tween(options: object | number): Tween;
- /**
+ /**
* Tween item to a state.
- *
+ *
* @see Item#tween(to, options)
- *
+ *
* @param to - the state at the end of the tweening
* @param options - the options or the duration
*/
- tweenTo(to: object, options: object | number): Tween
+ tweenTo(to: object, options: object | number): Tween;
- /**
+ /**
* Tween item from a state to its state before the tweening.
- *
+ *
* @see Item#tween(from, to, options)
- *
+ *
* @param from - the state at the start of the tweening
* @param options - the options or the duration
*/
- tweenFrom(from: object, options: object | number): Tween
-
+ tweenFrom(from: object, options: object | number): Tween;
}
-
- class Key {
- /**
+ class Key {
+ /**
* The current state of the keyboard modifiers.
- *
+ *
* @option modifiers.shift {Boolean} {@true if the shift key is
* pressed}.
* @option modifiers.control {Boolean} {@true if the control key is
@@ -2670,186 +2658,179 @@ declare namespace paper {
* @option modifiers.command {Boolean} {@true if the meta key is pressed
* on Mac, or the control key is pressed on Windows and Linux}.
*/
- static modifiers: any
+ static modifiers: any;
-
- /**
+ /**
* Checks whether the specified key is pressed.
- *
+ *
* @param key - any character or special key descriptor:
* {@values 'enter', 'space', 'shift', 'control', 'alt', 'meta',
* 'caps-lock', 'left', 'up', 'right', 'down', 'escape', 'delete',
* ...}
- *
+ *
* @return true if the key is pressed
*/
- static isDown(key: string): boolean
-
+ static isDown(key: string): boolean;
}
- /**
+ /**
* The KeyEvent object is received by the {@link Tool}'s keyboard
* handlers {@link Tool#onKeyDown}, {@link Tool#onKeyUp}. The KeyEvent object is
* the only parameter passed to these functions and contains information about
* the keyboard event.
*/
class KeyEvent extends Event {
- /**
+ /**
* The type of mouse event.
*/
- type: string
+ type: string;
- /**
+ /**
* The character representation of the key that caused this key event,
* taking into account the current key-modifiers (e.g. shift, control,
* caps-lock, etc.)
*/
- character: string
+ character: string;
- /**
+ /**
* The key that caused this key event, either as a lower-case character or
* special key descriptor.
*/
- key: string
-
+ key: string;
- /**
+ /**
* @return a string representation of the key event
*/
- toString(): string
-
+ toString(): string;
}
- /**
+ /**
* The Layer item represents a layer in a Paper.js project.
- *
+ *
* The layer which is currently active can be accessed through
* {@link Project#activeLayer}.
* An array of all layers in a project can be accessed through
* {@link Project#layers}.
*/
class Layer extends Group {
-
- /**
+ /**
* Creates a new Layer item and places it at the end of the
* {@link Project#layers} array. The newly created layer will be activated,
* so all newly created items will be placed within it.
- *
+ *
* @param children - An array of items that will be added to the
* newly created layer
*/
- constructor(children?: Item[])
+ constructor(children?: Item[]);
- /**
+ /**
* Creates a new Layer item and places it at the end of the
* {@link Project#layers} array. The newly created layer will be activated,
* so all newly created items will be placed within it.
- *
+ *
* @param object - an object containing the properties to be set on
* the layer
*/
- constructor(object: object)
+ constructor(object: object);
- /**
+ /**
* Activates the layer.
*/
- activate(): void
-
+ activate(): void;
}
- /**
+ /**
* An affine transformation matrix performs a linear mapping from 2D
* coordinates to other 2D coordinates that preserves the "straightness" and
* "parallelness" of lines.
- *
+ *
* Such a coordinate transformation can be represented by a 3 row by 3
* column matrix with an implied last row of `[ 0 0 1 ]`. This matrix
* transforms source coordinates `(x, y)` into destination coordinates `(x',y')`
* by considering them to be a column vector and multiplying the coordinate
* vector by the matrix according to the following process:
- *
+ *
* [ x ] [ a c tx ] [ x ] [ a * x + c * y + tx ]
* [ y ] = [ b d ty ] [ y ] = [ b * x + d * y + ty ]
* [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ]
- *
+ *
* Note the locations of b and c.
- *
+ *
* This class is optimized for speed and minimizes calculations based on its
* knowledge of the underlying matrix (as opposed to say simply performing
* matrix multiplication).
*/
- class Matrix {
- /**
+ class Matrix {
+ /**
* The value that affects the transformation along the x axis when scaling
* or rotating, positioned at (0, 0) in the transformation matrix.
*/
- a: number
+ a: number;
- /**
+ /**
* The value that affects the transformation along the y axis when rotating
* or skewing, positioned at (1, 0) in the transformation matrix.
*/
- b: number
+ b: number;
- /**
+ /**
* The value that affects the transformation along the x axis when rotating
* or skewing, positioned at (0, 1) in the transformation matrix.
*/
- c: number
+ c: number;
- /**
+ /**
* The value that affects the transformation along the y axis when scaling
* or rotating, positioned at (1, 1) in the transformation matrix.
*/
- d: number
+ d: number;
- /**
+ /**
* The distance by which to translate along the x axis, positioned at (2, 0)
* in the transformation matrix.
*/
- tx: number
+ tx: number;
- /**
+ /**
* The distance by which to translate along the y axis, positioned at (2, 1)
* in the transformation matrix.
*/
- ty: number
+ ty: number;
- /**
+ /**
* The matrix values as an array, in the same sequence as they are passed
* to {@link #initialize}.
*/
- readonly values: number[]
+ readonly values: number[];
- /**
+ /**
* The translation of the matrix as a vector.
*/
- readonly translation: Point
+ readonly translation: Point;
- /**
+ /**
* The scaling values of the matrix, if it can be decomposed.
- *
+ *
* @see #decompose()
*/
- readonly scaling: Point
+ readonly scaling: Point;
- /**
+ /**
* The rotation angle of the matrix, if it can be decomposed.
- *
+ *
* @see #decompose()
*/
- readonly rotation: number
+ readonly rotation: number;
-
- /**
+ /**
* Creates a 2D affine transformation matrix that describes the identity
* transformation.
*/
- constructor()
+ constructor();
- /**
+ /**
* Creates a 2D affine transformation matrix.
- *
+ *
* @param a - the a property of the transform
* @param b - the b property of the transform
* @param c - the c property of the transform
@@ -2857,298 +2838,304 @@ declare namespace paper {
* @param tx - the tx property of the transform
* @param ty - the ty property of the transform
*/
- constructor(a: number, b: number, c: number, d: number, tx: number, ty: number)
+ constructor(
+ a: number,
+ b: number,
+ c: number,
+ d: number,
+ tx: number,
+ ty: number
+ );
- /**
+ /**
* Creates a 2D affine transformation matrix.
- *
+ *
* @param values - the matrix values to initialize this matrix with
*/
- constructor(values: number[])
+ constructor(values: number[]);
- /**
+ /**
* Creates a 2D affine transformation matrix.
- *
+ *
* @param matrix - the matrix to copy the values from
*/
- constructor(matrix: Matrix)
+ constructor(matrix: Matrix);
- /**
+ /**
* Sets the matrix to the passed values. Note that any sequence of
* parameters that is supported by the various {@link Matrix} constructors
* also work for calls of `set()`.
*/
- set(...values: any[]): Point
+ set(...values: any[]): Point;
- /**
+ /**
* @return a copy of this transform
*/
- clone(): Matrix
+ clone(): Matrix;
- /**
+ /**
* Checks whether the two matrices describe the same transformation.
- *
+ *
* @param matrix - the matrix to compare this matrix to
- *
+ *
* @return true if the matrices are equal
*/
- equals(matrix: Matrix): boolean
+ equals(matrix: Matrix): boolean;
- /**
+ /**
* @return a string representation of this transform
*/
- toString(): string
+ toString(): string;
- /**
+ /**
* Resets the matrix by setting its values to the ones of the identity
* matrix that results in no transformation.
*/
- reset(): void
+ reset(): void;
- /**
+ /**
* Attempts to apply the matrix to the content of item that it belongs to,
* meaning its transformation is baked into the item's content or children.
- *
+ *
* @param recursively - controls whether to apply
* transformations recursively on children
- *
+ *
* @return true if the matrix was applied
*/
- apply(recursively?: boolean): boolean
+ apply(recursively?: boolean): boolean;
- /**
+ /**
* Concatenates this matrix with a translate transformation.
- *
+ *
* @param point - the vector to translate by
- *
+ *
* @return this affine transform
*/
- translate(point: Point): Matrix
+ translate(point: Point): Matrix;
- /**
+ /**
* Concatenates this matrix with a translate transformation.
- *
+ *
* @param dx - the distance to translate in the x direction
* @param dy - the distance to translate in the y direction
- *
+ *
* @return this affine transform
*/
- translate(dx: number, dy: number): Matrix
+ translate(dx: number, dy: number): Matrix;
- /**
+ /**
* Concatenates this matrix with a scaling transformation.
- *
+ *
* @param scale - the scaling factor
* @param center - the center for the scaling transformation
- *
+ *
* @return this affine transform
*/
- scale(scale: number, center?: Point): Matrix
+ scale(scale: number, center?: Point): Matrix;
- /**
+ /**
* Concatenates this matrix with a scaling transformation.
- *
+ *
* @param hor - the horizontal scaling factor
* @param ver - the vertical scaling factor
* @param center - the center for the scaling transformation
- *
+ *
* @return this affine transform
*/
- scale(hor: number, ver: number, center?: Point): Matrix
+ scale(hor: number, ver: number, center?: Point): Matrix;
- /**
+ /**
* Concatenates this matrix with a rotation transformation around an
* anchor point.
- *
+ *
* @param angle - the angle of rotation measured in degrees
* @param center - the anchor point to rotate around
- *
+ *
* @return this affine transform
*/
- rotate(angle: number, center: Point): Matrix
+ rotate(angle: number, center: Point): Matrix;
- /**
+ /**
* Concatenates this matrix with a rotation transformation around an
* anchor point.
- *
+ *
* @param angle - the angle of rotation measured in degrees
* @param x - the x coordinate of the anchor point
* @param y - the y coordinate of the anchor point
- *
+ *
* @return this affine transform
*/
- rotate(angle: number, x: number, y: number): Matrix
+ rotate(angle: number, x: number, y: number): Matrix;
- /**
+ /**
* Concatenates this matrix with a shear transformation.
- *
+ *
* @param shear - the shear factor in x and y direction
* @param center - the center for the shear transformation
- *
+ *
* @return this affine transform
*/
- shear(shear: Point, center?: Point): Matrix
+ shear(shear: Point, center?: Point): Matrix;
- /**
+ /**
* Concatenates this matrix with a shear transformation.
- *
+ *
* @param hor - the horizontal shear factor
* @param ver - the vertical shear factor
* @param center - the center for the shear transformation
- *
+ *
* @return this affine transform
*/
- shear(hor: number, ver: number, center?: Point): Matrix
+ shear(hor: number, ver: number, center?: Point): Matrix;
- /**
+ /**
* Concatenates this matrix with a skew transformation.
- *
+ *
* @param skew - the skew angles in x and y direction in degrees
* @param center - the center for the skew transformation
- *
+ *
* @return this affine transform
*/
- skew(skew: Point, center?: Point): Matrix
+ skew(skew: Point, center?: Point): Matrix;
- /**
+ /**
* Concatenates this matrix with a skew transformation.
- *
+ *
* @param hor - the horizontal skew angle in degrees
* @param ver - the vertical skew angle in degrees
* @param center - the center for the skew transformation
- *
+ *
* @return this affine transform
*/
- skew(hor: number, ver: number, center?: Point): Matrix
+ skew(hor: number, ver: number, center?: Point): Matrix;
- /**
+ /**
* Appends the specified matrix to this matrix. This is the equivalent of
* multiplying `(this matrix) * (specified matrix)`.
- *
+ *
* @param matrix - the matrix to append
- *
+ *
* @return this matrix, modified
*/
- append(matrix: Matrix): Matrix
+ append(matrix: Matrix): Matrix;
- /**
+ /**
* Prepends the specified matrix to this matrix. This is the equivalent of
* multiplying `(specified matrix) * (this matrix)`.
- *
+ *
* @param matrix - the matrix to prepend
- *
+ *
* @return this matrix, modified
*/
- prepend(matrix: Matrix): Matrix
+ prepend(matrix: Matrix): Matrix;
- /**
+ /**
* Returns a new matrix as the result of appending the specified matrix to
* this matrix. This is the equivalent of multiplying
* `(this matrix) * (specified matrix)`.
- *
+ *
* @param matrix - the matrix to append
- *
+ *
* @return the newly created matrix
*/
- appended(matrix: Matrix): Matrix
+ appended(matrix: Matrix): Matrix;
- /**
+ /**
* Returns a new matrix as the result of prepending the specified matrix
* to this matrix. This is the equivalent of multiplying
* `(specified matrix) * (this matrix)`.
- *
+ *
* @param matrix - the matrix to prepend
- *
+ *
* @return the newly created matrix
*/
- prepended(matrix: Matrix): Matrix
+ prepended(matrix: Matrix): Matrix;
- /**
+ /**
* Inverts the matrix, causing it to perform the opposite transformation.
* If the matrix is not invertible (in which case {@link #isSingular}
* returns true), `null` is returned.
- *
+ *
* @return this matrix, or `null`, if the matrix is singular.
*/
- invert(): Matrix
+ invert(): Matrix;
- /**
+ /**
* Creates a new matrix that is the inversion of this matrix, causing it to
* perform the opposite transformation. If the matrix is not invertible (in
* which case {@link #isSingular} returns true), `null` is returned.
- *
+ *
* @return this matrix, or `null`, if the matrix is singular.
*/
- inverted(): Matrix
+ inverted(): Matrix;
- /**
+ /**
* @return whether this matrix is the identity matrix
*/
- isIdentity(): boolean
+ isIdentity(): boolean;
- /**
+ /**
* Checks whether the matrix is invertible. A matrix is not invertible if
* the determinant is 0 or any value is infinite or NaN.
- *
+ *
* @return whether the matrix is invertible
*/
- isInvertible(): boolean
+ isInvertible(): boolean;
- /**
+ /**
* Checks whether the matrix is singular or not. Singular matrices cannot be
* inverted.
- *
+ *
* @return whether the matrix is singular
*/
- isSingular(): boolean
+ isSingular(): boolean;
- /**
+ /**
* Transforms a point and returns the result.
- *
+ *
* @param point - the point to be transformed
- *
+ *
* @return the transformed point
*/
- transform(point: Point): Point
+ transform(point: Point): Point;
- /**
+ /**
* Transforms an array of coordinates by this matrix and stores the results
* into the destination array, which is also returned.
- *
+ *
* @param src - the array containing the source points
* as x, y value pairs
* @param dst - the array into which to store the transformed
* point pairs
* @param count - the number of points to transform
- *
+ *
* @return the dst array, containing the transformed coordinates
*/
- transform(src: number[], dst: number[], count: number): number[]
+ transform(src: number[], dst: number[], count: number): number[];
- /**
+ /**
* Inverse transforms a point and returns the result.
- *
+ *
* @param point - the point to be transformed
*/
- inverseTransform(point: Point): Point
+ inverseTransform(point: Point): Point;
- /**
+ /**
* Decomposes the affine transformation described by this matrix into
* `scaling`, `rotation` and `skewing`, and returns an object with
* these properties.
- *
+ *
* @return the decomposed matrix
*/
- decompose(): object
+ decompose(): object;
- /**
+ /**
* Applies this matrix to the specified Canvas Context.
*/
- applyToContext(ctx: CanvasRenderingContext2D): void
-
+ applyToContext(ctx: CanvasRenderingContext2D): void;
}
- /**
+ /**
* The MouseEvent object is received by the {@link Item}'s mouse event
* handlers {@link Item#onMouseDown}, {@link Item#onMouseDrag},
* {@link Item#onMouseMove}, {@link Item#onMouseUp}, {@link Item#onClick},
@@ -3157,52 +3144,49 @@ declare namespace paper {
* to these functions and contains information about the mouse event.
*/
class MouseEvent extends Event {
- /**
+ /**
* The type of mouse event.
*/
- type: string
+ type: string;
- /**
+ /**
* The position of the mouse in project coordinates when the event was
* fired.
*/
- point: Point
+ point: Point;
- /**
+ /**
* The item that dispatched the event. It is different from
* {@link #currentTarget} when the event handler is called during
* the bubbling phase of the event.
*/
- target: Item
+ target: Item;
- /**
+ /**
* The current target for the event, as the event traverses the scene graph.
* It always refers to the element the event handler has been attached to as
* opposed to {@link #target} which identifies the element on
* which the event occurred.
*/
- currentTarget: Item
-
-
- delta: Point
+ currentTarget: Item;
+ delta: Point;
- /**
+ /**
* @return a string representation of the mouse event
*/
- toString(): string
-
+ toString(): string;
}
- /**
+ /**
* The `PaperScope` class represents the scope associated with a Paper
* context. When working with PaperScript, these scopes are automatically
* created for us, and through clever scoping the properties and methods of
* the active scope seem to become part of the global scope.
- *
+ *
* When working with normal JavaScript code, `PaperScope` objects need to be
* manually created and handled.
- *
+ *
* Paper classes can only be accessed through `PaperScope` objects. Thus in
* PaperScript they are global, while in JavaScript, they are available on the
* global {@link paper} object. For JavaScript you can use {@link
@@ -3210,19 +3194,19 @@ declare namespace paper {
* global scope. Note that when working with more than one scope, this still
* works for classes, but not for objects like {@link PaperScope#project}, since
* they are not updated in the injected scope if scopes are switched.
- *
+ *
* The global {@link paper} object is simply a reference to the currently active
* `PaperScope`.
*/
- class PaperScope {
- /**
+ class PaperScope {
+ /**
* The version of Paper.js, as a string.
*/
- readonly version: string
+ readonly version: string;
- /**
+ /**
* Gives access to paper's configurable settings.
- *
+ *
* @option [settings.insertItems=true] {Boolean} controls whether newly
* created items are automatically inserted into the scene graph, by
* adding them to {@link Project#activeLayer}
@@ -3234,168 +3218,169 @@ declare namespace paper {
* @option [settings.hitTolerance=0] {Number} the default tolerance for hit-
* tests, when no value is specified
*/
- settings: any
+ settings: any;
- /**
+ /**
* The currently active project.
*/
- project: Project
+ project: Project;
- /**
+ /**
* The list of all open projects within the current Paper.js context.
*/
- projects: Project[]
+ projects: Project[];
- /**
+ /**
* The reference to the active project's view.
*/
- readonly view: View
+ readonly view: View;
- /**
+ /**
* The reference to the active tool.
*/
- tool: Tool
+ tool: Tool;
- /**
+ /**
* The list of available tools.
*/
- tools: Tool[]
-
- Color: typeof Color
- CompoundPath: typeof CompoundPath
- Curve: typeof Curve
- CurveLocation: typeof CurveLocation
- Event: typeof Event
- Gradient: typeof Gradient
- GradientStop: typeof GradientStop
- Group: typeof Group
- HitResult: typeof HitResult
- Item: typeof Item
- Key: typeof Key
- KeyEvent: typeof KeyEvent
- Layer: typeof Layer
- Matrix: typeof Matrix
- MouseEvent: typeof MouseEvent
- PaperScope: typeof PaperScope
- PaperScript: typeof PaperScript
- Path: typeof Path
- PathItem: typeof PathItem
- Point: typeof Point
- PointText: typeof PointText
- Project: typeof Project
- Raster: typeof Raster
- Rectangle: typeof Rectangle
- Segment: typeof Segment
- Shape: typeof Shape
- Size: typeof Size
- Style: typeof Style
- SymbolDefinition: typeof SymbolDefinition
- SymbolItem: typeof SymbolItem
- TextItem: typeof TextItem
- Tool: typeof Tool
- ToolEvent: typeof ToolEvent
- Tween: typeof Tween
- View: typeof View
-
- /**
+ tools: Tool[];
+
+ Color: typeof Color;
+ CompoundPath: typeof CompoundPath;
+ Curve: typeof Curve;
+ CurveLocation: typeof CurveLocation;
+ Event: typeof Event;
+ Gradient: typeof Gradient;
+ GradientStop: typeof GradientStop;
+ Group: typeof Group;
+ HitResult: typeof HitResult;
+ Item: typeof Item;
+ Key: typeof Key;
+ KeyEvent: typeof KeyEvent;
+ Layer: typeof Layer;
+ Matrix: typeof Matrix;
+ MouseEvent: typeof MouseEvent;
+ PaperScope: typeof PaperScope;
+ PaperScript: typeof PaperScript;
+ Path: typeof Path;
+ PathItem: typeof PathItem;
+ Point: typeof Point;
+ PointText: typeof PointText;
+ Project: typeof Project;
+ Raster: typeof Raster;
+ Rectangle: typeof Rectangle;
+ Segment: typeof Segment;
+ Shape: typeof Shape;
+ Size: typeof Size;
+ Style: typeof Style;
+ SymbolDefinition: typeof SymbolDefinition;
+ SymbolItem: typeof SymbolItem;
+ TextItem: typeof TextItem;
+ Tool: typeof Tool;
+ ToolEvent: typeof ToolEvent;
+ Tween: typeof Tween;
+ View: typeof View;
+
+ /**
* Creates a PaperScope object.
*/
- constructor()
+ constructor();
- /**
+ /**
* Compiles the PaperScript code into a compiled function and executes it.
* The compiled function receives all properties of this {@link PaperScope}
* as arguments, to emulate a global scope with unaffected performance. It
* also installs global view and tool handlers automatically on the
* respective objects.
- *
+ *
* @option options.url {String} the url of the source, for source-map
* debugging
* @option options.source {String} the source to be used for the source-
* mapping, in case the code that's passed in has already been mingled.
- *
+ *
* @param code - the PaperScript code
* @param options - the compilation options
*/
- execute(code: string, options?: object): void
+ execute(code: string, options?: object): void;
- /**
+ /**
* Injects the paper scope into any other given scope. Can be used for
* example to inject the currently active PaperScope into the window's
* global scope, to emulate PaperScript-style globally accessible Paper
* classes and objects.
- *
+ *
* Please note: Using this method may override native constructors
* (e.g. Path). This may cause problems when using Paper.js in conjunction
* with other libraries that rely on these constructors. Keep the library
* scoped if you encounter issues caused by this.
*/
- install(scope: any): void
+ install(scope: any): void;
- /**
+ /**
* Sets up an empty project for us. If a canvas is provided, it also creates
* a {@link View} for it, both linked to this scope.
- *
+ *
* @param element - the HTML canvas element
* this scope should be associated with, or an ID string by which to find
* the element, or the size of the canvas to be created for usage in a web
* worker.
*/
- setup(element: HTMLCanvasElement | string | Size): void
+ setup(element: HTMLCanvasElement | string | Size): void;
- /**
+ /**
* Activates this PaperScope, so all newly created items will be placed
* in its active project.
*/
- activate(): void
+ activate(): void;
- /**
+ /**
* Retrieves a PaperScope object with the given scope id.
*/
- static get(id: any): PaperScope
-
+ static get(id: any): PaperScope;
}
-
- class PaperScript {
-
- /**
+ class PaperScript {
+ /**
* Compiles PaperScript code into JavaScript code.
- *
+ *
* @option options.url {String} the url of the source, for source-map
* generation
* @option options.source {String} the source to be used for the source-
* mapping, in case the code that's passed in has already been mingled.
- *
+ *
* @param code - the PaperScript code
* @param options - the compilation options
- *
+ *
* @return an object holding the compiled PaperScript translated
* into JavaScript code along with source-maps and other information.
*/
- static compile(code: string, options?: object): object
+ static compile(code: string, options?: object): object;
- /**
+ /**
* Compiles the PaperScript code into a compiled function and executes it.
* The compiled function receives all properties of the passed {@link
* PaperScope} as arguments, to emulate a global scope with unaffected
* performance. It also installs global view and tool handlers automatically
* on the respective objects.
- *
+ *
* @option options.url {String} the url of the source, for source-map
* generation
* @option options.source {String} the source to be used for the source-
* mapping, in case the code that's passed in has already been mingled.
- *
+ *
* @param code - the PaperScript code
* @param scope - the scope for which the code is executed
* @param options - the compilation options
- *
+ *
* @return the exports defined in the executed code
*/
- static execute(code: string, scope: PaperScope, options?: object): object
+ static execute(
+ code: string,
+ scope: PaperScope,
+ options?: object
+ ): object;
- /**
+ /**
* Loads, compiles and executes PaperScript code in the HTML document. Note
* that this method is executed automatically for all scripts in the
* document through a window load event. You can optionally call it earlier
@@ -3403,580 +3388,573 @@ declare namespace paper {
* setting the attribute `ignore="true"` or `data-paper-ignore="true"`, and
* call the `PaperScript.load(script)` method for each script separately
* when needed.
- *
+ *
* @param script - the script to load. If none is
* provided, all scripts of the HTML document are iterated over and
* loaded
- *
+ *
* @return the scope produced for the passed `script`, or
* `undefined` of multiple scripts area loaded
*/
- static load(script?: HTMLScriptElement): PaperScope
-
+ static load(script?: HTMLScriptElement): PaperScope;
}
- /**
+ /**
* The path item represents a path in a Paper.js project.
*/
class Path extends PathItem {
- /**
+ /**
* The segments contained within the path.
*/
- segments: Segment[]
+ segments: Segment[];
- /**
+ /**
* The first Segment contained within the path.
*/
- readonly firstSegment: Segment
+ readonly firstSegment: Segment;
- /**
+ /**
* The last Segment contained within the path.
*/
- readonly lastSegment: Segment
+ readonly lastSegment: Segment;
- /**
+ /**
* The curves contained within the path.
*/
- readonly curves: Curve[]
+ readonly curves: Curve[];
- /**
+ /**
* The first Curve contained within the path.
*/
- readonly firstCurve: Curve
+ readonly firstCurve: Curve;
- /**
+ /**
* The last Curve contained within the path.
*/
- readonly lastCurve: Curve
+ readonly lastCurve: Curve;
- /**
+ /**
* Specifies whether the path is closed. If it is closed, Paper.js connects
* the first and last segments.
*/
- closed: boolean
+ closed: boolean;
- /**
+ /**
* The approximate length of the path.
*/
- readonly length: number
+ readonly length: number;
- /**
+ /**
* The area that the path's geometry is covering. Self-intersecting paths
* can contain sub-areas that cancel each other out.
*/
- readonly area: number
+ readonly area: number;
- /**
+ /**
* Specifies whether the path and all its segments are selected. Cannot be
* `true` on an empty path.
*/
- fullySelected: boolean
-
+ fullySelected: boolean;
- /**
+ /**
* Creates a new path item and places it at the top of the active layer.
- *
+ *
* @param segments - An array of segments (or points to be
* converted to segments) that will be added to the path
*/
- constructor(segments?: Segment[])
+ constructor(segments?: Segment[]);
- /**
+ /**
* Creates a new path item from SVG path-data and places it at the top of
* the active layer.
- *
+ *
* @param pathData - the SVG path-data that describes the geometry
* of this path
*/
- constructor(pathData: string)
+ constructor(pathData: string);
- /**
+ /**
* Creates a new path item from an object description and places it at the
* top of the active layer.
- *
+ *
* @param object - an object containing properties to be set on the
* path
*/
- constructor(object: object)
+ constructor(object: object);
- /**
+ /**
* Adds one or more segments to the end of the {@link #segments} array of
* this path.
- *
+ *
* @param segment - the segment or point to be
* added.
- *
+ *
* @return the added segment(s). This is not necessarily
* the same object, e.g. if the segment to be added already belongs to
* another path.
*/
- add(...segment: (Segment | Point | number[])[]): Segment | Segment[]
+ add(...segment: (Segment | Point | number[])[]): Segment | Segment[];
- /**
+ /**
* Inserts one or more segments at a given index in the list of this path's
* segments.
- *
+ *
* @param index - the index at which to insert the segment
* @param segment - the segment or point to be inserted.
- *
+ *
* @return the added segment. This is not necessarily the same
* object, e.g. if the segment to be added already belongs to another path
*/
- insert(index: number, segment: Segment | Point): Segment
+ insert(index: number, segment: Segment | Point): Segment;
- /**
+ /**
* Adds an array of segments (or types that can be converted to segments)
* to the end of the {@link #segments} array.
- *
+ *
* @return 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
*/
- addSegments(segments: Segment[]): Segment[]
+ addSegments(segments: Segment[]): Segment[];
- /**
+ /**
* Inserts an array of segments at a given index in the path's
* {@link #segments} array.
- *
+ *
* @param index - the index at which to insert the segments
* @param segments - the segments to be inserted
- *
+ *
* @return 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(index: number, segments: Segment[]): Segment[]
+ insertSegments(index: number, segments: Segment[]): Segment[];
- /**
+ /**
* Removes the segment at the specified index of the path's
* {@link #segments} array.
- *
+ *
* @param index - the index of the segment to be removed
- *
+ *
* @return the removed segment
*/
- removeSegment(index: number): Segment
+ removeSegment(index: number): Segment;
- /**
+ /**
* Removes all segments from the path's {@link #segments} array.
- *
+ *
* @return an array containing the removed segments
*/
- removeSegments(): Segment[]
+ removeSegments(): Segment[];
- /**
+ /**
* Removes the segments from the specified `from` index to the `to` index
* from the path's {@link #segments} array.
- *
+ *
* @param from - the beginning index, inclusive
* @param to - the ending index, exclusive
- *
+ *
* @return an array containing the removed segments
*/
- removeSegments(from: number, to?: number): Segment[]
+ removeSegments(from: number, to?: number): Segment[];
- /**
+ /**
* Checks if any of the curves in the path have curve handles set.
- *
+ *
* @see Segment#hasHandles()
* @see Curve#hasHandles()
- *
+ *
* @return true if the path has curve handles set
*/
- hasHandles(): boolean
+ hasHandles(): boolean;
- /**
+ /**
* Clears the path's handles by setting their coordinates to zero,
* turning the path into a polygon (or a polyline if it isn't closed).
*/
- clearHandles(): void
+ clearHandles(): void;
- /**
+ /**
* Divides the path on the curve at the given offset or location into two
* curves, by inserting a new segment at the given location.
- *
+ *
* @see Curve#divideAt(location)
- *
+ *
* @param location - the offset or location on the
* path at which to divide the existing curve by inserting a new segment
- *
+ *
* @return the newly inserted segment if the location is valid,
* `null` otherwise
*/
- divideAt(location: number | CurveLocation): Segment
+ divideAt(location: number | CurveLocation): Segment;
- /**
+ /**
* Splits the path at the given offset or location. After splitting, the
* path will be open. If the path was open already, splitting will result in
* two paths.
- *
+ *
* @param location - the offset or location at which to
* split the path
- *
+ *
* @return the newly created path after splitting, if any
*/
- splitAt(location: number | CurveLocation): Path
+ splitAt(location: number | CurveLocation): Path;
- /**
+ /**
* Joins the path with the other specified path, which will be removed in
* the process. They can be joined if the first or last segments of either
* path lie in the same location. Locations are optionally compare with a
* provide `tolerance` value.
- *
+ *
* If `null` or `this` is passed as the other path, the path will be joined
* with itself if the first and last segment are in the same location.
- *
+ *
* @param path - the path to join this path with; `null` or `this` to
* join the path with itself
* @param tolerance - the tolerance with which to decide if two
* segments are to be considered the same location when joining
*/
- join(path: Path, tolerance?: number): void
+ join(path: Path, tolerance?: number): void;
- /**
+ /**
* Reduces the path by removing curves that have a length of 0,
* and unnecessary segments between two collinear flat curves.
- *
+ *
* @return the reduced path
*/
- reduce(options: any): Path
+ reduce(options: any): Path;
- /**
+ /**
* Attempts to create a new shape item with same geometry as this path item,
* and inherits all settings from it, similar to {@link Item#clone}.
- *
+ *
* @see Shape#toPath(insert)
- *
+ *
* @param insert - specifies whether the new shape should be
* inserted into the scene graph. When set to `true`, it is inserted above
* the path item
- *
+ *
* @return the newly created shape item with the same geometry as
* this path item if it can be matched, `null` otherwise
*/
- toShape(insert?: boolean): Shape
+ toShape(insert?: boolean): Shape;
- /**
+ /**
* Returns the curve location of the specified point if it lies on the
* path, `null` otherwise.
- *
+ *
* @param point - the point on the path
- *
+ *
* @return the curve location of the specified point
*/
- getLocationOf(point: Point): CurveLocation
+ getLocationOf(point: Point): CurveLocation;
- /**
+ /**
* Returns the length of the path from its beginning up to up to the
* specified point if it lies on the path, `null` otherwise.
- *
+ *
* @param point - the point on the path
- *
+ *
* @return the length of the path up to the specified point
*/
- getOffsetOf(point: Point): number
+ getOffsetOf(point: Point): number;
- /**
+ /**
* Returns the curve location of the specified offset on the path.
- *
+ *
* @param offset - the offset on the path, where `0` is at
* the beginning of the path and {@link Path#length} at the end
- *
+ *
* @return the curve location at the specified offset
*/
- getLocationAt(offset: number): CurveLocation
+ getLocationAt(offset: number): CurveLocation;
- /**
+ /**
* Calculates the point on the path at the given offset.
- *
+ *
* @param offset - the offset on the path, where `0` is at
* the beginning of the path and {@link Path#length} at the end
- *
+ *
* @return the point at the given offset
*/
- getPointAt(offset: number): Point
+ getPointAt(offset: number): Point;
- /**
+ /**
* Calculates the normalized tangent vector of the path at the given offset.
- *
+ *
* @param offset - the offset on the path, where `0` is at
* the beginning of the path and {@link Path#length} at the end
- *
+ *
* @return the normalized tangent vector at the given offset
*/
- getTangentAt(offset: number): Point
+ getTangentAt(offset: number): Point;
- /**
+ /**
* Calculates the normal vector of the path at the given offset.
- *
+ *
* @param offset - the offset on the path, where `0` is at
* the beginning of the path and {@link Path#length} at the end
- *
+ *
* @return the normal vector at the given offset
*/
- getNormalAt(offset: number): Point
+ getNormalAt(offset: number): Point;
- /**
+ /**
* Calculates the weighted tangent vector of the path at the given offset.
- *
+ *
* @param offset - the offset on the path, where `0` is at
* the beginning of the path and {@link Path#length} at the end
- *
+ *
* @return the weighted tangent vector at the given offset
*/
- getWeightedTangentAt(offset: number): Point
+ getWeightedTangentAt(offset: number): Point;
- /**
+ /**
* Calculates the weighted normal vector of the path at the given offset.
- *
+ *
* @param offset - the offset on the path, where `0` is at
* the beginning of the path and {@link Path#length} at the end
- *
+ *
* @return the weighted normal vector at the given offset
*/
- getWeightedNormalAt(offset: number): Point
+ getWeightedNormalAt(offset: number): Point;
- /**
+ /**
* Calculates the curvature of the path at the given offset. Curvatures
* indicate how sharply a path changes direction. A straight line has zero
* curvature, where as a circle has a constant curvature. The path's radius
* at the given offset is the reciprocal value of its curvature.
- *
+ *
* @param offset - the offset on the path, where `0` is at
* the beginning of the path and {@link Path#length} at the end
- *
+ *
* @return the normal vector at the given offset
*/
- getCurvatureAt(offset: number): number
+ getCurvatureAt(offset: number): number;
- /**
+ /**
* Calculates path offsets where the path is tangential to the provided
* tangent. Note that tangents at the start or end are included. Tangents at
* segment points are returned even if only one of their handles is
* collinear with the provided tangent.
- *
+ *
* @param tangent - the tangent to which the path must be tangential
- *
+ *
* @return path offsets where the path is tangential to the
* provided tangent
*/
- getOffsetsWithTangent(tangent: Point): number[]
-
+ getOffsetsWithTangent(tangent: Point): number[];
}
namespace Path {
-
class Line extends Path {
- /**
+ /**
* Creates a linear path item from two points describing a line.
- *
+ *
* @param from - the line's starting point
* @param to - the line's ending point
*/
- constructor(from: Point, to: Point)
+ constructor(from: Point, to: Point);
- /**
+ /**
* Creates a linear path item from the properties described by an object
* literal.
- *
+ *
* @param object - an object containing properties describing the
* path's attributes
*/
- constructor(object: object)
-
+ constructor(object: object);
}
class Circle extends Path {
- /**
+ /**
* Creates a circular path item.
- *
+ *
* @param center - the center point of the circle
* @param radius - the radius of the circle
*/
- constructor(center: Point, radius: number)
+ constructor(center: Point, radius: number);
- /**
+ /**
* Creates a circular path item from the properties described by an
* object literal.
- *
+ *
* @param object - an object containing properties describing the
* path's attributes
*/
- constructor(object: object)
-
+ constructor(object: object);
}
class Rectangle extends Path {
- /**
+ /**
* Creates a rectangular path item, with optionally rounded corners.
- *
+ *
* @param rectangle - the rectangle object describing the
* geometry of the rectangular path to be created
* @param radius - the size of the rounded corners
*/
- constructor(rectangle: paper.Rectangle, radius?: Size)
+ constructor(rectangle: paper.Rectangle, radius?: Size);
- /**
+ /**
* Creates a rectangular path item from a point and a size object.
- *
+ *
* @param point - the rectangle's top-left corner.
* @param size - the rectangle's size.
*/
- constructor(point: Point, size: Size)
+ constructor(point: Point, size: Size);
- /**
+ /**
* Creates a rectangular path item 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.
- *
+ *
* @param from - the first point defining the rectangle
* @param to - the second point defining the rectangle
*/
- constructor(from: Point, to: Point)
+ constructor(from: Point, to: Point);
- /**
+ /**
* Creates a rectangular path item from the properties described by an
* object literal.
- *
+ *
* @param object - an object containing properties describing the
* path's attributes
*/
- constructor(object: object)
-
+ constructor(object: object);
}
class Ellipse extends Path {
- /**
+ /**
* Creates an elliptical path item.
- *
+ *
* @param rectangle - the rectangle circumscribing the ellipse
*/
- constructor(rectangle: paper.Rectangle)
+ constructor(rectangle: paper.Rectangle);
- /**
+ /**
* Creates an elliptical path item from the properties described by an
* object literal.
- *
+ *
* @param object - an object containing properties describing the
* path's attributes
*/
- constructor(object: object)
-
+ constructor(object: object);
}
class Arc extends Path {
- /**
+ /**
* Creates a circular arc path item.
- *
+ *
* @param from - the starting point of the circular arc
* @param through - the point the arc passes through
* @param to - the end point of the arc
*/
- constructor(from: Point, through: Point, to: Point)
+ constructor(from: Point, through: Point, to: Point);
- /**
+ /**
* Creates an circular arc path item from the properties described by an
* object literal.
- *
+ *
* @param object - an object containing properties describing the
* path's attributes
*/
- constructor(object: object)
-
+ constructor(object: object);
}
class RegularPolygon extends Path {
- /**
+ /**
* Creates a regular polygon shaped path item.
- *
+ *
* @param center - the center point of the polygon
* @param sides - the number of sides of the polygon
* @param radius - the radius of the polygon
*/
- constructor(center: Point, sides: number, radius: number)
+ constructor(center: Point, sides: number, radius: number);
- /**
+ /**
* Creates a regular polygon shaped path item from the properties
* described by an object literal.
- *
+ *
* @param object - an object containing properties describing the
* path's attributes
*/
- constructor(object: object)
-
+ constructor(object: object);
}
class Star extends Path {
- /**
+ /**
* Creates a star shaped path item.
- *
+ *
* The largest of `radius1` and `radius2` will be the outer radius of
* the star. The smallest of radius1 and radius2 will be the inner
* radius.
- *
+ *
* @param center - the center point of the star
* @param points - the number of points of the star
*/
- constructor(center: Point, points: number, radius1: number, radius2: number)
-
- /**
+ constructor(
+ center: Point,
+ points: number,
+ radius1: number,
+ radius2: number
+ );
+
+ /**
* Creates a star shaped path item from the properties described by an
* object literal.
- *
+ *
* @param object - an object containing properties describing the
* path's attributes
*/
- constructor(object: object)
-
+ constructor(object: object);
}
}
- /**
+ /**
* The PathItem class is the base for any items that describe paths and
* offer standardised methods for drawing and path manipulation, such as
* {@link Path} and {@link CompoundPath}.
*/
class PathItem extends Item {
- /**
+ /**
* Returns a point that is guaranteed to be inside the path.
*/
- readonly interiorPoint: Point
+ readonly interiorPoint: Point;
- /**
+ /**
* Specifies whether the path as a whole is oriented clock-wise, by looking
* at the path's area.
* Note that self-intersecting paths and sub-paths of different orientation
* can result in areas that cancel each other out.
- *
+ *
* @see Path#area
* @see CompoundPath#area
*/
- clockwise: boolean
+ clockwise: boolean;
- /**
+ /**
* The path's geometry, formatted as SVG style path data.
*/
- pathData: string
+ pathData: string;
-
- /**
+ /**
* Unites the geometry of the specified path with this path's geometry
* and returns the result as a new path item.
- *
+ *
* @option [options.insert=true] {Boolean} whether the resulting item
* should be inserted back into the scene graph, above both paths
* involved in the operation
- *
+ *
* @param path - the path to unite with
* @param options - the boolean operation options
- *
+ *
* @return the resulting path item
*/
- unite(path: PathItem, options?: object): PathItem
+ unite(path: PathItem, options?: object): PathItem;
- /**
+ /**
* Intersects the geometry of the specified path with this path's
* geometry and returns the result as a new path item.
- *
+ *
* @option [options.insert=true] {Boolean} whether the resulting item
* should be inserted back into the scene graph, above both paths
* involved in the operation
@@ -3985,18 +3963,18 @@ declare namespace paper {
* of the paths are to be kept in the result, or whether the first
* path is only to be split at intersections, keeping the parts of
* the curves that intersect with the area of the second path.
- *
+ *
* @param path - the path to intersect with
* @param options - the boolean operation options
- *
+ *
* @return the resulting path item
*/
- intersect(path: PathItem, options?: object): PathItem
+ intersect(path: PathItem, options?: object): PathItem;
- /**
+ /**
* Subtracts the geometry of the specified path from this path's
* geometry and returns the result as a new path item.
- *
+ *
* @option [options.insert=true] {Boolean} whether the resulting item
* should be inserted back into the scene graph, above both paths
* involved in the operation
@@ -4005,35 +3983,35 @@ declare namespace paper {
* of the paths are to be kept in the result, or whether the first
* path is only to be split at intersections, removing the parts of
* the curves that intersect with the area of the second path.
- *
+ *
* @param path - the path to subtract
* @param options - the boolean operation options
- *
+ *
* @return the resulting path item
*/
- subtract(path: PathItem, options?: object): PathItem
+ subtract(path: PathItem, options?: object): PathItem;
- /**
+ /**
* Excludes the intersection of the geometry of the specified path with
* this path's geometry and returns the result as a new path item.
- *
+ *
* @option [options.insert=true] {Boolean} whether the resulting item
* should be inserted back into the scene graph, above both paths
* involved in the operation
- *
+ *
* @param path - the path to exclude the intersection of
* @param options - the boolean operation options
- *
+ *
* @return the resulting path item
*/
- exclude(path: PathItem, options?: object): PathItem
+ exclude(path: PathItem, options?: object): PathItem;
- /**
+ /**
* Splits the geometry of this path along the geometry of the specified
* path returns the result as a new group item. This is equivalent to
* calling {@link #subtract} and {@link #intersect} and
* putting the results into a new group.
- *
+ *
* @option [options.insert=true] {Boolean} whether the resulting item
* should be inserted back into the scene graph, above both paths
* involved in the operation
@@ -4041,140 +4019,140 @@ declare namespace paper {
* used, treating both paths as areas when determining which parts
* of the paths are to be kept in the result, or whether the first
* path is only to be split at intersections.
- *
+ *
* @param path - the path to divide by
* @param options - the boolean operation options
- *
+ *
* @return the resulting path item
*/
- divide(path: PathItem, options?: object): PathItem
+ divide(path: PathItem, options?: object): PathItem;
- /**
+ /**
* Fixes the orientation of the sub-paths of a compound-path, assuming
* that non of its sub-paths intersect, by reorienting them so that they
* are of different winding direction than their containing paths,
* except for disjoint sub-paths, i.e. islands, which are oriented so
* that they have the same winding direction as the the biggest path.
- *
+ *
* @param nonZero - controls if the non-zero fill-rule
* is to be applied, by counting the winding of each nested path and
* discarding sub-paths that do not contribute to the final result
* @param clockwise - if provided, the orientation of the root
* paths will be set to the orientation specified by `clockwise`,
* otherwise the orientation of the largest root child is used.
- *
+ *
* @return a reference to the item itself, reoriented
*/
- reorient(nonZero?: boolean, clockwise?: boolean): PathItem
+ reorient(nonZero?: boolean, clockwise?: boolean): PathItem;
- /**
+ /**
* Creates a path item from the given SVG path-data, determining if the
* data describes a plain path or a compound-path with multiple
* sub-paths.
- *
+ *
* @param pathData - the SVG path-data to parse
- *
+ *
* @return the newly created path item
*/
- static create(pathData: string): Path | CompoundPath
+ static create(pathData: string): Path | CompoundPath;
- /**
+ /**
* Creates a path item from the given segments array, determining if the
* array describes a plain path or a compound-path with multiple
* sub-paths.
- *
+ *
* @param segments - the segments array to parse
- *
+ *
* @return the newly created path item
*/
- static create(segments: number[][]): Path | CompoundPath
+ static create(segments: number[][]): Path | CompoundPath;
- /**
+ /**
* Creates a path item from the given object, determining if the
* contained information describes a plain path or a compound-path with
* multiple sub-paths.
- *
+ *
* @param object - an object containing the properties describing
* the item to be created
- *
+ *
* @return the newly created path item
*/
- static create(object: object): Path | CompoundPath
+ static create(object: object): Path | CompoundPath;
- /**
+ /**
* Returns all intersections between two {@link PathItem} items as an array
* of {@link CurveLocation} objects. {@link CompoundPath} items are also
* supported.
- *
+ *
* @see #getCrossings(path)
- *
+ *
* @param path - the other item to find the intersections with
* @param include - a callback function that can be used to
* filter out undesired locations right while they are collected. When
* defined, it shall return {@true to include a location}.
- *
+ *
* @return the locations of all intersection between the
* paths
*/
- getIntersections(path: PathItem, include?: Function): CurveLocation[]
+ getIntersections(path: PathItem, include?: Function): CurveLocation[];
- /**
+ /**
* Returns all crossings between two {@link PathItem} items as an array of
* {@link CurveLocation} objects. {@link CompoundPath} items are also
* supported. Crossings are intersections where the paths actually are
* crossing each other, as opposed to simply touching.
- *
+ *
* @see #getIntersections(path)
- *
+ *
* @param path - the other item to find the crossings with
- *
+ *
* @return the locations of all crossings between the
* paths
*/
- getCrossings(path: PathItem): CurveLocation[]
+ getCrossings(path: PathItem): CurveLocation[];
- /**
+ /**
* Returns the nearest location on the path item to the specified point.
- *
+ *
* @param point - the point for which we search the nearest location
- *
+ *
* @return the location on the path that's the closest to
* the specified point
*/
- getNearestLocation(point: Point): CurveLocation
+ getNearestLocation(point: Point): CurveLocation;
- /**
+ /**
* Returns the nearest point on the path item to the specified point.
- *
+ *
* @param point - the point for which we search the nearest point
- *
+ *
* @return the point on the path that's the closest to the specified
* point
*/
- getNearestPoint(point: Point): Point
+ getNearestPoint(point: Point): Point;
- /**
+ /**
* Reverses the orientation of the path item. When called on
* {@link CompoundPath} items, each of the nested paths is reversed. On
* {@link Path} items, the sequence of {@link Path#segments} is reversed.
*/
- reverse(): void
+ reverse(): void;
- /**
+ /**
* Flattens the curves in path items to a sequence of straight lines, by
* subdividing them enough times until the specified maximum error is met.
- *
+ *
* @param flatness - the maximum error between the flattened
* lines and the original curves
*/
- flatten(flatness?: number): void
+ flatten(flatness?: number): void;
- /**
+ /**
* Smooths the path item without changing the amount of segments in the path
* or moving the segments' locations, by smoothing and adjusting the angle
* and length of the segments' handles based on the position and distance of
* neighboring segments.
- *
+ *
* Smoothing works both for open paths and closed paths, and can be applied
* to the full path, as well as a sub-range of it. If a range is defined
* using the `options.from` and `options.to` properties, only the curve
@@ -4182,43 +4160,43 @@ declare namespace paper {
* are specified in negative indices, the indices are wrapped around the end
* of the curve. That way, a smoothing range in a close path can even wrap
* around the connection between the last and the first segment.
- *
+ *
* Four different smoothing methods are available:
- *
+ *
* - `'continuous'` smooths the path item by adjusting its curve handles so
* that the first and second derivatives of all involved curves are
* continuous across their boundaries.
- *
+ *
* This method tends to result in the smoothest results, but does not
* allow for further parametrization of the handles.
- *
+ *
* - `'asymmetric'` is based on the same principle as `'continuous'` but
* uses different factors so that the result is asymmetric. This used to
* the only method available until v0.10.0, and is currently still the
* default when no method is specified, for reasons of backward
* compatibility. It will eventually be removed.
- *
+ *
* - `'catmull-rom'` uses the Catmull-Rom spline to smooth the segment.
- *
+ *
* The optionally passed factor controls the knot parametrization of the
* algorithm:
- *
+ *
* - `0.0`: the standard, uniform Catmull-Rom spline
* - `0.5`: the centripetal Catmull-Rom spline, guaranteeing no
* self-intersections
* - `1.0`: the chordal Catmull-Rom spline
- *
+ *
* - `'geometric'` use a simple heuristic and empiric geometric method to
* smooth the segment's handles. The handles were weighted, meaning that
* big differences in distances between the segments will lead to
* probably undesired results.
- *
+ *
* The optionally passed factor defines the tension parameter (`0...1`),
* controlling the amount of smoothing as a factor by which to scale
* each handle.
- *
+ *
* @see Segment#smooth([options])
- *
+ *
* @option [options.type='asymmetric'] {String} the type of smoothing
* method: {@values 'continuous', 'asymmetric', 'catmull-rom',
* 'geometric'}
@@ -4234,35 +4212,35 @@ declare namespace paper {
* either be a segment index, or a segment or curve object that is part
* of the path. If the passed number is negative, the index is wrapped
* around the end of the path.
- *
+ *
* @param options - the smoothing options
*/
- smooth(options?: object): void
+ smooth(options?: object): void;
- /**
+ /**
* Fits a sequence of as few curves as possible through the path's anchor
* points, ignoring the path items's curve-handles, with an allowed maximum
* error. When called on {@link CompoundPath} items, each of the nested
* paths is simplified. On {@link Path} items, the {@link Path#segments}
* array is processed and replaced by the resulting sequence of fitted
* curves.
- *
+ *
* This method can be used to process and simplify the point data received
* from a mouse or touch device.
- *
+ *
* @param tolerance - the allowed maximum error when fitting
* the curves through the segment points
- *
+ *
* @return true if the method was capable of fitting curves
* through the path's segment points
*/
- simplify(tolerance?: number): boolean
+ simplify(tolerance?: number): boolean;
- /**
+ /**
* Interpolates between the two specified path items and uses the result
* as the geometry for this path item. The number of children and
* segments in the two paths involved in the operation should be the same.
- *
+ *
* @param from - the path item defining the geometry when `factor`
* is `0`
* @param to - the path item defining the geometry when `factor`
@@ -4270,76 +4248,76 @@ declare namespace paper {
* @param factor - the interpolation coefficient, typically between
* `0` and `1`, but extrapolation is possible too
*/
- interpolate(from: PathItem, to: PathItem, factor: number): void
+ interpolate(from: PathItem, to: PathItem, factor: number): void;
- /**
+ /**
* Compares the geometry of two paths to see if they describe the same
* shape, detecting cases where paths start in different segments or even
* use different amounts of curves to describe the same shape, as long as
* their orientation is the same, and their segments and handles really
* result in the same visual appearance of curves.
- *
+ *
* @param path - the path to compare this path's geometry with
- *
+ *
* @return true if two paths describe the same shape
*/
- compare(path: PathItem): boolean
+ compare(path: PathItem): boolean;
- /**
+ /**
* On a normal empty {@link Path}, the point is simply added as the path's
* first segment. If called on a {@link CompoundPath}, a new {@link Path} is
* created as a child and the point is added as its first segment.
- *
+ *
* @param point - the point in which to start the path
*/
- moveTo(point: Point): void
+ moveTo(point: Point): void;
- /**
+ /**
* Adds a straight curve to the path, from the the last segment in the path
* to the specified point.
- *
+ *
* @param point - the destination point of the newly added straight
* curve
*/
- lineTo(point: Point): void
+ lineTo(point: Point): void;
- /**
+ /**
* Adds an arc from the position of the last segment in the path, passing
* through the specified `through` point, to the specified `to` point, by
* adding one or more segments to the path.
- *
+ *
* @param through - the point where the arc should pass through
* @param to - the point where the arc should end
*/
- arcTo(through: Point, to: Point): void
+ arcTo(through: Point, to: Point): void;
- /**
+ /**
* Adds an arc from the position of the last segment in the path to
* the specified point, by adding one or more segments to the path.
- *
+ *
* @param to - the point where the arc should end
* @param clockwise - specifies whether the arc should be
* drawn in clockwise direction
*/
- arcTo(to: Point, clockwise?: boolean): void
+ arcTo(to: Point, clockwise?: boolean): void;
- /**
+ /**
* Adds a curve from the last segment in the path through the specified
* `through` point, to the specified destination point by adding one segment
* to the path.
- *
+ *
* @param through - the point through which the curve should pass
* @param to - the destination point of the newly added curve
* @param time - the curve-time parameter at which the
* `through` point is to be located
*/
- curveTo(through: Point, to: Point, time?: number): void
+ curveTo(through: Point, to: Point, time?: number): void;
- /**
+ /**
* Adds a cubic bezier curve to the path, from the last segment to the
* specified destination point, with the curve itself defined by two
* specified handles.
- *
+ *
* @param handle1 - the location of the first handle of the newly
* added curve in absolute coordinates, out of which the relative values
* for {@link Segment#handleOut} of its first segment are calculated
@@ -4348,16 +4326,16 @@ declare namespace paper {
* for {@link Segment#handleIn} of its second segment are calculated
* @param to - the destination point of the newly added curve
*/
- cubicCurveTo(handle1: Point, handle2: Point, to: Point): void
+ cubicCurveTo(handle1: Point, handle2: Point, to: Point): void;
- /**
+ /**
* Adds a quadratic bezier curve to the path, from the last segment to the
* specified destination point, with the curve itself defined by the
* specified handle.
- *
+ *
* Note that Paper.js only stores cubic curves, so the handle is actually
* converted.
- *
+ *
* @param handle - the location of the handle of the newly added
* quadratic curve in absolute coordinates, out of which the relative
* values for {@link Segment#handleOut} of the resulting cubic curve's
@@ -4365,698 +4343,692 @@ declare namespace paper {
* calculated
* @param to - the destination point of the newly added curve
*/
- quadraticCurveTo(handle: Point, to: Point): void
+ quadraticCurveTo(handle: Point, to: Point): void;
- /**
+ /**
* Closes the path. When closed, Paper.js connects the first and last
* segment of the path with an additional curve. The difference to setting
* {@link Path#closed} to `true` is that this will also merge the first
* segment with the last if they lie in the same location.
- *
+ *
* @see Path#closed
*/
- closePath(): void
+ closePath(): void;
- /**
+ /**
* If called on a {@link CompoundPath}, a new {@link Path} is created as a
* child and a point is added as its first segment relative to the position
* of the last segment of the current path.
*/
- moveBy(to: Point): void
+ moveBy(to: Point): void;
- /**
+ /**
* Adds a straight curve to the path, from the the last segment in the path
* to the `to` vector specified relatively to it.
- *
+ *
* @param point - the vector describing the destination of the newly
* added straight curve
*/
- lineBy(point: Point): void
+ lineBy(point: Point): void;
- /**
+ /**
* Adds an arc from the position of the last segment in the path, passing
* through the specified `through` vector, to the specified `to` vector, all
* specified relatively to it by these given vectors, by adding one or more
* segments to the path.
- *
+ *
* @param through - the vector where the arc should pass through
* @param to - the vector where the arc should end
*/
- arcBy(through: Point, to: Point): void
+ arcBy(through: Point, to: Point): void;
- /**
+ /**
* Adds an arc from the position of the last segment in the path to the `to`
* vector specified relatively to it, by adding one or more segments to the
* path.
- *
+ *
* @param to - the vector where the arc should end
* @param clockwise - specifies whether the arc should be
* drawn in clockwise direction
*/
- arcBy(to: Point, clockwise?: boolean): void
+ arcBy(to: Point, clockwise?: boolean): void;
- /**
+ /**
* Adds a curve from the last segment in the path through the specified
* `through` vector, to the specified `to` vector, all specified relatively
* to it by these given vectors, by adding one segment to the path.
- *
+ *
* @param through - the vector through which the curve should pass
* @param to - the destination vector of the newly added curve
* @param time - the curve-time parameter at which the
* `through` point is to be located
*/
- curveBy(through: Point, to: Point, time?: number): void
+ curveBy(through: Point, to: Point, time?: number): void;
- /**
+ /**
* Adds a cubic bezier curve to the path, from the last segment to the
* to the specified `to` vector, with the curve itself defined by two
* specified handles.
- *
+ *
* @param handle1 - the location of the first handle of the newly
* added curve
* @param handle2 - the location of the second handle of the newly
* added curve
* @param to - the destination point of the newly added curve
*/
- cubicCurveBy(handle1: Point, handle2: Point, to: Point): void
+ cubicCurveBy(handle1: Point, handle2: Point, to: Point): void;
- /**
+ /**
* Adds a quadratic bezier curve to the path, from the last segment to the
* specified destination point, with the curve itself defined by the
* specified handle.
- *
+ *
* Note that Paper.js only stores cubic curves, so the handle is actually
* converted.
- *
+ *
* @param handle - the handle of the newly added quadratic curve out
* of which the values for {@link Segment#handleOut} of the resulting
* cubic curve's first segment and {@link Segment#handleIn} of its
* second segment are calculated
* @param to - the destination point of the newly added curve
*/
- quadraticCurveBy(handle: Point, to: Point): void
-
+ quadraticCurveBy(handle: Point, to: Point): void;
}
- /**
+ /**
* The Point object represents a point in the two dimensional space
* of the Paper.js project. It is also used to represent two dimensional
* vector objects.
*/
- class Point {
- /**
+ class Point {
+ /**
* The x coordinate of the point
*/
- x: number
+ x: number;
- /**
+ /**
* The y coordinate of the point
*/
- y: number
+ y: number;
- /**
+ /**
* The length of the vector that is represented by this point's coordinates.
* Each point can be interpreted as a vector that points from the origin (`x
* = 0`, `y = 0`) to the point's location. Setting the length changes the
* location but keeps the vector's angle.
*/
- length: number
+ length: number;
- /**
+ /**
* The vector's angle in degrees, measured from the x-axis to the vector.
*/
- angle: number
+ angle: number;
- /**
+ /**
* The vector's angle in radians, measured from the x-axis to the vector.
*/
- angleInRadians: number
+ angleInRadians: number;
- /**
+ /**
* The quadrant of the {@link #angle} of the point.
- *
+ *
* Angles between 0 and 90 degrees are in quadrant `1`. Angles between 90
* and 180 degrees are in quadrant `2`, angles between 180 and 270 degrees
* are in quadrant `3` and angles between 270 and 360 degrees are in
* quadrant `4`.
*/
- readonly quadrant: number
+ readonly quadrant: number;
- /**
+ /**
* This property is only valid if the point is an anchor or handle point
* of a {@link Segment} or a {@link Curve}, or the position of an
* {@link Item}, as returned by {@link Item#position},
* {@link Segment#point}, {@link Segment#handleIn},
* {@link Segment#handleOut}, {@link Curve#point1}, {@link Curve#point2},
* {@link Curve#handle1}, {@link Curve#handle2}.
- *
+ *
* In those cases, it returns {@true if it the point is selected}.
- *
+ *
* Paper.js renders selected points on top of your project. This is very
* useful when debugging.
*/
- selected: boolean
+ selected: boolean;
-
- /**
+ /**
* Creates a Point object with the given x and y coordinates.
- *
+ *
* @param x - the x coordinate
* @param y - the y coordinate
*/
- constructor(x: number, y: number)
+ constructor(x: number, y: number);
- /**
+ /**
* Creates a Point object using the numbers in the given array as
* coordinates.
*/
- constructor(array: any[])
+ constructor(array: any[]);
- /**
+ /**
* Creates a Point object using the width and height values of the given
* Size object.
*/
- constructor(size: Size)
+ constructor(size: Size);
- /**
+ /**
* Creates a Point object using the coordinates of the given Point object.
*/
- constructor(point: Point)
+ constructor(point: Point);
- /**
+ /**
* Creates a Point object using the properties in the given object.
- *
+ *
* @param object - the object describing the point's properties
*/
- constructor(object: object)
+ constructor(object: object);
- /**
+ /**
* Sets the point to the passed values. Note that any sequence of parameters
* that is supported by the various {@link Point} constructors also work
* for calls of `set()`.
*/
- set(...values: any[]): Point
+ set(...values: any[]): Point;
- /**
+ /**
* Checks whether the coordinates of the point are equal to that of the
* supplied point.
- *
+ *
* @return true if the points are equal
*/
- equals(point: Point): boolean
+ equals(point: Point): boolean;
- /**
+ /**
* Returns a copy of the point.
- *
+ *
* @return the cloned point
*/
- clone(): Point
+ clone(): Point;
- /**
+ /**
* @return a string representation of the point
*/
- toString(): string
+ toString(): string;
- /**
+ /**
* Returns the smaller angle between two vectors. The angle is unsigned, no
* information about rotational direction is given.
- *
+ *
* @return the angle in degrees
*/
- getAngle(point: Point): number
+ getAngle(point: Point): number;
- /**
+ /**
* Returns the smaller angle between two vectors in radians. The angle is
* unsigned, no information about rotational direction is given.
- *
+ *
* @return the angle in radians
*/
- getAngleInRadians(point: Point): number
+ getAngleInRadians(point: Point): number;
- /**
+ /**
* Returns the angle between two vectors. The angle is directional and
* signed, giving information about the rotational direction.
- *
+ *
* Read more about angle units and orientation in the description of the
* {@link #angle} property.
- *
+ *
* @return the angle between the two vectors
*/
- getDirectedAngle(point: Point): number
+ getDirectedAngle(point: Point): number;
- /**
+ /**
* Returns the distance between the point and another point.
- *
+ *
* @param squared - Controls whether the distance should
* remain squared, or its square root should be calculated
*/
- getDistance(point: Point, squared?: boolean): number
+ getDistance(point: Point, squared?: boolean): number;
- /**
+ /**
* Normalize modifies the {@link #length} of the vector to `1` without
* changing its angle and returns it as a new point. The optional `length`
* parameter defines the length to normalize to. The object itself is not
* modified!
- *
+ *
* @param length - The length of the normalized vector
- *
+ *
* @return the normalized vector of the vector that is represented
* by this point's coordinates
*/
- normalize(length?: number): Point
+ normalize(length?: number): Point;
- /**
+ /**
* Rotates the point by the given angle around an optional center point.
* The object itself is not modified.
- *
+ *
* Read more about angle units and orientation in the description of the
* {@link #angle} property.
- *
+ *
* @param angle - the rotation angle
* @param center - the center point of the rotation
- *
+ *
* @return the rotated point
*/
- rotate(angle: number, center: Point): Point
+ rotate(angle: number, center: Point): Point;
- /**
+ /**
* Transforms the point by the matrix as a new point. The object itself is
* not modified!
- *
+ *
* @return the transformed point
*/
- transform(matrix: Matrix): Point
+ transform(matrix: Matrix): Point;
- /**
+ /**
* Returns the addition of the supplied value to both coordinates of
* the point as a new point.
* The object itself is not modified!
- *
+ *
* @param number - the number to add
- *
+ *
* @return the addition of the point and the value as a new point
*/
- add(number: number): Point
+ add(number: number): Point;
- /**
+ /**
* Returns the addition of the supplied point to the point as a new
* point.
* The object itself is not modified!
- *
+ *
* @param point - the point to add
- *
+ *
* @return the addition of the two points as a new point
*/
- add(point: Point): Point
+ add(point: Point): Point;
- /**
+ /**
* Returns the subtraction of the supplied value to both coordinates of
* the point as a new point.
* The object itself is not modified!
- *
+ *
* @param number - the number to subtract
- *
+ *
* @return the subtraction of the point and the value as a new point
*/
- subtract(number: number): Point
+ subtract(number: number): Point;
- /**
+ /**
* Returns the subtraction of the supplied point to the point as a new
* point.
* The object itself is not modified!
- *
+ *
* @param point - the point to subtract
- *
+ *
* @return the subtraction of the two points as a new point
*/
- subtract(point: Point): Point
+ subtract(point: Point): Point;
- /**
+ /**
* Returns the multiplication of the supplied value to both coordinates of
* the point as a new point.
* The object itself is not modified!
- *
+ *
* @param number - the number to multiply by
- *
+ *
* @return the multiplication of the point and the value as a new
* point
*/
- multiply(number: number): Point
+ multiply(number: number): Point;
- /**
+ /**
* Returns the multiplication of the supplied point to the point as a new
* point.
* The object itself is not modified!
- *
+ *
* @param point - the point to multiply by
- *
+ *
* @return the multiplication of the two points as a new point
*/
- multiply(point: Point): Point
+ multiply(point: Point): Point;
- /**
+ /**
* Returns the division of the supplied value to both coordinates of
* the point as a new point.
* The object itself is not modified!
- *
+ *
* @param number - the number to divide by
- *
+ *
* @return the division of the point and the value as a new point
*/
- divide(number: number): Point
+ divide(number: number): Point;
- /**
+ /**
* Returns the division of the supplied point to the point as a new
* point.
* The object itself is not modified!
- *
+ *
* @param point - the point to divide by
- *
+ *
* @return the division of the two points as a new point
*/
- divide(point: Point): Point
+ divide(point: Point): Point;
- /**
+ /**
* The modulo operator returns the integer remainders of dividing the point
* by the supplied value as a new point.
- *
+ *
* @return the integer remainders of dividing the point by the value
* as a new point
*/
- modulo(value: number): Point
+ modulo(value: number): Point;
- /**
+ /**
* The modulo operator returns the integer remainders of dividing the point
* by the supplied value as a new point.
- *
+ *
* @return the integer remainders of dividing the points by each
* other as a new point
*/
- modulo(point: Point): Point
+ modulo(point: Point): Point;
- /**
+ /**
* Checks whether the point is inside the boundaries of the rectangle.
- *
+ *
* @param rect - the rectangle to check against
- *
+ *
* @return true if the point is inside the rectangle
*/
- isInside(rect: Rectangle): boolean
+ isInside(rect: Rectangle): boolean;
- /**
+ /**
* Checks if the point is within a given distance of another point.
- *
+ *
* @param point - the point to check against
* @param tolerance - the maximum distance allowed
- *
+ *
* @return true if it is within the given distance
*/
- isClose(point: Point, tolerance: number): boolean
+ isClose(point: Point, tolerance: number): boolean;
- /**
+ /**
* Checks if the vector represented by this point is collinear (parallel) to
* another vector.
- *
+ *
* @param point - the vector to check against
- *
+ *
* @return true it is collinear
*/
- isCollinear(point: Point): boolean
+ isCollinear(point: Point): boolean;
- /**
+ /**
* Checks if the vector represented by this point is orthogonal
* (perpendicular) to another vector.
- *
+ *
* @param point - the vector to check against
- *
+ *
* @return true it is orthogonal
*/
- isOrthogonal(point: Point): boolean
+ isOrthogonal(point: Point): boolean;
- /**
+ /**
* Checks if this point has both the x and y coordinate set to 0.
- *
+ *
* @return true if both x and y are 0
*/
- isZero(): boolean
+ isZero(): boolean;
- /**
+ /**
* Checks if this point has an undefined value for at least one of its
* coordinates.
- *
+ *
* @return true if either x or y are not a number
*/
- isNaN(): boolean
+ isNaN(): boolean;
- /**
+ /**
* Checks if the vector is within the specified quadrant. Note that if the
* vector lies on the boundary between two quadrants, `true` will be
* returned for both quadrants.
- *
+ *
* @see #quadrant
- *
+ *
* @param quadrant - the quadrant to check against
- *
+ *
* @return true if either x or y are not a number
*/
- isInQuadrant(quadrant: number): boolean
+ isInQuadrant(quadrant: number): boolean;
- /**
+ /**
* Returns the dot product of the point and another point.
- *
+ *
* @return the dot product of the two points
*/
- dot(point: Point): number
+ dot(point: Point): number;
- /**
+ /**
* Returns the cross product of the point and another point.
- *
+ *
* @return the cross product of the two points
*/
- cross(point: Point): number
+ cross(point: Point): number;
- /**
+ /**
* Returns the projection of the point onto another point.
* Both points are interpreted as vectors.
- *
+ *
* @return the projection of the point onto another point
*/
- project(point: Point): Point
+ project(point: Point): Point;
- /**
+ /**
* Returns a new point with rounded {@link #x} and {@link #y} values. The
* object itself is not modified!
*/
- round(): Point
+ round(): Point;
- /**
+ /**
* Returns a new point with the nearest greater non-fractional values to the
* specified {@link #x} and {@link #y} values. The object itself is not
* modified!
*/
- ceil(): Point
+ ceil(): Point;
- /**
+ /**
* Returns a new point with the nearest smaller non-fractional values to the
* specified {@link #x} and {@link #y} values. The object itself is not
* modified!
*/
- floor(): Point
+ floor(): Point;
- /**
+ /**
* Returns a new point with the absolute values of the specified {@link #x}
* and {@link #y} values. The object itself is not modified!
*/
- abs(): Point
+ abs(): Point;
- /**
+ /**
* Returns a new point object with the smallest {@link #x} and
* {@link #y} of the supplied points.
- *
+ *
* @return the newly created point object
*/
- static min(point1: Point, point2: Point): Point
+ static min(point1: Point, point2: Point): Point;
- /**
+ /**
* Returns a new point object with the largest {@link #x} and
* {@link #y} of the supplied points.
- *
+ *
* @return the newly created point object
*/
- static max(point1: Point, point2: Point): Point
+ static max(point1: Point, point2: Point): Point;
- /**
+ /**
* Returns a point object with random {@link #x} and {@link #y} values
* between `0` and `1`.
- *
+ *
* @return the newly created point object
*/
- static random(): Point
-
+ static random(): Point;
}
- /**
+ /**
* A PointText item represents a piece of typography in your Paper.js
* project which starts from a certain point and extends by the amount of
* characters contained in it.
*/
class PointText extends TextItem {
- /**
+ /**
* The PointText's anchor point
*/
- point: Point
-
+ point: Point;
- /**
+ /**
* Creates a point text item
- *
+ *
* @param point - the position where the text will start
*/
- constructor(point: Point)
+ constructor(point: Point);
- /**
+ /**
* Creates a point text item from the properties described by an object
* literal.
- *
+ *
* @param object - an object containing properties describing the
* path's attributes
*/
- constructor(object: object)
-
+ constructor(object: object);
}
- /**
+ /**
* A Project object in Paper.js is what usually is referred to as the
* document: The top level object that holds all the items contained in the
* scene graph. As the term document is already taken in the browser context,
* it is called Project.
- *
+ *
* Projects allow the manipulation of the styles that are applied to all newly
* created items, give access to the selected items, and will in future versions
* offer ways to query for items in the scene graph defining specific
* requirements, and means to persist and load from different formats, such as
* SVG and PDF.
- *
+ *
* The currently active project can be accessed through the
* {@link PaperScope#project} variable.
- *
+ *
* An array of all open projects is accessible through the
* {@link PaperScope#projects} variable.
*/
- class Project {
- /**
+ class Project {
+ /**
* The reference to the project's view.
*/
- readonly view: View
+ readonly view: View;
- /**
+ /**
* The currently active path style. All selected items and newly
* created items will be styled with this style.
*/
- currentStyle: Style
+ currentStyle: Style;
- /**
+ /**
* The index of the project in the {@link PaperScope#projects} list.
*/
- readonly index: number
+ readonly index: number;
- /**
+ /**
* The layers contained within the project.
*/
- readonly layers: Layer[]
+ readonly layers: Layer[];
- /**
+ /**
* The layer which is currently active. New items will be created on this
* layer by default.
*/
- readonly activeLayer: Layer
+ readonly activeLayer: Layer;
- /**
+ /**
* The symbol definitions shared by all symbol items contained place ind
* project.
*/
- readonly symbolDefinitions: SymbolDefinition[]
+ readonly symbolDefinitions: SymbolDefinition[];
- /**
+ /**
* The selected items contained within the project.
*/
- readonly selectedItems: Item[]
-
+ readonly selectedItems: Item[];
- /**
+ /**
* Creates a Paper.js project containing one empty {@link Layer}, referenced
* by {@link Project#activeLayer}.
- *
+ *
* Note that when working with PaperScript, a project is automatically
* created for us and the {@link PaperScope#project} variable points to it.
- *
+ *
* @param element - the HTML canvas element
* that should be used as the element for the view, or an ID string by which
* to find the element, or the size of the canvas to be created for usage in
* a web worker.
*/
- constructor(element: HTMLCanvasElement | string | Size)
+ constructor(element: HTMLCanvasElement | string | Size);
- /**
+ /**
* Activates this project, so all newly created items will be placed
* in it.
*/
- activate(): void
+ activate(): void;
- /**
+ /**
* Clears the project by removing all {@link Project#layers}.
*/
- clear(): void
+ clear(): void;
- /**
+ /**
* Checks whether the project has any content or not.
*/
- isEmpty(): boolean
+ isEmpty(): boolean;
- /**
+ /**
* Removes this project from the {@link PaperScope#projects} list, and also
* removes its view, if one was defined.
*/
- remove(): void
+ remove(): void;
- /**
+ /**
* Selects all items in the project.
*/
- selectAll(): void
+ selectAll(): void;
- /**
+ /**
* Deselects all selected items in the project.
*/
- deselectAll(): void
+ deselectAll(): void;
- /**
+ /**
* Adds the specified layer at the end of the this project's {@link #layers}
* list.
- *
+ *
* @param layer - the layer to be added to the project
- *
+ *
* @return the added layer, or `null` if adding was not possible
*/
- addLayer(layer: Layer): Layer
+ addLayer(layer: Layer): Layer;
- /**
+ /**
* Inserts the specified layer at the specified index in this project's
* {@link #layers} list.
- *
+ *
* @param index - the index at which to insert the layer
* @param layer - the layer to be inserted in the project
- *
+ *
* @return the added layer, or `null` if adding was not possible
*/
- insertLayer(index: number, layer: Layer): Layer
+ insertLayer(index: number, layer: Layer): Layer;
- /**
+ /**
* Performs a hit-test on the items contained within the project at the
* location of the specified point.
- *
+ *
* The options object allows you to control the specifics of the hit-test
* and may contain a combination of the following values:
- *
+ *
* @option [options.tolerance={@link PaperScope#settings}.hitTolerance]
* {Number} the tolerance of the hit-test
* @option options.class {Function} only hit-test against a specific item
@@ -5087,49 +5059,49 @@ declare namespace paper {
* @option options.guides {Boolean} hit-test items that have {@link
* Item#guide} set to `true`
* @option options.selected {Boolean} only hit selected items
- *
+ *
* @param point - the point where the hit-test should be performed
- *
+ *
* @return a hit result object that contains more information
* about what exactly was hit or `null` if nothing was hit
*/
- hitTest(point: Point, options?: object): HitResult
+ hitTest(point: Point, options?: object): HitResult;
- /**
+ /**
* Performs a hit-test on the item and its children (if it is a {@link
* Group} or {@link Layer}) at the location of the specified point,
* returning all found hits.
- *
+ *
* The options object allows you to control the specifics of the hit-
* test. See {@link #hitTest} for a list of all options.
- *
+ *
* @see #hitTest(point[, options]);
- *
+ *
* @param point - the point where the hit-test should be performed
- *
+ *
* @return hit result objects for all hits, describing what
* exactly was hit or `null` if nothing was hit
*/
- hitTestAll(point: Point, options?: object): HitResult[]
+ hitTestAll(point: Point, options?: object): HitResult[];
- /**
+ /**
* Fetch items contained within the project whose properties match the
* criteria in the specified object.
- *
+ *
* Extended matching of properties is possible by providing a comparator
* function or regular expression. Matching points, colors only work as a
* comparison of the full object, not partial matching (e.g. only providing
* the x- coordinate to match all points with that x-value). Partial
* matching does work for {@link Item#data}.
- *
+ *
* Matching items against a rectangular area is also possible, by setting
* either `options.inside` or `options.overlapping` to a rectangle
* describing the area in which the items either have to be fully or partly
* contained.
- *
+ *
* @see Item#matches(options)
* @see Item#getItems(options)
- *
+ *
* @option [options.recursive=true] {Boolean} whether to loop recursively
* through all children, or stop at the current level
* @option options.match {Function} a match function to be called for each
@@ -5142,14 +5114,14 @@ declare namespace paper {
* to be fully contained
* @option options.overlapping {Rectangle} the rectangle with which the
* items need to at least partly overlap
- *
+ *
* @param options - the criteria to match against
- *
+ *
* @return the list of matching items contained in the project
*/
- getItems(options: object | Function): Item[]
+ getItems(options: object | Function): Item[];
- /**
+ /**
* Fetch the first item contained within the project whose properties
* match the criteria in the specified object.
* Extended matching is possible by providing a compare function or
@@ -5157,45 +5129,45 @@ declare namespace paper {
* of the full object, not partial matching (e.g. only providing the x-
* coordinate to match all points with that x-value). Partial matching
* does work for {@link Item#data}.
- *
+ *
* See {@link #getItems} for a selection of illustrated examples.
- *
+ *
* @param options - the criteria to match against
- *
+ *
* @return the first item in the project matching the given criteria
*/
- getItem(options: object | Function): Item
+ getItem(options: object | Function): Item;
- /**
+ /**
* Exports (serializes) the project with all its layers and child items to a
* JSON data object or string.
- *
+ *
* @option [options.asString=true] {Boolean} whether the JSON is returned as
* a `Object` or a `String`
* @option [options.precision=5] {Number} the amount of fractional digits in
* numbers used in JSON data
- *
+ *
* @param options - the serialization options
- *
+ *
* @return the exported JSON data
*/
- exportJSON(options?: object): string
+ exportJSON(options?: object): string;
- /**
+ /**
* Imports (deserializes) the stored JSON data into the project.
* Note that the project is not cleared first. You can call
* {@link Project#clear} to do so.
- *
+ *
* @param json - the JSON data to import from
- *
+ *
* @return the imported item
*/
- importJSON(json: string): Item
+ importJSON(json: string): Item;
- /**
+ /**
* Exports the project with all its layers and child items as an SVG DOM,
* all contained in one top level SVG group node.
- *
+ *
* @option [options.bounds='view'] {String|Rectangle} the bounds of the area
* to export, either as a string ({@values 'view', content'}), or a
* {@link Rectangle} object: `'view'` uses the view bounds,
@@ -5214,20 +5186,20 @@ declare namespace paper {
* @option [options.embedImages=true] {Boolean} whether raster images should
* be embedded as base64 data inlined in the xlink:href attribute, or
* kept as a link to their external URL.
- *
+ *
* @param options - the export options
- *
+ *
* @return the project converted to an SVG node or a
* `String` depending on `option.asString` value
*/
- exportSVG(options?: object): SVGElement | string
+ exportSVG(options?: object): SVGElement | string;
- /**
+ /**
* Converts the provided SVG content into Paper.js items and adds them to
* the active layer of this project.
* Note that the project is not cleared first. You can call
* {@link Project#clear} to do so.
- *
+ *
* @option [options.expandShapes=false] {Boolean} whether imported shape
* items should be expanded to path items
* @option options.onLoad {Function} the callback function to call once the
@@ -5242,89 +5214,88 @@ declare namespace paper {
* @option [options.applyMatrix={@link PaperScope#settings}.applyMatrix]
* {Boolean} whether the imported items should have their transformation
* matrices applied to their contents or not
- *
+ *
* @param svg - the SVG content to import, either as a SVG
* DOM node, a string containing SVG content, or a string describing the
* URL of the SVG file to fetch.
* @param options - the import options
- *
+ *
* @return the newly created Paper.js item containing the converted
* SVG content
*/
- importSVG(svg: SVGElement | string, options?: object): Item
+ importSVG(svg: SVGElement | string, options?: object): Item;
- /**
+ /**
* Imports the provided external SVG file, converts it into Paper.js items
* and adds them to the active layer of this project.
* Note that the project is not cleared first. You can call
* {@link Project#clear} to do so.
- *
+ *
* @param svg - the URL of the SVG file to fetch.
* @param onLoad - the callback function to call once the SVG
* content is loaded from the given URL receiving two arguments: the
* converted `item` and the original `svg` data as a string. Only
* required when loading from external files.
- *
+ *
* @return the newly created Paper.js item containing the converted
* SVG content
*/
- importSVG(svg: SVGElement | string, onLoad: Function): Item
-
+ importSVG(svg: SVGElement | string, onLoad: Function): Item;
}
- /**
+ /**
* The Raster item represents an image in a Paper.js project.
*/
class Raster extends Item {
- /**
+ /**
* The size of the raster in pixels.
*/
- size: Size
+ size: Size;
- /**
+ /**
* The width of the raster in pixels.
*/
- width: number
+ width: number;
- /**
+ /**
* The height of the raster in pixels.
*/
- height: number
+ height: number;
- /**
+ /**
* The loading state of the raster image.
*/
- readonly loaded: boolean
+ readonly loaded: boolean;
- /**
+ /**
* The resolution of the raster at its current size, in PPI (pixels per
* inch).
*/
- readonly resolution: Size
+ readonly resolution: Size;
- /**
+ /**
* The HTMLImageElement or Canvas element of the raster, if one is
* associated.
* Note that for consistency, a {@link #onLoad} event will be triggered on
* the raster even if the image has already finished loading before, or if
* we are setting the raster to a canvas.
*/
- image: HTMLImageElement | HTMLCanvasElement
+ image: HTMLImageElement | HTMLCanvasElement;
- /**
+ /**
* The Canvas object of the raster. If the raster was created from an image,
* accessing its canvas causes the raster to try and create one and draw the
* image into it. Depending on security policies, this might fail, in which
* case `null` is returned instead.
*/
- canvas: HTMLCanvasElement
+ canvas: HTMLCanvasElement;
- /**
+ /**
* The Canvas 2D drawing context of the raster.
*/
- context: CanvasRenderingContext2D
+ context: CanvasRenderingContext2D;
- /**
+ /**
* The source of the raster, which can be set using a DOM Image, a Canvas,
* a data url, a string describing the URL to load the image from, or the
* ID of a DOM element to get the image from (either a DOM Image or a
@@ -5333,557 +5304,552 @@ declare namespace paper {
* Note that for consistency, a {@link #onLoad} event will be triggered on
* the raster even if the image has already finished loading before.
*/
- source: HTMLImageElement | HTMLCanvasElement | string
+ source: HTMLImageElement | HTMLCanvasElement | string;
- /**
+ /**
* The crossOrigin value to be used when loading the image resource, in
* order to support CORS. Note that this needs to be set before setting the
* {@link #source} property in order to always work (e.g. when the image is
* cached in the browser).
*/
- crossOrigin: string
+ crossOrigin: string;
- /**
+ /**
* Determines if the raster is drawn with pixel smoothing when scaled up or
* down, and if so, at which quality its pixels are to be smoothed. The
* settings of this property control both the `imageSmoothingEnabled` and
* `imageSmoothingQuality` properties of the `CanvasRenderingContext2D`
* interface.
- *
+ *
* By default, smoothing is enabled at `'low'` quality. It can be set to of
* `'off'` to scale the raster's pixels by repeating the nearest neighboring
* pixels, or to `'low'`, `'medium'` or `'high'` to control the various
* degrees of available image smoothing quality.
- *
+ *
* For backward compatibility, it can can also be set to `false` (= `'off'`)
* or `true` (= `'low'`).
*/
- smoothing: string
+ smoothing: string;
- /**
+ /**
* The event handler function to be called when the underlying image has
* finished loading and is ready to be used. This is also triggered when
* the image is already loaded, or when a canvas is used instead of an
* image.
*/
- onLoad: Function | null
+ onLoad: Function | null;
- /**
+ /**
* The event handler function to be called when there is an error loading
* the underlying image.
*/
- onError: Function | null
+ onError: Function | null;
-
- /**
+ /**
* Creates a new raster item from the passed argument, and places it in the
* active layer. `source` can either be a DOM Image, a Canvas, or a string
* describing the URL to load the image from, or the ID of a DOM element to
* get the image from (either a DOM Image or a Canvas).
- *
+ *
* @param source - the source of
* the raster
* @param position - the center position at which the raster item is
* placed
*/
- constructor(source?: HTMLImageElement | HTMLCanvasElement | string, position?: Point)
+ constructor(
+ source?: HTMLImageElement | HTMLCanvasElement | string,
+ position?: Point
+ );
- /**
+ /**
* Creates a new empty raster of the given size, and places it in the
* active layer.
- *
+ *
* @param size - the size of the raster
* @param position - the center position at which the raster item is
* placed
*/
- constructor(size: Size, position?: Point)
+ constructor(size: Size, position?: Point);
- /**
+ /**
* Creates a new raster from an object description, and places it in the
* active layer.
- *
+ *
* @param object - an object containing properties to be set on the
* raster
*/
- constructor(object: object)
+ constructor(object: object);
- /**
+ /**
* Extracts a part of the Raster's content as a sub image, and returns it as
* a Canvas object.
- *
+ *
* @param rect - the boundaries of the sub image in pixel
* coordinates
- *
+ *
* @return the sub image as a Canvas object
*/
- getSubCanvas(rect: Rectangle): HTMLCanvasElement
+ getSubCanvas(rect: Rectangle): HTMLCanvasElement;
- /**
+ /**
* Extracts a part of the raster item's content as a new raster item, placed
* in exactly the same place as the original content.
- *
+ *
* @param rect - the boundaries of the sub raster in pixel
* coordinates
- *
+ *
* @return the sub raster as a newly created raster item
*/
- getSubRaster(rect: Rectangle): Raster
+ getSubRaster(rect: Rectangle): Raster;
- /**
+ /**
* Returns a Base 64 encoded `data:` URL representation of the raster.
*/
- toDataURL(): string
+ toDataURL(): string;
- /**
+ /**
* Draws an image on the raster.
- *
+ *
* @param point - the offset of the image as a point in pixel
* coordinates
*/
- drawImage(image: CanvasImageSource, point: Point): void
+ drawImage(image: CanvasImageSource, point: Point): void;
- /**
+ /**
* Calculates the average color of the image within the given path,
* rectangle or point. This can be used for creating raster image
* effects.
- *
+ *
* @return the average color contained in the area covered by the
* specified path, rectangle or point
*/
- getAverageColor(object: Path | Rectangle | Point): Color
+ getAverageColor(object: Path | Rectangle | Point): Color;
- /**
+ /**
* Gets the color of a pixel in the raster.
- *
+ *
* @param x - the x offset of the pixel in pixel coordinates
* @param y - the y offset of the pixel in pixel coordinates
- *
+ *
* @return the color of the pixel
*/
- getPixel(x: number, y: number): Color
+ getPixel(x: number, y: number): Color;
- /**
+ /**
* Gets the color of a pixel in the raster.
- *
+ *
* @param point - the offset of the pixel as a point in pixel
* coordinates
- *
+ *
* @return the color of the pixel
*/
- getPixel(point: Point): Color
+ getPixel(point: Point): Color;
- /**
+ /**
* Sets the color of the specified pixel to the specified color.
- *
+ *
* @param x - the x offset of the pixel in pixel coordinates
* @param y - the y offset of the pixel in pixel coordinates
* @param color - the color that the pixel will be set to
*/
- setPixel(x: number, y: number, color: Color): void
+ setPixel(x: number, y: number, color: Color): void;
- /**
+ /**
* Sets the color of the specified pixel to the specified color.
- *
+ *
* @param point - the offset of the pixel as a point in pixel
* coordinates
* @param color - the color that the pixel will be set to
*/
- setPixel(point: Point, color: Color): void
+ setPixel(point: Point, color: Color): void;
- /**
+ /**
* Clears the image, if it is backed by a canvas.
*/
- clear(): void
-
-
- createImageData(size: Size): ImageData
+ clear(): void;
-
- getImageData(rect: Rectangle): ImageData
+ createImageData(size: Size): ImageData;
-
- setImageData(data: ImageData, point: Point): void
+ getImageData(rect: Rectangle): ImageData;
+ setImageData(data: ImageData, point: Point): void;
}
- /**
+ /**
* 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.
*/
- class Rectangle {
- /**
+ class Rectangle {
+ /**
* The x position of the rectangle.
*/
- x: number
+ x: number;
- /**
+ /**
* The y position of the rectangle.
*/
- y: number
+ y: number;
- /**
+ /**
* The width of the rectangle.
*/
- width: number
+ width: number;
- /**
+ /**
* The height of the rectangle.
*/
- height: number
+ height: number;
- /**
+ /**
* The top-left point of the rectangle
*/
- point: Point
+ point: Point;
- /**
+ /**
* The size of the rectangle
*/
- size: Size
+ size: Size;
- /**
+ /**
* 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.
*/
- left: number
+ left: number;
- /**
+ /**
* The top coordinate of the rectangle. Note that this doesn't move the
* whole rectangle: the bottom won't move.
*/
- top: number
+ top: number;
- /**
+ /**
* 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.
*/
- right: number
+ right: number;
- /**
+ /**
* The bottom coordinate of the rectangle. Note that this doesn't move the
* whole rectangle: the top won't move.
*/
- bottom: number
+ bottom: number;
- /**
+ /**
* The center point of the rectangle.
*/
- center: Point
+ center: Point;
- /**
+ /**
* The top-left point of the rectangle.
*/
- topLeft: Point
+ topLeft: Point;
- /**
+ /**
* The top-right point of the rectangle.
*/
- topRight: Point
+ topRight: Point;
- /**
+ /**
* The bottom-left point of the rectangle.
*/
- bottomLeft: Point
+ bottomLeft: Point;
- /**
+ /**
* The bottom-right point of the rectangle.
*/
- bottomRight: Point
+ bottomRight: Point;
- /**
+ /**
* The left-center point of the rectangle.
*/
- leftCenter: Point
+ leftCenter: Point;
- /**
+ /**
* The top-center point of the rectangle.
*/
- topCenter: Point
+ topCenter: Point;
- /**
+ /**
* The right-center point of the rectangle.
*/
- rightCenter: Point
+ rightCenter: Point;
- /**
+ /**
* The bottom-center point of the rectangle.
*/
- bottomCenter: Point
+ bottomCenter: Point;
- /**
+ /**
* The area of the rectangle.
*/
- readonly area: number
+ readonly area: number;
- /**
+ /**
* Specifies whether an item's bounds are to appear as selected.
- *
+ *
* Paper.js draws the bounds of items with selected bounds on top of
* your project. This is very useful when debugging.
*/
- selected: boolean
-
+ selected: boolean;
- /**
+ /**
* Creates a Rectangle object.
- *
+ *
* @param point - the top-left point of the rectangle
* @param size - the size of the rectangle
*/
- constructor(point: Point, size: Size)
+ constructor(point: Point, size: Size);
- /**
+ /**
* Creates a rectangle object.
- *
+ *
* @param x - the left coordinate
* @param y - the top coordinate
*/
- constructor(x: number, y: number, width: number, height: number)
+ constructor(x: number, y: number, width: number, height: number);
- /**
+ /**
* 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.
- *
+ *
* @param from - the first point defining the rectangle
* @param to - the second point defining the rectangle
*/
- constructor(from: Point, to: Point)
+ constructor(from: Point, to: Point);
- /**
+ /**
* Creates a new rectangle object from the passed rectangle object.
*/
- constructor(rectangle: Rectangle)
+ constructor(rectangle: Rectangle);
- /**
+ /**
* Creates a Rectangle object.
- *
+ *
* @param object - an object containing properties to be set on the
* rectangle
*/
- constructor(object: object)
+ constructor(object: object);
- /**
+ /**
* Sets the rectangle to the passed values. Note that any sequence of
* parameters that is supported by the various {@link Rectangle}
* constructors also work for calls of `set()`.
*/
- set(...values: any[]): Rectangle
+ set(...values: any[]): Rectangle;
- /**
+ /**
* Returns a copy of the rectangle.
*/
- clone(): Rectangle
+ clone(): Rectangle;
- /**
+ /**
* Checks whether the coordinates and size of the rectangle are equal to
* that of the supplied rectangle.
- *
+ *
* @return true if the rectangles are equal
*/
- equals(rect: Rectangle): boolean
+ equals(rect: Rectangle): boolean;
- /**
+ /**
* @return a string representation of this rectangle
*/
- toString(): string
+ toString(): string;
- /**
+ /**
* @return true if the rectangle is empty
*/
- isEmpty(): boolean
+ isEmpty(): boolean;
- /**
+ /**
* Tests if the specified point is inside the boundary of the rectangle.
- *
+ *
* @param point - the specified point
- *
+ *
* @return true if the point is inside the rectangle's boundary
*/
- contains(point: Point): boolean
+ contains(point: Point): boolean;
- /**
+ /**
* Tests if the interior of the rectangle entirely contains the specified
* rectangle.
- *
+ *
* @param rect - the specified rectangle
- *
+ *
* @return true if the rectangle entirely contains the specified
* rectangle
*/
- contains(rect: Rectangle): boolean
+ contains(rect: Rectangle): boolean;
- /**
+ /**
* Tests if the interior of this rectangle intersects the interior of
* another rectangle. Rectangles just touching each other are considered as
* non-intersecting, except if a `epsilon` value is specified by which this
* rectangle's dimensions are increased before comparing.
- *
+ *
* @param rect - the specified rectangle
* @param epsilon - the epsilon against which to compare the
* rectangle's dimensions
- *
+ *
* @return true if the rectangle and the specified rectangle
* intersect each other
*/
- intersects(rect: Rectangle, epsilon?: number): boolean
+ intersects(rect: Rectangle, epsilon?: number): boolean;
- /**
+ /**
* Returns a new rectangle representing the intersection of this rectangle
* with the specified rectangle.
- *
+ *
* @param rect - the rectangle to be intersected with this
* rectangle
- *
+ *
* @return the largest rectangle contained in both the specified
* rectangle and in this rectangle
*/
- intersect(rect: Rectangle): Rectangle
+ intersect(rect: Rectangle): Rectangle;
- /**
+ /**
* Returns a new rectangle representing the union of this rectangle with the
* specified rectangle.
- *
+ *
* @param rect - the rectangle to be combined with this rectangle
- *
+ *
* @return the smallest rectangle containing both the specified
* rectangle and this rectangle
*/
- unite(rect: Rectangle): Rectangle
+ unite(rect: Rectangle): Rectangle;
- /**
+ /**
* 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} with the added
* point as an argument does not necessarily return `true`. The {@link
* Rectangle#contains(point)} method does not return `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 `false` for that point.
- *
+ *
* @return the smallest rectangle that contains both the
* original rectangle and the specified point
*/
- include(point: Point): Rectangle
+ include(point: Point): Rectangle;
- /**
+ /**
* Returns a new rectangle expanded by the specified amount in horizontal
* and vertical directions.
- *
+ *
* @param amount - the amount to expand the rectangle in
* both directions
- *
+ *
* @return the expanded rectangle
*/
- expand(amount: number | Size | Point): Rectangle
+ expand(amount: number | Size | Point): Rectangle;
- /**
+ /**
* Returns a new rectangle expanded by the specified amounts in horizontal
* and vertical directions.
- *
+ *
* @param hor - the amount to expand the rectangle in horizontal
* direction
* @param ver - the amount to expand the rectangle in vertical
* direction
- *
+ *
* @return the expanded rectangle
*/
- expand(hor: number, ver: number): Rectangle
+ expand(hor: number, ver: number): Rectangle;
- /**
+ /**
* Returns a new rectangle scaled by the specified amount from its center.
- *
+ *
* @return the scaled rectangle
*/
- scale(amount: number): Rectangle
+ scale(amount: number): Rectangle;
- /**
+ /**
* Returns a new rectangle scaled in horizontal direction by the specified
* `hor` amount and in vertical direction by the specified `ver` amount
* from its center.
- *
+ *
* @return the scaled rectangle
*/
- scale(hor: number, ver: number): Rectangle
-
+ scale(hor: number, ver: number): Rectangle;
}
- /**
+ /**
* The Segment object represents the points of a path through which its
* {@link Curve} objects pass. The segments of a path can be accessed through
* its {@link Path#segments} array.
- *
+ *
* Each segment consists of an anchor point ({@link Segment#point}) and
* optionaly an incoming and an outgoing handle ({@link Segment#handleIn} and
* {@link Segment#handleOut}), describing the tangents of the two {@link Curve}
* objects that are connected by this segment.
*/
- class Segment {
- /**
+ class Segment {
+ /**
* The anchor point of the segment.
*/
- point: Point
+ point: Point;
- /**
+ /**
* The handle point relative to the anchor point of the segment that
* describes the in tangent of the segment.
*/
- handleIn: Point
+ handleIn: Point;
- /**
+ /**
* The handle point relative to the anchor point of the segment that
* describes the out tangent of the segment.
*/
- handleOut: Point
+ handleOut: Point;
- /**
+ /**
* Specifies whether the segment is selected.
*/
- selected: boolean
+ selected: boolean;
- /**
+ /**
* The index of the segment in the {@link Path#segments} array that the
* segment belongs to.
*/
- readonly index: number
+ readonly index: number;
- /**
+ /**
* The path that the segment belongs to.
*/
- readonly path: Path
+ readonly path: Path;
- /**
+ /**
* The curve that the segment belongs to. For the last segment of an open
* path, the previous segment is returned.
*/
- readonly curve: Curve
+ readonly curve: Curve;
- /**
+ /**
* The curve location that describes this segment's position on the path.
*/
- readonly location: CurveLocation
+ readonly location: CurveLocation;
- /**
+ /**
* The next segment in the {@link Path#segments} array that the segment
* belongs to. If the segments belongs to a closed path, the first segment
* is returned for the last segment of the path.
*/
- readonly next: Segment
+ readonly next: Segment;
- /**
+ /**
* The previous segment in the {@link Path#segments} array that the
* segment belongs to. If the segments belongs to a closed path, the last
* segment is returned for the first segment of the path.
*/
- readonly previous: Segment
-
+ readonly previous: Segment;
- /**
+ /**
* Creates a new Segment object.
- *
+ *
* @param point - the anchor point of the segment
* @param handleIn - the handle point relative to the
* anchor point of the segment that describes the in tangent of the
@@ -5892,139 +5858,138 @@ declare namespace paper {
* anchor point of the segment that describes the out tangent of the
* segment
*/
- constructor(point?: Point, handleIn?: Point, handleOut?: Point)
+ constructor(point?: Point, handleIn?: Point, handleOut?: Point);
- /**
+ /**
* Creates a new Segment object.
- *
+ *
* @param object - an object containing properties to be set on the
* segment
*/
- constructor(object: object)
+ constructor(object: object);
- /**
+ /**
* Checks if the segment has any curve handles set.
- *
+ *
* @see Segment#handleIn
* @see Segment#handleOut
* @see Curve#hasHandles()
* @see Path#hasHandles()
- *
+ *
* @return true if the segment has handles set
*/
- hasHandles(): boolean
+ hasHandles(): boolean;
- /**
+ /**
* Checks if the segment connects two curves smoothly, meaning that its two
* handles are collinear and segment does not form a corner.
- *
+ *
* @see Point#isCollinear()
- *
+ *
* @return true if the segment is smooth
*/
- isSmooth(): boolean
+ isSmooth(): boolean;
- /**
+ /**
* Clears the segment's handles by setting their coordinates to zero,
* turning the segment into a corner.
*/
- clearHandles(): void
+ clearHandles(): void;
- /**
+ /**
* Smooths the bezier curves that pass through this segment by taking into
* account the segment's position and distance to the neighboring segments
* and changing the direction and length of the segment's handles
* accordingly without moving the segment itself.
- *
+ *
* Two different smoothing methods are available:
- *
+ *
* - `'catmull-rom'` uses the Catmull-Rom spline to smooth the segment.
- *
+ *
* The optionally passed factor controls the knot parametrization of the
* algorithm:
- *
+ *
* - `0.0`: the standard, uniform Catmull-Rom spline
* - `0.5`: the centripetal Catmull-Rom spline, guaranteeing no
* self-intersections
* - `1.0`: the chordal Catmull-Rom spline
- *
+ *
* - `'geometric'` use a simple heuristic and empiric geometric method to
* smooth the segment's handles. The handles were weighted, meaning that
* big differences in distances between the segments will lead to
* probably undesired results.
- *
+ *
* The optionally passed factor defines the tension parameter (`0...1`),
* controlling the amount of smoothing as a factor by which to scale
* each handle.
- *
+ *
* @see PathItem#smooth([options])
- *
+ *
* @option [options.type='catmull-rom'] {String} the type of smoothing
* method: {@values 'catmull-rom', 'geometric'}
* @option options.factor {Number} the factor parameterizing the smoothing
* method — default: `0.5` for `'catmull-rom'`, `0.4` for `'geometric'`
- *
+ *
* @param options - the smoothing options
*/
- smooth(options?: object): void
+ smooth(options?: object): void;
- /**
+ /**
* Checks if the this is the first segment in the {@link Path#segments}
* array.
- *
+ *
* @return true if this is the first segment
*/
- isFirst(): boolean
+ isFirst(): boolean;
- /**
+ /**
* Checks if the this is the last segment in the {@link Path#segments}
* array.
- *
+ *
* @return true if this is the last segment
*/
- isLast(): boolean
+ isLast(): boolean;
- /**
+ /**
* Reverses the {@link #handleIn} and {@link #handleOut} vectors of this
* segment, modifying the actual segment without creating a copy.
- *
+ *
* @return the reversed segment
*/
- reverse(): Segment
+ reverse(): Segment;
- /**
+ /**
* Returns the reversed the segment, without modifying the segment itself.
- *
+ *
* @return the reversed segment
*/
- reversed(): Segment
+ reversed(): Segment;
- /**
+ /**
* Removes the segment from the path that it belongs to.
- *
+ *
* @return true if the segment was removed
*/
- remove(): boolean
+ remove(): boolean;
-
- clone(): Segment
+ clone(): Segment;
- /**
+ /**
* @return a string representation of the segment
*/
- toString(): string
+ toString(): string;
- /**
+ /**
* Transform the segment by the specified matrix.
- *
+ *
* @param matrix - the matrix to transform the segment by
*/
- transform(matrix: Matrix): void
+ transform(matrix: Matrix): void;
- /**
+ /**
* Interpolates between the two specified segments and sets the point and
* handles of this segment accordingly.
- *
+ *
* @param from - the segment defining the geometry when `factor` is
* `0`
* @param to - the segment defining the geometry when `factor` is
@@ -6032,568 +5997,574 @@ declare namespace paper {
* @param factor - the interpolation coefficient, typically between
* `0` and `1`, but extrapolation is possible too
*/
- interpolate(from: Segment, to: Segment, factor: number): void
-
+ interpolate(from: Segment, to: Segment, factor: number): void;
}
-
class Shape extends Item {
- /**
+ /**
* The type of shape of the item as a string.
*/
- type: string
+ type: string;
- /**
+ /**
* The size of the shape.
*/
- size: Size
+ size: Size;
- /**
+ /**
* The radius of the shape, as a number if it is a circle, or a size object
* for ellipses and rounded rectangles.
*/
- radius: number | Size
+ radius: number | Size;
-
- /**
+ /**
* Creates a new path item with same geometry as this shape item, and
* inherits all settings from it, similar to {@link Item#clone}.
- *
+ *
* @see Path#toShape(insert)
- *
+ *
* @param insert - specifies whether the new path should be
* inserted into the scene graph. When set to `true`, it is inserted
* above the shape item
- *
+ *
* @return the newly created path item with the same geometry as
* this shape item
*/
- toPath(insert?: boolean): Path
-
+ toPath(insert?: boolean): Path;
}
namespace Shape {
-
class Circle extends Shape {
- /**
+ /**
* Creates a circular shape item.
- *
+ *
* @param center - the center point of the circle
* @param radius - the radius of the circle
*/
- constructor(center: Point, radius: number)
+ constructor(center: Point, radius: number);
- /**
+ /**
* Creates a circular shape item from the properties described by an
* object literal.
- *
+ *
* @param object - an object containing properties describing the
* shape's attributes
*/
- constructor(object: object)
-
+ constructor(object: object);
}
class Rectangle extends Shape {
- /**
+ /**
* Creates a rectangular shape item, with optionally rounded corners.
- *
+ *
* @param rectangle - the rectangle object describing the
* geometry of the rectangular shape to be created
* @param radius - the size of the rounded corners
*/
- constructor(rectangle: paper.Rectangle, radius?: Size)
+ constructor(rectangle: paper.Rectangle, radius?: Size);
- /**
+ /**
* Creates a rectangular shape item from a point and a size object.
- *
+ *
* @param point - the rectangle's top-left corner.
* @param size - the rectangle's size.
*/
- constructor(point: Point, size: Size)
+ constructor(point: Point, size: Size);
- /**
+ /**
* Creates a rectangular shape item 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.
- *
+ *
* @param from - the first point defining the rectangle
* @param to - the second point defining the rectangle
*/
- constructor(from: Point, to: Point)
+ constructor(from: Point, to: Point);
- /**
+ /**
* Creates a rectangular shape item from the properties described by an
* object literal.
- *
+ *
* @param object - an object containing properties describing the
* shape's attributes
*/
- constructor(object: object)
-
+ constructor(object: object);
}
class Ellipse extends Shape {
- /**
+ /**
* Creates an elliptical shape item.
- *
+ *
* @param rectangle - the rectangle circumscribing the ellipse
*/
- constructor(rectangle: paper.Rectangle)
+ constructor(rectangle: paper.Rectangle);
- /**
+ /**
* Creates an elliptical shape item from the properties described by an
* object literal.
- *
+ *
* @param object - an object containing properties describing the
* shape's attributes
*/
- constructor(object: object)
-
+ constructor(object: object);
}
}
- /**
+ /**
* The Size object is used to describe the size or dimensions of
* something, through its {@link #width} and {@link #height} properties.
*/
- class Size {
- /**
+ class Size {
+ /**
* The width of the size
*/
- width: number
+ width: number;
- /**
+ /**
* The height of the size
*/
- height: number
+ height: number;
-
- /**
+ /**
* Creates a Size object with the given width and height values.
- *
+ *
* @param width - the width
* @param height - the height
*/
- constructor(width: number, height: number)
+ constructor(width: number, height: number);
- /**
+ /**
* Creates a Size object using the numbers in the given array as
* dimensions.
*/
- constructor(array: any[])
+ constructor(array: any[]);
- /**
+ /**
* Creates a Size object using the coordinates of the given Size object.
*/
- constructor(size: Size)
+ constructor(size: Size);
- /**
+ /**
* Creates a Size object using the {@link Point#x} and {@link Point#y}
* values of the given Point object.
*/
- constructor(point: Point)
+ constructor(point: Point);
- /**
+ /**
* Creates a Size object using the properties in the given object.
*/
- constructor(object: object)
+ constructor(object: object);
- /**
+ /**
* Sets the size to the passed values. Note that any sequence of parameters
* that is supported by the various {@link Size} constructors also work
* for calls of `set()`.
*/
- set(...values: any[]): Size
+ set(...values: any[]): Size;
- /**
+ /**
* Checks whether the width and height of the size are equal to those of the
* supplied size.
- *
+ *
* @param size - the size to compare to
*/
- equals(size: Size): boolean
+ equals(size: Size): boolean;
- /**
+ /**
* Returns a copy of the size.
*/
- clone(): Size
+ clone(): Size;
- /**
+ /**
* @return a string representation of the size
*/
- toString(): string
+ toString(): string;
- /**
+ /**
* Returns the addition of the supplied value to the width and height of the
* size as a new size. The object itself is not modified!
- *
+ *
* @param number - the number to add
- *
+ *
* @return the addition of the size and the value as a new size
*/
- add(number: number): Size
+ add(number: number): Size;
- /**
+ /**
* Returns the addition of the width and height of the supplied size to the
* size as a new size. The object itself is not modified!
- *
+ *
* @param size - the size to add
- *
+ *
* @return the addition of the two sizes as a new size
*/
- add(size: Size): Size
+ add(size: Size): Size;
- /**
+ /**
* Returns the subtraction of the supplied value from the width and height
* of the size as a new size. The object itself is not modified!
* The object itself is not modified!
- *
+ *
* @param number - the number to subtract
- *
+ *
* @return the subtraction of the size and the value as a new size
*/
- subtract(number: number): Size
+ subtract(number: number): Size;
- /**
+ /**
* Returns the subtraction of the width and height of the supplied size from
* the size as a new size. The object itself is not modified!
- *
+ *
* @param size - the size to subtract
- *
+ *
* @return the subtraction of the two sizes as a new size
*/
- subtract(size: Size): Size
+ subtract(size: Size): Size;
- /**
+ /**
* Returns the multiplication of the supplied value with the width and
* height of the size as a new size. The object itself is not modified!
- *
+ *
* @param number - the number to multiply by
- *
+ *
* @return the multiplication of the size and the value as a new size
*/
- multiply(number: number): Size
+ multiply(number: number): Size;
- /**
+ /**
* Returns the multiplication of the width and height of the supplied size
* with the size as a new size. The object itself is not modified!
- *
+ *
* @param size - the size to multiply by
- *
+ *
* @return the multiplication of the two sizes as a new size
*/
- multiply(size: Size): Size
+ multiply(size: Size): Size;
- /**
+ /**
* Returns the division of the supplied value by the width and height of the
* size as a new size. The object itself is not modified!
- *
+ *
* @param number - the number to divide by
- *
+ *
* @return the division of the size and the value as a new size
*/
- divide(number: number): Size
+ divide(number: number): Size;
- /**
+ /**
* Returns the division of the width and height of the supplied size by the
* size as a new size. The object itself is not modified!
- *
+ *
* @param size - the size to divide by
- *
+ *
* @return the division of the two sizes as a new size
*/
- divide(size: Size): Size
+ divide(size: Size): Size;
- /**
+ /**
* The modulo operator returns the integer remainders of dividing the size
* by the supplied value as a new size.
- *
+ *
* @return the integer remainders of dividing the size by the value
* as a new size
*/
- modulo(value: number): Size
+ modulo(value: number): Size;
- /**
+ /**
* The modulo operator returns the integer remainders of dividing the size
* by the supplied size as a new size.
- *
+ *
* @return the integer remainders of dividing the sizes by each
* other as a new size
*/
- modulo(size: Size): Size
+ modulo(size: Size): Size;
- /**
+ /**
* Checks if this size has both the width and height set to 0.
- *
+ *
* @return true if both width and height are 0
*/
- isZero(): boolean
+ isZero(): boolean;
- /**
+ /**
* Checks if the width or the height of the size are NaN.
- *
+ *
* @return true if the width or height of the size are NaN
*/
- isNaN(): boolean
+ isNaN(): boolean;
- /**
+ /**
* Returns a new size with rounded {@link #width} and {@link #height}
* values. The object itself is not modified!
*/
- round(): Size
+ round(): Size;
- /**
+ /**
* Returns a new size with the nearest greater non-fractional values to the
* specified {@link #width} and {@link #height} values. The object itself is
* not modified!
*/
- ceil(): Size
+ ceil(): Size;
- /**
+ /**
* Returns a new size with the nearest smaller non-fractional values to the
* specified {@link #width} and {@link #height} values. The object itself is
* not modified!
*/
- floor(): Size
+ floor(): Size;
- /**
+ /**
* Returns a new size with the absolute values of the specified
* {@link #width} and {@link #height} values. The object itself is not
* modified!
*/
- abs(): Size
+ abs(): Size;
- /**
+ /**
* Returns a new size object with the smallest {@link #width} and
* {@link #height} of the supplied sizes.
- *
+ *
* @return the newly created size object
*/
- static min(size1: Size, size2: Size): Size
+ static min(size1: Size, size2: Size): Size;
- /**
+ /**
* Returns a new size object with the largest {@link #width} and
* {@link #height} of the supplied sizes.
- *
+ *
* @return the newly created size object
*/
- static max(size1: Size, size2: Size): Size
+ static max(size1: Size, size2: Size): Size;
- /**
+ /**
* Returns a size object with random {@link #width} and {@link #height}
* values between `0` and `1`.
- *
+ *
* @return the newly created size object
*/
- static random(): Size
-
+ static random(): Size;
}
- /**
+ /**
* Style is used for changing the visual styles of items
* contained within a Paper.js project and is returned by
* {@link Item#style} and {@link Project#currentStyle}.
- *
+ *
* All properties of Style are also reflected directly in {@link Item},
* i.e.: {@link Item#fillColor}.
- *
+ *
* To set multiple style properties in one go, you can pass an object to
* {@link Item#style}. This is a convenient way to define a style once and
* apply it to a series of items:
*/
- class Style {
- /**
+ class Style {
+ /**
* The view that this style belongs to.
*/
- readonly view: View
+ readonly view: View;
- /**
+ /**
* The color of the stroke.
*/
- strokeColor: Color | null
+ strokeColor: Color | null;
- /**
+ /**
* The width of the stroke.
*/
- strokeWidth: number
+ strokeWidth: number;
- /**
+ /**
* The shape to be used at the beginning and end of open {@link Path} items,
* when they have a stroke.
*/
- strokeCap: string
+ strokeCap: string;
- /**
+ /**
* The shape to be used at the segments and corners of {@link Path} items
* when they have a stroke.
*/
- strokeJoin: string
+ strokeJoin: string;
- /**
+ /**
* Specifies whether the stroke is to be drawn taking the current affine
* transformation into account (the default behavior), or whether it should
* appear as a non-scaling stroke.
*/
- strokeScaling: boolean
+ strokeScaling: boolean;
- /**
+ /**
* The dash offset of the stroke.
*/
- dashOffset: number
+ dashOffset: number;
- /**
+ /**
* Specifies an array containing the dash and gap lengths of the stroke.
*/
- dashArray: number[]
+ dashArray: number[];
- /**
+ /**
* The miter limit of the stroke. When two line segments meet at a sharp
* angle and miter joins have been specified for {@link #strokeJoin}, it is
* possible for the miter to extend far beyond the {@link #strokeWidth} of
* the path. The miterLimit imposes a limit on the ratio of the miter length
* to the {@link #strokeWidth}.
*/
- miterLimit: number
+ miterLimit: number;
- /**
+ /**
* The fill color.
*/
- fillColor: Color | null
+ fillColor: Color | null;
- /**
+ /**
* The fill-rule with which the shape gets filled. Please note that only
* modern browsers support fill-rules other than `'nonzero'`.
*/
- fillRule: string
+ fillRule: string;
- /**
+ /**
* The shadow color.
*/
- shadowColor: Color | null
+ shadowColor: Color | null;
- /**
+ /**
* The shadow's blur radius.
*/
- shadowBlur: number
+ shadowBlur: number;
- /**
+ /**
* The shadow's offset.
*/
- shadowOffset: Point
+ shadowOffset: Point;
- /**
+ /**
* The color the item is highlighted with when selected. If the item does
* not specify its own color, the color defined by its layer is used instead.
*/
- selectedColor: Color | null
+ selectedColor: Color | null;
+
+ /**
+ * The width of the selection stroke.
+ */
+ selectionWidth: number;
- /**
+ /**
+ * The shape to be used at the beginning and end of open {@link Path} items,
+ * when they have a selection stroke.
+ */
+ selectionCap: string;
+
+ /**
+ * The shape to be used at the segments and corners of {@link Path} items
+ * when they have a selection stroke.
+ */
+ selectionJoin: string;
+
+ /**
+ * The miter limit of the selection stroke.
+ */
+ selectedMiterLimit: number;
+
+ /**
* The font-family to be used in text content.
*/
- fontFamily: string
+ fontFamily: string;
- /**
+ /**
* The font-weight to be used in text content.
*/
- fontWeight: string | number
+ fontWeight: string | number;
- /**
+ /**
* The font size of text content, as a number in pixels, or as a string with
* optional units `'px'`, `'pt'` and `'em'`.
*/
- fontSize: number | string
+ fontSize: number | string;
- /**
+ /**
* The text leading of text content.
*/
- leading: number | string
+ leading: number | string;
- /**
+ /**
* The justification of text paragraphs.
*/
- justification: string
+ justification: string;
-
- /**
+ /**
* Style objects don't need to be created directly. Just pass an object to
* {@link Item#style} or {@link Project#currentStyle}, it will be converted
* to a Style object internally.
*/
- constructor(style: object)
-
+ constructor(style: object);
}
- /**
+ /**
* Symbols allow you to place multiple instances of an item in your
* project. This can save memory, since all instances of a symbol simply refer
* to the original item and it can speed up moving around complex objects, since
* internal properties such as segment lists and gradient positions don't need
* to be updated with every transformation.
*/
- class SymbolDefinition {
- /**
+ class SymbolDefinition {
+ /**
* The project that this symbol belongs to.
*/
- readonly project: Project
+ readonly project: Project;
- /**
+ /**
* The item used as the symbol's definition.
*/
- item: Item
-
+ item: Item;
- /**
+ /**
* Creates a Symbol definition.
- *
+ *
* @param item - the source item which is removed from the scene graph
* and becomes the symbol's definition.
*/
- constructor(item: Item, dontCenter?: boolean)
+ constructor(item: Item, dontCenter?: boolean);
- /**
+ /**
* Places in instance of the symbol in the project.
- *
+ *
* @param position - the position of the placed symbol
*/
- place(position?: Point): SymbolItem
+ place(position?: Point): SymbolItem;
- /**
+ /**
* Returns a copy of the symbol.
*/
- clone(): SymbolDefinition
+ clone(): SymbolDefinition;
- /**
+ /**
* Checks whether the symbol's definition is equal to the supplied symbol.
- *
+ *
* @return true if they are equal
*/
- equals(symbol: SymbolDefinition): boolean
-
+ equals(symbol: SymbolDefinition): boolean;
}
- /**
+ /**
* A symbol item represents an instance of a symbol which has been
* placed in a Paper.js project.
*/
class SymbolItem extends Item {
- /**
+ /**
* The symbol definition that the placed symbol refers to.
*/
- definition: SymbolDefinition
-
+ definition: SymbolDefinition;
- /**
+ /**
* Creates a new symbol item.
- *
+ *
* @param definition - the definition to place or an
* item to place as a symbol
* @param point - the center point of the placed symbol
*/
- constructor(definition: SymbolDefinition | Item, point?: Point)
-
+ constructor(definition: SymbolDefinition | Item, point?: Point);
}
- /**
+ /**
* The TextItem type allows you to create typography. Its functionality
* is inherited by different text item types such as {@link PointText}, and
* {@link AreaText} (coming soon). They each add a layer of functionality
@@ -6601,200 +6572,195 @@ declare namespace paper {
* functions that they inherit from TextItem.
*/
class TextItem extends Item {
- /**
+ /**
* The text contents of the text item.
*/
- content: string
+ content: string;
- /**
+ /**
* The font-family to be used in text content.
*/
- fontFamily: string
+ fontFamily: string;
- /**
+ /**
* The font-weight to be used in text content.
*/
- fontWeight: string | number
+ fontWeight: string | number;
- /**
+ /**
* The font size of text content, as a number in pixels, or as a string with
* optional units `'px'`, `'pt'` and `'em'`.
*/
- fontSize: number | string
+ fontSize: number | string;
- /**
+ /**
* The text leading of text content.
*/
- leading: number | string
+ leading: number | string;
- /**
+ /**
* The justification of text paragraphs.
*/
- justification: string
-
-
+ justification: string;
}
- /**
+ /**
* The Tool object refers to a script that the user can interact with by
* using the mouse and keyboard and can be accessed through the global
* `tool` variable. All its properties are also available in the paper
* scope.
- *
+ *
* The global `tool` variable only exists in scripts that contain mouse handler
* functions ({@link #onMouseMove}, {@link #onMouseDown}, {@link #onMouseDrag},
* {@link #onMouseUp}) or a keyboard handler function ({@link #onKeyDown},
* {@link #onKeyUp}).
*/
- class Tool {
- /**
+ class Tool {
+ /**
* The minimum distance the mouse has to drag before firing the onMouseDrag
* event, since the last onMouseDrag event.
*/
- minDistance: number
+ minDistance: number;
- /**
+ /**
* The maximum distance the mouse has to drag before firing the onMouseDrag
* event, since the last onMouseDrag event.
*/
- maxDistance: number
+ maxDistance: number;
-
- fixedDistance: number
+ fixedDistance: number;
- /**
+ /**
* The function to be called when the mouse button is pushed down. The
* function receives a {@link ToolEvent} object which contains information
* about the tool event.
*/
- onMouseDown: Function | null
+ onMouseDown: Function | null;
- /**
+ /**
* The function to be called when the mouse position changes while the mouse
* is being dragged. The function receives a {@link ToolEvent} object which
* contains information about the tool event.
*/
- onMouseDrag: Function | null
+ onMouseDrag: Function | null;
- /**
+ /**
* The function to be called the mouse moves within the project view. The
* function receives a {@link ToolEvent} object which contains information
* about the tool event.
*/
- onMouseMove: Function | null
+ onMouseMove: Function | null;
- /**
+ /**
* The function to be called when the mouse button is released. The function
* receives a {@link ToolEvent} object which contains information about the
* tool event.
*/
- onMouseUp: Function | null
+ onMouseUp: Function | null;
- /**
+ /**
* The function to be called when the user presses a key on the keyboard.
* The function receives a {@link KeyEvent} object which contains
* information about the keyboard event.
- *
+ *
* If the function returns `false`, the keyboard event will be prevented
* from bubbling up. This can be used for example to stop the window from
* scrolling, when you need the user to interact with arrow keys.
*/
- onKeyDown: Function | null
+ onKeyDown: Function | null;
- /**
+ /**
* The function to be called when the user releases a key on the keyboard.
* The function receives a {@link KeyEvent} object which contains
* information about the keyboard event.
- *
+ *
* If the function returns `false`, the keyboard event will be prevented
* from bubbling up. This can be used for example to stop the window from
* scrolling, when you need the user to interact with arrow keys.
*/
- onKeyUp: Function | null
-
+ onKeyUp: Function | null;
- /**
+ /**
* Activates this tool, meaning {@link PaperScope#tool} will
* point to it and it will be the one that receives tool events.
*/
- activate(): void
+ activate(): void;
- /**
+ /**
* Removes this tool from the {@link PaperScope#tools} list.
*/
- remove(): void
+ remove(): void;
- /**
+ /**
* Attach an event handler to the tool.
- *
+ *
* @param type - the event type: {@values 'mousedown', 'mouseup',
* 'mousedrag', 'mousemove', 'keydown', 'keyup'}
* @param function - the function to be called when the event
* occurs, receiving a {@link ToolEvent} object as its sole argument
- *
+ *
* @return this tool itself, so calls can be chained
*/
- on(type: string, callback: Function): Tool
+ on(type: string, callback: Function): Tool;
- /**
+ /**
* Attach one or more event handlers to the tool.
- *
+ *
* @param param - an object literal containing one or more of the
* following properties: {@values mousedown, mouseup, mousedrag,
* mousemove, keydown, keyup}
- *
+ *
* @return this tool itself, so calls can be chained
*/
- on(param: object): Tool
+ on(param: object): Tool;
- /**
+ /**
* Detach an event handler from the tool.
- *
+ *
* @param type - the event type: {@values 'mousedown', 'mouseup',
* 'mousedrag', 'mousemove', 'keydown', 'keyup'}
* @param function - the function to be detached
- *
+ *
* @return this tool itself, so calls can be chained
*/
- off(type: string, callback: Function): Tool
+ off(type: string, callback: Function): Tool;
- /**
+ /**
* Detach one or more event handlers from the tool.
- *
+ *
* @param param - an object literal containing one or more of the
* following properties: {@values mousedown, mouseup, mousedrag,
* mousemove, keydown, keyup}
- *
+ *
* @return this tool itself, so calls can be chained
*/
- off(param: object): Tool
+ off(param: object): Tool;
- /**
+ /**
* Emit an event on the tool.
- *
+ *
* @param type - the event type: {@values 'mousedown', 'mouseup',
* 'mousedrag', 'mousemove', 'keydown', 'keyup'}
* @param event - an object literal containing properties describing
* the event
- *
+ *
* @return true if the event had listeners
*/
- emit(type: string, event: object): boolean
+ emit(type: string, event: object): boolean;
- /**
+ /**
* Check if the tool has one or more event handlers of the specified type.
- *
+ *
* @param type - the event type: {@values 'mousedown', 'mouseup',
* 'mousedrag', 'mousemove', 'keydown', 'keyup'}
- *
+ *
* @return true if the tool has one or more event handlers of
* the specified type
*/
- responds(type: string): boolean
-
+ responds(type: string): boolean;
}
- /**
+ /**
* ToolEvent The ToolEvent object is received by the {@link Tool}'s mouse
* event handlers {@link Tool#onMouseDown}, {@link Tool#onMouseDrag},
* {@link Tool#onMouseMove} and {@link Tool#onMouseUp}. The ToolEvent
@@ -6802,90 +6768,87 @@ declare namespace paper {
* information about the mouse event.
*/
class ToolEvent extends Event {
- /**
+ /**
* The type of tool event.
*/
- type: string
+ type: string;
- /**
+ /**
* The position of the mouse in project coordinates when the event was
* fired.
*/
- point: Point
+ point: Point;
- /**
+ /**
* The position of the mouse in project coordinates when the previous
* event was fired.
*/
- lastPoint: Point
+ lastPoint: Point;
- /**
+ /**
* The position of the mouse in project coordinates when the mouse button
* was last clicked.
*/
- downPoint: Point
+ downPoint: Point;
- /**
+ /**
* The point in the middle between {@link #lastPoint} and
* {@link #point}. This is a useful position to use when creating
* artwork based on the moving direction of the mouse, as returned by
* {@link #delta}.
*/
- middlePoint: Point
+ middlePoint: Point;
- /**
+ /**
* The difference between the current position and the last position of the
* mouse when the event was fired. In case of the mouseup event, the
* difference to the mousedown position is returned.
*/
- delta: Point
+ delta: Point;
- /**
+ /**
* The number of times the mouse event was fired.
*/
- count: number
+ count: number;
- /**
+ /**
* The item at the position of the mouse (if any).
- *
+ *
* If the item is contained within one or more {@link Group} or
* {@link CompoundPath} items, the most top level group or compound path
* that it is contained within is returned.
*/
- item: Item
-
+ item: Item;
- /**
+ /**
* @return a string representation of the tool event
*/
- toString(): string
-
+ toString(): string;
}
- /**
+ /**
* Allows tweening `Object` properties between two states for a given
* duration. To tween properties on Paper.js {@link Item} instances,
* {@link Item#tween} can be used, which returns created
* tween instance.
- *
+ *
* @see Item#tween(from, to, options)
* @see Item#tween(to, options)
* @see Item#tween(options)
* @see Item#tweenTo(to, options)
* @see Item#tweenFrom(from, options)
*/
- class Tween {
- /**
+ class Tween {
+ /**
* The function to be called when the tween is updated. It receives an
* object as its sole argument, containing the current progress of the
* tweening and the factor calculated by the easing function.
*/
- onUpdate: Function | null
+ onUpdate: Function | null;
-
- /**
+ /**
* Creates a new tween.
- *
+ *
* @param object - the object to tween the properties on
* @param from - the state at the start of the tweening
* @param to - the state at the end of the tweening
@@ -6894,120 +6857,126 @@ declare namespace paper {
* function or the easing function
* @param start - whether to start tweening automatically
*/
- constructor(object: object, from: object, to: object, duration: number, easing?: string | Function, start?: boolean)
+ constructor(
+ object: object,
+ from: object,
+ to: object,
+ duration: number,
+ easing?: string | Function,
+ start?: boolean
+ );
- /**
+ /**
* Set a function that will be executed when the tween completes.
- *
+ *
* @param function - the function to execute when the tween
* completes
*/
- then(callback: Function): Tween
+ then(callback: Function): Tween;
- /**
+ /**
* Start tweening.
*/
- start(): Tween
+ start(): Tween;
- /**
+ /**
* Stop tweening.
*/
- stop(): Tween
-
+ stop(): Tween;
}
- /**
+ /**
* The View object wraps an HTML element and handles drawing and user
* interaction through mouse and keyboard for it. It offer means to scroll the
* view, find the currently visible bounds in project coordinates, or the
* center, both useful for constructing artwork that should appear centered on
* screen.
*/
- class View {
- /**
+ class View {
+ /**
* Controls whether the view is automatically updated in the next animation
* frame on changes, or whether you prefer to manually call
* {@link #update} or {@link #requestUpdate} after changes.
* Note that this is `true` by default, except for Node.js, where manual
* updates make more sense.
*/
- autoUpdate: boolean
+ autoUpdate: boolean;
- /**
+ /**
* The underlying native element.
*/
- readonly element: HTMLCanvasElement
+ readonly element: HTMLCanvasElement;
- /**
+ /**
* The ratio between physical pixels and device-independent pixels (DIPs)
* of the underlying canvas / device.
* It is `1` for normal displays, and `2` or more for
* high-resolution displays.
*/
- readonly pixelRatio: number
+ readonly pixelRatio: number;
- /**
+ /**
* The resoltuion of the underlying canvas / device in pixel per inch (DPI).
* It is `72` for normal displays, and `144` for high-resolution
* displays with a pixel-ratio of `2`.
*/
- readonly resolution: number
+ readonly resolution: number;
- /**
+ /**
* The size of the view. Changing the view's size will resize it's
* underlying element.
*/
- viewSize: Size
+ viewSize: Size;
- /**
+ /**
* The bounds of the currently visible area in project coordinates.
*/
- readonly bounds: Rectangle
+ readonly bounds: Rectangle;
- /**
+ /**
* The size of the visible area in project coordinates.
*/
- readonly size: Size
+ readonly size: Size;
- /**
+ /**
* The center of the visible area in project coordinates.
*/
- center: Point
+ center: Point;
- /**
+ /**
* The view's zoom factor by which the project coordinates are magnified.
- *
+ *
* @see #scaling
*/
- zoom: number
+ zoom: number;
- /**
+ /**
* The current rotation angle of the view, as described by its
* {@link #matrix}.
*/
- rotation: number
+ rotation: number;
- /**
+ /**
* The current scale factor of the view, as described by its
* {@link #matrix}.
- *
+ *
* @see #zoom
*/
- scaling: Point
+ scaling: Point;
- /**
+ /**
* The view's transformation matrix, defining the view onto the project's
* contents (position, zoom level, rotation, etc).
*/
- matrix: Matrix
+ matrix: Matrix;
- /**
+ /**
* Handler function to be called on each frame of an animation.
* The function receives an event object which contains information about
* the frame event:
- *
+ *
* @see Item#onFrame
- *
+ *
* @option event.count {Number} the number of times the frame event was
* fired
* @option event.time {Number} the total amount of time passed since the
@@ -7015,83 +6984,83 @@ declare namespace paper {
* @option event.delta {Number} the time passed in seconds since the last
* frame event
*/
- onFrame: Function | null
+ onFrame: Function | null;
- /**
+ /**
* Handler function that is called whenever a view is resized.
*/
- onResize: Function | null
+ onResize: Function | null;
- /**
+ /**
* The function to be called when the mouse button is pushed down on the
* view. The function receives a {@link MouseEvent} object which contains
* information about the mouse event.
* Note that such mouse events bubble up the scene graph hierarchy, reaching
* the view at the end, unless they are stopped before with {@link
* Event#stopPropagation()} or by returning `false` from a handler.
- *
+ *
* @see Item#onMouseDown
*/
- onMouseDown: Function | null
+ onMouseDown: Function | null;
- /**
+ /**
* The function to be called when the mouse position changes while the mouse
* is being dragged over the view. The function receives a {@link
* MouseEvent} object which contains information about the mouse event.
* Note that such mouse events bubble up the scene graph hierarchy, reaching
* the view at the end, unless they are stopped before with {@link
* Event#stopPropagation()} or by returning `false` from a handler.
- *
+ *
* @see Item#onMouseDrag
*/
- onMouseDrag: Function | null
+ onMouseDrag: Function | null;
- /**
+ /**
* The function to be called when the mouse button is released over the item.
* The function receives a {@link MouseEvent} object which contains
* information about the mouse event.
- *
+ *
* @see Item#onMouseUp
*/
- onMouseUp: Function | null
+ onMouseUp: Function | null;
- /**
+ /**
* The function to be called when the mouse clicks on the view. The function
* receives a {@link MouseEvent} object which contains information about the
* mouse event.
* Note that such mouse events bubble up the scene graph hierarchy, reaching
* the view at the end, unless they are stopped before with {@link
* Event#stopPropagation()} or by returning `false` from a handler.
- *
+ *
* @see Item#onClick
*/
- onClick: Function | null
+ onClick: Function | null;
- /**
+ /**
* The function to be called when the mouse double clicks on the view. The
* function receives a {@link MouseEvent} object which contains information
* about the mouse event.
* Note that such mouse events bubble up the scene graph hierarchy, reaching
* the view at the end, unless they are stopped before with {@link
* Event#stopPropagation()} or by returning `false` from a handler.
- *
+ *
* @see Item#onDoubleClick
*/
- onDoubleClick: Function | null
+ onDoubleClick: Function | null;
- /**
+ /**
* The function to be called repeatedly while the mouse moves over the
* view. The function receives a {@link MouseEvent} object which contains
* information about the mouse event.
* Note that such mouse events bubble up the scene graph hierarchy, reaching
* the view at the end, unless they are stopped before with {@link
* Event#stopPropagation()} or by returning `false` from a handler.
- *
+ *
* @see Item#onMouseMove
*/
- onMouseMove: Function | null
+ onMouseMove: Function | null;
- /**
+ /**
* The function to be called when the mouse moves over the view. This
* function will only be called again, once the mouse moved outside of the
* view first. The function receives a {@link MouseEvent} object which
@@ -7099,271 +7068,269 @@ declare namespace paper {
* Note that such mouse events bubble up the scene graph hierarchy, reaching
* the view at the end, unless they are stopped before with {@link
* Event#stopPropagation()} or by returning `false` from a handler.
- *
+ *
* @see Item#onMouseEnter
*/
- onMouseEnter: Function | null
+ onMouseEnter: Function | null;
- /**
+ /**
* The function to be called when the mouse moves out of the view.
* The function receives a {@link MouseEvent} object which contains
* information about the mouse event.
* Note that such mouse events bubble up the scene graph hierarchy, reaching
* the view at the end, unless they are stopped before with {@link
* Event#stopPropagation()} or by returning `false` from a handler.
- *
+ *
* @see View#onMouseLeave
*/
- onMouseLeave: Function | null
-
+ onMouseLeave: Function | null;
- /**
+ /**
* Removes this view from the project and frees the associated element.
*/
- remove(): void
+ remove(): void;
- /**
+ /**
* Updates the view if there are changes. Note that when using built-in
* event hanlders for interaction, animation and load events, this method is
* invoked for you automatically at the end.
- *
+ *
* @return true if the view was updated
*/
- update(): boolean
+ update(): boolean;
- /**
+ /**
* Requests an update of the view if there are changes through the browser's
* requestAnimationFrame() mechanism for smooth animation. Note that when
* using built-in event handlers for interaction, animation and load events,
* updates are automatically invoked for you automatically at the end.
*/
- requestUpdate(): void
+ requestUpdate(): void;
- /**
+ /**
* Makes all animation play by adding the view to the request animation
* loop.
*/
- play(): void
+ play(): void;
- /**
+ /**
* Makes all animation pause by removing the view from the request animation
* loop.
*/
- pause(): void
+ pause(): void;
- /**
+ /**
* Checks whether the view is currently visible within the current browser
* viewport.
- *
+ *
* @return true if the view is visible
*/
- isVisible(): boolean
+ isVisible(): boolean;
- /**
+ /**
* Checks whether the view is inserted into the browser DOM.
- *
+ *
* @return true if the view is inserted
*/
- isInserted(): boolean
+ isInserted(): boolean;
- /**
+ /**
* Translates (scrolls) the view by the given offset vector.
- *
+ *
* @param delta - the offset to translate the view by
*/
- translate(delta: Point): void
+ translate(delta: Point): void;
- /**
+ /**
* Rotates the view by a given angle around the given center point.
- *
+ *
* Angles are oriented clockwise and measured in degrees.
- *
+ *
* @see Matrix#rotate(angle[, center])
- *
+ *
* @param angle - the rotation angle
*/
- rotate(angle: number, center?: Point): void
+ rotate(angle: number, center?: Point): void;
- /**
+ /**
* Scales the view by the given value from its center point, or optionally
* from a supplied point.
- *
+ *
* @param scale - the scale factor
*/
- scale(scale: number, center?: Point): void
+ scale(scale: number, center?: Point): void;
- /**
+ /**
* Scales the view by the given values from its center point, or optionally
* from a supplied point.
- *
+ *
* @param hor - the horizontal scale factor
* @param ver - the vertical scale factor
*/
- scale(hor: number, ver: number, center?: Point): void
+ scale(hor: number, ver: number, center?: Point): void;
- /**
+ /**
* Shears the view by the given value from its center point, or optionally
* by a supplied point.
- *
+ *
* @see Matrix#shear(shear[, center])
- *
+ *
* @param shear - the horizontal and vertical shear factors as a point
*/
- shear(shear: Point, center?: Point): void
+ shear(shear: Point, center?: Point): void;
- /**
+ /**
* Shears the view by the given values from its center point, or optionally
* by a supplied point.
- *
+ *
* @see Matrix#shear(hor, ver[, center])
- *
+ *
* @param hor - the horizontal shear factor
* @param ver - the vertical shear factor
*/
- shear(hor: number, ver: number, center?: Point): void
+ shear(hor: number, ver: number, center?: Point): void;
- /**
+ /**
* Skews the view by the given angles from its center point, or optionally
* by a supplied point.
- *
+ *
* @see Matrix#shear(skew[, center])
- *
+ *
* @param skew - the horizontal and vertical skew angles in degrees
*/
- skew(skew: Point, center?: Point): void
+ skew(skew: Point, center?: Point): void;
- /**
+ /**
* Skews the view by the given angles from its center point, or optionally
* by a supplied point.
- *
+ *
* @see Matrix#shear(hor, ver[, center])
- *
+ *
* @param hor - the horizontal skew angle in degrees
* @param ver - the vertical sskew angle in degrees
*/
- skew(hor: number, ver: number, center?: Point): void
+ skew(hor: number, ver: number, center?: Point): void;
- /**
+ /**
* Transform the view.
- *
+ *
* @param matrix - the matrix by which the view shall be transformed
*/
- transform(matrix: Matrix): void
+ transform(matrix: Matrix): void;
- /**
+ /**
* Converts the passed point from project coordinate space to view
* coordinate space, which is measured in browser pixels in relation to the
* position of the view element.
- *
+ *
* @param point - the point in project coordinates to be converted
- *
+ *
* @return the point converted into view coordinates
*/
- projectToView(point: Point): Point
+ projectToView(point: Point): Point;
- /**
+ /**
* Converts the passed point from view coordinate space to project
* coordinate space.
- *
+ *
* @param point - the point in view coordinates to be converted
- *
+ *
* @return the point converted into project coordinates
*/
- viewToProject(point: Point): Point
+ viewToProject(point: Point): Point;
- /**
+ /**
* Determines and returns the event location in project coordinate space.
- *
+ *
* @param event - the native event object for which to determine the
* location.
- *
+ *
* @return the event point in project coordinates.
*/
- getEventPoint(event: Event): Point
+ getEventPoint(event: Event): Point;
- /**
+ /**
* Attach an event handler to the view.
- *
+ *
* @param type - the type of event: {@values 'frame', 'resize',
* 'mousedown', 'mouseup', 'mousedrag', 'click', 'doubleclick',
* 'mousemove', 'mouseenter', 'mouseleave'}
* @param function - the function to be called when the event
* occurs, receiving a {@link MouseEvent} or {@link Event} object as its
* sole argument
- *
+ *
* @return this view itself, so calls can be chained
*/
- on(type: string, callback: Function): View
+ on(type: string, callback: Function): View;
- /**
+ /**
* Attach one or more event handlers to the view.
- *
+ *
* @param param - an object literal containing one or more of the
* following properties: {@values frame, resize}
- *
+ *
* @return this view itself, so calls can be chained
*/
- on(param: object): View
+ on(param: object): View;
- /**
+ /**
* Detach an event handler from the view.
- *
+ *
* @param type - the event type: {@values 'frame', 'resize',
* 'mousedown', 'mouseup', 'mousedrag', 'click', 'doubleclick',
* 'mousemove', 'mouseenter', 'mouseleave'}
* @param function - the function to be detached
- *
+ *
* @return this view itself, so calls can be chained
*/
- off(type: string, callback: Function): View
+ off(type: string, callback: Function): View;
- /**
+ /**
* Detach one or more event handlers from the view.
- *
+ *
* @param param - an object literal containing one or more of the
* following properties: {@values frame, resize}
- *
+ *
* @return this view itself, so calls can be chained
*/
- off(param: object): View
+ off(param: object): View;
- /**
+ /**
* Emit an event on the view.
- *
+ *
* @param type - the event type: {@values 'frame', 'resize',
* 'mousedown', 'mouseup', 'mousedrag', 'click', 'doubleclick',
* 'mousemove', 'mouseenter', 'mouseleave'}
* @param event - an object literal containing properties describing
* the event
- *
+ *
* @return true if the event had listeners
*/
- emit(type: string, event: object): boolean
+ emit(type: string, event: object): boolean;
- /**
+ /**
* Check if the view has one or more event handlers of the specified type.
- *
+ *
* @param type - the event type: {@values 'frame', 'resize',
* 'mousedown', 'mouseup', 'mousedrag', 'click', 'doubleclick',
* 'mousemove', 'mouseenter', 'mouseleave'}
- *
+ *
* @return true if the view has one or more event handlers of
* the specified type
*/
- responds(type: string): boolean
-
+ responds(type: string): boolean;
}
}
-
-declare module 'paper/dist/paper-core'
-{
- const paperCore: Pick>;
- export = paperCore
+declare module "paper/dist/paper-core" {
+ const paperCore: Pick<
+ paper.PaperScope,
+ Exclude
+ >;
+ export = paperCore;
}
-declare module 'paper'
-{
+declare module "paper" {
const paperFull: paper.PaperScope;
- export = paperFull
+ export = paperFull;
}
diff --git a/src/core/PaperScope.js b/src/core/PaperScope.js
index 02f092002c..c92776180c 100644
--- a/src/core/PaperScope.js
+++ b/src/core/PaperScope.js
@@ -32,293 +32,299 @@
* The global {@link paper} object is simply a reference to the currently active
* `PaperScope`.
*/
-var PaperScope = Base.extend(/** @lends PaperScope# */{
- _class: 'PaperScope',
+var PaperScope = Base.extend(
+ /** @lends PaperScope# */ {
+ _class: "PaperScope",
- /**
- * Creates a PaperScope object.
- *
- * @name PaperScope#initialize
- * @function
- */
- // DOCS: initialize() parameters
- initialize: function PaperScope() {
- // element is only used internally when creating scopes for PaperScript.
- // Whenever a PaperScope is created, it automatically becomes the active
- // one.
- paper = this;
- // Default configurable settings.
- this.settings = new Base({
- applyMatrix: true,
- insertItems: true,
- handleSize: 4,
- hitTolerance: 0
- });
- this.project = null;
- this.projects = [];
- this.tools = [];
- // Assign a unique id to each scope .
- this._id = PaperScope._id++;
- PaperScope._scopes[this._id] = this;
- var proto = PaperScope.prototype;
- if (!this.support) {
- // Set up paper.support, as an object containing properties that
- // describe the support of various features.
- var ctx = CanvasProvider.getContext(1, 1) || {};
- proto.support = {
- nativeDash: 'setLineDash' in ctx || 'mozDash' in ctx,
- nativeBlendModes: BlendMode.nativeModes
- };
- CanvasProvider.release(ctx);
- }
- if (!this.agent) {
- // Use self.instead of window, to cover handle web-workers too.
- var user = self.navigator.userAgent.toLowerCase(),
- // Detect basic platforms, only mac internally required for now.
- os = (/(darwin|win|mac|linux|freebsd|sunos)/.exec(user)||[])[0],
- platform = os === 'darwin' ? 'mac' : os,
- agent = proto.agent = proto.browser = { platform: platform };
- if (platform)
- agent[platform] = true;
- // Use replace() to get all matches, and deal with Chrome/Webkit
- // overlap:
- // TODO: Do we need Mozilla next to Firefox? Other than the
- // different treatment of the Chrome/Webkit overlap
- // here: { chrome: true, webkit: false }, Mozilla missing is the
- // only difference to jQuery.browser
- user.replace(
- /(opera|chrome|safari|webkit|firefox|msie|trident|atom|node|jsdom)\/?\s*([.\d]+)(?:.*version\/([.\d]+))?(?:.*rv\:v?([.\d]+))?/g,
- function(match, n, v1, v2, rv) {
- // Do not set additional browsers once chrome is detected.
- if (!agent.chrome) {
- var v = n === 'opera' ? v2 :
- /^(node|trident)$/.test(n) ? rv : v1;
- agent.version = v;
- agent.versionNumber = parseFloat(v);
- n = { trident: 'msie', jsdom: 'node' }[n] || n;
- agent.name = n;
- agent[n] = true;
+ /**
+ * Creates a PaperScope object.
+ *
+ * @name PaperScope#initialize
+ * @function
+ */
+ // DOCS: initialize() parameters
+ initialize: function PaperScope() {
+ // element is only used internally when creating scopes for PaperScript.
+ // Whenever a PaperScope is created, it automatically becomes the active
+ // one.
+ paper = this;
+ // Default configurable settings.
+ this.settings = new Base({
+ applyMatrix: true,
+ insertItems: true,
+ handleSize: 4,
+ drawSelected: true,
+ hitTolerance: 0,
+ });
+ this.project = null;
+ this.projects = [];
+ this.tools = [];
+ // Assign a unique id to each scope .
+ this._id = PaperScope._id++;
+ PaperScope._scopes[this._id] = this;
+ var proto = PaperScope.prototype;
+ if (!this.support) {
+ // Set up paper.support, as an object containing properties that
+ // describe the support of various features.
+ var ctx = CanvasProvider.getContext(1, 1) || {};
+ proto.support = {
+ nativeDash: "setLineDash" in ctx || "mozDash" in ctx,
+ nativeBlendModes: BlendMode.nativeModes,
+ };
+ CanvasProvider.release(ctx);
+ }
+ if (!this.agent) {
+ // Use self.instead of window, to cover handle web-workers too.
+ var user = self.navigator.userAgent.toLowerCase(),
+ // Detect basic platforms, only mac internally required for now.
+ os = (/(darwin|win|mac|linux|freebsd|sunos)/.exec(user) ||
+ [])[0],
+ platform = os === "darwin" ? "mac" : os,
+ agent =
+ (proto.agent =
+ proto.browser =
+ { platform: platform });
+ if (platform) agent[platform] = true;
+ // Use replace() to get all matches, and deal with Chrome/Webkit
+ // overlap:
+ // TODO: Do we need Mozilla next to Firefox? Other than the
+ // different treatment of the Chrome/Webkit overlap
+ // here: { chrome: true, webkit: false }, Mozilla missing is the
+ // only difference to jQuery.browser
+ user.replace(
+ /(opera|chrome|safari|webkit|firefox|msie|trident|atom|node|jsdom)\/?\s*([.\d]+)(?:.*version\/([.\d]+))?(?:.*rv\:v?([.\d]+))?/g,
+ function (match, n, v1, v2, rv) {
+ // Do not set additional browsers once chrome is detected.
+ if (!agent.chrome) {
+ var v =
+ n === "opera"
+ ? v2
+ : /^(node|trident)$/.test(n)
+ ? rv
+ : v1;
+ agent.version = v;
+ agent.versionNumber = parseFloat(v);
+ n = { trident: "msie", jsdom: "node" }[n] || n;
+ agent.name = n;
+ agent[n] = true;
+ }
}
- }
- );
- if (agent.chrome)
- delete agent.webkit;
- if (agent.atom)
- delete agent.chrome;
- }
- },
+ );
+ if (agent.chrome) delete agent.webkit;
+ if (agent.atom) delete agent.chrome;
+ }
+ },
- /**
- * The version of Paper.js, as a string.
- *
- * @type String
- * @readonly
- */
- version: /*#=*/__options.version,
+ /**
+ * The version of Paper.js, as a string.
+ *
+ * @type String
+ * @readonly
+ */
+ version: /*#=*/ __options.version,
- /**
- * Gives access to paper's configurable settings.
- *
- * @name PaperScope#settings
- * @type Object
- *
- * @option [settings.insertItems=true] {Boolean} controls whether newly
- * created items are automatically inserted into the scene graph, by
- * adding them to {@link Project#activeLayer}
- * @option [settings.applyMatrix=true] {Boolean} controls what value newly
- * created items have their {@link Item#applyMatrix} property set to
- * (Note that not all items can set this to `false`)
- * @option [settings.handleSize=4] {Number} the size of the curve handles
- * when drawing selections
- * @option [settings.hitTolerance=0] {Number} the default tolerance for hit-
- * tests, when no value is specified
- */
+ /**
+ * Gives access to paper's configurable settings.
+ *
+ * @name PaperScope#settings
+ * @type Object
+ *
+ * @option [settings.insertItems=true] {Boolean} controls whether newly
+ * created items are automatically inserted into the scene graph, by
+ * adding them to {@link Project#activeLayer}
+ * @option [settings.applyMatrix=true] {Boolean} controls what value newly
+ * created items have their {@link Item#applyMatrix} property set to
+ * (Note that not all items can set this to `false`)
+ * @option [settings.handleSize=4] {Number} the size of the curve handles
+ * when drawing selections
+ * @option [settings.hitTolerance=0] {Number} the default tolerance for hit-
+ * tests, when no value is specified
+ */
- /**
- * The currently active project.
- *
- * @name PaperScope#project
- * @type Project
- */
+ /**
+ * The currently active project.
+ *
+ * @name PaperScope#project
+ * @type Project
+ */
- /**
- * The list of all open projects within the current Paper.js context.
- *
- * @name PaperScope#projects
- * @type Project[]
- */
+ /**
+ * The list of all open projects within the current Paper.js context.
+ *
+ * @name PaperScope#projects
+ * @type Project[]
+ */
- /**
- * The reference to the active project's view.
- *
- * @bean
- * @type View
- */
- getView: function() {
- var project = this.project;
- return project && project._view;
- },
+ /**
+ * The reference to the active project's view.
+ *
+ * @bean
+ * @type View
+ */
+ getView: function () {
+ var project = this.project;
+ return project && project._view;
+ },
- /**
- * The reference to the active tool.
- *
- * @name PaperScope#tool
- * @property
- * @type Tool
- */
+ /**
+ * The reference to the active tool.
+ *
+ * @name PaperScope#tool
+ * @property
+ * @type Tool
+ */
- /**
- * The list of available tools.
- *
- * @name PaperScope#tools
- * @property
- * @type Tool[]
- */
+ /**
+ * The list of available tools.
+ *
+ * @name PaperScope#tools
+ * @property
+ * @type Tool[]
+ */
- /**
- * A reference to the local scope. This is required, so `paper` will always
- * refer to the local scope, even when calling into it from another scope.
- * `paper.activate();` will have to be called in such a situation.
- *
- * @bean
- * @type PaperScript
- * @private
- */
- getPaper: function() {
- return this;
- },
+ /**
+ * A reference to the local scope. This is required, so `paper` will always
+ * refer to the local scope, even when calling into it from another scope.
+ * `paper.activate();` will have to be called in such a situation.
+ *
+ * @bean
+ * @type PaperScript
+ * @private
+ */
+ getPaper: function () {
+ return this;
+ },
- /**
- * Compiles the PaperScript code into a compiled function and executes it.
- * The compiled function receives all properties of this {@link PaperScope}
- * as arguments, to emulate a global scope with unaffected performance. It
- * also installs global view and tool handlers automatically on the
- * respective objects.
- *
- * @option options.url {String} the url of the source, for source-map
- * debugging
- * @option options.source {String} the source to be used for the source-
- * mapping, in case the code that's passed in has already been mingled.
- *
- * @param {String} code the PaperScript code
- * @param {Object} [options] the compilation options
- */
- execute: function(code, options) {
-/*#*/ if (__options.paperScript) {
- var exports = paper.PaperScript.execute(code, this, options);
- View.updateFocus();
- return exports;
-/*#*/ }
- },
+ /**
+ * Compiles the PaperScript code into a compiled function and executes it.
+ * The compiled function receives all properties of this {@link PaperScope}
+ * as arguments, to emulate a global scope with unaffected performance. It
+ * also installs global view and tool handlers automatically on the
+ * respective objects.
+ *
+ * @option options.url {String} the url of the source, for source-map
+ * debugging
+ * @option options.source {String} the source to be used for the source-
+ * mapping, in case the code that's passed in has already been mingled.
+ *
+ * @param {String} code the PaperScript code
+ * @param {Object} [options] the compilation options
+ */
+ execute: function (code, options) {
+ /*#*/ if (__options.paperScript) {
+ var exports = paper.PaperScript.execute(code, this, options);
+ View.updateFocus();
+ return exports;
+ /*#*/
+ }
+ },
- /**
- * Injects the paper scope into any other given scope. Can be used for
- * example to inject the currently active PaperScope into the window's
- * global scope, to emulate PaperScript-style globally accessible Paper
- * classes and objects.
- *
- * Please note: Using this method may override native constructors
- * (e.g. Path). This may cause problems when using Paper.js in conjunction
- * with other libraries that rely on these constructors. Keep the library
- * scoped if you encounter issues caused by this.
- *
- * @example
- * paper.install(window);
- */
- install: function(scope) {
- // Define project, view and tool as getters that redirect to these
- // values on the PaperScope, so they are kept up to date
- var that = this;
- Base.each(['project', 'view', 'tool'], function(key) {
- Base.define(scope, key, {
- configurable: true,
- get: function() {
- return that[key];
- }
+ /**
+ * Injects the paper scope into any other given scope. Can be used for
+ * example to inject the currently active PaperScope into the window's
+ * global scope, to emulate PaperScript-style globally accessible Paper
+ * classes and objects.
+ *
+ * Please note: Using this method may override native constructors
+ * (e.g. Path). This may cause problems when using Paper.js in conjunction
+ * with other libraries that rely on these constructors. Keep the library
+ * scoped if you encounter issues caused by this.
+ *
+ * @example
+ * paper.install(window);
+ */
+ install: function (scope) {
+ // Define project, view and tool as getters that redirect to these
+ // values on the PaperScope, so they are kept up to date
+ var that = this;
+ Base.each(["project", "view", "tool"], function (key) {
+ Base.define(scope, key, {
+ configurable: true,
+ get: function () {
+ return that[key];
+ },
+ });
});
- });
- // Copy over all fields from this scope to the destination.
- // Do not use Base.each, since we also want to enumerate over
- // fields on PaperScope.prototype, e.g. all classes
- for (var key in this)
+ // Copy over all fields from this scope to the destination.
+ // Do not use Base.each, since we also want to enumerate over
+ // fields on PaperScope.prototype, e.g. all classes
// Exclude all 'hidden' fields
- if (!/^_/.test(key) && this[key])
- scope[key] = this[key];
- },
+ for (var key in this)
+ if (!/^_/.test(key) && this[key]) scope[key] = this[key];
+ },
- /**
- * Sets up an empty project for us. If a canvas is provided, it also creates
- * a {@link View} for it, both linked to this scope.
- *
- * @param {HTMLCanvasElement|String|Size} element the HTML canvas element
- * this scope should be associated with, or an ID string by which to find
- * the element, or the size of the canvas to be created for usage in a web
- * worker.
- */
- setup: function(element) {
- // Make sure this is the active scope, so the created project and view
- // are automatically associated with it.
- paper = this;
- // Create an empty project for the scope.
- this.project = new Project(element);
- // This is needed in PaperScript.load().
- return this;
- },
+ /**
+ * Sets up an empty project for us. If a canvas is provided, it also creates
+ * a {@link View} for it, both linked to this scope.
+ *
+ * @param {HTMLCanvasElement|String|Size} element the HTML canvas element
+ * this scope should be associated with, or an ID string by which to find
+ * the element, or the size of the canvas to be created for usage in a web
+ * worker.
+ */
+ setup: function (element) {
+ // Make sure this is the active scope, so the created project and view
+ // are automatically associated with it.
+ paper = this;
+ // Create an empty project for the scope.
+ this.project = new Project(element);
+ // This is needed in PaperScript.load().
+ return this;
+ },
- createCanvas: function(width, height) {
- return CanvasProvider.getCanvas(width, height);
- },
+ createCanvas: function (width, height) {
+ return CanvasProvider.getCanvas(width, height);
+ },
- /**
- * Activates this PaperScope, so all newly created items will be placed
- * in its active project.
- */
- activate: function() {
- paper = this;
- },
+ /**
+ * Activates this PaperScope, so all newly created items will be placed
+ * in its active project.
+ */
+ activate: function () {
+ paper = this;
+ },
- clear: function() {
- // Remove all projects, views and tools.
- // This also removes the installed event handlers.
- var projects = this.projects,
- tools = this.tools;
- for (var i = projects.length - 1; i >= 0; i--)
- projects[i].remove();
- for (var i = tools.length - 1; i >= 0; i--)
- tools[i].remove();
- },
+ clear: function () {
+ // Remove all projects, views and tools.
+ // This also removes the installed event handlers.
+ var projects = this.projects,
+ tools = this.tools;
+ for (var i = projects.length - 1; i >= 0; i--) projects[i].remove();
+ for (var i = tools.length - 1; i >= 0; i--) tools[i].remove();
+ },
- remove: function() {
- this.clear();
- delete PaperScope._scopes[this._id];
- },
+ remove: function () {
+ this.clear();
+ delete PaperScope._scopes[this._id];
+ },
- statics: new function() {
- // Produces helpers to e.g. check for both 'canvas' and
- // 'data-paper-canvas' attributes:
- function handleAttribute(name) {
- name += 'Attribute';
- return function(el, attr) {
- return el[name](attr) || el[name]('data-paper-' + attr);
- };
- }
+ statics: new (function () {
+ // Produces helpers to e.g. check for both 'canvas' and
+ // 'data-paper-canvas' attributes:
+ function handleAttribute(name) {
+ name += "Attribute";
+ return function (el, attr) {
+ return el[name](attr) || el[name]("data-paper-" + attr);
+ };
+ }
- return /** @lends PaperScope */{
- _scopes: {},
- _id: 0,
+ return /** @lends PaperScope */ {
+ _scopes: {},
+ _id: 0,
- /**
- * Retrieves a PaperScope object with the given scope id.
- *
- * @param id
- * @return {PaperScope}
- */
- get: function(id) {
- return this._scopes[id] || null;
- },
+ /**
+ * Retrieves a PaperScope object with the given scope id.
+ *
+ * @param id
+ * @return {PaperScope}
+ */
+ get: function (id) {
+ return this._scopes[id] || null;
+ },
- getAttribute: handleAttribute('get'),
- hasAttribute: handleAttribute('has')
- };
+ getAttribute: handleAttribute("get"),
+ hasAttribute: handleAttribute("has"),
+ };
+ })(),
}
-});
+);
diff --git a/src/item/Item.js b/src/item/Item.js
index 83b34ed3f4..c598f7a959 100644
--- a/src/item/Item.js
+++ b/src/item/Item.js
@@ -20,273 +20,307 @@
* is unique to their type, but share the underlying properties and functions
* that they inherit from Item.
*/
-var Item = Base.extend(Emitter, /** @lends Item# */{
- statics: /** @lends Item */{
- /**
- * Override Item.extend() to merge the subclass' _serializeFields with
- * the parent class' _serializeFields.
- *
- * @private
- */
- extend: function extend(src) {
- if (src._serializeFields)
- src._serializeFields = Base.set({},
- this.prototype._serializeFields, src._serializeFields);
- return extend.base.apply(this, arguments);
- },
+var Item = Base.extend(
+ Emitter,
+ /** @lends Item# */ {
+ statics: /** @lends Item */ {
+ /**
+ * Override Item.extend() to merge the subclass' _serializeFields with
+ * the parent class' _serializeFields.
+ *
+ * @private
+ */
+ extend: function extend(src) {
+ if (src._serializeFields)
+ src._serializeFields = Base.set(
+ {},
+ this.prototype._serializeFields,
+ src._serializeFields
+ );
+ return extend.base.apply(this, arguments);
+ },
- /**
- * An object constant that can be passed to Item#initialize() to avoid
- * insertion into the scene graph.
- *
- * @private
- */
- NO_INSERT: { insert: false }
- },
+ /**
+ * An object constant that can be passed to Item#initialize() to avoid
+ * insertion into the scene graph.
+ *
+ * @private
+ */
+ NO_INSERT: { insert: false },
+ },
- _class: 'Item',
- _name: null,
- // All items apply their matrix by default.
- // Exceptions are Raster, SymbolItem, Clip and Shape.
- _applyMatrix: true,
- _canApplyMatrix: true,
- _canScaleStroke: false,
- _pivot: null,
- _visible: true,
- _blendMode: 'normal',
- _opacity: 1,
- _locked: false,
- _guide: false,
- _clipMask: false,
- _selection: 0,
- // Controls whether bounds should appear selected when the item is selected.
- // This is only turned off for Group, Layer and PathItem, where it can be
- // selected separately by setting item.bounds.selected = true;
- _selectBounds: true,
- _selectChildren: false,
- // Provide information about fields to be serialized, with their defaults
- // that can be omitted.
- _serializeFields: {
- name: null,
- applyMatrix: null,
- matrix: new Matrix(),
- pivot: null,
- visible: true,
- blendMode: 'normal',
- opacity: 1,
- locked: false,
- guide: false,
- clipMask: false,
- selected: false,
- data: {}
+ _class: "Item",
+ _name: null,
+ // All items apply their matrix by default.
+ // Exceptions are Raster, SymbolItem, Clip and Shape.
+ _applyMatrix: true,
+ _canApplyMatrix: true,
+ _canScaleStroke: false,
+ _pivot: null,
+ _visible: true,
+ _blendMode: "normal",
+ _opacity: 1,
+ _locked: false,
+ _guide: false,
+ _clipMask: false,
+ _selection: 0,
+ // Controls whether bounds should appear selected when the item is selected.
+ // This is only turned off for Group, Layer and PathItem, where it can be
+ // selected separately by setting item.bounds.selected = true;
+ _selectBounds: true,
+ _selectChildren: false,
+ // Provide information about fields to be serialized, with their defaults
+ // that can be omitted.
+ _serializeFields: {
+ name: null,
+ applyMatrix: null,
+ matrix: new Matrix(),
+ pivot: null,
+ visible: true,
+ blendMode: "normal",
+ opacity: 1,
+ locked: false,
+ guide: false,
+ clipMask: false,
+ selected: false,
+ data: {},
+ },
+ // Prioritize `applyMatrix` over `matrix`:
+ _prioritize: ["applyMatrix"],
},
- // Prioritize `applyMatrix` over `matrix`:
- _prioritize: ['applyMatrix']
-},
-new function() { // Injection scope for various item event handlers
- var handlers = ['onMouseDown', 'onMouseUp', 'onMouseDrag', 'onClick',
- 'onDoubleClick', 'onMouseMove', 'onMouseEnter', 'onMouseLeave'];
- return Base.each(handlers,
- function(name) {
- this._events[name] = {
- install: function(type) {
- this.getView()._countItemEvent(type, 1);
- },
+ new (function () {
+ // Injection scope for various item event handlers
+ var handlers = [
+ "onMouseDown",
+ "onMouseUp",
+ "onMouseDrag",
+ "onClick",
+ "onDoubleClick",
+ "onMouseMove",
+ "onMouseEnter",
+ "onMouseLeave",
+ ];
+ return Base.each(
+ handlers,
+ function (name) {
+ this._events[name] = {
+ install: function (type) {
+ this.getView()._countItemEvent(type, 1);
+ },
- uninstall: function(type) {
- this.getView()._countItemEvent(type, -1);
- }
- };
- }, {
- _events: {
- onFrame: {
- install: function() {
- this.getView()._animateItem(this, true);
+ uninstall: function (type) {
+ this.getView()._countItemEvent(type, -1);
+ },
+ };
+ },
+ {
+ _events: {
+ onFrame: {
+ install: function () {
+ this.getView()._animateItem(this, true);
+ },
+
+ uninstall: function () {
+ this.getView()._animateItem(this, false);
+ },
},
- uninstall: function() {
- this.getView()._animateItem(this, false);
- }
+ // Only for external sources, e.g. Raster
+ onLoad: {},
+ onError: {},
+ },
+ statics: {
+ _itemHandlers: handlers,
},
-
- // Only for external sources, e.g. Raster
- onLoad: {},
- onError: {}
- },
- statics: {
- _itemHandlers: handlers
}
- }
- );
-}, /** @lends Item# */{
- initialize: function Item() {
- // Do nothing, but declare it for named constructors.
- },
+ );
+ })(),
+ /** @lends Item# */ {
+ initialize: function Item() {
+ // Do nothing, but declare it for named constructors.
+ },
- /**
- * Private helper for #initialize() that tries setting properties from the
- * passed props object, and apply the point translation to the internal
- * matrix.
- *
- * @param {Object} props the properties to be applied to the item
- * @param {Point} point the point by which to transform the internal matrix
- * @return {Boolean} {@true if the properties were successfully be applied,
- * or if none were provided}
- */
- _initialize: function(props, point) {
- // Define this Item's unique id. But allow the creation of internally
- // used paths with no ids.
- var hasProps = props && Base.isPlainObject(props),
- internal = hasProps && props.internal === true,
- matrix = this._matrix = new Matrix(),
- // Allow setting another project than the currently active one.
- project = hasProps && props.project || paper.project,
- settings = paper.settings;
- this._id = internal ? null : UID.get();
- this._parent = this._index = null;
- // Inherit the applyMatrix setting from settings.applyMatrix
- this._applyMatrix = this._canApplyMatrix && settings.applyMatrix;
- // Handle matrix before everything else, to avoid issues with
- // #addChild() calling _changed() and accessing _matrix already.
- if (point)
- matrix.translate(point);
- matrix._owner = this;
- this._style = new Style(project._currentStyle, this, project);
- // Do not add to the project if it's an internal path, or if
- // props.insert or settings.isnertItems is false.
- if (internal || hasProps && props.insert == false
- || !settings.insertItems && !(hasProps && props.insert === true)) {
- this._setProject(project);
- } else {
- (hasProps && props.parent || project)
- ._insertItem(undefined, this, true); // _created = true
- }
- // Filter out Item.NO_INSERT before _set(), for performance reasons.
- if (hasProps && props !== Item.NO_INSERT) {
- this.set(props, {
- // Filter out these properties as they were handled above:
- internal: true, insert: true, project: true, parent: true
- });
- }
- return hasProps;
- },
+ /**
+ * Private helper for #initialize() that tries setting properties from the
+ * passed props object, and apply the point translation to the internal
+ * matrix.
+ *
+ * @param {Object} props the properties to be applied to the item
+ * @param {Point} point the point by which to transform the internal matrix
+ * @return {Boolean} {@true if the properties were successfully be applied,
+ * or if none were provided}
+ */
+ _initialize: function (props, point) {
+ // Define this Item's unique id. But allow the creation of internally
+ // used paths with no ids.
+ var hasProps = props && Base.isPlainObject(props),
+ internal = hasProps && props.internal === true,
+ matrix = (this._matrix = new Matrix()),
+ // Allow setting another project than the currently active one.
+ project = (hasProps && props.project) || paper.project,
+ settings = paper.settings;
+ this._id = internal ? null : UID.get();
+ this._parent = this._index = null;
+ // Inherit the applyMatrix setting from settings.applyMatrix
+ this._applyMatrix = this._canApplyMatrix && settings.applyMatrix;
+ // Handle matrix before everything else, to avoid issues with
+ // #addChild() calling _changed() and accessing _matrix already.
+ if (point) matrix.translate(point);
+ matrix._owner = this;
+ this._style = new Style(project._currentStyle, this, project);
+ // Do not add to the project if it's an internal path, or if
+ // props.insert or settings.isnertItems is false.
+ if (
+ internal ||
+ (hasProps && props.insert == false) ||
+ (!settings.insertItems && !(hasProps && props.insert === true))
+ ) {
+ this._setProject(project);
+ } else {
+ ((hasProps && props.parent) || project)._insertItem(
+ undefined,
+ this,
+ true
+ ); // _created = true
+ }
+ // Filter out Item.NO_INSERT before _set(), for performance reasons.
+ if (hasProps && props !== Item.NO_INSERT) {
+ this.set(props, {
+ // Filter out these properties as they were handled above:
+ internal: true,
+ insert: true,
+ project: true,
+ parent: true,
+ });
+ }
+ return hasProps;
+ },
- _serialize: function(options, dictionary) {
- var props = {},
- that = this;
-
- function serialize(fields) {
- for (var key in fields) {
- // value is the default value, only serialize if the current
- // value is different from it.
- var value = that[key];
- // Style#leading is a special case, as its default value is
- // dependent on the fontSize. Handle this here separately.
- if (!Base.equals(value, key === 'leading'
- ? fields.fontSize * 1.2 : fields[key])) {
- props[key] = Base.serialize(value, options,
+ _serialize: function (options, dictionary) {
+ var props = {},
+ that = this;
+
+ function serialize(fields) {
+ for (var key in fields) {
+ // value is the default value, only serialize if the current
+ // value is different from it.
+ var value = that[key];
+ // Style#leading is a special case, as its default value is
+ // dependent on the fontSize. Handle this here separately.
+ if (
+ !Base.equals(
+ value,
+ key === "leading"
+ ? fields.fontSize * 1.2
+ : fields[key]
+ )
+ ) {
+ props[key] = Base.serialize(
+ value,
+ options,
// Do not use compact mode for data
- key !== 'data', dictionary);
+ key !== "data",
+ dictionary
+ );
+ }
}
}
- }
- // Serialize fields that this Item subclass defines first
- serialize(this._serializeFields);
- // Serialize style fields, but only if they differ from defaults.
- // Do not serialize styles on Groups and Layers, since they just unify
- // their children's own styles.
- if (!(this instanceof Group))
- serialize(this._style._defaults);
- // There is no compact form for Item serialization, we always keep the
- // class.
- return [ this._class, props ];
- },
+ // Serialize fields that this Item subclass defines first
+ serialize(this._serializeFields);
+ // Serialize style fields, but only if they differ from defaults.
+ // Do not serialize styles on Groups and Layers, since they just unify
+ // their children's own styles.
+ if (!(this instanceof Group)) serialize(this._style._defaults);
+ // There is no compact form for Item serialization, we always keep the
+ // class.
+ return [this._class, props];
+ },
- /**
- * Private notifier that is called whenever a change occurs in this item or
- * its sub-elements, such as Segments, Curves, Styles, etc.
- *
- * @param {ChangeFlag} flags describes what exactly has changed
- */
- _changed: function(flags) {
- var symbol = this._symbol,
- cacheParent = this._parent || symbol,
- project = this._project;
- if (flags & /*#=*/ChangeFlag.GEOMETRY) {
- // Clear cached bounds, position and decomposed matrix whenever
- // geometry changes.
- this._bounds = this._position = this._decomposed = undefined;
- }
- if (flags & /*#=*/ChangeFlag.MATRIX) {
- this._globalMatrix = undefined;
- }
- if (cacheParent
- && (flags & /*#=*/(ChangeFlag.GEOMETRY | ChangeFlag.STROKE))) {
- // Clear cached bounds of all items that this item contributes to.
- // We call this on the parent, since the information is cached on
- // the parent, see getBounds().
- Item._clearBoundsCache(cacheParent);
- }
- if (flags & /*#=*/ChangeFlag.CHILDREN) {
- // Clear cached bounds of all items that this item contributes to.
- // Here we don't call this on the parent, since adding / removing a
- // child triggers this notification on the parent.
- Item._clearBoundsCache(this);
- }
- if (project)
- project._changed(flags, this);
- // If this item is a symbol's definition, notify it of the change too
- if (symbol)
- symbol._changed(flags);
- },
+ /**
+ * Private notifier that is called whenever a change occurs in this item or
+ * its sub-elements, such as Segments, Curves, Styles, etc.
+ *
+ * @param {ChangeFlag} flags describes what exactly has changed
+ */
+ _changed: function (flags) {
+ var symbol = this._symbol,
+ cacheParent = this._parent || symbol,
+ project = this._project;
+ if (flags & /*#=*/ ChangeFlag.GEOMETRY) {
+ // Clear cached bounds, position and decomposed matrix whenever
+ // geometry changes.
+ this._bounds = this._position = this._decomposed = undefined;
+ }
+ if (flags & /*#=*/ ChangeFlag.MATRIX) {
+ this._globalMatrix = undefined;
+ }
+ if (
+ cacheParent &&
+ flags & /*#=*/ (ChangeFlag.GEOMETRY | ChangeFlag.STROKE)
+ ) {
+ // Clear cached bounds of all items that this item contributes to.
+ // We call this on the parent, since the information is cached on
+ // the parent, see getBounds().
+ Item._clearBoundsCache(cacheParent);
+ }
+ if (flags & /*#=*/ ChangeFlag.CHILDREN) {
+ // Clear cached bounds of all items that this item contributes to.
+ // Here we don't call this on the parent, since adding / removing a
+ // child triggers this notification on the parent.
+ Item._clearBoundsCache(this);
+ }
+ if (project) project._changed(flags, this);
+ // If this item is a symbol's definition, notify it of the change too
+ if (symbol) symbol._changed(flags);
+ },
- /**
- * Sets the properties of the passed object literal on this item to the
- * values defined in the object literal, if the item has property of the
- * given name (or a setter defined for it).
- *
- * @name Item#set
- * @function
- * @param {Object} props
- * @return {Item} the item itself
- * @chainable
- *
- * @example {@paperscript}
- * // Setting properties through an object literal
- * var circle = new Path.Circle({
- * center: [80, 50],
- * radius: 35
- * });
- *
- * circle.set({
- * strokeColor: 'red',
- * strokeWidth: 10,
- * fillColor: 'black',
- * selected: true
- * });
- */
+ /**
+ * Sets the properties of the passed object literal on this item to the
+ * values defined in the object literal, if the item has property of the
+ * given name (or a setter defined for it).
+ *
+ * @name Item#set
+ * @function
+ * @param {Object} props
+ * @return {Item} the item itself
+ * @chainable
+ *
+ * @example {@paperscript}
+ * // Setting properties through an object literal
+ * var circle = new Path.Circle({
+ * center: [80, 50],
+ * radius: 35
+ * });
+ *
+ * circle.set({
+ * strokeColor: 'red',
+ * strokeWidth: 10,
+ * fillColor: 'black',
+ * selected: true
+ * });
+ */
- /**
- * The unique id of the item.
- *
- * @bean
- * @type Number
- */
- getId: function() {
- return this._id;
- },
+ /**
+ * The unique id of the item.
+ *
+ * @bean
+ * @type Number
+ */
+ getId: function () {
+ return this._id;
+ },
- /**
- * The class name of the item as a string.
- *
- * @name Item#className
- * @type String
- * @values 'Group', 'Layer', 'Path', 'CompoundPath', 'Shape', 'Raster',
- * 'SymbolItem', 'PointText'
- */
+ /**
+ * The class name of the item as a string.
+ *
+ * @name Item#className
+ * @type String
+ * @values 'Group', 'Layer', 'Path', 'CompoundPath', 'Shape', 'Raster',
+ * 'SymbolItem', 'PointText'
+ */
- /**
+ /**
* The name of the item. If the item has a name, it can be accessed by name
* through its parent's children list.
*
@@ -309,4625 +343,4822 @@ new function() { // Injection scope for various item event handlers
* // The path can be accessed by name:
* group.children['example'].fillColor = 'red';
*/
- getName: function() {
- return this._name;
- },
+ getName: function () {
+ return this._name;
+ },
- setName: function(name) {
- // NOTE: Don't check if the name has changed and bail out if it has not,
- // because setName is used internally also to update internal structures
- // when an item is moved from one parent to another.
-
- // If the item already had a name, remove the reference to it from the
- // parent's children object:
- if (this._name)
- this._removeNamed();
- // See if the name is a simple number, which we cannot support due to
- // the named lookup on the children array.
- if (name === (+name) + '')
- throw new Error(
- 'Names consisting only of numbers are not supported.');
- var owner = this._getOwner();
- if (name && owner) {
- var children = owner._children,
- namedChildren = owner._namedChildren;
- (namedChildren[name] = namedChildren[name] || []).push(this);
- // Only set this item if there isn't one under the same name already
- if (!(name in children))
- children[name] = this;
- }
- this._name = name || undefined;
- this._changed(/*#=*/ChangeFlag.ATTRIBUTE);
- },
+ setName: function (name) {
+ // NOTE: Don't check if the name has changed and bail out if it has not,
+ // because setName is used internally also to update internal structures
+ // when an item is moved from one parent to another.
+
+ // If the item already had a name, remove the reference to it from the
+ // parent's children object:
+ if (this._name) this._removeNamed();
+ // See if the name is a simple number, which we cannot support due to
+ // the named lookup on the children array.
+ if (name === +name + "")
+ throw new Error(
+ "Names consisting only of numbers are not supported."
+ );
+ var owner = this._getOwner();
+ if (name && owner) {
+ var children = owner._children,
+ namedChildren = owner._namedChildren;
+ (namedChildren[name] = namedChildren[name] || []).push(this);
+ // Only set this item if there isn't one under the same name already
+ if (!(name in children)) children[name] = this;
+ }
+ this._name = name || undefined;
+ this._changed(/*#=*/ ChangeFlag.ATTRIBUTE);
+ },
- /**
- * The path style of the item.
- *
- * @bean
- * @name Item#getStyle
- * @type Style
- *
- * @example {@paperscript}
- * // Applying several styles to an item in one go, by passing an object
- * // to its style property:
- * var circle = new Path.Circle({
- * center: [80, 50],
- * radius: 30
- * });
- * circle.style = {
- * fillColor: 'blue',
- * strokeColor: 'red',
- * strokeWidth: 5
- * };
- *
- * @example {@paperscript split=true height=100}
- * // Copying the style of another item:
- * var path = new Path.Circle({
- * center: [50, 50],
- * radius: 30,
- * fillColor: 'red'
- * });
- *
- * var path2 = new Path.Circle({
- * center: new Point(180, 50),
- * radius: 20
- * });
- *
- * // Copy the path style of path:
- * path2.style = path.style;
- *
- * @example {@paperscript}
- * // Applying the same style object to multiple items:
- * var myStyle = {
- * fillColor: 'red',
- * strokeColor: 'blue',
- * strokeWidth: 4
- * };
- *
- * var path = new Path.Circle({
- * center: [50, 50],
- * radius: 30
- * });
- * path.style = myStyle;
- *
- * var path2 = new Path.Circle({
- * center: new Point(150, 50),
- * radius: 20
- * });
- * path2.style = myStyle;
- */
- getStyle: function() {
- return this._style;
- },
+ /**
+ * The path style of the item.
+ *
+ * @bean
+ * @name Item#getStyle
+ * @type Style
+ *
+ * @example {@paperscript}
+ * // Applying several styles to an item in one go, by passing an object
+ * // to its style property:
+ * var circle = new Path.Circle({
+ * center: [80, 50],
+ * radius: 30
+ * });
+ * circle.style = {
+ * fillColor: 'blue',
+ * strokeColor: 'red',
+ * strokeWidth: 5
+ * };
+ *
+ * @example {@paperscript split=true height=100}
+ * // Copying the style of another item:
+ * var path = new Path.Circle({
+ * center: [50, 50],
+ * radius: 30,
+ * fillColor: 'red'
+ * });
+ *
+ * var path2 = new Path.Circle({
+ * center: new Point(180, 50),
+ * radius: 20
+ * });
+ *
+ * // Copy the path style of path:
+ * path2.style = path.style;
+ *
+ * @example {@paperscript}
+ * // Applying the same style object to multiple items:
+ * var myStyle = {
+ * fillColor: 'red',
+ * strokeColor: 'blue',
+ * strokeWidth: 4
+ * };
+ *
+ * var path = new Path.Circle({
+ * center: [50, 50],
+ * radius: 30
+ * });
+ * path.style = myStyle;
+ *
+ * var path2 = new Path.Circle({
+ * center: new Point(150, 50),
+ * radius: 20
+ * });
+ * path2.style = myStyle;
+ */
+ getStyle: function () {
+ return this._style;
+ },
- setStyle: function(style) {
- // Don't access _style directly so Path#getStyle() can be overridden for
- // CompoundPaths.
- this.getStyle().set(style);
- }
-}, Base.each(['locked', 'visible', 'blendMode', 'opacity', 'guide'],
- // Produce getter/setters for properties. We need setters because we want to
- // call _changed() if a property was modified.
- function(name) {
- var part = Base.capitalize(name),
- key = '_' + name,
- flags = {
- // #locked does not change appearance, all others do:
- locked: /*#=*/ChangeFlag.ATTRIBUTE,
- // #visible changes appearance
- visible: /*#=*/(Change.ATTRIBUTE | Change.GEOMETRY)
- };
- this['get' + part] = function() {
- return this[key];
- };
- this['set' + part] = function(value) {
- if (value != this[key]) {
- this[key] = value;
- this._changed(flags[name] || /*#=*/Change.ATTRIBUTE);
- }
- };
+ setStyle: function (style) {
+ // Don't access _style directly so Path#getStyle() can be overridden for
+ // CompoundPaths.
+ this.getStyle().set(style);
+ },
},
-{}), /** @lends Item# */{
- // Enforce creation of beans, as bean getters have hidden parameters.
- // See #getPosition() below.
- beans: true,
+ Base.each(
+ ["locked", "visible", "blendMode", "opacity", "guide"],
+ // Produce getter/setters for properties. We need setters because we want to
+ // call _changed() if a property was modified.
+ function (name) {
+ var part = Base.capitalize(name),
+ key = "_" + name,
+ flags = {
+ // #locked does not change appearance, all others do:
+ locked: /*#=*/ ChangeFlag.ATTRIBUTE,
+ // #visible changes appearance
+ visible: /*#=*/ Change.ATTRIBUTE | Change.GEOMETRY,
+ };
+ this["get" + part] = function () {
+ return this[key];
+ };
+ this["set" + part] = function (value) {
+ if (value != this[key]) {
+ this[key] = value;
+ this._changed(flags[name] || /*#=*/ Change.ATTRIBUTE);
+ }
+ };
+ },
+ {}
+ ),
+ /** @lends Item# */ {
+ // Enforce creation of beans, as bean getters have hidden parameters.
+ // See #getPosition() below.
+ beans: true,
- // NOTE: These properties have their getter / setters produced in the
- // injection scope above.
+ // NOTE: These properties have their getter / setters produced in the
+ // injection scope above.
- /**
- * Specifies whether the item is locked. When set to `true`, item
- * interactions with the mouse are disabled.
- *
- * @name Item#locked
- * @type Boolean
- * @default false
- *
- * @example {@paperscript}
- * var unlockedItem = new Path.Circle({
- * center: view.center - [35, 0],
- * radius: 30,
- * fillColor: 'springgreen',
- * onMouseDown: function() {
- * this.fillColor = Color.random();
- * }
- * });
- *
- * var lockedItem = new Path.Circle({
- * center: view.center + [35, 0],
- * radius: 30,
- * fillColor: 'crimson',
- * locked: true,
- * // This event won't be triggered because the item is locked.
- * onMouseDown: function() {
- * this.fillColor = Color.random();
- * }
- * });
- *
- * new PointText({
- * content: 'Click on both circles to see which one is locked.',
- * point: view.center - [0, 35],
- * justification: 'center'
- * });
- */
+ /**
+ * Specifies whether the item is locked. When set to `true`, item
+ * interactions with the mouse are disabled.
+ *
+ * @name Item#locked
+ * @type Boolean
+ * @default false
+ *
+ * @example {@paperscript}
+ * var unlockedItem = new Path.Circle({
+ * center: view.center - [35, 0],
+ * radius: 30,
+ * fillColor: 'springgreen',
+ * onMouseDown: function() {
+ * this.fillColor = Color.random();
+ * }
+ * });
+ *
+ * var lockedItem = new Path.Circle({
+ * center: view.center + [35, 0],
+ * radius: 30,
+ * fillColor: 'crimson',
+ * locked: true,
+ * // This event won't be triggered because the item is locked.
+ * onMouseDown: function() {
+ * this.fillColor = Color.random();
+ * }
+ * });
+ *
+ * new PointText({
+ * content: 'Click on both circles to see which one is locked.',
+ * point: view.center - [0, 35],
+ * justification: 'center'
+ * });
+ */
- /**
- * Specifies whether the item is visible. When set to `false`, the item
- * won't be drawn.
- *
- * @name Item#visible
- * @type Boolean
- * @default true
- *
- * @example {@paperscript}
- * // Hiding an item:
- * var path = new Path.Circle({
- * center: [50, 50],
- * radius: 20,
- * fillColor: 'red'
- * });
- *
- * // Hide the path:
- * path.visible = false;
- */
+ /**
+ * Specifies whether the item is visible. When set to `false`, the item
+ * won't be drawn.
+ *
+ * @name Item#visible
+ * @type Boolean
+ * @default true
+ *
+ * @example {@paperscript}
+ * // Hiding an item:
+ * var path = new Path.Circle({
+ * center: [50, 50],
+ * radius: 20,
+ * fillColor: 'red'
+ * });
+ *
+ * // Hide the path:
+ * path.visible = false;
+ */
- /**
- * The blend mode with which the item is composited onto the canvas. Both
- * the standard canvas compositing modes, as well as the new CSS blend modes
- * are supported. If blend-modes cannot be rendered natively, they are
- * emulated. Be aware that emulation can have an impact on performance.
- *
- * @name Item#blendMode
- * @type String
- * @values 'normal', 'multiply', 'screen', 'overlay', 'soft-light', 'hard-
- * light', 'color-dodge', 'color-burn', 'darken', 'lighten',
- * 'difference', 'exclusion', 'hue', 'saturation', 'luminosity',
- * 'color', 'add', 'subtract', 'average', 'pin-light', 'negation',
- * 'source-over', 'source-in', 'source-out', 'source-atop',
- * 'destination-over', 'destination-in', 'destination-out',
- * 'destination-atop', 'lighter', 'darker', 'copy', 'xor'
- * @default 'normal'
- *
- * @example {@paperscript}
- * // Setting an item's blend mode:
- *
- * // Create a white rectangle in the background
- * // with the same dimensions as the view:
- * var background = new Path.Rectangle(view.bounds);
- * background.fillColor = 'white';
- *
- * var circle = new Path.Circle({
- * center: [80, 50],
- * radius: 35,
- * fillColor: 'red'
- * });
- *
- * var circle2 = new Path.Circle({
- * center: new Point(120, 50),
- * radius: 35,
- * fillColor: 'blue'
- * });
- *
- * // Set the blend mode of circle2:
- * circle2.blendMode = 'multiply';
- */
+ /**
+ * The blend mode with which the item is composited onto the canvas. Both
+ * the standard canvas compositing modes, as well as the new CSS blend modes
+ * are supported. If blend-modes cannot be rendered natively, they are
+ * emulated. Be aware that emulation can have an impact on performance.
+ *
+ * @name Item#blendMode
+ * @type String
+ * @values 'normal', 'multiply', 'screen', 'overlay', 'soft-light', 'hard-
+ * light', 'color-dodge', 'color-burn', 'darken', 'lighten',
+ * 'difference', 'exclusion', 'hue', 'saturation', 'luminosity',
+ * 'color', 'add', 'subtract', 'average', 'pin-light', 'negation',
+ * 'source-over', 'source-in', 'source-out', 'source-atop',
+ * 'destination-over', 'destination-in', 'destination-out',
+ * 'destination-atop', 'lighter', 'darker', 'copy', 'xor'
+ * @default 'normal'
+ *
+ * @example {@paperscript}
+ * // Setting an item's blend mode:
+ *
+ * // Create a white rectangle in the background
+ * // with the same dimensions as the view:
+ * var background = new Path.Rectangle(view.bounds);
+ * background.fillColor = 'white';
+ *
+ * var circle = new Path.Circle({
+ * center: [80, 50],
+ * radius: 35,
+ * fillColor: 'red'
+ * });
+ *
+ * var circle2 = new Path.Circle({
+ * center: new Point(120, 50),
+ * radius: 35,
+ * fillColor: 'blue'
+ * });
+ *
+ * // Set the blend mode of circle2:
+ * circle2.blendMode = 'multiply';
+ */
- /**
- * The opacity of the item as a value between `0` and `1`.
- *
- * @name Item#opacity
- * @type Number
- * @default 1
- *
- * @example {@paperscript}
- * // Making an item 50% transparent:
- * var circle = new Path.Circle({
- * center: [80, 50],
- * radius: 35,
- * fillColor: 'red'
- * });
- *
- * var circle2 = new Path.Circle({
- * center: new Point(120, 50),
- * radius: 35,
- * fillColor: 'blue',
- * strokeColor: 'green',
- * strokeWidth: 10
- * });
- *
- * // Make circle2 50% transparent:
- * circle2.opacity = 0.5;
- */
+ /**
+ * The opacity of the item as a value between `0` and `1`.
+ *
+ * @name Item#opacity
+ * @type Number
+ * @default 1
+ *
+ * @example {@paperscript}
+ * // Making an item 50% transparent:
+ * var circle = new Path.Circle({
+ * center: [80, 50],
+ * radius: 35,
+ * fillColor: 'red'
+ * });
+ *
+ * var circle2 = new Path.Circle({
+ * center: new Point(120, 50),
+ * radius: 35,
+ * fillColor: 'blue',
+ * strokeColor: 'green',
+ * strokeWidth: 10
+ * });
+ *
+ * // Make circle2 50% transparent:
+ * circle2.opacity = 0.5;
+ */
- // TODO: Implement guides
- /**
- * Specifies whether the item functions as a guide. When set to `true`, the
- * item will be drawn at the end as a guide.
- *
- * @name Item#guide
- * @type Boolean
- * @default true
- * @ignore
- */
+ // TODO: Implement guides
+ /**
+ * Specifies whether the item functions as a guide. When set to `true`, the
+ * item will be drawn at the end as a guide.
+ *
+ * @name Item#guide
+ * @type Boolean
+ * @default true
+ * @ignore
+ */
- getSelection: function() {
- return this._selection;
- },
+ getSelection: function () {
+ return this._selection;
+ },
- setSelection: function(selection) {
- if (selection !== this._selection) {
- this._selection = selection;
- var project = this._project;
- if (project) {
- project._updateSelection(this);
- this._changed(/*#=*/Change.ATTRIBUTE);
+ setSelection: function (selection) {
+ if (selection !== this._selection) {
+ this._selection = selection;
+ var project = this._project;
+ if (project) {
+ project._updateSelection(this);
+ this._changed(/*#=*/ Change.ATTRIBUTE);
+ }
}
- }
- },
-
- _changeSelection: function(flag, selected) {
- var selection = this._selection;
- this.setSelection(selected ? selection | flag : selection & ~flag);
- },
-
- /**
- * Specifies whether the item is selected. This will also return `true` for
- * {@link Group} items if they are partially selected, e.g. groups
- * containing selected or partially selected paths.
- *
- * 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.
- *
- * @bean
- * @type Boolean
- * @default false
- * @see Project#selectedItems
- * @see Segment#selected
- * @see Curve#selected
- * @see Point#selected
- *
- * @example {@paperscript}
- * // Selecting an item:
- * var path = new Path.Circle({
- * center: [80, 50],
- * radius: 35
- * });
- * path.selected = true; // Select the path
- */
- isSelected: function() {
- if (this._selectChildren) {
- var children = this._children;
- for (var i = 0, l = children.length; i < l; i++)
- if (children[i].isSelected())
- return true;
- }
- return !!(this._selection & /*#=*/ItemSelection.ITEM);
- },
-
- setSelected: function(selected) {
- if (this._selectChildren) {
- var children = this._children;
- for (var i = 0, l = children.length; i < l; i++)
- children[i].setSelected(selected);
- }
- this._changeSelection(/*#=*/ItemSelection.ITEM, selected);
- },
+ },
- isFullySelected: function() {
- var children = this._children,
- selected = !!(this._selection & /*#=*/ItemSelection.ITEM);
- if (children && selected) {
- for (var i = 0, l = children.length; i < l; i++)
- if (!children[i].isFullySelected())
- return false;
- return true;
- }
- // If there are no children, this is the same as #selected
- return selected;
- },
+ _changeSelection: function (flag, selected) {
+ var selection = this._selection;
+ this.setSelection(selected ? selection | flag : selection & ~flag);
+ },
- setFullySelected: function(selected) {
- var children = this._children;
- if (children) {
- for (var i = 0, l = children.length; i < l; i++)
- children[i].setFullySelected(selected);
- }
- this._changeSelection(/*#=*/ItemSelection.ITEM, selected);
- },
+ /**
+ * Specifies whether the item is selected. This will also return `true` for
+ * {@link Group} items if they are partially selected, e.g. groups
+ * containing selected or partially selected paths.
+ *
+ * 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.
+ *
+ * @bean
+ * @type Boolean
+ * @default false
+ * @see Project#selectedItems
+ * @see Segment#selected
+ * @see Curve#selected
+ * @see Point#selected
+ *
+ * @example {@paperscript}
+ * // Selecting an item:
+ * var path = new Path.Circle({
+ * center: [80, 50],
+ * radius: 35
+ * });
+ * path.selected = true; // Select the path
+ */
+ isSelected: function () {
+ if (this._selectChildren) {
+ var children = this._children;
+ for (var i = 0, l = children.length; i < l; i++)
+ if (children[i].isSelected()) return true;
+ }
+ return !!(this._selection & /*#=*/ ItemSelection.ITEM);
+ },
- /**
- * Specifies whether the item defines a clip mask. This can only be set on
- * paths and compound paths, and only if the item is already contained
- * within a clipping group.
- *
- * @bean
- * @type Boolean
- * @default false
- */
- isClipMask: function() {
- return this._clipMask;
- },
+ setSelected: function (selected) {
+ if (this._selectChildren) {
+ var children = this._children;
+ for (var i = 0, l = children.length; i < l; i++)
+ children[i].setSelected(selected);
+ }
+ this._changeSelection(/*#=*/ ItemSelection.ITEM, selected);
+ },
- setClipMask: function(clipMask) {
- // On-the-fly conversion to boolean:
- if (this._clipMask != (clipMask = !!clipMask)) {
- this._clipMask = clipMask;
- if (clipMask) {
- this.setFillColor(null);
- this.setStrokeColor(null);
+ isFullySelected: function () {
+ var children = this._children,
+ selected = !!(this._selection & /*#=*/ ItemSelection.ITEM);
+ if (children && selected) {
+ for (var i = 0, l = children.length; i < l; i++)
+ if (!children[i].isFullySelected()) return false;
+ return true;
}
- this._changed(/*#=*/Change.ATTRIBUTE);
- // Tell the parent the clipping mask has changed
- if (this._parent)
- this._parent._changed(/*#=*/ChangeFlag.CLIPPING);
- }
- },
+ // If there are no children, this is the same as #selected
+ return selected;
+ },
- // TODO: get/setIsolated (print specific feature)
- // TODO: get/setKnockout (print specific feature)
- // TODO: get/setAlphaIsShape
+ setFullySelected: function (selected) {
+ var children = this._children;
+ if (children) {
+ for (var i = 0, l = children.length; i < l; i++)
+ children[i].setFullySelected(selected);
+ }
+ this._changeSelection(/*#=*/ ItemSelection.ITEM, selected);
+ },
- /**
- * A plain javascript object which can be used to store
- * arbitrary data on the item.
- *
- * @bean
- * @type Object
- *
- * @example
- * var path = new Path();
- * path.data.remember = 'milk';
- *
- * @example
- * var path = new Path();
- * path.data.malcolm = new Point(20, 30);
- * console.log(path.data.malcolm.x); // 20
- *
- * @example
- * var path = new Path();
- * path.data = {
- * home: 'Omicron Theta',
- * found: 2338,
- * pets: ['Spot']
- * };
- * console.log(path.data.pets.length); // 1
- *
- * @example
- * var path = new Path({
- * data: {
- * home: 'Omicron Theta',
- * found: 2338,
- * pets: ['Spot']
- * }
- * });
- * console.log(path.data.pets.length); // 1
- */
- getData: function() {
- if (!this._data)
- this._data = {};
- return this._data;
- },
+ /**
+ * Specifies whether the item defines a clip mask. This can only be set on
+ * paths and compound paths, and only if the item is already contained
+ * within a clipping group.
+ *
+ * @bean
+ * @type Boolean
+ * @default false
+ */
+ isClipMask: function () {
+ return this._clipMask;
+ },
- setData: function(data) {
- this._data = data;
- },
+ setClipMask: function (clipMask) {
+ // On-the-fly conversion to boolean:
+ if (this._clipMask != (clipMask = !!clipMask)) {
+ this._clipMask = clipMask;
+ if (clipMask) {
+ this.setFillColor(null);
+ this.setStrokeColor(null);
+ }
+ this._changed(/*#=*/ Change.ATTRIBUTE);
+ // Tell the parent the clipping mask has changed
+ if (this._parent)
+ this._parent._changed(/*#=*/ ChangeFlag.CLIPPING);
+ }
+ },
- /**
- * {@grouptitle Position and Bounding Boxes}
- *
- * The item's position within the parent item's coordinate system. By
- * default, this is the {@link Rectangle#center} of the item's
- * {@link #bounds} rectangle.
- *
- * @bean
- * @type Point
- *
- * @example {@paperscript}
- * // Changing the position of a path:
- *
- * // Create a circle at position { x: 10, y: 10 }
- * var circle = new Path.Circle({
- * center: new Point(10, 10),
- * radius: 10,
- * fillColor: 'red'
- * });
- *
- * // Move the circle to { x: 20, y: 20 }
- * circle.position = new Point(20, 20);
- *
- * // Move the circle 100 points to the right and 50 points down
- * circle.position += new Point(100, 50);
- *
- * @example {@paperscript split=true height=100}
- * // Changing the x coordinate of an item's position:
- *
- * // Create a circle at position { x: 20, y: 20 }
- * var circle = new Path.Circle({
- * center: new Point(20, 20),
- * radius: 10,
- * fillColor: 'red'
- * });
- *
- * // Move the circle 100 points to the right
- * circle.position.x += 100;
- */
- getPosition: function(_dontLink) {
- // Cache position value.
- // Pass true for _dontLink in getCenter(), so receive back a normal point
- var ctor = _dontLink ? Point : LinkedPoint;
- // Do not cache LinkedPoints directly, since we would not be able to
- // use them to calculate the difference in #setPosition, as when it is
- // modified, it would hold new values already and only then cause the
- // calling of #setPosition.
- var position = this._position ||
- (this._position = this._getPositionFromBounds());
- return new ctor(position.x, position.y, this, 'setPosition');
- },
+ // TODO: get/setIsolated (print specific feature)
+ // TODO: get/setKnockout (print specific feature)
+ // TODO: get/setAlphaIsShape
- setPosition: function(/* point */) {
- // Calculate the distance to the current position, by which to
- // translate the item. Pass true for _dontLink, as we do not need a
- // LinkedPoint to simply calculate this distance.
- this.translate(Point.read(arguments).subtract(this.getPosition(true)));
- },
+ /**
+ * A plain javascript object which can be used to store
+ * arbitrary data on the item.
+ *
+ * @bean
+ * @type Object
+ *
+ * @example
+ * var path = new Path();
+ * path.data.remember = 'milk';
+ *
+ * @example
+ * var path = new Path();
+ * path.data.malcolm = new Point(20, 30);
+ * console.log(path.data.malcolm.x); // 20
+ *
+ * @example
+ * var path = new Path();
+ * path.data = {
+ * home: 'Omicron Theta',
+ * found: 2338,
+ * pets: ['Spot']
+ * };
+ * console.log(path.data.pets.length); // 1
+ *
+ * @example
+ * var path = new Path({
+ * data: {
+ * home: 'Omicron Theta',
+ * found: 2338,
+ * pets: ['Spot']
+ * }
+ * });
+ * console.log(path.data.pets.length); // 1
+ */
+ getData: function () {
+ if (!this._data) this._data = {};
+ return this._data;
+ },
- /**
- * Internal method used to calculate position either from pivot point or
- * bounds.
- * @param {Rectangle} bounds if provided, these bounds are used instead of
- * calling getBounds()
- * @return {Point} the transformed pivot point or the center of the bounds
- * @private
- */
- _getPositionFromBounds: function(bounds) {
- // If an pivot point is provided, use it to determine position
- // based on the matrix. Otherwise use the center of the bounds.
- return this._pivot
+ setData: function (data) {
+ this._data = data;
+ },
+
+ /**
+ * {@grouptitle Position and Bounding Boxes}
+ *
+ * The item's position within the parent item's coordinate system. By
+ * default, this is the {@link Rectangle#center} of the item's
+ * {@link #bounds} rectangle.
+ *
+ * @bean
+ * @type Point
+ *
+ * @example {@paperscript}
+ * // Changing the position of a path:
+ *
+ * // Create a circle at position { x: 10, y: 10 }
+ * var circle = new Path.Circle({
+ * center: new Point(10, 10),
+ * radius: 10,
+ * fillColor: 'red'
+ * });
+ *
+ * // Move the circle to { x: 20, y: 20 }
+ * circle.position = new Point(20, 20);
+ *
+ * // Move the circle 100 points to the right and 50 points down
+ * circle.position += new Point(100, 50);
+ *
+ * @example {@paperscript split=true height=100}
+ * // Changing the x coordinate of an item's position:
+ *
+ * // Create a circle at position { x: 20, y: 20 }
+ * var circle = new Path.Circle({
+ * center: new Point(20, 20),
+ * radius: 10,
+ * fillColor: 'red'
+ * });
+ *
+ * // Move the circle 100 points to the right
+ * circle.position.x += 100;
+ */
+ getPosition: function (_dontLink) {
+ // Cache position value.
+ // Pass true for _dontLink in getCenter(), so receive back a normal point
+ var ctor = _dontLink ? Point : LinkedPoint;
+ // Do not cache LinkedPoints directly, since we would not be able to
+ // use them to calculate the difference in #setPosition, as when it is
+ // modified, it would hold new values already and only then cause the
+ // calling of #setPosition.
+ var position =
+ this._position ||
+ (this._position = this._getPositionFromBounds());
+ return new ctor(position.x, position.y, this, "setPosition");
+ },
+
+ setPosition: function (/* point */) {
+ // Calculate the distance to the current position, by which to
+ // translate the item. Pass true for _dontLink, as we do not need a
+ // LinkedPoint to simply calculate this distance.
+ this.translate(
+ Point.read(arguments).subtract(this.getPosition(true))
+ );
+ },
+
+ /**
+ * Internal method used to calculate position either from pivot point or
+ * bounds.
+ * @param {Rectangle} bounds if provided, these bounds are used instead of
+ * calling getBounds()
+ * @return {Point} the transformed pivot point or the center of the bounds
+ * @private
+ */
+ _getPositionFromBounds: function (bounds) {
+ // If an pivot point is provided, use it to determine position
+ // based on the matrix. Otherwise use the center of the bounds.
+ return this._pivot
? this._matrix._transformPoint(this._pivot)
: (bounds || this.getBounds()).getCenter(true);
- },
+ },
- /**
- * The item's pivot point specified in the item coordinate system, defining
- * the point around which all transformations are hinging. This is also the
- * reference point for {@link #position}. By default, it is set to `null`,
- * meaning the {@link Rectangle#center} of the item's {@link #bounds}
- * rectangle is used as pivot.
- *
- * @bean
- * @type Point
- * @default null
- */
- getPivot: function() {
- var pivot = this._pivot;
- return pivot
- ? new LinkedPoint(pivot.x, pivot.y, this, 'setPivot')
+ /**
+ * The item's pivot point specified in the item coordinate system, defining
+ * the point around which all transformations are hinging. This is also the
+ * reference point for {@link #position}. By default, it is set to `null`,
+ * meaning the {@link Rectangle#center} of the item's {@link #bounds}
+ * rectangle is used as pivot.
+ *
+ * @bean
+ * @type Point
+ * @default null
+ */
+ getPivot: function () {
+ var pivot = this._pivot;
+ return pivot
+ ? new LinkedPoint(pivot.x, pivot.y, this, "setPivot")
: null;
- },
-
- setPivot: function(/* point */) {
- // Clone existing points since we're caching internally.
- this._pivot = Point.read(arguments, 0, { clone: true, readNull: true });
- // No need for _changed() since the only thing this affects is _position
- this._position = undefined;
- }
-}, Base.each({ // Produce getters for bounds properties:
- getStrokeBounds: { stroke: true },
- getHandleBounds: { handle: true },
- getInternalBounds: { internal: true }
- },
- function(options, key) {
- this[key] = function(matrix) {
- return this.getBounds(matrix, options);
- };
- },
-/** @lends Item# */{
- // Enforce creation of beans, as bean getters have hidden parameters.
- // See _matrix parameter above.
- beans: true,
-
- getBounds: function(matrix, options) {
- var hasMatrix = options || matrix instanceof Matrix,
- opts = Base.set({}, hasMatrix ? options : matrix,
- this._boundsOptions);
- // We can only cache the bounds if the path uses stroke-scaling, or if
- // no stroke is involved in the calculation of the bounds.
- // When strokeScaling is false, the bounds are affected by the zoom
- // level of the view, hence we can't cache.
- // TODO: Look more into handling of stroke-scaling, e.g. on groups with
- // some children that have strokeScaling, as well as SymbolItem with
- // SymbolDefinition that have strokeScaling!
- // TODO: Once that is resolved, we should be able to turn off
- // opts.stroke if a resolved item definition does not have a stroke,
- // allowing the code to share caches between #strokeBounds and #bounds.
- if (!opts.stroke || this.getStrokeScaling())
- opts.cacheItem = this;
- // If we're caching bounds, pass on this item as cacheItem, so
- // the children can setup _boundsCache structures for it.
- var rect = this._getCachedBounds(hasMatrix && matrix, opts).rect;
- // If we're returning '#bounds', create a LinkedRectangle that uses
- // the setBounds() setter to update the Item whenever the bounds are
- // changed:
- return !arguments.length
- ? new LinkedRectangle(rect.x, rect.y, rect.width, rect.height,
- this, 'setBounds')
- : rect;
- },
-
- setBounds: function(/* rect */) {
- var rect = Rectangle.read(arguments),
- bounds = this.getBounds(),
- _matrix = this._matrix,
- matrix = new Matrix(),
- center = rect.getCenter();
- // Read this from bottom to top:
- // Translate to new center:
- matrix.translate(center);
- // Scale to new Size, if size changes and avoid divisions by 0:
- if (rect.width != bounds.width || rect.height != bounds.height) {
- // If a previous transformation resulted in a non-invertible matrix,
- // Restore to the last revertible matrix stored in _backup, and get
- // the bounds again. That way, we can prevent collapsing to 0-size.
- if (!_matrix.isInvertible()) {
- _matrix.set(_matrix._backup
- || new Matrix().translate(_matrix.getTranslation()));
- bounds = this.getBounds();
- }
- matrix.scale(
- bounds.width !== 0 ? rect.width / bounds.width : 0,
- bounds.height !== 0 ? rect.height / bounds.height : 0);
- }
- // Translate to bounds center:
- center = bounds.getCenter();
- matrix.translate(-center.x, -center.y);
- // Now execute the transformation
- this.transform(matrix);
- },
+ },
- /**
- * Protected method used in all the bounds getters. It loops through all the
- * children, gets their bounds and finds the bounds around all of them.
- * Subclasses override it to define calculations for the various required
- * bounding types.
- */
- _getBounds: function(matrix, options) {
- // NOTE: We cannot cache these results here, since we do not get
- // _changed() notifications here for changing geometry in children.
- // But cacheName is used in sub-classes such as SymbolItem and Raster.
- var children = this._children;
- // TODO: What to return if nothing is defined, e.g. empty Groups?
- // Scriptographer behaves weirdly then too.
- if (!children || !children.length)
- return new Rectangle();
- // Call _updateBoundsCache() even when the group only holds empty /
- // invisible items), so future changes in these items will cause right
- // handling of _boundsCache.
- Item._updateBoundsCache(this, options.cacheItem);
- return Item._getBounds(children, matrix, options);
+ setPivot: function (/* point */) {
+ // Clone existing points since we're caching internally.
+ this._pivot = Point.read(arguments, 0, {
+ clone: true,
+ readNull: true,
+ });
+ // No need for _changed() since the only thing this affects is _position
+ this._position = undefined;
+ },
},
+ Base.each(
+ {
+ // Produce getters for bounds properties:
+ getStrokeBounds: { stroke: true },
+ getHandleBounds: { handle: true },
+ getInternalBounds: { internal: true },
+ },
+ function (options, key) {
+ this[key] = function (matrix) {
+ return this.getBounds(matrix, options);
+ };
+ },
+ /** @lends Item# */ {
+ // Enforce creation of beans, as bean getters have hidden parameters.
+ // See _matrix parameter above.
+ beans: true,
+
+ getBounds: function (matrix, options) {
+ var hasMatrix = options || matrix instanceof Matrix,
+ opts = Base.set(
+ {},
+ hasMatrix ? options : matrix,
+ this._boundsOptions
+ );
+ // We can only cache the bounds if the path uses stroke-scaling, or if
+ // no stroke is involved in the calculation of the bounds.
+ // When strokeScaling is false, the bounds are affected by the zoom
+ // level of the view, hence we can't cache.
+ // TODO: Look more into handling of stroke-scaling, e.g. on groups with
+ // some children that have strokeScaling, as well as SymbolItem with
+ // SymbolDefinition that have strokeScaling!
+ // TODO: Once that is resolved, we should be able to turn off
+ // opts.stroke if a resolved item definition does not have a stroke,
+ // allowing the code to share caches between #strokeBounds and #bounds.
+ if (!opts.stroke || this.getStrokeScaling())
+ opts.cacheItem = this;
+ // If we're caching bounds, pass on this item as cacheItem, so
+ // the children can setup _boundsCache structures for it.
+ var rect = this._getCachedBounds(
+ hasMatrix && matrix,
+ opts
+ ).rect;
+ // If we're returning '#bounds', create a LinkedRectangle that uses
+ // the setBounds() setter to update the Item whenever the bounds are
+ // changed:
+ return !arguments.length
+ ? new LinkedRectangle(
+ rect.x,
+ rect.y,
+ rect.width,
+ rect.height,
+ this,
+ "setBounds"
+ )
+ : rect;
+ },
- _getBoundsCacheKey: function(options, internal) {
- return [
- options.stroke ? 1 : 0,
- options.handle ? 1 : 0,
- internal ? 1 : 0
- ].join('');
- },
+ setBounds: function (/* rect */) {
+ var rect = Rectangle.read(arguments),
+ bounds = this.getBounds(),
+ _matrix = this._matrix,
+ matrix = new Matrix(),
+ center = rect.getCenter();
+ // Read this from bottom to top:
+ // Translate to new center:
+ matrix.translate(center);
+ // Scale to new Size, if size changes and avoid divisions by 0:
+ if (
+ rect.width != bounds.width ||
+ rect.height != bounds.height
+ ) {
+ // If a previous transformation resulted in a non-invertible matrix,
+ // Restore to the last revertible matrix stored in _backup, and get
+ // the bounds again. That way, we can prevent collapsing to 0-size.
+ if (!_matrix.isInvertible()) {
+ _matrix.set(
+ _matrix._backup ||
+ new Matrix().translate(_matrix.getTranslation())
+ );
+ bounds = this.getBounds();
+ }
+ matrix.scale(
+ bounds.width !== 0 ? rect.width / bounds.width : 0,
+ bounds.height !== 0 ? rect.height / bounds.height : 0
+ );
+ }
+ // Translate to bounds center:
+ center = bounds.getCenter();
+ matrix.translate(-center.x, -center.y);
+ // Now execute the transformation
+ this.transform(matrix);
+ },
- /**
- * Private method that deals with the calling of _getBounds, recursive
- * matrix concatenation and handles all the complicated caching mechanisms.
- */
- _getCachedBounds: function(matrix, options, noInternal) {
- // See if we can cache these bounds. We only cache the bounds
- // transformed with the internally stored _matrix, (the default if no
- // matrix is passed).
- matrix = matrix && matrix._orNullIfIdentity();
- // Do not transform by the internal matrix for internal, untransformed
- // bounds.
- var internal = options.internal && !noInternal,
- cacheItem = options.cacheItem,
- _matrix = internal ? null : this._matrix._orNullIfIdentity(),
- // Create a key for caching, reflecting all bounds options.
- cacheKey = cacheItem && (!matrix || matrix.equals(_matrix))
- && this._getBoundsCacheKey(options, internal),
- bounds = this._bounds;
- // NOTE: This needs to happen before returning cached values, since even
- // then, _boundsCache needs to be kept up-to-date.
- Item._updateBoundsCache(this._parent || this._symbol, cacheItem);
- if (cacheKey && bounds && cacheKey in bounds) {
- var cached = bounds[cacheKey];
- return {
- rect: cached.rect.clone(),
- nonscaling: cached.nonscaling
- };
- }
- var res = this._getBounds(matrix || _matrix, options),
- // Support two versions of _getBounds(): One that directly returns a
- // Rectangle, and one that returns a bounds object with nonscaling.
- rect = res.rect || res,
- style = this._style,
- nonscaling = res.nonscaling || style.hasStroke()
- && !style.getStrokeScaling();
- // If we can cache the result, update the _bounds cache structure
- // before returning
- if (cacheKey) {
- if (!bounds) {
- this._bounds = bounds = {};
- }
- var cached = bounds[cacheKey] = {
- rect: rect.clone(),
- nonscaling: nonscaling,
- // Mark as internal, so Item#transform() won't transform it
- internal: internal
- };
- }
- return {
- rect: rect,
- nonscaling: nonscaling
- };
- },
+ /**
+ * Protected method used in all the bounds getters. It loops through all the
+ * children, gets their bounds and finds the bounds around all of them.
+ * Subclasses override it to define calculations for the various required
+ * bounding types.
+ */
+ _getBounds: function (matrix, options) {
+ // NOTE: We cannot cache these results here, since we do not get
+ // _changed() notifications here for changing geometry in children.
+ // But cacheName is used in sub-classes such as SymbolItem and Raster.
+ var children = this._children;
+ // TODO: What to return if nothing is defined, e.g. empty Groups?
+ // Scriptographer behaves weirdly then too.
+ if (!children || !children.length) return new Rectangle();
+ // Call _updateBoundsCache() even when the group only holds empty /
+ // invisible items), so future changes in these items will cause right
+ // handling of _boundsCache.
+ Item._updateBoundsCache(this, options.cacheItem);
+ return Item._getBounds(children, matrix, options);
+ },
- /**
- * Returns to correct matrix to use to transform stroke related geometries
- * when calculating bounds: the item's matrix if {@link #strokeScaling} is
- * `true`, otherwise the parent's inverted view matrix. The returned matrix
- * is always shiftless, meaning its translation vector is reset to zero.
- */
- _getStrokeMatrix: function(matrix, options) {
- var parent = this.getStrokeScaling() ? null
- : options && options.internal ? this
- : this._parent || this._symbol && this._symbol._item,
- mx = parent ? parent.getViewMatrix().invert() : matrix;
- return mx && mx._shiftless();
- },
+ _getBoundsCacheKey: function (options, internal) {
+ return [
+ options.stroke ? 1 : 0,
+ options.handle ? 1 : 0,
+ internal ? 1 : 0,
+ ].join("");
+ },
- statics: /** @lends Item */{
- /**
- * Set up a boundsCache structure that keeps track of items that keep
- * cached bounds that depend on this item. We store this in the parent,
- * for multiple reasons:
- * The parent receives CHILDREN change notifications for when its
- * children are added or removed and can thus clear the cache, and we
- * save a lot of memory, e.g. when grouping 100 items and asking the
- * group for its bounds. If stored on the children, we would have 100
- * times the same structure.
- */
- _updateBoundsCache: function(parent, item) {
- if (parent && item) {
- // Set-up the parent's boundsCache structure if it does not
- // exist yet and add the item to it.
- var id = item._id,
- ref = parent._boundsCache = parent._boundsCache || {
- // Use a hash-table for ids and an array for the list,
- // so we can keep track of items that were added already
- ids: {},
- list: []
+ /**
+ * Private method that deals with the calling of _getBounds, recursive
+ * matrix concatenation and handles all the complicated caching mechanisms.
+ */
+ _getCachedBounds: function (matrix, options, noInternal) {
+ // See if we can cache these bounds. We only cache the bounds
+ // transformed with the internally stored _matrix, (the default if no
+ // matrix is passed).
+ matrix = matrix && matrix._orNullIfIdentity();
+ // Do not transform by the internal matrix for internal, untransformed
+ // bounds.
+ var internal = options.internal && !noInternal,
+ cacheItem = options.cacheItem,
+ _matrix = internal
+ ? null
+ : this._matrix._orNullIfIdentity(),
+ // Create a key for caching, reflecting all bounds options.
+ cacheKey =
+ cacheItem &&
+ (!matrix || matrix.equals(_matrix)) &&
+ this._getBoundsCacheKey(options, internal),
+ bounds = this._bounds;
+ // NOTE: This needs to happen before returning cached values, since even
+ // then, _boundsCache needs to be kept up-to-date.
+ Item._updateBoundsCache(
+ this._parent || this._symbol,
+ cacheItem
+ );
+ if (cacheKey && bounds && cacheKey in bounds) {
+ var cached = bounds[cacheKey];
+ return {
+ rect: cached.rect.clone(),
+ nonscaling: cached.nonscaling,
};
- if (!ref.ids[id]) {
- ref.list.push(item);
- ref.ids[id] = item;
}
- }
- },
-
- /**
- * Clears cached bounds of all items that the children of this item are
- * contributing to. See _updateBoundsCache() for an explanation why this
- * information is stored on parents, not the children themselves.
- */
- _clearBoundsCache: function(item) {
- // This is defined as a static method so Symbol can used it too.
- // Clear the position as well, since it's depending on bounds.
- var cache = item._boundsCache;
- if (cache) {
- // Erase cache before looping, to prevent circular recursion.
- item._bounds = item._position = item._boundsCache = undefined;
- for (var i = 0, list = cache.list, l = list.length; i < l; i++){
- var other = list[i];
- if (other !== item) {
- other._bounds = other._position = undefined;
- // We need to recursively call _clearBoundsCache, as
- // when the cache for the other item's children is not
- // valid anymore, that propagates up the scene graph.
- if (other._boundsCache)
- Item._clearBoundsCache(other);
+ var res = this._getBounds(matrix || _matrix, options),
+ // Support two versions of _getBounds(): One that directly returns a
+ // Rectangle, and one that returns a bounds object with nonscaling.
+ rect = res.rect || res,
+ style = this._style,
+ nonscaling =
+ res.nonscaling ||
+ (style.hasStroke() && !style.getStrokeScaling());
+ // If we can cache the result, update the _bounds cache structure
+ // before returning
+ if (cacheKey) {
+ if (!bounds) {
+ this._bounds = bounds = {};
}
+ var cached = (bounds[cacheKey] = {
+ rect: rect.clone(),
+ nonscaling: nonscaling,
+ // Mark as internal, so Item#transform() won't transform it
+ internal: internal,
+ });
}
- }
- },
-
- /**
- * Gets the combined bounds of all specified items.
- */
- _getBounds: function(items, matrix, options) {
- var x1 = Infinity,
- x2 = -x1,
- y1 = x1,
- y2 = x2,
- nonscaling = false;
- // NOTE: As soon as one child-item has non-scaling strokes, the full
- // bounds need to be considered non-scaling for caching purposes.
- options = options || {};
- for (var i = 0, l = items.length; i < l; i++) {
- var item = items[i];
- // Item is handled if it is visible and not recursively empty.
- // This avoid errors with nested empty groups (#1467).
- if (item._visible && !item.isEmpty(true)) {
- // Pass true for noInternal, since even when getting
- // internal bounds for this item, we need to apply the
- // matrices to its children.
- var bounds = item._getCachedBounds(
- matrix && matrix.appended(item._matrix), options, true),
- rect = bounds.rect;
- x1 = Math.min(rect.x, x1);
- y1 = Math.min(rect.y, y1);
- x2 = Math.max(rect.x + rect.width, x2);
- y2 = Math.max(rect.y + rect.height, y2);
- if (bounds.nonscaling)
- nonscaling = true;
- }
- }
- return {
- rect: isFinite(x1)
- ? new Rectangle(x1, y1, x2 - x1, y2 - y1)
- : new Rectangle(),
- nonscaling: nonscaling
- };
- }
- }
+ return {
+ rect: rect,
+ nonscaling: nonscaling,
+ };
+ },
- /**
- * The bounding rectangle of the item excluding stroke width.
- *
- * @name Item#bounds
- * @type Rectangle
- */
+ /**
+ * Returns to correct matrix to use to transform stroke related geometries
+ * when calculating bounds: the item's matrix if {@link #strokeScaling} is
+ * `true`, otherwise the parent's inverted view matrix. The returned matrix
+ * is always shiftless, meaning its translation vector is reset to zero.
+ */
+ _getStrokeMatrix: function (matrix, options) {
+ var parent = this.getStrokeScaling()
+ ? null
+ : options && options.internal
+ ? this
+ : this._parent || (this._symbol && this._symbol._item),
+ mx = parent ? parent.getViewMatrix().invert() : matrix;
+ return mx && mx._shiftless();
+ },
- /**
- * The bounding rectangle of the item including stroke width.
- *
- * @name Item#strokeBounds
- * @type Rectangle
- */
+ statics: /** @lends Item */ {
+ /**
+ * Set up a boundsCache structure that keeps track of items that keep
+ * cached bounds that depend on this item. We store this in the parent,
+ * for multiple reasons:
+ * The parent receives CHILDREN change notifications for when its
+ * children are added or removed and can thus clear the cache, and we
+ * save a lot of memory, e.g. when grouping 100 items and asking the
+ * group for its bounds. If stored on the children, we would have 100
+ * times the same structure.
+ */
+ _updateBoundsCache: function (parent, item) {
+ if (parent && item) {
+ // Set-up the parent's boundsCache structure if it does not
+ // exist yet and add the item to it.
+ var id = item._id,
+ ref = (parent._boundsCache =
+ parent._boundsCache || {
+ // Use a hash-table for ids and an array for the list,
+ // so we can keep track of items that were added already
+ ids: {},
+ list: [],
+ });
+ if (!ref.ids[id]) {
+ ref.list.push(item);
+ ref.ids[id] = item;
+ }
+ }
+ },
- /**
- * The bounding rectangle of the item including handles.
- *
- * @name Item#handleBounds
- * @type Rectangle
- */
+ /**
+ * Clears cached bounds of all items that the children of this item are
+ * contributing to. See _updateBoundsCache() for an explanation why this
+ * information is stored on parents, not the children themselves.
+ */
+ _clearBoundsCache: function (item) {
+ // This is defined as a static method so Symbol can used it too.
+ // Clear the position as well, since it's depending on bounds.
+ var cache = item._boundsCache;
+ if (cache) {
+ // Erase cache before looping, to prevent circular recursion.
+ item._bounds =
+ item._position =
+ item._boundsCache =
+ undefined;
+ for (
+ var i = 0, list = cache.list, l = list.length;
+ i < l;
+ i++
+ ) {
+ var other = list[i];
+ if (other !== item) {
+ other._bounds = other._position = undefined;
+ // We need to recursively call _clearBoundsCache, as
+ // when the cache for the other item's children is not
+ // valid anymore, that propagates up the scene graph.
+ if (other._boundsCache)
+ Item._clearBoundsCache(other);
+ }
+ }
+ }
+ },
- /**
- * The bounding rectangle of the item without any matrix transformations.
- *
- * Typical use case would be drawing a frame around the object where you
- * want to draw something of the same size, position, rotation, and scaling,
- * like a selection frame.
- *
- * @name Item#internalBounds
- * @type Rectangle
- */
+ /**
+ * Gets the combined bounds of all specified items.
+ */
+ _getBounds: function (items, matrix, options) {
+ var x1 = Infinity,
+ x2 = -x1,
+ y1 = x1,
+ y2 = x2,
+ nonscaling = false;
+ // NOTE: As soon as one child-item has non-scaling strokes, the full
+ // bounds need to be considered non-scaling for caching purposes.
+ options = options || {};
+ for (var i = 0, l = items.length; i < l; i++) {
+ var item = items[i];
+ // Item is handled if it is visible and not recursively empty.
+ // This avoid errors with nested empty groups (#1467).
+ if (item._visible && !item.isEmpty(true)) {
+ // Pass true for noInternal, since even when getting
+ // internal bounds for this item, we need to apply the
+ // matrices to its children.
+ var bounds = item._getCachedBounds(
+ matrix && matrix.appended(item._matrix),
+ options,
+ true
+ ),
+ rect = bounds.rect;
+ x1 = Math.min(rect.x, x1);
+ y1 = Math.min(rect.y, y1);
+ x2 = Math.max(rect.x + rect.width, x2);
+ y2 = Math.max(rect.y + rect.height, y2);
+ if (bounds.nonscaling) nonscaling = true;
+ }
+ }
+ return {
+ rect: isFinite(x1)
+ ? new Rectangle(x1, y1, x2 - x1, y2 - y1)
+ : new Rectangle(),
+ nonscaling: nonscaling,
+ };
+ },
+ },
- /**
- * The rough bounding rectangle of the item that is sure to include all of
- * the drawing, including stroke width.
- *
- * @name Item#roughBounds
- * @type Rectangle
- * @ignore
- */
-}), /** @lends Item# */{
- // Enforce creation of beans, as bean getters have hidden parameters.
- // See #getGlobalMatrix() below.
- beans: true,
-
- _decompose: function() {
- // Only decompose if the item isn't directly baking transformations into
- // its content.
- return this._applyMatrix
- ? null
- : this._decomposed || (this._decomposed = this._matrix.decompose());
- },
+ /**
+ * The bounding rectangle of the item excluding stroke width.
+ *
+ * @name Item#bounds
+ * @type Rectangle
+ */
+
+ /**
+ * The bounding rectangle of the item including stroke width.
+ *
+ * @name Item#strokeBounds
+ * @type Rectangle
+ */
+
+ /**
+ * The bounding rectangle of the item including handles.
+ *
+ * @name Item#handleBounds
+ * @type Rectangle
+ */
+
+ /**
+ * The bounding rectangle of the item without any matrix transformations.
+ *
+ * Typical use case would be drawing a frame around the object where you
+ * want to draw something of the same size, position, rotation, and scaling,
+ * like a selection frame.
+ *
+ * @name Item#internalBounds
+ * @type Rectangle
+ */
+
+ /**
+ * The rough bounding rectangle of the item that is sure to include all of
+ * the drawing, including stroke width.
+ *
+ * @name Item#roughBounds
+ * @type Rectangle
+ * @ignore
+ */
+ }
+ ),
+ /** @lends Item# */ {
+ // Enforce creation of beans, as bean getters have hidden parameters.
+ // See #getGlobalMatrix() below.
+ beans: true,
+
+ _decompose: function () {
+ // Only decompose if the item isn't directly baking transformations into
+ // its content.
+ return this._applyMatrix
+ ? null
+ : this._decomposed ||
+ (this._decomposed = this._matrix.decompose());
+ },
- /**
- * The current rotation angle of the item, as described by its
- * {@link #matrix}.
- * Please note that this only returns meaningful values for items with
- * {@link #applyMatrix} set to `false`, meaning they do not directly bake
- * transformations into their content.
- *
- * @bean
- * @type Number
- */
- getRotation: function() {
- var decomposed = this._decompose();
- // Return 0 if matrix wasn't decomposed, e.g. on items with #applyMatrix
- return decomposed ? decomposed.rotation : 0;
- },
+ /**
+ * The current rotation angle of the item, as described by its
+ * {@link #matrix}.
+ * Please note that this only returns meaningful values for items with
+ * {@link #applyMatrix} set to `false`, meaning they do not directly bake
+ * transformations into their content.
+ *
+ * @bean
+ * @type Number
+ */
+ getRotation: function () {
+ var decomposed = this._decompose();
+ // Return 0 if matrix wasn't decomposed, e.g. on items with #applyMatrix
+ return decomposed ? decomposed.rotation : 0;
+ },
- setRotation: function(rotation) {
- var current = this.getRotation();
- if (current != null && rotation != null) {
- // Preserve the cached _decomposed values over rotation, and only
- // update the rotation property on it.
- var decomposed = this._decomposed;
- this.rotate(rotation - current);
- if (decomposed) {
- decomposed.rotation = rotation;
- this._decomposed = decomposed;
+ setRotation: function (rotation) {
+ var current = this.getRotation();
+ if (current != null && rotation != null) {
+ // Preserve the cached _decomposed values over rotation, and only
+ // update the rotation property on it.
+ var decomposed = this._decomposed;
+ this.rotate(rotation - current);
+ if (decomposed) {
+ decomposed.rotation = rotation;
+ this._decomposed = decomposed;
+ }
}
- }
- },
+ },
- /**
- * The current scale factor of the item, as described by its
- * {@link #matrix}.
- * Please note that this only returns meaningful values for items with
- * {@link #applyMatrix} set to `false`, meaning they do not directly bake
- * transformations into their content.
- *
- * @bean
- * @type Point
- */
- getScaling: function() {
- var decomposed = this._decompose(),
- s = decomposed && decomposed.scaling;
- // Return [1, 1] if matrix wasn't decomposed, e.g. with #applyMatrix.
- return new LinkedPoint(s ? s.x : 1, s ? s.y : 1, this, 'setScaling');
- },
+ /**
+ * The current scale factor of the item, as described by its
+ * {@link #matrix}.
+ * Please note that this only returns meaningful values for items with
+ * {@link #applyMatrix} set to `false`, meaning they do not directly bake
+ * transformations into their content.
+ *
+ * @bean
+ * @type Point
+ */
+ getScaling: function () {
+ var decomposed = this._decompose(),
+ s = decomposed && decomposed.scaling;
+ // Return [1, 1] if matrix wasn't decomposed, e.g. with #applyMatrix.
+ return new LinkedPoint(
+ s ? s.x : 1,
+ s ? s.y : 1,
+ this,
+ "setScaling"
+ );
+ },
- setScaling: function(/* scaling */) {
- var current = this.getScaling(),
- // Clone existing points since we're caching internally.
- scaling = Point.read(arguments, 0, { clone: true, readNull: true });
- if (current && scaling && !current.equals(scaling)) {
- // See #setRotation() for preservation of _decomposed.
- var rotation = this.getRotation(),
- decomposed = this._decomposed,
- matrix = new Matrix(),
- isZero = Numerical.isZero;
- // Create a matrix in which the scaling is applied in the non-
- // rotated state, so it is always applied before the rotation.
- // TODO: What about skewing? Do we need separately stored values for
- // these properties, and apply them separately from the matrix?
- if (isZero(current.x) || isZero(current.y)) {
- // If current scaling is destructive (at least one axis is 0),
- // create a new matrix that applies the desired rotation,
- // translation and scaling, without also preserving skewing.
- matrix.translate(decomposed.translation);
- if (rotation) {
- matrix.rotate(rotation);
+ setScaling: function (/* scaling */) {
+ var current = this.getScaling(),
+ // Clone existing points since we're caching internally.
+ scaling = Point.read(arguments, 0, {
+ clone: true,
+ readNull: true,
+ });
+ if (current && scaling && !current.equals(scaling)) {
+ // See #setRotation() for preservation of _decomposed.
+ var rotation = this.getRotation(),
+ decomposed = this._decomposed,
+ matrix = new Matrix(),
+ isZero = Numerical.isZero;
+ // Create a matrix in which the scaling is applied in the non-
+ // rotated state, so it is always applied before the rotation.
+ // TODO: What about skewing? Do we need separately stored values for
+ // these properties, and apply them separately from the matrix?
+ if (isZero(current.x) || isZero(current.y)) {
+ // If current scaling is destructive (at least one axis is 0),
+ // create a new matrix that applies the desired rotation,
+ // translation and scaling, without also preserving skewing.
+ matrix.translate(decomposed.translation);
+ if (rotation) {
+ matrix.rotate(rotation);
+ }
+ matrix.scale(scaling.x, scaling.y);
+ this._matrix.set(matrix);
+ } else {
+ var center = this.getPosition(true);
+ matrix.translate(center);
+ if (rotation) matrix.rotate(rotation);
+ matrix.scale(scaling.x / current.x, scaling.y / current.y);
+ if (rotation) matrix.rotate(-rotation);
+ matrix.translate(center.negate());
+ this.transform(matrix);
+ }
+ if (decomposed) {
+ decomposed.scaling = scaling;
+ this._decomposed = decomposed;
}
- matrix.scale(scaling.x, scaling.y);
- this._matrix.set(matrix);
- } else {
- var center = this.getPosition(true);
- matrix.translate(center);
- if (rotation)
- matrix.rotate(rotation);
- matrix.scale(scaling.x / current.x, scaling.y / current.y);
- if (rotation)
- matrix.rotate(-rotation);
- matrix.translate(center.negate());
- this.transform(matrix);
- }
- if (decomposed) {
- decomposed.scaling = scaling;
- this._decomposed = decomposed;
}
- }
- },
+ },
- /**
- * The item's transformation matrix, defining position and dimensions in
- * relation to its parent item in which it is contained.
- *
- * @bean
- * @type Matrix
- */
- getMatrix: function() {
- return this._matrix;
- },
+ /**
+ * The item's transformation matrix, defining position and dimensions in
+ * relation to its parent item in which it is contained.
+ *
+ * @bean
+ * @type Matrix
+ */
+ getMatrix: function () {
+ return this._matrix;
+ },
- setMatrix: function() {
- // Use Matrix#initialize to easily copy over values.
- // NOTE: calling initialize() also calls #_changed() for us, through its
- // call to #set() / #reset(), and this also handles _applyMatrix for us.
- var matrix = this._matrix;
- matrix.set.apply(matrix, arguments);
- },
+ setMatrix: function () {
+ // Use Matrix#initialize to easily copy over values.
+ // NOTE: calling initialize() also calls #_changed() for us, through its
+ // call to #set() / #reset(), and this also handles _applyMatrix for us.
+ var matrix = this._matrix;
+ matrix.set.apply(matrix, arguments);
+ },
- /**
- * The item's global transformation matrix in relation to the global project
- * coordinate space. Note that the view's transformations resulting from
- * zooming and panning are not factored in.
- *
- * @bean
- * @type Matrix
- */
- getGlobalMatrix: function(_dontClone) {
- var matrix = this._globalMatrix;
- if (matrix) {
- // If there's a cached global matrix for this item, check if all its
- // parents also have one. If it's missing in any of its parents, it
- // means the child's cached version isn't valid anymore.
- // For better performance, we also use the occasion of this loop to
- // clear cached version of items parents.
- var parent = this._parent;
- var parents = [];
- while (parent) {
- if (!parent._globalMatrix) {
- matrix = null;
- // Also clear global matrix of item's parents.
- for (var i = 0, l = parents.length; i < l; i++) {
- parents[i]._globalMatrix = null;
+ /**
+ * The item's global transformation matrix in relation to the global project
+ * coordinate space. Note that the view's transformations resulting from
+ * zooming and panning are not factored in.
+ *
+ * @bean
+ * @type Matrix
+ */
+ getGlobalMatrix: function (_dontClone) {
+ var matrix = this._globalMatrix;
+ if (matrix) {
+ // If there's a cached global matrix for this item, check if all its
+ // parents also have one. If it's missing in any of its parents, it
+ // means the child's cached version isn't valid anymore.
+ // For better performance, we also use the occasion of this loop to
+ // clear cached version of items parents.
+ var parent = this._parent;
+ var parents = [];
+ while (parent) {
+ if (!parent._globalMatrix) {
+ matrix = null;
+ // Also clear global matrix of item's parents.
+ for (var i = 0, l = parents.length; i < l; i++) {
+ parents[i]._globalMatrix = null;
+ }
+ break;
}
- break;
+ parents.push(parent);
+ parent = parent._parent;
}
- parents.push(parent);
- parent = parent._parent;
}
- }
- if (!matrix) {
- matrix = this._globalMatrix = this._matrix.clone();
- var parent = this._parent;
- if (parent)
- matrix.prepend(parent.getGlobalMatrix(true));
- }
- return _dontClone ? matrix : matrix.clone();
- },
+ if (!matrix) {
+ matrix = this._globalMatrix = this._matrix.clone();
+ var parent = this._parent;
+ if (parent) matrix.prepend(parent.getGlobalMatrix(true));
+ }
+ return _dontClone ? matrix : matrix.clone();
+ },
- /**
- * The item's global matrix in relation to the view coordinate space. This
- * means that the view's transformations resulting from zooming and panning
- * are factored in.
- *
- * @bean
- * @type Matrix
- */
- getViewMatrix: function() {
- return this.getGlobalMatrix().prepend(this.getView()._matrix);
- },
+ /**
+ * The item's global matrix in relation to the view coordinate space. This
+ * means that the view's transformations resulting from zooming and panning
+ * are factored in.
+ *
+ * @bean
+ * @type Matrix
+ */
+ getViewMatrix: function () {
+ return this.getGlobalMatrix().prepend(this.getView()._matrix);
+ },
- /**
- * Controls whether the transformations applied to the item (e.g. through
- * {@link #transform(matrix)}, {@link #rotate(angle)},
- * {@link #scale(scale)}, etc.) are stored in its {@link #matrix} property,
- * or whether they are directly applied to its contents or children (passed
- * on to the segments in {@link Path} items, the children of {@link Group}
- * items, etc.).
- *
- * @bean
- * @type Boolean
- * @default true
- */
- getApplyMatrix: function() {
- return this._applyMatrix;
- },
+ /**
+ * Controls whether the transformations applied to the item (e.g. through
+ * {@link #transform(matrix)}, {@link #rotate(angle)},
+ * {@link #scale(scale)}, etc.) are stored in its {@link #matrix} property,
+ * or whether they are directly applied to its contents or children (passed
+ * on to the segments in {@link Path} items, the children of {@link Group}
+ * items, etc.).
+ *
+ * @bean
+ * @type Boolean
+ * @default true
+ */
+ getApplyMatrix: function () {
+ return this._applyMatrix;
+ },
- setApplyMatrix: function(apply) {
- // Tell #transform() to apply the internal matrix if _applyMatrix
- // can be set to true.
- if (this._applyMatrix = this._canApplyMatrix && !!apply)
- this.transform(null, true);
- },
+ setApplyMatrix: function (apply) {
+ // Tell #transform() to apply the internal matrix if _applyMatrix
+ // can be set to true.
+ if ((this._applyMatrix = this._canApplyMatrix && !!apply))
+ this.transform(null, true);
+ },
- /**
- * @bean
- * @deprecated use {@link #applyMatrix} instead.
- */
- getTransformContent: '#getApplyMatrix',
- setTransformContent: '#setApplyMatrix',
-}, /** @lends Item# */{
- /**
- * {@grouptitle Project Hierarchy}
- * The project that this item belongs to.
- *
- * @type Project
- * @bean
- */
- getProject: function() {
- return this._project;
+ /**
+ * @bean
+ * @deprecated use {@link #applyMatrix} instead.
+ */
+ getTransformContent: "#getApplyMatrix",
+ setTransformContent: "#setApplyMatrix",
},
+ /** @lends Item# */ {
+ /**
+ * {@grouptitle Project Hierarchy}
+ * The project that this item belongs to.
+ *
+ * @type Project
+ * @bean
+ */
+ getProject: function () {
+ return this._project;
+ },
- _setProject: function(project, installEvents) {
- if (this._project !== project) {
- // Uninstall events before switching project, then install them
- // again.
- // NOTE: _installEvents handles all children too!
- if (this._project)
- this._installEvents(false);
- this._project = project;
+ _setProject: function (project, installEvents) {
+ if (this._project !== project) {
+ // Uninstall events before switching project, then install them
+ // again.
+ // NOTE: _installEvents handles all children too!
+ if (this._project) this._installEvents(false);
+ this._project = project;
+ var children = this._children;
+ for (var i = 0, l = children && children.length; i < l; i++)
+ children[i]._setProject(project);
+ // We need to call _installEvents(true) again, but merge it with
+ // handling of installEvents argument below.
+ installEvents = true;
+ }
+ if (installEvents) this._installEvents(true);
+ },
+
+ /**
+ * The view that this item belongs to.
+ * @type View
+ * @bean
+ */
+ getView: function () {
+ return this._project._view;
+ },
+
+ /**
+ * Overrides Emitter#_installEvents to also call _installEvents on all
+ * children.
+ */
+ _installEvents: function _installEvents(install) {
+ _installEvents.base.call(this, install);
var children = this._children;
for (var i = 0, l = children && children.length; i < l; i++)
- children[i]._setProject(project);
- // We need to call _installEvents(true) again, but merge it with
- // handling of installEvents argument below.
- installEvents = true;
- }
- if (installEvents)
- this._installEvents(true);
- },
+ children[i]._installEvents(install);
+ },
- /**
- * The view that this item belongs to.
- * @type View
- * @bean
- */
- getView: function() {
- return this._project._view;
- },
+ /**
+ * The layer that this item is contained within.
+ *
+ * @type Layer
+ * @bean
+ */
+ getLayer: function () {
+ var parent = this;
+ while ((parent = parent._parent)) {
+ if (parent instanceof Layer) return parent;
+ }
+ return null;
+ },
- /**
- * Overrides Emitter#_installEvents to also call _installEvents on all
- * children.
- */
- _installEvents: function _installEvents(install) {
- _installEvents.base.call(this, install);
- var children = this._children;
- for (var i = 0, l = children && children.length; i < l; i++)
- children[i]._installEvents(install);
- },
+ /**
+ * The item that this item is contained within.
+ *
+ * @type Item
+ * @bean
+ *
+ * @example
+ * var path = new Path();
+ *
+ * // New items are placed in the active layer:
+ * console.log(path.parent == project.activeLayer); // true
+ *
+ * var group = new Group();
+ * group.addChild(path);
+ *
+ * // Now the parent of the path has become the group:
+ * console.log(path.parent == group); // true
+ *
+ * @example // Setting the parent of the item to another item
+ * var path = new Path();
+ *
+ * // New items are placed in the active layer:
+ * console.log(path.parent == project.activeLayer); // true
+ *
+ * var group = new Group();
+ * path.parent = group;
+ *
+ * // Now the parent of the path has become the group:
+ * console.log(path.parent == group); // true
+ *
+ * // The path is now contained in the children list of group:
+ * console.log(group.children[0] == path); // true
+ *
+ * @example // Setting the parent of an item in the constructor
+ * var group = new Group();
+ *
+ * var path = new Path({
+ * parent: group
+ * });
+ *
+ * // The parent of the path is the group:
+ * console.log(path.parent == group); // true
+ *
+ * // The path is contained in the children list of group:
+ * console.log(group.children[0] == path); // true
+ */
+ getParent: function () {
+ return this._parent;
+ },
- /**
- * The layer that this item is contained within.
- *
- * @type Layer
- * @bean
- */
- getLayer: function() {
- var parent = this;
- while (parent = parent._parent) {
- if (parent instanceof Layer)
- return parent;
- }
- return null;
- },
+ setParent: function (item) {
+ return item.addChild(this);
+ },
- /**
- * The item that this item is contained within.
- *
- * @type Item
- * @bean
- *
- * @example
- * var path = new Path();
- *
- * // New items are placed in the active layer:
- * console.log(path.parent == project.activeLayer); // true
- *
- * var group = new Group();
- * group.addChild(path);
- *
- * // Now the parent of the path has become the group:
- * console.log(path.parent == group); // true
- *
- * @example // Setting the parent of the item to another item
- * var path = new Path();
- *
- * // New items are placed in the active layer:
- * console.log(path.parent == project.activeLayer); // true
- *
- * var group = new Group();
- * path.parent = group;
- *
- * // Now the parent of the path has become the group:
- * console.log(path.parent == group); // true
- *
- * // The path is now contained in the children list of group:
- * console.log(group.children[0] == path); // true
- *
- * @example // Setting the parent of an item in the constructor
- * var group = new Group();
- *
- * var path = new Path({
- * parent: group
- * });
- *
- * // The parent of the path is the group:
- * console.log(path.parent == group); // true
- *
- * // The path is contained in the children list of group:
- * console.log(group.children[0] == path); // true
- */
- getParent: function() {
- return this._parent;
- },
+ /**
+ * Private helper to return the owner, either the parent, or the project
+ * for top-level layers. See Layer#_getOwner()
+ */
+ _getOwner: "#getParent",
- setParent: function(item) {
- return item.addChild(this);
- },
+ /**
+ * The children items contained within this item. Items that define a
+ * {@link #name} can also be accessed by name.
+ *
+ * Please note: The children array should not be modified directly
+ * using array functions. To remove single items from the children list, use
+ * {@link Item#remove()}, to remove all items from the children list, use
+ * {@link Item#removeChildren()}. To add items to the children list, use
+ * {@link Item#addChild(item)} or {@link Item#insertChild(index,item)}.
+ *
+ * @type Item[]
+ * @bean
+ *
+ * @example {@paperscript}
+ * // Accessing items in the children array:
+ * var path = new Path.Circle({
+ * center: [80, 50],
+ * radius: 35
+ * });
+ *
+ * // Create a group and move the path into it:
+ * var group = new Group();
+ * group.addChild(path);
+ *
+ * // Access the path through the group's children array:
+ * group.children[0].fillColor = 'red';
+ *
+ * @example {@paperscript}
+ * // Accessing children by name:
+ * var path = new Path.Circle({
+ * center: [80, 50],
+ * radius: 35
+ * });
+ * // Set the name of the path:
+ * path.name = 'example';
+ *
+ * // Create a group and move the path into it:
+ * var group = new Group();
+ * group.addChild(path);
+ *
+ * // The path can be accessed by name:
+ * group.children['example'].fillColor = 'orange';
+ *
+ * @example {@paperscript}
+ * // Passing an array of items to item.children:
+ * var path = new Path.Circle({
+ * center: [80, 50],
+ * radius: 35
+ * });
+ *
+ * var group = new Group();
+ * group.children = [path];
+ *
+ * // The path is the first child of the group:
+ * group.firstChild.fillColor = 'green';
+ */
+ getChildren: function () {
+ return this._children;
+ },
- /**
- * Private helper to return the owner, either the parent, or the project
- * for top-level layers. See Layer#_getOwner()
- */
- _getOwner: '#getParent',
+ setChildren: function (items) {
+ this.removeChildren();
+ this.addChildren(items);
+ },
- /**
- * The children items contained within this item. Items that define a
- * {@link #name} can also be accessed by name.
- *
- * Please note: The children array should not be modified directly
- * using array functions. To remove single items from the children list, use
- * {@link Item#remove()}, to remove all items from the children list, use
- * {@link Item#removeChildren()}. To add items to the children list, use
- * {@link Item#addChild(item)} or {@link Item#insertChild(index,item)}.
- *
- * @type Item[]
- * @bean
- *
- * @example {@paperscript}
- * // Accessing items in the children array:
- * var path = new Path.Circle({
- * center: [80, 50],
- * radius: 35
- * });
- *
- * // Create a group and move the path into it:
- * var group = new Group();
- * group.addChild(path);
- *
- * // Access the path through the group's children array:
- * group.children[0].fillColor = 'red';
- *
- * @example {@paperscript}
- * // Accessing children by name:
- * var path = new Path.Circle({
- * center: [80, 50],
- * radius: 35
- * });
- * // Set the name of the path:
- * path.name = 'example';
- *
- * // Create a group and move the path into it:
- * var group = new Group();
- * group.addChild(path);
- *
- * // The path can be accessed by name:
- * group.children['example'].fillColor = 'orange';
- *
- * @example {@paperscript}
- * // Passing an array of items to item.children:
- * var path = new Path.Circle({
- * center: [80, 50],
- * radius: 35
- * });
- *
- * var group = new Group();
- * group.children = [path];
- *
- * // The path is the first child of the group:
- * group.firstChild.fillColor = 'green';
- */
- getChildren: function() {
- return this._children;
- },
+ /**
+ * The first item contained within this item. This is a shortcut for
+ * accessing `item.children[0]`.
+ *
+ * @type Item
+ * @bean
+ */
+ getFirstChild: function () {
+ return (this._children && this._children[0]) || null;
+ },
- setChildren: function(items) {
- this.removeChildren();
- this.addChildren(items);
- },
+ /**
+ * The last item contained within this item.This is a shortcut for
+ * accessing `item.children[item.children.length - 1]`.
+ *
+ * @type Item
+ * @bean
+ */
+ getLastChild: function () {
+ return (
+ (this._children && this._children[this._children.length - 1]) ||
+ null
+ );
+ },
- /**
- * The first item contained within this item. This is a shortcut for
- * accessing `item.children[0]`.
- *
- * @type Item
- * @bean
- */
- getFirstChild: function() {
- return this._children && this._children[0] || null;
- },
+ /**
+ * The next item on the same level as this item.
+ *
+ * @type Item
+ * @bean
+ */
+ getNextSibling: function () {
+ var owner = this._getOwner();
+ return (owner && owner._children[this._index + 1]) || null;
+ },
- /**
- * The last item contained within this item.This is a shortcut for
- * accessing `item.children[item.children.length - 1]`.
- *
- * @type Item
- * @bean
- */
- getLastChild: function() {
- return this._children && this._children[this._children.length - 1]
- || null;
- },
+ /**
+ * The previous item on the same level as this item.
+ *
+ * @type Item
+ * @bean
+ */
+ getPreviousSibling: function () {
+ var owner = this._getOwner();
+ return (owner && owner._children[this._index - 1]) || null;
+ },
- /**
- * The next item on the same level as this item.
- *
- * @type Item
- * @bean
- */
- getNextSibling: function() {
- var owner = this._getOwner();
- return owner && owner._children[this._index + 1] || null;
- },
+ /**
+ * The index of this item within the list of its parent's children.
+ *
+ * @type Number
+ * @bean
+ */
+ getIndex: function () {
+ return this._index;
+ },
- /**
- * The previous item on the same level as this item.
- *
- * @type Item
- * @bean
- */
- getPreviousSibling: function() {
- var owner = this._getOwner();
- return owner && owner._children[this._index - 1] || null;
- },
+ equals: function (item) {
+ // NOTE: We do not compare name and selected state.
+ // TODO: Consider not comparing locked and visible also?
+ return (
+ item === this ||
+ (item &&
+ this._class === item._class &&
+ this._style.equals(item._style) &&
+ this._matrix.equals(item._matrix) &&
+ this._locked === item._locked &&
+ this._visible === item._visible &&
+ this._blendMode === item._blendMode &&
+ this._opacity === item._opacity &&
+ this._clipMask === item._clipMask &&
+ this._guide === item._guide &&
+ this._equals(item)) ||
+ false
+ );
+ },
- /**
- * The index of this item within the list of its parent's children.
- *
- * @type Number
- * @bean
- */
- getIndex: function() {
- return this._index;
- },
+ /**
+ * A private helper for #equals(), to be overridden in sub-classes. When it
+ * is called, item is always defined, of the same class as `this` and has
+ * equal general state attributes such as matrix, style, opacity, etc.
+ */
+ _equals: function (item) {
+ return Base.equals(this._children, item._children);
+ },
- equals: function(item) {
- // NOTE: We do not compare name and selected state.
- // TODO: Consider not comparing locked and visible also?
- return item === this || item && this._class === item._class
- && this._style.equals(item._style)
- && this._matrix.equals(item._matrix)
- && this._locked === item._locked
- && this._visible === item._visible
- && this._blendMode === item._blendMode
- && this._opacity === item._opacity
- && this._clipMask === item._clipMask
- && this._guide === item._guide
- && this._equals(item)
- || false;
- },
+ /**
+ * Clones the item within the same project and places the copy above the
+ * item.
+ *
+ * @option [insert=true] specifies whether the copy should be
+ * inserted into the scene graph. When set to `true`, it is inserted
+ * above the original
+ * @option [deep=true] specifies whether the item's children should also be
+ * cloned
+ *
+ * @param {Object} [options={ insert: true, deep: true }]
+ *
+ * @return {Item} the newly cloned item
+ * @chainable
+ *
+ * @example {@paperscript}
+ * // Cloning items:
+ * var circle = new Path.Circle({
+ * center: [50, 50],
+ * radius: 10,
+ * fillColor: 'red'
+ * });
+ *
+ * // Make 20 copies of the circle:
+ * for (var i = 0; i < 20; i++) {
+ * var copy = circle.clone();
+ *
+ * // Distribute the copies horizontally, so we can see them:
+ * copy.position.x += i * copy.bounds.width;
+ * }
+ */
+ clone: function (options) {
+ var copy = new this.constructor(Item.NO_INSERT),
+ children = this._children,
+ // Both `insert` and `deep` are true by default:
+ insert = Base.pick(
+ options ? options.insert : undefined,
+ // Also support boolean parameter for insert, default: true.
+ options === undefined || options === true
+ ),
+ deep = Base.pick(options ? options.deep : undefined, true);
+ // On items with children, for performance reasons due to the way that
+ // styles are currently "flattened" into existing children, we need to
+ // clone attributes first, then content.
+ // For all other items, it's the other way around, since applying
+ // attributes might have an impact on their content.
+ if (children) copy.copyAttributes(this);
+ // Only copy content if we don't have children or if we're ask to create
+ // a deep clone, which is the default.
+ if (!children || deep) copy.copyContent(this);
+ if (!children) copy.copyAttributes(this);
+ if (insert) copy.insertAbove(this);
+ // Make sure we're not overriding the original name in the same parent
+ var name = this._name,
+ parent = this._parent;
+ if (name && parent) {
+ var children = parent._children,
+ orig = name,
+ i = 1;
+ while (children[name]) name = orig + " " + i++;
+ if (name !== orig) copy.setName(name);
+ }
+ return copy;
+ },
- /**
- * A private helper for #equals(), to be overridden in sub-classes. When it
- * is called, item is always defined, of the same class as `this` and has
- * equal general state attributes such as matrix, style, opacity, etc.
- */
- _equals: function(item) {
- return Base.equals(this._children, item._children);
- },
+ /**
+ * Copies the content of the specified item over to this item.
+ *
+ * @param {Item} source the item to copy the content from
+ */
+ copyContent: function (source) {
+ var children = source._children;
+ // Clone all children and add them to the copy. tell #addChild we're
+ // cloning, as needed by CompoundPath#insertChild().
+ for (var i = 0, l = children && children.length; i < l; i++) {
+ this.addChild(children[i].clone(false), true);
+ }
+ },
- /**
- * Clones the item within the same project and places the copy above the
- * item.
- *
- * @option [insert=true] specifies whether the copy should be
- * inserted into the scene graph. When set to `true`, it is inserted
- * above the original
- * @option [deep=true] specifies whether the item's children should also be
- * cloned
- *
- * @param {Object} [options={ insert: true, deep: true }]
- *
- * @return {Item} the newly cloned item
- * @chainable
- *
- * @example {@paperscript}
- * // Cloning items:
- * var circle = new Path.Circle({
- * center: [50, 50],
- * radius: 10,
- * fillColor: 'red'
- * });
- *
- * // Make 20 copies of the circle:
- * for (var i = 0; i < 20; i++) {
- * var copy = circle.clone();
- *
- * // Distribute the copies horizontally, so we can see them:
- * copy.position.x += i * copy.bounds.width;
- * }
- */
- clone: function(options) {
- var copy = new this.constructor(Item.NO_INSERT),
- children = this._children,
- // Both `insert` and `deep` are true by default:
- insert = Base.pick(options ? options.insert : undefined,
- // Also support boolean parameter for insert, default: true.
- options === undefined || options === true),
- deep = Base.pick(options ? options.deep : undefined, true);
- // On items with children, for performance reasons due to the way that
- // styles are currently "flattened" into existing children, we need to
- // clone attributes first, then content.
- // For all other items, it's the other way around, since applying
- // attributes might have an impact on their content.
- if (children)
- copy.copyAttributes(this);
- // Only copy content if we don't have children or if we're ask to create
- // a deep clone, which is the default.
- if (!children || deep)
- copy.copyContent(this);
- if (!children)
- copy.copyAttributes(this);
- if (insert)
- copy.insertAbove(this);
- // Make sure we're not overriding the original name in the same parent
- var name = this._name,
- parent = this._parent;
- if (name && parent) {
- var children = parent._children,
- orig = name,
- i = 1;
- while (children[name])
- name = orig + ' ' + (i++);
- if (name !== orig)
- copy.setName(name);
- }
- return copy;
+ /**
+ * Copies all attributes of the specified item over to this item. This
+ * includes its style, visibility, matrix, pivot, blend-mode, opacity,
+ * selection state, data, name, etc.
+ *
+ * @param {Item} source the item to copy the attributes from
+ * @param {Boolean} excludeMatrix whether to exclude the transformation
+ * matrix when copying all attributes
+ */
+ copyAttributes: function (source, excludeMatrix) {
+ // Copy over style
+ this.setStyle(source._style);
+ // Only copy over these fields if they are actually defined in 'source',
+ // meaning the default value has been overwritten (default is on
+ // prototype).
+ var keys = [
+ "_locked",
+ "_visible",
+ "_blendMode",
+ "_opacity",
+ "_clipMask",
+ "_guide",
+ ];
+ for (var i = 0, l = keys.length; i < l; i++) {
+ var key = keys[i];
+ if (source.hasOwnProperty(key)) this[key] = source[key];
+ }
+ // Use Matrix#initialize to easily copy over values.
+ if (!excludeMatrix) this._matrix.set(source._matrix, true);
+ // We can't just set _applyMatrix as many item types won't allow it,
+ // e.g. creating a Shape in Path#toShape().
+ // Using the setter instead takes care of it.
+ // NOTE: This will also bake in the matrix that we just initialized,
+ // in case #applyMatrix is true.
+ this.setApplyMatrix(source._applyMatrix);
+ this.setPivot(source._pivot);
+ // Copy over the selection state, use setSelection so the item
+ // is also added to Project#_selectionItems if it is selected.
+ this.setSelection(source._selection);
+ // Copy over data and name as well.
+ var data = source._data,
+ name = source._name;
+ this._data = data ? Base.clone(data) : null;
+ if (name) this.setName(name);
+ },
+
+ /**
+ * @name Item#rasterize
+ * @function
+ * @param {Number} [resolution=view.resolution]
+ * @param {Boolean} [insert=true]
+ * @deprecated use {@link #rasterize(options)} instead.
+ */
+ /**
+ * Rasterizes the item into a newly created Raster object. The item itself
+ * is not removed after rasterization.
+ *
+ * @option [resolution=view.resolution] {Number} the desired resolution to
+ * be used when rasterizing, in pixels per inch (DPI). If not specified,
+ * the value of `view.resolution` is used by default.
+ * @option [raster=null] {Raster} specifies a raster to be reused when
+ * rasterizing. If the raster has the desired size already, then the
+ * underlying canvas is reused and no new memory needs to be allocated.
+ * If no raster is provided, a new raster item is created and returned
+ * instead.
+ * @option [insert=true] {Boolean} specifies whether the raster should be
+ * inserted into the scene graph. When set to `true`, it is inserted
+ * above the rasterized item.
+ *
+ * @name Item#rasterize
+ * @function
+ * @param {Object} [options={}] the rasterization options
+ * @return {Raster} the reused raster or the newly created raster item
+ *
+ * @example {@paperscript}
+ * // Rasterizing an item:
+ * var circle = new Path.Circle({
+ * center: [50, 50],
+ * radius: 5,
+ * fillColor: 'red'
+ * });
+ *
+ * // Create a rasterized version of the path:
+ * var raster = circle.rasterize();
+ *
+ * // Move it 100pt to the right:
+ * raster.position.x += 100;
+ *
+ * // Scale the path and the raster by 300%, so we can compare them:
+ * circle.scale(5);
+ * raster.scale(5);
+ */
+ rasterize: function (arg0, arg1) {
+ var resolution, insert, raster;
+ if (Base.isPlainObject(arg0)) {
+ resolution = arg0.resolution;
+ insert = arg0.insert;
+ raster = arg0.raster;
+ } else {
+ resolution = arg0;
+ insert = arg1;
+ }
+ if (!raster) {
+ raster = new Raster(Item.NO_INSERT);
+ }
+ var bounds = this.getStrokeBounds(),
+ scale = (resolution || this.getView().getResolution()) / 72,
+ // Floor top-left corner and ceil bottom-right corner, to never
+ // blur or cut pixels.
+ topLeft = bounds.getTopLeft().floor(),
+ bottomRight = bounds.getBottomRight().ceil(),
+ boundsSize = new Size(bottomRight.subtract(topLeft)),
+ rasterSize = boundsSize.multiply(scale);
+ // Pass `true` for clear, so reused rasters don't draw over old pixels.
+ raster.setSize(rasterSize, true);
+
+ if (!rasterSize.isZero()) {
+ var ctx = raster.getContext(true),
+ matrix = new Matrix()
+ .scale(scale)
+ .translate(topLeft.negate());
+ ctx.save();
+ matrix.applyToContext(ctx);
+ // See Project#draw() for an explanation of new Base()
+ this.draw(ctx, new Base({ matrices: [matrix] }));
+ ctx.restore();
+ }
+ raster._matrix.set(
+ new Matrix()
+ .translate(topLeft.add(boundsSize.divide(2)))
+ // Take resolution into account and scale back to original size.
+ .scale(1 / scale)
+ );
+ if (insert === undefined || insert) {
+ raster.insertAbove(this);
+ }
+ return raster;
+ },
+
+ /**
+ * {@grouptitle Geometric Tests}
+ *
+ * Checks whether the item's geometry contains the given point.
+ *
+ * @example {@paperscript} // Click within and outside the star below
+ * // Create a star shaped path:
+ * var path = new Path.Star({
+ * center: [50, 50],
+ * points: 12,
+ * radius1: 20,
+ * radius2: 40,
+ * fillColor: 'black'
+ * });
+ *
+ * // Whenever the user presses the mouse:
+ * function onMouseDown(event) {
+ * // If the position of the mouse is within the path,
+ * // set its fill color to red, otherwise set it to
+ * // black:
+ * if (path.contains(event.point)) {
+ * path.fillColor = 'red';
+ * } else {
+ * path.fillColor = 'black';
+ * }
+ * }
+ *
+ * @param {Point} point the point to check for
+ * @return {Boolean}
+ */
+ contains: function (/* point */) {
+ // See CompoundPath#_contains() for the reason for !!
+ var matrix = this._matrix;
+ return (
+ matrix.isInvertible() &&
+ !!this._contains(
+ matrix._inverseTransform(Point.read(arguments))
+ )
+ );
+ },
+
+ _contains: function (point) {
+ var children = this._children;
+ if (children) {
+ for (var i = children.length - 1; i >= 0; i--) {
+ if (children[i].contains(point)) return true;
+ }
+ return false;
+ }
+ // We only implement it here for items with rectangular content,
+ // for anything else we need to override #contains()
+ return point.isInside(this.getInternalBounds());
+ },
+
+ // DOCS:
+ // TEST:
+ /**
+ * @param {Rectangle} rect the rectangle to check against
+ * @return {Boolean}
+ */
+ isInside: function (/* rect */) {
+ return Rectangle.read(arguments).contains(this.getBounds());
+ },
+
+ // Internal helper function, used at the moment for intersects check only.
+ // TODO: Move #getIntersections() to Item, make it handle all type of items
+ // through _asPathItem(), and support Group items as well, taking nested
+ // matrices into account properly!
+ _asPathItem: function () {
+ // Creates a temporary rectangular path item with this item's bounds.
+ return new Path.Rectangle({
+ rectangle: this.getInternalBounds(),
+ matrix: this._matrix,
+ insert: false,
+ });
+ },
+
+ // DOCS:
+ // TEST:
+ /**
+ * @param {Item} item the item to check against
+ * @return {Boolean}
+ */
+ intersects: function (item, _matrix) {
+ if (!(item instanceof Item)) return false;
+ // Tell getIntersections() to return as soon as some intersections are
+ // found, because all we care for here is there are some or none:
+ return (
+ this._asPathItem().getIntersections(
+ item._asPathItem(),
+ null,
+ _matrix,
+ true
+ ).length > 0
+ );
+ },
},
+ new (function () {
+ // Injection scope for hit-test functions shared with project
+ function hitTest(/* point, options */) {
+ var args = arguments;
+ return this._hitTest(Point.read(args), HitResult.getOptions(args));
+ }
- /**
- * Copies the content of the specified item over to this item.
- *
- * @param {Item} source the item to copy the content from
- */
- copyContent: function(source) {
- var children = source._children;
- // Clone all children and add them to the copy. tell #addChild we're
- // cloning, as needed by CompoundPath#insertChild().
- for (var i = 0, l = children && children.length; i < l; i++) {
- this.addChild(children[i].clone(false), true);
+ function hitTestAll(/* point, options */) {
+ var args = arguments,
+ point = Point.read(args),
+ options = HitResult.getOptions(args),
+ all = [];
+ this._hitTest(point, new Base({ all: all }, options));
+ return all;
}
- },
- /**
- * Copies all attributes of the specified item over to this item. This
- * includes its style, visibility, matrix, pivot, blend-mode, opacity,
- * selection state, data, name, etc.
- *
- * @param {Item} source the item to copy the attributes from
- * @param {Boolean} excludeMatrix whether to exclude the transformation
- * matrix when copying all attributes
- */
- copyAttributes: function(source, excludeMatrix) {
- // Copy over style
- this.setStyle(source._style);
- // Only copy over these fields if they are actually defined in 'source',
- // meaning the default value has been overwritten (default is on
- // prototype).
- var keys = ['_locked', '_visible', '_blendMode', '_opacity',
- '_clipMask', '_guide'];
- for (var i = 0, l = keys.length; i < l; i++) {
- var key = keys[i];
- if (source.hasOwnProperty(key))
- this[key] = source[key];
+ function hitTestChildren(point, options, viewMatrix, _exclude) {
+ // NOTE: _exclude is only used in Group#_hitTestChildren()
+ // to exclude #clipItem
+ var children = this._children;
+ if (children) {
+ // Loop backwards, so items that get drawn last are tested first.
+ for (var i = children.length - 1; i >= 0; i--) {
+ var child = children[i];
+ var res =
+ child !== _exclude &&
+ child._hitTest(point, options, viewMatrix);
+ // Only return the found result if we're not asked to collect
+ // all matches through hitTestAll()
+ if (res && !options.all) return res;
+ }
+ }
+ return null;
}
- // Use Matrix#initialize to easily copy over values.
- if (!excludeMatrix)
- this._matrix.set(source._matrix, true);
- // We can't just set _applyMatrix as many item types won't allow it,
- // e.g. creating a Shape in Path#toShape().
- // Using the setter instead takes care of it.
- // NOTE: This will also bake in the matrix that we just initialized,
- // in case #applyMatrix is true.
- this.setApplyMatrix(source._applyMatrix);
- this.setPivot(source._pivot);
- // Copy over the selection state, use setSelection so the item
- // is also added to Project#_selectionItems if it is selected.
- this.setSelection(source._selection);
- // Copy over data and name as well.
- var data = source._data,
- name = source._name;
- this._data = data ? Base.clone(data) : null;
- if (name)
- this.setName(name);
- },
- /**
- * @name Item#rasterize
- * @function
- * @param {Number} [resolution=view.resolution]
- * @param {Boolean} [insert=true]
- * @deprecated use {@link #rasterize(options)} instead.
- */
- /**
- * Rasterizes the item into a newly created Raster object. The item itself
- * is not removed after rasterization.
- *
- * @option [resolution=view.resolution] {Number} the desired resolution to
- * be used when rasterizing, in pixels per inch (DPI). If not specified,
- * the value of `view.resolution` is used by default.
- * @option [raster=null] {Raster} specifies a raster to be reused when
- * rasterizing. If the raster has the desired size already, then the
- * underlying canvas is reused and no new memory needs to be allocated.
- * If no raster is provided, a new raster item is created and returned
- * instead.
- * @option [insert=true] {Boolean} specifies whether the raster should be
- * inserted into the scene graph. When set to `true`, it is inserted
- * above the rasterized item.
- *
- * @name Item#rasterize
- * @function
- * @param {Object} [options={}] the rasterization options
- * @return {Raster} the reused raster or the newly created raster item
- *
- * @example {@paperscript}
- * // Rasterizing an item:
- * var circle = new Path.Circle({
- * center: [50, 50],
- * radius: 5,
- * fillColor: 'red'
- * });
- *
- * // Create a rasterized version of the path:
- * var raster = circle.rasterize();
- *
- * // Move it 100pt to the right:
- * raster.position.x += 100;
- *
- * // Scale the path and the raster by 300%, so we can compare them:
- * circle.scale(5);
- * raster.scale(5);
- */
- rasterize: function(arg0, arg1) {
- var resolution,
- insert,
- raster;
- if (Base.isPlainObject(arg0)) {
- resolution = arg0.resolution;
- insert = arg0.insert;
- raster = arg0.raster;
- } else {
- resolution = arg0;
- insert = arg1;
- }
- if (!raster) {
- raster = new Raster(Item.NO_INSERT);
- }
- var bounds = this.getStrokeBounds(),
- scale = (resolution || this.getView().getResolution()) / 72,
- // Floor top-left corner and ceil bottom-right corner, to never
- // blur or cut pixels.
- topLeft = bounds.getTopLeft().floor(),
- bottomRight = bounds.getBottomRight().ceil(),
- boundsSize = new Size(bottomRight.subtract(topLeft)),
- rasterSize = boundsSize.multiply(scale);
- // Pass `true` for clear, so reused rasters don't draw over old pixels.
- raster.setSize(rasterSize, true);
-
- if (!rasterSize.isZero()) {
- var ctx = raster.getContext(true),
- matrix = new Matrix().scale(scale).translate(topLeft.negate());
- ctx.save();
- matrix.applyToContext(ctx);
- // See Project#draw() for an explanation of new Base()
- this.draw(ctx, new Base({ matrices: [matrix] }));
- ctx.restore();
- }
- raster._matrix.set(
- new Matrix()
- .translate(topLeft.add(boundsSize.divide(2)))
- // Take resolution into account and scale back to original size.
- .scale(1 / scale)
- );
- if (insert === undefined || insert) {
- raster.insertAbove(this);
- }
- return raster;
- },
-
- /**
- * {@grouptitle Geometric Tests}
- *
- * Checks whether the item's geometry contains the given point.
- *
- * @example {@paperscript} // Click within and outside the star below
- * // Create a star shaped path:
- * var path = new Path.Star({
- * center: [50, 50],
- * points: 12,
- * radius1: 20,
- * radius2: 40,
- * fillColor: 'black'
- * });
- *
- * // Whenever the user presses the mouse:
- * function onMouseDown(event) {
- * // If the position of the mouse is within the path,
- * // set its fill color to red, otherwise set it to
- * // black:
- * if (path.contains(event.point)) {
- * path.fillColor = 'red';
- * } else {
- * path.fillColor = 'black';
- * }
- * }
- *
- * @param {Point} point the point to check for
- * @return {Boolean}
- */
- contains: function(/* point */) {
- // See CompoundPath#_contains() for the reason for !!
- var matrix = this._matrix;
- return (
- matrix.isInvertible() &&
- !!this._contains(matrix._inverseTransform(Point.read(arguments)))
- );
- },
-
- _contains: function(point) {
- var children = this._children;
- if (children) {
- for (var i = children.length - 1; i >= 0; i--) {
- if (children[i].contains(point))
- return true;
- }
- return false;
- }
- // We only implement it here for items with rectangular content,
- // for anything else we need to override #contains()
- return point.isInside(this.getInternalBounds());
- },
-
- // DOCS:
- // TEST:
- /**
- * @param {Rectangle} rect the rectangle to check against
- * @return {Boolean}
- */
- isInside: function(/* rect */) {
- return Rectangle.read(arguments).contains(this.getBounds());
- },
-
- // Internal helper function, used at the moment for intersects check only.
- // TODO: Move #getIntersections() to Item, make it handle all type of items
- // through _asPathItem(), and support Group items as well, taking nested
- // matrices into account properly!
- _asPathItem: function() {
- // Creates a temporary rectangular path item with this item's bounds.
- return new Path.Rectangle({
- rectangle: this.getInternalBounds(),
- matrix: this._matrix,
- insert: false,
+ Project.inject({
+ hitTest: hitTest,
+ hitTestAll: hitTestAll,
+ _hitTest: hitTestChildren,
});
- },
- // DOCS:
- // TEST:
- /**
- * @param {Item} item the item to check against
- * @return {Boolean}
- */
- intersects: function(item, _matrix) {
- if (!(item instanceof Item))
- return false;
- // Tell getIntersections() to return as soon as some intersections are
- // found, because all we care for here is there are some or none:
- return this._asPathItem().getIntersections(item._asPathItem(), null,
- _matrix, true).length > 0;
- }
-},
-new function() { // Injection scope for hit-test functions shared with project
- function hitTest(/* point, options */) {
- var args = arguments;
- return this._hitTest(
- Point.read(args),
- HitResult.getOptions(args));
- }
+ return {
+ // NOTE: Documentation is in the scope that follows.
+ hitTest: hitTest,
+ hitTestAll: hitTestAll,
+ _hitTestChildren: hitTestChildren,
+ };
+ })(),
+ /** @lends Item# */ {
+ /**
+ * {@grouptitle Hit-testing, Fetching and Matching Items}
+ *
+ * Performs a hit-test on the item and its children (if it is a {@link
+ * Group} or {@link Layer}) at the location of the specified point,
+ * returning the first found hit.
+ *
+ * The options object allows you to control the specifics of the hit-
+ * test and may contain a combination of the following values:
+ *
+ * @name Item#hitTest
+ * @function
+ *
+ * @option [options.tolerance={@link PaperScope#settings}.hitTolerance]
+ * {Number} the tolerance of the hit-test
+ * @option options.class {Function} only hit-test against a specific item
+ * class, or any of its sub-classes, by providing the constructor
+ * function against which an `instanceof` check is performed:
+ * {@values Group, Layer, Path, CompoundPath, Shape, Raster,
+ * SymbolItem, PointText, ...}
+ * @option options.match {Function} a match function to be called for each
+ * found hit result: Return `true` to return the result, `false` to keep
+ * searching
+ * @option [options.fill=true] {Boolean} hit-test the fill of items
+ * @option [options.stroke=true] {Boolean} hit-test the stroke of path
+ * items, taking into account the setting of stroke color and width
+ * @option [options.segments=true] {Boolean} hit-test for {@link
+ * Segment#point} of {@link Path} items
+ * @option options.curves {Boolean} hit-test the curves of path items,
+ * without taking the stroke color or width into account
+ * @option options.handles {Boolean} hit-test for the handles ({@link
+ * Segment#handleIn} / {@link Segment#handleOut}) of path segments.
+ * @option options.ends {Boolean} only hit-test for the first or last
+ * segment points of open path items
+ * @option options.position {Boolean} hit-test the {@link Item#position} of
+ * of items, which depends on the setting of {@link Item#pivot}
+ * @option options.center {Boolean} hit-test the {@link Rectangle#center} of
+ * the bounding rectangle of items ({@link Item#bounds})
+ * @option options.bounds {Boolean} hit-test the corners and side-centers of
+ * the bounding rectangle of items ({@link Item#bounds})
+ * @option options.guides {Boolean} hit-test items that have {@link
+ * Item#guide} set to `true`
+ * @option options.selected {Boolean} only hit selected items
+ *
+ * @param {Point} point the point where the hit-test should be performed
+ * (in global coordinates system).
+ * @param {Object} [options={ fill: true, stroke: true, segments: true,
+ * tolerance: settings.hitTolerance }]
+ * @return {HitResult} a hit result object describing what exactly was hit
+ * or `null` if nothing was hit
+ */
- function hitTestAll(/* point, options */) {
- var args = arguments,
- point = Point.read(args),
- options = HitResult.getOptions(args),
- all = [];
- this._hitTest(point, new Base({ all: all }, options));
- return all;
- }
+ /**
+ * Performs a hit-test on the item and its children (if it is a {@link
+ * Group} or {@link Layer}) at the location of the specified point,
+ * returning all found hits.
+ *
+ * The options object allows you to control the specifics of the hit-
+ * test. See {@link #hitTest(point[, options])} for a list of all options.
+ *
+ * @name Item#hitTestAll
+ * @function
+ * @param {Point} point the point where the hit-test should be performed
+ * (in global coordinates system).
+ * @param {Object} [options={ fill: true, stroke: true, segments: true,
+ * tolerance: settings.hitTolerance }]
+ * @return {HitResult[]} hit result objects for all hits, describing what
+ * exactly was hit or `null` if nothing was hit
+ * @see #hitTest(point[, options]);
+ */
- function hitTestChildren(point, options, viewMatrix, _exclude) {
- // NOTE: _exclude is only used in Group#_hitTestChildren()
- // to exclude #clipItem
- var children = this._children;
- if (children) {
- // Loop backwards, so items that get drawn last are tested first.
- for (var i = children.length - 1; i >= 0; i--) {
- var child = children[i];
- var res = child !== _exclude && child._hitTest(point, options,
- viewMatrix);
- // Only return the found result if we're not asked to collect
- // all matches through hitTestAll()
- if (res && !options.all)
- return res;
+ _hitTest: function (point, options, parentViewMatrix) {
+ if (
+ this._locked ||
+ !this._visible ||
+ (this._guide && !options.guides) ||
+ this.isEmpty()
+ ) {
+ return null;
}
- }
- return null;
- }
-
- Project.inject({
- hitTest: hitTest,
- hitTestAll: hitTestAll,
- _hitTest: hitTestChildren
- });
-
- return {
- // NOTE: Documentation is in the scope that follows.
- hitTest: hitTest,
- hitTestAll: hitTestAll,
- _hitTestChildren: hitTestChildren,
- };
-}, /** @lends Item# */{
- /**
- * {@grouptitle Hit-testing, Fetching and Matching Items}
- *
- * Performs a hit-test on the item and its children (if it is a {@link
- * Group} or {@link Layer}) at the location of the specified point,
- * returning the first found hit.
- *
- * The options object allows you to control the specifics of the hit-
- * test and may contain a combination of the following values:
- *
- * @name Item#hitTest
- * @function
- *
- * @option [options.tolerance={@link PaperScope#settings}.hitTolerance]
- * {Number} the tolerance of the hit-test
- * @option options.class {Function} only hit-test against a specific item
- * class, or any of its sub-classes, by providing the constructor
- * function against which an `instanceof` check is performed:
- * {@values Group, Layer, Path, CompoundPath, Shape, Raster,
- * SymbolItem, PointText, ...}
- * @option options.match {Function} a match function to be called for each
- * found hit result: Return `true` to return the result, `false` to keep
- * searching
- * @option [options.fill=true] {Boolean} hit-test the fill of items
- * @option [options.stroke=true] {Boolean} hit-test the stroke of path
- * items, taking into account the setting of stroke color and width
- * @option [options.segments=true] {Boolean} hit-test for {@link
- * Segment#point} of {@link Path} items
- * @option options.curves {Boolean} hit-test the curves of path items,
- * without taking the stroke color or width into account
- * @option options.handles {Boolean} hit-test for the handles ({@link
- * Segment#handleIn} / {@link Segment#handleOut}) of path segments.
- * @option options.ends {Boolean} only hit-test for the first or last
- * segment points of open path items
- * @option options.position {Boolean} hit-test the {@link Item#position} of
- * of items, which depends on the setting of {@link Item#pivot}
- * @option options.center {Boolean} hit-test the {@link Rectangle#center} of
- * the bounding rectangle of items ({@link Item#bounds})
- * @option options.bounds {Boolean} hit-test the corners and side-centers of
- * the bounding rectangle of items ({@link Item#bounds})
- * @option options.guides {Boolean} hit-test items that have {@link
- * Item#guide} set to `true`
- * @option options.selected {Boolean} only hit selected items
- *
- * @param {Point} point the point where the hit-test should be performed
- * (in global coordinates system).
- * @param {Object} [options={ fill: true, stroke: true, segments: true,
- * tolerance: settings.hitTolerance }]
- * @return {HitResult} a hit result object describing what exactly was hit
- * or `null` if nothing was hit
- */
-
- /**
- * Performs a hit-test on the item and its children (if it is a {@link
- * Group} or {@link Layer}) at the location of the specified point,
- * returning all found hits.
- *
- * The options object allows you to control the specifics of the hit-
- * test. See {@link #hitTest(point[, options])} for a list of all options.
- *
- * @name Item#hitTestAll
- * @function
- * @param {Point} point the point where the hit-test should be performed
- * (in global coordinates system).
- * @param {Object} [options={ fill: true, stroke: true, segments: true,
- * tolerance: settings.hitTolerance }]
- * @return {HitResult[]} hit result objects for all hits, describing what
- * exactly was hit or `null` if nothing was hit
- * @see #hitTest(point[, options]);
- */
- _hitTest: function(point, options, parentViewMatrix) {
- if (this._locked || !this._visible || this._guide && !options.guides
- || this.isEmpty()) {
- return null;
- }
-
- // Check if the point is withing roughBounds + tolerance, but only if
- // this item does not have children, since we'd have to travel up the
- // chain already to determine the rough bounds.
- var matrix = this._matrix,
- // Keep the accumulated matrices up to this item in options, so we
- // can keep calculating the correct _tolerancePadding values.
- viewMatrix = parentViewMatrix
+ // Check if the point is withing roughBounds + tolerance, but only if
+ // this item does not have children, since we'd have to travel up the
+ // chain already to determine the rough bounds.
+ var matrix = this._matrix,
+ // Keep the accumulated matrices up to this item in options, so we
+ // can keep calculating the correct _tolerancePadding values.
+ viewMatrix = parentViewMatrix
? parentViewMatrix.appended(matrix)
- // If this is the first one in the recursion, factor in the
- // zoom of the view and the globalMatrix of the item.
- : this.getGlobalMatrix().prepend(this.getView()._matrix),
- // Calculate the transformed padding as 2D size that describes the
- // transformed tolerance circle / ellipse. Make sure it's never 0
- // since we're using it for division (see checkBounds()).
- tolerance = Math.max(options.tolerance, /*#=*/Numerical.EPSILON),
- // Hit-tests are performed in the item's local coordinate space.
- // To calculate the correct 2D padding for tolerance, we therefore
- // need to apply the inverted item matrix.
- tolerancePadding = options._tolerancePadding = new Size(
- Path._getStrokePadding(tolerance,
- matrix._shiftless().invert()));
- // Transform point to local coordinates.
- point = matrix._inverseTransform(point);
- // If the matrix is non-reversible, point will now be `null`:
- if (!point || !this._children &&
- !this.getBounds({ internal: true, stroke: true, handle: true })
- .expand(tolerancePadding.multiply(2))._containsPoint(point)) {
- return null;
- }
-
- // See if we should check self (own content), by filtering for type,
- // guides and selected items if that's required.
- var checkSelf = !(options.guides && !this._guide
- || options.selected && !this.isSelected()
- // Support legacy Item#type property to match hyphenated
- // class-names.
- || options.type && options.type !== Base.hyphenate(this._class)
- || options.class && !(this instanceof options.class)),
- match = options.match,
- that = this,
- bounds,
- res;
-
- function filter(hit) {
- if (hit && match && !match(hit))
- hit = null;
- // If we're collecting all matches, add it to options.all
- if (hit && options.all)
- options.all.push(hit);
- return hit;
- }
+ : // If this is the first one in the recursion, factor in the
+ // zoom of the view and the globalMatrix of the item.
+ this.getGlobalMatrix().prepend(this.getView()._matrix),
+ // Calculate the transformed padding as 2D size that describes the
+ // transformed tolerance circle / ellipse. Make sure it's never 0
+ // since we're using it for division (see checkBounds()).
+ tolerance = Math.max(
+ options.tolerance,
+ /*#=*/ Numerical.EPSILON
+ ),
+ // Hit-tests are performed in the item's local coordinate space.
+ // To calculate the correct 2D padding for tolerance, we therefore
+ // need to apply the inverted item matrix.
+ tolerancePadding = (options._tolerancePadding = new Size(
+ Path._getStrokePadding(
+ tolerance,
+ matrix._shiftless().invert()
+ )
+ ));
+ // Transform point to local coordinates.
+ point = matrix._inverseTransform(point);
+ // If the matrix is non-reversible, point will now be `null`:
+ if (
+ !point ||
+ (!this._children &&
+ !this.getBounds({
+ internal: true,
+ stroke: true,
+ handle: true,
+ })
+ .expand(tolerancePadding.multiply(2))
+ ._containsPoint(point))
+ ) {
+ return null;
+ }
- function checkPoint(type, part) {
- var pt = part ? bounds['get' + part]() : that.getPosition();
- // Since there are transformations, we cannot simply use a numerical
- // tolerance value. Instead, we divide by a padding size, see above.
- if (point.subtract(pt).divide(tolerancePadding).length <= 1) {
- return new HitResult(type, that, {
- name: part ? Base.hyphenate(part) : type,
- point: pt
- });
+ // See if we should check self (own content), by filtering for type,
+ // guides and selected items if that's required.
+ var checkSelf = !(
+ (options.guides && !this._guide) ||
+ (options.selected && !this.isSelected()) ||
+ // Support legacy Item#type property to match hyphenated
+ // class-names.
+ (options.type &&
+ options.type !== Base.hyphenate(this._class)) ||
+ (options.class && !(this instanceof options.class))
+ ),
+ match = options.match,
+ that = this,
+ bounds,
+ res;
+
+ function filter(hit) {
+ if (hit && match && !match(hit)) hit = null;
+ // If we're collecting all matches, add it to options.all
+ if (hit && options.all) options.all.push(hit);
+ return hit;
}
- }
- var checkPosition = options.position,
- checkCenter = options.center,
- checkBounds = options.bounds;
- // Ignore top level layers by checking for _parent:
- if (checkSelf && this._parent
- && (checkPosition || checkCenter || checkBounds)) {
- if (checkCenter || checkBounds) {
- // Get the internal, untransformed bounds, as we check against
- // transformed points.
- bounds = this.getInternalBounds();
+ function checkPoint(type, part) {
+ var pt = part ? bounds["get" + part]() : that.getPosition();
+ // Since there are transformations, we cannot simply use a numerical
+ // tolerance value. Instead, we divide by a padding size, see above.
+ if (point.subtract(pt).divide(tolerancePadding).length <= 1) {
+ return new HitResult(type, that, {
+ name: part ? Base.hyphenate(part) : type,
+ point: pt,
+ });
+ }
}
- res = checkPosition && checkPoint('position') ||
- checkCenter && checkPoint('center', 'Center');
- if (!res && checkBounds) {
- // TODO: Move these into a static property on Rectangle?
- var points = [
- 'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight',
- 'LeftCenter', 'TopCenter', 'RightCenter', 'BottomCenter'
- ];
- for (var i = 0; i < 8 && !res; i++) {
- res = checkPoint('bounds', points[i]);
+
+ var checkPosition = options.position,
+ checkCenter = options.center,
+ checkBounds = options.bounds;
+ // Ignore top level layers by checking for _parent:
+ if (
+ checkSelf &&
+ this._parent &&
+ (checkPosition || checkCenter || checkBounds)
+ ) {
+ if (checkCenter || checkBounds) {
+ // Get the internal, untransformed bounds, as we check against
+ // transformed points.
+ bounds = this.getInternalBounds();
}
+ res =
+ (checkPosition && checkPoint("position")) ||
+ (checkCenter && checkPoint("center", "Center"));
+ if (!res && checkBounds) {
+ // TODO: Move these into a static property on Rectangle?
+ var points = [
+ "TopLeft",
+ "TopRight",
+ "BottomLeft",
+ "BottomRight",
+ "LeftCenter",
+ "TopCenter",
+ "RightCenter",
+ "BottomCenter",
+ ];
+ for (var i = 0; i < 8 && !res; i++) {
+ res = checkPoint("bounds", points[i]);
+ }
+ }
+ res = filter(res);
}
- res = filter(res);
- }
- if (!res) {
- res = this._hitTestChildren(point, options, viewMatrix)
- // NOTE: We don't call match on _hitTestChildren() because
- // it is already called internally.
- || checkSelf
- && filter(this._hitTestSelf(point, options, viewMatrix,
- // If the item has a non-scaling stroke, we need to
- // apply the inverted viewMatrix to stroke dimensions.
- this.getStrokeScaling() ? null
- : viewMatrix._shiftless().invert()))
- || null;
- }
- // Transform the point back to the outer coordinate system.
- if (res && res.point) {
- res.point = matrix.transform(res.point);
- }
- return res;
- },
+ if (!res) {
+ res =
+ this._hitTestChildren(point, options, viewMatrix) ||
+ // NOTE: We don't call match on _hitTestChildren() because
+ // it is already called internally.
+ (checkSelf &&
+ filter(
+ this._hitTestSelf(
+ point,
+ options,
+ viewMatrix,
+ // If the item has a non-scaling stroke, we need to
+ // apply the inverted viewMatrix to stroke dimensions.
+ this.getStrokeScaling()
+ ? null
+ : viewMatrix._shiftless().invert()
+ )
+ )) ||
+ null;
+ }
+ // Transform the point back to the outer coordinate system.
+ if (res && res.point) {
+ res.point = matrix.transform(res.point);
+ }
+ return res;
+ },
- _hitTestSelf: function(point, options) {
- // The default implementation honly handles 'fill' through #_contains()
- if (options.fill && this.hasFill() && this._contains(point))
- return new HitResult('fill', this);
- },
+ _hitTestSelf: function (point, options) {
+ // The default implementation honly handles 'fill' through #_contains()
+ if (options.fill && this.hasFill() && this._contains(point))
+ return new HitResult("fill", this);
+ },
- /**
- * Checks whether the item matches the criteria described by the given
- * object, by iterating over all of its properties and matching against
- * their values through {@link #matches(name, compare)}.
- *
- * See {@link Project#getItems(options)} for a selection of illustrated
- * examples.
- *
- * @name Item#matches
- * @function
- *
- * @param {Object|Function} options the criteria to match against
- * @return {Boolean} {@true if the item matches all the criteria}
- * @see #getItems(options)
- */
- /**
- * Checks whether the item matches the given criteria. Extended matching is
- * possible by providing a compare function or a regular expression.
- * Matching points, colors only work as a comparison of the full object, not
- * partial matching (e.g. only providing the x-coordinate to match all
- * points with that x-value). Partial matching does work for
- * {@link Item#data}.
- *
- * See {@link Project#getItems(options)} for a selection of illustrated
- * examples.
- *
- * @name Item#matches
- * @function
- *
- * @param {String} name the name of the state to match against
- * @param {Object} compare the value, function or regular expression to
- * compare against
- * @return {Boolean} {@true if the item matches the state}
- * @see #getItems(options)
- */
- matches: function(name, compare) {
- // matchObject() is used to match against objects in a nested manner.
- // This is useful for matching against Item#data.
- function matchObject(obj1, obj2) {
- for (var i in obj1) {
- if (obj1.hasOwnProperty(i)) {
- var val1 = obj1[i],
- val2 = obj2[i];
- if (Base.isPlainObject(val1) && Base.isPlainObject(val2)) {
- if (!matchObject(val1, val2))
+ /**
+ * Checks whether the item matches the criteria described by the given
+ * object, by iterating over all of its properties and matching against
+ * their values through {@link #matches(name, compare)}.
+ *
+ * See {@link Project#getItems(options)} for a selection of illustrated
+ * examples.
+ *
+ * @name Item#matches
+ * @function
+ *
+ * @param {Object|Function} options the criteria to match against
+ * @return {Boolean} {@true if the item matches all the criteria}
+ * @see #getItems(options)
+ */
+ /**
+ * Checks whether the item matches the given criteria. Extended matching is
+ * possible by providing a compare function or a regular expression.
+ * Matching points, colors only work as a comparison of the full object, not
+ * partial matching (e.g. only providing the x-coordinate to match all
+ * points with that x-value). Partial matching does work for
+ * {@link Item#data}.
+ *
+ * See {@link Project#getItems(options)} for a selection of illustrated
+ * examples.
+ *
+ * @name Item#matches
+ * @function
+ *
+ * @param {String} name the name of the state to match against
+ * @param {Object} compare the value, function or regular expression to
+ * compare against
+ * @return {Boolean} {@true if the item matches the state}
+ * @see #getItems(options)
+ */
+ matches: function (name, compare) {
+ // matchObject() is used to match against objects in a nested manner.
+ // This is useful for matching against Item#data.
+ function matchObject(obj1, obj2) {
+ for (var i in obj1) {
+ if (obj1.hasOwnProperty(i)) {
+ var val1 = obj1[i],
+ val2 = obj2[i];
+ if (
+ Base.isPlainObject(val1) &&
+ Base.isPlainObject(val2)
+ ) {
+ if (!matchObject(val1, val2)) return false;
+ } else if (!Base.equals(val1, val2)) {
return false;
- } else if (!Base.equals(val1, val2)) {
- return false;
+ }
}
}
+ return true;
}
- return true;
- }
- var type = typeof name;
- if (type === 'object') {
- // `name` is the match object, not a string
- for (var key in name) {
- if (name.hasOwnProperty(key) && !this.matches(key, name[key]))
- return false;
- }
- return true;
- } else if (type === 'function') {
- return name(this);
- } else if (name === 'match') {
- return compare(this);
- } else {
- var value = /^(empty|editable)$/.test(name)
- // Handle boolean test functions separately, by calling them
- // to get the value.
- ? this['is' + Base.capitalize(name)]()
- // Support legacy Item#type property to match hyphenated
+ var type = typeof name;
+ if (type === "object") {
+ // `name` is the match object, not a string
+ for (var key in name) {
+ if (
+ name.hasOwnProperty(key) &&
+ !this.matches(key, name[key])
+ )
+ return false;
+ }
+ return true;
+ } else if (type === "function") {
+ return name(this);
+ } else if (name === "match") {
+ return compare(this);
+ } else {
+ var value = /^(empty|editable)$/.test(name)
+ ? // Handle boolean test functions separately, by calling them
+ // to get the value.
+ this["is" + Base.capitalize(name)]()
+ : // Support legacy Item#type property to match hyphenated
// class-names.
// TODO: Remove after December 2016.
- : name === 'type'
- ? Base.hyphenate(this._class)
- : this[name];
- if (name === 'class') {
- if (typeof compare === 'function')
- return this instanceof compare;
- // Compare further with the _class property value instead.
- value = this._class;
- }
- if (typeof compare === 'function') {
- return !!compare(value);
- } else if (compare) {
- if (compare.test) { // RegExp-ish
- return compare.test(value);
- } else if (Base.isPlainObject(compare)) {
- return matchObject(compare, value);
+ name === "type"
+ ? Base.hyphenate(this._class)
+ : this[name];
+ if (name === "class") {
+ if (typeof compare === "function")
+ return this instanceof compare;
+ // Compare further with the _class property value instead.
+ value = this._class;
+ }
+ if (typeof compare === "function") {
+ return !!compare(value);
+ } else if (compare) {
+ if (compare.test) {
+ // RegExp-ish
+ return compare.test(value);
+ } else if (Base.isPlainObject(compare)) {
+ return matchObject(compare, value);
+ }
}
+ return Base.equals(value, compare);
}
- return Base.equals(value, compare);
- }
- },
+ },
- /**
- * Fetch the descendants (children or children of children) of this item
- * that match the properties in the specified object. Extended matching is
- * possible by providing a compare function or regular expression. Matching
- * points, colors only work as a comparison of the full object, not partial
- * matching (e.g. only providing the x- coordinate to match all points with
- * that x-value). Partial matching does work for {@link Item#data}.
- *
- * Matching items against a rectangular area is also possible, by setting
- * either `options.inside` or `options.overlapping` to a rectangle
- * describing the area in which the items either have to be fully or partly
- * contained.
- *
- * See {@link Project#getItems(options)} for a selection of illustrated
- * examples.
- *
- * @option [options.recursive=true] {Boolean} whether to loop recursively
- * through all children, or stop at the current level
- * @option options.match {Function} a match function to be called for each
- * item, allowing the definition of more flexible item checks that are
- * not bound to properties. If no other match properties are defined,
- * this function can also be passed instead of the `options` object
- * @option options.class {Function} the constructor function of the item
- * type to match against
- * @option options.inside {Rectangle} the rectangle in which the items need
- * to be fully contained
- * @option options.overlapping {Rectangle} the rectangle with which the
- * items need to at least partly overlap
- *
- * @param {Object|Function} options the criteria to match against
- * @return {Item[]} the list of matching descendant items
- * @see #matches(options)
- */
- getItems: function(options) {
- return Item._getItems(this, options, this._matrix);
- },
+ /**
+ * Fetch the descendants (children or children of children) of this item
+ * that match the properties in the specified object. Extended matching is
+ * possible by providing a compare function or regular expression. Matching
+ * points, colors only work as a comparison of the full object, not partial
+ * matching (e.g. only providing the x- coordinate to match all points with
+ * that x-value). Partial matching does work for {@link Item#data}.
+ *
+ * Matching items against a rectangular area is also possible, by setting
+ * either `options.inside` or `options.overlapping` to a rectangle
+ * describing the area in which the items either have to be fully or partly
+ * contained.
+ *
+ * See {@link Project#getItems(options)} for a selection of illustrated
+ * examples.
+ *
+ * @option [options.recursive=true] {Boolean} whether to loop recursively
+ * through all children, or stop at the current level
+ * @option options.match {Function} a match function to be called for each
+ * item, allowing the definition of more flexible item checks that are
+ * not bound to properties. If no other match properties are defined,
+ * this function can also be passed instead of the `options` object
+ * @option options.class {Function} the constructor function of the item
+ * type to match against
+ * @option options.inside {Rectangle} the rectangle in which the items need
+ * to be fully contained
+ * @option options.overlapping {Rectangle} the rectangle with which the
+ * items need to at least partly overlap
+ *
+ * @param {Object|Function} options the criteria to match against
+ * @return {Item[]} the list of matching descendant items
+ * @see #matches(options)
+ */
+ getItems: function (options) {
+ return Item._getItems(this, options, this._matrix);
+ },
- /**
- * Fetch the first descendant (child or child of child) of this item
- * that matches the properties in the specified object.
- * Extended matching is possible by providing a compare function or
- * regular expression. Matching points, colors only work as a comparison
- * of the full object, not partial matching (e.g. only providing the x-
- * coordinate to match all points with that x-value). Partial matching
- * does work for {@link Item#data}.
- * See {@link Project#getItems(match)} for a selection of illustrated
- * examples.
- *
- * @param {Object|Function} options the criteria to match against
- * @return {Item} the first descendant item matching the given criteria
- * @see #getItems(options)
- */
- getItem: function(options) {
- return Item._getItems(this, options, this._matrix, null, true)[0]
- || null;
- },
+ /**
+ * Fetch the first descendant (child or child of child) of this item
+ * that matches the properties in the specified object.
+ * Extended matching is possible by providing a compare function or
+ * regular expression. Matching points, colors only work as a comparison
+ * of the full object, not partial matching (e.g. only providing the x-
+ * coordinate to match all points with that x-value). Partial matching
+ * does work for {@link Item#data}.
+ * See {@link Project#getItems(match)} for a selection of illustrated
+ * examples.
+ *
+ * @param {Object|Function} options the criteria to match against
+ * @return {Item} the first descendant item matching the given criteria
+ * @see #getItems(options)
+ */
+ getItem: function (options) {
+ return (
+ Item._getItems(this, options, this._matrix, null, true)[0] ||
+ null
+ );
+ },
- statics: /** @lends Item */{
- // NOTE: We pass children instead of item as first argument so the
- // method can be used for Project#layers as well in Project.
- _getItems: function _getItems(item, options, matrix, param, firstOnly) {
- if (!param) {
- // Set up a couple of "side-car" values for the recursive calls
- // of _getItems below, mainly related to the handling of
- // inside / overlapping:
- var obj = typeof options === 'object' && options,
- overlapping = obj && obj.overlapping,
- inside = obj && obj.inside,
- // If overlapping is set, we also perform the inside check:
- bounds = overlapping || inside,
- rect = bounds && Rectangle.read([bounds]);
- param = {
- items: [], // The list to contain the results.
- recursive: obj && obj.recursive !== false,
- inside: !!inside,
- overlapping: !!overlapping,
- rect: rect,
- path: overlapping && new Path.Rectangle({
- rectangle: rect,
- insert: false
- })
- };
- if (obj) {
- // Create a copy of the options object that doesn't contain
- // these special properties:
- options = Base.filter({}, options, {
- recursive: true, inside: true, overlapping: true
- });
- }
- }
- var children = item._children,
- items = param.items,
- rect = param.rect;
- matrix = rect && (matrix || new Matrix());
- for (var i = 0, l = children && children.length; i < l; i++) {
- var child = children[i],
- childMatrix = matrix && matrix.appended(child._matrix),
- add = true;
- if (rect) {
- var bounds = child.getBounds(childMatrix);
- // Regardless of the setting of inside / overlapping, if the
- // bounds don't even intersect, we can skip this child.
- if (!rect.intersects(bounds))
- continue;
- if (!(rect.contains(bounds)
- // First check the bounds, if the rect is fully
- // contained, we are always overlapping, and don't
- // need to perform further checks, otherwise perform
- // a proper #intersects() check:
- || param.overlapping && (bounds.contains(rect)
- || param.path.intersects(child, childMatrix))))
- add = false;
- }
- if (add && child.matches(options)) {
- items.push(child);
- if (firstOnly)
- break;
+ statics: /** @lends Item */ {
+ // NOTE: We pass children instead of item as first argument so the
+ // method can be used for Project#layers as well in Project.
+ _getItems: function _getItems(
+ item,
+ options,
+ matrix,
+ param,
+ firstOnly
+ ) {
+ if (!param) {
+ // Set up a couple of "side-car" values for the recursive calls
+ // of _getItems below, mainly related to the handling of
+ // inside / overlapping:
+ var obj = typeof options === "object" && options,
+ overlapping = obj && obj.overlapping,
+ inside = obj && obj.inside,
+ // If overlapping is set, we also perform the inside check:
+ bounds = overlapping || inside,
+ rect = bounds && Rectangle.read([bounds]);
+ param = {
+ items: [], // The list to contain the results.
+ recursive: obj && obj.recursive !== false,
+ inside: !!inside,
+ overlapping: !!overlapping,
+ rect: rect,
+ path:
+ overlapping &&
+ new Path.Rectangle({
+ rectangle: rect,
+ insert: false,
+ }),
+ };
+ if (obj) {
+ // Create a copy of the options object that doesn't contain
+ // these special properties:
+ options = Base.filter({}, options, {
+ recursive: true,
+ inside: true,
+ overlapping: true,
+ });
+ }
}
- if (param.recursive !== false) {
- _getItems(child, options, childMatrix, param, firstOnly);
+ var children = item._children,
+ items = param.items,
+ rect = param.rect;
+ matrix = rect && (matrix || new Matrix());
+ for (var i = 0, l = children && children.length; i < l; i++) {
+ var child = children[i],
+ childMatrix = matrix && matrix.appended(child._matrix),
+ add = true;
+ if (rect) {
+ var bounds = child.getBounds(childMatrix);
+ // Regardless of the setting of inside / overlapping, if the
+ // bounds don't even intersect, we can skip this child.
+ if (!rect.intersects(bounds)) continue;
+ if (
+ !(
+ rect.contains(bounds) ||
+ // First check the bounds, if the rect is fully
+ // contained, we are always overlapping, and don't
+ // need to perform further checks, otherwise perform
+ // a proper #intersects() check:
+ (param.overlapping &&
+ (bounds.contains(rect) ||
+ param.path.intersects(
+ child,
+ childMatrix
+ )))
+ )
+ )
+ add = false;
+ }
+ if (add && child.matches(options)) {
+ items.push(child);
+ if (firstOnly) break;
+ }
+ if (param.recursive !== false) {
+ _getItems(
+ child,
+ options,
+ childMatrix,
+ param,
+ firstOnly
+ );
+ }
+ if (firstOnly && items.length > 0) break;
}
- if (firstOnly && items.length > 0)
- break;
- }
- return items;
- }
- }
-}, /** @lends Item# */{
- /**
- * {@grouptitle Importing / Exporting JSON and SVG}
- *
- * Exports (serializes) the item with its content and child items to a JSON
- * data string.
- *
- * @name Item#exportJSON
- * @function
- *
- * @option [options.asString=true] {Boolean} whether the JSON is returned as
- * a `Object` or a `String`
- * @option [options.precision=5] {Number} the amount of fractional digits in
- * numbers used in JSON data
- *
- * @param {Object} [options] the serialization options
- * @return {String} the exported JSON data
- */
-
- /**
- * Imports (deserializes) the stored JSON data into this item. If the data
- * describes an item of the same class or a parent class of the item, the
- * data is imported into the item itself. If not, the imported item is added
- * to this item's {@link Item#children} list. Note that not all type of
- * items can have children.
- *
- * @param {String} json the JSON data to import from
- * @return {Item}
- */
- importJSON: function(json) {
- // Try importing into `this`. If another item is returned, try adding
- // it as a child (this won't be successful on some classes, returning
- // null).
- var res = Base.importJSON(json, this);
- return res !== this ? this.addChild(res) : res;
+ return items;
+ },
+ },
},
+ /** @lends Item# */ {
+ /**
+ * {@grouptitle Importing / Exporting JSON and SVG}
+ *
+ * Exports (serializes) the item with its content and child items to a JSON
+ * data string.
+ *
+ * @name Item#exportJSON
+ * @function
+ *
+ * @option [options.asString=true] {Boolean} whether the JSON is returned as
+ * a `Object` or a `String`
+ * @option [options.precision=5] {Number} the amount of fractional digits in
+ * numbers used in JSON data
+ *
+ * @param {Object} [options] the serialization options
+ * @return {String} the exported JSON data
+ */
- /**
- * Exports the item with its content and child items as an SVG DOM.
- *
- * @name Item#exportSVG
- * @function
- *
- * @option [options.bounds='view'] {String|Rectangle} the bounds of the area
- * to export, either as a string ({@values 'view', content'}), or a
- * {@link Rectangle} object: `'view'` uses the view bounds,
- * `'content'` uses the stroke bounds of all content
- * @option [options.matrix=paper.view.matrix] {Matrix} the matrix with which
- * to transform the exported content: If `options.bounds` is set to
- * `'view'`, `paper.view.matrix` is used, for all other settings of
- * `options.bounds` the identity matrix is used.
- * @option [options.asString=false] {Boolean} whether a SVG node or a
- * `String` is to be returned
- * @option [options.precision=5] {Number} the amount of fractional digits in
- * numbers used in SVG data
- * @option [options.matchShapes=false] {Boolean} whether path items should
- * tried to be converted to SVG shape items (rect, circle, ellipse,
- * line, polyline, polygon), if their geometries match
- * @option [options.embedImages=true] {Boolean} whether raster images should
- * be embedded as base64 data inlined in the xlink:href attribute, or
- * kept as a link to their external URL.
- *
- * @param {Object} [options] the export options
- * @return {SVGElement|String} the item converted to an SVG node or a
- * `String` depending on `option.asString` value
- */
+ /**
+ * Imports (deserializes) the stored JSON data into this item. If the data
+ * describes an item of the same class or a parent class of the item, the
+ * data is imported into the item itself. If not, the imported item is added
+ * to this item's {@link Item#children} list. Note that not all type of
+ * items can have children.
+ *
+ * @param {String} json the JSON data to import from
+ * @return {Item}
+ */
+ importJSON: function (json) {
+ // Try importing into `this`. If another item is returned, try adding
+ // it as a child (this won't be successful on some classes, returning
+ // null).
+ var res = Base.importJSON(json, this);
+ return res !== this ? this.addChild(res) : res;
+ },
- /**
- * Converts the provided SVG content into Paper.js items and adds them to
- * the this item's children list. Note that the item is not cleared first.
- * You can call {@link Item#removeChildren()} to do so.
- *
- * @name Item#importSVG
- * @function
- *
- * @option [options.expandShapes=false] {Boolean} whether imported shape
- * items should be expanded to path items
- * @option options.onLoad {Function} the callback function to call once the
- * SVG content is loaded from the given URL receiving two arguments: the
- * converted `item` and the original `svg` data as a string. Only
- * required when loading from external resources.
- * @option options.onError {Function} the callback function to call if an
- * error occurs during loading. Only required when loading from external
- * resources.
- * @option [options.insert=true] {Boolean} whether the imported items should
- * be added to the item that `importSVG()` is called on
- * @option [options.applyMatrix={@link PaperScope#settings}.applyMatrix]
- * {Boolean} whether the imported items should have their transformation
- * matrices applied to their contents or not
- *
- * @param {SVGElement|String} svg the SVG content to import, either as a SVG
- * DOM node, a string containing SVG content, or a string describing the
- * URL of the SVG file to fetch.
- * @param {Object} [options] the import options
- * @return {Item} the newly created Paper.js item containing the converted
- * SVG content
- */
- /**
- * Imports the provided external SVG file, converts it into Paper.js items
- * and adds them to the this item's children list. Note that the item is not
- * cleared first. You can call {@link Item#removeChildren()} to do so.
- *
- * @name Item#importSVG
- * @function
- *
- * @param {SVGElement|String} svg the URL of the SVG file to fetch.
- * @param {Function} onLoad the callback function to call once the SVG
- * content is loaded from the given URL receiving two arguments: the
- * converted `item` and the original `svg` data as a string. Only
- * required when loading from external files.
- * @return {Item} the newly created Paper.js item containing the converted
- * SVG content
- */
+ /**
+ * Exports the item with its content and child items as an SVG DOM.
+ *
+ * @name Item#exportSVG
+ * @function
+ *
+ * @option [options.bounds='view'] {String|Rectangle} the bounds of the area
+ * to export, either as a string ({@values 'view', content'}), or a
+ * {@link Rectangle} object: `'view'` uses the view bounds,
+ * `'content'` uses the stroke bounds of all content
+ * @option [options.matrix=paper.view.matrix] {Matrix} the matrix with which
+ * to transform the exported content: If `options.bounds` is set to
+ * `'view'`, `paper.view.matrix` is used, for all other settings of
+ * `options.bounds` the identity matrix is used.
+ * @option [options.asString=false] {Boolean} whether a SVG node or a
+ * `String` is to be returned
+ * @option [options.precision=5] {Number} the amount of fractional digits in
+ * numbers used in SVG data
+ * @option [options.matchShapes=false] {Boolean} whether path items should
+ * tried to be converted to SVG shape items (rect, circle, ellipse,
+ * line, polyline, polygon), if their geometries match
+ * @option [options.embedImages=true] {Boolean} whether raster images should
+ * be embedded as base64 data inlined in the xlink:href attribute, or
+ * kept as a link to their external URL.
+ *
+ * @param {Object} [options] the export options
+ * @return {SVGElement|String} the item converted to an SVG node or a
+ * `String` depending on `option.asString` value
+ */
- /**
- * {@grouptitle Hierarchy Operations}
- *
- * Adds the specified item as a child of this item at the end of the its
- * {@link #children} list. You can use this function for groups, compound
- * paths and layers.
- *
- * @param {Item} item the item to be added as a child
- * @return {Item} the added item, or `null` if adding was not possible
- */
- addChild: function(item) {
- return this.insertChild(undefined, item);
- },
+ /**
+ * Converts the provided SVG content into Paper.js items and adds them to
+ * the this item's children list. Note that the item is not cleared first.
+ * You can call {@link Item#removeChildren()} to do so.
+ *
+ * @name Item#importSVG
+ * @function
+ *
+ * @option [options.expandShapes=false] {Boolean} whether imported shape
+ * items should be expanded to path items
+ * @option options.onLoad {Function} the callback function to call once the
+ * SVG content is loaded from the given URL receiving two arguments: the
+ * converted `item` and the original `svg` data as a string. Only
+ * required when loading from external resources.
+ * @option options.onError {Function} the callback function to call if an
+ * error occurs during loading. Only required when loading from external
+ * resources.
+ * @option [options.insert=true] {Boolean} whether the imported items should
+ * be added to the item that `importSVG()` is called on
+ * @option [options.applyMatrix={@link PaperScope#settings}.applyMatrix]
+ * {Boolean} whether the imported items should have their transformation
+ * matrices applied to their contents or not
+ *
+ * @param {SVGElement|String} svg the SVG content to import, either as a SVG
+ * DOM node, a string containing SVG content, or a string describing the
+ * URL of the SVG file to fetch.
+ * @param {Object} [options] the import options
+ * @return {Item} the newly created Paper.js item containing the converted
+ * SVG content
+ */
+ /**
+ * Imports the provided external SVG file, converts it into Paper.js items
+ * and adds them to the this item's children list. Note that the item is not
+ * cleared first. You can call {@link Item#removeChildren()} to do so.
+ *
+ * @name Item#importSVG
+ * @function
+ *
+ * @param {SVGElement|String} svg the URL of the SVG file to fetch.
+ * @param {Function} onLoad the callback function to call once the SVG
+ * content is loaded from the given URL receiving two arguments: the
+ * converted `item` and the original `svg` data as a string. Only
+ * required when loading from external files.
+ * @return {Item} the newly created Paper.js item containing the converted
+ * SVG content
+ */
- /**
- * Inserts the specified item as a child of this item at the specified index
- * in its {@link #children} list. You can use this function for groups,
- * compound paths and layers.
- *
- * @param {Number} index the index at which to insert the item
- * @param {Item} item the item to be inserted as a child
- * @return {Item} the inserted item, or `null` if inserting was not possible
- */
- insertChild: function(index, item) {
- var res = item ? this.insertChildren(index, [item]) : null;
- return res && res[0];
- },
+ /**
+ * {@grouptitle Hierarchy Operations}
+ *
+ * Adds the specified item as a child of this item at the end of the its
+ * {@link #children} list. You can use this function for groups, compound
+ * paths and layers.
+ *
+ * @param {Item} item the item to be added as a child
+ * @return {Item} the added item, or `null` if adding was not possible
+ */
+ addChild: function (item) {
+ return this.insertChild(undefined, item);
+ },
- /**
- * Adds the specified items as children of this item at the end of the its
- * children list. You can use this function for groups, compound paths and
- * layers.
- *
- * @param {Item[]} items the items to be added as children
- * @return {Item[]} the added items, or `null` if adding was not possible
- */
- addChildren: function(items) {
- return this.insertChildren(this._children.length, items);
- },
+ /**
+ * Inserts the specified item as a child of this item at the specified index
+ * in its {@link #children} list. You can use this function for groups,
+ * compound paths and layers.
+ *
+ * @param {Number} index the index at which to insert the item
+ * @param {Item} item the item to be inserted as a child
+ * @return {Item} the inserted item, or `null` if inserting was not possible
+ */
+ insertChild: function (index, item) {
+ var res = item ? this.insertChildren(index, [item]) : null;
+ return res && res[0];
+ },
- /**
- * Inserts the specified items as children of this item at the specified
- * index in its {@link #children} list. You can use this function for
- * groups, compound paths and layers.
- *
- * @param {Number} index
- * @param {Item[]} items the items to be appended as children
- * @return {Item[]} the inserted items, or `null` if inserted was not
- * possible
- */
- insertChildren: function(index, items) {
- var children = this._children;
- if (children && items && items.length > 0) {
- // We need to clone items because it may be an Item#children array.
- // Also, we're removing elements if they don't match _type.
- // Use Base.slice() because items can be an arguments object.
- items = Base.slice(items);
- // Remove the items from their parents first, since they might be
- // inserted into their own parents, affecting indices.
- // Use the loop also to filter invalid items.
- var inserted = {};
- for (var i = items.length - 1; i >= 0; i--) {
- var item = items[i],
- id = item && item._id;
- // If an item was inserted already, it must be included multiple
- // times in the items array. Only insert once.
- if (!item || inserted[id]) {
- items.splice(i, 1);
- } else {
- // Notify parent of change. Don't notify item itself yet,
- // as we're doing so when adding it to the new owner below.
- item._remove(false, true);
- inserted[id] = true;
+ /**
+ * Adds the specified items as children of this item at the end of the its
+ * children list. You can use this function for groups, compound paths and
+ * layers.
+ *
+ * @param {Item[]} items the items to be added as children
+ * @return {Item[]} the added items, or `null` if adding was not possible
+ */
+ addChildren: function (items) {
+ return this.insertChildren(this._children.length, items);
+ },
+
+ /**
+ * Inserts the specified items as children of this item at the specified
+ * index in its {@link #children} list. You can use this function for
+ * groups, compound paths and layers.
+ *
+ * @param {Number} index
+ * @param {Item[]} items the items to be appended as children
+ * @return {Item[]} the inserted items, or `null` if inserted was not
+ * possible
+ */
+ insertChildren: function (index, items) {
+ var children = this._children;
+ if (children && items && items.length > 0) {
+ // We need to clone items because it may be an Item#children array.
+ // Also, we're removing elements if they don't match _type.
+ // Use Base.slice() because items can be an arguments object.
+ items = Base.slice(items);
+ // Remove the items from their parents first, since they might be
+ // inserted into their own parents, affecting indices.
+ // Use the loop also to filter invalid items.
+ var inserted = {};
+ for (var i = items.length - 1; i >= 0; i--) {
+ var item = items[i],
+ id = item && item._id;
+ // If an item was inserted already, it must be included multiple
+ // times in the items array. Only insert once.
+ if (!item || inserted[id]) {
+ items.splice(i, 1);
+ } else {
+ // Notify parent of change. Don't notify item itself yet,
+ // as we're doing so when adding it to the new owner below.
+ item._remove(false, true);
+ inserted[id] = true;
+ }
}
+ Base.splice(children, items, index, 0);
+ var project = this._project,
+ // See #_remove() for an explanation of this:
+ notifySelf = project._changes;
+ for (var i = 0, l = items.length; i < l; i++) {
+ var item = items[i],
+ name = item._name;
+ item._parent = this;
+ item._setProject(project, true);
+ // Set the name again to make sure all name lookup structures
+ // are kept in sync.
+ if (name) item.setName(name);
+ if (notifySelf) item._changed(/*#=*/ Change.INSERTION);
+ }
+ this._changed(/*#=*/ Change.CHILDREN);
+ } else {
+ items = null;
}
- Base.splice(children, items, index, 0);
- var project = this._project,
- // See #_remove() for an explanation of this:
- notifySelf = project._changes;
- for (var i = 0, l = items.length; i < l; i++) {
- var item = items[i],
- name = item._name;
- item._parent = this;
- item._setProject(project, true);
- // Set the name again to make sure all name lookup structures
- // are kept in sync.
- if (name)
- item.setName(name);
- if (notifySelf)
- item._changed(/*#=*/Change.INSERTION);
- }
- this._changed(/*#=*/Change.CHILDREN);
- } else {
- items = null;
- }
- return items;
- },
-
- // Internal alias, so both Project and Item can be used in #copyTo(), and
- // through _getOwner() in the various Item#insert*() methods.
- _insertItem: '#insertChild',
+ return items;
+ },
- /**
- * Private helper method used by {@link #insertAbove(item)} and
- * {@link #insertBelow(item)}, to insert this item in relation to a
- * specified other item.
- *
- * @param {Item} item the item in relation to which which it should be
- * inserted
- * @param {Number} offset the offset at which the item should be inserted
- * @return {Item} the inserted item, or `null` if inserting was not possible
- */
- _insertAt: function(item, offset) {
- var owner = item && item._getOwner(),
- // Only insert if the item is not the same as `this`, and if it
- // actually has an owner into which we can insert.
- res = item !== this && owner ? this : null;
- if (res) {
- // Notify parent of change. Don't notify item itself yet,
- // as we're doing so when adding it to the new owner below.
- res._remove(false, true);
- owner._insertItem(item._index + offset, res);
- }
- return res;
- },
+ // Internal alias, so both Project and Item can be used in #copyTo(), and
+ // through _getOwner() in the various Item#insert*() methods.
+ _insertItem: "#insertChild",
- /**
- * Inserts this item above the specified item.
- *
- * @param {Item} item the item above which it should be inserted
- * @return {Item} the inserted item, or `null` if inserting was not possible
- */
- insertAbove: function(item) {
- return this._insertAt(item, 1);
- },
+ /**
+ * Private helper method used by {@link #insertAbove(item)} and
+ * {@link #insertBelow(item)}, to insert this item in relation to a
+ * specified other item.
+ *
+ * @param {Item} item the item in relation to which which it should be
+ * inserted
+ * @param {Number} offset the offset at which the item should be inserted
+ * @return {Item} the inserted item, or `null` if inserting was not possible
+ */
+ _insertAt: function (item, offset) {
+ var owner = item && item._getOwner(),
+ // Only insert if the item is not the same as `this`, and if it
+ // actually has an owner into which we can insert.
+ res = item !== this && owner ? this : null;
+ if (res) {
+ // Notify parent of change. Don't notify item itself yet,
+ // as we're doing so when adding it to the new owner below.
+ res._remove(false, true);
+ owner._insertItem(item._index + offset, res);
+ }
+ return res;
+ },
- /**
- * Inserts this item below the specified item.
- *
- * @param {Item} item the item below which it should be inserted
- * @return {Item} the inserted item, or `null` if inserting was not possible
- */
- insertBelow: function(item) {
- return this._insertAt(item, 0);
- },
+ /**
+ * Inserts this item above the specified item.
+ *
+ * @param {Item} item the item above which it should be inserted
+ * @return {Item} the inserted item, or `null` if inserting was not possible
+ */
+ insertAbove: function (item) {
+ return this._insertAt(item, 1);
+ },
- /**
- * Sends this item to the back of all other items within the same parent.
- */
- sendToBack: function() {
- var owner = this._getOwner();
- return owner ? owner._insertItem(0, this) : null;
- },
+ /**
+ * Inserts this item below the specified item.
+ *
+ * @param {Item} item the item below which it should be inserted
+ * @return {Item} the inserted item, or `null` if inserting was not possible
+ */
+ insertBelow: function (item) {
+ return this._insertAt(item, 0);
+ },
- /**
- * Brings this item to the front of all other items within the same parent.
- */
- bringToFront: function() {
- var owner = this._getOwner();
- return owner ? owner._insertItem(undefined, this) : null;
- },
+ /**
+ * Sends this item to the back of all other items within the same parent.
+ */
+ sendToBack: function () {
+ var owner = this._getOwner();
+ return owner ? owner._insertItem(0, this) : null;
+ },
- /**
- * Inserts the specified item as a child of this item by appending it to
- * the list of children and moving it above all other children. You can
- * use this function for groups, compound paths and layers.
- *
- * @function
- * @param {Item} item the item to be appended as a child
- * @deprecated use {@link #addChild(item)} instead.
- */
- appendTop: '#addChild',
+ /**
+ * Brings this item to the front of all other items within the same parent.
+ */
+ bringToFront: function () {
+ var owner = this._getOwner();
+ return owner ? owner._insertItem(undefined, this) : null;
+ },
- /**
- * Inserts the specified item as a child of this item by appending it to
- * the list of children and moving it below all other children. You can
- * use this function for groups, compound paths and layers.
- *
- * @param {Item} item the item to be appended as a child
- * @deprecated use {@link #insertChild(index, item)} instead.
- */
- appendBottom: function(item) {
- return this.insertChild(0, item);
- },
+ /**
+ * Inserts the specified item as a child of this item by appending it to
+ * the list of children and moving it above all other children. You can
+ * use this function for groups, compound paths and layers.
+ *
+ * @function
+ * @param {Item} item the item to be appended as a child
+ * @deprecated use {@link #addChild(item)} instead.
+ */
+ appendTop: "#addChild",
- /**
- * Moves this item above the specified item.
- *
- * @function
- * @param {Item} item the item above which it should be moved
- * @return {Boolean} {@true if it was moved}
- * @deprecated use {@link #insertAbove(item)} instead.
- */
- moveAbove: '#insertAbove',
+ /**
+ * Inserts the specified item as a child of this item by appending it to
+ * the list of children and moving it below all other children. You can
+ * use this function for groups, compound paths and layers.
+ *
+ * @param {Item} item the item to be appended as a child
+ * @deprecated use {@link #insertChild(index, item)} instead.
+ */
+ appendBottom: function (item) {
+ return this.insertChild(0, item);
+ },
- /**
- * Moves the item below the specified item.
- *
- * @function
- * @param {Item} item the item below which it should be moved
- * @return {Boolean} {@true if it was moved}
- * @deprecated use {@link #insertBelow(item)} instead.
- */
- moveBelow: '#insertBelow',
+ /**
+ * Moves this item above the specified item.
+ *
+ * @function
+ * @param {Item} item the item above which it should be moved
+ * @return {Boolean} {@true if it was moved}
+ * @deprecated use {@link #insertAbove(item)} instead.
+ */
+ moveAbove: "#insertAbove",
- /**
- * Adds it to the specified owner, which can be either a {@link Item} or a
- * {@link Project}.
- *
- * @param {Project|Layer|Group|CompoundPath} owner the item or project to
- * add the item to
- * @return {Item} the item itself, if it was successfully added
- * @chainable
- */
- addTo: function(owner) {
- return owner._insertItem(undefined, this);
- },
+ /**
+ * Moves the item below the specified item.
+ *
+ * @function
+ * @param {Item} item the item below which it should be moved
+ * @return {Boolean} {@true if it was moved}
+ * @deprecated use {@link #insertBelow(item)} instead.
+ */
+ moveBelow: "#insertBelow",
- /**
- * Clones the item and adds it to the specified owner, which can be either
- * a {@link Item} or a {@link Project}.
- *
- * @param {Project|Layer|Group|CompoundPath} owner the item or project to
- * copy the item to
- * @return {Item} the new copy of the item, if it was successfully added
- * @chainable
- */
- copyTo: function(owner) {
- return this.clone(false).addTo(owner);
- },
+ /**
+ * Adds it to the specified owner, which can be either a {@link Item} or a
+ * {@link Project}.
+ *
+ * @param {Project|Layer|Group|CompoundPath} owner the item or project to
+ * add the item to
+ * @return {Item} the item itself, if it was successfully added
+ * @chainable
+ */
+ addTo: function (owner) {
+ return owner._insertItem(undefined, this);
+ },
- /**
- * If this is a group, layer or compound-path with only one child-item,
- * the child-item is moved outside and the parent is erased. Otherwise, the
- * item itself is returned unmodified.
- *
- * @return {Item} the reduced item
- */
- reduce: function(options) {
- var children = this._children;
- if (children && children.length === 1) {
- var child = children[0].reduce(options);
- // Make sure the reduced item has the same parent as the original.
- if (this._parent) {
- child.insertAbove(this);
- this.remove();
- } else {
- child.remove();
- }
- return child;
- }
- return this;
- },
+ /**
+ * Clones the item and adds it to the specified owner, which can be either
+ * a {@link Item} or a {@link Project}.
+ *
+ * @param {Project|Layer|Group|CompoundPath} owner the item or project to
+ * copy the item to
+ * @return {Item} the new copy of the item, if it was successfully added
+ * @chainable
+ */
+ copyTo: function (owner) {
+ return this.clone(false).addTo(owner);
+ },
- /**
- * Removes the item from its parent's named children list.
- */
- _removeNamed: function() {
- var owner = this._getOwner();
- if (owner) {
- var children = owner._children,
- namedChildren = owner._namedChildren,
- name = this._name,
- namedArray = namedChildren[name],
- index = namedArray ? namedArray.indexOf(this) : -1;
- if (index !== -1) {
- // Remove the named reference
- if (children[name] == this)
- delete children[name];
- // Remove this entry
- namedArray.splice(index, 1);
- // If there are any items left in the named array, set the first
- // of them to be children[this.name]
- if (namedArray.length) {
- children[name] = namedArray[0];
+ /**
+ * If this is a group, layer or compound-path with only one child-item,
+ * the child-item is moved outside and the parent is erased. Otherwise, the
+ * item itself is returned unmodified.
+ *
+ * @return {Item} the reduced item
+ */
+ reduce: function (options) {
+ var children = this._children;
+ if (children && children.length === 1) {
+ var child = children[0].reduce(options);
+ // Make sure the reduced item has the same parent as the original.
+ if (this._parent) {
+ child.insertAbove(this);
+ this.remove();
} else {
- // Otherwise delete the empty array
- delete namedChildren[name];
+ child.remove();
}
+ return child;
}
- }
- },
+ return this;
+ },
- /**
- * Removes the item from its parent's children list.
- */
- _remove: function(notifySelf, notifyParent) {
- var owner = this._getOwner(),
- project = this._project,
- index = this._index;
- if (this._style)
- this._style._dispose();
- if (owner) {
- // Handle named children separately from index:
- if (this._name)
- this._removeNamed();
- // Handle index separately from owner: There are situations where
- // the item is already removed from its list through Base.splice()
- // and index set to undefined, but the owner is still set,
- // e.g. in #removeChildren():
- if (index != null) {
- // Only required for layers but not enough to merit an override.
- if (project._activeLayer === this)
- project._activeLayer = this.getNextSibling()
- || this.getPreviousSibling();
- Base.splice(owner._children, null, index, 1);
+ /**
+ * Removes the item from its parent's named children list.
+ */
+ _removeNamed: function () {
+ var owner = this._getOwner();
+ if (owner) {
+ var children = owner._children,
+ namedChildren = owner._namedChildren,
+ name = this._name,
+ namedArray = namedChildren[name],
+ index = namedArray ? namedArray.indexOf(this) : -1;
+ if (index !== -1) {
+ // Remove the named reference
+ if (children[name] == this) delete children[name];
+ // Remove this entry
+ namedArray.splice(index, 1);
+ // If there are any items left in the named array, set the first
+ // of them to be children[this.name]
+ if (namedArray.length) {
+ children[name] = namedArray[0];
+ } else {
+ // Otherwise delete the empty array
+ delete namedChildren[name];
+ }
+ }
}
- this._installEvents(false);
- // Notify self of the insertion change. We only need this
- // notification if we're tracking changes for now.
- if (notifySelf && project._changes)
- this._changed(/*#=*/Change.INSERTION);
- // Notify owner of changed children (this can be the project too).
- if (notifyParent)
- owner._changed(/*#=*/Change.CHILDREN, this);
- this._parent = null;
- return true;
- }
- return false;
- },
+ },
- /**
- * Removes the item and all its children from the project. The item is not
- * destroyed and can be inserted again after removal.
- *
- * @return {Boolean} {@true if the item was removed}
- */
- remove: function() {
- // Notify self and parent of change:
- return this._remove(true, true);
- },
+ /**
+ * Removes the item from its parent's children list.
+ */
+ _remove: function (notifySelf, notifyParent) {
+ var owner = this._getOwner(),
+ project = this._project,
+ index = this._index;
+ if (this._style) this._style._dispose();
+ if (owner) {
+ // Handle named children separately from index:
+ if (this._name) this._removeNamed();
+ // Handle index separately from owner: There are situations where
+ // the item is already removed from its list through Base.splice()
+ // and index set to undefined, but the owner is still set,
+ // e.g. in #removeChildren():
+ if (index != null) {
+ // Only required for layers but not enough to merit an override.
+ if (project._activeLayer === this)
+ project._activeLayer =
+ this.getNextSibling() || this.getPreviousSibling();
+ Base.splice(owner._children, null, index, 1);
+ }
+ this._installEvents(false);
+ // Notify self of the insertion change. We only need this
+ // notification if we're tracking changes for now.
+ if (notifySelf && project._changes)
+ this._changed(/*#=*/ Change.INSERTION);
+ // Notify owner of changed children (this can be the project too).
+ if (notifyParent) owner._changed(/*#=*/ Change.CHILDREN, this);
+ this._parent = null;
+ return true;
+ }
+ return false;
+ },
- /**
- * Replaces this item with the provided new item which will takes its place
- * in the project hierarchy instead.
- *
- * @param {Item} item the item that will replace this item
- * @return {Boolean} {@true if the item was replaced}
- */
- replaceWith: function(item) {
- var ok = item && item.insertBelow(this);
- if (ok)
- this.remove();
- return ok;
- },
+ /**
+ * Removes the item and all its children from the project. The item is not
+ * destroyed and can be inserted again after removal.
+ *
+ * @return {Boolean} {@true if the item was removed}
+ */
+ remove: function () {
+ // Notify self and parent of change:
+ return this._remove(true, true);
+ },
- /**
- * Removes all of the item's {@link #children} (if any).
- *
- * @name Item#removeChildren
- * @alias Item#clear
- * @function
- * @return {Item[]} an array containing the removed items
- */
- /**
- * Removes the children from the specified `start` index to and excluding
- * the `end` index from the parent's {@link #children} array.
- *
- * @name Item#removeChildren
- * @function
- * @param {Number} start the beginning index, inclusive
- * @param {Number} [end=children.length] the ending index, exclusive
- * @return {Item[]} an array containing the removed items
- */
- removeChildren: function(start, end) {
- if (!this._children)
- return null;
- start = start || 0;
- end = Base.pick(end, this._children.length);
- // Use Base.splice(), which adjusts #_index for the items above, and
- // deletes it for the removed items. Calling #_remove() afterwards is
- // fine, since it only calls Base.splice() if #_index is set.
- var removed = Base.splice(this._children, null, start, end - start);
- for (var i = removed.length - 1; i >= 0; i--) {
- // Don't notify parent each time, notify it separately after.
- removed[i]._remove(true, false);
- }
- if (removed.length > 0)
- this._changed(/*#=*/Change.CHILDREN);
- return removed;
- },
+ /**
+ * Replaces this item with the provided new item which will takes its place
+ * in the project hierarchy instead.
+ *
+ * @param {Item} item the item that will replace this item
+ * @return {Boolean} {@true if the item was replaced}
+ */
+ replaceWith: function (item) {
+ var ok = item && item.insertBelow(this);
+ if (ok) this.remove();
+ return ok;
+ },
- // DOCS Item#clear()
- clear: '#removeChildren',
+ /**
+ * Removes all of the item's {@link #children} (if any).
+ *
+ * @name Item#removeChildren
+ * @alias Item#clear
+ * @function
+ * @return {Item[]} an array containing the removed items
+ */
+ /**
+ * Removes the children from the specified `start` index to and excluding
+ * the `end` index from the parent's {@link #children} array.
+ *
+ * @name Item#removeChildren
+ * @function
+ * @param {Number} start the beginning index, inclusive
+ * @param {Number} [end=children.length] the ending index, exclusive
+ * @return {Item[]} an array containing the removed items
+ */
+ removeChildren: function (start, end) {
+ if (!this._children) return null;
+ start = start || 0;
+ end = Base.pick(end, this._children.length);
+ // Use Base.splice(), which adjusts #_index for the items above, and
+ // deletes it for the removed items. Calling #_remove() afterwards is
+ // fine, since it only calls Base.splice() if #_index is set.
+ var removed = Base.splice(this._children, null, start, end - start);
+ for (var i = removed.length - 1; i >= 0; i--) {
+ // Don't notify parent each time, notify it separately after.
+ removed[i]._remove(true, false);
+ }
+ if (removed.length > 0) this._changed(/*#=*/ Change.CHILDREN);
+ return removed;
+ },
- /**
- * Reverses the order of the item's children
- */
- reverseChildren: function() {
- if (this._children) {
- this._children.reverse();
- // Adjust indices
- for (var i = 0, l = this._children.length; i < l; i++)
- this._children[i]._index = i;
- this._changed(/*#=*/Change.CHILDREN);
- }
- },
+ // DOCS Item#clear()
+ clear: "#removeChildren",
- /**
- * {@grouptitle Tests}
- * Specifies whether the item has any content or not. The meaning of what
- * content is differs from type to type. For example, a {@link Group} with
- * no children, a {@link TextItem} with no text content and a {@link Path}
- * with no segments all are considered empty.
- *
- * @param {Boolean} [recursively=false] whether an item with children should be
- * considered empty if all its descendants are empty
- * @return {Boolean}
- */
- isEmpty: function(recursively) {
- var children = this._children;
- var numChildren = children ? children.length : 0;
- if (recursively) {
- // In recursive check, item is empty if all its children are empty.
- for (var i = 0; i < numChildren; i++) {
- if (!children[i].isEmpty(recursively)) {
- return false;
+ /**
+ * Reverses the order of the item's children
+ */
+ reverseChildren: function () {
+ if (this._children) {
+ this._children.reverse();
+ // Adjust indices
+ for (var i = 0, l = this._children.length; i < l; i++)
+ this._children[i]._index = i;
+ this._changed(/*#=*/ Change.CHILDREN);
+ }
+ },
+
+ /**
+ * {@grouptitle Tests}
+ * Specifies whether the item has any content or not. The meaning of what
+ * content is differs from type to type. For example, a {@link Group} with
+ * no children, a {@link TextItem} with no text content and a {@link Path}
+ * with no segments all are considered empty.
+ *
+ * @param {Boolean} [recursively=false] whether an item with children should be
+ * considered empty if all its descendants are empty
+ * @return {Boolean}
+ */
+ isEmpty: function (recursively) {
+ var children = this._children;
+ var numChildren = children ? children.length : 0;
+ if (recursively) {
+ // In recursive check, item is empty if all its children are empty.
+ for (var i = 0; i < numChildren; i++) {
+ if (!children[i].isEmpty(recursively)) {
+ return false;
+ }
}
+ return true;
}
- return true;
- }
- return !numChildren;
- },
+ return !numChildren;
+ },
- /**
- * Checks whether the item is editable.
- *
- * @return {Boolean} {@true when neither the item, nor its parents are
- * locked or hidden}
- * @ignore
- */
- // TODO: Item#isEditable is currently ignored in the documentation, as
- // locking an item currently has no effect
- isEditable: function() {
- var item = this;
- while (item) {
- if (!item._visible || item._locked)
- return false;
- item = item._parent;
- }
- return true;
- },
+ /**
+ * Checks whether the item is editable.
+ *
+ * @return {Boolean} {@true when neither the item, nor its parents are
+ * locked or hidden}
+ * @ignore
+ */
+ // TODO: Item#isEditable is currently ignored in the documentation, as
+ // locking an item currently has no effect
+ isEditable: function () {
+ var item = this;
+ while (item) {
+ if (!item._visible || item._locked) return false;
+ item = item._parent;
+ }
+ return true;
+ },
- /**
- * Checks whether the item is valid, i.e. it hasn't been removed.
- *
- * @return {Boolean} {@true if the item is valid}
- */
- // TODO: isValid / checkValid
+ /**
+ * Checks whether the item is valid, i.e. it hasn't been removed.
+ *
+ * @return {Boolean} {@true if the item is valid}
+ */
+ // TODO: isValid / checkValid
- /**
- * {@grouptitle Style Tests}
- *
- * Checks whether the item has a fill.
- *
- * @return {Boolean} {@true if the item has a fill}
- */
- hasFill: function() {
- return this.getStyle().hasFill();
- },
+ /**
+ * {@grouptitle Style Tests}
+ *
+ * Checks whether the item has a fill.
+ *
+ * @return {Boolean} {@true if the item has a fill}
+ */
+ hasFill: function () {
+ return this.getStyle().hasFill();
+ },
- /**
- * Checks whether the item has a stroke.
- *
- * @return {Boolean} {@true if the item has a stroke}
- */
- hasStroke: function() {
- return this.getStyle().hasStroke();
- },
+ /**
+ * Checks whether the item has a stroke.
+ *
+ * @return {Boolean} {@true if the item has a stroke}
+ */
+ hasStroke: function () {
+ return this.getStyle().hasStroke();
+ },
- /**
- * Checks whether the item has a shadow.
- *
- * @return {Boolean} {@true if the item has a shadow}
- */
- hasShadow: function() {
- return this.getStyle().hasShadow();
- },
+ /**
+ * Checks whether the item has a shadow.
+ *
+ * @return {Boolean} {@true if the item has a shadow}
+ */
+ hasShadow: function () {
+ return this.getStyle().hasShadow();
+ },
- /**
- * Returns -1 if 'this' is above 'item', 1 if below, 0 if their order is not
- * defined in such a way, e.g. if one is a descendant of the other.
- */
- _getOrder: function(item) {
- // Private method that produces a list of ancestors, starting with the
- // root and ending with the actual element as the last entry.
- function getList(item) {
- var list = [];
- do {
- list.unshift(item);
- } while (item = item._parent);
- return list;
- }
- var list1 = getList(this),
- list2 = getList(item);
- for (var i = 0, l = Math.min(list1.length, list2.length); i < l; i++) {
- if (list1[i] != list2[i]) {
- // Found the position in the parents list where the two start
- // to differ. Look at who's above who.
- return list1[i]._index < list2[i]._index ? 1 : -1;
+ /**
+ * Returns -1 if 'this' is above 'item', 1 if below, 0 if their order is not
+ * defined in such a way, e.g. if one is a descendant of the other.
+ */
+ _getOrder: function (item) {
+ // Private method that produces a list of ancestors, starting with the
+ // root and ending with the actual element as the last entry.
+ function getList(item) {
+ var list = [];
+ do {
+ list.unshift(item);
+ } while ((item = item._parent));
+ return list;
}
- }
- return 0;
- },
-
- /**
- * {@grouptitle Hierarchy Tests}
- *
- * Checks if the item contains any children items.
- *
- * @return {Boolean} {@true it has one or more children}
- */
- hasChildren: function() {
- return this._children && this._children.length > 0;
- },
+ var list1 = getList(this),
+ list2 = getList(item);
+ for (
+ var i = 0, l = Math.min(list1.length, list2.length);
+ i < l;
+ i++
+ ) {
+ if (list1[i] != list2[i]) {
+ // Found the position in the parents list where the two start
+ // to differ. Look at who's above who.
+ return list1[i]._index < list2[i]._index ? 1 : -1;
+ }
+ }
+ return 0;
+ },
- /**
- * Checks whether the item and all its parents are inserted into scene graph
- * or not.
- *
- * @return {Boolean} {@true if the item is inserted into the scene graph}
- */
- isInserted: function() {
- return this._parent ? this._parent.isInserted() : false;
- },
+ /**
+ * {@grouptitle Hierarchy Tests}
+ *
+ * Checks if the item contains any children items.
+ *
+ * @return {Boolean} {@true it has one or more children}
+ */
+ hasChildren: function () {
+ return this._children && this._children.length > 0;
+ },
- /**
- * Checks if this item is above the specified item in the stacking order
- * of the project.
- *
- * @param {Item} item the item to check against
- * @return {Boolean} {@true if it is above the specified item}
- */
- isAbove: function(item) {
- return this._getOrder(item) === -1;
- },
+ /**
+ * Checks whether the item and all its parents are inserted into scene graph
+ * or not.
+ *
+ * @return {Boolean} {@true if the item is inserted into the scene graph}
+ */
+ isInserted: function () {
+ return this._parent ? this._parent.isInserted() : false;
+ },
- /**
- * Checks if the item is below the specified item in the stacking order of
- * the project.
- *
- * @param {Item} item the item to check against
- * @return {Boolean} {@true if it is below the specified item}
- */
- isBelow: function(item) {
- return this._getOrder(item) === 1;
- },
+ /**
+ * Checks if this item is above the specified item in the stacking order
+ * of the project.
+ *
+ * @param {Item} item the item to check against
+ * @return {Boolean} {@true if it is above the specified item}
+ */
+ isAbove: function (item) {
+ return this._getOrder(item) === -1;
+ },
- /**
- * Checks whether the specified item is the parent of the item.
- *
- * @param {Item} item the item to check against
- * @return {Boolean} {@true if it is the parent of the item}
- */
- isParent: function(item) {
- return this._parent === item;
- },
+ /**
+ * Checks if the item is below the specified item in the stacking order of
+ * the project.
+ *
+ * @param {Item} item the item to check against
+ * @return {Boolean} {@true if it is below the specified item}
+ */
+ isBelow: function (item) {
+ return this._getOrder(item) === 1;
+ },
- /**
- * Checks whether the specified item is a child of the item.
- *
- * @param {Item} item the item to check against
- * @return {Boolean} {@true it is a child of the item}
- */
- isChild: function(item) {
- return item && item._parent === this;
- },
+ /**
+ * Checks whether the specified item is the parent of the item.
+ *
+ * @param {Item} item the item to check against
+ * @return {Boolean} {@true if it is the parent of the item}
+ */
+ isParent: function (item) {
+ return this._parent === item;
+ },
- /**
- * Checks if the item is contained within the specified item.
- *
- * @param {Item} item the item to check against
- * @return {Boolean} {@true if it is inside the specified item}
- */
- isDescendant: function(item) {
- var parent = this;
- while (parent = parent._parent) {
- if (parent === item)
- return true;
- }
- return false;
- },
+ /**
+ * Checks whether the specified item is a child of the item.
+ *
+ * @param {Item} item the item to check against
+ * @return {Boolean} {@true it is a child of the item}
+ */
+ isChild: function (item) {
+ return item && item._parent === this;
+ },
- /**
- * Checks if the item is an ancestor of the specified item.
- *
- * @param {Item} item the item to check against
- * @return {Boolean} {@true if the item is an ancestor of the specified
- * item}
- */
- isAncestor: function(item) {
- return item ? item.isDescendant(this) : false;
- },
+ /**
+ * Checks if the item is contained within the specified item.
+ *
+ * @param {Item} item the item to check against
+ * @return {Boolean} {@true if it is inside the specified item}
+ */
+ isDescendant: function (item) {
+ var parent = this;
+ while ((parent = parent._parent)) {
+ if (parent === item) return true;
+ }
+ return false;
+ },
- /**
- * Checks if the item is an a sibling of the specified item.
- *
- * @param {Item} item the item to check against
- * @return {Boolean} {@true if the item is aa sibling of the specified item}
- */
- isSibling: function(item) {
- return this._parent === item._parent;
- },
+ /**
+ * Checks if the item is an ancestor of the specified item.
+ *
+ * @param {Item} item the item to check against
+ * @return {Boolean} {@true if the item is an ancestor of the specified
+ * item}
+ */
+ isAncestor: function (item) {
+ return item ? item.isDescendant(this) : false;
+ },
- /**
- * Checks whether the item is grouped with the specified item.
- *
- * @param {Item} item
- * @return {Boolean} {@true if the items are grouped together}
- */
- isGroupedWith: function(item) {
- var parent = this._parent;
- while (parent) {
- // Find group parents. Check for parent._parent, since don't want
- // top level layers, because they also inherit from Group
- if (parent._parent
- && /^(Group|Layer|CompoundPath)$/.test(parent._class)
- && item.isDescendant(parent))
+ /**
+ * Checks if the item is an a sibling of the specified item.
+ *
+ * @param {Item} item the item to check against
+ * @return {Boolean} {@true if the item is aa sibling of the specified item}
+ */
+ isSibling: function (item) {
+ return this._parent === item._parent;
+ },
+
+ /**
+ * Checks whether the item is grouped with the specified item.
+ *
+ * @param {Item} item
+ * @return {Boolean} {@true if the items are grouped together}
+ */
+ isGroupedWith: function (item) {
+ var parent = this._parent;
+ while (parent) {
+ // Find group parents. Check for parent._parent, since don't want
+ // top level layers, because they also inherit from Group
+ if (
+ parent._parent &&
+ /^(Group|Layer|CompoundPath)$/.test(parent._class) &&
+ item.isDescendant(parent)
+ )
return true;
- // Keep walking up otherwise
- parent = parent._parent;
- }
- return false;
- },
+ // Keep walking up otherwise
+ parent = parent._parent;
+ }
+ return false;
+ },
- // Document all style properties which get injected into Item by Style:
+ // Document all style properties which get injected into Item by Style:
- /**
- * {@grouptitle Stroke Style}
- *
- * The color of the stroke.
- *
- * @name Item#strokeColor
- * @property
- * @type ?Color
- *
- * @example {@paperscript}
- * // Setting the stroke color of a path:
- *
- * // Create a circle shaped path at { x: 80, y: 50 }
- * // with a radius of 35:
- * var circle = new Path.Circle({
- * center: [80, 50],
- * radius: 35
- * });
- *
- * // Set its stroke color to RGB red:
- * circle.strokeColor = new Color(1, 0, 0);
- */
+ /**
+ * {@grouptitle Stroke Style}
+ *
+ * The color of the stroke.
+ *
+ * @name Item#strokeColor
+ * @property
+ * @type ?Color
+ *
+ * @example {@paperscript}
+ * // Setting the stroke color of a path:
+ *
+ * // Create a circle shaped path at { x: 80, y: 50 }
+ * // with a radius of 35:
+ * var circle = new Path.Circle({
+ * center: [80, 50],
+ * radius: 35
+ * });
+ *
+ * // Set its stroke color to RGB red:
+ * circle.strokeColor = new Color(1, 0, 0);
+ */
- /**
- * The width of the stroke.
- *
- * @name Item#strokeWidth
- * @property
- * @type Number
- *
- * @example {@paperscript}
- * // Setting an item's stroke width:
- *
- * // Create a circle shaped path at { x: 80, y: 50 }
- * // with a radius of 35:
- * var circle = new Path.Circle({
- * center: [80, 50],
- * radius: 35,
- * strokeColor: 'red'
- * });
- *
- * // Set its stroke width to 10:
- * circle.strokeWidth = 10;
- */
+ /**
+ * The width of the stroke.
+ *
+ * @name Item#strokeWidth
+ * @property
+ * @type Number
+ *
+ * @example {@paperscript}
+ * // Setting an item's stroke width:
+ *
+ * // Create a circle shaped path at { x: 80, y: 50 }
+ * // with a radius of 35:
+ * var circle = new Path.Circle({
+ * center: [80, 50],
+ * radius: 35,
+ * strokeColor: 'red'
+ * });
+ *
+ * // Set its stroke width to 10:
+ * circle.strokeWidth = 10;
+ */
- /**
- * The shape to be used at the beginning and end of open {@link Path} items,
- * when they have a stroke.
- *
- * @name Item#strokeCap
- * @property
- * @type String
- * @values 'round', 'square', 'butt'
- * @default 'butt'
- *
- * @example {@paperscript height=200}
- * // A look at the different stroke caps:
- *
- * var line = new Path({
- * segments: [[80, 50], [420, 50]],
- * strokeColor: 'black',
- * strokeWidth: 20,
- * selected: true
- * });
- *
- * // Set the stroke cap of the line to be round:
- * line.strokeCap = 'round';
- *
- * // Copy the path and set its stroke cap to be square:
- * var line2 = line.clone();
- * line2.position.y += 50;
- * line2.strokeCap = 'square';
- *
- * // Make another copy and set its stroke cap to be butt:
- * var line2 = line.clone();
- * line2.position.y += 100;
- * line2.strokeCap = 'butt';
- */
+ /**
+ * The shape to be used at the beginning and end of open {@link Path} items,
+ * when they have a stroke.
+ *
+ * @name Item#strokeCap
+ * @property
+ * @type String
+ * @values 'round', 'square', 'butt'
+ * @default 'butt'
+ *
+ * @example {@paperscript height=200}
+ * // A look at the different stroke caps:
+ *
+ * var line = new Path({
+ * segments: [[80, 50], [420, 50]],
+ * strokeColor: 'black',
+ * strokeWidth: 20,
+ * selected: true
+ * });
+ *
+ * // Set the stroke cap of the line to be round:
+ * line.strokeCap = 'round';
+ *
+ * // Copy the path and set its stroke cap to be square:
+ * var line2 = line.clone();
+ * line2.position.y += 50;
+ * line2.strokeCap = 'square';
+ *
+ * // Make another copy and set its stroke cap to be butt:
+ * var line2 = line.clone();
+ * line2.position.y += 100;
+ * line2.strokeCap = 'butt';
+ */
- /**
- * The shape to be used at the segments and corners of {@link Path} items
- * when they have a stroke.
- *
- * @name Item#strokeJoin
- * @property
- * @type String
- * @values 'miter', 'round', 'bevel'
- * @default 'miter'
- *
- * @example {@paperscript height=120}
- * // A look at the different stroke joins:
- * var path = new Path({
- * segments: [[80, 100], [120, 40], [160, 100]],
- * strokeColor: 'black',
- * strokeWidth: 20,
- * // Select the path, in order to see where the stroke is formed:
- * selected: true
- * });
- *
- * var path2 = path.clone();
- * path2.position.x += path2.bounds.width * 1.5;
- * path2.strokeJoin = 'round';
- *
- * var path3 = path2.clone();
- * path3.position.x += path3.bounds.width * 1.5;
- * path3.strokeJoin = 'bevel';
- */
+ /**
+ * The shape to be used at the segments and corners of {@link Path} items
+ * when they have a stroke.
+ *
+ * @name Item#strokeJoin
+ * @property
+ * @type String
+ * @values 'miter', 'round', 'bevel'
+ * @default 'miter'
+ *
+ * @example {@paperscript height=120}
+ * // A look at the different stroke joins:
+ * var path = new Path({
+ * segments: [[80, 100], [120, 40], [160, 100]],
+ * strokeColor: 'black',
+ * strokeWidth: 20,
+ * // Select the path, in order to see where the stroke is formed:
+ * selected: true
+ * });
+ *
+ * var path2 = path.clone();
+ * path2.position.x += path2.bounds.width * 1.5;
+ * path2.strokeJoin = 'round';
+ *
+ * var path3 = path2.clone();
+ * path3.position.x += path3.bounds.width * 1.5;
+ * path3.strokeJoin = 'bevel';
+ */
- /**
- * The dash offset of the stroke.
- *
- * @name Item#dashOffset
- * @property
- * @type Number
- * @default 0
- */
+ /**
+ * The dash offset of the stroke.
+ *
+ * @name Item#dashOffset
+ * @property
+ * @type Number
+ * @default 0
+ */
- /**
- * Specifies whether the stroke is to be drawn taking the current affine
- * transformation into account (the default behavior), or whether it should
- * appear as a non-scaling stroke.
- *
- * @name Item#strokeScaling
- * @property
- * @type Boolean
- * @default true
- */
+ /**
+ * Specifies whether the stroke is to be drawn taking the current affine
+ * transformation into account (the default behavior), or whether it should
+ * appear as a non-scaling stroke.
+ *
+ * @name Item#strokeScaling
+ * @property
+ * @type Boolean
+ * @default true
+ */
- /**
- * Specifies an array containing the dash and gap lengths of the stroke.
- *
- * @example {@paperscript}
- * var path = new Path.Circle({
- * center: [80, 50],
- * radius: 40,
- * strokeWidth: 2,
- * strokeColor: 'black'
- * });
- *
- * // Set the dashed stroke to [10pt dash, 4pt gap]:
- * path.dashArray = [10, 4];
- *
- * @name Item#dashArray
- * @property
- * @type Number[]
- * @default []
- */
+ /**
+ * Specifies an array containing the dash and gap lengths of the stroke.
+ *
+ * @example {@paperscript}
+ * var path = new Path.Circle({
+ * center: [80, 50],
+ * radius: 40,
+ * strokeWidth: 2,
+ * strokeColor: 'black'
+ * });
+ *
+ * // Set the dashed stroke to [10pt dash, 4pt gap]:
+ * path.dashArray = [10, 4];
+ *
+ * @name Item#dashArray
+ * @property
+ * @type Number[]
+ * @default []
+ */
- /**
- * The miter limit of the stroke.
- * When two line segments meet at a sharp angle and miter joins have been
- * specified for {@link Item#strokeJoin}, it is possible for the miter to
- * extend far beyond the {@link Item#strokeWidth} of the path. The
- * miterLimit imposes a limit on the ratio of the miter length to the
- * {@link Item#strokeWidth}.
- *
- * @name Item#miterLimit
- * @property
- * @type Number
- * @default 10
- */
+ /**
+ * The miter limit of the stroke.
+ * When two line segments meet at a sharp angle and miter joins have been
+ * specified for {@link Item#strokeJoin}, it is possible for the miter to
+ * extend far beyond the {@link Item#strokeWidth} of the path. The
+ * miterLimit imposes a limit on the ratio of the miter length to the
+ * {@link Item#strokeWidth}.
+ *
+ * @name Item#miterLimit
+ * @property
+ * @type Number
+ * @default 10
+ */
- /**
- * {@grouptitle Fill Style}
- *
- * The fill color of the item.
- *
- * @name Item#fillColor
- * @property
- * @type ?Color
- *
- * @example {@paperscript}
- * // Setting the fill color of a path to red:
- *
- * // Create a circle shaped path at { x: 80, y: 50 }
- * // with a radius of 35:
- * var circle = new Path.Circle({
- * center: [80, 50],
- * radius: 35
- * });
- *
- * // Set the fill color of the circle to RGB red:
- * circle.fillColor = new Color(1, 0, 0);
- */
+ /**
+ * {@grouptitle Fill Style}
+ *
+ * The fill color of the item.
+ *
+ * @name Item#fillColor
+ * @property
+ * @type ?Color
+ *
+ * @example {@paperscript}
+ * // Setting the fill color of a path to red:
+ *
+ * // Create a circle shaped path at { x: 80, y: 50 }
+ * // with a radius of 35:
+ * var circle = new Path.Circle({
+ * center: [80, 50],
+ * radius: 35
+ * });
+ *
+ * // Set the fill color of the circle to RGB red:
+ * circle.fillColor = new Color(1, 0, 0);
+ */
- /**
- * The fill-rule with which the shape gets filled. Please note that only
- * modern browsers support fill-rules other than `'nonzero'`.
- *
- * @name Item#fillRule
- * @property
- * @type String
- * @values 'nonzero', 'evenodd'
- * @default 'nonzero'
- */
+ /**
+ * The fill-rule with which the shape gets filled. Please note that only
+ * modern browsers support fill-rules other than `'nonzero'`.
+ *
+ * @name Item#fillRule
+ * @property
+ * @type String
+ * @values 'nonzero', 'evenodd'
+ * @default 'nonzero'
+ */
- /**
- * {@grouptitle Shadow Style}
- *
- * The shadow color.
- *
- * @property
- * @name Item#shadowColor
- * @type ?Color
- *
- * @example {@paperscript}
- * // Creating a circle with a black shadow:
- *
- * var circle = new Path.Circle({
- * center: [80, 50],
- * radius: 35,
- * fillColor: 'white',
- * // Set the shadow color of the circle to RGB black:
- * shadowColor: new Color(0, 0, 0),
- * // Set the shadow blur radius to 12:
- * shadowBlur: 12,
- * // Offset the shadow by { x: 5, y: 5 }
- * shadowOffset: new Point(5, 5)
- * });
- */
+ /**
+ * {@grouptitle Shadow Style}
+ *
+ * The shadow color.
+ *
+ * @property
+ * @name Item#shadowColor
+ * @type ?Color
+ *
+ * @example {@paperscript}
+ * // Creating a circle with a black shadow:
+ *
+ * var circle = new Path.Circle({
+ * center: [80, 50],
+ * radius: 35,
+ * fillColor: 'white',
+ * // Set the shadow color of the circle to RGB black:
+ * shadowColor: new Color(0, 0, 0),
+ * // Set the shadow blur radius to 12:
+ * shadowBlur: 12,
+ * // Offset the shadow by { x: 5, y: 5 }
+ * shadowOffset: new Point(5, 5)
+ * });
+ */
- /**
- * The shadow's blur radius.
- *
- * @property
- * @name Item#shadowBlur
- * @type Number
- * @default 0
- */
+ /**
+ * The shadow's blur radius.
+ *
+ * @property
+ * @name Item#shadowBlur
+ * @type Number
+ * @default 0
+ */
- /**
- * The shadow's offset.
- *
- * @property
- * @name Item#shadowOffset
- * @type Point
- * @default 0
- */
+ /**
+ * The shadow's offset.
+ *
+ * @property
+ * @name Item#shadowOffset
+ * @type Point
+ * @default 0
+ */
- // TODO: Find a better name than selectedColor. It should also be used for
- // guides, etc.
- /**
- * {@grouptitle Selection Style}
- *
- * The color the item is highlighted with when selected. If the item does
- * not specify its own color, the color defined by its layer is used instead.
- *
- * @name Item#selectedColor
- * @property
- * @type ?Color
- */
-}, Base.each(['rotate', 'scale', 'shear', 'skew'], function(key) {
- var rotate = key === 'rotate';
- this[key] = function(/* value, center */) {
- var args = arguments,
- value = (rotate ? Base : Point).read(args),
- center = Point.read(args, 0, { readNull: true });
- return this.transform(new Matrix()[key](value,
- center || this.getPosition(true)));
- };
-}, /** @lends Item# */{
- /**
- * {@grouptitle Transform Functions}
- *
- * Translates (moves) the item by the given offset views.
- *
- * @param {Point} delta the offset to translate the item by
- */
- translate: function(/* delta */) {
- var mx = new Matrix();
- return this.transform(mx.translate.apply(mx, arguments));
+ // TODO: Find a better name than selectedColor. It should also be used for
+ // guides, etc.
+ /**
+ * {@grouptitle Selection Style}
+ *
+ * The color the item is highlighted with when selected. If the item does
+ * not specify its own color, the color defined by its layer is used instead.
+ *
+ * @name Item#selectedColor
+ * @property
+ * @type ?Color
+ */
},
+ Base.each(
+ ["rotate", "scale", "shear", "skew"],
+ function (key) {
+ var rotate = key === "rotate";
+ this[key] = function (/* value, center */) {
+ var args = arguments,
+ value = (rotate ? Base : Point).read(args),
+ center = Point.read(args, 0, { readNull: true });
+ return this.transform(
+ new Matrix()[key](value, center || this.getPosition(true))
+ );
+ };
+ },
+ /** @lends Item# */ {
+ /**
+ * {@grouptitle Transform Functions}
+ *
+ * Translates (moves) the item by the given offset views.
+ *
+ * @param {Point} delta the offset to translate the item by
+ */
+ translate: function (/* delta */) {
+ var mx = new Matrix();
+ return this.transform(mx.translate.apply(mx, arguments));
+ },
- /**
- * Rotates the item by a given angle around the given center point.
- *
- * Angles are oriented clockwise and measured in degrees.
- *
- * @name Item#rotate
- * @function
- * @param {Number} angle the rotation angle
- * @param {Point} [center={@link Item#position}]
- * @see Matrix#rotate(angle[, center])
- *
- * @example {@paperscript}
- * // Rotating an item:
- *
- * // Create a rectangle shaped path with its top left
- * // point at {x: 80, y: 25} and a size of {width: 50, height: 50}:
- * var path = new Path.Rectangle(new Point(80, 25), new Size(50, 50));
- * path.fillColor = 'black';
- *
- * // Rotate the path by 30 degrees:
- * path.rotate(30);
- *
- * @example {@paperscript height=200}
- * // Rotating an item around a specific point:
- *
- * // Create a rectangle shaped path with its top left
- * // point at {x: 175, y: 50} and a size of {width: 100, height: 100}:
- * var topLeft = new Point(175, 50);
- * var size = new Size(100, 100);
- * var path = new Path.Rectangle(topLeft, size);
- * path.fillColor = 'black';
- *
- * // Draw a circle shaped path in the center of the view,
- * // to show the rotation point:
- * var circle = new Path.Circle({
- * center: view.center,
- * radius: 5,
- * fillColor: 'white'
- * });
- *
- * // Each frame rotate the path 3 degrees around the center point
- * // of the view:
- * function onFrame(event) {
- * path.rotate(3, view.center);
- * }
- */
-
- /**
- * Scales the item by the given value from its center point, or optionally
- * from a supplied point.
- *
- * @name Item#scale
- * @function
- * @param {Number} scale the scale factor
- * @param {Point} [center={@link Item#position}]
- *
- * @example {@paperscript}
- * // Scaling an item from its center point:
- *
- * // Create a circle shaped path at { x: 80, y: 50 }
- * // with a radius of 20:
- * var circle = new Path.Circle({
- * center: [80, 50],
- * radius: 20,
- * fillColor: 'red'
- * });
- *
- * // Scale the path by 150% from its center point
- * circle.scale(1.5);
- *
- * @example {@paperscript}
- * // Scaling an item from a specific point:
- *
- * // Create a circle shaped path at { x: 80, y: 50 }
- * // with a radius of 20:
- * var circle = new Path.Circle({
- * center: [80, 50],
- * radius: 20,
- * fillColor: 'red'
- * });
- *
- * // Scale the path 150% from its bottom left corner
- * circle.scale(1.5, circle.bounds.bottomLeft);
- */
- /**
- * Scales the item by the given values from its center point, or optionally
- * from a supplied point.
- *
- * @name Item#scale
- * @function
- * @param {Number} hor the horizontal scale factor
- * @param {Number} ver the vertical scale factor
- * @param {Point} [center={@link Item#position}]
- *
- * @example {@paperscript}
- * // Scaling an item horizontally by 300%:
- *
- * // Create a circle shaped path at { x: 100, y: 50 }
- * // with a radius of 20:
- * var circle = new Path.Circle({
- * center: [100, 50],
- * radius: 20,
- * fillColor: 'red'
- * });
- *
- * // Scale the path horizontally by 300%
- * circle.scale(3, 1);
- */
-
- // TODO: Add test for item shearing, as it might be behaving oddly.
- /**
- * Shears the item by the given value from its center point, or optionally
- * by a supplied point.
- *
- * @name Item#shear
- * @function
- * @param {Point} shear the horizontal and vertical shear factors as a point
- * @param {Point} [center={@link Item#position}]
- * @see Matrix#shear(shear[, center])
- */
- /**
- * Shears the item by the given values from its center point, or optionally
- * by a supplied point.
- *
- * @name Item#shear
- * @function
- * @param {Number} hor the horizontal shear factor
- * @param {Number} ver the vertical shear factor
- * @param {Point} [center={@link Item#position}]
- * @see Matrix#shear(hor, ver[, center])
- */
-
- /**
- * Skews the item by the given angles from its center point, or optionally
- * by a supplied point.
- *
- * @name Item#skew
- * @function
- * @param {Point} skew the horizontal and vertical skew angles in degrees
- * @param {Point} [center={@link Item#position}]
- * @see Matrix#shear(skew[, center])
- */
- /**
- * Skews the item by the given angles from its center point, or optionally
- * by a supplied point.
- *
- * @name Item#skew
- * @function
- * @param {Number} hor the horizontal skew angle in degrees
- * @param {Number} ver the vertical sskew angle in degrees
- * @param {Point} [center={@link Item#position}]
- * @see Matrix#shear(hor, ver[, center])
- */
-
- /**
- * Transform the item.
- *
- * @param {Matrix} matrix the matrix by which the item shall be transformed
- */
- // TODO: Implement flags:
- // @param {String[]} flags array of any of the following: 'objects',
- // 'children', 'fill-gradients', 'fill-patterns', 'stroke-patterns',
- // 'lines'. Default: ['objects', 'children']
- transform: function(matrix, _applyRecursively, _setApplyMatrix) {
- var _matrix = this._matrix,
- transformMatrix = matrix && !matrix.isIdentity(),
- // If no matrix is provided, or the matrix is the identity, we might
- // still have some work to do: _setApplyMatrix or _applyRecursively.
- applyMatrix = (
- _setApplyMatrix && this._canApplyMatrix ||
- this._applyMatrix && (
- // Don't apply _matrix if the result of concatenating with
- // matrix would be identity.
- transformMatrix || !_matrix.isIdentity() ||
- // Even if it's an identity matrix, we may still need to
- // recursively apply the matrix to children.
- _applyRecursively && this._children
- )
- );
- // Bail out if there is nothing to do.
- if (!transformMatrix && !applyMatrix)
- return this;
- // Simply prepend the internal matrix with the passed one:
- if (transformMatrix) {
- // Keep a backup of the last valid state before the matrix becomes
- // non-invertible. This is then used again in setBounds to restore.
- if (!matrix.isInvertible() && _matrix.isInvertible())
- _matrix._backup = _matrix.getValues();
- // Pass `true` for _dontNotify, as we're handling this after.
- _matrix.prepend(matrix, true);
- // When a new matrix was applied, we also need to transform gradient
- // color points. These always need transforming, regardless of
- // #applyMatrix, as they are defined in the parent's coordinate
- // system.
- // TODO: Introduce options to control whether fills should be
- // transformed or not.
- var style = this._style,
- // Pass true for _dontMerge so we don't recursively transform
- // styles on groups' children.
- fillColor = style.getFillColor(true),
- strokeColor = style.getStrokeColor(true);
- if (fillColor)
- fillColor.transform(matrix);
- if (strokeColor)
- strokeColor.transform(matrix);
- }
- // Call #_transformContent() now, if we need to directly apply the
- // internal _matrix transformations to the item's content.
- // Application is not possible on Raster, PointText, SymbolItem, since
- // the matrix is where the actual transformation state is stored.
-
- if (applyMatrix && (applyMatrix = this._transformContent(
- _matrix, _applyRecursively, _setApplyMatrix))) {
- // Pivot is provided in the parent's coordinate system, so transform
- // it along too.
- var pivot = this._pivot;
- if (pivot)
- _matrix._transformPoint(pivot, pivot, true);
- // Reset the internal matrix to the identity transformation if
- // it was possible to apply it, but do not notify owner of change.
- _matrix.reset(true);
- // Set the internal _applyMatrix flag to true if we're told to
- // do so
- if (_setApplyMatrix && this._canApplyMatrix)
- this._applyMatrix = true;
- }
- // Calling _changed will clear _bounds and _position, but depending
- // on matrix we can calculate and set them again, so preserve them.
- var bounds = this._bounds,
- position = this._position;
- if (transformMatrix || applyMatrix) {
- this._changed(/*#=*/Change.MATRIX);
- }
- // Detect matrices that contain only translations and scaling
- // and transform the cached _bounds and _position without having to
- // fully recalculate each time.
- var decomp = transformMatrix && bounds && matrix.decompose();
- if (decomp && decomp.skewing.isZero() && decomp.rotation % 90 === 0) {
- // Transform the old bound by looping through all the cached
- // bounds in _bounds and transform each.
- for (var key in bounds) {
- var cache = bounds[key];
- // If any item involved in the determination of these bounds has
- // non-scaling strokes, delete the cache now as it can't be
- // preserved through the transformation.
- if (cache.nonscaling) {
- delete bounds[key];
- } else if (applyMatrix || !cache.internal) {
- // If these are internal bounds, only transform them if this
- // item applied its matrix.
- var rect = cache.rect;
- matrix._transformBounds(rect, rect);
+ /**
+ * Rotates the item by a given angle around the given center point.
+ *
+ * Angles are oriented clockwise and measured in degrees.
+ *
+ * @name Item#rotate
+ * @function
+ * @param {Number} angle the rotation angle
+ * @param {Point} [center={@link Item#position}]
+ * @see Matrix#rotate(angle[, center])
+ *
+ * @example {@paperscript}
+ * // Rotating an item:
+ *
+ * // Create a rectangle shaped path with its top left
+ * // point at {x: 80, y: 25} and a size of {width: 50, height: 50}:
+ * var path = new Path.Rectangle(new Point(80, 25), new Size(50, 50));
+ * path.fillColor = 'black';
+ *
+ * // Rotate the path by 30 degrees:
+ * path.rotate(30);
+ *
+ * @example {@paperscript height=200}
+ * // Rotating an item around a specific point:
+ *
+ * // Create a rectangle shaped path with its top left
+ * // point at {x: 175, y: 50} and a size of {width: 100, height: 100}:
+ * var topLeft = new Point(175, 50);
+ * var size = new Size(100, 100);
+ * var path = new Path.Rectangle(topLeft, size);
+ * path.fillColor = 'black';
+ *
+ * // Draw a circle shaped path in the center of the view,
+ * // to show the rotation point:
+ * var circle = new Path.Circle({
+ * center: view.center,
+ * radius: 5,
+ * fillColor: 'white'
+ * });
+ *
+ * // Each frame rotate the path 3 degrees around the center point
+ * // of the view:
+ * function onFrame(event) {
+ * path.rotate(3, view.center);
+ * }
+ */
+
+ /**
+ * Scales the item by the given value from its center point, or optionally
+ * from a supplied point.
+ *
+ * @name Item#scale
+ * @function
+ * @param {Number} scale the scale factor
+ * @param {Point} [center={@link Item#position}]
+ *
+ * @example {@paperscript}
+ * // Scaling an item from its center point:
+ *
+ * // Create a circle shaped path at { x: 80, y: 50 }
+ * // with a radius of 20:
+ * var circle = new Path.Circle({
+ * center: [80, 50],
+ * radius: 20,
+ * fillColor: 'red'
+ * });
+ *
+ * // Scale the path by 150% from its center point
+ * circle.scale(1.5);
+ *
+ * @example {@paperscript}
+ * // Scaling an item from a specific point:
+ *
+ * // Create a circle shaped path at { x: 80, y: 50 }
+ * // with a radius of 20:
+ * var circle = new Path.Circle({
+ * center: [80, 50],
+ * radius: 20,
+ * fillColor: 'red'
+ * });
+ *
+ * // Scale the path 150% from its bottom left corner
+ * circle.scale(1.5, circle.bounds.bottomLeft);
+ */
+ /**
+ * Scales the item by the given values from its center point, or optionally
+ * from a supplied point.
+ *
+ * @name Item#scale
+ * @function
+ * @param {Number} hor the horizontal scale factor
+ * @param {Number} ver the vertical scale factor
+ * @param {Point} [center={@link Item#position}]
+ *
+ * @example {@paperscript}
+ * // Scaling an item horizontally by 300%:
+ *
+ * // Create a circle shaped path at { x: 100, y: 50 }
+ * // with a radius of 20:
+ * var circle = new Path.Circle({
+ * center: [100, 50],
+ * radius: 20,
+ * fillColor: 'red'
+ * });
+ *
+ * // Scale the path horizontally by 300%
+ * circle.scale(3, 1);
+ */
+
+ // TODO: Add test for item shearing, as it might be behaving oddly.
+ /**
+ * Shears the item by the given value from its center point, or optionally
+ * by a supplied point.
+ *
+ * @name Item#shear
+ * @function
+ * @param {Point} shear the horizontal and vertical shear factors as a point
+ * @param {Point} [center={@link Item#position}]
+ * @see Matrix#shear(shear[, center])
+ */
+ /**
+ * Shears the item by the given values from its center point, or optionally
+ * by a supplied point.
+ *
+ * @name Item#shear
+ * @function
+ * @param {Number} hor the horizontal shear factor
+ * @param {Number} ver the vertical shear factor
+ * @param {Point} [center={@link Item#position}]
+ * @see Matrix#shear(hor, ver[, center])
+ */
+
+ /**
+ * Skews the item by the given angles from its center point, or optionally
+ * by a supplied point.
+ *
+ * @name Item#skew
+ * @function
+ * @param {Point} skew the horizontal and vertical skew angles in degrees
+ * @param {Point} [center={@link Item#position}]
+ * @see Matrix#shear(skew[, center])
+ */
+ /**
+ * Skews the item by the given angles from its center point, or optionally
+ * by a supplied point.
+ *
+ * @name Item#skew
+ * @function
+ * @param {Number} hor the horizontal skew angle in degrees
+ * @param {Number} ver the vertical sskew angle in degrees
+ * @param {Point} [center={@link Item#position}]
+ * @see Matrix#shear(hor, ver[, center])
+ */
+
+ /**
+ * Transform the item.
+ *
+ * @param {Matrix} matrix the matrix by which the item shall be transformed
+ */
+ // TODO: Implement flags:
+ // @param {String[]} flags array of any of the following: 'objects',
+ // 'children', 'fill-gradients', 'fill-patterns', 'stroke-patterns',
+ // 'lines'. Default: ['objects', 'children']
+ transform: function (matrix, _applyRecursively, _setApplyMatrix) {
+ var _matrix = this._matrix,
+ transformMatrix = matrix && !matrix.isIdentity(),
+ // If no matrix is provided, or the matrix is the identity, we might
+ // still have some work to do: _setApplyMatrix or _applyRecursively.
+ applyMatrix =
+ (_setApplyMatrix && this._canApplyMatrix) ||
+ (this._applyMatrix &&
+ // Don't apply _matrix if the result of concatenating with
+ // matrix would be identity.
+ (transformMatrix ||
+ !_matrix.isIdentity() ||
+ // Even if it's an identity matrix, we may still need to
+ // recursively apply the matrix to children.
+ (_applyRecursively && this._children)));
+ // Bail out if there is nothing to do.
+ if (!transformMatrix && !applyMatrix) return this;
+ // Simply prepend the internal matrix with the passed one:
+ if (transformMatrix) {
+ // Keep a backup of the last valid state before the matrix becomes
+ // non-invertible. This is then used again in setBounds to restore.
+ if (!matrix.isInvertible() && _matrix.isInvertible())
+ _matrix._backup = _matrix.getValues();
+ // Pass `true` for _dontNotify, as we're handling this after.
+ _matrix.prepend(matrix, true);
+ // When a new matrix was applied, we also need to transform gradient
+ // color points. These always need transforming, regardless of
+ // #applyMatrix, as they are defined in the parent's coordinate
+ // system.
+ // TODO: Introduce options to control whether fills should be
+ // transformed or not.
+ var style = this._style,
+ // Pass true for _dontMerge so we don't recursively transform
+ // styles on groups' children.
+ fillColor = style.getFillColor(true),
+ strokeColor = style.getStrokeColor(true);
+ if (fillColor) fillColor.transform(matrix);
+ if (strokeColor) strokeColor.transform(matrix);
}
- }
- this._bounds = bounds;
- // If we have cached bounds, try to determine _position as its
- // center. Use _boundsOptions do get the cached default bounds.
- var cached = bounds[this._getBoundsCacheKey(
- this._boundsOptions || {})];
- if (cached) {
- // use this method to handle pivot case (see #1503)
- this._position = this._getPositionFromBounds(cached.rect);
- }
- } else if (transformMatrix && position && this._pivot) {
- // If the item has a pivot defined, it means that the default
- // position defined as the center of the bounds won't shift with
- // arbitrary transformations and we can therefore update _position:
- this._position = matrix._transformPoint(position, position);
- }
- // Allow chaining here, since transform() is related to Matrix functions
- return this;
- },
+ // Call #_transformContent() now, if we need to directly apply the
+ // internal _matrix transformations to the item's content.
+ // Application is not possible on Raster, PointText, SymbolItem, since
+ // the matrix is where the actual transformation state is stored.
+
+ if (
+ applyMatrix &&
+ (applyMatrix = this._transformContent(
+ _matrix,
+ _applyRecursively,
+ _setApplyMatrix
+ ))
+ ) {
+ // Pivot is provided in the parent's coordinate system, so transform
+ // it along too.
+ var pivot = this._pivot;
+ if (pivot) _matrix._transformPoint(pivot, pivot, true);
+ // Reset the internal matrix to the identity transformation if
+ // it was possible to apply it, but do not notify owner of change.
+ _matrix.reset(true);
+ // Set the internal _applyMatrix flag to true if we're told to
+ // do so
+ if (_setApplyMatrix && this._canApplyMatrix)
+ this._applyMatrix = true;
+ }
+ // Calling _changed will clear _bounds and _position, but depending
+ // on matrix we can calculate and set them again, so preserve them.
+ var bounds = this._bounds,
+ position = this._position;
+ if (transformMatrix || applyMatrix) {
+ this._changed(/*#=*/ Change.MATRIX);
+ }
+ // Detect matrices that contain only translations and scaling
+ // and transform the cached _bounds and _position without having to
+ // fully recalculate each time.
+ var decomp = transformMatrix && bounds && matrix.decompose();
+ if (
+ decomp &&
+ decomp.skewing.isZero() &&
+ decomp.rotation % 90 === 0
+ ) {
+ // Transform the old bound by looping through all the cached
+ // bounds in _bounds and transform each.
+ for (var key in bounds) {
+ var cache = bounds[key];
+ // If any item involved in the determination of these bounds has
+ // non-scaling strokes, delete the cache now as it can't be
+ // preserved through the transformation.
+ if (cache.nonscaling) {
+ delete bounds[key];
+ } else if (applyMatrix || !cache.internal) {
+ // If these are internal bounds, only transform them if this
+ // item applied its matrix.
+ var rect = cache.rect;
+ matrix._transformBounds(rect, rect);
+ }
+ }
+ this._bounds = bounds;
+ // If we have cached bounds, try to determine _position as its
+ // center. Use _boundsOptions do get the cached default bounds.
+ var cached =
+ bounds[
+ this._getBoundsCacheKey(this._boundsOptions || {})
+ ];
+ if (cached) {
+ // use this method to handle pivot case (see #1503)
+ this._position = this._getPositionFromBounds(
+ cached.rect
+ );
+ }
+ } else if (transformMatrix && position && this._pivot) {
+ // If the item has a pivot defined, it means that the default
+ // position defined as the center of the bounds won't shift with
+ // arbitrary transformations and we can therefore update _position:
+ this._position = matrix._transformPoint(position, position);
+ }
+ // Allow chaining here, since transform() is related to Matrix functions
+ return this;
+ },
- _transformContent: function(matrix, applyRecursively, setApplyMatrix) {
- var children = this._children;
- if (children) {
- for (var i = 0, l = children.length; i < l; i++) {
- children[i].transform(matrix, applyRecursively, setApplyMatrix);
- }
- return true;
- }
- },
+ _transformContent: function (
+ matrix,
+ applyRecursively,
+ setApplyMatrix
+ ) {
+ var children = this._children;
+ if (children) {
+ for (var i = 0, l = children.length; i < l; i++) {
+ children[i].transform(
+ matrix,
+ applyRecursively,
+ setApplyMatrix
+ );
+ }
+ return true;
+ }
+ },
- /**
- * Converts the specified point from global project coordinate space to the
- * item's own local coordinate space.
- *
- * @param {Point} point the point to be transformed
- * @return {Point} the transformed point as a new instance
- */
- globalToLocal: function(/* point */) {
- return this.getGlobalMatrix(true)._inverseTransform(
- Point.read(arguments));
- },
+ /**
+ * Converts the specified point from global project coordinate space to the
+ * item's own local coordinate space.
+ *
+ * @param {Point} point the point to be transformed
+ * @return {Point} the transformed point as a new instance
+ */
+ globalToLocal: function (/* point */) {
+ return this.getGlobalMatrix(true)._inverseTransform(
+ Point.read(arguments)
+ );
+ },
- /**
- * Converts the specified point from the item's own local coordinate space
- * to the global project coordinate space.
- *
- * @param {Point} point the point to be transformed
- * @return {Point} the transformed point as a new instance
- */
- localToGlobal: function(/* point */) {
- return this.getGlobalMatrix(true)._transformPoint(
- Point.read(arguments));
- },
+ /**
+ * Converts the specified point from the item's own local coordinate space
+ * to the global project coordinate space.
+ *
+ * @param {Point} point the point to be transformed
+ * @return {Point} the transformed point as a new instance
+ */
+ localToGlobal: function (/* point */) {
+ return this.getGlobalMatrix(true)._transformPoint(
+ Point.read(arguments)
+ );
+ },
- /**
- * Converts the specified point from the parent's coordinate space to
- * item's own local coordinate space.
- *
- * @param {Point} point the point to be transformed
- * @return {Point} the transformed point as a new instance
- */
- parentToLocal: function(/* point */) {
- return this._matrix._inverseTransform(Point.read(arguments));
- },
+ /**
+ * Converts the specified point from the parent's coordinate space to
+ * item's own local coordinate space.
+ *
+ * @param {Point} point the point to be transformed
+ * @return {Point} the transformed point as a new instance
+ */
+ parentToLocal: function (/* point */) {
+ return this._matrix._inverseTransform(Point.read(arguments));
+ },
- /**
- * Converts the specified point from the item's own local coordinate space
- * to the parent's coordinate space.
- *
- * @param {Point} point the point to be transformed
- * @return {Point} the transformed point as a new instance
- */
- localToParent: function(/* point */) {
- return this._matrix._transformPoint(Point.read(arguments));
- },
+ /**
+ * Converts the specified point from the item's own local coordinate space
+ * to the parent's coordinate space.
+ *
+ * @param {Point} point the point to be transformed
+ * @return {Point} the transformed point as a new instance
+ */
+ localToParent: function (/* point */) {
+ return this._matrix._transformPoint(Point.read(arguments));
+ },
- /**
- * Transform the item so that its {@link #bounds} fit within the specified
- * rectangle, without changing its aspect ratio.
- *
- * @param {Rectangle} rectangle
- * @param {Boolean} [fill=false]
- *
- * @example {@paperscript height=100}
- * // Fitting an item to the bounding rectangle of another item's bounding
- * // rectangle:
- *
- * // Create a rectangle shaped path with its top left corner
- * // at {x: 80, y: 25} and a size of {width: 75, height: 50}:
- * var path = new Path.Rectangle({
- * point: [80, 25],
- * size: [75, 50],
- * fillColor: 'black'
- * });
- *
- * // Create a circle shaped path with its center at {x: 80, y: 50}
- * // and a radius of 30.
- * var circlePath = new Path.Circle({
- * center: [80, 50],
- * radius: 30,
- * fillColor: 'red'
- * });
- *
- * // Fit the circlePath to the bounding rectangle of
- * // the rectangular path:
- * circlePath.fitBounds(path.bounds);
- *
- * @example {@paperscript height=100}
- * // Fitting an item to the bounding rectangle of another item's bounding
- * // rectangle with the fill parameter set to true:
- *
- * // Create a rectangle shaped path with its top left corner
- * // at {x: 80, y: 25} and a size of {width: 75, height: 50}:
- * var path = new Path.Rectangle({
- * point: [80, 25],
- * size: [75, 50],
- * fillColor: 'black'
- * });
- *
- * // Create a circle shaped path with its center at {x: 80, y: 50}
- * // and a radius of 30.
- * var circlePath = new Path.Circle({
- * center: [80, 50],
- * radius: 30,
- * fillColor: 'red'
- * });
- *
- * // Fit the circlePath to the bounding rectangle of
- * // the rectangular path:
- * circlePath.fitBounds(path.bounds, true);
- *
- * @example {@paperscript height=200}
- * // Fitting an item to the bounding rectangle of the view
- * var path = new Path.Circle({
- * center: [80, 50],
- * radius: 30,
- * fillColor: 'red'
- * });
- *
- * // Fit the path to the bounding rectangle of the view:
- * path.fitBounds(view.bounds);
- */
- fitBounds: function(rectangle, fill) {
- // TODO: Think about passing options with various ways of defining
- // fitting. Compare with InDesign fitting to see possible options.
- rectangle = Rectangle.read(arguments);
- var bounds = this.getBounds(),
- itemRatio = bounds.height / bounds.width,
- rectRatio = rectangle.height / rectangle.width,
- scale = (fill ? itemRatio > rectRatio : itemRatio < rectRatio)
- ? rectangle.width / bounds.width
- : rectangle.height / bounds.height,
- newBounds = new Rectangle(new Point(),
- new Size(bounds.width * scale, bounds.height * scale));
- newBounds.setCenter(rectangle.getCenter());
- this.setBounds(newBounds);
- }
-}), /** @lends Item# */{
- /**
- * {@grouptitle Event Handlers}
- *
- * Item level handler function to be called on each frame of an animation.
- * The function receives an event object which contains information about
- * the frame event:
- *
- * @option event.count {Number} the number of times the frame event was
- * fired
- * @option event.time {Number} the total amount of time passed since the
- * first frame event in seconds
- * @option event.delta {Number} the time passed in seconds since the last
- * frame event
- *
- * @name Item#onFrame
- * @property
- * @type ?Function
- * @see View#onFrame
- *
- * @example {@paperscript}
- * // Creating an animation:
- *
- * // Create a rectangle shaped path with its top left point at:
- * // {x: 50, y: 25} and a size of {width: 50, height: 50}
- * var path = new Path.Rectangle(new Point(50, 25), new Size(50, 50));
- * path.fillColor = 'black';
- *
- * path.onFrame = function(event) {
- * // Every frame, rotate the path by 3 degrees:
- * this.rotate(3);
- * }
- */
+ /**
+ * Transform the item so that its {@link #bounds} fit within the specified
+ * rectangle, without changing its aspect ratio.
+ *
+ * @param {Rectangle} rectangle
+ * @param {Boolean} [fill=false]
+ *
+ * @example {@paperscript height=100}
+ * // Fitting an item to the bounding rectangle of another item's bounding
+ * // rectangle:
+ *
+ * // Create a rectangle shaped path with its top left corner
+ * // at {x: 80, y: 25} and a size of {width: 75, height: 50}:
+ * var path = new Path.Rectangle({
+ * point: [80, 25],
+ * size: [75, 50],
+ * fillColor: 'black'
+ * });
+ *
+ * // Create a circle shaped path with its center at {x: 80, y: 50}
+ * // and a radius of 30.
+ * var circlePath = new Path.Circle({
+ * center: [80, 50],
+ * radius: 30,
+ * fillColor: 'red'
+ * });
+ *
+ * // Fit the circlePath to the bounding rectangle of
+ * // the rectangular path:
+ * circlePath.fitBounds(path.bounds);
+ *
+ * @example {@paperscript height=100}
+ * // Fitting an item to the bounding rectangle of another item's bounding
+ * // rectangle with the fill parameter set to true:
+ *
+ * // Create a rectangle shaped path with its top left corner
+ * // at {x: 80, y: 25} and a size of {width: 75, height: 50}:
+ * var path = new Path.Rectangle({
+ * point: [80, 25],
+ * size: [75, 50],
+ * fillColor: 'black'
+ * });
+ *
+ * // Create a circle shaped path with its center at {x: 80, y: 50}
+ * // and a radius of 30.
+ * var circlePath = new Path.Circle({
+ * center: [80, 50],
+ * radius: 30,
+ * fillColor: 'red'
+ * });
+ *
+ * // Fit the circlePath to the bounding rectangle of
+ * // the rectangular path:
+ * circlePath.fitBounds(path.bounds, true);
+ *
+ * @example {@paperscript height=200}
+ * // Fitting an item to the bounding rectangle of the view
+ * var path = new Path.Circle({
+ * center: [80, 50],
+ * radius: 30,
+ * fillColor: 'red'
+ * });
+ *
+ * // Fit the path to the bounding rectangle of the view:
+ * path.fitBounds(view.bounds);
+ */
+ fitBounds: function (rectangle, fill) {
+ // TODO: Think about passing options with various ways of defining
+ // fitting. Compare with InDesign fitting to see possible options.
+ rectangle = Rectangle.read(arguments);
+ var bounds = this.getBounds(),
+ itemRatio = bounds.height / bounds.width,
+ rectRatio = rectangle.height / rectangle.width,
+ scale = (
+ fill ? itemRatio > rectRatio : itemRatio < rectRatio
+ )
+ ? rectangle.width / bounds.width
+ : rectangle.height / bounds.height,
+ newBounds = new Rectangle(
+ new Point(),
+ new Size(bounds.width * scale, bounds.height * scale)
+ );
+ newBounds.setCenter(rectangle.getCenter());
+ this.setBounds(newBounds);
+ },
+ }
+ ),
+ /** @lends Item# */ {
+ /**
+ * {@grouptitle Event Handlers}
+ *
+ * Item level handler function to be called on each frame of an animation.
+ * The function receives an event object which contains information about
+ * the frame event:
+ *
+ * @option event.count {Number} the number of times the frame event was
+ * fired
+ * @option event.time {Number} the total amount of time passed since the
+ * first frame event in seconds
+ * @option event.delta {Number} the time passed in seconds since the last
+ * frame event
+ *
+ * @name Item#onFrame
+ * @property
+ * @type ?Function
+ * @see View#onFrame
+ *
+ * @example {@paperscript}
+ * // Creating an animation:
+ *
+ * // Create a rectangle shaped path with its top left point at:
+ * // {x: 50, y: 25} and a size of {width: 50, height: 50}
+ * var path = new Path.Rectangle(new Point(50, 25), new Size(50, 50));
+ * path.fillColor = 'black';
+ *
+ * path.onFrame = function(event) {
+ * // Every frame, rotate the path by 3 degrees:
+ * this.rotate(3);
+ * }
+ */
- /**
- * The function to be called when the mouse button is pushed down on the
- * item. The function receives a {@link MouseEvent} object which contains
- * information about the mouse event.
- * Note that such mouse events bubble up the scene graph hierarchy and will
- * reach the view, unless they are stopped with {@link
- * Event#stopPropagation()} or by returning `false` from the handler.
- *
- * @name Item#onMouseDown
- * @property
- * @type ?Function
- * @see View#onMouseDown
- *
- * @example {@paperscript}
- * // Press the mouse button down on the circle shaped path, to make it red:
- *
- * // Create a circle shaped path at the center of the view:
- * var path = new Path.Circle({
- * center: view.center,
- * radius: 25,
- * fillColor: 'black'
- * });
- *
- * // When the mouse is pressed on the item,
- * // set its fill color to red:
- * path.onMouseDown = function(event) {
- * this.fillColor = 'red';
- * }
- *
- * @example {@paperscript}
- * // Press the mouse on the circle shaped paths to remove them:
- *
- * // Loop 30 times:
- * for (var i = 0; i < 30; i++) {
- * // Create a circle shaped path at a random position
- * // in the view:
- * var path = new Path.Circle({
- * center: Point.random() * view.size,
- * radius: 25,
- * fillColor: 'black',
- * strokeColor: 'white'
- * });
- *
- * // When the mouse is pressed on the item, remove it:
- * path.onMouseDown = function(event) {
- * this.remove();
- * }
- * }
- */
+ /**
+ * The function to be called when the mouse button is pushed down on the
+ * item. The function receives a {@link MouseEvent} object which contains
+ * information about the mouse event.
+ * Note that such mouse events bubble up the scene graph hierarchy and will
+ * reach the view, unless they are stopped with {@link
+ * Event#stopPropagation()} or by returning `false` from the handler.
+ *
+ * @name Item#onMouseDown
+ * @property
+ * @type ?Function
+ * @see View#onMouseDown
+ *
+ * @example {@paperscript}
+ * // Press the mouse button down on the circle shaped path, to make it red:
+ *
+ * // Create a circle shaped path at the center of the view:
+ * var path = new Path.Circle({
+ * center: view.center,
+ * radius: 25,
+ * fillColor: 'black'
+ * });
+ *
+ * // When the mouse is pressed on the item,
+ * // set its fill color to red:
+ * path.onMouseDown = function(event) {
+ * this.fillColor = 'red';
+ * }
+ *
+ * @example {@paperscript}
+ * // Press the mouse on the circle shaped paths to remove them:
+ *
+ * // Loop 30 times:
+ * for (var i = 0; i < 30; i++) {
+ * // Create a circle shaped path at a random position
+ * // in the view:
+ * var path = new Path.Circle({
+ * center: Point.random() * view.size,
+ * radius: 25,
+ * fillColor: 'black',
+ * strokeColor: 'white'
+ * });
+ *
+ * // When the mouse is pressed on the item, remove it:
+ * path.onMouseDown = function(event) {
+ * this.remove();
+ * }
+ * }
+ */
- /**
- * The function to be called when the mouse position changes while the mouse
- * is being dragged over the item. The function receives a {@link
- * MouseEvent} object which contains information about the mouse event.
- * Note that such mouse events bubble up the scene graph hierarchy and will
- * reach the view, unless they are stopped with {@link
- * Event#stopPropagation()} or by returning `false` from the handler.
- *
- * @name Item#onMouseDrag
- * @property
- * @type ?Function
- * @see View#onMouseDrag
- *
- * @example {@paperscript height=240}
- * // Press and drag the mouse on the blue circle to move it:
- *
- * // Create a circle shaped path at the center of the view:
- * var path = new Path.Circle({
- * center: view.center,
- * radius: 50,
- * fillColor: 'blue'
- * });
- *
- * // Install a drag event handler that moves the path along.
- * path.onMouseDrag = function(event) {
- * path.position += event.delta;
- * }
- */
+ /**
+ * The function to be called when the mouse position changes while the mouse
+ * is being dragged over the item. The function receives a {@link
+ * MouseEvent} object which contains information about the mouse event.
+ * Note that such mouse events bubble up the scene graph hierarchy and will
+ * reach the view, unless they are stopped with {@link
+ * Event#stopPropagation()} or by returning `false` from the handler.
+ *
+ * @name Item#onMouseDrag
+ * @property
+ * @type ?Function
+ * @see View#onMouseDrag
+ *
+ * @example {@paperscript height=240}
+ * // Press and drag the mouse on the blue circle to move it:
+ *
+ * // Create a circle shaped path at the center of the view:
+ * var path = new Path.Circle({
+ * center: view.center,
+ * radius: 50,
+ * fillColor: 'blue'
+ * });
+ *
+ * // Install a drag event handler that moves the path along.
+ * path.onMouseDrag = function(event) {
+ * path.position += event.delta;
+ * }
+ */
- /**
- * The function to be called when the mouse button is released over the item.
- * The function receives a {@link MouseEvent} object which contains
- * information about the mouse event.
- * Note that such mouse events bubble up the scene graph hierarchy and will
- * reach the view, unless they are stopped with {@link
- * Event#stopPropagation()} or by returning `false` from the handler.
- *
- * @name Item#onMouseUp
- * @property
- * @type ?Function
- * @see View#onMouseUp
- *
- * @example {@paperscript}
- * // Release the mouse button over the circle shaped path, to make it red:
- *
- * // Create a circle shaped path at the center of the view:
- * var path = new Path.Circle({
- * center: view.center,
- * radius: 25,
- * fillColor: 'black'
- * });
- *
- * // When the mouse is released over the item,
- * // set its fill color to red:
- * path.onMouseUp = function(event) {
- * this.fillColor = 'red';
- * }
- */
+ /**
+ * The function to be called when the mouse button is released over the item.
+ * The function receives a {@link MouseEvent} object which contains
+ * information about the mouse event.
+ * Note that such mouse events bubble up the scene graph hierarchy and will
+ * reach the view, unless they are stopped with {@link
+ * Event#stopPropagation()} or by returning `false` from the handler.
+ *
+ * @name Item#onMouseUp
+ * @property
+ * @type ?Function
+ * @see View#onMouseUp
+ *
+ * @example {@paperscript}
+ * // Release the mouse button over the circle shaped path, to make it red:
+ *
+ * // Create a circle shaped path at the center of the view:
+ * var path = new Path.Circle({
+ * center: view.center,
+ * radius: 25,
+ * fillColor: 'black'
+ * });
+ *
+ * // When the mouse is released over the item,
+ * // set its fill color to red:
+ * path.onMouseUp = function(event) {
+ * this.fillColor = 'red';
+ * }
+ */
- /**
- * The function to be called when the mouse clicks on the item. The function
- * receives a {@link MouseEvent} object which contains information about the
- * mouse event.
- * Note that such mouse events bubble up the scene graph hierarchy and will
- * reach the view, unless they are stopped with {@link
- * Event#stopPropagation()} or by returning `false` from the handler.
- *
- * @name Item#onClick
- * @property
- * @type ?Function
- * @see View#onClick
- *
- * @example {@paperscript}
- * // Click on the circle shaped path, to make it red:
- *
- * // Create a circle shaped path at the center of the view:
- * var path = new Path.Circle({
- * center: view.center,
- * radius: 25,
- * fillColor: 'black'
- * });
- *
- * // When the mouse is clicked on the item,
- * // set its fill color to red:
- * path.onClick = function(event) {
- * this.fillColor = 'red';
- * }
- *
- * @example {@paperscript}
- * // Click on the circle shaped paths to remove them:
- *
- * // Loop 30 times:
- * for (var i = 0; i < 30; i++) {
- * // Create a circle shaped path at a random position
- * // in the view:
- * var path = new Path.Circle({
- * center: Point.random() * view.size,
- * radius: 25,
- * fillColor: 'black',
- * strokeColor: 'white'
- * });
- *
- * // When the mouse clicks on the item, remove it:
- * path.onClick = function(event) {
- * this.remove();
- * }
- * }
- */
+ /**
+ * The function to be called when the mouse clicks on the item. The function
+ * receives a {@link MouseEvent} object which contains information about the
+ * mouse event.
+ * Note that such mouse events bubble up the scene graph hierarchy and will
+ * reach the view, unless they are stopped with {@link
+ * Event#stopPropagation()} or by returning `false` from the handler.
+ *
+ * @name Item#onClick
+ * @property
+ * @type ?Function
+ * @see View#onClick
+ *
+ * @example {@paperscript}
+ * // Click on the circle shaped path, to make it red:
+ *
+ * // Create a circle shaped path at the center of the view:
+ * var path = new Path.Circle({
+ * center: view.center,
+ * radius: 25,
+ * fillColor: 'black'
+ * });
+ *
+ * // When the mouse is clicked on the item,
+ * // set its fill color to red:
+ * path.onClick = function(event) {
+ * this.fillColor = 'red';
+ * }
+ *
+ * @example {@paperscript}
+ * // Click on the circle shaped paths to remove them:
+ *
+ * // Loop 30 times:
+ * for (var i = 0; i < 30; i++) {
+ * // Create a circle shaped path at a random position
+ * // in the view:
+ * var path = new Path.Circle({
+ * center: Point.random() * view.size,
+ * radius: 25,
+ * fillColor: 'black',
+ * strokeColor: 'white'
+ * });
+ *
+ * // When the mouse clicks on the item, remove it:
+ * path.onClick = function(event) {
+ * this.remove();
+ * }
+ * }
+ */
- /**
- * The function to be called when the mouse double clicks on the item. The
- * function receives a {@link MouseEvent} object which contains information
- * about the mouse event.
- * Note that such mouse events bubble up the scene graph hierarchy and will
- * reach the view, unless they are stopped with {@link
- * Event#stopPropagation()} or by returning `false` from the handler.
- *
- * @name Item#onDoubleClick
- * @property
- * @type ?Function
- * @see View#onDoubleClick
- *
- * @example {@paperscript}
- * // Double click on the circle shaped path, to make it red:
- *
- * // Create a circle shaped path at the center of the view:
- * var path = new Path.Circle({
- * center: view.center,
- * radius: 25,
- * fillColor: 'black'
- * });
- *
- * // When the mouse is double clicked on the item,
- * // set its fill color to red:
- * path.onDoubleClick = function(event) {
- * this.fillColor = 'red';
- * }
- *
- * @example {@paperscript}
- * // Double click on the circle shaped paths to remove them:
- *
- * // Loop 30 times:
- * for (var i = 0; i < 30; i++) {
- * // Create a circle shaped path at a random position
- * // in the view:
- * var path = new Path.Circle({
- * center: Point.random() * view.size,
- * radius: 25,
- * fillColor: 'black',
- * strokeColor: 'white'
- * });
- *
- * // When the mouse is double clicked on the item, remove it:
- * path.onDoubleClick = function(event) {
- * this.remove();
- * }
- * }
- */
+ /**
+ * The function to be called when the mouse double clicks on the item. The
+ * function receives a {@link MouseEvent} object which contains information
+ * about the mouse event.
+ * Note that such mouse events bubble up the scene graph hierarchy and will
+ * reach the view, unless they are stopped with {@link
+ * Event#stopPropagation()} or by returning `false` from the handler.
+ *
+ * @name Item#onDoubleClick
+ * @property
+ * @type ?Function
+ * @see View#onDoubleClick
+ *
+ * @example {@paperscript}
+ * // Double click on the circle shaped path, to make it red:
+ *
+ * // Create a circle shaped path at the center of the view:
+ * var path = new Path.Circle({
+ * center: view.center,
+ * radius: 25,
+ * fillColor: 'black'
+ * });
+ *
+ * // When the mouse is double clicked on the item,
+ * // set its fill color to red:
+ * path.onDoubleClick = function(event) {
+ * this.fillColor = 'red';
+ * }
+ *
+ * @example {@paperscript}
+ * // Double click on the circle shaped paths to remove them:
+ *
+ * // Loop 30 times:
+ * for (var i = 0; i < 30; i++) {
+ * // Create a circle shaped path at a random position
+ * // in the view:
+ * var path = new Path.Circle({
+ * center: Point.random() * view.size,
+ * radius: 25,
+ * fillColor: 'black',
+ * strokeColor: 'white'
+ * });
+ *
+ * // When the mouse is double clicked on the item, remove it:
+ * path.onDoubleClick = function(event) {
+ * this.remove();
+ * }
+ * }
+ */
- /**
- * The function to be called repeatedly while the mouse moves over the item.
- * The function receives a {@link MouseEvent} object which contains
- * information about the mouse event.
- * Note that such mouse events bubble up the scene graph hierarchy and will
- * reach the view, unless they are stopped with {@link
- * Event#stopPropagation()} or by returning `false` from the handler.
- *
- * @name Item#onMouseMove
- * @property
- * @type ?Function
- * @see View#onMouseMove
- *
- * @example {@paperscript}
- * // Move over the circle shaped path, to change its opacity:
- *
- * // Create a circle shaped path at the center of the view:
- * var path = new Path.Circle({
- * center: view.center,
- * radius: 25,
- * fillColor: 'black'
- * });
- *
- * // When the mouse moves on top of the item, set its opacity
- * // to a random value between 0 and 1:
- * path.onMouseMove = function(event) {
- * this.opacity = Math.random();
- * }
- */
+ /**
+ * The function to be called repeatedly while the mouse moves over the item.
+ * The function receives a {@link MouseEvent} object which contains
+ * information about the mouse event.
+ * Note that such mouse events bubble up the scene graph hierarchy and will
+ * reach the view, unless they are stopped with {@link
+ * Event#stopPropagation()} or by returning `false` from the handler.
+ *
+ * @name Item#onMouseMove
+ * @property
+ * @type ?Function
+ * @see View#onMouseMove
+ *
+ * @example {@paperscript}
+ * // Move over the circle shaped path, to change its opacity:
+ *
+ * // Create a circle shaped path at the center of the view:
+ * var path = new Path.Circle({
+ * center: view.center,
+ * radius: 25,
+ * fillColor: 'black'
+ * });
+ *
+ * // When the mouse moves on top of the item, set its opacity
+ * // to a random value between 0 and 1:
+ * path.onMouseMove = function(event) {
+ * this.opacity = Math.random();
+ * }
+ */
- /**
- * The function to be called when the mouse moves over the item. This
- * function will only be called again, once the mouse moved outside of the
- * item first. The function receives a {@link MouseEvent} object which
- * contains information about the mouse event.
- * Note that such mouse events bubble up the scene graph hierarchy and will
- * reach the view, unless they are stopped with {@link
- * Event#stopPropagation()} or by returning `false` from the handler.
- *
- * @name Item#onMouseEnter
- * @property
- * @type ?Function
- * @see View#onMouseEnter
- *
- * @example {@paperscript}
- * // When you move the mouse over the item, its fill color is set to red.
- * // When you move the mouse outside again, its fill color is set back
- * // to black.
- *
- * // Create a circle shaped path at the center of the view:
- * var path = new Path.Circle({
- * center: view.center,
- * radius: 25,
- * fillColor: 'black'
- * });
- *
- * // When the mouse enters the item, set its fill color to red:
- * path.onMouseEnter = function(event) {
- * this.fillColor = 'red';
- * }
- *
- * // When the mouse leaves the item, set its fill color to black:
- * path.onMouseLeave = function(event) {
- * this.fillColor = 'black';
- * }
- * @example {@paperscript}
- * // When you click the mouse, you create new circle shaped items. When you
- * // move the mouse over the item, its fill color is set to red. When you
- * // move the mouse outside again, its fill color is set back
- * // to black.
- *
- * function enter(event) {
- * this.fillColor = 'red';
- * }
- *
- * function leave(event) {
- * this.fillColor = 'black';
- * }
- *
- * // When the mouse is pressed:
- * function onMouseDown(event) {
- * // Create a circle shaped path at the position of the mouse:
- * var path = new Path.Circle(event.point, 25);
- * path.fillColor = 'black';
- *
- * // When the mouse enters the item, set its fill color to red:
- * path.onMouseEnter = enter;
- *
- * // When the mouse leaves the item, set its fill color to black:
- * path.onMouseLeave = leave;
- * }
- */
+ /**
+ * The function to be called when the mouse moves over the item. This
+ * function will only be called again, once the mouse moved outside of the
+ * item first. The function receives a {@link MouseEvent} object which
+ * contains information about the mouse event.
+ * Note that such mouse events bubble up the scene graph hierarchy and will
+ * reach the view, unless they are stopped with {@link
+ * Event#stopPropagation()} or by returning `false` from the handler.
+ *
+ * @name Item#onMouseEnter
+ * @property
+ * @type ?Function
+ * @see View#onMouseEnter
+ *
+ * @example {@paperscript}
+ * // When you move the mouse over the item, its fill color is set to red.
+ * // When you move the mouse outside again, its fill color is set back
+ * // to black.
+ *
+ * // Create a circle shaped path at the center of the view:
+ * var path = new Path.Circle({
+ * center: view.center,
+ * radius: 25,
+ * fillColor: 'black'
+ * });
+ *
+ * // When the mouse enters the item, set its fill color to red:
+ * path.onMouseEnter = function(event) {
+ * this.fillColor = 'red';
+ * }
+ *
+ * // When the mouse leaves the item, set its fill color to black:
+ * path.onMouseLeave = function(event) {
+ * this.fillColor = 'black';
+ * }
+ * @example {@paperscript}
+ * // When you click the mouse, you create new circle shaped items. When you
+ * // move the mouse over the item, its fill color is set to red. When you
+ * // move the mouse outside again, its fill color is set back
+ * // to black.
+ *
+ * function enter(event) {
+ * this.fillColor = 'red';
+ * }
+ *
+ * function leave(event) {
+ * this.fillColor = 'black';
+ * }
+ *
+ * // When the mouse is pressed:
+ * function onMouseDown(event) {
+ * // Create a circle shaped path at the position of the mouse:
+ * var path = new Path.Circle(event.point, 25);
+ * path.fillColor = 'black';
+ *
+ * // When the mouse enters the item, set its fill color to red:
+ * path.onMouseEnter = enter;
+ *
+ * // When the mouse leaves the item, set its fill color to black:
+ * path.onMouseLeave = leave;
+ * }
+ */
- /**
- * The function to be called when the mouse moves out of the item.
- * The function receives a {@link MouseEvent} object which contains
- * information about the mouse event.
- * Note that such mouse events bubble up the scene graph hierarchy and will
- * reach the view, unless they are stopped with {@link
- * Event#stopPropagation()} or by returning `false` from the handler.
- *
- * @name Item#onMouseLeave
- * @property
- * @type ?Function
- * @see View#onMouseLeave
- *
- * @example {@paperscript}
- * // Move the mouse over the circle shaped path and then move it out
- * // of it again to set its fill color to red:
- *
- * // Create a circle shaped path at the center of the view:
- * var path = new Path.Circle({
- * center: view.center,
- * radius: 25,
- * fillColor: 'black'
- * });
- *
- * // When the mouse leaves the item, set its fill color to red:
- * path.onMouseLeave = function(event) {
- * this.fillColor = 'red';
- * }
- */
+ /**
+ * The function to be called when the mouse moves out of the item.
+ * The function receives a {@link MouseEvent} object which contains
+ * information about the mouse event.
+ * Note that such mouse events bubble up the scene graph hierarchy and will
+ * reach the view, unless they are stopped with {@link
+ * Event#stopPropagation()} or by returning `false` from the handler.
+ *
+ * @name Item#onMouseLeave
+ * @property
+ * @type ?Function
+ * @see View#onMouseLeave
+ *
+ * @example {@paperscript}
+ * // Move the mouse over the circle shaped path and then move it out
+ * // of it again to set its fill color to red:
+ *
+ * // Create a circle shaped path at the center of the view:
+ * var path = new Path.Circle({
+ * center: view.center,
+ * radius: 25,
+ * fillColor: 'black'
+ * });
+ *
+ * // When the mouse leaves the item, set its fill color to red:
+ * path.onMouseLeave = function(event) {
+ * this.fillColor = 'red';
+ * }
+ */
- /**
- * {@grouptitle Event Handling}
- *
- * Attaches an event handler to the item.
- *
- * @name Item#on
- * @function
- * @param {String} type the type of event: {@values 'frame', mousedown',
- * 'mouseup', 'mousedrag', 'click', 'doubleclick', 'mousemove',
- * 'mouseenter', 'mouseleave'}
- * @param {Function} function the function to be called when the event
- * occurs, receiving a {@link MouseEvent} or {@link Event} object as its
- * sole argument
- * @return {Item} this item itself, so calls can be chained
- * @chainable
- *
- * @example {@paperscript}
- * // Change the fill color of the path to red when the mouse enters its
- * // shape and back to black again, when it leaves its shape.
- *
- * // Create a circle shaped path at the center of the view:
- * var path = new Path.Circle({
- * center: view.center,
- * radius: 25,
- * fillColor: 'black'
- * });
- *
- * // When the mouse enters the item, set its fill color to red:
- * path.on('mouseenter', function() {
- * this.fillColor = 'red';
- * });
- *
- * // When the mouse leaves the item, set its fill color to black:
- * path.on('mouseleave', function() {
- * this.fillColor = 'black';
- * });
- */
- /**
- * Attaches one or more event handlers to the item.
- *
- * @name Item#on
- * @function
- * @param {Object} object an object containing one or more of the following
- * properties: {@values frame, mousedown, mouseup, mousedrag, click,
- * doubleclick, mousemove, mouseenter, mouseleave}
- * @return {Item} this item itself, so calls can be chained
- * @chainable
- *
- * @example {@paperscript}
- * // Change the fill color of the path to red when the mouse enters its
- * // shape and back to black again, when it leaves its shape.
- *
- * // Create a circle shaped path at the center of the view:
- * var path = new Path.Circle({
- * center: view.center,
- * radius: 25
- * });
- * path.fillColor = 'black';
- *
- * // When the mouse enters the item, set its fill color to red:
- * path.on({
- * mouseenter: function(event) {
- * this.fillColor = 'red';
- * },
- * mouseleave: function(event) {
- * this.fillColor = 'black';
- * }
- * });
- * @example {@paperscript}
- * // When you click the mouse, you create new circle shaped items. When you
- * // move the mouse over the item, its fill color is set to red. When you
- * // move the mouse outside again, its fill color is set black.
- *
- * var pathHandlers = {
- * mouseenter: function(event) {
- * this.fillColor = 'red';
- * },
- * mouseleave: function(event) {
- * this.fillColor = 'black';
- * }
- * }
- *
- * // When the mouse is pressed:
- * function onMouseDown(event) {
- * // Create a circle shaped path at the position of the mouse:
- * var path = new Path.Circle({
- * center: event.point,
- * radius: 25,
- * fillColor: 'black'
- * });
- *
- * // Attach the handers inside the object literal to the path:
- * path.on(pathHandlers);
- * }
- */
+ /**
+ * {@grouptitle Event Handling}
+ *
+ * Attaches an event handler to the item.
+ *
+ * @name Item#on
+ * @function
+ * @param {String} type the type of event: {@values 'frame', mousedown',
+ * 'mouseup', 'mousedrag', 'click', 'doubleclick', 'mousemove',
+ * 'mouseenter', 'mouseleave'}
+ * @param {Function} function the function to be called when the event
+ * occurs, receiving a {@link MouseEvent} or {@link Event} object as its
+ * sole argument
+ * @return {Item} this item itself, so calls can be chained
+ * @chainable
+ *
+ * @example {@paperscript}
+ * // Change the fill color of the path to red when the mouse enters its
+ * // shape and back to black again, when it leaves its shape.
+ *
+ * // Create a circle shaped path at the center of the view:
+ * var path = new Path.Circle({
+ * center: view.center,
+ * radius: 25,
+ * fillColor: 'black'
+ * });
+ *
+ * // When the mouse enters the item, set its fill color to red:
+ * path.on('mouseenter', function() {
+ * this.fillColor = 'red';
+ * });
+ *
+ * // When the mouse leaves the item, set its fill color to black:
+ * path.on('mouseleave', function() {
+ * this.fillColor = 'black';
+ * });
+ */
+ /**
+ * Attaches one or more event handlers to the item.
+ *
+ * @name Item#on
+ * @function
+ * @param {Object} object an object containing one or more of the following
+ * properties: {@values frame, mousedown, mouseup, mousedrag, click,
+ * doubleclick, mousemove, mouseenter, mouseleave}
+ * @return {Item} this item itself, so calls can be chained
+ * @chainable
+ *
+ * @example {@paperscript}
+ * // Change the fill color of the path to red when the mouse enters its
+ * // shape and back to black again, when it leaves its shape.
+ *
+ * // Create a circle shaped path at the center of the view:
+ * var path = new Path.Circle({
+ * center: view.center,
+ * radius: 25
+ * });
+ * path.fillColor = 'black';
+ *
+ * // When the mouse enters the item, set its fill color to red:
+ * path.on({
+ * mouseenter: function(event) {
+ * this.fillColor = 'red';
+ * },
+ * mouseleave: function(event) {
+ * this.fillColor = 'black';
+ * }
+ * });
+ * @example {@paperscript}
+ * // When you click the mouse, you create new circle shaped items. When you
+ * // move the mouse over the item, its fill color is set to red. When you
+ * // move the mouse outside again, its fill color is set black.
+ *
+ * var pathHandlers = {
+ * mouseenter: function(event) {
+ * this.fillColor = 'red';
+ * },
+ * mouseleave: function(event) {
+ * this.fillColor = 'black';
+ * }
+ * }
+ *
+ * // When the mouse is pressed:
+ * function onMouseDown(event) {
+ * // Create a circle shaped path at the position of the mouse:
+ * var path = new Path.Circle({
+ * center: event.point,
+ * radius: 25,
+ * fillColor: 'black'
+ * });
+ *
+ * // Attach the handers inside the object literal to the path:
+ * path.on(pathHandlers);
+ * }
+ */
- /**
- * Detach an event handler from the item.
- *
- * @name Item#off
- * @function
- * @param {String} type the type of event: {@values 'frame', mousedown',
- * 'mouseup', 'mousedrag', 'click', 'doubleclick', 'mousemove',
- * 'mouseenter', 'mouseleave'}
- * @param {Function} function the function to be detached
- * @return {Item} this item itself, so calls can be chained
- * @chainable
- */
- /**
- * Detach one or more event handlers to the item.
- *
- * @name Item#off
- * @function
- * @param {Object} object an object containing one or more of the following
- * properties: {@values frame, mousedown, mouseup, mousedrag, click,
- * doubleclick, mousemove, mouseenter, mouseleave}
- * @return {Item} this item itself, so calls can be chained
- * @chainable
- */
+ /**
+ * Detach an event handler from the item.
+ *
+ * @name Item#off
+ * @function
+ * @param {String} type the type of event: {@values 'frame', mousedown',
+ * 'mouseup', 'mousedrag', 'click', 'doubleclick', 'mousemove',
+ * 'mouseenter', 'mouseleave'}
+ * @param {Function} function the function to be detached
+ * @return {Item} this item itself, so calls can be chained
+ * @chainable
+ */
+ /**
+ * Detach one or more event handlers to the item.
+ *
+ * @name Item#off
+ * @function
+ * @param {Object} object an object containing one or more of the following
+ * properties: {@values frame, mousedown, mouseup, mousedrag, click,
+ * doubleclick, mousemove, mouseenter, mouseleave}
+ * @return {Item} this item itself, so calls can be chained
+ * @chainable
+ */
- /**
- * Emit an event on the item.
- *
- * @name Item#emit
- * @function
- * @param {String} type the type of event: {@values 'frame', mousedown',
- * 'mouseup', 'mousedrag', 'click', 'doubleclick', 'mousemove',
- * 'mouseenter', 'mouseleave'}
- * @param {Object} event an object literal containing properties describing
- * the event
- * @return {Boolean} {@true if the event had listeners}
- */
+ /**
+ * Emit an event on the item.
+ *
+ * @name Item#emit
+ * @function
+ * @param {String} type the type of event: {@values 'frame', mousedown',
+ * 'mouseup', 'mousedrag', 'click', 'doubleclick', 'mousemove',
+ * 'mouseenter', 'mouseleave'}
+ * @param {Object} event an object literal containing properties describing
+ * the event
+ * @return {Boolean} {@true if the event had listeners}
+ */
- /**
- * Check if the item has one or more event handlers of the specified type.
- *
- * @name Item#responds
- * @function
- * @param {String} type the type of event: {@values 'frame', mousedown',
- * 'mouseup', 'mousedrag', 'click', 'doubleclick', 'mousemove',
- * 'mouseenter', 'mouseleave'}
- * @return {Boolean} {@true if the item has one or more event handlers of
- * the specified type}
- */
+ /**
+ * Check if the item has one or more event handlers of the specified type.
+ *
+ * @name Item#responds
+ * @function
+ * @param {String} type the type of event: {@values 'frame', mousedown',
+ * 'mouseup', 'mousedrag', 'click', 'doubleclick', 'mousemove',
+ * 'mouseenter', 'mouseleave'}
+ * @return {Boolean} {@true if the item has one or more event handlers of
+ * the specified type}
+ */
- /**
- * Private method that sets Path related styles on the canvas context.
- * Not defined in Path as it is required by other classes too,
- * e.g. PointText.
- */
- _setStyles: function(ctx, param, viewMatrix) {
- // We can access internal properties since we're only using this on
- // items without children, where styles would be merged.
- var style = this._style,
- matrix = this._matrix;
- if (style.hasFill()) {
- ctx.fillStyle = style.getFillColor().toCanvasStyle(ctx, matrix);
- }
- if (style.hasStroke()) {
- ctx.strokeStyle = style.getStrokeColor().toCanvasStyle(ctx, matrix);
- ctx.lineWidth = style.getStrokeWidth();
- var strokeJoin = style.getStrokeJoin(),
- strokeCap = style.getStrokeCap(),
- miterLimit = style.getMiterLimit();
- if (strokeJoin)
- ctx.lineJoin = strokeJoin;
- if (strokeCap)
- ctx.lineCap = strokeCap;
- if (miterLimit)
- ctx.miterLimit = miterLimit;
- if (paper.support.nativeDash) {
- var dashArray = style.getDashArray(),
- dashOffset = style.getDashOffset();
- if (dashArray && dashArray.length) {
- if ('setLineDash' in ctx) {
- ctx.setLineDash(dashArray);
- ctx.lineDashOffset = dashOffset;
- } else {
- ctx.mozDash = dashArray;
- ctx.mozDashOffset = dashOffset;
+ /**
+ * Private method that sets Path related styles on the canvas context.
+ * Not defined in Path as it is required by other classes too,
+ * e.g. PointText.
+ */
+ _setStyles: function (ctx, param, viewMatrix) {
+ // We can access internal properties since we're only using this on
+ // items without children, where styles would be merged.
+ var style = this._style,
+ matrix = this._matrix;
+ if (style.hasFill()) {
+ ctx.fillStyle = style.getFillColor().toCanvasStyle(ctx, matrix);
+ }
+ if (style.hasStroke()) {
+ ctx.strokeStyle = style
+ .getStrokeColor()
+ .toCanvasStyle(ctx, matrix);
+ ctx.lineWidth = style.getStrokeWidth();
+ var strokeJoin = style.getStrokeJoin(),
+ strokeCap = style.getStrokeCap(),
+ miterLimit = style.getMiterLimit();
+ if (strokeJoin) ctx.lineJoin = strokeJoin;
+ if (strokeCap) ctx.lineCap = strokeCap;
+ if (miterLimit) ctx.miterLimit = miterLimit;
+ if (paper.support.nativeDash) {
+ var dashArray = style.getDashArray(),
+ dashOffset = style.getDashOffset();
+ if (dashArray && dashArray.length) {
+ if ("setLineDash" in ctx) {
+ ctx.setLineDash(dashArray);
+ ctx.lineDashOffset = dashOffset;
+ } else {
+ ctx.mozDash = dashArray;
+ ctx.mozDashOffset = dashOffset;
+ }
}
}
}
- }
- if (style.hasShadow()) {
- // In Canvas, shadows unfortunately ignore all transformations
- // completely. As almost no browser supports ctx.currentTransform,
- // we need to calculate our own here, and then use it to transform
- // the shadow-blur and offset accordingly.
- var pixelRatio = param.pixelRatio || 1,
- mx = viewMatrix._shiftless().prepend(
- new Matrix().scale(pixelRatio, pixelRatio)),
- // Transform the blur value as a vector and use its new length:
- blur = mx.transform(new Point(style.getShadowBlur(), 0)),
- offset = mx.transform(this.getShadowOffset());
- ctx.shadowColor = style.getShadowColor().toCanvasStyle(ctx);
- ctx.shadowBlur = blur.getLength();
- ctx.shadowOffsetX = offset.x;
- ctx.shadowOffsetY = offset.y;
- }
- },
+ if (style.hasShadow()) {
+ // In Canvas, shadows unfortunately ignore all transformations
+ // completely. As almost no browser supports ctx.currentTransform,
+ // we need to calculate our own here, and then use it to transform
+ // the shadow-blur and offset accordingly.
+ var pixelRatio = param.pixelRatio || 1,
+ mx = viewMatrix
+ ._shiftless()
+ .prepend(new Matrix().scale(pixelRatio, pixelRatio)),
+ // Transform the blur value as a vector and use its new length:
+ blur = mx.transform(new Point(style.getShadowBlur(), 0)),
+ offset = mx.transform(this.getShadowOffset());
+ ctx.shadowColor = style.getShadowColor().toCanvasStyle(ctx);
+ ctx.shadowBlur = blur.getLength();
+ ctx.shadowOffsetX = offset.x;
+ ctx.shadowOffsetY = offset.y;
+ }
+ },
- draw: function(ctx, param, parentStrokeMatrix) {
- // Each time the project gets drawn, it's _updateVersion is increased.
- // Keep the _updateVersion of drawn items in sync, so we have an easy
- // way to know for which selected items we need to draw selection info.
- var updateVersion = this._updateVersion = this._project._updateVersion;
- // Now bail out if no actual drawing is required.
- if (!this._visible || this._opacity === 0)
- return;
- // Keep calculating the current global matrix, by keeping a history
- // and pushing / popping as we go along.
- var matrices = param.matrices,
- viewMatrix = param.viewMatrix,
- matrix = this._matrix,
- globalMatrix = matrices[matrices.length - 1].appended(matrix);
- // If this item is not invertible, do not draw it. It appears to be a
- // good idea generally to not draw in such circumstances, e.g. SVG
- // handles it the same way.
- if (!globalMatrix.isInvertible())
- return;
-
- // Since globalMatrix does not take the view's matrix into account (we
- // could have multiple views with different zooms), we may have to
- // prepend the view's matrix.
- // NOTE: viewMatrix is only provided if it isn't the identity matrix.
- viewMatrix = viewMatrix ? viewMatrix.appended(globalMatrix)
+ draw: function (ctx, param, parentStrokeMatrix) {
+ // Each time the project gets drawn, it's _updateVersion is increased.
+ // Keep the _updateVersion of drawn items in sync, so we have an easy
+ // way to know for which selected items we need to draw selection info.
+ var updateVersion = (this._updateVersion =
+ this._project._updateVersion);
+ // Now bail out if no actual drawing is required.
+ if (!this._visible || this._opacity === 0) return;
+ // Keep calculating the current global matrix, by keeping a history
+ // and pushing / popping as we go along.
+ var matrices = param.matrices,
+ viewMatrix = param.viewMatrix,
+ matrix = this._matrix,
+ globalMatrix = matrices[matrices.length - 1].appended(matrix);
+ // If this item is not invertible, do not draw it. It appears to be a
+ // good idea generally to not draw in such circumstances, e.g. SVG
+ // handles it the same way.
+ if (!globalMatrix.isInvertible()) return;
+
+ // Since globalMatrix does not take the view's matrix into account (we
+ // could have multiple views with different zooms), we may have to
+ // prepend the view's matrix.
+ // NOTE: viewMatrix is only provided if it isn't the identity matrix.
+ viewMatrix = viewMatrix
+ ? viewMatrix.appended(globalMatrix)
: globalMatrix;
- // Only keep track of transformation if told so. See Project#draw()
- matrices.push(globalMatrix);
- if (param.updateMatrix) {
- this._globalMatrix = globalMatrix;
- }
+ // Only keep track of transformation if told so. See Project#draw()
+ matrices.push(globalMatrix);
+ if (param.updateMatrix) {
+ this._globalMatrix = globalMatrix;
+ }
- // If the item has a blendMode or is defining an opacity, draw it on
- // a temporary canvas first and composite the canvas afterwards.
- // Paths with an opacity < 1 that both define a fillColor
- // and strokeColor also need to be drawn on a temporary canvas
- // first, since otherwise their stroke is drawn half transparent
- // over their fill.
- // Exclude Raster items since they never draw a stroke and handle
- // opacity by themselves (they also don't call _setStyles)
- var blendMode = this._blendMode,
- opacity = Numerical.clamp(this._opacity, 0, 1),
- normalBlend = blendMode === 'normal',
- nativeBlend = BlendMode.nativeModes[blendMode],
- // Determine if we can draw directly, or if we need to draw into a
- // separate canvas and then composite onto the main canvas.
- direct = normalBlend && opacity === 1
- || param.dontStart // e.g. CompoundPath
- || param.clip
+ // If the item has a blendMode or is defining an opacity, draw it on
+ // a temporary canvas first and composite the canvas afterwards.
+ // Paths with an opacity < 1 that both define a fillColor
+ // and strokeColor also need to be drawn on a temporary canvas
+ // first, since otherwise their stroke is drawn half transparent
+ // over their fill.
+ // Exclude Raster items since they never draw a stroke and handle
+ // opacity by themselves (they also don't call _setStyles)
+ var blendMode = this._blendMode,
+ opacity = Numerical.clamp(this._opacity, 0, 1),
+ normalBlend = blendMode === "normal",
+ nativeBlend = BlendMode.nativeModes[blendMode],
+ // Determine if we can draw directly, or if we need to draw into a
+ // separate canvas and then composite onto the main canvas.
+ direct =
+ (normalBlend && opacity === 1) ||
+ param.dontStart || // e.g. CompoundPath
+ param.clip ||
// If native blending is possible, see if the item allows it
- || (nativeBlend || normalBlend && opacity < 1)
- && this._canComposite(),
- pixelRatio = param.pixelRatio || 1,
- mainCtx, itemOffset, prevOffset;
- if (!direct) {
- // Apply the parent's global matrix to the calculation of correct
- // bounds.
- var bounds = this.getStrokeBounds(viewMatrix);
- if (!bounds.width || !bounds.height) {
- // Item won't be drawn so its global matrix need to be removed
- // from the stack (#1561).
- matrices.pop();
- return;
+ ((nativeBlend || (normalBlend && opacity < 1)) &&
+ this._canComposite()),
+ pixelRatio = param.pixelRatio || 1,
+ mainCtx,
+ itemOffset,
+ prevOffset;
+ if (!direct) {
+ // Apply the parent's global matrix to the calculation of correct
+ // bounds.
+ var bounds = this.getStrokeBounds(viewMatrix);
+ if (!bounds.width || !bounds.height) {
+ // Item won't be drawn so its global matrix need to be removed
+ // from the stack (#1561).
+ matrices.pop();
+ return;
+ }
+ // Store previous offset and save the main context, so we can
+ // draw onto it later.
+ prevOffset = param.offset;
+ // Floor the offset and ceil the size, so we don't cut off any
+ // antialiased pixels when drawing onto the temporary canvas.
+ itemOffset = param.offset = bounds.getTopLeft().floor();
+ // Set ctx to the context of the temporary canvas, so we draw onto
+ // it, instead of the mainCtx.
+ mainCtx = ctx;
+ ctx = CanvasProvider.getContext(
+ bounds.getSize().ceil().add(1).multiply(pixelRatio)
+ );
+ if (pixelRatio !== 1) ctx.scale(pixelRatio, pixelRatio);
}
- // Store previous offset and save the main context, so we can
- // draw onto it later.
- prevOffset = param.offset;
- // Floor the offset and ceil the size, so we don't cut off any
- // antialiased pixels when drawing onto the temporary canvas.
- itemOffset = param.offset = bounds.getTopLeft().floor();
- // Set ctx to the context of the temporary canvas, so we draw onto
- // it, instead of the mainCtx.
- mainCtx = ctx;
- ctx = CanvasProvider.getContext(bounds.getSize().ceil().add(1)
- .multiply(pixelRatio));
- if (pixelRatio !== 1)
- ctx.scale(pixelRatio, pixelRatio);
- }
- ctx.save();
- // Get the transformation matrix for non-scaling strokes.
- var strokeMatrix = parentStrokeMatrix
- ? parentStrokeMatrix.appended(matrix)
- // pass `true` for dontMerge
- : this._canScaleStroke && !this.getStrokeScaling(true)
- && viewMatrix,
- // If we're drawing into a separate canvas and a clipItem is defined
- // for the current rendering loop, we need to draw the clip item
- // again.
- clip = !direct && param.clipItem,
- // If we're drawing with a strokeMatrix, the CTM is reset either way
- // so we don't need to set it, except when we also have to draw a
- // clipItem.
- transform = !strokeMatrix || clip;
- // If drawing directly, handle opacity and native blending now,
- // otherwise we will do it later when the temporary canvas is composited.
- if (direct) {
- ctx.globalAlpha = opacity;
- if (nativeBlend)
- ctx.globalCompositeOperation = blendMode;
- } else if (transform) {
- // Translate the context so the topLeft of the item is at (0, 0)
- // on the temporary canvas.
- ctx.translate(-itemOffset.x, -itemOffset.y);
- }
- if (transform) {
- // Apply viewMatrix when drawing into temporary canvas.
- (direct ? matrix : viewMatrix).applyToContext(ctx);
- }
- if (clip) {
- param.clipItem.draw(ctx, param.extend({ clip: true }));
- }
- if (strokeMatrix) {
- // Reset the transformation but take HiDPI pixel ratio into account.
- ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
- // Also offset again when drawing non-directly.
- // NOTE: Don't use itemOffset since offset might be from the parent,
- // e.g. CompoundPath
- var offset = param.offset;
- if (offset)
- ctx.translate(-offset.x, -offset.y);
- }
- this._draw(ctx, param, viewMatrix, strokeMatrix);
- ctx.restore();
- matrices.pop();
- if (param.clip && !param.dontFinish) {
- // Pass fill-rule to handle clipping with compound-paths (#1361).
- ctx.clip(this.getFillRule());
- }
- // If a temporary canvas was created, composite it onto the main canvas:
- if (!direct) {
- // Use BlendMode.process even for processing normal blendMode with
- // opacity.
- BlendMode.process(blendMode, ctx, mainCtx, opacity,
+ ctx.save();
+ // Get the transformation matrix for non-scaling strokes.
+ var strokeMatrix = parentStrokeMatrix
+ ? parentStrokeMatrix.appended(matrix)
+ : // pass `true` for dontMerge
+ this._canScaleStroke &&
+ !this.getStrokeScaling(true) &&
+ viewMatrix,
+ // If we're drawing into a separate canvas and a clipItem is defined
+ // for the current rendering loop, we need to draw the clip item
+ // again.
+ clip = !direct && param.clipItem,
+ // If we're drawing with a strokeMatrix, the CTM is reset either way
+ // so we don't need to set it, except when we also have to draw a
+ // clipItem.
+ transform = !strokeMatrix || clip;
+ // If drawing directly, handle opacity and native blending now,
+ // otherwise we will do it later when the temporary canvas is composited.
+ if (direct) {
+ ctx.globalAlpha = opacity;
+ if (nativeBlend) ctx.globalCompositeOperation = blendMode;
+ } else if (transform) {
+ // Translate the context so the topLeft of the item is at (0, 0)
+ // on the temporary canvas.
+ ctx.translate(-itemOffset.x, -itemOffset.y);
+ }
+ if (transform) {
+ // Apply viewMatrix when drawing into temporary canvas.
+ (direct ? matrix : viewMatrix).applyToContext(ctx);
+ }
+ if (clip) {
+ param.clipItem.draw(ctx, param.extend({ clip: true }));
+ }
+ if (strokeMatrix) {
+ // Reset the transformation but take HiDPI pixel ratio into account.
+ ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
+ // Also offset again when drawing non-directly.
+ // NOTE: Don't use itemOffset since offset might be from the parent,
+ // e.g. CompoundPath
+ var offset = param.offset;
+ if (offset) ctx.translate(-offset.x, -offset.y);
+ }
+ this._draw(ctx, param, viewMatrix, strokeMatrix);
+ ctx.restore();
+ matrices.pop();
+ if (param.clip && !param.dontFinish) {
+ // Pass fill-rule to handle clipping with compound-paths (#1361).
+ ctx.clip(this.getFillRule());
+ }
+ // If a temporary canvas was created, composite it onto the main canvas:
+ if (!direct) {
+ // Use BlendMode.process even for processing normal blendMode with
+ // opacity.
+ BlendMode.process(
+ blendMode,
+ ctx,
+ mainCtx,
+ opacity,
// Calculate the pixel offset of the temporary canvas to the
// main canvas. We also need to factor in the pixel-ratio.
- itemOffset.subtract(prevOffset).multiply(pixelRatio));
- // Return the temporary context, so it can be reused
- CanvasProvider.release(ctx);
- // Restore previous offset.
- param.offset = prevOffset;
- }
- },
+ itemOffset.subtract(prevOffset).multiply(pixelRatio)
+ );
+ // Return the temporary context, so it can be reused
+ CanvasProvider.release(ctx);
+ // Restore previous offset.
+ param.offset = prevOffset;
+ }
+ },
- /**
- * Checks the _updateVersion of the item to see if it got drawn in the draw
- * loop. If the version is out of sync, the item is either not in the scene
- * graph anymore or is invisible.
- */
- _isUpdated: function(updateVersion) {
- var parent = this._parent;
- // For compound-paths, use the _updateVersion of the parent, because the
- // shape gets drawn at once at might get cached (e.g. Path2D soon).
- if (parent instanceof CompoundPath)
- return parent._isUpdated(updateVersion);
- // In case a parent is visible but isn't drawn (e.g. opacity == 0), the
- // _updateVersion of all its children will not be updated, but the
- // children should still be considered updated, and selections should be
- // drawn for them. Excluded are only items with _visible == false:
- var updated = this._updateVersion === updateVersion;
- if (!updated && parent && parent._visible
- && parent._isUpdated(updateVersion)) {
- this._updateVersion = updateVersion;
- updated = true;
- }
- return updated;
- },
+ /**
+ * Checks the _updateVersion of the item to see if it got drawn in the draw
+ * loop. If the version is out of sync, the item is either not in the scene
+ * graph anymore or is invisible.
+ */
+ _isUpdated: function (updateVersion) {
+ var parent = this._parent;
+ // For compound-paths, use the _updateVersion of the parent, because the
+ // shape gets drawn at once at might get cached (e.g. Path2D soon).
+ if (parent instanceof CompoundPath)
+ return parent._isUpdated(updateVersion);
+ // In case a parent is visible but isn't drawn (e.g. opacity == 0), the
+ // _updateVersion of all its children will not be updated, but the
+ // children should still be considered updated, and selections should be
+ // drawn for them. Excluded are only items with _visible == false:
+ var updated = this._updateVersion === updateVersion;
+ if (
+ !updated &&
+ parent &&
+ parent._visible &&
+ parent._isUpdated(updateVersion)
+ ) {
+ this._updateVersion = updateVersion;
+ updated = true;
+ }
+ return updated;
+ },
- _drawSelection: function(ctx, matrix, size, selectionItems, updateVersion) {
- var selection = this._selection,
- itemSelected = selection & /*#=*/ItemSelection.ITEM,
- boundsSelected = selection & /*#=*/ItemSelection.BOUNDS
- || itemSelected && this._selectBounds,
- positionSelected = selection & /*#=*/ItemSelection.POSITION;
- if (!this._drawSelected)
- itemSelected = false;
- if ((itemSelected || boundsSelected || positionSelected)
- && this._isUpdated(updateVersion)) {
- // Allow definition of selected color on a per item and per
- // layer level, with a fallback to #009dec
- var layer,
- color = this.getSelectedColor(true) || (layer = this.getLayer())
- && layer.getSelectedColor(true),
- mx = matrix.appended(this.getGlobalMatrix(true)),
- half = size / 2;
- ctx.strokeStyle = ctx.fillStyle = color
- ? color.toCanvasStyle(ctx) : '#009dec';
- if (itemSelected)
- this._drawSelected(ctx, mx, selectionItems);
- if (positionSelected) {
- // Convert position from the parent's coordinates system to the
- // global one:
- var pos = this.getPosition(true),
- parent = this._parent,
- point = parent ? parent.localToGlobal(pos) : pos,
- x = point.x,
- y = point.y;
- ctx.beginPath();
- ctx.arc(x, y, half, 0, Math.PI * 2, true);
- ctx.stroke();
- var deltas = [[0, -1], [1, 0], [0, 1], [-1, 0]],
- start = half,
- end = size + 1;
- for (var i = 0; i < 4; i++) {
- var delta = deltas[i],
- dx = delta[0],
- dy = delta[1];
- ctx.moveTo(x + dx * start, y + dy * start);
- ctx.lineTo(x + dx * end, y + dy * end);
+ _drawSelection: function (
+ ctx,
+ matrix,
+ size,
+ selectionItems,
+ updateVersion
+ ) {
+ ctx.lineWidth = this._style.selectedWidth;
+ ctx.lineCap = this._style.selectedCap;
+ ctx.lineJoin = this._style.selectedJoin;
+ ctx.miterLimit = this._style.selectedMiterLimit;
+ var selection = this._selection,
+ itemSelected = selection & /*#=*/ ItemSelection.ITEM,
+ boundsSelected =
+ selection & /*#=*/ ItemSelection.BOUNDS ||
+ (itemSelected && this._selectBounds),
+ positionSelected = selection & /*#=*/ ItemSelection.POSITION;
+ if (!this._drawSelected) itemSelected = false;
+ if (
+ (itemSelected || boundsSelected || positionSelected) &&
+ this._isUpdated(updateVersion)
+ ) {
+ // Allow definition of selected color on a per item and per
+ // layer level, with a fallback to #009dec
+ var layer,
+ color =
+ this.getSelectedColor(true) ||
+ ((layer = this.getLayer()) &&
+ layer.getSelectedColor(true)),
+ mx = matrix.appended(this.getGlobalMatrix(true)),
+ half = size / 2;
+ ctx.strokeStyle = ctx.fillStyle = color
+ ? color.toCanvasStyle(ctx)
+ : "#009dec";
+ if (itemSelected) this._drawSelected(ctx, mx, selectionItems);
+ if (positionSelected) {
+ // Convert position from the parent's coordinates system to the
+ // global one:
+ var pos = this.getPosition(true),
+ parent = this._parent,
+ point = parent ? parent.localToGlobal(pos) : pos,
+ x = point.x,
+ y = point.y;
+ ctx.beginPath();
+ ctx.arc(x, y, half, 0, Math.PI * 2, true);
ctx.stroke();
+ var deltas = [
+ [0, -1],
+ [1, 0],
+ [0, 1],
+ [-1, 0],
+ ],
+ start = half,
+ end = size + 1;
+ for (var i = 0; i < 4; i++) {
+ var delta = deltas[i],
+ dx = delta[0],
+ dy = delta[1];
+ ctx.moveTo(x + dx * start, y + dy * start);
+ ctx.lineTo(x + dx * end, y + dy * end);
+ ctx.stroke();
+ }
}
- }
- if (boundsSelected) {
- var coords = mx._transformCorners(this.getInternalBounds());
- // Now draw a rectangle that connects the transformed
- // bounds corners, and draw the corners.
- ctx.beginPath();
- for (var i = 0; i < 8; i++) {
- ctx[!i ? 'moveTo' : 'lineTo'](coords[i], coords[++i]);
- }
- ctx.closePath();
- ctx.stroke();
- for (var i = 0; i < 8; i++) {
- ctx.fillRect(coords[i] - half, coords[++i] - half,
- size, size);
+ if (boundsSelected) {
+ var coords = mx._transformCorners(this.getInternalBounds());
+ // Now draw a rectangle that connects the transformed
+ // bounds corners, and draw the corners.
+ ctx.beginPath();
+ for (var i = 0; i < 8; i++) {
+ ctx[!i ? "moveTo" : "lineTo"](coords[i], coords[++i]);
+ }
+ ctx.closePath();
+ ctx.stroke();
+ for (var i = 0; i < 8; i++) {
+ ctx.fillRect(
+ coords[i] - half,
+ coords[++i] - half,
+ size,
+ size
+ );
+ }
}
}
- }
- },
-
- _canComposite: function() {
- return false;
- }
-}, Base.each(['down', 'drag', 'up', 'move'], function(key) {
- this['removeOn' + Base.capitalize(key)] = function() {
- var hash = {};
- hash[key] = true;
- return this.removeOn(hash);
- };
-}, /** @lends Item# */{
- /**
- * {@grouptitle Remove On Event}
- *
- * Removes the item when the events specified in the passed options object
- * occur.
- *
- * @option options.move {Boolean) remove the item when the next {@link
- * Tool#onMouseMove} event is fired.
- *
- * @option options.drag {Boolena) remove the item when the next {@link
- * Tool#onMouseDrag} event is fired.
- *
- * @option options.down {Boolean) remove the item when the next {@link
- * Tool#onMouseDown} event is fired.
- *
- * @option options.up {Boolean) remove the item when the next {@link
- * Tool#onMouseUp} event is fired.
- *
- * @name Item#removeOn
- * @function
- * @param {Object} options
- *
- * @example {@paperscript height=200}
- * // Click and drag below:
- * function onMouseDrag(event) {
- * // Create a circle shaped path at the mouse position,
- * // with a radius of 10:
- * var path = new Path.Circle({
- * center: event.point,
- * radius: 10,
- * fillColor: 'black'
- * });
- *
- * // Remove the path on the next onMouseDrag or onMouseDown event:
- * path.removeOn({
- * drag: true,
- * down: true
- * });
- * }
- */
-
- /**
- * Removes the item when the next {@link Tool#onMouseMove} event is fired.
- *
- * @name Item#removeOnMove
- * @function
- *
- * @example {@paperscript height=200}
- * // Move your mouse below:
- * function onMouseMove(event) {
- * // Create a circle shaped path at the mouse position,
- * // with a radius of 10:
- * var path = new Path.Circle({
- * center: event.point,
- * radius: 10,
- * fillColor: 'black'
- * });
- *
- * // On the next move event, automatically remove the path:
- * path.removeOnMove();
- * }
- */
-
- /**
- * Removes the item when the next {@link Tool#onMouseDown} event is fired.
- *
- * @name Item#removeOnDown
- * @function
- *
- * @example {@paperscript height=200}
- * // Click a few times below:
- * function onMouseDown(event) {
- * // Create a circle shaped path at the mouse position,
- * // with a radius of 10:
- * var path = new Path.Circle({
- * center: event.point,
- * radius: 10,
- * fillColor: 'black'
- * });
- *
- * // Remove the path, next time the mouse is pressed:
- * path.removeOnDown();
- * }
- */
-
- /**
- * Removes the item when the next {@link Tool#onMouseDrag} event is fired.
- *
- * @name Item#removeOnDrag
- * @function
- *
- * @example {@paperscript height=200}
- * // Click and drag below:
- * function onMouseDrag(event) {
- * // Create a circle shaped path at the mouse position,
- * // with a radius of 10:
- * var path = new Path.Circle({
- * center: event.point,
- * radius: 10,
- * fillColor: 'black'
- * });
- *
- * // On the next drag event, automatically remove the path:
- * path.removeOnDrag();
- * }
- */
+ },
- /**
- * Removes the item when the next {@link Tool#onMouseUp} event is fired.
- *
- * @name Item#removeOnUp
- * @function
- *
- * @example {@paperscript height=200}
- * // Click a few times below:
- * function onMouseDown(event) {
- * // Create a circle shaped path at the mouse position,
- * // with a radius of 10:
- * var path = new Path.Circle({
- * center: event.point,
- * radius: 10,
- * fillColor: 'black'
- * });
- *
- * // Remove the path, when the mouse is released:
- * path.removeOnUp();
- * }
- */
- // TODO: implement Item#removeOnFrame
- removeOn: function(obj) {
- for (var name in obj) {
- if (obj[name]) {
- var key = 'mouse' + name,
- project = this._project,
- sets = project._removeSets = project._removeSets || {};
- sets[key] = sets[key] || {};
- sets[key][this._id] = this;
- }
+ _canComposite: function () {
+ return false;
+ },
+ },
+ Base.each(
+ ["down", "drag", "up", "move"],
+ function (key) {
+ this["removeOn" + Base.capitalize(key)] = function () {
+ var hash = {};
+ hash[key] = true;
+ return this.removeOn(hash);
+ };
+ },
+ /** @lends Item# */ {
+ /**
+ * {@grouptitle Remove On Event}
+ *
+ * Removes the item when the events specified in the passed options object
+ * occur.
+ *
+ * @option options.move {Boolean) remove the item when the next {@link
+ * Tool#onMouseMove} event is fired.
+ *
+ * @option options.drag {Boolena) remove the item when the next {@link
+ * Tool#onMouseDrag} event is fired.
+ *
+ * @option options.down {Boolean) remove the item when the next {@link
+ * Tool#onMouseDown} event is fired.
+ *
+ * @option options.up {Boolean) remove the item when the next {@link
+ * Tool#onMouseUp} event is fired.
+ *
+ * @name Item#removeOn
+ * @function
+ * @param {Object} options
+ *
+ * @example {@paperscript height=200}
+ * // Click and drag below:
+ * function onMouseDrag(event) {
+ * // Create a circle shaped path at the mouse position,
+ * // with a radius of 10:
+ * var path = new Path.Circle({
+ * center: event.point,
+ * radius: 10,
+ * fillColor: 'black'
+ * });
+ *
+ * // Remove the path on the next onMouseDrag or onMouseDown event:
+ * path.removeOn({
+ * drag: true,
+ * down: true
+ * });
+ * }
+ */
+
+ /**
+ * Removes the item when the next {@link Tool#onMouseMove} event is fired.
+ *
+ * @name Item#removeOnMove
+ * @function
+ *
+ * @example {@paperscript height=200}
+ * // Move your mouse below:
+ * function onMouseMove(event) {
+ * // Create a circle shaped path at the mouse position,
+ * // with a radius of 10:
+ * var path = new Path.Circle({
+ * center: event.point,
+ * radius: 10,
+ * fillColor: 'black'
+ * });
+ *
+ * // On the next move event, automatically remove the path:
+ * path.removeOnMove();
+ * }
+ */
+
+ /**
+ * Removes the item when the next {@link Tool#onMouseDown} event is fired.
+ *
+ * @name Item#removeOnDown
+ * @function
+ *
+ * @example {@paperscript height=200}
+ * // Click a few times below:
+ * function onMouseDown(event) {
+ * // Create a circle shaped path at the mouse position,
+ * // with a radius of 10:
+ * var path = new Path.Circle({
+ * center: event.point,
+ * radius: 10,
+ * fillColor: 'black'
+ * });
+ *
+ * // Remove the path, next time the mouse is pressed:
+ * path.removeOnDown();
+ * }
+ */
+
+ /**
+ * Removes the item when the next {@link Tool#onMouseDrag} event is fired.
+ *
+ * @name Item#removeOnDrag
+ * @function
+ *
+ * @example {@paperscript height=200}
+ * // Click and drag below:
+ * function onMouseDrag(event) {
+ * // Create a circle shaped path at the mouse position,
+ * // with a radius of 10:
+ * var path = new Path.Circle({
+ * center: event.point,
+ * radius: 10,
+ * fillColor: 'black'
+ * });
+ *
+ * // On the next drag event, automatically remove the path:
+ * path.removeOnDrag();
+ * }
+ */
+
+ /**
+ * Removes the item when the next {@link Tool#onMouseUp} event is fired.
+ *
+ * @name Item#removeOnUp
+ * @function
+ *
+ * @example {@paperscript height=200}
+ * // Click a few times below:
+ * function onMouseDown(event) {
+ * // Create a circle shaped path at the mouse position,
+ * // with a radius of 10:
+ * var path = new Path.Circle({
+ * center: event.point,
+ * radius: 10,
+ * fillColor: 'black'
+ * });
+ *
+ * // Remove the path, when the mouse is released:
+ * path.removeOnUp();
+ * }
+ */
+ // TODO: implement Item#removeOnFrame
+ removeOn: function (obj) {
+ for (var name in obj) {
+ if (obj[name]) {
+ var key = "mouse" + name,
+ project = this._project,
+ sets = (project._removeSets =
+ project._removeSets || {});
+ sets[key] = sets[key] || {};
+ sets[key][this._id] = this;
+ }
+ }
+ return this;
+ },
}
- return this;
- }
-}), /** @lends Item# */{
- /**
- * {@grouptitle Tweening Functions}
- *
- * Tween item between two states.
- *
- * @name Item#tween
- *
- * @option options.duration {Number} the duration of the tweening
- * @option [options.easing='linear'] {Function|String} an easing function or the type
- * of the easing: {@values 'linear' 'easeInQuad' 'easeOutQuad'
- * 'easeInOutQuad' 'easeInCubic' 'easeOutCubic' 'easeInOutCubic'
- * 'easeInQuart' 'easeOutQuart' 'easeInOutQuart' 'easeInQuint'
- * 'easeOutQuint' 'easeInOutQuint'}
- * @option [options.start=true] {Boolean} whether to start tweening automatically
- *
- * @function
- * @param {Object} from the state at the start of the tweening
- * @param {Object} to the state at the end of the tweening
- * @param {Object|Number} options the options or the duration
- * @return {Tween}
- *
- * @example {@paperscript height=100}
- * // Tween fillColor:
- * var path = new Path.Circle({
- * radius: view.bounds.height * 0.4,
- * center: view.center
- * });
- * path.tween(
- * { fillColor: 'blue' },
- * { fillColor: 'red' },
- * 3000
- * );
- * @example {@paperscript height=100}
- * // Tween rotation:
- * var path = new Shape.Rectangle({
- * fillColor: 'red',
- * center: [50, view.center.y],
- * size: [60, 60]
- * });
- * path.tween({
- * rotation: 180,
- * 'position.x': view.bounds.width - 50,
- * 'fillColor.hue': '+= 90'
- * }, {
- * easing: 'easeInOutCubic',
- * duration: 2000
- * });
- */
- /**
- * Tween item to a state.
- *
- * @name Item#tween
- *
- * @function
- * @param {Object} to the state at the end of the tweening
- * @param {Object|Number} options the options or the duration
- * @return {Tween}
- *
- * @example {@paperscript height=200}
- * // Tween a nested property with relative values
- * var path = new Path.Rectangle({
- * size: [100, 100],
- * position: view.center,
- * fillColor: 'red',
- * });
- *
- * var delta = { x: path.bounds.width / 2, y: 0 };
- *
- * path.tween({
- * 'segments[1].point': ['+=', delta],
- * 'segments[2].point.x': '-= 50'
- * }, 3000);
- *
- * @see Item#tween(from, to, options)
- */
- /**
- * Tween item.
- *
- * @name Item#tween
- *
- * @function
- * @param {Object|Number} options the options or the duration
- * @return {Tween}
- *
- * @see Item#tween(from, to, options)
- *
- * @example {@paperscript height=100}
- * // Start an empty tween and just use the update callback:
- * var path = new Path.Circle({
- * fillColor: 'blue',
- * radius: view.bounds.height * 0.4,
- * center: view.center,
- * });
- * var pathFrom = path.clone({ insert: false })
- * var pathTo = new Path.Rectangle({
- * position: view.center,
- * rectangle: path.bounds,
- * insert: false
- * });
- * path.tween(2000).onUpdate = function(event) {
- * path.interpolate(pathFrom, pathTo, event.factor)
- * };
- */
- tween: function(from, to, options) {
- if (!options) {
- // If there are only two or one arguments, shift arguments to the
- // left by one (omit `from`):
- options = to;
- to = from;
- from = null;
+ ),
+ /** @lends Item# */ {
+ /**
+ * {@grouptitle Tweening Functions}
+ *
+ * Tween item between two states.
+ *
+ * @name Item#tween
+ *
+ * @option options.duration {Number} the duration of the tweening
+ * @option [options.easing='linear'] {Function|String} an easing function or the type
+ * of the easing: {@values 'linear' 'easeInQuad' 'easeOutQuad'
+ * 'easeInOutQuad' 'easeInCubic' 'easeOutCubic' 'easeInOutCubic'
+ * 'easeInQuart' 'easeOutQuart' 'easeInOutQuart' 'easeInQuint'
+ * 'easeOutQuint' 'easeInOutQuint'}
+ * @option [options.start=true] {Boolean} whether to start tweening automatically
+ *
+ * @function
+ * @param {Object} from the state at the start of the tweening
+ * @param {Object} to the state at the end of the tweening
+ * @param {Object|Number} options the options or the duration
+ * @return {Tween}
+ *
+ * @example {@paperscript height=100}
+ * // Tween fillColor:
+ * var path = new Path.Circle({
+ * radius: view.bounds.height * 0.4,
+ * center: view.center
+ * });
+ * path.tween(
+ * { fillColor: 'blue' },
+ * { fillColor: 'red' },
+ * 3000
+ * );
+ * @example {@paperscript height=100}
+ * // Tween rotation:
+ * var path = new Shape.Rectangle({
+ * fillColor: 'red',
+ * center: [50, view.center.y],
+ * size: [60, 60]
+ * });
+ * path.tween({
+ * rotation: 180,
+ * 'position.x': view.bounds.width - 50,
+ * 'fillColor.hue': '+= 90'
+ * }, {
+ * easing: 'easeInOutCubic',
+ * duration: 2000
+ * });
+ */
+ /**
+ * Tween item to a state.
+ *
+ * @name Item#tween
+ *
+ * @function
+ * @param {Object} to the state at the end of the tweening
+ * @param {Object|Number} options the options or the duration
+ * @return {Tween}
+ *
+ * @example {@paperscript height=200}
+ * // Tween a nested property with relative values
+ * var path = new Path.Rectangle({
+ * size: [100, 100],
+ * position: view.center,
+ * fillColor: 'red',
+ * });
+ *
+ * var delta = { x: path.bounds.width / 2, y: 0 };
+ *
+ * path.tween({
+ * 'segments[1].point': ['+=', delta],
+ * 'segments[2].point.x': '-= 50'
+ * }, 3000);
+ *
+ * @see Item#tween(from, to, options)
+ */
+ /**
+ * Tween item.
+ *
+ * @name Item#tween
+ *
+ * @function
+ * @param {Object|Number} options the options or the duration
+ * @return {Tween}
+ *
+ * @see Item#tween(from, to, options)
+ *
+ * @example {@paperscript height=100}
+ * // Start an empty tween and just use the update callback:
+ * var path = new Path.Circle({
+ * fillColor: 'blue',
+ * radius: view.bounds.height * 0.4,
+ * center: view.center,
+ * });
+ * var pathFrom = path.clone({ insert: false })
+ * var pathTo = new Path.Rectangle({
+ * position: view.center,
+ * rectangle: path.bounds,
+ * insert: false
+ * });
+ * path.tween(2000).onUpdate = function(event) {
+ * path.interpolate(pathFrom, pathTo, event.factor)
+ * };
+ */
+ tween: function (from, to, options) {
if (!options) {
+ // If there are only two or one arguments, shift arguments to the
+ // left by one (omit `from`):
options = to;
- to = null;
+ to = from;
+ from = null;
+ if (!options) {
+ options = to;
+ to = null;
+ }
}
- }
- var easing = options && options.easing,
- start = options && options.start,
- duration = options != null && (
- typeof options === 'number' ? options : options.duration
- ),
- tween = new Tween(this, from, to, duration, easing, start);
- function onFrame(event) {
- tween._handleFrame(event.time * 1000);
- if (!tween.running) {
- this.off('frame', onFrame);
+ var easing = options && options.easing,
+ start = options && options.start,
+ duration =
+ options != null &&
+ (typeof options === "number" ? options : options.duration),
+ tween = new Tween(this, from, to, duration, easing, start);
+ function onFrame(event) {
+ tween._handleFrame(event.time * 1000);
+ if (!tween.running) {
+ this.off("frame", onFrame);
+ }
}
- }
- if (duration) {
- this.on('frame', onFrame);
- }
- return tween;
- },
+ if (duration) {
+ this.on("frame", onFrame);
+ }
+ return tween;
+ },
- /**
- *
- * Tween item to a state.
- *
- * @function
- * @param {Object} to the state at the end of the tweening
- * @param {Object|Number} options the options or the duration
- * @return {Tween}
- *
- * @see Item#tween(to, options)
- */
- tweenTo: function(to, options) {
- return this.tween(null, to, options);
- },
+ /**
+ *
+ * Tween item to a state.
+ *
+ * @function
+ * @param {Object} to the state at the end of the tweening
+ * @param {Object|Number} options the options or the duration
+ * @return {Tween}
+ *
+ * @see Item#tween(to, options)
+ */
+ tweenTo: function (to, options) {
+ return this.tween(null, to, options);
+ },
- /**
- *
- * Tween item from a state to its state before the tweening.
- *
- * @function
- * @param {Object} from the state at the start of the tweening
- * @param {Object|Number} options the options or the duration
- * @return {Tween}
- *
- * @see Item#tween(from, to, options)
- *
- * @example {@paperscript height=100}
- * // Tween fillColor from red to the path's initial fillColor:
- * var path = new Path.Circle({
- * fillColor: 'blue',
- * radius: view.bounds.height * 0.4,
- * center: view.center
- * });
- * path.tweenFrom({ fillColor: 'red' }, { duration: 1000 });
- */
- tweenFrom: function(from, options) {
- return this.tween(from, null, options);
+ /**
+ *
+ * Tween item from a state to its state before the tweening.
+ *
+ * @function
+ * @param {Object} from the state at the start of the tweening
+ * @param {Object|Number} options the options or the duration
+ * @return {Tween}
+ *
+ * @see Item#tween(from, to, options)
+ *
+ * @example {@paperscript height=100}
+ * // Tween fillColor from red to the path's initial fillColor:
+ * var path = new Path.Circle({
+ * fillColor: 'blue',
+ * radius: view.bounds.height * 0.4,
+ * center: view.center
+ * });
+ * path.tweenFrom({ fillColor: 'red' }, { duration: 1000 });
+ */
+ tweenFrom: function (from, options) {
+ return this.tween(from, null, options);
+ },
}
-});
+);
diff --git a/src/item/Project.js b/src/item/Project.js
index 232a900f9d..85036015a8 100644
--- a/src/item/Project.js
+++ b/src/item/Project.js
@@ -30,869 +30,874 @@
* An array of all open projects is accessible through the
* {@link PaperScope#projects} variable.
*/
-var Project = PaperScopeItem.extend(/** @lends Project# */{
- _class: 'Project',
- _list: 'projects',
- _reference: 'project',
- _compactSerialize: true, // Never include the class name for Project
-
- // TODO: Add arguments to define pages
- /**
- * Creates a Paper.js project containing one empty {@link Layer}, referenced
- * by {@link Project#activeLayer}.
- *
- * Note that when working with PaperScript, a project is automatically
- * created for us and the {@link PaperScope#project} variable points to it.
- *
- * @param {HTMLCanvasElement|String|Size} element the HTML canvas element
- * that should be used as the element for the view, or an ID string by which
- * to find the element, or the size of the canvas to be created for usage in
- * a web worker.
- */
- initialize: function Project(element) {
- // Activate straight away by passing true to PaperScopeItem constructor,
- // so paper.project is set, as required by Layer and DoumentView
- // constructors.
- PaperScopeItem.call(this, true);
- this._children = [];
- this._namedChildren = {};
- this._activeLayer = null;
- this._currentStyle = new Style(null, null, this);
- // If no view is provided, we create a 1x1 px canvas view just so we
- // have something to do size calculations with.
- // (e.g. PointText#_getBounds)
- this._view = View.create(this,
- element || CanvasProvider.getCanvas(1, 1));
- this._selectionItems = {};
- this._selectionCount = 0;
- // See Item#draw() for an explanation of _updateVersion
- this._updateVersion = 0;
- // Change tracking, not in use for now. Activate once required:
- // this._changes = [];
- // this._changesById = {};
- },
-
- _serialize: function(options, dictionary) {
- // Just serialize layers to an array for now, they will be unserialized
- // into the active project automatically. We might want to add proper
- // project serialization later, but deserialization of a layers array
- // will always work.
- return Base.serialize(this._children, options, true, dictionary);
- },
-
- /**
- * Private notifier that is called whenever a change occurs in the project.
- *
- * @param {ChangeFlag} flags describes what exactly has changed
- * @param {Item} item the item that has caused the change
- */
- _changed: function(flags, item) {
- if (flags & /*#=*/ChangeFlag.APPEARANCE) {
- var view = this._view;
- if (view) {
- // Never draw changes right away. Simply mark view as "dirty"
- // and request an update through view.requestUpdate().
- view._needsUpdate = true;
- if (!view._requested && view._autoUpdate)
- view.requestUpdate();
+var Project = PaperScopeItem.extend(
+ /** @lends Project# */ {
+ _class: "Project",
+ _list: "projects",
+ _reference: "project",
+ _compactSerialize: true, // Never include the class name for Project
+
+ // TODO: Add arguments to define pages
+ /**
+ * Creates a Paper.js project containing one empty {@link Layer}, referenced
+ * by {@link Project#activeLayer}.
+ *
+ * Note that when working with PaperScript, a project is automatically
+ * created for us and the {@link PaperScope#project} variable points to it.
+ *
+ * @param {HTMLCanvasElement|String|Size} element the HTML canvas element
+ * that should be used as the element for the view, or an ID string by which
+ * to find the element, or the size of the canvas to be created for usage in
+ * a web worker.
+ */
+ initialize: function Project(element) {
+ // Activate straight away by passing true to PaperScopeItem constructor,
+ // so paper.project is set, as required by Layer and DoumentView
+ // constructors.
+ PaperScopeItem.call(this, true);
+ this._children = [];
+ this._namedChildren = {};
+ this._activeLayer = null;
+ this._currentStyle = new Style(null, null, this);
+ // If no view is provided, we create a 1x1 px canvas view just so we
+ // have something to do size calculations with.
+ // (e.g. PointText#_getBounds)
+ this._view = View.create(
+ this,
+ element || CanvasProvider.getCanvas(1, 1)
+ );
+ this._selectionItems = {};
+ this._selectionCount = 0;
+ // See Item#draw() for an explanation of _updateVersion
+ this._updateVersion = 0;
+ // Change tracking, not in use for now. Activate once required:
+ // this._changes = [];
+ // this._changesById = {};
+ },
+
+ _serialize: function (options, dictionary) {
+ // Just serialize layers to an array for now, they will be unserialized
+ // into the active project automatically. We might want to add proper
+ // project serialization later, but deserialization of a layers array
+ // will always work.
+ return Base.serialize(this._children, options, true, dictionary);
+ },
+
+ /**
+ * Private notifier that is called whenever a change occurs in the project.
+ *
+ * @param {ChangeFlag} flags describes what exactly has changed
+ * @param {Item} item the item that has caused the change
+ */
+ _changed: function (flags, item) {
+ if (flags & /*#=*/ ChangeFlag.APPEARANCE) {
+ var view = this._view;
+ if (view) {
+ // Never draw changes right away. Simply mark view as "dirty"
+ // and request an update through view.requestUpdate().
+ view._needsUpdate = true;
+ if (!view._requested && view._autoUpdate)
+ view.requestUpdate();
+ }
}
- }
- // Have project keep track of changed items so they can be iterated.
- // This can be used for example to update the SVG tree. Needs to be
- // activated in Project
- var changes = this._changes;
- if (changes && item) {
- var changesById = this._changesById,
- id = item._id,
- entry = changesById[id];
- if (entry) {
- entry.flags |= flags;
- } else {
- changes.push(changesById[id] = { item: item, flags: flags });
+ // Have project keep track of changed items so they can be iterated.
+ // This can be used for example to update the SVG tree. Needs to be
+ // activated in Project
+ var changes = this._changes;
+ if (changes && item) {
+ var changesById = this._changesById,
+ id = item._id,
+ entry = changesById[id];
+ if (entry) {
+ entry.flags |= flags;
+ } else {
+ changes.push(
+ (changesById[id] = { item: item, flags: flags })
+ );
+ }
}
- }
- },
-
- /**
- * Activates this project, so all newly created items will be placed
- * in it.
- *
- * @name Project#activate
- * @function
- */
-
- /**
- * Clears the project by removing all {@link Project#layers}.
- */
- clear: function() {
- var children = this._children;
- for (var i = children.length - 1; i >= 0; i--)
- children[i].remove();
- },
-
- /**
- * Checks whether the project has any content or not.
- *
- * @return {Boolean}
- */
- isEmpty: function() {
- return !this._children.length;
- },
-
- /**
- * Removes this project from the {@link PaperScope#projects} list, and also
- * removes its view, if one was defined.
- */
- remove: function remove() {
- if (!remove.base.call(this))
- return false;
- if (this._view)
- this._view.remove();
- return true;
- },
-
- /**
- * The reference to the project's view.
- *
- * @bean
- * @type View
- */
- getView: function() {
- return this._view;
- },
-
- /**
- * The currently active path style. All selected items and newly
- * created items will be styled with this style.
- *
- * @bean
- * @type Style
- *
- * @example {@paperscript}
- * project.currentStyle = {
- * fillColor: 'red',
- * strokeColor: 'black',
- * strokeWidth: 5
- * }
- *
- * // The following paths will take over all style properties of
- * // the current style:
- * var path = new Path.Circle(new Point(75, 50), 30);
- * var path2 = new Path.Circle(new Point(175, 50), 20);
- *
- * @example {@paperscript}
- * project.currentStyle.fillColor = 'red';
- *
- * // The following path will take over the fill color we just set:
- * var path = new Path.Circle(new Point(75, 50), 30);
- * var path2 = new Path.Circle(new Point(175, 50), 20);
- */
- getCurrentStyle: function() {
- return this._currentStyle;
- },
-
- setCurrentStyle: function(style) {
- // TODO: Style selected items with the style:
- this._currentStyle.set(style);
- },
-
- /**
- * The index of the project in the {@link PaperScope#projects} list.
- *
- * @bean
- * @type Number
- */
- getIndex: function() {
- return this._index;
- },
-
- /**
- * Gives access to the project's configurable options.
- *
- * @bean
- * @type Object
- * @deprecated use {@link PaperScope#settings} instead.
- */
- getOptions: function() {
- return this._scope.settings;
- },
-
- /**
- * {@grouptitle Project Content}
- *
- * The layers contained within the project.
- *
- * @bean
- * @type Layer[]
- */
- getLayers: function() {
- return this._children;
- },
-
- // TODO: Define #setLayers()?
-
- /**
- * The layer which is currently active. New items will be created on this
- * layer by default.
- *
- * @bean
- * @type Layer
- */
- getActiveLayer: function() {
- return this._activeLayer || new Layer({ project: this, insert: true });
- },
-
- /**
- * The symbol definitions shared by all symbol items contained place ind
- * project.
- *
- * @bean
- * @type SymbolDefinition[]
- */
- getSymbolDefinitions: function() {
- var definitions = [],
- ids = {};
- this.getItems({
- class: SymbolItem,
- match: function(item) {
- var definition = item._definition,
- id = definition._id;
- if (!ids[id]) {
- ids[id] = true;
- definitions.push(definition);
+ },
+
+ /**
+ * Activates this project, so all newly created items will be placed
+ * in it.
+ *
+ * @name Project#activate
+ * @function
+ */
+
+ /**
+ * Clears the project by removing all {@link Project#layers}.
+ */
+ clear: function () {
+ var children = this._children;
+ for (var i = children.length - 1; i >= 0; i--) children[i].remove();
+ },
+
+ /**
+ * Checks whether the project has any content or not.
+ *
+ * @return {Boolean}
+ */
+ isEmpty: function () {
+ return !this._children.length;
+ },
+
+ /**
+ * Removes this project from the {@link PaperScope#projects} list, and also
+ * removes its view, if one was defined.
+ */
+ remove: function remove() {
+ if (!remove.base.call(this)) return false;
+ if (this._view) this._view.remove();
+ return true;
+ },
+
+ /**
+ * The reference to the project's view.
+ *
+ * @bean
+ * @type View
+ */
+ getView: function () {
+ return this._view;
+ },
+
+ /**
+ * The currently active path style. All selected items and newly
+ * created items will be styled with this style.
+ *
+ * @bean
+ * @type Style
+ *
+ * @example {@paperscript}
+ * project.currentStyle = {
+ * fillColor: 'red',
+ * strokeColor: 'black',
+ * strokeWidth: 5
+ * }
+ *
+ * // The following paths will take over all style properties of
+ * // the current style:
+ * var path = new Path.Circle(new Point(75, 50), 30);
+ * var path2 = new Path.Circle(new Point(175, 50), 20);
+ *
+ * @example {@paperscript}
+ * project.currentStyle.fillColor = 'red';
+ *
+ * // The following path will take over the fill color we just set:
+ * var path = new Path.Circle(new Point(75, 50), 30);
+ * var path2 = new Path.Circle(new Point(175, 50), 20);
+ */
+ getCurrentStyle: function () {
+ return this._currentStyle;
+ },
+
+ setCurrentStyle: function (style) {
+ // TODO: Style selected items with the style:
+ this._currentStyle.set(style);
+ },
+
+ /**
+ * The index of the project in the {@link PaperScope#projects} list.
+ *
+ * @bean
+ * @type Number
+ */
+ getIndex: function () {
+ return this._index;
+ },
+
+ /**
+ * Gives access to the project's configurable options.
+ *
+ * @bean
+ * @type Object
+ * @deprecated use {@link PaperScope#settings} instead.
+ */
+ getOptions: function () {
+ return this._scope.settings;
+ },
+
+ /**
+ * {@grouptitle Project Content}
+ *
+ * The layers contained within the project.
+ *
+ * @bean
+ * @type Layer[]
+ */
+ getLayers: function () {
+ return this._children;
+ },
+
+ // TODO: Define #setLayers()?
+
+ /**
+ * The layer which is currently active. New items will be created on this
+ * layer by default.
+ *
+ * @bean
+ * @type Layer
+ */
+ getActiveLayer: function () {
+ return (
+ this._activeLayer || new Layer({ project: this, insert: true })
+ );
+ },
+
+ /**
+ * The symbol definitions shared by all symbol items contained place ind
+ * project.
+ *
+ * @bean
+ * @type SymbolDefinition[]
+ */
+ getSymbolDefinitions: function () {
+ var definitions = [],
+ ids = {};
+ this.getItems({
+ class: SymbolItem,
+ match: function (item) {
+ var definition = item._definition,
+ id = definition._id;
+ if (!ids[id]) {
+ ids[id] = true;
+ definitions.push(definition);
+ }
+ return false; // No need to collect them.
+ },
+ });
+ return definitions;
+ },
+
+ /**
+ * @bean
+ * @deprecated use {@link #symbolDefinitions} instead.
+ */
+ getSymbols: "getSymbolDefinitions",
+
+ /**
+ * The selected items contained within the project.
+ *
+ * @bean
+ * @type Item[]
+ */
+ getSelectedItems: function () {
+ // TODO: Return groups if their children are all selected, and filter
+ // out their children from the list.
+ // TODO: The order of these items should be that of their drawing order.
+ var selectionItems = this._selectionItems,
+ items = [];
+ for (var id in selectionItems) {
+ var item = selectionItems[id],
+ selection = item._selection;
+ if (
+ selection & /*#=*/ ItemSelection.ITEM &&
+ item.isInserted()
+ ) {
+ items.push(item);
+ } else if (!selection) {
+ this._updateSelection(item);
}
- return false; // No need to collect them.
}
- });
- return definitions;
- },
-
- /**
- * @bean
- * @deprecated use {@link #symbolDefinitions} instead.
- */
- getSymbols: 'getSymbolDefinitions',
-
- /**
- * The selected items contained within the project.
- *
- * @bean
- * @type Item[]
- */
- getSelectedItems: function() {
- // TODO: Return groups if their children are all selected, and filter
- // out their children from the list.
- // TODO: The order of these items should be that of their drawing order.
- var selectionItems = this._selectionItems,
- items = [];
- for (var id in selectionItems) {
- var item = selectionItems[id],
- selection = item._selection;
- if ((selection & /*#=*/ItemSelection.ITEM) && item.isInserted()) {
- items.push(item);
- } else if (!selection) {
- this._updateSelection(item);
+ return items;
+ },
+ // TODO: Implement setSelectedItems?
+
+ _updateSelection: function (item) {
+ var id = item._id,
+ selectionItems = this._selectionItems;
+ if (item._selection) {
+ if (selectionItems[id] !== item) {
+ this._selectionCount++;
+ selectionItems[id] = item;
+ }
+ } else if (selectionItems[id] === item) {
+ this._selectionCount--;
+ delete selectionItems[id];
}
- }
- return items;
- },
- // TODO: Implement setSelectedItems?
-
- _updateSelection: function(item) {
- var id = item._id,
- selectionItems = this._selectionItems;
- if (item._selection) {
- if (selectionItems[id] !== item) {
- this._selectionCount++;
- selectionItems[id] = item;
+ },
+
+ /**
+ * Selects all items in the project.
+ */
+ selectAll: function () {
+ var children = this._children;
+ for (var i = 0, l = children.length; i < l; i++)
+ children[i].setFullySelected(true);
+ },
+
+ /**
+ * Deselects all selected items in the project.
+ */
+ deselectAll: function () {
+ var selectionItems = this._selectionItems;
+ for (var i in selectionItems)
+ selectionItems[i].setFullySelected(false);
+ },
+
+ /**
+ * {@grouptitle Hierarchy Operations}
+ *
+ * Adds the specified layer at the end of the this project's {@link #layers}
+ * list.
+ *
+ * @param {Layer} layer the layer to be added to the project
+ * @return {Layer} the added layer, or `null` if adding was not possible
+ */
+ addLayer: function (layer) {
+ return this.insertLayer(undefined, layer);
+ },
+
+ /**
+ * Inserts the specified layer at the specified index in this project's
+ * {@link #layers} list.
+ *
+ * @param {Number} index the index at which to insert the layer
+ * @param {Layer} layer the layer to be inserted in the project
+ * @return {Layer} the added layer, or `null` if adding was not possible
+ */
+ insertLayer: function (index, layer) {
+ if (layer instanceof Layer) {
+ // Notify parent of change. Don't notify item itself yet,
+ // as we're doing so when adding it to the new owner below.
+ layer._remove(false, true);
+ Base.splice(this._children, [layer], index, 0);
+ layer._setProject(this, true);
+ // Set the name again to make sure all name lookup structures
+ // are kept in sync.
+ var name = layer._name;
+ if (name) layer.setName(name);
+ // See Item#_remove() for an explanation of this:
+ if (this._changes) layer._changed(/*#=*/ Change.INSERTION);
+ // TODO: this._changed(/*#=*/Change.LAYERS);
+ // Also activate this layer if there was none before
+ if (!this._activeLayer) this._activeLayer = layer;
+ } else {
+ layer = null;
}
- } else if (selectionItems[id] === item) {
- this._selectionCount--;
- delete selectionItems[id];
- }
- },
-
- /**
- * Selects all items in the project.
- */
- selectAll: function() {
- var children = this._children;
- for (var i = 0, l = children.length; i < l; i++)
- children[i].setFullySelected(true);
- },
-
- /**
- * Deselects all selected items in the project.
- */
- deselectAll: function() {
- var selectionItems = this._selectionItems;
- for (var i in selectionItems)
- selectionItems[i].setFullySelected(false);
- },
-
- /**
- * {@grouptitle Hierarchy Operations}
- *
- * Adds the specified layer at the end of the this project's {@link #layers}
- * list.
- *
- * @param {Layer} layer the layer to be added to the project
- * @return {Layer} the added layer, or `null` if adding was not possible
- */
- addLayer: function(layer) {
- return this.insertLayer(undefined, layer);
- },
-
- /**
- * Inserts the specified layer at the specified index in this project's
- * {@link #layers} list.
- *
- * @param {Number} index the index at which to insert the layer
- * @param {Layer} layer the layer to be inserted in the project
- * @return {Layer} the added layer, or `null` if adding was not possible
- */
- insertLayer: function(index, layer) {
- if (layer instanceof Layer) {
- // Notify parent of change. Don't notify item itself yet,
- // as we're doing so when adding it to the new owner below.
- layer._remove(false, true);
- Base.splice(this._children, [layer], index, 0);
- layer._setProject(this, true);
- // Set the name again to make sure all name lookup structures
- // are kept in sync.
- var name = layer._name;
- if (name)
- layer.setName(name);
- // See Item#_remove() for an explanation of this:
- if (this._changes)
- layer._changed(/*#=*/Change.INSERTION);
- // TODO: this._changed(/*#=*/Change.LAYERS);
- // Also activate this layer if there was none before
- if (!this._activeLayer)
- this._activeLayer = layer;
- } else {
- layer = null;
- }
- return layer;
- },
-
- // Project#_insertItem() and Item#_insertItem() are helper functions called
- // in Item#copyTo(), and through _getOwner() in the various Item#insert*()
- // methods. They are called the same to facilitate so duck-typing.
- _insertItem: function(index, item, _created) {
- item = this.insertLayer(index, item)
+ return layer;
+ },
+
+ // Project#_insertItem() and Item#_insertItem() are helper functions called
+ // in Item#copyTo(), and through _getOwner() in the various Item#insert*()
+ // methods. They are called the same to facilitate so duck-typing.
+ _insertItem: function (index, item, _created) {
+ item =
+ this.insertLayer(index, item) ||
// Anything else than layers needs to be added to a layer first.
// If none exists yet, create one now, then add the item to it.
- || (this._activeLayer || this._insertItem(undefined,
- new Layer(Item.NO_INSERT), true)) // _created = true
- .insertChild(index, item);
- // If a layer was newly created, also activate it.
- if (_created && item.activate)
- item.activate();
- return item;
- },
-
- /**
- * {@grouptitle Hit-testing, Fetching and Matching Items}
- *
- * Performs a hit-test on the items contained within the project at the
- * location of the specified point.
- *
- * The options object allows you to control the specifics of the hit-test
- * and may contain a combination of the following values:
- *
- * @name Project#hitTest
- * @function
- *
- * @option [options.tolerance={@link PaperScope#settings}.hitTolerance]
- * {Number} the tolerance of the hit-test
- * @option options.class {Function} only hit-test against a specific item
- * class, or any of its sub-classes, by providing the constructor
- * function against which an `instanceof` check is performed:
- * {@values Group, Layer, Path, CompoundPath, Shape, Raster,
- * SymbolItem, PointText, ...}
- * @option options.match {Function} a match function to be called for each
- * found hit result: Return `true` to return the result, `false` to keep
- * searching
- * @option [options.fill=true] {Boolean} hit-test the fill of items
- * @option [options.stroke=true] {Boolean} hit-test the stroke of path
- * items, taking into account the setting of stroke color and width
- * @option [options.segments=true] {Boolean} hit-test for {@link
- * Segment#point} of {@link Path} items
- * @option options.curves {Boolean} hit-test the curves of path items,
- * without taking the stroke color or width into account
- * @option options.handles {Boolean} hit-test for the handles ({@link
- * Segment#handleIn} / {@link Segment#handleOut}) of path segments.
- * @option options.ends {Boolean} only hit-test for the first or last
- * segment points of open path items
- * @option options.position {Boolean} hit-test the {@link Item#position} of
- * of items, which depends on the setting of {@link Item#pivot}
- * @option options.center {Boolean} hit-test the {@link Rectangle#center} of
- * the bounding rectangle of items ({@link Item#bounds})
- * @option options.bounds {Boolean} hit-test the corners and side-centers of
- * the bounding rectangle of items ({@link Item#bounds})
- * @option options.guides {Boolean} hit-test items that have {@link
- * Item#guide} set to `true`
- * @option options.selected {Boolean} only hit selected items
- *
- * @param {Point} point the point where the hit-test should be performed
- * @param {Object} [options={ fill: true, stroke: true, segments: true,
- * tolerance: settings.hitTolerance }]
- * @return {HitResult} a hit result object that contains more information
- * about what exactly was hit or `null` if nothing was hit
- */
- // NOTE: Implementation is in Item#hitTest()
-
- /**
- * Performs a hit-test on the item and its children (if it is a {@link
- * Group} or {@link Layer}) at the location of the specified point,
- * returning all found hits.
- *
- * The options object allows you to control the specifics of the hit-
- * test. See {@link #hitTest(point[, options])} for a list of all options.
- *
- * @name Project#hitTestAll
- * @function
- * @param {Point} point the point where the hit-test should be performed
- * @param {Object} [options={ fill: true, stroke: true, segments: true,
- * tolerance: settings.hitTolerance }]
- * @return {HitResult[]} hit result objects for all hits, describing what
- * exactly was hit or `null` if nothing was hit
- * @see #hitTest(point[, options]);
- */
- // NOTE: Implementation is in Item#hitTestAll()
-
- /**
- *
- * Fetch items contained within the project whose properties match the
- * criteria in the specified object.
- *
- * Extended matching of properties is possible by providing a comparator
- * function or regular expression. Matching points, colors only work as a
- * comparison of the full object, not partial matching (e.g. only providing
- * the x- coordinate to match all points with that x-value). Partial
- * matching does work for {@link Item#data}.
- *
- * Matching items against a rectangular area is also possible, by setting
- * either `options.inside` or `options.overlapping` to a rectangle
- * describing the area in which the items either have to be fully or partly
- * contained.
- *
- * @option [options.recursive=true] {Boolean} whether to loop recursively
- * through all children, or stop at the current level
- * @option options.match {Function} a match function to be called for each
- * item, allowing the definition of more flexible item checks that are
- * not bound to properties. If no other match properties are defined,
- * this function can also be passed instead of the `match` object
- * @option options.class {Function} the constructor function of the item
- * type to match against
- * @option options.inside {Rectangle} the rectangle in which the items need
- * to be fully contained
- * @option options.overlapping {Rectangle} the rectangle with which the
- * items need to at least partly overlap
- *
- * @see Item#matches(options)
- * @see Item#getItems(options)
- * @param {Object|Function} options the criteria to match against
- * @return {Item[]} the list of matching items contained in the project
- *
- * @example {@paperscript} // Fetch all selected path items:
- * var path1 = new Path.Circle({
- * center: [50, 50],
- * radius: 25,
- * fillColor: 'black'
- * });
- *
- * var path2 = new Path.Circle({
- * center: [150, 50],
- * radius: 25,
- * fillColor: 'black'
- * });
- *
- * // Select path2:
- * path2.selected = true;
- *
- * // Fetch all selected path items:
- * var items = project.getItems({
- * selected: true,
- * class: Path
- * });
- *
- * // Change the fill color of the selected path to red:
- * items[0].fillColor = 'red';
- *
- * @example {@paperscript} // Fetch all items with a specific fill color:
- * var path1 = new Path.Circle({
- * center: [50, 50],
- * radius: 25,
- * fillColor: 'black'
- * });
- *
- * var path2 = new Path.Circle({
- * center: [150, 50],
- * radius: 25,
- * fillColor: 'purple'
- * });
- *
- * // Fetch all items with a purple fill color:
- * var items = project.getItems({
- * fillColor: 'purple'
- * });
- *
- * // Select the fetched item:
- * items[0].selected = true;
- *
- * @example {@paperscript} // Fetch items at a specific position:
- * var path1 = new Path.Circle({
- * center: [50, 50],
- * radius: 25,
- * fillColor: 'black'
- * });
- *
- * var path2 = new Path.Circle({
- * center: [150, 50],
- * radius: 25,
- * fillColor: 'black'
- * });
- *
- * // Fetch all path items positioned at {x: 150, y: 150}:
- * var items = project.getItems({
- * position: [150, 50]
- * });
- *
- * // Select the fetched path:
- * items[0].selected = true;
- *
- * @example {@paperscript} // Fetch items using a comparator function:
- *
- * // Create a circle shaped path:
- * var path1 = new Path.Circle({
- * center: [50, 50],
- * radius: 25,
- * fillColor: 'black'
- * });
- *
- * // Create a circle shaped path with 50% opacity:
- * var path2 = new Path.Circle({
- * center: [150, 50],
- * radius: 25,
- * fillColor: 'black',
- * opacity: 0.5
- * });
- *
- * // Fetch all items whose opacity is smaller than 1
- * var items = paper.project.getItems({
- * opacity: function(value) {
- * return value < 1;
- * }
- * });
- *
- * // Select the fetched item:
- * items[0].selected = true;
- *
- * @example {@paperscript} // Fetch items using a comparator function (2):
- *
- * // Create a rectangle shaped path (4 segments):
- * var path1 = new Path.Rectangle({
- * from: [25, 25],
- * to: [75, 75],
- * strokeColor: 'black',
- * strokeWidth: 10
- * });
- *
- * // Create a line shaped path (2 segments):
- * var path2 = new Path.Line({
- * from: [125, 50],
- * to: [175, 50],
- * strokeColor: 'black',
- * strokeWidth: 10
- * });
- *
- * // Fetch all paths with 2 segments:
- * var items = project.getItems({
- * class: Path,
- * segments: function(segments) {
- * return segments.length == 2;
- * }
- * });
- *
- * // Select the fetched path:
- * items[0].selected = true;
- *
- * @example {@paperscript} // Match (nested) properties of the data property:
- *
- * // Create a black circle shaped path:
- * var path1 = new Path.Circle({
- * center: [50, 50],
- * radius: 25,
- * fillColor: 'black',
- * data: {
- * person: {
- * name: 'john',
- * length: 200,
- * hair: true
- * }
- * }
- * });
- *
- * // Create a red circle shaped path:
- * var path2 = new Path.Circle({
- * center: [150, 50],
- * radius: 25,
- * fillColor: 'red',
- * data: {
- * person: {
- * name: 'john',
- * length: 180,
- * hair: false
- * }
- * }
- * });
- *
- * // Fetch all items whose data object contains a person
- * // object whose name is john and length is 180:
- * var items = paper.project.getItems({
- * data: {
- * person: {
- * name: 'john',
- * length: 180
- * }
- * }
- * });
- *
- * // Select the fetched item:
- * items[0].selected = true;
- *
- * @example {@paperscript} // Match strings using regular expressions:
- *
- * // Create a path named 'aardvark':
- * var path1 = new Path.Circle({
- * center: [50, 50],
- * radius: 25,
- * fillColor: 'black',
- * name: 'aardvark'
- * });
- *
- * // Create a path named 'apple':
- * var path2 = new Path.Circle({
- * center: [150, 50],
- * radius: 25,
- * fillColor: 'black',
- * name: 'apple'
- * });
- *
- * // Create a path named 'banana':
- * var path2 = new Path.Circle({
- * center: [250, 50],
- * radius: 25,
- * fillColor: 'black',
- * name: 'banana'
- * });
- *
- * // Fetch all items that have a name starting with 'a':
- * var items = project.getItems({
- * name: /^a/
- * });
- *
- * // Change the fill color of the matched items:
- * for (var i = 0; i < items.length; i++) {
- * items[i].fillColor = 'red';
- * }
- */
- getItems: function(options) {
- return Item._getItems(this, options);
- },
-
- /**
- * Fetch the first item contained within the project whose properties
- * match the criteria in the specified object.
- * Extended matching is possible by providing a compare function or
- * regular expression. Matching points, colors only work as a comparison
- * of the full object, not partial matching (e.g. only providing the x-
- * coordinate to match all points with that x-value). Partial matching
- * does work for {@link Item#data}.
- *
- * See {@link #getItems(options)} for a selection of illustrated examples.
- *
- * @param {Object|Function} options the criteria to match against
- * @return {Item} the first item in the project matching the given criteria
- */
- getItem: function(options) {
- return Item._getItems(this, options, null, null, true)[0] || null;
- },
-
- /**
- * {@grouptitle Importing / Exporting JSON and SVG}
- *
- * Exports (serializes) the project with all its layers and child items to a
- * JSON data object or string.
- *
- * @name Project#exportJSON
- * @function
- *
- * @option [options.asString=true] {Boolean} whether the JSON is returned as
- * a `Object` or a `String`
- * @option [options.precision=5] {Number} the amount of fractional digits in
- * numbers used in JSON data
- *
- * @param {Object} [options] the serialization options
- * @return {String} the exported JSON data
- */
-
- /**
- * Imports (deserializes) the stored JSON data into the project.
- * Note that the project is not cleared first. You can call
- * {@link Project#clear()} to do so.
- *
- * @param {String} json the JSON data to import from
- * @return {Item} the imported item
- */
- importJSON: function(json) {
- this.activate();
- // Provide the activeLayer as a possible target for layers, but only if
- // it's empty.
- var layer = this._activeLayer;
- return Base.importJSON(json, layer && layer.isEmpty() && layer);
- },
-
- /**
- * Exports the project with all its layers and child items as an SVG DOM,
- * all contained in one top level SVG group node.
- *
- * @name Project#exportSVG
- * @function
- *
- * @option [options.bounds='view'] {String|Rectangle} the bounds of the area
- * to export, either as a string ({@values 'view', content'}), or a
- * {@link Rectangle} object: `'view'` uses the view bounds,
- * `'content'` uses the stroke bounds of all content
- * @option [options.matrix=paper.view.matrix] {Matrix} the matrix with which
- * to transform the exported content: If `options.bounds` is set to
- * `'view'`, `paper.view.matrix` is used, for all other settings of
- * `options.bounds` the identity matrix is used.
- * @option [options.asString=false] {Boolean} whether a SVG node or a
- * `String` is to be returned
- * @option [options.precision=5] {Number} the amount of fractional digits in
- * numbers used in SVG data
- * @option [options.matchShapes=false] {Boolean} whether path items should
- * tried to be converted to SVG shape items (rect, circle, ellipse,
- * line, polyline, polygon), if their geometries match
- * @option [options.embedImages=true] {Boolean} whether raster images should
- * be embedded as base64 data inlined in the xlink:href attribute, or
- * kept as a link to their external URL.
- *
- * @param {Object} [options] the export options
- * @return {SVGElement|String} the project converted to an SVG node or a
- * `String` depending on `option.asString` value
- */
-
- /**
- * Converts the provided SVG content into Paper.js items and adds them to
- * the active layer of this project.
- * Note that the project is not cleared first. You can call
- * {@link Project#clear()} to do so.
- *
- * @name Project#importSVG
- * @function
- *
- * @option [options.expandShapes=false] {Boolean} whether imported shape
- * items should be expanded to path items
- * @option options.onLoad {Function} the callback function to call once the
- * SVG content is loaded from the given URL receiving two arguments: the
- * converted `item` and the original `svg` data as a string. Only
- * required when loading from external resources.
- * @option options.onError {Function} the callback function to call if an
- * error occurs during loading. Only required when loading from external
- * resources.
- * @option [options.insert=true] {Boolean} whether the imported items should
- * be added to the project that `importSVG()` is called on
- * @option [options.applyMatrix={@link PaperScope#settings}.applyMatrix]
- * {Boolean} whether the imported items should have their transformation
- * matrices applied to their contents or not
- *
- * @param {SVGElement|String} svg the SVG content to import, either as a SVG
- * DOM node, a string containing SVG content, or a string describing the
- * URL of the SVG file to fetch.
- * @param {Object} [options] the import options
- * @return {Item} the newly created Paper.js item containing the converted
- * SVG content
- */
- /**
- * Imports the provided external SVG file, converts it into Paper.js items
- * and adds them to the active layer of this project.
- * Note that the project is not cleared first. You can call
- * {@link Project#clear()} to do so.
- *
- * @name Project#importSVG
- * @function
- *
- * @param {SVGElement|String} svg the URL of the SVG file to fetch.
- * @param {Function} onLoad the callback function to call once the SVG
- * content is loaded from the given URL receiving two arguments: the
- * converted `item` and the original `svg` data as a string. Only
- * required when loading from external files.
- * @return {Item} the newly created Paper.js item containing the converted
- * SVG content
- */
-
- removeOn: function(type) {
- var sets = this._removeSets;
- if (sets) {
- // Always clear the drag set on mouseup
- if (type === 'mouseup')
- sets.mousedrag = null;
- var set = sets[type];
- if (set) {
- for (var id in set) {
- var item = set[id];
- // If we remove this item, we also need to erase it from all
- // other sets.
- for (var key in sets) {
- var other = sets[key];
- if (other && other != set)
- delete other[item._id];
+ (
+ this._activeLayer ||
+ this._insertItem(undefined, new Layer(Item.NO_INSERT), true)
+ ) // _created = true
+ .insertChild(index, item);
+ // If a layer was newly created, also activate it.
+ if (_created && item.activate) item.activate();
+ return item;
+ },
+
+ /**
+ * {@grouptitle Hit-testing, Fetching and Matching Items}
+ *
+ * Performs a hit-test on the items contained within the project at the
+ * location of the specified point.
+ *
+ * The options object allows you to control the specifics of the hit-test
+ * and may contain a combination of the following values:
+ *
+ * @name Project#hitTest
+ * @function
+ *
+ * @option [options.tolerance={@link PaperScope#settings}.hitTolerance]
+ * {Number} the tolerance of the hit-test
+ * @option options.class {Function} only hit-test against a specific item
+ * class, or any of its sub-classes, by providing the constructor
+ * function against which an `instanceof` check is performed:
+ * {@values Group, Layer, Path, CompoundPath, Shape, Raster,
+ * SymbolItem, PointText, ...}
+ * @option options.match {Function} a match function to be called for each
+ * found hit result: Return `true` to return the result, `false` to keep
+ * searching
+ * @option [options.fill=true] {Boolean} hit-test the fill of items
+ * @option [options.stroke=true] {Boolean} hit-test the stroke of path
+ * items, taking into account the setting of stroke color and width
+ * @option [options.segments=true] {Boolean} hit-test for {@link
+ * Segment#point} of {@link Path} items
+ * @option options.curves {Boolean} hit-test the curves of path items,
+ * without taking the stroke color or width into account
+ * @option options.handles {Boolean} hit-test for the handles ({@link
+ * Segment#handleIn} / {@link Segment#handleOut}) of path segments.
+ * @option options.ends {Boolean} only hit-test for the first or last
+ * segment points of open path items
+ * @option options.position {Boolean} hit-test the {@link Item#position} of
+ * of items, which depends on the setting of {@link Item#pivot}
+ * @option options.center {Boolean} hit-test the {@link Rectangle#center} of
+ * the bounding rectangle of items ({@link Item#bounds})
+ * @option options.bounds {Boolean} hit-test the corners and side-centers of
+ * the bounding rectangle of items ({@link Item#bounds})
+ * @option options.guides {Boolean} hit-test items that have {@link
+ * Item#guide} set to `true`
+ * @option options.selected {Boolean} only hit selected items
+ *
+ * @param {Point} point the point where the hit-test should be performed
+ * @param {Object} [options={ fill: true, stroke: true, segments: true,
+ * tolerance: settings.hitTolerance }]
+ * @return {HitResult} a hit result object that contains more information
+ * about what exactly was hit or `null` if nothing was hit
+ */
+ // NOTE: Implementation is in Item#hitTest()
+
+ /**
+ * Performs a hit-test on the item and its children (if it is a {@link
+ * Group} or {@link Layer}) at the location of the specified point,
+ * returning all found hits.
+ *
+ * The options object allows you to control the specifics of the hit-
+ * test. See {@link #hitTest(point[, options])} for a list of all options.
+ *
+ * @name Project#hitTestAll
+ * @function
+ * @param {Point} point the point where the hit-test should be performed
+ * @param {Object} [options={ fill: true, stroke: true, segments: true,
+ * tolerance: settings.hitTolerance }]
+ * @return {HitResult[]} hit result objects for all hits, describing what
+ * exactly was hit or `null` if nothing was hit
+ * @see #hitTest(point[, options]);
+ */
+ // NOTE: Implementation is in Item#hitTestAll()
+
+ /**
+ *
+ * Fetch items contained within the project whose properties match the
+ * criteria in the specified object.
+ *
+ * Extended matching of properties is possible by providing a comparator
+ * function or regular expression. Matching points, colors only work as a
+ * comparison of the full object, not partial matching (e.g. only providing
+ * the x- coordinate to match all points with that x-value). Partial
+ * matching does work for {@link Item#data}.
+ *
+ * Matching items against a rectangular area is also possible, by setting
+ * either `options.inside` or `options.overlapping` to a rectangle
+ * describing the area in which the items either have to be fully or partly
+ * contained.
+ *
+ * @option [options.recursive=true] {Boolean} whether to loop recursively
+ * through all children, or stop at the current level
+ * @option options.match {Function} a match function to be called for each
+ * item, allowing the definition of more flexible item checks that are
+ * not bound to properties. If no other match properties are defined,
+ * this function can also be passed instead of the `match` object
+ * @option options.class {Function} the constructor function of the item
+ * type to match against
+ * @option options.inside {Rectangle} the rectangle in which the items need
+ * to be fully contained
+ * @option options.overlapping {Rectangle} the rectangle with which the
+ * items need to at least partly overlap
+ *
+ * @see Item#matches(options)
+ * @see Item#getItems(options)
+ * @param {Object|Function} options the criteria to match against
+ * @return {Item[]} the list of matching items contained in the project
+ *
+ * @example {@paperscript} // Fetch all selected path items:
+ * var path1 = new Path.Circle({
+ * center: [50, 50],
+ * radius: 25,
+ * fillColor: 'black'
+ * });
+ *
+ * var path2 = new Path.Circle({
+ * center: [150, 50],
+ * radius: 25,
+ * fillColor: 'black'
+ * });
+ *
+ * // Select path2:
+ * path2.selected = true;
+ *
+ * // Fetch all selected path items:
+ * var items = project.getItems({
+ * selected: true,
+ * class: Path
+ * });
+ *
+ * // Change the fill color of the selected path to red:
+ * items[0].fillColor = 'red';
+ *
+ * @example {@paperscript} // Fetch all items with a specific fill color:
+ * var path1 = new Path.Circle({
+ * center: [50, 50],
+ * radius: 25,
+ * fillColor: 'black'
+ * });
+ *
+ * var path2 = new Path.Circle({
+ * center: [150, 50],
+ * radius: 25,
+ * fillColor: 'purple'
+ * });
+ *
+ * // Fetch all items with a purple fill color:
+ * var items = project.getItems({
+ * fillColor: 'purple'
+ * });
+ *
+ * // Select the fetched item:
+ * items[0].selected = true;
+ *
+ * @example {@paperscript} // Fetch items at a specific position:
+ * var path1 = new Path.Circle({
+ * center: [50, 50],
+ * radius: 25,
+ * fillColor: 'black'
+ * });
+ *
+ * var path2 = new Path.Circle({
+ * center: [150, 50],
+ * radius: 25,
+ * fillColor: 'black'
+ * });
+ *
+ * // Fetch all path items positioned at {x: 150, y: 150}:
+ * var items = project.getItems({
+ * position: [150, 50]
+ * });
+ *
+ * // Select the fetched path:
+ * items[0].selected = true;
+ *
+ * @example {@paperscript} // Fetch items using a comparator function:
+ *
+ * // Create a circle shaped path:
+ * var path1 = new Path.Circle({
+ * center: [50, 50],
+ * radius: 25,
+ * fillColor: 'black'
+ * });
+ *
+ * // Create a circle shaped path with 50% opacity:
+ * var path2 = new Path.Circle({
+ * center: [150, 50],
+ * radius: 25,
+ * fillColor: 'black',
+ * opacity: 0.5
+ * });
+ *
+ * // Fetch all items whose opacity is smaller than 1
+ * var items = paper.project.getItems({
+ * opacity: function(value) {
+ * return value < 1;
+ * }
+ * });
+ *
+ * // Select the fetched item:
+ * items[0].selected = true;
+ *
+ * @example {@paperscript} // Fetch items using a comparator function (2):
+ *
+ * // Create a rectangle shaped path (4 segments):
+ * var path1 = new Path.Rectangle({
+ * from: [25, 25],
+ * to: [75, 75],
+ * strokeColor: 'black',
+ * strokeWidth: 10
+ * });
+ *
+ * // Create a line shaped path (2 segments):
+ * var path2 = new Path.Line({
+ * from: [125, 50],
+ * to: [175, 50],
+ * strokeColor: 'black',
+ * strokeWidth: 10
+ * });
+ *
+ * // Fetch all paths with 2 segments:
+ * var items = project.getItems({
+ * class: Path,
+ * segments: function(segments) {
+ * return segments.length == 2;
+ * }
+ * });
+ *
+ * // Select the fetched path:
+ * items[0].selected = true;
+ *
+ * @example {@paperscript} // Match (nested) properties of the data property:
+ *
+ * // Create a black circle shaped path:
+ * var path1 = new Path.Circle({
+ * center: [50, 50],
+ * radius: 25,
+ * fillColor: 'black',
+ * data: {
+ * person: {
+ * name: 'john',
+ * length: 200,
+ * hair: true
+ * }
+ * }
+ * });
+ *
+ * // Create a red circle shaped path:
+ * var path2 = new Path.Circle({
+ * center: [150, 50],
+ * radius: 25,
+ * fillColor: 'red',
+ * data: {
+ * person: {
+ * name: 'john',
+ * length: 180,
+ * hair: false
+ * }
+ * }
+ * });
+ *
+ * // Fetch all items whose data object contains a person
+ * // object whose name is john and length is 180:
+ * var items = paper.project.getItems({
+ * data: {
+ * person: {
+ * name: 'john',
+ * length: 180
+ * }
+ * }
+ * });
+ *
+ * // Select the fetched item:
+ * items[0].selected = true;
+ *
+ * @example {@paperscript} // Match strings using regular expressions:
+ *
+ * // Create a path named 'aardvark':
+ * var path1 = new Path.Circle({
+ * center: [50, 50],
+ * radius: 25,
+ * fillColor: 'black',
+ * name: 'aardvark'
+ * });
+ *
+ * // Create a path named 'apple':
+ * var path2 = new Path.Circle({
+ * center: [150, 50],
+ * radius: 25,
+ * fillColor: 'black',
+ * name: 'apple'
+ * });
+ *
+ * // Create a path named 'banana':
+ * var path2 = new Path.Circle({
+ * center: [250, 50],
+ * radius: 25,
+ * fillColor: 'black',
+ * name: 'banana'
+ * });
+ *
+ * // Fetch all items that have a name starting with 'a':
+ * var items = project.getItems({
+ * name: /^a/
+ * });
+ *
+ * // Change the fill color of the matched items:
+ * for (var i = 0; i < items.length; i++) {
+ * items[i].fillColor = 'red';
+ * }
+ */
+ getItems: function (options) {
+ return Item._getItems(this, options);
+ },
+
+ /**
+ * Fetch the first item contained within the project whose properties
+ * match the criteria in the specified object.
+ * Extended matching is possible by providing a compare function or
+ * regular expression. Matching points, colors only work as a comparison
+ * of the full object, not partial matching (e.g. only providing the x-
+ * coordinate to match all points with that x-value). Partial matching
+ * does work for {@link Item#data}.
+ *
+ * See {@link #getItems(options)} for a selection of illustrated examples.
+ *
+ * @param {Object|Function} options the criteria to match against
+ * @return {Item} the first item in the project matching the given criteria
+ */
+ getItem: function (options) {
+ return Item._getItems(this, options, null, null, true)[0] || null;
+ },
+
+ /**
+ * {@grouptitle Importing / Exporting JSON and SVG}
+ *
+ * Exports (serializes) the project with all its layers and child items to a
+ * JSON data object or string.
+ *
+ * @name Project#exportJSON
+ * @function
+ *
+ * @option [options.asString=true] {Boolean} whether the JSON is returned as
+ * a `Object` or a `String`
+ * @option [options.precision=5] {Number} the amount of fractional digits in
+ * numbers used in JSON data
+ *
+ * @param {Object} [options] the serialization options
+ * @return {String} the exported JSON data
+ */
+
+ /**
+ * Imports (deserializes) the stored JSON data into the project.
+ * Note that the project is not cleared first. You can call
+ * {@link Project#clear()} to do so.
+ *
+ * @param {String} json the JSON data to import from
+ * @return {Item} the imported item
+ */
+ importJSON: function (json) {
+ this.activate();
+ // Provide the activeLayer as a possible target for layers, but only if
+ // it's empty.
+ var layer = this._activeLayer;
+ return Base.importJSON(json, layer && layer.isEmpty() && layer);
+ },
+
+ /**
+ * Exports the project with all its layers and child items as an SVG DOM,
+ * all contained in one top level SVG group node.
+ *
+ * @name Project#exportSVG
+ * @function
+ *
+ * @option [options.bounds='view'] {String|Rectangle} the bounds of the area
+ * to export, either as a string ({@values 'view', content'}), or a
+ * {@link Rectangle} object: `'view'` uses the view bounds,
+ * `'content'` uses the stroke bounds of all content
+ * @option [options.matrix=paper.view.matrix] {Matrix} the matrix with which
+ * to transform the exported content: If `options.bounds` is set to
+ * `'view'`, `paper.view.matrix` is used, for all other settings of
+ * `options.bounds` the identity matrix is used.
+ * @option [options.asString=false] {Boolean} whether a SVG node or a
+ * `String` is to be returned
+ * @option [options.precision=5] {Number} the amount of fractional digits in
+ * numbers used in SVG data
+ * @option [options.matchShapes=false] {Boolean} whether path items should
+ * tried to be converted to SVG shape items (rect, circle, ellipse,
+ * line, polyline, polygon), if their geometries match
+ * @option [options.embedImages=true] {Boolean} whether raster images should
+ * be embedded as base64 data inlined in the xlink:href attribute, or
+ * kept as a link to their external URL.
+ *
+ * @param {Object} [options] the export options
+ * @return {SVGElement|String} the project converted to an SVG node or a
+ * `String` depending on `option.asString` value
+ */
+
+ /**
+ * Converts the provided SVG content into Paper.js items and adds them to
+ * the active layer of this project.
+ * Note that the project is not cleared first. You can call
+ * {@link Project#clear()} to do so.
+ *
+ * @name Project#importSVG
+ * @function
+ *
+ * @option [options.expandShapes=false] {Boolean} whether imported shape
+ * items should be expanded to path items
+ * @option options.onLoad {Function} the callback function to call once the
+ * SVG content is loaded from the given URL receiving two arguments: the
+ * converted `item` and the original `svg` data as a string. Only
+ * required when loading from external resources.
+ * @option options.onError {Function} the callback function to call if an
+ * error occurs during loading. Only required when loading from external
+ * resources.
+ * @option [options.insert=true] {Boolean} whether the imported items should
+ * be added to the project that `importSVG()` is called on
+ * @option [options.applyMatrix={@link PaperScope#settings}.applyMatrix]
+ * {Boolean} whether the imported items should have their transformation
+ * matrices applied to their contents or not
+ *
+ * @param {SVGElement|String} svg the SVG content to import, either as a SVG
+ * DOM node, a string containing SVG content, or a string describing the
+ * URL of the SVG file to fetch.
+ * @param {Object} [options] the import options
+ * @return {Item} the newly created Paper.js item containing the converted
+ * SVG content
+ */
+ /**
+ * Imports the provided external SVG file, converts it into Paper.js items
+ * and adds them to the active layer of this project.
+ * Note that the project is not cleared first. You can call
+ * {@link Project#clear()} to do so.
+ *
+ * @name Project#importSVG
+ * @function
+ *
+ * @param {SVGElement|String} svg the URL of the SVG file to fetch.
+ * @param {Function} onLoad the callback function to call once the SVG
+ * content is loaded from the given URL receiving two arguments: the
+ * converted `item` and the original `svg` data as a string. Only
+ * required when loading from external files.
+ * @return {Item} the newly created Paper.js item containing the converted
+ * SVG content
+ */
+
+ removeOn: function (type) {
+ var sets = this._removeSets;
+ if (sets) {
+ // Always clear the drag set on mouseup
+ if (type === "mouseup") sets.mousedrag = null;
+ var set = sets[type];
+ if (set) {
+ for (var id in set) {
+ var item = set[id];
+ // If we remove this item, we also need to erase it from all
+ // other sets.
+ for (var key in sets) {
+ var other = sets[key];
+ if (other && other != set) delete other[item._id];
+ }
+ item.remove();
}
- item.remove();
+ sets[type] = null;
}
- sets[type] = null;
}
- }
- },
-
- draw: function(ctx, matrix, pixelRatio) {
- // Increase the _updateVersion before the draw-loop. After that, items
- // that are visible will have their _updateVersion set to the new value.
- this._updateVersion++;
- ctx.save();
- matrix.applyToContext(ctx);
- // Use new Base() so we can use param.extend() to easily override values
- var children = this._children,
- param = new Base({
- offset: new Point(0, 0),
- pixelRatio: pixelRatio,
- viewMatrix: matrix.isIdentity() ? null : matrix,
- matrices: [new Matrix()], // Start with the identity matrix.
- // Tell the drawing routine that we want to keep _globalMatrix
- // up to date. Item#rasterize() and Raster#getAverageColor()
- // should not set this.
- updateMatrix: true
- });
- for (var i = 0, l = children.length; i < l; i++) {
- children[i].draw(ctx, param);
- }
- ctx.restore();
+ },
- // Draw the selection of the selected items in the project:
- if (this._selectionCount > 0) {
+ draw: function (ctx, matrix, pixelRatio) {
+ // Increase the _updateVersion before the draw-loop. After that, items
+ // that are visible will have their _updateVersion set to the new value.
+ this._updateVersion++;
ctx.save();
- ctx.strokeWidth = 1;
- var items = this._selectionItems,
- size = this._scope.settings.handleSize,
- version = this._updateVersion;
- for (var id in items) {
- items[id]._drawSelection(ctx, matrix, size, items, version);
+ matrix.applyToContext(ctx);
+ // Use new Base() so we can use param.extend() to easily override values
+ var children = this._children,
+ param = new Base({
+ offset: new Point(0, 0),
+ pixelRatio: pixelRatio,
+ viewMatrix: matrix.isIdentity() ? null : matrix,
+ matrices: [new Matrix()], // Start with the identity matrix.
+ // Tell the drawing routine that we want to keep _globalMatrix
+ // up to date. Item#rasterize() and Raster#getAverageColor()
+ // should not set this.
+ updateMatrix: true,
+ });
+ for (var i = 0, l = children.length; i < l; i++) {
+ children[i].draw(ctx, param);
}
ctx.restore();
- }
+
+ // Draw the selection of the selected items in the project:
+ if (this._selectionCount > 0 && this._scope.settings.drawSelected) {
+ ctx.save();
+ ctx.strokeWidth = 1;
+ var items = this._selectionItems,
+ size = this._scope.settings.handleSize,
+ version = this._updateVersion;
+ for (var id in items) {
+ items[id]._drawSelection(ctx, matrix, size, items, version);
+ }
+ ctx.restore();
+ }
+ },
}
-});
+);
diff --git a/src/style/Style.js b/src/style/Style.js
index 72609b9197..78b9214e0e 100644
--- a/src/style/Style.js
+++ b/src/style/Style.js
@@ -67,639 +67,685 @@
* };
*
*/
-var Style = Base.extend(new function() {
- // Defaults for items without text-styles (PathItem, Shape, Raster, ...):
- var itemDefaults = {
- // Paths
- fillColor: null,
- fillRule: 'nonzero',
- strokeColor: null,
- strokeWidth: 1,
- strokeCap: 'butt',
- strokeJoin: 'miter',
- strokeScaling: true,
- miterLimit: 10,
- dashOffset: 0,
- dashArray: [],
- // Shadows
- shadowColor: null,
- shadowBlur: 0,
- shadowOffset: new Point(),
- // Selection
- selectedColor: null
- },
- // Defaults for Group, Layer and Project (anything item that allows nesting
- // needs to be able to pass down text styles as well):
- groupDefaults = Base.set({}, itemDefaults, {
- // Characters
- fontFamily: 'sans-serif',
- fontWeight: 'normal',
- fontSize: 12,
- leading: null,
- // Paragraphs
- justification: 'left'
- }),
- // Defaults for TextItem (override default fillColor to black):
- textDefaults = Base.set({}, groupDefaults, {
- fillColor: new Color() // black
- }),
- flags = {
- strokeWidth: /*#=*/Change.STROKE,
- strokeCap: /*#=*/Change.STROKE,
- strokeJoin: /*#=*/Change.STROKE,
- // strokeScaling can change the coordinates of cached path items
- strokeScaling: /*#=*/(Change.STROKE | Change.GEOMETRY),
- miterLimit: /*#=*/Change.STROKE,
- fontFamily: /*#=*/Change.GEOMETRY,
- fontWeight: /*#=*/Change.GEOMETRY,
- fontSize: /*#=*/Change.GEOMETRY,
- font: /*#=*/Change.GEOMETRY, // deprecated, links to fontFamily
- leading: /*#=*/Change.GEOMETRY,
- justification: /*#=*/Change.GEOMETRY
- },
- item = {
- // Enforce creation of beans, as bean getters have hidden parameters,
- // see _dontMerge argument below.
- beans: true
- },
- fields = /** @lends Style# */{
- _class: 'Style',
- beans: true,
-
- initialize: function Style(style, _owner, _project) {
- // We keep values in a separate object that we can iterate over.
- this._values = {};
- this._owner = _owner;
- this._project = _owner && _owner._project || _project
- || paper.project;
- // Use different defaults based on the owner
- this._defaults = !_owner || _owner instanceof Group ? groupDefaults
- : _owner instanceof TextItem ? textDefaults
- : itemDefaults;
- if (style)
- this.set(style);
- }
- };
-
- // Iterate over groupDefaults to inject getters / setters, to cover all
- // properties
- Base.each(groupDefaults, function(value, key) {
- var isColor = /Color$/.test(key),
- isPoint = key === 'shadowOffset',
- part = Base.capitalize(key),
- flag = flags[key],
- set = 'set' + part,
- get = 'get' + part;
-
- // Define getters and setters to be injected into this class.
- // This is how style values are handled:
- // - Style values are all stored in this._values
- // - The style object starts with an empty _values object, with fallback
- // on _defaults through code in the getter below.
- // - Only the styles that are explicitly set on the object get defined
- // in _values.
- // - Color values are not stored as converted colors immediately. The
- // raw value is stored, and conversion only happens in the getter.
- fields[set] = function(value) {
- var owner = this._owner,
- children = owner && owner._children,
- applyToChildren = children && children.length > 0
- && !(owner instanceof CompoundPath);
- // Only unify styles on children of Groups, excluding CompoundPaths.
- if (applyToChildren) {
- for (var i = 0, l = children.length; i < l; i++)
- children[i]._style[set](value);
- }
- // Always store selectedColor in item _values to make sure that
- // group selected bounds and position color is coherent whether it
- // has children or not when the value is set.
- if ((key === 'selectedColor' || !applyToChildren)
- && key in this._defaults) {
- var old = this._values[key];
- if (old !== value) {
- if (isColor) {
- // The old value may be a native string or other color
- // description that wasn't coerced to a color object yet
- if (old) {
- Color._setOwner(old, null);
- old._canvasStyle = null;
- }
- if (value && value.constructor === Color) {
- // NOTE: If value is not a Color, it is only
- // converted and cloned in the getter further down.
- value = Color._setOwner(value, owner,
+var Style = Base.extend(
+ new (function () {
+ // Defaults for items without text-styles (PathItem, Shape, Raster, ...):
+ var itemDefaults = {
+ // Paths
+ fillColor: null,
+ fillRule: "nonzero",
+ strokeColor: null,
+ strokeWidth: 1,
+ strokeCap: "butt",
+ strokeJoin: "miter",
+ strokeScaling: true,
+ miterLimit: 10,
+ dashOffset: 0,
+ dashArray: [],
+ // Shadows
+ shadowColor: null,
+ shadowBlur: 0,
+ shadowOffset: new Point(),
+ // Selection
+ selectedColor: null,
+ selectedWidth: 2,
+ selectedCap: "round",
+ selectedJoin: "round",
+ selectedMiterLimit: 1,
+ },
+ // Defaults for Group, Layer and Project (anything item that allows nesting
+ // needs to be able to pass down text styles as well):
+ groupDefaults = Base.set({}, itemDefaults, {
+ // Characters
+ fontFamily: "sans-serif",
+ fontWeight: "normal",
+ fontSize: 12,
+ leading: null,
+ // Paragraphs
+ justification: "left",
+ }),
+ // Defaults for TextItem (override default fillColor to black):
+ textDefaults = Base.set({}, groupDefaults, {
+ fillColor: new Color(), // black
+ }),
+ flags = {
+ strokeWidth: /*#=*/ Change.STROKE,
+ strokeCap: /*#=*/ Change.STROKE,
+ strokeJoin: /*#=*/ Change.STROKE,
+ // strokeScaling can change the coordinates of cached path items
+ strokeScaling: /*#=*/ Change.STROKE | Change.GEOMETRY,
+ miterLimit: /*#=*/ Change.STROKE,
+ fontFamily: /*#=*/ Change.GEOMETRY,
+ fontWeight: /*#=*/ Change.GEOMETRY,
+ fontSize: /*#=*/ Change.GEOMETRY,
+ font: /*#=*/ Change.GEOMETRY, // deprecated, links to fontFamily
+ leading: /*#=*/ Change.GEOMETRY,
+ justification: /*#=*/ Change.GEOMETRY,
+ },
+ item = {
+ // Enforce creation of beans, as bean getters have hidden parameters,
+ // see _dontMerge argument below.
+ beans: true,
+ },
+ fields = /** @lends Style# */ {
+ _class: "Style",
+ beans: true,
+
+ initialize: function Style(style, _owner, _project) {
+ // We keep values in a separate object that we can iterate over.
+ this._values = {};
+ this._owner = _owner;
+ this._project =
+ (_owner && _owner._project) ||
+ _project ||
+ paper.project;
+ // Use different defaults based on the owner
+ this._defaults =
+ !_owner || _owner instanceof Group
+ ? groupDefaults
+ : _owner instanceof TextItem
+ ? textDefaults
+ : itemDefaults;
+ if (style) this.set(style);
+ },
+ };
+
+ // Iterate over groupDefaults to inject getters / setters, to cover all
+ // properties
+ Base.each(groupDefaults, function (value, key) {
+ var isColor = /Color$/.test(key),
+ isPoint = key === "shadowOffset",
+ part = Base.capitalize(key),
+ flag = flags[key],
+ set = "set" + part,
+ get = "get" + part;
+
+ // Define getters and setters to be injected into this class.
+ // This is how style values are handled:
+ // - Style values are all stored in this._values
+ // - The style object starts with an empty _values object, with fallback
+ // on _defaults through code in the getter below.
+ // - Only the styles that are explicitly set on the object get defined
+ // in _values.
+ // - Color values are not stored as converted colors immediately. The
+ // raw value is stored, and conversion only happens in the getter.
+ fields[set] = function (value) {
+ var owner = this._owner,
+ children = owner && owner._children,
+ applyToChildren =
+ children &&
+ children.length > 0 &&
+ !(owner instanceof CompoundPath);
+ // Only unify styles on children of Groups, excluding CompoundPaths.
+ if (applyToChildren) {
+ for (var i = 0, l = children.length; i < l; i++)
+ children[i]._style[set](value);
+ }
+ // Always store selectedColor in item _values to make sure that
+ // group selected bounds and position color is coherent whether it
+ // has children or not when the value is set.
+ if (
+ (key === "selectedColor" || !applyToChildren) &&
+ key in this._defaults
+ ) {
+ var old = this._values[key];
+ if (old !== value) {
+ if (isColor) {
+ // The old value may be a native string or other color
+ // description that wasn't coerced to a color object yet
+ if (old) {
+ Color._setOwner(old, null);
+ old._canvasStyle = null;
+ }
+ if (value && value.constructor === Color) {
+ // NOTE: If value is not a Color, it is only
+ // converted and cloned in the getter further down.
+ value = Color._setOwner(
+ value,
+ owner,
// Only provide a color-setter if the style
// is to be applied to the children:
- applyToChildren && set);
+ applyToChildren && set
+ );
+ }
}
- }
- // NOTE: We do not convert the values to Colors in the
- // setter. This only happens once the getter is called.
- this._values[key] = value;
- // Notify the owner of the style change STYLE is always set,
- // additional flags come from flags, as used for STROKE:
- if (owner)
- owner._changed(flag || /*#=*/Change.STYLE);
- }
- }
- };
-
- fields[get] = function(_dontMerge) {
- var owner = this._owner,
- children = owner && owner._children,
- applyToChildren = children && children.length > 0
- && !(owner instanceof CompoundPath),
- value;
- // If the owner has children, walk through all of them and see if
- // they all have the same style.
- // If true is passed for _dontMerge, don't merge children styles.
- if (applyToChildren && !_dontMerge) {
- for (var i = 0, l = children.length; i < l; i++) {
- var childValue = children[i]._style[get]();
- if (!i) {
- value = childValue;
- } else if (!Base.equals(value, childValue)) {
- // If there is another child with a different
- // style, the style is not defined:
- return undefined;
+ // NOTE: We do not convert the values to Colors in the
+ // setter. This only happens once the getter is called.
+ this._values[key] = value;
+ // Notify the owner of the style change STYLE is always set,
+ // additional flags come from flags, as used for STROKE:
+ if (owner) owner._changed(flag || /*#=*/ Change.STYLE);
}
}
- } else if (key in this._defaults) {
- var value = this._values[key];
- if (value === undefined) {
- value = this._defaults[key];
- // Clone defaults if available:
- if (value && value.clone) {
- value = value.clone();
+ };
+
+ fields[get] = function (_dontMerge) {
+ var owner = this._owner,
+ children = owner && owner._children,
+ applyToChildren =
+ children &&
+ children.length > 0 &&
+ !(owner instanceof CompoundPath),
+ value;
+ // If the owner has children, walk through all of them and see if
+ // they all have the same style.
+ // If true is passed for _dontMerge, don't merge children styles.
+ if (applyToChildren && !_dontMerge) {
+ for (var i = 0, l = children.length; i < l; i++) {
+ var childValue = children[i]._style[get]();
+ if (!i) {
+ value = childValue;
+ } else if (!Base.equals(value, childValue)) {
+ // If there is another child with a different
+ // style, the style is not defined:
+ return undefined;
+ }
}
- } else {
- var ctor = isColor ? Color : isPoint ? Point : null;
- if (ctor && !(value && value.constructor === ctor)) {
- // Convert to a Color / Point, and stored result of the
- // conversion.
- this._values[key] = value = ctor.read([value], 0,
- { readNull: true, clone: true });
+ } else if (key in this._defaults) {
+ var value = this._values[key];
+ if (value === undefined) {
+ value = this._defaults[key];
+ // Clone defaults if available:
+ if (value && value.clone) {
+ value = value.clone();
+ }
+ } else {
+ var ctor = isColor ? Color : isPoint ? Point : null;
+ if (ctor && !(value && value.constructor === ctor)) {
+ // Convert to a Color / Point, and stored result of the
+ // conversion.
+ this._values[key] = value = ctor.read([value], 0, {
+ readNull: true,
+ clone: true,
+ });
+ }
}
}
+ if (value && isColor) {
+ // Color._setOwner() may clone the color if it already has a
+ // different owner (e.g. resulting from `childValue` above).
+ // Only provide a color-setter if the style is to be applied to
+ // the children:
+ value = Color._setOwner(
+ value,
+ owner,
+ applyToChildren && set
+ );
+ }
+ return value;
+ };
+
+ // Inject style getters and setters into the Item class, which redirect
+ // calls to the linked style object.
+ item[get] = function (_dontMerge) {
+ return this._style[get](_dontMerge);
+ };
+
+ item[set] = function (value) {
+ this._style[set](value);
+ };
+ });
+
+ // Create aliases for deprecated properties. The lookup table contains the
+ // part after 'get' / 'set':
+ // TODO: Remove once deprecated long enough, after December 2016.
+ Base.each(
+ {
+ Font: "FontFamily",
+ WindingRule: "FillRule",
+ },
+ function (value, key) {
+ var get = "get" + key,
+ set = "set" + key;
+ fields[get] = item[get] = "#get" + value;
+ fields[set] = item[set] = "#set" + value;
}
- if (value && isColor) {
- // Color._setOwner() may clone the color if it already has a
- // different owner (e.g. resulting from `childValue` above).
- // Only provide a color-setter if the style is to be applied to
- // the children:
- value = Color._setOwner(value, owner, applyToChildren && set);
- }
- return value;
- };
-
- // Inject style getters and setters into the Item class, which redirect
- // calls to the linked style object.
- item[get] = function(_dontMerge) {
- return this._style[get](_dontMerge);
- };
-
- item[set] = function(value) {
- this._style[set](value);
- };
- });
-
- // Create aliases for deprecated properties. The lookup table contains the
- // part after 'get' / 'set':
- // TODO: Remove once deprecated long enough, after December 2016.
- Base.each({
- Font: 'FontFamily',
- WindingRule: 'FillRule'
- }, function(value, key) {
- var get = 'get' + key,
- set = 'set' + key;
- fields[get] = item[get] = '#get' + value;
- fields[set] = item[set] = '#set' + value;
- });
-
- Item.inject(item);
- return fields;
-}, /** @lends Style# */{
- set: function(style) {
- // If the passed style object is also a Style, clone its cloneable
- // fields rather than simply copying them.
- var isStyle = style instanceof Style,
- // Use the other stlyle's _values object for iteration
- values = isStyle ? style._values : style;
- if (values) {
- for (var key in values) {
- if (key in this._defaults) {
- var value = values[key];
- // Delegate to setter, so Group styles work too.
- this[key] = value && isStyle && value.clone
- ? value.clone() : value;
+ );
+
+ Item.inject(item);
+ return fields;
+ })(),
+ /** @lends Style# */ {
+ set: function (style) {
+ // If the passed style object is also a Style, clone its cloneable
+ // fields rather than simply copying them.
+ var isStyle = style instanceof Style,
+ // Use the other stlyle's _values object for iteration
+ values = isStyle ? style._values : style;
+ if (values) {
+ for (var key in values) {
+ if (key in this._defaults) {
+ var value = values[key];
+ // Delegate to setter, so Group styles work too.
+ this[key] =
+ value && isStyle && value.clone
+ ? value.clone()
+ : value;
+ }
}
}
- }
- },
-
- equals: function(style) {
- // Since we're dealing with defaults, loop through style values in both
- // objects and compare with default fall-back. But in the secondary pass
- // only check against keys that weren't already in the first object:
- function compare(style1, style2, secondary) {
- var values1 = style1._values,
- values2 = style2._values,
- defaults2 = style2._defaults;
- for (var key in values1) {
- var value1 = values1[key],
- value2 = values2[key];
- if (!(secondary && key in values2) && !Base.equals(value1,
- value2 === undefined ? defaults2[key] : value2))
- return false;
+ },
+
+ equals: function (style) {
+ // Since we're dealing with defaults, loop through style values in both
+ // objects and compare with default fall-back. But in the secondary pass
+ // only check against keys that weren't already in the first object:
+ function compare(style1, style2, secondary) {
+ var values1 = style1._values,
+ values2 = style2._values,
+ defaults2 = style2._defaults;
+ for (var key in values1) {
+ var value1 = values1[key],
+ value2 = values2[key];
+ if (
+ !(secondary && key in values2) &&
+ !Base.equals(
+ value1,
+ value2 === undefined ? defaults2[key] : value2
+ )
+ )
+ return false;
+ }
+ return true;
}
- return true;
- }
-
- return style === this || style && this._class === style._class
- && compare(this, style)
- && compare(style, this, true)
- || false;
- },
-
- _dispose: function() {
- var color;
- color = this.getFillColor();
- if (color) color._canvasStyle = null;
- color = this.getStrokeColor();
- if (color) color._canvasStyle = null;
- color = this.getShadowColor();
- if (color) color._canvasStyle = null;
- },
-
- // DOCS: Style#hasFill()
- hasFill: function() {
- var color = this.getFillColor();
- return !!color && color.alpha > 0;
- },
-
- // DOCS: Style#hasStroke()
- hasStroke: function() {
- var color = this.getStrokeColor();
- return !!color && color.alpha > 0 && this.getStrokeWidth() > 0;
- },
-
- // DOCS: Style#hasShadow()
- hasShadow: function() {
- var color = this.getShadowColor();
- // In order to draw a shadow, we need either a shadow blur or an
- // offset, or both.
- return !!color && color.alpha > 0 && (this.getShadowBlur() > 0
- || !this.getShadowOffset().isZero());
- },
-
- /**
- * The view that this style belongs to.
- *
- * @bean
- * @type View
- */
- getView: function() {
- return this._project._view;
- },
-
- // Overrides
-
- getFontStyle: function() {
- var fontSize = this.getFontSize();
- // To prevent an obscure iOS 7 crash, we have to convert the size to a
- // string first before passing it to the regular expression.
- // The following nonsensical statement would also prevent the bug,
- // proving that the issue is not the regular expression itself, but
- // something deeper down in the optimizer:
- // `if (size === 0) size = 0;`
- return this.getFontWeight()
- + ' ' + fontSize + (/[a-z]/i.test(fontSize + '') ? ' ' : 'px ')
- + this.getFontFamily();
- },
-
- /**
- * @bean
- * @private
- * @deprecated use {@link #fontFamily} instead.
- */
- getFont: '#getFontFamily',
- setFont: '#setFontFamily',
-
- getLeading: function getLeading() {
- // Override leading to return fontSize * 1.2 by default.
- var leading = getLeading.base.call(this),
- fontSize = this.getFontSize();
- if (/pt|em|%|px/.test(fontSize))
- fontSize = this.getView().getPixelSize(fontSize);
- return leading != null ? leading : fontSize * 1.2;
- }
- // DOCS: why isn't the example code showing up?
- /**
- * Style objects don't need to be created directly. Just pass an object to
- * {@link Item#style} or {@link Project#currentStyle}, it will be converted
- * to a Style object internally.
- *
- * @name Style#initialize
- * @param {Object} style
- */
-
- /**
- * {@grouptitle Stroke Style}
- *
- * The color of the stroke.
- *
- * @name Style#strokeColor
- * @property
- * @type ?Color
- *
- * @example {@paperscript}
- * // Setting the stroke color of a path:
- *
- * // Create a circle shaped path at { x: 80, y: 50 }
- * // with a radius of 35:
- * var circle = new Path.Circle(new Point(80, 50), 35);
- *
- * // Set its stroke color to RGB red:
- * circle.strokeColor = new Color(1, 0, 0);
- */
-
- /**
- * The width of the stroke.
- *
- * @name Style#strokeWidth
- * @property
- * @type Number
- * @default 1
- *
- * @example {@paperscript}
- * // Setting an item's stroke width:
- *
- * // Create a circle shaped path at { x: 80, y: 50 }
- * // with a radius of 35:
- * var circle = new Path.Circle(new Point(80, 50), 35);
- *
- * // Set its stroke color to black:
- * circle.strokeColor = 'black';
- *
- * // Set its stroke width to 10:
- * circle.strokeWidth = 10;
- */
-
- /**
- * The shape to be used at the beginning and end of open {@link Path} items,
- * when they have a stroke.
- *
- * @name Style#strokeCap
- * @property
- * @type String
- * @values 'round', 'square', 'butt'
- * @default 'butt'
- *
- * @example {@paperscript height=200}
- * // A look at the different stroke caps:
- *
- * var line = new Path(new Point(80, 50), new Point(420, 50));
- * line.strokeColor = 'black';
- * line.strokeWidth = 20;
- *
- * // Select the path, so we can see where the stroke is formed:
- * line.selected = true;
- *
- * // Set the stroke cap of the line to be round:
- * line.strokeCap = 'round';
- *
- * // Copy the path and set its stroke cap to be square:
- * var line2 = line.clone();
- * line2.position.y += 50;
- * line2.strokeCap = 'square';
- *
- * // Make another copy and set its stroke cap to be butt:
- * var line2 = line.clone();
- * line2.position.y += 100;
- * line2.strokeCap = 'butt';
- */
-
- /**
- * The shape to be used at the segments and corners of {@link Path} items
- * when they have a stroke.
- *
- * @name Style#strokeJoin
- * @property
- * @type String
- * @values 'miter', 'round', 'bevel'
- * @default 'miter'
- *
- * @example {@paperscript height=120}
- * // A look at the different stroke joins:
- * var path = new Path();
- * path.add(new Point(80, 100));
- * path.add(new Point(120, 40));
- * path.add(new Point(160, 100));
- * path.strokeColor = 'black';
- * path.strokeWidth = 20;
- *
- * // Select the path, so we can see where the stroke is formed:
- * path.selected = true;
- *
- * var path2 = path.clone();
- * path2.position.x += path2.bounds.width * 1.5;
- * path2.strokeJoin = 'round';
- *
- * var path3 = path2.clone();
- * path3.position.x += path3.bounds.width * 1.5;
- * path3.strokeJoin = 'bevel';
- */
-
- /**
- * Specifies whether the stroke is to be drawn taking the current affine
- * transformation into account (the default behavior), or whether it should
- * appear as a non-scaling stroke.
- *
- * @name Style#strokeScaling
- * @property
- * @type Boolean
- * @default true
- */
-
- /**
- * The dash offset of the stroke.
- *
- * @name Style#dashOffset
- * @property
- * @type Number
- * @default 0
- */
-
- /**
- * Specifies an array containing the dash and gap lengths of the stroke.
- *
- * @example {@paperscript}
- * var path = new Path.Circle(new Point(80, 50), 40);
- * path.strokeWidth = 2;
- * path.strokeColor = 'black';
- *
- * // Set the dashed stroke to [10pt dash, 4pt gap]:
- * path.dashArray = [10, 4];
- *
- * @name Style#dashArray
- * @property
- * @type Number[]
- * @default []
- */
-
- /**
- * The miter limit of the stroke. When two line segments meet at a sharp
- * angle and miter joins have been specified for {@link #strokeJoin}, it is
- * possible for the miter to extend far beyond the {@link #strokeWidth} of
- * the path. The miterLimit imposes a limit on the ratio of the miter length
- * to the {@link #strokeWidth}.
- *
- * @name Style#miterLimit
- * @property
- * @default 10
- * @type Number
- */
-
- /**
- * {@grouptitle Fill Style}
- *
- * The fill color.
- *
- * @name Style#fillColor
- * @property
- * @type ?Color
- *
- * @example {@paperscript}
- * // Setting the fill color of a path to red:
- *
- * // Create a circle shaped path at { x: 80, y: 50 }
- * // with a radius of 35:
- * var circle = new Path.Circle(new Point(80, 50), 35);
- *
- * // Set the fill color of the circle to RGB red:
- * circle.fillColor = new Color(1, 0, 0);
- */
-
- /**
- * The fill-rule with which the shape gets filled. Please note that only
- * modern browsers support fill-rules other than `'nonzero'`.
- *
- * @name Style#fillRule
- * @property
- * @type String
- * @values 'nonzero', 'evenodd'
- * @default 'nonzero'
- */
-
- /**
- * {@grouptitle Shadow Style}
- *
- * The shadow color.
- *
- * @property
- * @name Style#shadowColor
- * @type ?Color
- *
- * @example {@paperscript}
- * // Creating a circle with a black shadow:
- *
- * var circle = new Path.Circle({
- * center: [80, 50],
- * radius: 35,
- * fillColor: 'white',
- * // Set the shadow color of the circle to RGB black:
- * shadowColor: new Color(0, 0, 0),
- * // Set the shadow blur radius to 12:
- * shadowBlur: 12,
- * // Offset the shadow by { x: 5, y: 5 }
- * shadowOffset: new Point(5, 5)
- * });
- */
-
- /**
- * The shadow's blur radius.
- *
- * @property
- * @name Style#shadowBlur
- * @type Number
- * @default 0
- */
-
- /**
- * The shadow's offset.
- *
- * @property
- * @name Style#shadowOffset
- * @type Point
- * @default 0
- */
-
- /**
- * {@grouptitle Selection Style}
- *
- * The color the item is highlighted with when selected. If the item does
- * not specify its own color, the color defined by its layer is used instead.
- *
- * @name Style#selectedColor
- * @property
- * @type ?Color
- */
-
- /**
- * {@grouptitle Character Style}
- *
- * The font-family to be used in text content.
- *
- * @name Style#fontFamily
- * @type String
- * @default 'sans-serif'
- */
-
- /**
- *
- * The font-weight to be used in text content.
- *
- * @name Style#fontWeight
- * @type String|Number
- * @default 'normal'
- */
-
- /**
- * The font size of text content, as a number in pixels, or as a string with
- * optional units `'px'`, `'pt'` and `'em'`.
- *
- * @name Style#fontSize
- * @type Number|String
- * @default 10
- */
-
- /**
- *
- * The font-family to be used in text content, as one string.
- *
- * @name Style#font
- * @type String
- * @default 'sans-serif'
- * @deprecated use {@link #fontFamily} instead.
- */
-
- /**
- * The text leading of text content.
- *
- * @name Style#leading
- * @type Number|String
- * @default fontSize * 1.2
- */
-
- /**
- * {@grouptitle Paragraph Style}
- *
- * The justification of text paragraphs.
- *
- * @name Style#justification
- * @type String
- * @values 'left', 'right', 'center'
- * @default 'left'
- */
-});
+ return (
+ style === this ||
+ (style &&
+ this._class === style._class &&
+ compare(this, style) &&
+ compare(style, this, true)) ||
+ false
+ );
+ },
+
+ _dispose: function () {
+ var color;
+ color = this.getFillColor();
+ if (color) color._canvasStyle = null;
+ color = this.getStrokeColor();
+ if (color) color._canvasStyle = null;
+ color = this.getShadowColor();
+ if (color) color._canvasStyle = null;
+ },
+
+ // DOCS: Style#hasFill()
+ hasFill: function () {
+ var color = this.getFillColor();
+ return !!color && color.alpha > 0;
+ },
+
+ // DOCS: Style#hasStroke()
+ hasStroke: function () {
+ var color = this.getStrokeColor();
+ return !!color && color.alpha > 0 && this.getStrokeWidth() > 0;
+ },
+
+ // DOCS: Style#hasShadow()
+ hasShadow: function () {
+ var color = this.getShadowColor();
+ // In order to draw a shadow, we need either a shadow blur or an
+ // offset, or both.
+ return (
+ !!color &&
+ color.alpha > 0 &&
+ (this.getShadowBlur() > 0 || !this.getShadowOffset().isZero())
+ );
+ },
+
+ /**
+ * The view that this style belongs to.
+ *
+ * @bean
+ * @type View
+ */
+ getView: function () {
+ return this._project._view;
+ },
+
+ // Overrides
+
+ getFontStyle: function () {
+ var fontSize = this.getFontSize();
+ // To prevent an obscure iOS 7 crash, we have to convert the size to a
+ // string first before passing it to the regular expression.
+ // The following nonsensical statement would also prevent the bug,
+ // proving that the issue is not the regular expression itself, but
+ // something deeper down in the optimizer:
+ // `if (size === 0) size = 0;`
+ return (
+ this.getFontWeight() +
+ " " +
+ fontSize +
+ (/[a-z]/i.test(fontSize + "") ? " " : "px ") +
+ this.getFontFamily()
+ );
+ },
+
+ /**
+ * @bean
+ * @private
+ * @deprecated use {@link #fontFamily} instead.
+ */
+ getFont: "#getFontFamily",
+ setFont: "#setFontFamily",
+
+ getLeading: function getLeading() {
+ // Override leading to return fontSize * 1.2 by default.
+ var leading = getLeading.base.call(this),
+ fontSize = this.getFontSize();
+ if (/pt|em|%|px/.test(fontSize))
+ fontSize = this.getView().getPixelSize(fontSize);
+ return leading != null ? leading : fontSize * 1.2;
+ },
+
+ // DOCS: why isn't the example code showing up?
+ /**
+ * Style objects don't need to be created directly. Just pass an object to
+ * {@link Item#style} or {@link Project#currentStyle}, it will be converted
+ * to a Style object internally.
+ *
+ * @name Style#initialize
+ * @param {Object} style
+ */
+
+ /**
+ * {@grouptitle Stroke Style}
+ *
+ * The color of the stroke.
+ *
+ * @name Style#strokeColor
+ * @property
+ * @type ?Color
+ *
+ * @example {@paperscript}
+ * // Setting the stroke color of a path:
+ *
+ * // Create a circle shaped path at { x: 80, y: 50 }
+ * // with a radius of 35:
+ * var circle = new Path.Circle(new Point(80, 50), 35);
+ *
+ * // Set its stroke color to RGB red:
+ * circle.strokeColor = new Color(1, 0, 0);
+ */
+
+ /**
+ * The width of the stroke.
+ *
+ * @name Style#strokeWidth
+ * @property
+ * @type Number
+ * @default 1
+ *
+ * @example {@paperscript}
+ * // Setting an item's stroke width:
+ *
+ * // Create a circle shaped path at { x: 80, y: 50 }
+ * // with a radius of 35:
+ * var circle = new Path.Circle(new Point(80, 50), 35);
+ *
+ * // Set its stroke color to black:
+ * circle.strokeColor = 'black';
+ *
+ * // Set its stroke width to 10:
+ * circle.strokeWidth = 10;
+ */
+
+ /**
+ * The shape to be used at the beginning and end of open {@link Path} items,
+ * when they have a stroke.
+ *
+ * @name Style#strokeCap
+ * @property
+ * @type String
+ * @values 'round', 'square', 'butt'
+ * @default 'butt'
+ *
+ * @example {@paperscript height=200}
+ * // A look at the different stroke caps:
+ *
+ * var line = new Path(new Point(80, 50), new Point(420, 50));
+ * line.strokeColor = 'black';
+ * line.strokeWidth = 20;
+ *
+ * // Select the path, so we can see where the stroke is formed:
+ * line.selected = true;
+ *
+ * // Set the stroke cap of the line to be round:
+ * line.strokeCap = 'round';
+ *
+ * // Copy the path and set its stroke cap to be square:
+ * var line2 = line.clone();
+ * line2.position.y += 50;
+ * line2.strokeCap = 'square';
+ *
+ * // Make another copy and set its stroke cap to be butt:
+ * var line2 = line.clone();
+ * line2.position.y += 100;
+ * line2.strokeCap = 'butt';
+ */
+
+ /**
+ * The shape to be used at the segments and corners of {@link Path} items
+ * when they have a stroke.
+ *
+ * @name Style#strokeJoin
+ * @property
+ * @type String
+ * @values 'miter', 'round', 'bevel'
+ * @default 'miter'
+ *
+ * @example {@paperscript height=120}
+ * // A look at the different stroke joins:
+ * var path = new Path();
+ * path.add(new Point(80, 100));
+ * path.add(new Point(120, 40));
+ * path.add(new Point(160, 100));
+ * path.strokeColor = 'black';
+ * path.strokeWidth = 20;
+ *
+ * // Select the path, so we can see where the stroke is formed:
+ * path.selected = true;
+ *
+ * var path2 = path.clone();
+ * path2.position.x += path2.bounds.width * 1.5;
+ * path2.strokeJoin = 'round';
+ *
+ * var path3 = path2.clone();
+ * path3.position.x += path3.bounds.width * 1.5;
+ * path3.strokeJoin = 'bevel';
+ */
+
+ /**
+ * Specifies whether the stroke is to be drawn taking the current affine
+ * transformation into account (the default behavior), or whether it should
+ * appear as a non-scaling stroke.
+ *
+ * @name Style#strokeScaling
+ * @property
+ * @type Boolean
+ * @default true
+ */
+
+ /**
+ * The dash offset of the stroke.
+ *
+ * @name Style#dashOffset
+ * @property
+ * @type Number
+ * @default 0
+ */
+
+ /**
+ * Specifies an array containing the dash and gap lengths of the stroke.
+ *
+ * @example {@paperscript}
+ * var path = new Path.Circle(new Point(80, 50), 40);
+ * path.strokeWidth = 2;
+ * path.strokeColor = 'black';
+ *
+ * // Set the dashed stroke to [10pt dash, 4pt gap]:
+ * path.dashArray = [10, 4];
+ *
+ * @name Style#dashArray
+ * @property
+ * @type Number[]
+ * @default []
+ */
+
+ /**
+ * The miter limit of the stroke. When two line segments meet at a sharp
+ * angle and miter joins have been specified for {@link #strokeJoin}, it is
+ * possible for the miter to extend far beyond the {@link #strokeWidth} of
+ * the path. The miterLimit imposes a limit on the ratio of the miter length
+ * to the {@link #strokeWidth}.
+ *
+ * @name Style#miterLimit
+ * @property
+ * @default 10
+ * @type Number
+ */
+
+ /**
+ * {@grouptitle Fill Style}
+ *
+ * The fill color.
+ *
+ * @name Style#fillColor
+ * @property
+ * @type ?Color
+ *
+ * @example {@paperscript}
+ * // Setting the fill color of a path to red:
+ *
+ * // Create a circle shaped path at { x: 80, y: 50 }
+ * // with a radius of 35:
+ * var circle = new Path.Circle(new Point(80, 50), 35);
+ *
+ * // Set the fill color of the circle to RGB red:
+ * circle.fillColor = new Color(1, 0, 0);
+ */
+
+ /**
+ * The fill-rule with which the shape gets filled. Please note that only
+ * modern browsers support fill-rules other than `'nonzero'`.
+ *
+ * @name Style#fillRule
+ * @property
+ * @type String
+ * @values 'nonzero', 'evenodd'
+ * @default 'nonzero'
+ */
+
+ /**
+ * {@grouptitle Shadow Style}
+ *
+ * The shadow color.
+ *
+ * @property
+ * @name Style#shadowColor
+ * @type ?Color
+ *
+ * @example {@paperscript}
+ * // Creating a circle with a black shadow:
+ *
+ * var circle = new Path.Circle({
+ * center: [80, 50],
+ * radius: 35,
+ * fillColor: 'white',
+ * // Set the shadow color of the circle to RGB black:
+ * shadowColor: new Color(0, 0, 0),
+ * // Set the shadow blur radius to 12:
+ * shadowBlur: 12,
+ * // Offset the shadow by { x: 5, y: 5 }
+ * shadowOffset: new Point(5, 5)
+ * });
+ */
+
+ /**
+ * The shadow's blur radius.
+ *
+ * @property
+ * @name Style#shadowBlur
+ * @type Number
+ * @default 0
+ */
+
+ /**
+ * The shadow's offset.
+ *
+ * @property
+ * @name Style#shadowOffset
+ * @type Point
+ * @default 0
+ */
+
+ /**
+ * {@grouptitle Selection Style}
+ *
+ * The color the item is highlighted with when selected. If the item does
+ * not specify its own color, the color defined by its layer is used instead.
+ *
+ * @name Style#selectedColor
+ * @property
+ * @type ?Color
+ */
+
+ /**
+ * {@grouptitle Character Style}
+ *
+ * The font-family to be used in text content.
+ *
+ * @name Style#fontFamily
+ * @type String
+ * @default 'sans-serif'
+ */
+
+ /**
+ *
+ * The font-weight to be used in text content.
+ *
+ * @name Style#fontWeight
+ * @type String|Number
+ * @default 'normal'
+ */
+
+ /**
+ * The font size of text content, as a number in pixels, or as a string with
+ * optional units `'px'`, `'pt'` and `'em'`.
+ *
+ * @name Style#fontSize
+ * @type Number|String
+ * @default 10
+ */
+
+ /**
+ *
+ * The font-family to be used in text content, as one string.
+ *
+ * @name Style#font
+ * @type String
+ * @default 'sans-serif'
+ * @deprecated use {@link #fontFamily} instead.
+ */
+
+ /**
+ * The text leading of text content.
+ *
+ * @name Style#leading
+ * @type Number|String
+ * @default fontSize * 1.2
+ */
+
+ /**
+ * {@grouptitle Paragraph Style}
+ *
+ * The justification of text paragraphs.
+ *
+ * @name Style#justification
+ * @type String
+ * @values 'left', 'right', 'center'
+ * @default 'left'
+ */
+ }
+);