From 30ed7bc4ed751cbf85674f4980ec02adec478a98 Mon Sep 17 00:00:00 2001 From: JohnBlackburne Date: Mon, 6 Mar 2017 09:28:39 +0000 Subject: [PATCH 1/6] New optimised sort function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 
A sort function that is more like Array.sortOn. Instead of using function it sorts on a field, either x, y or z, specified by a constant. x and y allow sorting on the two display axes, while z is unused by Starling, so can be used to sort on arbitrary data. --- DisplayObjectContainer.as | 655 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 655 insertions(+) create mode 100644 DisplayObjectContainer.as diff --git a/DisplayObjectContainer.as b/DisplayObjectContainer.as new file mode 100644 index 000000000..2ff7a0ad0 --- /dev/null +++ b/DisplayObjectContainer.as @@ -0,0 +1,655 @@ +// ================================================================================================= +// +// Starling Framework +// Copyright Gamua GmbH. All Rights Reserved. +// +// This program is free software. You can redistribute and/or modify it +// in accordance with the terms of the accompanying license agreement. +// +// ================================================================================================= + +package starling.display +{ + import flash.geom.Matrix; + import flash.geom.Point; + import flash.geom.Rectangle; + import flash.system.Capabilities; + import flash.utils.getQualifiedClassName; + + import starling.core.starling_internal; + import starling.errors.AbstractClassError; + import starling.events.Event; + import starling.filters.FragmentFilter; + import starling.rendering.BatchToken; + import starling.rendering.Painter; + import starling.utils.MatrixUtil; + + use namespace starling_internal; + + /** + * A DisplayObjectContainer represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. By + * maintaining an ordered list of children, it defines the back-to-front positioning of the + * children within the display tree. + * + *

A container does not a have size in itself. The width and height properties represent the + * extents of its children. Changing those properties will scale all children accordingly.

+ * + *

As this is an abstract class, you can't instantiate it directly, but have to + * use a subclass instead. The most lightweight container class is "Sprite".

+ * + * Adding and removing children + * + *

The class defines methods that allow you to add or remove children. When you add a child, + * it will be added at the frontmost position, possibly occluding a child that was added + * before. You can access the children via an index. The first child will have index 0, the + * second child index 1, etc.

