Commit ffc6eaa9 authored by Adam Barth's avatar Adam Barth

Add an origin parameter to transforms

This parameter makes it easier to do math when you don't want to center your
tranform at (0, 0).
parent 5fb31769
...@@ -287,13 +287,13 @@ class RenderShrinkWrapHeight extends RenderProxyBox { ...@@ -287,13 +287,13 @@ class RenderShrinkWrapHeight extends RenderProxyBox {
double getMinIntrinsicWidth(BoxConstraints constraints) { double getMinIntrinsicWidth(BoxConstraints constraints) {
if (child == null) if (child == null)
return constraints.constrainWidth(0.0); return constraints.constrainWidth(0.0);
return child.getMinIntrinsicWidth(_getInnerConstraints(constraints)); return child.getMinIntrinsicWidth(_getInnerConstraints(constraints));
} }
double getMaxIntrinsicWidth(BoxConstraints constraints) { double getMaxIntrinsicWidth(BoxConstraints constraints) {
if (child == null) if (child == null)
return constraints.constrainWidth(0.0); return constraints.constrainWidth(0.0);
return child.getMaxIntrinsicWidth(_getInnerConstraints(constraints)); return child.getMaxIntrinsicWidth(_getInnerConstraints(constraints));
} }
double getMinIntrinsicHeight(BoxConstraints constraints) { double getMinIntrinsicHeight(BoxConstraints constraints) {
...@@ -516,13 +516,24 @@ class RenderDecoratedBox extends RenderProxyBox { ...@@ -516,13 +516,24 @@ class RenderDecoratedBox extends RenderProxyBox {
class RenderTransform extends RenderProxyBox { class RenderTransform extends RenderProxyBox {
RenderTransform({ RenderTransform({
Matrix4 transform, Matrix4 transform,
Offset origin,
RenderBox child RenderBox child
}) : super(child) { }) : super(child) {
assert(transform != null); assert(transform != null);
this.transform = transform; this.transform = transform;
this.origin = origin;
} }
Matrix4 _transform; Matrix4 _transform;
Offset _origin;
Offset get origin => _origin;
void set origin (Offset newOrigin) {
if (_origin == newOrigin)
return;
_origin = newOrigin;
markNeedsPaint();
}
void set transform(Matrix4 newTransform) { void set transform(Matrix4 newTransform) {
assert(newTransform != null); assert(newTransform != null);
...@@ -562,10 +573,20 @@ class RenderTransform extends RenderProxyBox { ...@@ -562,10 +573,20 @@ class RenderTransform extends RenderProxyBox {
markNeedsPaint(); markNeedsPaint();
} }
Matrix4 get _effectiveTransform {
if (_origin == null)
return _transform;
return new Matrix4
.identity()
.translate(_origin.dx, _origin.dy)
.multiply(_transform)
.translate(-_origin.dx, -_origin.dy);
}
bool hitTest(HitTestResult result, { Point position }) { bool hitTest(HitTestResult result, { Point position }) {
Matrix4 inverse = new Matrix4.zero(); Matrix4 inverse = new Matrix4.zero();
// TODO(abarth): Check the determinant for degeneracy. // TODO(abarth): Check the determinant for degeneracy.
inverse.copyInverse(_transform); inverse.copyInverse(_effectiveTransform);
Vector3 position3 = new Vector3(position.x, position.y, 0.0); Vector3 position3 = new Vector3(position.x, position.y, 0.0);
Vector3 transformed3 = inverse.transform3(position3); Vector3 transformed3 = inverse.transform3(position3);
...@@ -575,18 +596,18 @@ class RenderTransform extends RenderProxyBox { ...@@ -575,18 +596,18 @@ class RenderTransform extends RenderProxyBox {
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
if (child != null) if (child != null)
context.paintChildWithTransform(child, offset.toPoint(), _transform); context.paintChildWithTransform(child, offset.toPoint(), _effectiveTransform);
} }
void applyPaintTransform(Matrix4 transform) { void applyPaintTransform(Matrix4 transform) {
super.applyPaintTransform(transform); super.applyPaintTransform(transform);
transform.multiply(_transform); transform.multiply(_effectiveTransform);
} }
String debugDescribeSettings(String prefix) { String debugDescribeSettings(String prefix) {
List<String> result = _transform.toString().split('\n').map((s) => '$prefix $s\n').toList(); List<String> result = _transform.toString().split('\n').map((s) => '$prefix $s\n').toList();
result.removeLast(); result.removeLast();
return '${super.debugDescribeSettings(prefix)}${prefix}transform matrix:\n${result.join()}'; return '${super.debugDescribeSettings(prefix)}${prefix}transform matrix:\n${result.join()}\n${prefix}origin: ${origin}\n';
} }
} }
......
...@@ -154,17 +154,19 @@ class ClipOval extends OneChildRenderObjectWrapper { ...@@ -154,17 +154,19 @@ class ClipOval extends OneChildRenderObjectWrapper {
// POSITIONING AND SIZING NODES // POSITIONING AND SIZING NODES
class Transform extends OneChildRenderObjectWrapper { class Transform extends OneChildRenderObjectWrapper {
Transform({ Key key, this.transform, Widget child }) Transform({ Key key, this.transform, this.origin, Widget child })
: super(key: key, child: child); : super(key: key, child: child);
final Matrix4 transform; final Matrix4 transform;
final Offset origin;
RenderTransform createNode() => new RenderTransform(transform: transform); RenderTransform createNode() => new RenderTransform(transform: transform, origin: origin);
RenderTransform get renderObject => super.renderObject; RenderTransform get renderObject => super.renderObject;
void syncRenderObject(Transform old) { void syncRenderObject(Transform old) {
super.syncRenderObject(old); super.syncRenderObject(old);
renderObject.transform = transform; renderObject.transform = transform;
renderObject.origin = origin;
} }
} }
......
import 'package:sky/widgets.dart';
import 'package:test/test.dart';
import 'widget_tester.dart';
void main() {
test('Transform origin', () {
WidgetTester tester = new WidgetTester();
bool didReceiveTap = false;
tester.pumpFrame(() {
return new Stack([
new Positioned(
top: 100.0,
left: 100.0,
child: new Container(
width: 100.0,
height: 100.0,
decoration: new BoxDecoration(
backgroundColor: new Color(0xFF0000FF)
)
)
),
new Positioned(
top: 100.0,
left: 100.0,
child: new Container(
width: 100.0,
height: 100.0,
child: new Transform(
transform: new Matrix4.identity().scale(0.5, 0.5),
origin: new Offset(100.0, 50.0),
child: new GestureDetector(
onTap: () {
didReceiveTap = true;
},
child: new Container()
)
)
)
)
]);
});
expect(didReceiveTap, isFalse);
tester.tapAt(new Point(110.0, 110.0));
expect(didReceiveTap, isFalse);
tester.tapAt(new Point(190.0, 150.0));
expect(didReceiveTap, isTrue);
});
}
...@@ -90,7 +90,10 @@ class WidgetTester { ...@@ -90,7 +90,10 @@ class WidgetTester {
} }
void tap(Widget widget, { int pointer: 1 }) { void tap(Widget widget, { int pointer: 1 }) {
Point location = getCenter(widget); tapAt(getCenter(widget), pointer: pointer);
}
void tapAt(Point location, { int pointer: 1 }) {
HitTestResult result = _hitTest(location); HitTestResult result = _hitTest(location);
TestPointer p = new TestPointer(pointer); TestPointer p = new TestPointer(pointer);
_dispatchEvent(p.down(location), result); _dispatchEvent(p.down(location), result);
......
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