mirror of
https://github.com/rive-app/rive-flutter.git
synced 2025-05-17 13:26:03 +08:00
Adding new clipping.
This commit is contained in:
Binary file not shown.
@ -515,14 +515,14 @@ class RiveCoreContext {
|
||||
object.distance = value;
|
||||
}
|
||||
break;
|
||||
case ClippingShapeBase.shapeIdPropertyKey:
|
||||
case ClippingShapeBase.sourceIdPropertyKey:
|
||||
if (object is ClippingShapeBase && value is int) {
|
||||
object.shapeId = value;
|
||||
object.sourceId = value;
|
||||
}
|
||||
break;
|
||||
case ClippingShapeBase.clipOpValuePropertyKey:
|
||||
case ClippingShapeBase.fillRulePropertyKey:
|
||||
if (object is ClippingShapeBase && value is int) {
|
||||
object.clipOpValue = value;
|
||||
object.fillRule = value;
|
||||
}
|
||||
break;
|
||||
case ClippingShapeBase.isVisiblePropertyKey:
|
||||
@ -703,8 +703,8 @@ class RiveCoreContext {
|
||||
case CubicWeightBase.inIndicesPropertyKey:
|
||||
case CubicWeightBase.outValuesPropertyKey:
|
||||
case CubicWeightBase.outIndicesPropertyKey:
|
||||
case ClippingShapeBase.shapeIdPropertyKey:
|
||||
case ClippingShapeBase.clipOpValuePropertyKey:
|
||||
case ClippingShapeBase.sourceIdPropertyKey:
|
||||
case ClippingShapeBase.fillRulePropertyKey:
|
||||
case DrawRulesBase.drawTargetIdPropertyKey:
|
||||
case TendonBase.boneIdPropertyKey:
|
||||
return uintType;
|
||||
@ -844,10 +844,10 @@ class RiveCoreContext {
|
||||
return (object as CubicWeightBase).outValues;
|
||||
case CubicWeightBase.outIndicesPropertyKey:
|
||||
return (object as CubicWeightBase).outIndices;
|
||||
case ClippingShapeBase.shapeIdPropertyKey:
|
||||
return (object as ClippingShapeBase).shapeId;
|
||||
case ClippingShapeBase.clipOpValuePropertyKey:
|
||||
return (object as ClippingShapeBase).clipOpValue;
|
||||
case ClippingShapeBase.sourceIdPropertyKey:
|
||||
return (object as ClippingShapeBase).sourceId;
|
||||
case ClippingShapeBase.fillRulePropertyKey:
|
||||
return (object as ClippingShapeBase).fillRule;
|
||||
case DrawRulesBase.drawTargetIdPropertyKey:
|
||||
return (object as DrawRulesBase).drawTargetId;
|
||||
case TendonBase.boneIdPropertyKey:
|
||||
@ -1094,11 +1094,11 @@ class RiveCoreContext {
|
||||
case CubicWeightBase.outIndicesPropertyKey:
|
||||
(object as CubicWeightBase).outIndices = value;
|
||||
break;
|
||||
case ClippingShapeBase.shapeIdPropertyKey:
|
||||
(object as ClippingShapeBase).shapeId = value;
|
||||
case ClippingShapeBase.sourceIdPropertyKey:
|
||||
(object as ClippingShapeBase).sourceId = value;
|
||||
break;
|
||||
case ClippingShapeBase.clipOpValuePropertyKey:
|
||||
(object as ClippingShapeBase).clipOpValue = value;
|
||||
case ClippingShapeBase.fillRulePropertyKey:
|
||||
(object as ClippingShapeBase).fillRule = value;
|
||||
break;
|
||||
case DrawRulesBase.drawTargetIdPropertyKey:
|
||||
(object as DrawRulesBase).drawTargetId = value;
|
||||
|
@ -13,48 +13,46 @@ abstract class ClippingShapeBase extends Component {
|
||||
Set<int> get coreTypes => {ClippingShapeBase.typeKey, ComponentBase.typeKey};
|
||||
|
||||
/// --------------------------------------------------------------------------
|
||||
/// ShapeId field with key 92.
|
||||
int _shapeId;
|
||||
static const int shapeIdPropertyKey = 92;
|
||||
/// SourceId field with key 92.
|
||||
int _sourceId;
|
||||
static const int sourceIdPropertyKey = 92;
|
||||
|
||||
/// Identifier used to track the shape to use as a clipping source.
|
||||
int get shapeId => _shapeId;
|
||||
/// Identifier used to track the node to use as a clipping source.
|
||||
int get sourceId => _sourceId;
|
||||
|
||||
/// Change the [_shapeId] field value.
|
||||
/// [shapeIdChanged] will be invoked only if the field's value has changed.
|
||||
set shapeId(int value) {
|
||||
if (_shapeId == value) {
|
||||
/// Change the [_sourceId] field value.
|
||||
/// [sourceIdChanged] will be invoked only if the field's value has changed.
|
||||
set sourceId(int value) {
|
||||
if (_sourceId == value) {
|
||||
return;
|
||||
}
|
||||
int from = _shapeId;
|
||||
_shapeId = value;
|
||||
shapeIdChanged(from, value);
|
||||
int from = _sourceId;
|
||||
_sourceId = value;
|
||||
sourceIdChanged(from, value);
|
||||
}
|
||||
|
||||
void shapeIdChanged(int from, int to);
|
||||
void sourceIdChanged(int from, int to);
|
||||
|
||||
/// --------------------------------------------------------------------------
|
||||
/// ClipOpValue field with key 93.
|
||||
int _clipOpValue = 0;
|
||||
static const int clipOpValuePropertyKey = 93;
|
||||
/// FillRule field with key 93.
|
||||
int _fillRule = 0;
|
||||
static const int fillRulePropertyKey = 93;
|
||||
|
||||
/// Backing enum value for the clipping operation type (intersection or
|
||||
/// difference).
|
||||
int get clipOpValue => _clipOpValue;
|
||||
/// Backing enum value for the clipping fill rule (nonZero or evenOdd).
|
||||
int get fillRule => _fillRule;
|
||||
|
||||
/// Change the [_clipOpValue] field value.
|
||||
/// [clipOpValueChanged] will be invoked only if the field's value has
|
||||
/// changed.
|
||||
set clipOpValue(int value) {
|
||||
if (_clipOpValue == value) {
|
||||
/// Change the [_fillRule] field value.
|
||||
/// [fillRuleChanged] will be invoked only if the field's value has changed.
|
||||
set fillRule(int value) {
|
||||
if (_fillRule == value) {
|
||||
return;
|
||||
}
|
||||
int from = _clipOpValue;
|
||||
_clipOpValue = value;
|
||||
clipOpValueChanged(from, value);
|
||||
int from = _fillRule;
|
||||
_fillRule = value;
|
||||
fillRuleChanged(from, value);
|
||||
}
|
||||
|
||||
void clipOpValueChanged(int from, int to);
|
||||
void fillRuleChanged(int from, int to);
|
||||
|
||||
/// --------------------------------------------------------------------------
|
||||
/// IsVisible field with key 94.
|
||||
|
@ -241,25 +241,25 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
||||
_drawables.clear();
|
||||
_rules.clear();
|
||||
buildDrawOrder(_drawables, null, _rules);
|
||||
Set<DrawTarget> rootRules = {};
|
||||
var root = DrawTarget();
|
||||
for (final nodeRules in _rules) {
|
||||
for (final target in nodeRules.targets) {
|
||||
target.dependents.clear();
|
||||
}
|
||||
}
|
||||
for (final nodeRules in _rules) {
|
||||
for (final target in nodeRules.targets) {
|
||||
root.dependents.add(target);
|
||||
var dependentRules = target.drawable?.flattenedDrawRules;
|
||||
if (dependentRules != null) {
|
||||
for (final dependentRule in dependentRules.targets) {
|
||||
dependentRule.dependents.add(target);
|
||||
}
|
||||
} else {
|
||||
rootRules.add(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
var sorter = DependencySorter<Component>();
|
||||
sorter.reset();
|
||||
if (rootRules.isNotEmpty) {
|
||||
rootRules.forEach(sorter.visit);
|
||||
}
|
||||
_sortedDrawRules = sorter.order.cast<DrawTarget>();
|
||||
_sortedDrawRules = sorter.sort(root).cast<DrawTarget>().skip(1).toList();
|
||||
sortDrawOrder();
|
||||
}
|
||||
|
||||
|
@ -34,27 +34,7 @@ abstract class Drawable extends DrawableBase {
|
||||
if (!clip.isVisible) {
|
||||
continue;
|
||||
}
|
||||
var shape = clip.shape;
|
||||
var fillInWorld = shape.fillInWorld;
|
||||
if (!fillInWorld) {
|
||||
canvas.transform(shape.worldTransform.mat4);
|
||||
}
|
||||
if (clip.clipOp == ClipOp.difference) {
|
||||
var path = Path();
|
||||
path.fillType = PathFillType.evenOdd;
|
||||
path.addPath(artboard.path, Offset.zero);
|
||||
path.addPath(clip.shape.fillPath, Offset.zero);
|
||||
canvas.clipPath(path);
|
||||
} else {
|
||||
canvas.clipPath(clip.shape.fillPath);
|
||||
}
|
||||
if (!fillInWorld) {
|
||||
assert(
|
||||
clip.shapeInverseWorld != null,
|
||||
'Expect shapeInverseWorld to have been '
|
||||
'created by the time we draw');
|
||||
canvas.transform(clip.shapeInverseWorld.mat4);
|
||||
}
|
||||
canvas.clipPath(clip.clippingPath);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1,62 +1,83 @@
|
||||
import 'dart:ui';
|
||||
import 'package:rive/src/rive_core/component_dirt.dart';
|
||||
import 'package:rive/src/rive_core/math/mat2d.dart';
|
||||
import 'package:rive/src/rive_core/node.dart';
|
||||
import 'package:rive/src/rive_core/shapes/shape.dart';
|
||||
import 'package:rive/src/generated/shapes/clipping_shape_base.dart';
|
||||
export 'package:rive/src/generated/shapes/clipping_shape_base.dart';
|
||||
|
||||
enum ClipOp { intersection, difference }
|
||||
|
||||
class ClippingShape extends ClippingShapeBase {
|
||||
ClipOp get clipOp => ClipOp.values[clipOpValue];
|
||||
set clipOp(ClipOp value) => clipOpValue = value.index;
|
||||
Mat2D _shapeInverseWorld;
|
||||
Mat2D get shapeInverseWorld => _shapeInverseWorld;
|
||||
Shape _shape;
|
||||
Shape get shape => _shape;
|
||||
set shape(Shape value) {
|
||||
if (_shape == value) {
|
||||
final Path clippingPath = Path();
|
||||
final List<Shape> _shapes = [];
|
||||
PathFillType get fillType => PathFillType.values[fillRule];
|
||||
set fillType(PathFillType type) => fillRule = type.index;
|
||||
Node _source;
|
||||
Node get source => _source;
|
||||
set source(Node value) {
|
||||
if (_source == value) {
|
||||
return;
|
||||
}
|
||||
_shape = value;
|
||||
shapeId = value?.id;
|
||||
_source = value;
|
||||
sourceId = value?.id;
|
||||
}
|
||||
|
||||
@override
|
||||
void clipOpValueChanged(int from, int to) {
|
||||
void fillRuleChanged(int from, int to) {
|
||||
parent?.addDirt(ComponentDirt.clip, recurse: true);
|
||||
addDirt(ComponentDirt.path);
|
||||
}
|
||||
|
||||
@override
|
||||
void shapeIdChanged(int from, int to) {
|
||||
shape = context?.resolve(to);
|
||||
void sourceIdChanged(int from, int to) {
|
||||
_source = context?.resolve(to);
|
||||
}
|
||||
|
||||
@override
|
||||
void onAddedDirty() {
|
||||
super.onAddedDirty();
|
||||
if (shapeId != null) {
|
||||
shape = context?.resolve(shapeId);
|
||||
if (sourceId != null) {
|
||||
_source = context?.resolve(sourceId);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void buildDependencies() {
|
||||
super.buildDependencies();
|
||||
shape?.addDependent(this);
|
||||
_shapes.clear();
|
||||
_source?.forAll((component) {
|
||||
if (component is Shape) {
|
||||
_shapes.add(component);
|
||||
component.pathComposer.addDependent(this);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
addDirt(ComponentDirt.path);
|
||||
}
|
||||
|
||||
@override
|
||||
void onRemoved() {
|
||||
super.onRemoved();
|
||||
_shapes.clear();
|
||||
}
|
||||
|
||||
@override
|
||||
void update(int dirt) {
|
||||
if (dirt & ComponentDirt.worldTransform != 0 &&
|
||||
shape != null &&
|
||||
!shape.fillInWorld) {
|
||||
_shapeInverseWorld ??= Mat2D();
|
||||
Mat2D.invert(_shapeInverseWorld, shape.worldTransform);
|
||||
if (dirt & (ComponentDirt.worldTransform | ComponentDirt.path) != 0 &&
|
||||
source != null) {
|
||||
clippingPath.reset();
|
||||
clippingPath.fillType = fillType;
|
||||
for (final shape in _shapes) {
|
||||
if (!shape.fillInWorld) {
|
||||
clippingPath.addPath(shape.fillPath, Offset.zero,
|
||||
matrix4: shape.worldTransform.mat4);
|
||||
} else {
|
||||
clippingPath.addPath(shape.fillPath, Offset.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void isVisibleChanged(bool from, bool to) {
|
||||
_shape?.addDirt(ComponentDirt.paint);
|
||||
_source?.addDirt(ComponentDirt.paint);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:ui';
|
||||
import 'package:rive/src/rive_core/component_dirt.dart';
|
||||
import 'package:rive/src/rive_core/shapes/shape_paint_container.dart';
|
||||
import 'package:rive/src/generated/shapes/paint/fill_base.dart';
|
||||
|
@ -34,7 +34,7 @@ class Shape extends ShapeBase with ShapePaintContainer {
|
||||
}
|
||||
|
||||
void _markComposerDirty() {
|
||||
_pathComposer?.addDirt(ComponentDirt.path);
|
||||
_pathComposer?.addDirt(ComponentDirt.path, recurse: true);
|
||||
invalidateStrokeEffects();
|
||||
}
|
||||
|
||||
|
@ -83,22 +83,7 @@ class TarjansDependencySorter<T extends DependencyGraphNode<T>>
|
||||
|
||||
if (!visit(root)) {
|
||||
// if we detect cycles, go find them all
|
||||
_perm.clear();
|
||||
_temp.clear();
|
||||
_cycleNodes.clear();
|
||||
_order.clear();
|
||||
|
||||
var cycles =
|
||||
stronglyConnectedComponents<T>([root], (T node) => node.dependents);
|
||||
|
||||
cycles.forEach((cycle) {
|
||||
// cycles of len 1 are not cycles.
|
||||
if (cycle.length > 1) {
|
||||
cycle.forEach((cycleMember) {
|
||||
_cycleNodes.add(cycleMember);
|
||||
});
|
||||
}
|
||||
});
|
||||
findCycles(root);
|
||||
|
||||
// revisit the tree, skipping nodes on any cycle.
|
||||
visit(root);
|
||||
@ -107,6 +92,26 @@ class TarjansDependencySorter<T extends DependencyGraphNode<T>>
|
||||
return _order;
|
||||
}
|
||||
|
||||
HashSet<T> findCycles(T n) {
|
||||
_perm.clear();
|
||||
_temp.clear();
|
||||
_cycleNodes.clear();
|
||||
_order.clear();
|
||||
|
||||
var cycles =
|
||||
stronglyConnectedComponents<T>([n], (T node) => node.dependents);
|
||||
|
||||
cycles.forEach((cycle) {
|
||||
// cycles of len 1 are not cycles.
|
||||
if (cycle.length > 1) {
|
||||
cycle.forEach((cycleMember) {
|
||||
_cycleNodes.add(cycleMember);
|
||||
});
|
||||
}
|
||||
});
|
||||
return _cycleNodes;
|
||||
}
|
||||
|
||||
@override
|
||||
bool visit(T n) {
|
||||
if (cycleNodes.contains(n)) {
|
||||
|
Reference in New Issue
Block a user