+ * + * Adding and removing objects from a container triggers non-bubbling events. + * + * + * + * Especially the ADDED_TO_STAGE event is very helpful, as it allows you to + * automatically execute some logic (e.g. start an animation) when an object is rendered the + * first time. + * + * @see Sprite + * @see DisplayObject + */ + public class DisplayObjectContainer extends DisplayObject + { + // members + + private var _children:Vector.; + private var _touchGroup:Boolean; + + // helper objects + private static var sHelperMatrix:Matrix = new Matrix(); + private static var sHelperPoint:Point = new Point(); + private static var sBroadcastListeners:Vector. = new []; + private static var sSortBuffer:Vector. = new []; + private static var sCacheToken:BatchToken = new BatchToken(); + + public static const ON_X:int = 0; + public static const ON_Y:int = 1; + public static const ON_Z:int = 2; + public static const ON_X_REVERSE:int = 4; + public static const ON_Y_REVERSE:int = 5; + public static const ON_Z_REVERSE:int = 6; + + // construction + + /** @private */ + public function DisplayObjectContainer() + { + if (Capabilities.isDebugger && + getQualifiedClassName(this) == "starling.display::DisplayObjectContainer") + { + throw new AbstractClassError(); + } + + _children = new []; + } + + /** Disposes the resources of all children. */ + public override function dispose():void + { + for (var i:int=_children.length-1; i>=0; --i) + _children[i].dispose(); + + super.dispose(); + } + + // child management + + /** Adds a child to the container. It will be at the frontmost position. */ + public function addChild(child:DisplayObject):DisplayObject + { + return addChildAt(child, _children.length); + } + + /** Adds a child to the container at a certain index. */ + public function addChildAt(child:DisplayObject, index:int):DisplayObject + { + var numChildren:int = _children.length; + + if (index >= 0 && index <= numChildren) + { + setRequiresRedraw(); + + if (child.parent == this) + { + setChildIndex(child, index); // avoids dispatching events + } + else + { + _children.insertAt(index, child); + + child.removeFromParent(); + child.setParent(this); + child.dispatchEventWith(Event.ADDED, true); + + if (stage) + { + var container:DisplayObjectContainer = child as DisplayObjectContainer; + if (container) container.broadcastEventWith(Event.ADDED_TO_STAGE); + else child.dispatchEventWith(Event.ADDED_TO_STAGE); + } + } + + return child; + } + else + { + throw new RangeError("Invalid child index"); + } + } + + /** Removes a child from the container. If the object is not a child, the method returns + * null. If requested, the child will be disposed right away. */ + public function removeChild(child:DisplayObject, dispose:Boolean=false):DisplayObject + { + var childIndex:int = getChildIndex(child); + if (childIndex != -1) return removeChildAt(childIndex, dispose); + else return null; + } + + /** Removes a child at a certain index. The index positions of any display objects above + * the child are decreased by 1. If requested, the child will be disposed right away. */ + public function removeChildAt(index:int, dispose:Boolean=false):DisplayObject + { + if (index >= 0 && index < _children.length) + { + setRequiresRedraw(); + + var child:DisplayObject = _children[index]; + child.dispatchEventWith(Event.REMOVED, true); + + if (stage) + { + var container:DisplayObjectContainer = child as DisplayObjectContainer; + if (container) container.broadcastEventWith(Event.REMOVED_FROM_STAGE); + else child.dispatchEventWith(Event.REMOVED_FROM_STAGE); + } + + child.setParent(null); + index = _children.indexOf(child); // index might have changed by event handler + if (index >= 0) _children.removeAt(index); + if (dispose) child.dispose(); + + return child; + } + else + { + throw new RangeError("Invalid child index"); + } + } + + /** Removes a range of children from the container (endIndex included). + * If no arguments are given, all children will be removed. */ + public function removeChildren(beginIndex:int=0, endIndex:int=-1, dispose:Boolean=false):void + { + if (endIndex < 0 || endIndex >= numChildren) + endIndex = numChildren - 1; + + for (var i:int=beginIndex; i<=endIndex; ++i) + removeChildAt(beginIndex, dispose); + } + + /** Returns a child object at a certain index. If you pass a negative index, + * '-1' will return the last child, '-2' the second to last child, etc. */ + public function getChildAt(index:int):DisplayObject + { + var numChildren:int = _children.length; + + if (index < 0) + index = numChildren + index; + + if (index >= 0 && index < numChildren) + return _children[index]; + else + throw new RangeError("Invalid child index"); + } + + /** Returns a child object with a certain name (non-recursively). */ + public function getChildByName(name:String):DisplayObject + { + var numChildren:int = _children.length; + for (var i:int=0; i out.x) minX = out.x; + if (maxX < out.right) maxX = out.right; + if (minY > out.y) minY = out.y; + if (maxY < out.bottom) maxY = out.bottom; + } + + out.setTo(minX, minY, maxX - minX, maxY - minY); + } + + return out; + } + + /** @inheritDoc */ + public override function hitTest(localPoint:Point):DisplayObject + { + if (!visible || !touchable || !hitTestMask(localPoint)) return null; + + var target:DisplayObject = null; + var localX:Number = localPoint.x; + var localY:Number = localPoint.y; + var numChildren:int = _children.length; + + for (var i:int = numChildren - 1; i >= 0; --i) // front to back! + { + var child:DisplayObject = _children[i]; + if (child.isMask) continue; + + sHelperMatrix.copyFrom(child.transformationMatrix); + sHelperMatrix.invert(); + + MatrixUtil.transformCoords(sHelperMatrix, localX, localY, sHelperPoint); + target = child.hitTest(sHelperPoint); + + if (target) return _touchGroup ? this : target; + } + + return null; + } + + /** @inheritDoc */ + public override function render(painter:Painter):void + { + var numChildren:int = _children.length; + var frameID:uint = painter.frameID; + var cacheEnabled:Boolean = frameID !=0; + var selfOrParentChanged:Boolean = _lastParentOrSelfChangeFrameID == frameID; + + painter.pushState(); + + for (var i:int=0; i, compareFunc:Function, + startIndex:int, length:int, + buffer:Vector.):void + { + // This is a port of the C++ merge sort algorithm shown here: + // http://www.cprogramming.com/tutorial/computersciencetheory/mergesort.html + + if (length > 1) + { + var i:int; + var endIndex:int = startIndex + length; + var halfLength:int = length / 2; + var l:int = startIndex; // current position in the left subvector + var r:int = startIndex + halfLength; // current position in the right subvector + + // sort each subvector + mergeSort(input, compareFunc, startIndex, halfLength, buffer); + mergeSort(input, compareFunc, startIndex + halfLength, length - halfLength, buffer); + + // merge the vectors, using the buffer vector for temporary storage + for (i = 0; i < length; i++) + { + // Check to see if any elements remain in the left vector; + // if so, we check if there are any elements left in the right vector; + // if so, we compare them. Otherwise, we know that the merge must + // take the element from the left vector. */ + if (l < startIndex + halfLength && + (r == endIndex || compareFunc(input[l], input[r]) <= 0)) + { + buffer[i] = input[l]; + l++; + } + else + { + buffer[i] = input[r]; + r++; + } + } + + // copy the sorted subvector back to the input + for(i = startIndex; i < endIndex; i++) + input[i] = buffer[int(i - startIndex)]; + } + } + + private static function mergeSortOnKey(input:Vector., key:int, + startIndex:int, length:int, + buffer:Vector.):void + { + // This is a port of the C++ merge sort algorithm shown here: + // http://www.cprogramming.com/tutorial/computersciencetheory/mergesort.html + + if (length > 1) + { + var i:int; + var endIndex:int = startIndex + length; + var halfLength:int = length / 2; + var l:int = startIndex; // current position in the left subvector + var r:int = startIndex + halfLength; // current position in the right subvector + + // sort each subvector + mergeSortOnKey(input, key, startIndex, halfLength, buffer); + mergeSortOnKey(input, key, startIndex + halfLength, length - halfLength, buffer); + + // merge the vectors, using the buffer vector for temporary storage + switch(key) + { + case ON_X: + for (i = 0; i < length; i++) + { + if (l < startIndex + halfLength && + (r == endIndex || input[l].x <= input[r].x)) + { + buffer[i] = input[l]; + l++; + } + else + { + buffer[i] = input[r]; + r++; + } + } + break; + case ON_Y: + for (i = 0; i < length; i++) + { + if (l < startIndex + halfLength && + (r == endIndex || input[l].y <= input[r].y)) + { + buffer[i] = input[l]; + l++; + } + else + { + buffer[i] = input[r]; + r++; + } + } + break; + case ON_Z: + for (i = 0; i < length; i++) + { + if (l < startIndex + halfLength && + (r == endIndex || input[l].z <= input[r].z)) + { + buffer[i] = input[l]; + l++; + } + else + { + buffer[i] = input[r]; + r++; + } + } + break; + case ON_X_REVERSE: + for (i = 0; i < length; i++) + { + if (l < startIndex + halfLength && + (r == endIndex || input[l].x > input[r].x)) + { + buffer[i] = input[l]; + l++; + } + else + { + buffer[i] = input[r]; + r++; + } + } + break; + case ON_Y_REVERSE: + for (i = 0; i < length; i++) + { + if (l < startIndex + halfLength && + (r == endIndex || input[l].y > input[r].y)) + { + buffer[i] = input[l]; + l++; + } + else + { + buffer[i] = input[r]; + r++; + } + } + break; + case ON_Z_REVERSE: + for (i = 0; i < length; i++) + { + if (l < startIndex + halfLength && + (r == endIndex || input[l].z > input[r].z)) + { + buffer[i] = input[l]; + l++; + } + else + { + buffer[i] = input[r]; + r++; + } + } + break; + } + // copy the sorted subvector back to the input + for(i = startIndex; i < endIndex; i++) + input[i] = buffer[int(i - startIndex)]; + } + } + + + /** @private */ + internal function getChildEventListeners(object:DisplayObject, eventType:String, + listeners:Vector.):void + { + var container:DisplayObjectContainer = object as DisplayObjectContainer; + + if (object.hasEventListener(eventType)) + listeners[listeners.length] = object; // avoiding 'push' + + if (container) + { + var children:Vector. = container._children; + var numChildren:int = children.length; + + for (var i:int=0; i Date: Mon, 6 Mar 2017 09:30:57 +0000 Subject: [PATCH 2/6] New optimised sort function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 
A sort function that is more like Array.sortOn. Instead of using function it sorts on a field, either x, y or z, specified by a constant. x and y allow sorting on the two display axes, while z is unused by Starling, so can be used to sort on arbitrary data. --- .../display/DisplayObjectContainer.as | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/starling/src/starling/display/DisplayObjectContainer.as b/starling/src/starling/display/DisplayObjectContainer.as index e5d718f70..2ff7a0ad0 100644 --- a/starling/src/starling/display/DisplayObjectContainer.as +++ b/starling/src/starling/display/DisplayObjectContainer.as @@ -77,6 +77,13 @@ package starling.display private static var sSortBuffer:Vector. = new []; private static var sCacheToken:BatchToken = new BatchToken(); + public static const ON_X:int = 0; + public static const ON_Y:int = 1; + public static const ON_Z:int = 2; + public static const ON_X_REVERSE:int = 4; + public static const ON_Y_REVERSE:int = 5; + public static const ON_Z_REVERSE:int = 6; + // construction /** @private */ @@ -268,6 +275,13 @@ package starling.display setRequiresRedraw(); } + public function sortChildrenOnKey(key:int):void { + sSortBuffer.length = _children.length; + mergeSortOnKey(_children, key, 0, _children.length, sSortBuffer); + sSortBuffer.length = 0; + setRequiresRedraw(); + } + /** Determines if a certain object is a child of the container (recursively). */ public function contains(child:DisplayObject):Boolean { @@ -492,6 +506,132 @@ package starling.display input[i] = buffer[int(i - startIndex)]; } } + + private static function mergeSortOnKey(input:Vector., key:int, + startIndex:int, length:int, + buffer:Vector.):void + { + // This is a port of the C++ merge sort algorithm shown here: + // http://www.cprogramming.com/tutorial/computersciencetheory/mergesort.html + + if (length > 1) + { + var i:int; + var endIndex:int = startIndex + length; + var halfLength:int = length / 2; + var l:int = startIndex; // current position in the left subvector + var r:int = startIndex + halfLength; // current position in the right subvector + + // sort each subvector + mergeSortOnKey(input, key, startIndex, halfLength, buffer); + mergeSortOnKey(input, key, startIndex + halfLength, length - halfLength, buffer); + + // merge the vectors, using the buffer vector for temporary storage + switch(key) + { + case ON_X: + for (i = 0; i < length; i++) + { + if (l < startIndex + halfLength && + (r == endIndex || input[l].x <= input[r].x)) + { + buffer[i] = input[l]; + l++; + } + else + { + buffer[i] = input[r]; + r++; + } + } + break; + case ON_Y: + for (i = 0; i < length; i++) + { + if (l < startIndex + halfLength && + (r == endIndex || input[l].y <= input[r].y)) + { + buffer[i] = input[l]; + l++; + } + else + { + buffer[i] = input[r]; + r++; + } + } + break; + case ON_Z: + for (i = 0; i < length; i++) + { + if (l < startIndex + halfLength && + (r == endIndex || input[l].z <= input[r].z)) + { + buffer[i] = input[l]; + l++; + } + else + { + buffer[i] = input[r]; + r++; + } + } + break; + case ON_X_REVERSE: + for (i = 0; i < length; i++) + { + if (l < startIndex + halfLength && + (r == endIndex || input[l].x > input[r].x)) + { + buffer[i] = input[l]; + l++; + } + else + { + buffer[i] = input[r]; + r++; + } + } + break; + case ON_Y_REVERSE: + for (i = 0; i < length; i++) + { + if (l < startIndex + halfLength && + (r == endIndex || input[l].y > input[r].y)) + { + buffer[i] = input[l]; + l++; + } + else + { + buffer[i] = input[r]; + r++; + } + } + break; + case ON_Z_REVERSE: + for (i = 0; i < length; i++) + { + if (l < startIndex + halfLength && + (r == endIndex || input[l].z > input[r].z)) + { + buffer[i] = input[l]; + l++; + } + else + { + buffer[i] = input[r]; + r++; + } + } + break; + } + // copy the sorted subvector back to the input + for(i = startIndex; i < endIndex; i++) + input[i] = buffer[int(i - startIndex)]; + } + } + /** @private */ internal function getChildEventListeners(object:DisplayObject, eventType:String, From 9f7b6f561ae152eb33ec965e4f549585f60ebdd4 Mon Sep 17 00:00:00 2001 From: JohnBlackburne Date: Mon, 6 Mar 2017 09:31:43 +0000 Subject: [PATCH 3/6] rm misplaced copy --- DisplayObjectContainer.as | 655 -------------------------------------- 1 file changed, 655 deletions(-) delete mode 100644 DisplayObjectContainer.as diff --git a/DisplayObjectContainer.as b/DisplayObjectContainer.as deleted file mode 100644 index 2ff7a0ad0..000000000 --- a/DisplayObjectContainer.as +++ /dev/null @@ -1,655 +0,0 @@ -// ================================================================================================= -// -// Starling Framework -// Copyright Gamua GmbH. All Rights Reserved. -// -// This program is free software. You can redistribute and/or modify it -// in accordance with the terms of the accompanying license agreement. -// -// ================================================================================================= - -package starling.display -{ - import flash.geom.Matrix; - import flash.geom.Point; - import flash.geom.Rectangle; - import flash.system.Capabilities; - import flash.utils.getQualifiedClassName; - - import starling.core.starling_internal; - import starling.errors.AbstractClassError; - import starling.events.Event; - import starling.filters.FragmentFilter; - import starling.rendering.BatchToken; - import starling.rendering.Painter; - import starling.utils.MatrixUtil; - - use namespace starling_internal; - - /** - * A DisplayObjectContainer represents a collection of display objects. - * It is the base class of all display objects that act as a container for other objects. By - * maintaining an ordered list of children, it defines the back-to-front positioning of the - * children within the display tree. - * - *

A container does not a have size in itself. The width and height properties represent the - * extents of its children. Changing those properties will scale all children accordingly.

- * - *

As this is an abstract class, you can't instantiate it directly, but have to - * use a subclass instead. The most lightweight container class is "Sprite".

- * - * Adding and removing children - * - *

The class defines methods that allow you to add or remove children. When you add a child, - * it will be added at the frontmost position, possibly occluding a child that was added - * before. You can access the children via an index. The first child will have index 0, the - * second child index 1, etc.

- * - * Adding and removing objects from a container triggers non-bubbling events. - * - *
    - *
  • Event.ADDED: the object was added to a parent.
  • - *
  • Event.ADDED_TO_STAGE: the object was added to a parent that is - * connected to the stage, thus becoming visible now.
  • - *
  • Event.REMOVED: the object was removed from a parent.
  • - *
  • Event.REMOVED_FROM_STAGE: the object was removed from a parent that - * is connected to the stage, thus becoming invisible now.
  • - *
- * - * Especially the ADDED_TO_STAGE event is very helpful, as it allows you to - * automatically execute some logic (e.g. start an animation) when an object is rendered the - * first time. - * - * @see Sprite - * @see DisplayObject - */ - public class DisplayObjectContainer extends DisplayObject - { - // members - - private var _children:Vector.; - private var _touchGroup:Boolean; - - // helper objects - private static var sHelperMatrix:Matrix = new Matrix(); - private static var sHelperPoint:Point = new Point(); - private static var sBroadcastListeners:Vector. = new []; - private static var sSortBuffer:Vector. = new []; - private static var sCacheToken:BatchToken = new BatchToken(); - - public static const ON_X:int = 0; - public static const ON_Y:int = 1; - public static const ON_Z:int = 2; - public static const ON_X_REVERSE:int = 4; - public static const ON_Y_REVERSE:int = 5; - public static const ON_Z_REVERSE:int = 6; - - // construction - - /** @private */ - public function DisplayObjectContainer() - { - if (Capabilities.isDebugger && - getQualifiedClassName(this) == "starling.display::DisplayObjectContainer") - { - throw new AbstractClassError(); - } - - _children = new []; - } - - /** Disposes the resources of all children. */ - public override function dispose():void - { - for (var i:int=_children.length-1; i>=0; --i) - _children[i].dispose(); - - super.dispose(); - } - - // child management - - /** Adds a child to the container. It will be at the frontmost position. */ - public function addChild(child:DisplayObject):DisplayObject - { - return addChildAt(child, _children.length); - } - - /** Adds a child to the container at a certain index. */ - public function addChildAt(child:DisplayObject, index:int):DisplayObject - { - var numChildren:int = _children.length; - - if (index >= 0 && index <= numChildren) - { - setRequiresRedraw(); - - if (child.parent == this) - { - setChildIndex(child, index); // avoids dispatching events - } - else - { - _children.insertAt(index, child); - - child.removeFromParent(); - child.setParent(this); - child.dispatchEventWith(Event.ADDED, true); - - if (stage) - { - var container:DisplayObjectContainer = child as DisplayObjectContainer; - if (container) container.broadcastEventWith(Event.ADDED_TO_STAGE); - else child.dispatchEventWith(Event.ADDED_TO_STAGE); - } - } - - return child; - } - else - { - throw new RangeError("Invalid child index"); - } - } - - /** Removes a child from the container. If the object is not a child, the method returns - * null. If requested, the child will be disposed right away. */ - public function removeChild(child:DisplayObject, dispose:Boolean=false):DisplayObject - { - var childIndex:int = getChildIndex(child); - if (childIndex != -1) return removeChildAt(childIndex, dispose); - else return null; - } - - /** Removes a child at a certain index. The index positions of any display objects above - * the child are decreased by 1. If requested, the child will be disposed right away. */ - public function removeChildAt(index:int, dispose:Boolean=false):DisplayObject - { - if (index >= 0 && index < _children.length) - { - setRequiresRedraw(); - - var child:DisplayObject = _children[index]; - child.dispatchEventWith(Event.REMOVED, true); - - if (stage) - { - var container:DisplayObjectContainer = child as DisplayObjectContainer; - if (container) container.broadcastEventWith(Event.REMOVED_FROM_STAGE); - else child.dispatchEventWith(Event.REMOVED_FROM_STAGE); - } - - child.setParent(null); - index = _children.indexOf(child); // index might have changed by event handler - if (index >= 0) _children.removeAt(index); - if (dispose) child.dispose(); - - return child; - } - else - { - throw new RangeError("Invalid child index"); - } - } - - /** Removes a range of children from the container (endIndex included). - * If no arguments are given, all children will be removed. */ - public function removeChildren(beginIndex:int=0, endIndex:int=-1, dispose:Boolean=false):void - { - if (endIndex < 0 || endIndex >= numChildren) - endIndex = numChildren - 1; - - for (var i:int=beginIndex; i<=endIndex; ++i) - removeChildAt(beginIndex, dispose); - } - - /** Returns a child object at a certain index. If you pass a negative index, - * '-1' will return the last child, '-2' the second to last child, etc. */ - public function getChildAt(index:int):DisplayObject - { - var numChildren:int = _children.length; - - if (index < 0) - index = numChildren + index; - - if (index >= 0 && index < numChildren) - return _children[index]; - else - throw new RangeError("Invalid child index"); - } - - /** Returns a child object with a certain name (non-recursively). */ - public function getChildByName(name:String):DisplayObject - { - var numChildren:int = _children.length; - for (var i:int=0; i out.x) minX = out.x; - if (maxX < out.right) maxX = out.right; - if (minY > out.y) minY = out.y; - if (maxY < out.bottom) maxY = out.bottom; - } - - out.setTo(minX, minY, maxX - minX, maxY - minY); - } - - return out; - } - - /** @inheritDoc */ - public override function hitTest(localPoint:Point):DisplayObject - { - if (!visible || !touchable || !hitTestMask(localPoint)) return null; - - var target:DisplayObject = null; - var localX:Number = localPoint.x; - var localY:Number = localPoint.y; - var numChildren:int = _children.length; - - for (var i:int = numChildren - 1; i >= 0; --i) // front to back! - { - var child:DisplayObject = _children[i]; - if (child.isMask) continue; - - sHelperMatrix.copyFrom(child.transformationMatrix); - sHelperMatrix.invert(); - - MatrixUtil.transformCoords(sHelperMatrix, localX, localY, sHelperPoint); - target = child.hitTest(sHelperPoint); - - if (target) return _touchGroup ? this : target; - } - - return null; - } - - /** @inheritDoc */ - public override function render(painter:Painter):void - { - var numChildren:int = _children.length; - var frameID:uint = painter.frameID; - var cacheEnabled:Boolean = frameID !=0; - var selfOrParentChanged:Boolean = _lastParentOrSelfChangeFrameID == frameID; - - painter.pushState(); - - for (var i:int=0; i, compareFunc:Function, - startIndex:int, length:int, - buffer:Vector.):void - { - // This is a port of the C++ merge sort algorithm shown here: - // http://www.cprogramming.com/tutorial/computersciencetheory/mergesort.html - - if (length > 1) - { - var i:int; - var endIndex:int = startIndex + length; - var halfLength:int = length / 2; - var l:int = startIndex; // current position in the left subvector - var r:int = startIndex + halfLength; // current position in the right subvector - - // sort each subvector - mergeSort(input, compareFunc, startIndex, halfLength, buffer); - mergeSort(input, compareFunc, startIndex + halfLength, length - halfLength, buffer); - - // merge the vectors, using the buffer vector for temporary storage - for (i = 0; i < length; i++) - { - // Check to see if any elements remain in the left vector; - // if so, we check if there are any elements left in the right vector; - // if so, we compare them. Otherwise, we know that the merge must - // take the element from the left vector. */ - if (l < startIndex + halfLength && - (r == endIndex || compareFunc(input[l], input[r]) <= 0)) - { - buffer[i] = input[l]; - l++; - } - else - { - buffer[i] = input[r]; - r++; - } - } - - // copy the sorted subvector back to the input - for(i = startIndex; i < endIndex; i++) - input[i] = buffer[int(i - startIndex)]; - } - } - - private static function mergeSortOnKey(input:Vector., key:int, - startIndex:int, length:int, - buffer:Vector.):void - { - // This is a port of the C++ merge sort algorithm shown here: - // http://www.cprogramming.com/tutorial/computersciencetheory/mergesort.html - - if (length > 1) - { - var i:int; - var endIndex:int = startIndex + length; - var halfLength:int = length / 2; - var l:int = startIndex; // current position in the left subvector - var r:int = startIndex + halfLength; // current position in the right subvector - - // sort each subvector - mergeSortOnKey(input, key, startIndex, halfLength, buffer); - mergeSortOnKey(input, key, startIndex + halfLength, length - halfLength, buffer); - - // merge the vectors, using the buffer vector for temporary storage - switch(key) - { - case ON_X: - for (i = 0; i < length; i++) - { - if (l < startIndex + halfLength && - (r == endIndex || input[l].x <= input[r].x)) - { - buffer[i] = input[l]; - l++; - } - else - { - buffer[i] = input[r]; - r++; - } - } - break; - case ON_Y: - for (i = 0; i < length; i++) - { - if (l < startIndex + halfLength && - (r == endIndex || input[l].y <= input[r].y)) - { - buffer[i] = input[l]; - l++; - } - else - { - buffer[i] = input[r]; - r++; - } - } - break; - case ON_Z: - for (i = 0; i < length; i++) - { - if (l < startIndex + halfLength && - (r == endIndex || input[l].z <= input[r].z)) - { - buffer[i] = input[l]; - l++; - } - else - { - buffer[i] = input[r]; - r++; - } - } - break; - case ON_X_REVERSE: - for (i = 0; i < length; i++) - { - if (l < startIndex + halfLength && - (r == endIndex || input[l].x > input[r].x)) - { - buffer[i] = input[l]; - l++; - } - else - { - buffer[i] = input[r]; - r++; - } - } - break; - case ON_Y_REVERSE: - for (i = 0; i < length; i++) - { - if (l < startIndex + halfLength && - (r == endIndex || input[l].y > input[r].y)) - { - buffer[i] = input[l]; - l++; - } - else - { - buffer[i] = input[r]; - r++; - } - } - break; - case ON_Z_REVERSE: - for (i = 0; i < length; i++) - { - if (l < startIndex + halfLength && - (r == endIndex || input[l].z > input[r].z)) - { - buffer[i] = input[l]; - l++; - } - else - { - buffer[i] = input[r]; - r++; - } - } - break; - } - // copy the sorted subvector back to the input - for(i = startIndex; i < endIndex; i++) - input[i] = buffer[int(i - startIndex)]; - } - } - - - /** @private */ - internal function getChildEventListeners(object:DisplayObject, eventType:String, - listeners:Vector.):void - { - var container:DisplayObjectContainer = object as DisplayObjectContainer; - - if (object.hasEventListener(eventType)) - listeners[listeners.length] = object; // avoiding 'push' - - if (container) - { - var children:Vector. = container._children; - var numChildren:int = children.length; - - for (var i:int=0; i Date: Tue, 7 Mar 2017 09:11:05 +0000 Subject: [PATCH 4/6] Update with new sort field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initial version had 'z' alongside 'x' and 'y', for sorting on arbitrary data. But DisplayObject has no 'z' field, and adding one made no sense as it‘s not for z-sorting. Instead add 'sort' field, update sort code and also make that the default. --- .../src/starling/display/DisplayObject.as | 41 +++++++------------ .../display/DisplayObjectContainer.as | 18 ++++---- 2 files changed, 23 insertions(+), 36 deletions(-) diff --git a/starling/src/starling/display/DisplayObject.as b/starling/src/starling/display/DisplayObject.as index bbc4b655b..af0fbcb09 100755 --- a/starling/src/starling/display/DisplayObject.as +++ b/starling/src/starling/display/DisplayObject.as @@ -141,6 +141,7 @@ package starling.display private var _orientationChanged:Boolean; private var _is3D:Boolean; private var _maskee:DisplayObject; + private var _sort:Number; // internal members (for fast access on rendering) @@ -395,42 +396,24 @@ package starling.display */ public function drawToBitmapData(out:BitmapData=null):BitmapData { - var painter:Painter = Starling.painter; var stage:Stage = Starling.current.stage; - var viewPort:Rectangle = Starling.current.viewPort; - var stageWidth:Number = stage.stageWidth; + var stageWidth:Number = stage.stageWidth; var stageHeight:Number = stage.stageHeight; - var scaleX:Number = viewPort.width / stageWidth; - var scaleY:Number = viewPort.height / stageHeight; - var backBufferScale:Number = painter.backBufferScaleFactor; - var projectionX:Number, projectionY:Number; - var bounds:Rectangle; - - if (this is Stage) - { - projectionX = viewPort.x < 0 ? -viewPort.x / scaleX : 0.0; - projectionY = viewPort.y < 0 ? -viewPort.y / scaleY : 0.0; - - out ||= new BitmapData(painter.backBufferWidth * backBufferScale, - painter.backBufferHeight * backBufferScale); - } - else - { - bounds = getBounds(_parent, sHelperRect); - projectionX = bounds.x; - projectionY = bounds.y; + var scale:Number = Starling.contentScaleFactor; + var painter:Painter = Starling.painter; + var bounds:Rectangle = this is Stage ? + stage.getStageBounds(this, sHelperRect) : getBounds(_parent, sHelperRect); - out ||= new BitmapData(Math.ceil(bounds.width * scaleX * backBufferScale), - Math.ceil(bounds.height * scaleY * backBufferScale)); - } + if (out == null) + out = new BitmapData(Math.ceil(bounds.width * scale), + Math.ceil(bounds.height * scale)); painter.clear(); painter.pushState(); painter.state.renderTarget = null; painter.state.setModelviewMatricesToIdentity(); painter.setStateTo(transformationMatrix); - painter.state.setProjectionMatrix(projectionX, projectionY, - painter.backBufferWidth / scaleX, painter.backBufferHeight / scaleY, + painter.state.setProjectionMatrix(bounds.x, bounds.y, stageWidth, stageHeight, stageWidth, stageHeight, stage.cameraPosition); render(painter); @@ -1149,6 +1132,10 @@ package starling.display } } + + public function get sort():Number { return _sort; } + public function set sort(value:Number):void { _sort = value;} + /** The display object container that contains this display object. */ public function get parent():DisplayObjectContainer { return _parent; } diff --git a/starling/src/starling/display/DisplayObjectContainer.as b/starling/src/starling/display/DisplayObjectContainer.as index 2ff7a0ad0..3f8166ebb 100644 --- a/starling/src/starling/display/DisplayObjectContainer.as +++ b/starling/src/starling/display/DisplayObjectContainer.as @@ -77,12 +77,12 @@ package starling.display private static var sSortBuffer:Vector. = new []; private static var sCacheToken:BatchToken = new BatchToken(); - public static const ON_X:int = 0; - public static const ON_Y:int = 1; - public static const ON_Z:int = 2; - public static const ON_X_REVERSE:int = 4; - public static const ON_Y_REVERSE:int = 5; - public static const ON_Z_REVERSE:int = 6; + public static const ON_SORT:int = 0; + public static const ON_X:int = 1; + public static const ON_Y:int = 2; + public static const ON_SORT_REVERSE:int = 4; + public static const ON_X_REVERSE:int = 5; + public static const ON_Y_REVERSE:int = 6; // construction @@ -275,7 +275,7 @@ package starling.display setRequiresRedraw(); } - public function sortChildrenOnKey(key:int):void { + public function sortChildrenOnKey(key:int = ON_SORT):void { sSortBuffer.length = _children.length; mergeSortOnKey(_children, key, 0, _children.length, sSortBuffer); sSortBuffer.length = 0; @@ -609,11 +609,11 @@ package starling.display } } break; - case ON_Z_REVERSE: + default: for (i = 0; i < length; i++) { if (l < startIndex + halfLength && - (r == endIndex || input[l].z > input[r].z)) + (r == endIndex || input[l].sort > input[r].sort)) { buffer[i] = input[l]; l++; From 8d6bb565c07c0a9680169d6aba37d20ee383bb6f Mon Sep 17 00:00:00 2001 From: JohnBlackburne Date: Wed, 8 Mar 2017 06:01:51 +0000 Subject: [PATCH 5/6] Simplify Simplify. Now adds no public methods to class, and implements only one method, sorting on the 'sort' field, which it calls if no function is supplied. --- .../display/DisplayObjectContainer.as | 155 +++--------------- 1 file changed, 26 insertions(+), 129 deletions(-) diff --git a/starling/src/starling/display/DisplayObjectContainer.as b/starling/src/starling/display/DisplayObjectContainer.as index 3f8166ebb..11a3e8dda 100644 --- a/starling/src/starling/display/DisplayObjectContainer.as +++ b/starling/src/starling/display/DisplayObjectContainer.as @@ -77,13 +77,6 @@ package starling.display private static var sSortBuffer:Vector. = new []; private static var sCacheToken:BatchToken = new BatchToken(); - public static const ON_SORT:int = 0; - public static const ON_X:int = 1; - public static const ON_Y:int = 2; - public static const ON_SORT_REVERSE:int = 4; - public static const ON_X_REVERSE:int = 5; - public static const ON_Y_REVERSE:int = 6; - // construction /** @private */ @@ -270,14 +263,11 @@ package starling.display public function sortChildren(compareFunction:Function):void { sSortBuffer.length = _children.length; - mergeSort(_children, compareFunction, 0, _children.length, sSortBuffer); - sSortBuffer.length = 0; - setRequiresRedraw(); - } - - public function sortChildrenOnKey(key:int = ON_SORT):void { - sSortBuffer.length = _children.length; - mergeSortOnKey(_children, key, 0, _children.length, sSortBuffer); + if (compareFunction) { + mergeSort(_children, compareFunction, 0, _children.length, sSortBuffer); + } else { + mergeSortNoFunction(_children, 0, _children.length, sSortBuffer); + } sSortBuffer.length = 0; setRequiresRedraw(); } @@ -506,132 +496,39 @@ package starling.display input[i] = buffer[int(i - startIndex)]; } } - - private static function mergeSortOnKey(input:Vector., key:int, + + private static function mergeSortNoFunction(input:Vector., startIndex:int, length:int, - buffer:Vector.):void - { - // This is a port of the C++ merge sort algorithm shown here: - // http://www.cprogramming.com/tutorial/computersciencetheory/mergesort.html + buffer:Vector.):void { - if (length > 1) - { + if (length > 1) { var i:int; var endIndex:int = startIndex + length; var halfLength:int = length / 2; - var l:int = startIndex; // current position in the left subvector - var r:int = startIndex + halfLength; // current position in the right subvector + var l:int = startIndex; + var r:int = startIndex + halfLength; - // sort each subvector - mergeSortOnKey(input, key, startIndex, halfLength, buffer); - mergeSortOnKey(input, key, startIndex + halfLength, length - halfLength, buffer); + mergeSortNoFunction(input, startIndex, halfLength, buffer); + mergeSortNoFunction(input, startIndex + halfLength, length - halfLength, buffer); - // merge the vectors, using the buffer vector for temporary storage - switch(key) - { - case ON_X: - for (i = 0; i < length; i++) - { - if (l < startIndex + halfLength && - (r == endIndex || input[l].x <= input[r].x)) - { - buffer[i] = input[l]; - l++; - } - else - { - buffer[i] = input[r]; - r++; - } - } - break; - case ON_Y: - for (i = 0; i < length; i++) - { - if (l < startIndex + halfLength && - (r == endIndex || input[l].y <= input[r].y)) - { - buffer[i] = input[l]; - l++; - } - else - { - buffer[i] = input[r]; - r++; - } - } - break; - case ON_Z: - for (i = 0; i < length; i++) - { - if (l < startIndex + halfLength && - (r == endIndex || input[l].z <= input[r].z)) - { - buffer[i] = input[l]; - l++; - } - else - { - buffer[i] = input[r]; - r++; - } - } - break; - case ON_X_REVERSE: - for (i = 0; i < length; i++) - { - if (l < startIndex + halfLength && - (r == endIndex || input[l].x > input[r].x)) - { - buffer[i] = input[l]; - l++; - } - else - { - buffer[i] = input[r]; - r++; - } - } - break; - case ON_Y_REVERSE: - for (i = 0; i < length; i++) - { - if (l < startIndex + halfLength && - (r == endIndex || input[l].y > input[r].y)) - { - buffer[i] = input[l]; - l++; - } - else - { - buffer[i] = input[r]; - r++; - } - } - break; - default: - for (i = 0; i < length; i++) - { - if (l < startIndex + halfLength && - (r == endIndex || input[l].sort > input[r].sort)) - { - buffer[i] = input[l]; - l++; - } - else - { - buffer[i] = input[r]; - r++; - } - } - break; + for (i = 0; i < length; i++) { + if (l < startIndex + halfLength && + (r == endIndex || input[l].sort <= input[r].sort)) + { + buffer[i] = input[l]; + l++; + } + else + { + buffer[i] = input[r]; + r++; + } } - // copy the sorted subvector back to the input + for(i = startIndex; i < endIndex; i++) input[i] = buffer[int(i - startIndex)]; } } - /** @private */ internal function getChildEventListeners(object:DisplayObject, eventType:String, From b13b6138c1d0388cc62e3d096e68dd73c54ce849 Mon Sep 17 00:00:00 2001 From: JohnBlackburne Date: Wed, 8 Mar 2017 08:53:08 +0000 Subject: [PATCH 6/6] Update to match latest Starling --- .../src/starling/display/DisplayObject.as | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/starling/src/starling/display/DisplayObject.as b/starling/src/starling/display/DisplayObject.as index af0fbcb09..9392d260d 100755 --- a/starling/src/starling/display/DisplayObject.as +++ b/starling/src/starling/display/DisplayObject.as @@ -141,7 +141,7 @@ package starling.display private var _orientationChanged:Boolean; private var _is3D:Boolean; private var _maskee:DisplayObject; - private var _sort:Number; + private var _sort:uint; // internal members (for fast access on rendering) @@ -396,24 +396,42 @@ package starling.display */ public function drawToBitmapData(out:BitmapData=null):BitmapData { + var painter:Painter = Starling.painter; var stage:Stage = Starling.current.stage; - var stageWidth:Number = stage.stageWidth; + var viewPort:Rectangle = Starling.current.viewPort; + var stageWidth:Number = stage.stageWidth; var stageHeight:Number = stage.stageHeight; - var scale:Number = Starling.contentScaleFactor; - var painter:Painter = Starling.painter; - var bounds:Rectangle = this is Stage ? - stage.getStageBounds(this, sHelperRect) : getBounds(_parent, sHelperRect); + var scaleX:Number = viewPort.width / stageWidth; + var scaleY:Number = viewPort.height / stageHeight; + var backBufferScale:Number = painter.backBufferScaleFactor; + var projectionX:Number, projectionY:Number; + var bounds:Rectangle; - if (out == null) - out = new BitmapData(Math.ceil(bounds.width * scale), - Math.ceil(bounds.height * scale)); + if (this is Stage) + { + projectionX = viewPort.x < 0 ? -viewPort.x / scaleX : 0.0; + projectionY = viewPort.y < 0 ? -viewPort.y / scaleY : 0.0; + + out ||= new BitmapData(painter.backBufferWidth * backBufferScale, + painter.backBufferHeight * backBufferScale); + } + else + { + bounds = getBounds(_parent, sHelperRect); + projectionX = bounds.x; + projectionY = bounds.y; + + out ||= new BitmapData(Math.ceil(bounds.width * scaleX * backBufferScale), + Math.ceil(bounds.height * scaleY * backBufferScale)); + } painter.clear(); painter.pushState(); painter.state.renderTarget = null; painter.state.setModelviewMatricesToIdentity(); painter.setStateTo(transformationMatrix); - painter.state.setProjectionMatrix(bounds.x, bounds.y, stageWidth, stageHeight, + painter.state.setProjectionMatrix(projectionX, projectionY, + painter.backBufferWidth / scaleX, painter.backBufferHeight / scaleY, stageWidth, stageHeight, stage.cameraPosition); render(painter); @@ -1133,8 +1151,8 @@ package starling.display } - public function get sort():Number { return _sort; } - public function set sort(value:Number):void { _sort = value;} + public function get sort():uint { return _sort; } + public function set sort(value:uint):void { _sort = value;} /** The display object container that contains this display object. */ public function get parent():DisplayObjectContainer { return _parent; }