flutter/examples/game/lib/node.dart
Viktor Lidholt 43d97f6903 Correctly handle touches together with zPosition
Renames hitTest to isPointInside

Refactor sorting of children in nodes

Fixes zPosition in sprites and hides internal methods

Adds scaleX / scaleY properties

R=abarth@chromium.org

Review URL: https://codereview.chromium.org/1190393004.
2015-06-19 11:15:58 -07:00

360 lines
8.0 KiB
Dart

part of sprites;
double convertDegrees2Radians(double degrees) => degrees * Math.PI/180.8;
double convertRadians2Degrees(double radians) => radians * 180.0/Math.PI;
class Node {
// Member variables
SpriteBox _spriteBox;
Node _parent;
Point _position;
double _rotation;
Matrix4 _transformMatrix;
Matrix4 _transformMatrixNodeToBox;
Matrix4 _transformMatrixBoxToNode;
double _scaleX;
double _scaleY;
bool visible;
double _zPosition;
int _addedOrder;
int _childrenLastAddedOrder;
bool _childrenNeedSorting;
bool paused = false;
bool _userInteractionEnabled = false;
bool handleMultiplePointers = false;
int _handlingPointer;
List<Node>_children;
// Constructors
Node() {
_rotation = 0.0;
_position = Point.origin;
_scaleX = _scaleY = 1.0;
_transformMatrix = new Matrix4.identity();
_children = [];
_childrenNeedSorting = false;
_childrenLastAddedOrder = 0;
_zPosition = 0.0;
visible = true;
}
// Property setters and getters
SpriteBox get spriteBox => _spriteBox;
Node get parent => _parent;
double get rotation => _rotation;
void set rotation(double rotation) {
assert(rotation != null);
_rotation = rotation;
_invalidateTransformMatrix();
}
Point get position => _position;
void set position(Point position) {
assert(position != null);
_position = position;
_invalidateTransformMatrix();
}
double get zPosition => _zPosition;
void set zPosition(double zPosition) {
assert(zPosition != null);
_zPosition = zPosition;
if (_parent != null) {
_parent._childrenNeedSorting = true;
}
}
double get scale {
assert(_scaleX == _scaleY);
return _scaleX;
}
void set scale(double scale) {
assert(scale != null);
_scaleX = _scaleY = scale;
_invalidateTransformMatrix();
}
double get scaleX => _scaleX;
void set scaleX(double scaleX) {
assert(scaleX != null);
_scaleX = scaleX;
_invalidateTransformMatrix();
}
double get scaleY => _scaleY;
void set scaleY(double scaleY) {
assert(scaleY != null);
_scaleY = scaleY;
_invalidateTransformMatrix();
}
List<Node> get children {
_sortChildren();
return _children;
}
// Adding and removing children
void addChild(Node child) {
assert(child != null);
assert(child._parent == null);
_childrenNeedSorting = true;
_children.add(child);
child._parent = this;
child._spriteBox = this._spriteBox;
_childrenLastAddedOrder += 1;
child._addedOrder = _childrenLastAddedOrder;
if (_spriteBox != null) _spriteBox._eventTargets = null;
}
void removeChild(Node child) {
assert(child != null);
if (_children.remove(child)) {
child._parent = null;
child._spriteBox = null;
if (_spriteBox != null) _spriteBox._eventTargets = null;
}
}
void removeFromParent() {
assert(_parent != null);
_parent.removeChild(this);
}
void removeAllChildren() {
for (Node child in _children) {
child._parent = null;
child._spriteBox = null;
}
_children = [];
_childrenNeedSorting = false;
if (_spriteBox != null) _spriteBox._eventTargets = null;
}
void _sortChildren() {
// Sort children primarily by zPosition, secondarily by added order
if (_childrenNeedSorting) {
_children.sort((Node a, Node b) {
if (a._zPosition == b._zPosition) {
return a._addedOrder - b._addedOrder;
}
else if (a._zPosition > b._zPosition) {
return 1;
}
else {
return -1;
}
});
_childrenNeedSorting = false;
}
}
// Calculating the transformation matrix
Matrix4 get transformMatrix {
if (_transformMatrix != null) {
return _transformMatrix;
}
double cx, sx, cy, sy;
if (_rotation == 0.0) {
cx = 1.0;
sx = 0.0;
cy = 1.0;
sy = 0.0;
}
else {
double radiansX = convertDegrees2Radians(_rotation);
double radiansY = convertDegrees2Radians(_rotation);
cx = Math.cos(radiansX);
sx = Math.sin(radiansX);
cy = Math.cos(radiansY);
sy = Math.sin(radiansY);
}
// Create transformation matrix for scale, position and rotation
_transformMatrix = new Matrix4(cy * _scaleX, sy * _scaleX, 0.0, 0.0,
-sx * _scaleY, cx * _scaleY, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
_position.x, _position.y, 0.0, 1.0);
return _transformMatrix;
}
void _invalidateTransformMatrix() {
_transformMatrix = null;
_invalidateToBoxTransformMatrix();
}
void _invalidateToBoxTransformMatrix () {
_transformMatrixNodeToBox = null;
_transformMatrixBoxToNode = null;
for (Node child in children) {
child._invalidateToBoxTransformMatrix();
}
}
// Transforms to other nodes
Matrix4 _nodeToBoxMatrix() {
assert(_spriteBox != null);
if (_transformMatrixNodeToBox != null) {
return _transformMatrixNodeToBox;
}
if (_parent == null) {
// Base case, we are at the top
assert(this == _spriteBox.rootNode);
_transformMatrixNodeToBox = new Matrix4.copy(_spriteBox.transformMatrix).multiply(transformMatrix);
}
else {
_transformMatrixNodeToBox = new Matrix4.copy(_parent._nodeToBoxMatrix()).multiply(transformMatrix);
}
return _transformMatrixNodeToBox;
}
Matrix4 _boxToNodeMatrix() {
assert(_spriteBox != null);
if (_transformMatrixBoxToNode != null) {
return _transformMatrixBoxToNode;
}
_transformMatrixBoxToNode = new Matrix4.copy(_nodeToBoxMatrix());
_transformMatrixBoxToNode.invert();
return _transformMatrixBoxToNode;
}
Point convertPointToNodeSpace(Point boxPoint) {
assert(boxPoint != null);
assert(_spriteBox != null);
Vector4 v =_boxToNodeMatrix().transform(new Vector4(boxPoint.x, boxPoint.y, 0.0, 1.0));
return new Point(v[0], v[1]);
}
Point convertPointToBoxSpace(Point nodePoint) {
assert(nodePoint != null);
assert(_spriteBox != null);
Vector4 v =_nodeToBoxMatrix().transform(new Vector4(nodePoint.x, nodePoint.y, 0.0, 1.0));
return new Point(v[0], v[1]);
}
Point convertPointFromNode(Point point, Node node) {
assert(node != null);
assert(point != null);
assert(_spriteBox != null);
assert(_spriteBox == node._spriteBox);
Point boxPoint = node.convertPointToBoxSpace(point);
Point localPoint = convertPointToNodeSpace(boxPoint);
return localPoint;
}
// Hit test
bool isPointInside(Point nodePoint) {
assert(nodePoint != null);
return false;
}
// Rendering
void _visit(PictureRecorder canvas) {
assert(canvas != null);
if (!visible) return;
_prePaint(canvas);
_visitChildren(canvas);
_postPaint(canvas);
}
void _prePaint(PictureRecorder canvas) {
canvas.save();
// Get the transformation matrix and apply transform
canvas.concat(transformMatrix.storage);
}
void paint(PictureRecorder canvas) {
}
void _visitChildren(PictureRecorder canvas) {
// Sort children if needed
_sortChildren();
int i = 0;
// Visit children behind this node
while (i < _children.length) {
Node child = _children[i];
if (child.zPosition >= 0.0) break;
child._visit(canvas);
i++;
}
// Paint this node
paint(canvas);
// Visit children in front of this node
while (i < _children.length) {
Node child = _children[i];
child._visit(canvas);
i++;
}
}
void _postPaint(PictureRecorder canvas) {
canvas.restore();
}
// Receiving update calls
void update(double dt) {
}
void spriteBoxPerformedLayout() {
}
// Handling user interaction
bool get userInteractionEnabled => _userInteractionEnabled;
void set userInteractionEnabled(bool userInteractionEnabled) {
_userInteractionEnabled = userInteractionEnabled;
if (_spriteBox != null) _spriteBox._eventTargets = null;
}
bool handleEvent(SpriteBoxEvent event) {
return false;
}
}