-
Notifications
You must be signed in to change notification settings - Fork 824
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make setOrientationChanged() public #954
Comments
Aha, I see what you mean! You're right, that probably would not hurt. I guess May I ask what exactly you are doing with the matrix? So that I understand the use-case behind this request. Thanks in advance! |
I', doing exactly what I showed in the example - translating it on the y-axis based on a given parameter - I have this sliding panel which shows one one of it parts based on the param's value (it's a Here's my code: public function get upgradeDecksVisibility():Number { return _upgradeDecksVisibility; }
public function set upgradeDecksVisibility(value:Number):void {
if(_upgradesSprite.y == 0)
return;
_upgradeDecksVisibility = value;
setOrientationChangedWorkaround();
}
override public function get transformationMatrix():Matrix {
if(_upgradesSprite.y == 0)
return super.transformationMatrix;
var mat:Matrix = super.transformationMatrix;
mat.translate(0, -_upgradesSprite.y * _upgradeDecksVisibility);
return mat;
}
override public function set transformationMatrix(matrix:Matrix):void {
super.transformationMatrix = matrix;
if(_upgradesSprite.y == 0)
return;
setOrientationChangedWorkaround();
} |
And yes, |
After playing with it for a while I found another problem. If you take a look at how I changed To solve this I'd need another method as well, something to read Also, it would be great to have more control when overriding public function get transformationMatrix():Matrix
{
if (_orientationChanged)
{
_orientationChanged = false;
rebuildTransformationMatrix(_transformationMatrix, _x, _y, _pivotX, _pivotY, _scaleX, _scaleY, _skewX, _skewY, _rotation);
}
return _transformationMatrix;
protected function rebuildTransformationMatrix(output:Matrix, x:Number, y:Number, pivotX:Number, pivotY:Number, scaleX:Number, scaleY:Number, skewX:Number, skewY:Number, rotation:Number):void
{
if (_skewX == 0.0 && _skewY == 0.0)
{
// optimization: no skewing / rotation simplifies the matrix math
if (_rotation == 0.0)
{
_transformationMatrix.setTo(_scaleX, 0.0, 0.0, _scaleY,
_x - _pivotX * _scaleX, _y - _pivotY * _scaleY);
}
else
{
var cos:Number = Math.cos(_rotation);
var sin:Number = Math.sin(_rotation);
var a:Number = _scaleX * cos;
var b:Number = _scaleX * sin;
var c:Number = _scaleY * -sin;
var d:Number = _scaleY * cos;
var tx:Number = _x - _pivotX * a - _pivotY * c;
var ty:Number = _y - _pivotX * b - _pivotY * d;
_transformationMatrix.setTo(a, b, c, d, tx, ty);
}
}
else
{
_transformationMatrix.identity();
_transformationMatrix.scale(_scaleX, _scaleY);
MatrixUtil.skew(_transformationMatrix, _skewX, _skewY);
_transformationMatrix.rotate(_rotation);
_transformationMatrix.translate(_x, _y);
if (_pivotX != 0.0 || _pivotY != 0.0)
{
// prepend pivot transformation
_transformationMatrix.tx = _x - _transformationMatrix.a * _pivotX
- _transformationMatrix.c * _pivotY;
_transformationMatrix.ty = _y - _transformationMatrix.b * _pivotX
- _transformationMatrix.d * _pivotY;
}
}
} |
Ah, I see! Mhm, you're right, that would be necessary then, too. Hm ... I understand your request, and that those changes would make it possible to implement your transformation-matrix modification like that. But isn't there an easier way to achieve this? I.e. how would you approach this with the original display list API? (My problem is that I don't think that many developers will need to do what you're doing here, and I'm hesitant to enlarge the API footprint for something that's maybe not entirely necessary.) |
Well, I think many developers won't need all of the fancy architectural choices in Starling, but it's still a good thing to have them there anyway, right? :) I think that this small refactoring (adding the Here's another scenario that I'll have to implement in my game (it's a card game btw) that would require a new property: |
Well, I think many developers won't need all of the fancy architectural choices in Starling, but it's still a good thing to have them there anyway, right? :) Yeah, of course, I'm all with you — what I was trying to say is that I have to constantly question what to add and what not. Or, in other words, it's equally important to decide what's "in" than what's "out". 😉 |
Thanks for the additional examples. So, in a nutshell, you want to be able to have additional properties on a display object influence the transformation matrix that's created. (And make sure that on changing any of those properties, the matrix is recreated.) |
Yeah, I understand. But I see this kind of approach - having an internally used method which "get's it done" - separated from the internally used logic (like with the
Yes, exactly. |
Just getting back to this. Did you make any changes maybe? |
BTW, I just noticed there's a similar thing for Sprite3D - Also, currently calling |
Sorry, I've been very busy during the last days to finally get the "Starling Handbook" out of the door. I hope to be able to do that next week — and then I'll look into all those open issues and make a decision and/or continue the discussion. Sorry for the delay! I'm really bad at multitasking these things. 😉 |
Sure, I understand, been there :) Would be great if you made the change next week however, I need this in quite a lot of places in my project and I have to rely on local hacks currently :) |
Daniel, if you want me to create a pull request for this, let me know. |
Thanks a lot for the offer, Łukasz, but I want to play around this with myself, anyway — check out performance considerations, etc. Thanks, anyway! I'm afraid, though, it definitely won't be ready this week. I really wanted to make it happen, but some personal issues intervened ... sorry!! |
Sure, not a problem. I don't think there're any performance issues to consider here however :) |
BTW, a very similar approach to the one I proposed is already implemented in Sprite3D (a method called Would be cool to replace it with |
OK, so I played a bit more with this and here's what I've changed:
protected var _orientationChanged:Boolean; // was private
/*...*/
protected function setOrientationChanged():void // was private
/*...*/
public function get transformationMatrix():Matrix
{
if (_orientationChanged)
{
_orientationChanged = false;
rebuildTransformationMatrices(_transformationMatrix, null, true, _x, _y, NaN, _pivotX, _pivotY, NaN, _scaleX, _scaleY, NaN, _skewX, _skewY, NaN, NaN, NaN, _rotation);
}
return _transformationMatrix;
}
protected function rebuildTransformationMatrices(output2D:Matrix, output3D:Matrix3D, is2D:Boolean, x:Number, y:Number, z:Number, pivotX:Number, pivotY:Number, pivotZ:Number, scaleX:Number, scaleY:Number, scaleZ:Number, skewX:Number, skewY:Number, skewZ:Number, rotationX:Number, rotationY:Number, rotationZ:Number):void
{
if (skewX == 0.0 && skewY == 0.0)
{
// optimization: no skewing / rotation simplifies the matrix math
if (rotationZ == 0.0)
{
output2D.setTo(scaleX, 0.0, 0.0, scaleY, x - pivotX * scaleX, y - pivotY * scaleY);
}
else
{
var cos:Number = Math.cos(rotationZ);
var sin:Number = Math.sin(rotationZ);
var a:Number = scaleX * cos;
var b:Number = scaleX * sin;
var c:Number = scaleY * -sin;
var d:Number = scaleY * cos;
var tx:Number = x - pivotX * a - pivotY * c;
var ty:Number = y - pivotX * b - pivotY * d;
output2D.setTo(a, b, c, d, tx, ty);
}
}
else
{
output2D.identity();
output2D.scale(scaleX, scaleY);
MatrixUtil.skew(output2D, skewX, skewY);
output2D.rotate(rotationZ);
output2D.translate(x, y);
if (pivotX != 0.0 || pivotY != 0.0)
{
// prepend pivot transformation
output2D.tx = x - output2D.a * pivotX - output2D.c * pivotY;
output2D.ty = y - output2D.b * pivotX - output2D.d * pivotY;
}
}
}
protected static const E:Number = 0.00001; // was private
/*...*/
protected var _transformationChanged:Boolean; // was private
protected var _is2D:Boolean; // was private
/*...*/
public override function get transformationMatrix():Matrix
{
if (_transformationChanged)
{
rebuildTransformationMatrices(_transformationMatrix, _transformationMatrix3D, _is2D, x, y, _z, pivotX, pivotY, _pivotZ, scaleX, scaleY, _scaleZ, skewX, skewY, 0, _rotationX, _rotationY, rotation);
//updateMatrices();
_transformationChanged = false;
}
return _transformationMatrix;
}
/*...*/
public override function get transformationMatrix3D():Matrix3D
{
if (_transformationChanged)
{
rebuildTransformationMatrices(_transformationMatrix, _transformationMatrix3D, _is2D, x, y, _z, pivotX, pivotY, _pivotZ, scaleX, scaleY, _scaleZ, skewX, skewY, 0, _rotationX, _rotationY, rotation);
//updateMatrices();
_transformationChanged = false;
}
return _transformationMatrix3D;
}
/*...*/
override protected function rebuildTransformationMatrices(output2D:Matrix, output3D:Matrix3D, is2D:Boolean, x:Number, y:Number, z:Number, pivotX:Number, pivotY:Number, pivotZ:Number, scaleX:Number, scaleY:Number, scaleZ:Number, skewX:Number, skewY:Number, skewZ:Number, rotationX:Number, rotationY:Number, rotationZ:Number):void {
output3D.identity();
if(scaleX != 1.0 || scaleY != 1.0 || scaleZ != 1.0)
output3D.appendScale(scaleX || E, scaleY || E, scaleZ || E);
if(rotationX != 0.0)
output3D.appendRotation(rad2deg(rotationX), Vector3D.X_AXIS);
if(rotationY != 0.0)
output3D.appendRotation(rad2deg(rotationY), Vector3D.Y_AXIS);
if(rotationZ != 0.0)
output3D.appendRotation(rad2deg(rotationZ), Vector3D.Z_AXIS);
if(x != 0.0 || y != 0.0 || z != 0.0)
output3D.appendTranslation(x, y, z);
if(pivotX != 0.0 || pivotY != 0.0 || pivotZ != 0.0)
output3D.prependTranslation(-pivotX, -pivotY, -pivotZ);
if(is2D)
MatrixUtil.convertTo2D(output3D, output2D);
else
output2D.identity();
} I tested with my project, everything works as expected. Also, I think there's no point in keeping Performance wise, nothing has changed. Rebuilding matrices (which is already a CPU heavy operation) happens in a separate method now, so the cost of the whole thing increased only by the cost of calling a method, which is neglectable when compared to the cost of what this method actually does. |
Daniel, maybe we can get back to this now? I tested it with my current project, I can't see any problems with it. One more thing I had to do was changing |
I'm looking into it right now, Łukasz! Finally ...! I know I must be super annoying to you by now (:bowtie:), but I'm still not 100% sold that this is a necessary change. The main issue is that we're talking about the DisplayObject class — this makes it really special, because 99% of all code that people write is happening in subclasses of that class. Having something Anything that's protected in DisplayObject, on the other hand, is constantly visible to basically everybody. And even if you don't need it, you see that it's there, and you might wonder if calling [In other words, it's not the additional method calls that are problematic — if it were just a case of creating that So, I know you've got some code that depends on such a feature, and it would be useful to you to have that feature like you explained. I can totally understand that. But isn't there a way you could achieve the same with existing functionality? For example, if we take your card example: I had similar requirements in the past, and I always did it by encapsulating the card in a container. The Card class is a DisplayObjectContainer that contains an Image that's the actual card. Then to mark your cards as "tapped" or "exhausted", you can rotate the card Image by 90 degrees (using a property called This worked very well for my use-cases! What should also make us suspicious is that what you want to have is not possible in the standard Flash display list API, and still people got around well with it. This is not a good argument — we should always add functionality to Starling where the original display list is lacking important functionality. But, as I said, in the case of the DisplayObject class, of all classes — we need to be especially careful. I know that this is not what you wanted to hear, but please consider my arguments and check out if you could achieve what you want by different means — not via dirty trick, of course, but simply an alternative approach. |
But the thing is - it is something you have to call when you change the orientation of the object :) The problems is, currently, that you can't call it in your subclass, because it's
For a simple trick, this would work. But it's insanely problematic with more than one such property, sometimes you'd have to put containers inside containers inside containers just to have a simple effect which, with these changes, can be done in a couple of lines (and changed in no time, which is very important when you're designing a feature you're not really sure of how it should work). So, no, definitely this isn't an option.
But you see, what I'm proposing won't change the standard/internal behaviour - it'll just add new ways of doing things. Nothing will change for people using Starling - they won't even notice :) Seriously, these things should be made more accessible, because it won't only make some things possible, it will also make DisplayObject's and Sprite3D's internals more understandable. So, if you don't see any problems in introducing As for For And let me emphasize it one more time: these changes are not aimed at messing with Starling's internals, they do the opposite - they isolate or encapsulate the internally used stuff. Currently the internal implementation is monolithic and not easy to get your head around - you change one thing and you need to make sure you checked where it's used, if it's not overridden somewhere internally, if it won't produce any side effects etc., but with these change you can more easily make sure they're changing only the thing they wan't to change. Because, let's be clear, it's not like by making everything I hope I made myself clear :) And I hope I made you see these won't make things more exposed to errors in the future as Starling changes, but it will in fact make the opposite. |
No, only when you add additional properties that modify the orientation of the object. Normally, you use [ It's not that I don't understand your reasoning; I just don't understand yet how generations of Flash programmers could live without that feature if it lacking makes everything sooo complicated. ;-) But let me play around with the code a little more — perhaps I can come up with a solution I can live with. The main difference in our opinions is maybe that I'm seeing things as implementation details (private) which you want to customise (protected). And my mantra "if in doubt, leave it out", forces me to be very conspicuous, because I'm still having doubts and every method I add to DisplayObject is a method that might be back haunting me forever. ... btw, since it's related to my doubts — how would you have tackled this issue in classic Flash? |
(And, again, there actually is no such thing as (Just to maybe help understand where my hesitation is coming from.) |
Oh, come on :) I hear the same reasoning from people who still code in C every day - how come I now need all of this OOP stuff when we can do almost anything with a bit of structural hacking :) I'm not saying things can't be done the way people used to do it for the past years - I'm looking for ways of doing it better :)
But still, there's a huge difference between And again about this whole idea, it's not really like I'm reinventing the wheel :) I'm proposing to introduce two concepts which have been used in similar scenarios for years.
That's all I'm asking for. As I said, it's not like it encourages to mess with the internals. What we have now forces you to hack the internals. Like with my example, I can either hack it which is not safe and requires a lot of work OR I can go with the container approach which is only a bit more safe (it would still relay on things like how batching is implemented, when things are invalidated, etc.), requires much more work and it a nightmare if you have to introduce changes (because the actual functionality is unnaturally spread amongst a couple of classes). The proposed approach does not have these issues and it does not introduce any new issues - it does not expose the internals, it encapsulates and separates the internals even more than they are separated now; people who want to mess with the internals will be able to do it in a more controlled, safe way, which means less hacks, which means less bugs and more code which will work with the future versions of the framework. |
Okay, you'll find an approach to solve this in the commit I just made. What actually changed my mind was your remark that a lot of the Sprite3D code would also benefit from this change. 😉 I still think that this change is going to be used only rarely, but it's easier to sell if my own code benefits from the change, too. 😜 There are now two additional methods in the
I don't think we really need read-access to Please have a look at these changes and tell me what you think. |
First of all, you're the man, Daniel :) I'll check your changes tomorrow and let you know. I think |
OK, one thing that's not currently there and is quite essential for this idea to really shine - updateTransformationMatrix(output2D:Matrix, output3D:Matrix3D, is2D:Boolean, x:Number, y:Number, z:Number, pivotX:Number, pivotY:Number, pivotZ:Number, scaleX:Number, scaleY:Number, scaleZ:Number, skewX:Number, skewY:Number, skewZ:Number, rotationX:Number, rotationY:Number, rotationZ:Number):void I know it might not look pretty (it's not that bad IMO), but it lets you to modify the input only when overriding override function updateTransformationMatrix(output2D:Matrix, output3D:Matrix3D, is2D:Boolean, x:Number, y:Number, z:Number, pivotX:Number, pivotY:Number, pivotZ:Number, scaleX:Number, scaleY:Number, scaleZ:Number, skewX:Number, skewY:Number, skewZ:Number, rotationX:Number, rotationY:Number, rotationZ:Number):void {
x += someParam * 5;
super.updateTransformationMatrix(/* ... */);
} |
OK, so I gave your changes a look and here are my thoughts:
|
You should now have all the features you need! Let me know if that works for you. I'm still not sold that this problem could not have been solved by wrapping the object in a container, so consider this a special gift for a loyal Starling user. 😉 |
Brilliant, Daniel, I'll give it a go today :)
It could, but as I explained, this would require much more work when making modifications, because often you'd need ti change the entire hierarchy built this way to add a new feature or change how a given feature works. With this approach it's just changing an input param and it works :) And the possibilities are really vast - now with just a couple of lines you can add a new anchor point (similar to pivot), which would work only for some special transformations (I use it myself, so I don't need to change pivot constantly, but I can animate different rotations around a moving point). To push it even further, I think I don't need it that much right now however 😄 but I encourage you to give it a look and think about it if you get'll a minute. And thanks one more time 👍 |
Yes, I'm sure there are ways to make good use of this – maybe I'll be thankful to have it like that, too, soon enough. 😉 As for the render method: I'm all with you with that one, I don't like the way everything's bundled in that DisplayObjectContainer method – but here, performance just overrules everything, IMHO, so it's a very delicate manner. Perhaps it can be "bought" with some optimisations in other parts of the API, though. 😄 |
Daniel, can you change
I understand, but do you think that it would make a difference to have one more method call? It can't be that bad. I know it'd be called each frame but there are things happening which are relatively much, much slower, so it shouldn't matter. |
A similar thing is needed for |
I just simplified As for |
I don't think we're talking about the same thing :) I want to override |
We're talking about one method call per object, per frame in the worst case, and that worst case occurs quite frequently. Do this in a few places, and that quickly adds up! I've spent many many hours optimising method calls away — trust me, in AS3, it does unfortunately make a difference. |
OK, I understand. About the
|
Also, isn't it a bug? |
No, they just store references to the matrices from the super class. |
OK, I see how it works. But you wouldn't need it, if you passed the matrices as params to |
Yes, I'm already working on that. 😉 |
I think this solution is rather clean! Looking forward to your code review. 😉 |
I like the idea with two methods - one for 2D stuff and the other which adds the 3D stuff. A minor thing, it would be cool to have this as a public/protected constant: private static const E:Number = 0.00001; I have found a bug however - It's probably because in my scenario |
Ooops, yes, I could reproduce that error. Fixed with 3060cd1. As for |
OK, it looks like everything works as expected now :) Thanks one more time for making the changes, Daniel! |
I'm glad to hear everything is working fine now! I'm happy I could help. 😎 |
Currently, if you override the
transformationMatrix
property (so your object is translated/scaled/rotated according to something else than x/y/rotation/scale etc.) you need a hack to callsetOrientationChanged()
, like this one:I don't see any risks in making
setOrientationChanged()
public -setRequiresRedraw()
is public after all and used similarly.The text was updated successfully, but these errors were encountered: