From 46cfa4384ae642d1df7a48ea3192564c573e705f Mon Sep 17 00:00:00 2001 From: Luigi Rosso Date: Fri, 11 Dec 2020 15:41:32 -0800 Subject: [PATCH] Adding support for stars, shapes, and trim path fixes. --- CHANGELOG.md | 11 ++ lib/src/generated/rive_core_context.dart | 41 ++++++ lib/src/generated/shapes/polygon_base.dart | 69 ++++++++++ lib/src/generated/shapes/star_base.dart | 50 ++++++++ .../shapes/paint/trim_path_drawing.dart | 120 +++++++++++------- lib/src/rive_core/shapes/polygon.dart | 29 +++++ lib/src/rive_core/shapes/star.dart | 35 +++++ pubspec.yaml | 2 +- 8 files changed, 308 insertions(+), 49 deletions(-) create mode 100644 lib/src/generated/shapes/polygon_base.dart create mode 100644 lib/src/generated/shapes/star_base.dart create mode 100644 lib/src/rive_core/shapes/polygon.dart create mode 100644 lib/src/rive_core/shapes/star.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 686eb81..a224f76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## [0.6.4] - 2020-12-11 15:43:01 + +- Adding support for parametric polygon and star shapes. +- Fixes to trim paths that wrap around the entire shape. +- Expose mix value in SimpleAnimation, allows for mixing multiple animations together. + +## [0.6.3] - 2020-11-17 16:02:47 + +- Added support for parametric path origins. +- Fixes for rendering artboards with non-zero origin values. + ## [0.6.2+3] - 2020-11-11 12:13:00 - Added Artboard tests. diff --git a/lib/src/generated/rive_core_context.dart b/lib/src/generated/rive_core_context.dart index 385a04d..1286070 100644 --- a/lib/src/generated/rive_core_context.dart +++ b/lib/src/generated/rive_core_context.dart @@ -44,8 +44,10 @@ import 'package:rive/src/generated/shapes/parametric_path_base.dart'; import 'package:rive/src/generated/shapes/path_composer_base.dart'; import 'package:rive/src/generated/shapes/path_vertex_base.dart'; import 'package:rive/src/generated/shapes/points_path_base.dart'; +import 'package:rive/src/generated/shapes/polygon_base.dart'; import 'package:rive/src/generated/shapes/rectangle_base.dart'; import 'package:rive/src/generated/shapes/shape_base.dart'; +import 'package:rive/src/generated/shapes/star_base.dart'; import 'package:rive/src/generated/shapes/straight_vertex_base.dart'; import 'package:rive/src/generated/shapes/triangle_base.dart'; import 'package:rive/src/generated/transform_component_base.dart'; @@ -82,8 +84,10 @@ import 'package:rive/src/rive_core/shapes/paint/stroke.dart'; import 'package:rive/src/rive_core/shapes/paint/trim_path.dart'; import 'package:rive/src/rive_core/shapes/path_composer.dart'; import 'package:rive/src/rive_core/shapes/points_path.dart'; +import 'package:rive/src/rive_core/shapes/polygon.dart'; import 'package:rive/src/rive_core/shapes/rectangle.dart'; import 'package:rive/src/rive_core/shapes/shape.dart'; +import 'package:rive/src/rive_core/shapes/star.dart'; import 'package:rive/src/rive_core/shapes/straight_vertex.dart'; import 'package:rive/src/rive_core/shapes/triangle.dart'; @@ -147,6 +151,10 @@ class RiveCoreContext { return Ellipse(); case ClippingShapeBase.typeKey: return ClippingShape(); + case PolygonBase.typeKey: + return Polygon(); + case StarBase.typeKey: + return Star(); case PathComposerBase.typeKey: return PathComposer(); case CubicDetachedVertexBase.typeKey: @@ -540,6 +548,21 @@ class RiveCoreContext { object.isVisible = value; } break; + case PolygonBase.pointsPropertyKey: + if (object is PolygonBase && value is int) { + object.points = value; + } + break; + case PolygonBase.cornerRadiusPropertyKey: + if (object is PolygonBase && value is double) { + object.cornerRadius = value; + } + break; + case StarBase.innerRadiusPropertyKey: + if (object is StarBase && value is double) { + object.innerRadius = value; + } + break; case CubicDetachedVertexBase.inRotationPropertyKey: if (object is CubicDetachedVertexBase && value is double) { object.inRotation = value; @@ -715,6 +738,7 @@ class RiveCoreContext { case CubicWeightBase.outIndicesPropertyKey: case ClippingShapeBase.sourceIdPropertyKey: case ClippingShapeBase.fillRulePropertyKey: + case PolygonBase.pointsPropertyKey: case DrawRulesBase.drawTargetIdPropertyKey: case TendonBase.boneIdPropertyKey: return uintType; @@ -753,6 +777,8 @@ class RiveCoreContext { case RectangleBase.cornerRadiusPropertyKey: case CubicMirroredVertexBase.rotationPropertyKey: case CubicMirroredVertexBase.distancePropertyKey: + case PolygonBase.cornerRadiusPropertyKey: + case StarBase.innerRadiusPropertyKey: case CubicDetachedVertexBase.inRotationPropertyKey: case CubicDetachedVertexBase.inDistancePropertyKey: case CubicDetachedVertexBase.outRotationPropertyKey: @@ -860,6 +886,8 @@ class RiveCoreContext { return (object as ClippingShapeBase).sourceId; case ClippingShapeBase.fillRulePropertyKey: return (object as ClippingShapeBase).fillRule; + case PolygonBase.pointsPropertyKey: + return (object as PolygonBase).points; case DrawRulesBase.drawTargetIdPropertyKey: return (object as DrawRulesBase).drawTargetId; case TendonBase.boneIdPropertyKey: @@ -940,6 +968,10 @@ class RiveCoreContext { return (object as CubicMirroredVertexBase).rotation; case CubicMirroredVertexBase.distancePropertyKey: return (object as CubicMirroredVertexBase).distance; + case PolygonBase.cornerRadiusPropertyKey: + return (object as PolygonBase).cornerRadius; + case StarBase.innerRadiusPropertyKey: + return (object as StarBase).innerRadius; case CubicDetachedVertexBase.inRotationPropertyKey: return (object as CubicDetachedVertexBase).inRotation; case CubicDetachedVertexBase.inDistancePropertyKey: @@ -1116,6 +1148,9 @@ class RiveCoreContext { case ClippingShapeBase.fillRulePropertyKey: (object as ClippingShapeBase).fillRule = value; break; + case PolygonBase.pointsPropertyKey: + (object as PolygonBase).points = value; + break; case DrawRulesBase.drawTargetIdPropertyKey: (object as DrawRulesBase).drawTargetId = value; break; @@ -1232,6 +1267,12 @@ class RiveCoreContext { case CubicMirroredVertexBase.distancePropertyKey: (object as CubicMirroredVertexBase).distance = value; break; + case PolygonBase.cornerRadiusPropertyKey: + (object as PolygonBase).cornerRadius = value; + break; + case StarBase.innerRadiusPropertyKey: + (object as StarBase).innerRadius = value; + break; case CubicDetachedVertexBase.inRotationPropertyKey: (object as CubicDetachedVertexBase).inRotation = value; break; diff --git a/lib/src/generated/shapes/polygon_base.dart b/lib/src/generated/shapes/polygon_base.dart new file mode 100644 index 0000000..3f10c86 --- /dev/null +++ b/lib/src/generated/shapes/polygon_base.dart @@ -0,0 +1,69 @@ +/// Core automatically generated lib/src/generated/shapes/polygon_base.dart. +/// Do not modify manually. + +import 'package:rive/src/generated/component_base.dart'; +import 'package:rive/src/generated/container_component_base.dart'; +import 'package:rive/src/generated/node_base.dart'; +import 'package:rive/src/generated/shapes/parametric_path_base.dart'; +import 'package:rive/src/generated/shapes/path_base.dart'; +import 'package:rive/src/generated/transform_component_base.dart'; +import 'package:rive/src/rive_core/shapes/parametric_path.dart'; + +abstract class PolygonBase extends ParametricPath { + static const int typeKey = 51; + @override + int get coreType => PolygonBase.typeKey; + @override + Set get coreTypes => { + PolygonBase.typeKey, + ParametricPathBase.typeKey, + PathBase.typeKey, + NodeBase.typeKey, + TransformComponentBase.typeKey, + ContainerComponentBase.typeKey, + ComponentBase.typeKey + }; + + /// -------------------------------------------------------------------------- + /// Points field with key 125. + int _points = 5; + static const int pointsPropertyKey = 125; + + /// The number of points for the polygon. + int get points => _points; + + /// Change the [_points] field value. + /// [pointsChanged] will be invoked only if the field's value has changed. + set points(int value) { + if (_points == value) { + return; + } + int from = _points; + _points = value; + pointsChanged(from, value); + } + + void pointsChanged(int from, int to); + + /// -------------------------------------------------------------------------- + /// CornerRadius field with key 126. + double _cornerRadius = 0; + static const int cornerRadiusPropertyKey = 126; + + /// The corner radius. + double get cornerRadius => _cornerRadius; + + /// Change the [_cornerRadius] field value. + /// [cornerRadiusChanged] will be invoked only if the field's value has + /// changed. + set cornerRadius(double value) { + if (_cornerRadius == value) { + return; + } + double from = _cornerRadius; + _cornerRadius = value; + cornerRadiusChanged(from, value); + } + + void cornerRadiusChanged(double from, double to); +} diff --git a/lib/src/generated/shapes/star_base.dart b/lib/src/generated/shapes/star_base.dart new file mode 100644 index 0000000..746a257 --- /dev/null +++ b/lib/src/generated/shapes/star_base.dart @@ -0,0 +1,50 @@ +/// Core automatically generated lib/src/generated/shapes/star_base.dart. +/// Do not modify manually. + +import 'package:rive/src/generated/component_base.dart'; +import 'package:rive/src/generated/container_component_base.dart'; +import 'package:rive/src/generated/node_base.dart'; +import 'package:rive/src/generated/shapes/parametric_path_base.dart'; +import 'package:rive/src/generated/shapes/path_base.dart'; +import 'package:rive/src/generated/shapes/polygon_base.dart'; +import 'package:rive/src/generated/transform_component_base.dart'; +import 'package:rive/src/rive_core/shapes/polygon.dart'; + +abstract class StarBase extends Polygon { + static const int typeKey = 52; + @override + int get coreType => StarBase.typeKey; + @override + Set get coreTypes => { + StarBase.typeKey, + PolygonBase.typeKey, + ParametricPathBase.typeKey, + PathBase.typeKey, + NodeBase.typeKey, + TransformComponentBase.typeKey, + ContainerComponentBase.typeKey, + ComponentBase.typeKey + }; + + /// -------------------------------------------------------------------------- + /// InnerRadius field with key 127. + double _innerRadius = 0.5; + static const int innerRadiusPropertyKey = 127; + + /// Percentage of width/height to project inner points of the star. + double get innerRadius => _innerRadius; + + /// Change the [_innerRadius] field value. + /// [innerRadiusChanged] will be invoked only if the field's value has + /// changed. + set innerRadius(double value) { + if (_innerRadius == value) { + return; + } + double from = _innerRadius; + _innerRadius = value; + innerRadiusChanged(from, value); + } + + void innerRadiusChanged(double from, double to); +} diff --git a/lib/src/rive_core/shapes/paint/trim_path_drawing.dart b/lib/src/rive_core/shapes/paint/trim_path_drawing.dart index 680e281..14f0c21 100644 --- a/lib/src/rive_core/shapes/paint/trim_path_drawing.dart +++ b/lib/src/rive_core/shapes/paint/trim_path_drawing.dart @@ -1,94 +1,118 @@ +import 'dart:math'; import 'dart:ui'; -double _appendPathSegmentSequential(Iterator metricsIterator, - Path to, double offset, double start, double stop) { - double nextOffset = offset; - do { - PathMetric metric = metricsIterator.current; - nextOffset = offset + metric.length; +class _FirstExtractedPath { + final Path path; + double length; + final PathMetric metric; + _FirstExtractedPath(this.path, this.metric, this.length); +} + +_FirstExtractedPath _appendPathSegmentSequential( + Iterable metrics, Path result, double start, double stop, + {_FirstExtractedPath first}) { + double nextOffset = 0; + double offset = 0; + for (final metric in metrics) { + nextOffset += metric.length; if (start < nextOffset) { - Path extracted = metric.extractPath(start - offset, stop - offset); + var st = max(0.0, start - offset); + var et = min(metric.length, stop - offset); + var extractLength = et - st; + Path extracted = metric.extractPath(st, et); if (extracted != null) { - to.addPath(extracted, Offset.zero); + if (first == null) { + // ignore: parameter_assignments + first = _FirstExtractedPath(extracted, metric, extractLength); + } else if (first.metric == metric) { + first.length += extractLength; + first.path.extendWithPath(extracted, Offset.zero); + } else { + if (metric.isClosed && extractLength == metric.length) { + extracted.close(); + } + result.addPath(extracted, Offset.zero); + } } if (stop < nextOffset) { break; } } - // ignore: parameter_assignments offset = nextOffset; - } while (metricsIterator.moveNext()); - return offset; + } + return first; } void _appendPathSegmentSync( - PathMetric metric, Path to, double offset, double start, double stop) { - double nextOffset = offset + metric.length; + PathMetric metric, Path to, double start, double stop, + {bool startWithMoveTo = true}) { + double nextOffset = metric.length; if (start < nextOffset) { - Path extracted = metric.extractPath(start - offset, stop - offset); + Path extracted = metric.extractPath(start, stop); if (extracted != null) { - to.addPath(extracted, Offset.zero); + if (startWithMoveTo) { + to.addPath(extracted, Offset.zero); + } else { + to.extendWithPath(extracted, Offset.zero); + } } } } void _trimPathSequential( Path path, Path result, double startT, double stopT, bool complement) { - PathMetrics metrics = path.computeMetrics(); + var metrics = path.computeMetrics().toList(growable: false); double totalLength = 0.0; - for (final PathMetric metric in metrics) { + for (final metric in metrics) { totalLength += metric.length; } - metrics = path.computeMetrics(); double trimStart = totalLength * startT; double trimStop = totalLength * stopT; - double offset = 0.0; - Iterator metricsIterator = metrics.iterator; - metricsIterator.moveNext(); + _FirstExtractedPath first; if (complement) { - if (trimStart > 0.0) { - offset = _appendPathSegmentSequential( - metricsIterator, result, offset, 0.0, trimStart); - } if (trimStop < totalLength) { - offset = _appendPathSegmentSequential( - metricsIterator, result, offset, trimStop, totalLength); + first = + _appendPathSegmentSequential(metrics, result, trimStop, totalLength); } - } else { - if (trimStart < trimStop) { - offset = _appendPathSegmentSequential( - metricsIterator, result, offset, trimStart, trimStop); + if (trimStart > 0.0) { + _appendPathSegmentSequential(metrics, result, 0.0, trimStart, + first: first); } + } else if (trimStart < trimStop) { + first = _appendPathSegmentSequential(metrics, result, trimStart, trimStop); + } + if (first != null) { + if (first.length == first.metric.length) { + first.path.close(); + } + result.addPath(first.path, Offset.zero); } } void _trimPathSync( Path path, Path result, double startT, double stopT, bool complement) { - final PathMetrics metrics = path.computeMetrics(); - for (final PathMetric metric in metrics) { + final metrics = path.computeMetrics().toList(growable: false); + for (final metric in metrics) { double length = metric.length; double trimStart = length * startT; double trimStop = length * stopT; if (complement) { + bool extractStart = trimStop < length; + if (extractStart) { + _appendPathSegmentSync(metric, result, trimStop, length); + } if (trimStart > 0.0) { - _appendPathSegmentSync(metric, result, 0.0, 0.0, trimStart); - } - if (trimStop < length) { - _appendPathSegmentSync(metric, result, 0.0, trimStop, length); - } - } else { - if (trimStart < trimStop) { - _appendPathSegmentSync(metric, result, 0.0, trimStart, trimStop); + _appendPathSegmentSync(metric, result, 0.0, trimStart, + startWithMoveTo: !extractStart || !metric.isClosed); } + } else if (trimStart < trimStop) { + _appendPathSegmentSync(metric, result, trimStart, trimStop); } } } void updateTrimPath(Path path, Path result, double startT, double stopT, - bool complement, bool isSequential) { - if (isSequential) { - _trimPathSequential(path, result, startT, stopT, complement); - } else { - _trimPathSync(path, result, startT, stopT, complement); - } -} + bool complement, bool isSequential) => + isSequential + ? _trimPathSequential(path, result, startT, stopT, complement) + : _trimPathSync(path, result, startT, stopT, complement); diff --git a/lib/src/rive_core/shapes/polygon.dart b/lib/src/rive_core/shapes/polygon.dart new file mode 100644 index 0000000..41d2da2 --- /dev/null +++ b/lib/src/rive_core/shapes/polygon.dart @@ -0,0 +1,29 @@ +import 'dart:math'; +import 'package:rive/src/rive_core/shapes/path_vertex.dart'; +import 'package:rive/src/rive_core/bones/weight.dart'; +import 'package:rive/src/rive_core/shapes/straight_vertex.dart'; +import 'package:rive/src/generated/shapes/polygon_base.dart'; +export 'package:rive/src/generated/shapes/polygon_base.dart'; + +class Polygon extends PolygonBase { + @override + void cornerRadiusChanged(double from, double to) => markPathDirty(); + @override + void pointsChanged(int from, int to) => markPathDirty(); + @override + List> get vertices { + var vertexList = List(points); + var halfWidth = width / 2; + var halfHeight = height / 2; + var angle = -pi / 2; + var inc = 2 * pi / points; + for (int i = 0; i < points; i++) { + vertexList[i] = StraightVertex() + ..x = cos(angle) * halfWidth + ..y = sin(angle) * halfHeight + ..radius = cornerRadius; + angle += inc; + } + return vertexList; + } +} diff --git a/lib/src/rive_core/shapes/star.dart b/lib/src/rive_core/shapes/star.dart new file mode 100644 index 0000000..fad61c9 --- /dev/null +++ b/lib/src/rive_core/shapes/star.dart @@ -0,0 +1,35 @@ +import 'dart:math'; +import 'package:rive/src/rive_core/bones/weight.dart'; +import 'package:rive/src/rive_core/shapes/path_vertex.dart'; +import 'package:rive/src/rive_core/shapes/straight_vertex.dart'; +import 'package:rive/src/generated/shapes/star_base.dart'; +export 'package:rive/src/generated/shapes/star_base.dart'; + +class Star extends StarBase { + @override + void innerRadiusChanged(double from, double to) => markPathDirty(); + @override + List> get vertices { + var actualPoints = points * 2; + var vertexList = List(actualPoints); + var halfWidth = width / 2; + var halfHeight = height / 2; + var innerHalfWidth = width * innerRadius / 2; + var innerHalfHeight = height * innerRadius / 2; + var angle = -pi / 2; + var inc = 2 * pi / actualPoints; + for (int i = 0; i < actualPoints; i++) { + vertexList[i++] = StraightVertex() + ..x = cos(angle) * halfWidth + ..y = sin(angle) * halfHeight + ..radius = cornerRadius; + angle += inc; + vertexList[i] = StraightVertex() + ..x = cos(angle) * innerHalfWidth + ..y = sin(angle) * innerHalfHeight + ..radius = cornerRadius; + angle += inc; + } + return vertexList; + } +} diff --git a/pubspec.yaml b/pubspec.yaml index d092a75..8fe421a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: rive description: Rive 2 Flutter Runtime. This package provides runtime functionality for playing back and interacting with animations built with the Rive editor available at https://rive.app. -version: 0.6.3 +version: 0.6.4 repository: https://github.com/rive-app/rive-flutter homepage: https://rive.app