Commit 10649ac4 authored by Tong Mu's avatar Tong Mu Committed by Flutter GitHub Bot

Fix TransformLayer with perspective transform (#49441)

parent 6a7fac22
...@@ -1655,9 +1655,8 @@ class TransformLayer extends OffsetLayer { ...@@ -1655,9 +1655,8 @@ class TransformLayer extends OffsetLayer {
} }
if (_invertedTransform == null) if (_invertedTransform == null)
return null; return null;
final Vector4 vector = Vector4(localPosition.dx, localPosition.dy, 0.0, 1.0);
final Vector4 result = _invertedTransform.transform(vector); return MatrixUtils.transformPoint(_invertedTransform, localPosition);
return Offset(result[0], result[1]);
} }
@override @override
......
...@@ -90,7 +90,7 @@ void main() { ...@@ -90,7 +90,7 @@ void main() {
}); });
test('ContainerLayer.findAllAnnotations returns children\'s opacity (true)', () { test('ContainerLayer.findAllAnnotations returns children\'s opacity (true)', () {
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
ContainerLayer(), ContainerLayer(),
children: <Object>[ children: <Object>[
...@@ -108,7 +108,7 @@ void main() { ...@@ -108,7 +108,7 @@ void main() {
}); });
test('ContainerLayer.findAllAnnotations returns children\'s opacity (false)', () { test('ContainerLayer.findAllAnnotations returns children\'s opacity (false)', () {
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
ContainerLayer(), ContainerLayer(),
children: <Object>[ children: <Object>[
...@@ -127,7 +127,7 @@ void main() { ...@@ -127,7 +127,7 @@ void main() {
}); });
test('ContainerLayer.findAllAnnotations returns false as opacity when finding nothing', () { test('ContainerLayer.findAllAnnotations returns false as opacity when finding nothing', () {
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
ContainerLayer(), ContainerLayer(),
children: <Object>[ children: <Object>[
...@@ -148,7 +148,7 @@ void main() { ...@@ -148,7 +148,7 @@ void main() {
const Offset insidePosition = Offset(-5, 5); const Offset insidePosition = Offset(-5, 5);
const Offset outsidePosition = Offset(5, 5); const Offset outsidePosition = Offset(5, 5);
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
OffsetLayer(offset: const Offset(-10, 0)), OffsetLayer(offset: const Offset(-10, 0)),
children: <Object>[ children: <Object>[
...@@ -175,7 +175,7 @@ void main() { ...@@ -175,7 +175,7 @@ void main() {
const Offset insidePosition = Offset(11, 11); const Offset insidePosition = Offset(11, 11);
const Offset outsidePosition = Offset(19, 19); const Offset outsidePosition = Offset(19, 19);
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
ClipRectLayer(clipRect: const Offset(10, 10) & const Size(5, 5)), ClipRectLayer(clipRect: const Offset(10, 10) & const Size(5, 5)),
children: <Object>[ children: <Object>[
...@@ -214,7 +214,7 @@ void main() { ...@@ -214,7 +214,7 @@ void main() {
const Offset insidePosition = Offset(12, 12); const Offset insidePosition = Offset(12, 12);
const Offset outsidePosition = Offset(11, 11); const Offset outsidePosition = Offset(11, 11);
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
ClipRRectLayer(clipRRect: rrect), ClipRRectLayer(clipRRect: rrect),
children: <Object>[ children: <Object>[
...@@ -258,7 +258,7 @@ void main() { ...@@ -258,7 +258,7 @@ void main() {
const Offset insidePosition = Offset(11, 11); const Offset insidePosition = Offset(11, 11);
const Offset outsidePosition = Offset(12, 12); const Offset outsidePosition = Offset(12, 12);
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
ClipPathLayer(clipPath: path), ClipPathLayer(clipPath: path),
children: <Object>[ children: <Object>[
...@@ -296,7 +296,7 @@ void main() { ...@@ -296,7 +296,7 @@ void main() {
const Offset insidePosition = Offset(40, 80); const Offset insidePosition = Offset(40, 80);
const Offset outsidePosition = Offset(20, 40); const Offset outsidePosition = Offset(20, 40);
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
TransformLayer(transform: transform), TransformLayer(transform: transform),
children: <Object>[ children: <Object>[
...@@ -324,10 +324,92 @@ void main() { ...@@ -324,10 +324,92 @@ void main() {
); );
}); });
test('TransformLayer.findAllAnnotations correctly transforms with perspective', () {
// Test the 4 corners of a transformed annotated region.
final Matrix4 transform = Matrix4.identity()
..setEntry(3, 2, 0.005)
..rotateX(-0.2)
..rotateY(0.2);
final Layer root = _withBackgroundAnnotation(0,
_Layers(
TransformLayer(transform: transform),
children: <Object>[
_TestAnnotatedLayer(
1,
opaque: true,
size: const Size(30, 40),
offset: const Offset(10, 20),
),
]
).build(),
);
void expectOneAnnotation({
@required Offset globalPosition,
@required int value,
@required Offset localPosition,
}) {
expect(
root.findAllAnnotations<int>(globalPosition).entries.toList(),
_equalToAnnotationResult<int>(
<AnnotationEntry<int>>[
AnnotationEntry<int>(annotation: value, localPosition: localPosition),
],
maxCoordinateRelativeDiff: 0.005,
),
);
}
expectOneAnnotation(
globalPosition: const Offset(10.0, 19.7),
value: 0,
localPosition: const Offset(10.0, 19.7),
);
expectOneAnnotation(
globalPosition: const Offset(10.1, 19.8),
value: 1,
localPosition: const Offset(10.0, 20.0),
);
expectOneAnnotation(
globalPosition: const Offset(10.5, 62.8),
value: 0,
localPosition: const Offset(10.5, 62.8),
);
expectOneAnnotation(
globalPosition: const Offset(10.6, 62.7),
value: 1,
localPosition: const Offset(10.1, 59.9),
);
expectOneAnnotation(
globalPosition: const Offset(42.6, 40.8),
value: 0,
localPosition: const Offset(42.6, 40.8),
);
expectOneAnnotation(
globalPosition: const Offset(42.5, 40.9),
value: 1,
localPosition: const Offset(39.9, 40.0),
);
expectOneAnnotation(
globalPosition: const Offset(43.5, 63.5),
value: 0,
localPosition: const Offset(43.5, 63.5),
);
expectOneAnnotation(
globalPosition: const Offset(43.4, 63.4),
value: 1,
localPosition: const Offset(39.9, 59.9),
);
});
test('TransformLayer.findAllAnnotations skips when transform is irreversible', () { test('TransformLayer.findAllAnnotations skips when transform is irreversible', () {
final Matrix4 transform = Matrix4.diagonal3Values(1, 0, 1); final Matrix4 transform = Matrix4.diagonal3Values(1, 0, 1);
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
TransformLayer(transform: transform), TransformLayer(transform: transform),
children: <Object>[ children: <Object>[
...@@ -360,7 +442,7 @@ void main() { ...@@ -360,7 +442,7 @@ void main() {
const Offset insidePosition = Offset(11, 11); const Offset insidePosition = Offset(11, 11);
const Offset outsidePosition = Offset(12, 12); const Offset outsidePosition = Offset(12, 12);
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
PhysicalModelLayer( PhysicalModelLayer(
clipPath: path, clipPath: path,
...@@ -398,7 +480,7 @@ void main() { ...@@ -398,7 +480,7 @@ void main() {
const Offset insidePosition = Offset(-5, 5); const Offset insidePosition = Offset(-5, 5);
const Offset outsidePosition = Offset(5, 5); const Offset outsidePosition = Offset(5, 5);
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
LeaderLayer( LeaderLayer(
link: LayerLink(), link: LayerLink(),
...@@ -428,7 +510,7 @@ void main() { ...@@ -428,7 +510,7 @@ void main() {
'and return the given opacity (false) during a successful hit', () { 'and return the given opacity (false) during a successful hit', () {
const Offset position = Offset(5, 5); const Offset position = Offset(5, 5);
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
AnnotatedRegionLayer<int>(1, opaque: false), AnnotatedRegionLayer<int>(1, opaque: false),
children: <Object>[ children: <Object>[
...@@ -451,7 +533,7 @@ void main() { ...@@ -451,7 +533,7 @@ void main() {
'and return the given opacity (true) during a successful hit', () { 'and return the given opacity (true) during a successful hit', () {
const Offset position = Offset(5, 5); const Offset position = Offset(5, 5);
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
AnnotatedRegionLayer<int>(1, opaque: true), AnnotatedRegionLayer<int>(1, opaque: true),
children: <Object>[ children: <Object>[
...@@ -472,7 +554,7 @@ void main() { ...@@ -472,7 +554,7 @@ void main() {
test('AnnotatedRegionLayer.findAllAnnotations has default opacity as false', () { test('AnnotatedRegionLayer.findAllAnnotations has default opacity as false', () {
const Offset position = Offset(5, 5); const Offset position = Offset(5, 5);
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
AnnotatedRegionLayer<int>(1), AnnotatedRegionLayer<int>(1),
children: <Object>[ children: <Object>[
...@@ -495,7 +577,7 @@ void main() { ...@@ -495,7 +577,7 @@ void main() {
'children\'s opacity (false) during a failed hit', () { 'children\'s opacity (false) during a failed hit', () {
const Offset position = Offset(5, 5); const Offset position = Offset(5, 5);
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
AnnotatedRegionLayer<int>(1, opaque: true, size: Size.zero), AnnotatedRegionLayer<int>(1, opaque: true, size: Size.zero),
children: <Object>[ children: <Object>[
...@@ -517,7 +599,7 @@ void main() { ...@@ -517,7 +599,7 @@ void main() {
'children\'s opacity (true) during a failed hit', () { 'children\'s opacity (true) during a failed hit', () {
const Offset position = Offset(5, 5); const Offset position = Offset(5, 5);
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
AnnotatedRegionLayer<int>(1, opaque: false, size: Size.zero), AnnotatedRegionLayer<int>(1, opaque: false, size: Size.zero),
children: <Object>[ children: <Object>[
...@@ -538,7 +620,7 @@ void main() { ...@@ -538,7 +620,7 @@ void main() {
'during a successful hit if it is not opaque', () { 'during a successful hit if it is not opaque', () {
const Offset position = Offset(5, 5); const Offset position = Offset(5, 5);
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
AnnotatedRegionLayer<int>(1, opaque: false), AnnotatedRegionLayer<int>(1, opaque: false),
children: <Object>[ children: <Object>[
...@@ -561,7 +643,7 @@ void main() { ...@@ -561,7 +643,7 @@ void main() {
'during a successful hit if it is opaque', () { 'during a successful hit if it is opaque', () {
const Offset position = Offset(5, 5); const Offset position = Offset(5, 5);
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
AnnotatedRegionLayer<int>(1, opaque: true), AnnotatedRegionLayer<int>(1, opaque: true),
children: <Object>[ children: <Object>[
...@@ -584,7 +666,7 @@ void main() { ...@@ -584,7 +666,7 @@ void main() {
// The target position would have fallen outside if not for the offset. // The target position would have fallen outside if not for the offset.
const Offset position = Offset(100, 100); const Offset position = Offset(100, 100);
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
AnnotatedRegionLayer<int>( AnnotatedRegionLayer<int>(
1, 1,
...@@ -619,7 +701,7 @@ void main() { ...@@ -619,7 +701,7 @@ void main() {
// The target position would have fallen inside if not for the offset. // The target position would have fallen inside if not for the offset.
const Offset position = Offset(10, 10); const Offset position = Offset(10, 10);
final Layer root = _appendAnnotationIfNotOpaque(1000, final Layer root = _withBackgroundAnnotation(1000,
_Layers( _Layers(
AnnotatedRegionLayer<int>( AnnotatedRegionLayer<int>(
1, 1,
...@@ -642,14 +724,12 @@ void main() { ...@@ -642,14 +724,12 @@ void main() {
}); });
} }
/// Append `value` to the result of the annotations test of `layer` if and only /// A [ContainerLayer] that contains a stack of layers: `layer` in the front,
/// if it is opaque at the given location. /// and another layer annotated with `value` in the back.
/// ///
/// It is a utility function that helps checking the opacity returned by /// It is a utility function that helps checking the opacity returned by
/// [Layer.findAnnotations]. /// [Layer.findAnnotations].
/// Technically it is a [ContainerLayer] that contains `layer` followed by Layer _withBackgroundAnnotation(int value, Layer layer) {
/// another layer annotated with `value`.
Layer _appendAnnotationIfNotOpaque(int value, Layer layer) {
return _Layers( return _Layers(
ContainerLayer(), ContainerLayer(),
children: <Object>[ children: <Object>[
...@@ -746,11 +826,22 @@ class _TestAnnotatedLayer extends Layer { ...@@ -746,11 +826,22 @@ class _TestAnnotatedLayer extends Layer {
} }
} }
Matcher _equalToAnnotationResult<T>(List<AnnotationEntry<int>> list) { bool _almostEqual(double a, double b, double maxRelativeDiff) {
assert(maxRelativeDiff >= 0);
assert(maxRelativeDiff < 1);
return (a - b).abs() <= a.abs() * maxRelativeDiff;
}
Matcher _equalToAnnotationResult<T>(
List<AnnotationEntry<int>> list, {
double maxCoordinateRelativeDiff = 0,
}) {
return pairwiseCompare<AnnotationEntry<int>, AnnotationEntry<int>>( return pairwiseCompare<AnnotationEntry<int>, AnnotationEntry<int>>(
list, list,
(AnnotationEntry<int> a, AnnotationEntry<int> b) { (AnnotationEntry<int> a, AnnotationEntry<int> b) {
return a.annotation == b.annotation && a.localPosition == b.localPosition; return a.annotation == b.annotation
&& _almostEqual(a.localPosition.dx, b.localPosition.dx, maxCoordinateRelativeDiff)
&& _almostEqual(a.localPosition.dy, b.localPosition.dy, maxCoordinateRelativeDiff);
}, },
'equal to', 'equal to',
); );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment