Adding support for stars, shapes, and trim path fixes.

This commit is contained in:
Luigi Rosso
2020-12-11 15:41:32 -08:00
parent 7887a1133c
commit 46cfa4384a
8 changed files with 308 additions and 49 deletions

View File

@ -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.

View File

@ -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;

View File

@ -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<int> 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);
}

View File

@ -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<int> 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);
}

View File

@ -1,94 +1,118 @@
import 'dart:math';
import 'dart:ui';
double _appendPathSegmentSequential(Iterator<PathMetric> 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<PathMetric> 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<PathMetric> 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);

View File

@ -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<PathVertex<Weight>> get vertices {
var vertexList = List<StraightVertex>(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;
}
}

View File

@ -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<PathVertex<Weight>> get vertices {
var actualPoints = points * 2;
var vertexList = List<StraightVertex>(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;
}
}

View File

@ -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