blob: 30855b8886432129bc8f00ed8512d66412102bcf [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.6
part of ui;
/// A complex, one-dimensional subset of a plane.
///
/// A path consists of a number of subpaths, and a _current point_.
///
/// Subpaths consist of segments of various types, such as lines,
/// arcs, or beziers. Subpaths can be open or closed, and can
/// self-intersect.
///
/// Closed subpaths enclose a (possibly discontiguous) region of the
/// plane based on the current [fillType].
///
/// The _current point_ is initially at the origin. After each
/// operation adding a segment to a subpath, the current point is
/// updated to the end of that segment.
///
/// Paths can be drawn on canvases using [Canvas.drawPath], and can
/// used to create clip regions using [Canvas.clipPath].
abstract class Path {
/// Create a new empty [Path] object.
factory Path() {
if (engine.experimentalUseSkia) {
return engine.SkPath();
} else {
return engine.SurfacePath();
}
}
/// Creates a copy of another [Path].
///
/// This copy is fast and does not require additional memory unless either
/// the `source` path or the path returned by this constructor are modified.
factory Path.from(Path source) {
if (engine.experimentalUseSkia) {
return engine.SkPath.from(source);
} else {
return engine.SurfacePath.from(source);
}
}
/// Determines how the interior of this path is calculated.
///
/// Defaults to the non-zero winding rule, [PathFillType.nonZero].
PathFillType get fillType;
set fillType(PathFillType value);
/// Starts a new subpath at the given coordinate.
void moveTo(double x, double y);
/// Starts a new subpath at the given offset from the current point.
void relativeMoveTo(double dx, double dy);
/// Adds a straight line segment from the current point to the given
/// point.
void lineTo(double x, double y);
/// Adds a straight line segment from the current point to the point
/// at the given offset from the current point.
void relativeLineTo(double dx, double dy);
/// Adds a quadratic bezier segment that curves from the current
/// point to the given point (x2,y2), using the control point
/// (x1,y1).
void quadraticBezierTo(double x1, double y1, double x2, double y2);
/// Adds a quadratic bezier segment that curves from the current
/// point to the point at the offset (x2,y2) from the current point,
/// using the control point at the offset (x1,y1) from the current
/// point.
void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2);
/// Adds a cubic bezier segment that curves from the current point
/// to the given point (x3,y3), using the control points (x1,y1) and
/// (x2,y2).
void cubicTo(
double x1, double y1, double x2, double y2, double x3, double y3);
/// Adds a cubic bezier segment that curves from the current point
/// to the point at the offset (x3,y3) from the current point, using
/// the control points at the offsets (x1,y1) and (x2,y2) from the
/// current point.
void relativeCubicTo(
double x1, double y1, double x2, double y2, double x3, double y3);
/// Adds a bezier segment that curves from the current point to the
/// given point (x2,y2), using the control points (x1,y1) and the
/// weight w. If the weight is greater than 1, then the curve is a
/// hyperbola; if the weight equals 1, it's a parabola; and if it is
/// less than 1, it is an ellipse.
void conicTo(double x1, double y1, double x2, double y2, double w);
/// Adds a bezier segment that curves from the current point to the
/// point at the offset (x2,y2) from the current point, using the
/// control point at the offset (x1,y1) from the current point and
/// the weight w. If the weight is greater than 1, then the curve is
/// a hyperbola; if the weight equals 1, it's a parabola; and if it
/// is less than 1, it is an ellipse.
void relativeConicTo(double x1, double y1, double x2, double y2, double w);
/// If the `forceMoveTo` argument is false, adds a straight line
/// segment and an arc segment.
///
/// If the `forceMoveTo` argument is true, starts a new subpath
/// consisting of an arc segment.
///
/// In either case, the arc segment consists of the arc that follows
/// the edge of the oval bounded by the given rectangle, from
/// startAngle radians around the oval up to startAngle + sweepAngle
/// radians around the oval, with zero radians being the point on
/// the right hand side of the oval that crosses the horizontal line
/// that intersects the center of the rectangle and with positive
/// angles going clockwise around the oval.
///
/// The line segment added if `forceMoveTo` is false starts at the
/// current point and ends at the start of the arc.
void arcTo(
Rect rect, double startAngle, double sweepAngle, bool forceMoveTo);
/// Appends up to four conic curves weighted to describe an oval of `radius`
/// and rotated by `rotation`.
///
/// The first curve begins from the last point in the path and the last ends
/// at `arcEnd`. The curves follow a path in a direction determined by
/// `clockwise` and `largeArc` in such a way that the sweep angle
/// is always less than 360 degrees.
///
/// A simple line is appended if either either radii are zero or the last
/// point in the path is `arcEnd`. The radii are scaled to fit the last path
/// point if both are greater than zero but too small to describe an arc.
///
/// See Conversion from endpoint to center parametrization described in
/// https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
/// as reference for implementation.
void arcToPoint(
Offset arcEnd, {
Radius radius = Radius.zero,
double rotation = 0.0,
bool largeArc = false,
bool clockwise = true,
});
/// Appends up to four conic curves weighted to describe an oval of `radius`
/// and rotated by `rotation`.
///
/// The last path point is described by (px, py).
///
/// The first curve begins from the last point in the path and the last ends
/// at `arcEndDelta.dx + px` and `arcEndDelta.dy + py`. The curves follow a
/// path in a direction determined by `clockwise` and `largeArc`
/// in such a way that the sweep angle is always less than 360 degrees.
///
/// A simple line is appended if either either radii are zero, or, both
/// `arcEndDelta.dx` and `arcEndDelta.dy` are zero. The radii are scaled to
/// fit the last path point if both are greater than zero but too small to
/// describe an arc.
void relativeArcToPoint(
Offset arcEndDelta, {
Radius radius = Radius.zero,
double rotation = 0.0,
bool largeArc = false,
bool clockwise = true,
});
/// Adds a new subpath that consists of four lines that outline the
/// given rectangle.
void addRect(Rect rect);
/// Adds a new subpath that consists of a curve that forms the
/// ellipse that fills the given rectangle.
///
/// To add a circle, pass an appropriate rectangle as `oval`.
/// [Rect.fromCircle] can be used to easily describe the circle's center
/// [Offset] and radius.
void addOval(Rect oval);
/// Adds a new subpath with one arc segment that consists of the arc
/// that follows the edge of the oval bounded by the given
/// rectangle, from startAngle radians around the oval up to
/// startAngle + sweepAngle radians around the oval, with zero
/// radians being the point on the right hand side of the oval that
/// crosses the horizontal line that intersects the center of the
/// rectangle and with positive angles going clockwise around the
/// oval.
void addArc(Rect oval, double startAngle, double sweepAngle);
/// Adds a new subpath with a sequence of line segments that connect the given
/// points.
///
/// If `close` is true, a final line segment will be added that connects the
/// last point to the first point.
///
/// The `points` argument is interpreted as offsets from the origin.
void addPolygon(List<Offset> points, bool close);
/// Adds a new subpath that consists of the straight lines and
/// curves needed to form the rounded rectangle described by the
/// argument.
void addRRect(RRect rrect);
/// Adds a new subpath that consists of the given `path` offset by the given
/// `offset`.
///
/// If `matrix4` is specified, the path will be transformed by this matrix
/// after the matrix is translated by the given offset. The matrix is a 4x4
/// matrix stored in column major order.
void addPath(Path path, Offset offset, {Float64List matrix4});
/// Adds the given path to this path by extending the current segment of this
/// path with the first segment of the given path.
///
/// If `matrix4` is specified, the path will be transformed by this matrix
/// after the matrix is translated by the given `offset`. The matrix is a 4x4
/// matrix stored in column major order.
void extendWithPath(Path path, Offset offset, {Float64List matrix4});
/// Closes the last subpath, as if a straight line had been drawn
/// from the current point to the first point of the subpath.
void close();
/// Clears the [Path] object of all subpaths, returning it to the
/// same state it had when it was created. The _current point_ is
/// reset to the origin.
void reset();
/// Tests to see if the given point is within the path. (That is, whether the
/// point would be in the visible portion of the path if the path was used
/// with [Canvas.clipPath].)
///
/// The `point` argument is interpreted as an offset from the origin.
///
/// Returns true if the point is in the path, and false otherwise.
///
/// Note: Not very efficient, it creates a canvas, plays path and calls
/// Context2D isPointInPath. If performance becomes issue, retaining
/// RawRecordingCanvas can remove create/remove rootElement cost.
bool contains(Offset point);
/// Returns a copy of the path with all the segments of every
/// subpath translated by the given offset.
Path shift(Offset offset);
/// Returns a copy of the path with all the segments of every
/// sub path transformed by the given matrix.
Path transform(Float64List matrix4);
/// Computes the bounding rectangle for this path.
///
/// A path containing only axis-aligned points on the same straight line will
/// have no area, and therefore `Rect.isEmpty` will return true for such a
/// path. Consider checking `rect.width + rect.height > 0.0` instead, or
/// using the [computeMetrics] API to check the path length.
///
/// For many more elaborate paths, the bounds may be inaccurate. For example,
/// when a path contains a circle, the points used to compute the bounds are
/// the circle's implied control points, which form a square around the
/// circle; if the circle has a transformation applied using [transform] then
/// that square is rotated, and the (axis-aligned, non-rotated) bounding box
/// therefore ends up grossly overestimating the actual area covered by the
/// circle.
// see https://skia.org/user/api/SkPath_Reference#SkPath_getBounds
Rect getBounds();
/// Combines the two paths according to the manner specified by the given
/// `operation`.
///
/// The resulting path will be constructed from non-overlapping contours. The
/// curve order is reduced where possible so that cubics may be turned into
/// quadratics, and quadratics maybe turned into lines.
static Path combine(PathOperation operation, Path path1, Path path2) {
assert(path1 != null);
assert(path2 != null);
if (engine.experimentalUseSkia) {
return engine.SkPath.combine(operation, path1, path2);
}
throw UnimplementedError();
}
/// Creates a [PathMetrics] object for this path.
///
/// If `forceClosed` is set to true, the contours of the path will be measured
/// as if they had been closed, even if they were not explicitly closed.
PathMetrics computeMetrics({bool forceClosed = false});
}