diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 64bf8bb514d..95c9fc594fc 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -34,6 +34,12 @@ bool _offsetIsValid(Offset offset) { return true; } +bool _radiusIsValid(Radius radius) { + assert(radius != null, 'Radius argument was null.'); + assert(!radius.x.isNaN && !radius.y.isNaN, 'Radius argument contained a NaN value.'); + return true; +} + Color _scaleAlpha(Color a, double factor) { return a.withAlpha((a.alpha * factor).round().clamp(0, 255)); } @@ -860,6 +866,64 @@ class Path extends NativeFieldWrapperClass2 { void _arcTo(double left, double top, double right, double bottom, double startAngle, double sweepAngle, bool forceMoveTo) native "Path_arcTo"; + /// 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. + /// + void arcToPoint(Offset arcEnd, { + Radius radius: Radius.zero, + double rotation: 0.0, + bool largeArc: false, + bool clockwise: true, + }) { + assert(_offsetIsValid(arcEnd)); + assert(_radiusIsValid(radius)); + _arcToPoint(arcEnd.dx, arcEnd.dy, radius.x, radius.y, rotation, + largeArc, clockwise); + } + void _arcToPoint(double arcEndX, double arcEndY, double radiusX, + double radiusY, double rotation, bool largeArc, + bool clockwise) native "Path_arcToPoint"; + + + /// 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, + }) { + assert(_offsetIsValid(arcEndDelta)); + assert(_radiusIsValid(radius)); + _relativeArcToPoint(arcEndDelta.dx, arcEndDelta.dy, radius.x, radius.y, + rotation, largeArc, clockwise); + } + void _relativeArcToPoint(double arcEndX, double arcEndY, double radiusX, + double radiusY, double rotation, + bool largeArc, bool clockwise) + native "Path_relativeArcToPoint"; + /// Adds a new subpath that consists of four lines that outline the /// given rectangle. void addRect(Rect rect) { diff --git a/lib/ui/painting/path.cc b/lib/ui/painting/path.cc index e4638e7439f..ee85ca2c694 100644 --- a/lib/ui/painting/path.cc +++ b/lib/ui/painting/path.cc @@ -25,29 +25,31 @@ static void Path_constructor(Dart_NativeArguments args) { IMPLEMENT_WRAPPERTYPEINFO(ui, Path); #define FOR_EACH_BINDING(V) \ - V(Path, getFillType) \ - V(Path, setFillType) \ - V(Path, moveTo) \ - V(Path, relativeMoveTo) \ - V(Path, lineTo) \ - V(Path, relativeLineTo) \ - V(Path, quadraticBezierTo) \ - V(Path, relativeQuadraticBezierTo) \ - V(Path, cubicTo) \ - V(Path, relativeCubicTo) \ - V(Path, conicTo) \ - V(Path, relativeConicTo) \ - V(Path, arcTo) \ - V(Path, addRect) \ - V(Path, addOval) \ V(Path, addArc) \ - V(Path, addPolygon) \ - V(Path, addRRect) \ + V(Path, addOval) \ V(Path, addPath) \ - V(Path, extendWithPath) \ + V(Path, addPolygon) \ + V(Path, addRect) \ + V(Path, addRRect) \ + V(Path, arcTo) \ + V(Path, arcToPoint) \ V(Path, close) \ - V(Path, reset) \ + V(Path, conicTo) \ V(Path, contains) \ + V(Path, cubicTo) \ + V(Path, extendWithPath) \ + V(Path, getFillType) \ + V(Path, lineTo) \ + V(Path, moveTo) \ + V(Path, quadraticBezierTo) \ + V(Path, relativeArcToPoint) \ + V(Path, relativeConicTo) \ + V(Path, relativeCubicTo) \ + V(Path, relativeLineTo) \ + V(Path, relativeMoveTo) \ + V(Path, relativeQuadraticBezierTo) \ + V(Path, reset) \ + V(Path, setFillType) \ V(Path, shift) \ V(Path, transform) @@ -139,6 +141,39 @@ void CanvasPath::arcTo(float left, forceMoveTo); } +void CanvasPath::arcToPoint(float arcEndX, + float arcEndY, + float radiusX, + float radiusY, + float xAxisRotation, + bool isLargeArc, + bool isClockwiseDirection) { + const auto arcSize = isLargeArc ? SkPath::ArcSize::kLarge_ArcSize + : SkPath::ArcSize::kSmall_ArcSize; + const auto direction = isClockwiseDirection + ? SkPath::Direction::kCW_Direction + : SkPath::Direction::kCCW_Direction; + + path_.arcTo(radiusX, radiusY, xAxisRotation, arcSize, direction, arcEndX, + arcEndY); +} + +void CanvasPath::relativeArcToPoint(float arcEndDeltaX, + float arcEndDeltaY, + float radiusX, + float radiusY, + float xAxisRotation, + bool isLargeArc, + bool isClockwiseDirection) { + const auto arcSize = isLargeArc ? SkPath::ArcSize::kLarge_ArcSize + : SkPath::ArcSize::kSmall_ArcSize; + const auto direction = isClockwiseDirection + ? SkPath::Direction::kCW_Direction + : SkPath::Direction::kCCW_Direction; + path_.rArcTo(radiusX, radiusY, xAxisRotation, arcSize, direction, + arcEndDeltaX, arcEndDeltaY); +} + void CanvasPath::addRect(float left, float top, float right, float bottom) { path_.addRect(SkRect::MakeLTRB(left, top, right, bottom)); } diff --git a/lib/ui/painting/path.h b/lib/ui/painting/path.h index 40e959e6f51..900ca0e3f57 100644 --- a/lib/ui/painting/path.h +++ b/lib/ui/painting/path.h @@ -53,6 +53,20 @@ class CanvasPath : public fxl::RefCountedThreadSafe, float startAngle, float sweepAngle, bool forceMoveTo); + void arcToPoint(float arcEndX, + float arcEndY, + float radiusX, + float radiusY, + float xAxisRotation, + bool isLargeArc, + bool isClockwiseDirection); + void relativeArcToPoint(float arcEndDeltaX, + float arcEndDeltaY, + float radiusX, + float radiusY, + float xAxisRotation, + bool isLargeArc, + bool isClockwiseDirection); void addRect(float left, float top, float right, float bottom); void addOval(float left, float top, float right, float bottom); void addArc(float left,