Commit e01592a0 authored by Ian Hickson's avatar Ian Hickson Committed by Adam Barth

Fix globalToLocal and update spinning_mixed (#6035)

* globalToLocal was just broken when there was a rotation and a
  translation at the same time. This fixes that and adds a test.

* update graphic used by spinning_mixed since the old one went 404.

* simplify some of the code in the demo.

* fix MatrixUtils.transformPoint to be consistent with how we transform
  points elsewhere.

* stop transforming points elsewhere, just use
  MatrixUtils.transformPoint.

* make the Widget binding handle not having a root element.

* make the spinning_mixed demo update its widget tree.
parent 23b4318e
...@@ -51,7 +51,7 @@ void attachWidgetTreeToRenderTree(RenderProxyBox container) { ...@@ -51,7 +51,7 @@ void attachWidgetTreeToRenderTree(RenderProxyBox container) {
new RaisedButton( new RaisedButton(
child: new Row( child: new Row(
children: <Widget>[ children: <Widget>[
new Image.network('http://flutter.io/favicon.ico'), new Image.network('https://flutter.io/images/favicon.png'),
new Text('PRESS ME'), new Text('PRESS ME'),
] ]
), ),
...@@ -83,9 +83,9 @@ void rotate(Duration timeStamp) { ...@@ -83,9 +83,9 @@ void rotate(Duration timeStamp) {
double delta = (timeStamp - timeBase).inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND; // radians double delta = (timeStamp - timeBase).inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND; // radians
transformBox.setIdentity(); transformBox.setIdentity();
transformBox.translate(transformBox.size.width / 2.0, transformBox.size.height / 2.0);
transformBox.rotateZ(delta); transformBox.rotateZ(delta);
transformBox.translate(-transformBox.size.width / 2.0, -transformBox.size.height / 2.0);
owner.buildScope(element);
} }
void main() { void main() {
...@@ -98,7 +98,7 @@ void main() { ...@@ -98,7 +98,7 @@ void main() {
flexRoot.add(proxy); flexRoot.add(proxy);
addFlexChildSolidColor(flexRoot, const Color(0xFF0000FF), flex: 1); addFlexChildSolidColor(flexRoot, const Color(0xFF0000FF), flex: 1);
transformBox = new RenderTransform(child: flexRoot, transform: new Matrix4.identity()); transformBox = new RenderTransform(child: flexRoot, transform: new Matrix4.identity(), alignment: FractionalOffset.center);
RenderPadding root = new RenderPadding(padding: new EdgeInsets.all(80.0), child: transformBox); RenderPadding root = new RenderPadding(padding: new EdgeInsets.all(80.0), child: transformBox);
binding.renderView.child = root; binding.renderView.child = root;
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:ui' as ui show lerpDouble; import 'dart:ui' as ui show lerpDouble;
import 'package:flutter/foundation.dart';
import 'package:flutter/physics.dart'; import 'package:flutter/physics.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
...@@ -184,6 +185,16 @@ class AnimationController extends Animation<double> ...@@ -184,6 +185,16 @@ class AnimationController extends Animation<double>
/// ///
/// Returns a [Future] that completes when the animation is complete. /// Returns a [Future] that completes when the animation is complete.
Future<Null> forward({ double from }) { Future<Null> forward({ double from }) {
assert(() {
if (duration == null) {
throw new FlutterError(
'AnimationController.forward() called with no default Duration.\n'
'The "duration" property should be set, either in the constructor or later, before '
'calling the forward() function.'
);
}
return true;
});
_direction = _AnimationDirection.forward; _direction = _AnimationDirection.forward;
if (from != null) if (from != null)
value = from; value = from;
...@@ -194,6 +205,16 @@ class AnimationController extends Animation<double> ...@@ -194,6 +205,16 @@ class AnimationController extends Animation<double>
/// ///
/// Returns a [Future] that completes when the animation is complete. /// Returns a [Future] that completes when the animation is complete.
Future<Null> reverse({ double from }) { Future<Null> reverse({ double from }) {
assert(() {
if (duration == null) {
throw new FlutterError(
'AnimationController.reverse() called with no default Duration.\n'
'The "duration" property should be set, either in the constructor or later, before '
'calling the reverse() function.'
);
}
return true;
});
_direction = _AnimationDirection.reverse; _direction = _AnimationDirection.reverse;
if (from != null) if (from != null)
value = from; value = from;
...@@ -206,7 +227,17 @@ class AnimationController extends Animation<double> ...@@ -206,7 +227,17 @@ class AnimationController extends Animation<double>
Future<Null> animateTo(double target, { Duration duration, Curve curve: Curves.linear }) { Future<Null> animateTo(double target, { Duration duration, Curve curve: Curves.linear }) {
Duration simulationDuration = duration; Duration simulationDuration = duration;
if (simulationDuration == null) { if (simulationDuration == null) {
assert(this.duration != null); assert(() {
if (this.duration == null) {
throw new FlutterError(
'AnimationController.animateTo() called with no explicit Duration and no default Duration.\n'
'Either the "duration" argument to the animateTo() method should be provided, or the '
'"duration" property should be set, either in the constructor or later, before '
'calling the animateTo() function.'
);
}
return true;
});
double range = upperBound - lowerBound; double range = upperBound - lowerBound;
double remainingFraction = range.isFinite ? (target - _value).abs() / range : 1.0; double remainingFraction = range.isFinite ? (target - _value).abs() / range : 1.0;
simulationDuration = this.duration * remainingFraction; simulationDuration = this.duration * remainingFraction;
...@@ -233,6 +264,17 @@ class AnimationController extends Animation<double> ...@@ -233,6 +264,17 @@ class AnimationController extends Animation<double>
min ??= lowerBound; min ??= lowerBound;
max ??= upperBound; max ??= upperBound;
period ??= duration; period ??= duration;
assert(() {
if (duration == null) {
throw new FlutterError(
'AnimationController.repeat() called with no explicit Duration and default Duration.\n'
'Either the "duration" argument to the repeat() method should be provided, or the '
'"duration" property should be set, either in the constructor or later, before '
'calling the repeat() function.'
);
}
return true;
});
return animateWith(new _RepeatingSimulation(min, max, period)); return animateWith(new _RepeatingSimulation(min, max, period));
} }
......
...@@ -16,24 +16,24 @@ class MatrixUtils { ...@@ -16,24 +16,24 @@ class MatrixUtils {
/// Returns the given [transform] matrix as Offset, if the matrix is nothing /// Returns the given [transform] matrix as Offset, if the matrix is nothing
/// but a 2D translation. /// but a 2D translation.
/// ///
/// Returns null, otherwise. /// Otherwise, returns null.
static Offset getAsTranslation(Matrix4 transform) { static Offset getAsTranslation(Matrix4 transform) {
assert(transform != null); assert(transform != null);
Float64List values = transform.storage; Float64List values = transform.storage;
// Values are stored in column-major order. // Values are stored in column-major order.
if (values[0] == 1.0 && if (values[0] == 1.0 && // col 1
values[1] == 0.0 && values[1] == 0.0 &&
values[2] == 0.0 && values[2] == 0.0 &&
values[3] == 0.0 && values[3] == 0.0 &&
values[4] == 0.0 && values[4] == 0.0 && // col 2
values[5] == 1.0 && values[5] == 1.0 &&
values[6] == 0.0 && values[6] == 0.0 &&
values[7] == 0.0 && values[7] == 0.0 &&
values[8] == 0.0 && values[8] == 0.0 && // col 3
values[9] == 0.0 && values[9] == 0.0 &&
values[10] == 1.0 && values[10] == 1.0 &&
values[11] == 0.0 && values[11] == 0.0 &&
values[14] == 0.0 && values[14] == 0.0 && // bottom of col 4 (values 12 and 13 are the x and y offsets)
values[15] == 1.0) { values[15] == 1.0) {
return new Offset(values[12], values[13]); return new Offset(values[12], values[13]);
} }
......
...@@ -1339,7 +1339,8 @@ abstract class RenderBox extends RenderObject { ...@@ -1339,7 +1339,8 @@ abstract class RenderBox extends RenderObject {
/// function to factor those transforms into the calculation. /// function to factor those transforms into the calculation.
/// ///
/// The RenderBox implementation takes care of adjusting the matrix for the /// The RenderBox implementation takes care of adjusting the matrix for the
/// position of the given child. /// position of the given child as determined during layout and stored on the
/// child's [parentData] in the [BoxParentData.offset] field.
@override @override
void applyPaintTransform(RenderObject child, Matrix4 transform) { void applyPaintTransform(RenderObject child, Matrix4 transform) {
assert(child.parent == this); assert(child.parent == this);
...@@ -1348,20 +1349,24 @@ abstract class RenderBox extends RenderObject { ...@@ -1348,20 +1349,24 @@ abstract class RenderBox extends RenderObject {
transform.translate(offset.dx, offset.dy); transform.translate(offset.dx, offset.dy);
} }
Matrix4 _collectPaintTransform() {
assert(attached);
final List<RenderObject> renderers = <RenderObject>[];
for (RenderObject renderer = this; renderer != null; renderer = renderer.parent)
renderers.add(renderer);
final Matrix4 transform = new Matrix4.identity();
for (int index = renderers.length - 1; index > 0; index -= 1)
renderers[index].applyPaintTransform(renderers[index - 1], transform);
return transform;
}
/// Convert the given point from the global coodinate system to the local /// Convert the given point from the global coodinate system to the local
/// coordinate system for this box. /// coordinate system for this box.
/// ///
/// If the transform from global coordinates to local coordinates is /// If the transform from global coordinates to local coordinates is
/// degenerate, this function returns Point.origin. /// degenerate, this function returns Point.origin.
Point globalToLocal(Point point) { Point globalToLocal(Point point) {
assert(attached); final Matrix4 transform = _collectPaintTransform();
Matrix4 transform = new Matrix4.identity();
RenderObject renderer = this;
while (renderer.parent is RenderObject) {
RenderObject rendererParent = renderer.parent;
rendererParent.applyPaintTransform(renderer, transform);
renderer = rendererParent;
}
double det = transform.invert(); double det = transform.invert();
if (det == 0.0) if (det == 0.0)
return Point.origin; return Point.origin;
...@@ -1371,13 +1376,7 @@ abstract class RenderBox extends RenderObject { ...@@ -1371,13 +1376,7 @@ abstract class RenderBox extends RenderObject {
/// Convert the given point from the local coordinate system for this box to /// Convert the given point from the local coordinate system for this box to
/// the global coordinate system. /// the global coordinate system.
Point localToGlobal(Point point) { Point localToGlobal(Point point) {
List<RenderObject> renderers = <RenderObject>[]; return MatrixUtils.transformPoint(_collectPaintTransform(), point);
for (RenderObject renderer = this; renderer != null; renderer = renderer.parent)
renderers.add(renderer);
Matrix4 transform = new Matrix4.identity();
for (int index = renderers.length - 1; index > 0; index -= 1)
renderers[index].applyPaintTransform(renderers[index - 1], transform);
return MatrixUtils.transformPoint(transform, point);
} }
/// Returns a rectangle that contains all the pixels painted by this box. /// Returns a rectangle that contains all the pixels painted by this box.
......
...@@ -378,16 +378,14 @@ class RenderFlow extends RenderBox ...@@ -378,16 +378,14 @@ class RenderFlow extends RenderBox
final Matrix4 transform = childParentData._transform; final Matrix4 transform = childParentData._transform;
if (transform == null) if (transform == null)
continue; continue;
Matrix4 inverse = new Matrix4.zero(); final Matrix4 inverse = new Matrix4.zero();
double determinate = inverse.copyInverse(transform); final double determinate = inverse.copyInverse(transform);
if (determinate == 0.0) { if (determinate == 0.0) {
// We cannot invert the transform. That means the child doesn't appear // We cannot invert the transform. That means the child doesn't appear
// on screen and cannot be hit. // on screen and cannot be hit.
continue; continue;
} }
final Vector3 position3 = new Vector3(position.x, position.y, 0.0); final Point childPosition = MatrixUtils.transformPoint(inverse, position);
final Vector3 transformed3 = inverse.transform3(position3);
Point childPosition = new Point(transformed3.x, transformed3.y);
if (child.hitTest(result, position: childPosition)) if (child.hitTest(result, position: childPosition))
return true; return true;
} }
......
...@@ -6,6 +6,7 @@ import 'dart:ui' as ui show ImageFilter; ...@@ -6,6 +6,7 @@ import 'dart:ui' as ui show ImageFilter;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/painting.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:vector_math/vector_math_64.dart'; import 'package:vector_math/vector_math_64.dart';
...@@ -1367,9 +1368,7 @@ class RenderTransform extends RenderProxyBox { ...@@ -1367,9 +1368,7 @@ class RenderTransform extends RenderProxyBox {
// doesn't appear on screen and cannot be hit. // doesn't appear on screen and cannot be hit.
return false; return false;
} }
Vector3 position3 = new Vector3(position.x, position.y, 0.0); position = MatrixUtils.transformPoint(inverse, position);
Vector3 transformed3 = inverse.transform3(position3);
position = new Point(transformed3.x, transformed3.y);
} }
return super.hitTest(result, position: position); return super.hitTest(result, position: position);
} }
...@@ -1515,9 +1514,7 @@ class RenderFittedBox extends RenderProxyBox { ...@@ -1515,9 +1514,7 @@ class RenderFittedBox extends RenderProxyBox {
// doesn't appear on screen and cannot be hit. // doesn't appear on screen and cannot be hit.
return false; return false;
} }
Vector3 position3 = new Vector3(position.x, position.y, 0.0); position = MatrixUtils.transformPoint(inverse, position);
Vector3 transformed3 = inverse.transform3(position3);
position = new Point(transformed3.x, transformed3.y);
return super.hitTest(result, position: position); return super.hitTest(result, position: position);
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/painting.dart';
import 'package:vector_math/vector_math_64.dart'; import 'package:vector_math/vector_math_64.dart';
import 'box.dart'; import 'box.dart';
...@@ -93,9 +94,7 @@ class RenderRotatedBox extends RenderBox with RenderObjectWithChildMixin<RenderB ...@@ -93,9 +94,7 @@ class RenderRotatedBox extends RenderBox with RenderObjectWithChildMixin<RenderB
if (child == null || _paintTransform == null) if (child == null || _paintTransform == null)
return false; return false;
Matrix4 inverse = new Matrix4.inverted(_paintTransform); Matrix4 inverse = new Matrix4.inverted(_paintTransform);
Vector3 position3 = new Vector3(position.x, position.y, 0.0); return child.hitTest(result, position: MatrixUtils.transformPoint(inverse, position));
Vector3 transformed3 = inverse.transform3(position3);
return child.hitTest(result, position: new Point(transformed3.x, transformed3.y));
} }
void _paintChild(PaintingContext context, Offset offset) { void _paintChild(PaintingContext context, Offset offset) {
......
...@@ -293,7 +293,8 @@ abstract class WidgetsBinding extends BindingBase implements GestureBinding, Ren ...@@ -293,7 +293,8 @@ abstract class WidgetsBinding extends BindingBase implements GestureBinding, Ren
return true; return true;
}); });
try { try {
buildOwner.buildScope(renderViewElement); if (renderViewElement != null)
buildOwner.buildScope(renderViewElement);
super.beginFrame(); super.beginFrame();
buildOwner.finalizeTree(); buildOwner.finalizeTree();
} finally { } finally {
...@@ -340,7 +341,8 @@ abstract class WidgetsBinding extends BindingBase implements GestureBinding, Ren ...@@ -340,7 +341,8 @@ abstract class WidgetsBinding extends BindingBase implements GestureBinding, Ren
void reassembleApplication() { void reassembleApplication() {
_needToReportFirstFrame = true; _needToReportFirstFrame = true;
preventThisFrameFromBeingReportedAsFirstFrame(); preventThisFrameFromBeingReportedAsFirstFrame();
buildOwner.reassemble(renderViewElement); if (renderViewElement != null)
buildOwner.reassemble(renderViewElement);
super.reassembleApplication(); super.reassembleApplication();
} }
} }
......
...@@ -7,7 +7,7 @@ import 'package:flutter/painting.dart'; ...@@ -7,7 +7,7 @@ import 'package:flutter/painting.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
void main() { void main() {
test("TextSpan equals", () { test('TextSpan equals', () {
TextSpan a1 = new TextSpan(text: 'a'); TextSpan a1 = new TextSpan(text: 'a');
TextSpan a2 = new TextSpan(text: 'a'); TextSpan a2 = new TextSpan(text: 'a');
TextSpan b1 = new TextSpan(children: <TextSpan>[ a1 ]); TextSpan b1 = new TextSpan(children: <TextSpan>[ a1 ]);
...@@ -28,7 +28,7 @@ void main() { ...@@ -28,7 +28,7 @@ void main() {
expect(c1 == b2, isFalse); expect(c1 == b2, isFalse);
}); });
test("TextSpan ", () { test('TextSpan', () {
final TextSpan test = new TextSpan( final TextSpan test = new TextSpan(
text: 'a', text: 'a',
style: new TextStyle( style: new TextStyle(
......
...@@ -43,13 +43,20 @@ TestRenderingFlutterBinding get renderer { ...@@ -43,13 +43,20 @@ TestRenderingFlutterBinding get renderer {
return _renderer; return _renderer;
} }
void layout(RenderBox box, { BoxConstraints constraints, EnginePhase phase: EnginePhase.layout }) { /// Place the box in the render tree, at the given size and with the given
/// alignment on the screen.
void layout(RenderBox box, {
BoxConstraints constraints,
FractionalOffset alignment: FractionalOffset.center,
EnginePhase phase: EnginePhase.layout
}) {
assert(box != null); // If you want to just repump the last box, call pumpFrame(). assert(box != null); // If you want to just repump the last box, call pumpFrame().
assert(box.parent == null); // We stick the box in another, so you can't reuse it easily, sorry. assert(box.parent == null); // We stick the box in another, so you can't reuse it easily, sorry.
renderer.renderView.child = null; renderer.renderView.child = null;
if (constraints != null) { if (constraints != null) {
box = new RenderPositionedBox( box = new RenderPositionedBox(
alignment: alignment,
child: new RenderConstrainedBox( child: new RenderConstrainedBox(
additionalConstraints: constraints, additionalConstraints: constraints,
child: box child: box
......
This diff is collapsed.
...@@ -38,6 +38,7 @@ void main() { ...@@ -38,6 +38,7 @@ void main() {
Point insidePoint = insideBox.localToGlobal(new Point(100.0, 50.0)); Point insidePoint = insideBox.localToGlobal(new Point(100.0, 50.0));
Point outsidePoint = outsideBox.localToGlobal(new Point(200.0, 100.0)); Point outsidePoint = outsideBox.localToGlobal(new Point(200.0, 100.0));
expect(outsidePoint, equals(const Point(500.0, 350.0)));
expect(insidePoint, equals(outsidePoint)); expect(insidePoint, equals(outsidePoint));
}); });
......
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