Adding new clipping.

This commit is contained in:
Luigi Rosso
2020-09-22 15:54:53 -07:00
parent d6729c2d84
commit 317f9ca68d
9 changed files with 118 additions and 114 deletions

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -34,7 +34,7 @@ class Shape extends ShapeBase with ShapePaintContainer {
}
void _markComposerDirty() {
_pathComposer?.addDirt(ComponentDirt.path);
_pathComposer?.addDirt(ComponentDirt.path, recurse: true);
invalidateStrokeEffects();
}

View File

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