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