-
Notifications
You must be signed in to change notification settings - Fork 9
cc.math.Path
Path Objects are used in several places of the engine:
- input for a
cc.action.PathAction
. - backend for
cc.render.RenderingContext
stroke/fill capabilities. - cached in a
cc.math.Shape
object to get tessellation of fill/stroke cache advantage.
Internally, a Path is build of cc.math.path.Segment
object instances. Being a cc.math.Path
and i.e. a cc.math.path.LineSegment
both a segment, we can build very complex path objects.
Internally, Paths are a composition of cc.math.path.SubPath
objects. SubPath or Contours, are a collection of Segments, which can be either open or closed. For example a closed arc will be a SubPath with just one cc.math.path.SegmentArc
, while a poly-line can be a SubPath with a hundred cc.math.path.SegmentLine
segments. With this structure it is very easy to get the path length, flatten the path into a poly-line, or get the paths contour points.
The Path is built incrementally by calling methods that add a new Segment. If the current SubPath is closed, a new SubPath is created, and the segment added to that new SubPath.
cc.math.path.Segment
is the base of all Path and Segment types. There are two types of Segments:
- simple segments, like a bezier or a line.
- compound segments, which are a collection of other segments, like a
cc.math.Path
orcc.math.SubPath
.
A cc.math.path.Segment
object has methods for:
/**
* Get the segment's length. If it is a compound segment,
* the sum of all its segments length will be returned.
*/
getLength() -> number;
/**
* Trace the segment and get a collection of points on it.
*/
trace( dstArray {Array<cc.math.Vector>=}, numPoints {number=} ) -> cc.math.Vector[];
/**
* Get a point on the segment.
* Assuming the Segment will be of size 1, then
* 0 will be the origin segment point, and
* 1 the final segment point,
* The normalizedPos parameter represents a point on
* this segment proportional to its value.
*
* For segments like beziers, the returned point will be
* the result of solving the curve for the parameter, and not
* necessarily the point at the proportional curve length position.
*/
getValueAt( normalizedPos {number}, out {cc.math.Vector=} ) -> cc.math.Vector;
/**
* Get the first point in the Segment.
* + For a SubPath will be its first Segment's starting point.
* + For a Path will be its first SubPath's starting point.
*/
getStartingPoint() -> cc.math.Vector;
/**
* Get the last point in the Segment.
* + For a SubPath will be its last Segment's end point.
* + For a Path will be its last SubPath's end point.
*/
getEndingPoint() -> cc.math.Vector;
/**
* Build a copy of this segment, either a complete path or a line.
*/
clone() -> cc.math.path.Segment;
To build the path, the path object offers the following methods:
The path will be a poly-line.
static createFromPoints( points {Array<cc.math.Vector>} ) -> cc.math.Path
A segment uses the current SubPath's last point as its first point. A Path will be build for example like this:
path.beginPath().
moveTo(0, 0).
bezierCurveTo(W, 0, 0, H, W, H).
quadraticCurveTo(0, H, 0, 0).
closePath();
beginPath()
method will reset all the current path information, discarding all the previously created SubPaths and segments.
moveTo( x {number}, y {number}, matrix {Float32Array=} )
is used to move the path tracer to the desired position after calling beginPath.
closePath()
will add a new cc.math.path.SegmentLine
which closes the path.
The following methods are available for adding segments. All segment-adding functions will optionally receive a 3x3 matrix with an affine transformation. If set, the segment points will be transformed by this matrix. This is what the RenderingContext object does will defining the current path.
moveTo( x, y, matrix {Float32Array=} )
lineTo( x, y, matrix {Float32Array=} )
rect( x, y, w, h, matrix {Float32Array=} )
Arcs are defined by a position, a radius, and starting and ending angle. Arcs will always take the least diff angle between start and end angle values. Hence, the maximum angle an arc will have is 2Pi radians. If anticlockwise is true, the resulting angle will be the complimentary up to 2Pi radians. This parameter only makes sense for non closed arcs.
arc( x, y, radius, startAngle, endAngle, anticlockwise, matrix {Float32Array=})
x1,y1 is the curve control point.
quadraticCurveTo( x1, y1, x2, y2, matrix {Float32Array=} )
x0,y0 and x1,y1 are the two curve control points.
bezierCurveTo( x0, y0, x1, y1, x2, y2, matrix {Float32Array=} )
If the tension is not 0.5, this method will define a cardinal spline of the specified tension. This method will create as much chained caltmullRom/cardinal spline segments as it can based on the points array length.
catmullRomTo( points {Array<cc.math.Point>}, closed {boolean}, tension, matrix {Float32Array=} )
Similar to the previous method, but this one will create just one catmullRom segment.
catmullRomTo( cp0x, cp0x, cp1x, cp1y, p1x, p1y, tension, matrix {Float32Array=} )
The process of creating geometry for a given path is controlled by two methods: getStrokeGeometry
and getFillGeometry
. This methods will refer to the Path internal cache values and will return the same geomtry for both operations whenever possible.
If stroking conditions change when getStrokeGeometry
is called, new geometry will be generated.
This is a cost intensive operation. If the path is dynamically built per frame, calling these methods may severely impact performance. Whenever possible, cache the path object instead of calling the cc.render.RenderingContext
path creation methods. Also have a look at the Shape object for caching multiple paths and stroke/fill attributes into one single draw call.
The stroking operations are dependent of line attributes. If the attributes have changed from the previous call, a new stroke geometry will be calculated. Also, if the path has changed by adding new segments, the stroke geometry will as well be recalculated.
getStrokeGeometry( attributes {cc.math.path.geometry.StrokeGeometryAttributes=} )
Get the fill geometry. If the path has changed by adding new segments, the fill geometry will be recalculated.
getFillGeometry( )