[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: NSView boundsRotation
From: |
Fred Kiefer |
Subject: |
Re: NSView boundsRotation |
Date: |
Wed, 20 Jul 2011 22:52:19 +0200 |
User-agent: |
Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.2.18) Gecko/20110616 SUSE/3.1.11 Thunderbird/3.1.11 |
Just to keep you updated on this issue. I added the frame and bounds
rotation as instance variables to the NSView and this did not change the
test results. I now think that Apple is using a different code path for
setBoundsRotation: that doesn't call rotateByAngle:. The second method
does a relative rotation of the view and as far as I know at the moment
our code there is correct. But in setBoundsRotation: strange things seem
to happen. I tried to reimplement this method as an absolute rotation,
but even that yields different results from what Apple produces. You can
find the code below. The strange thing is that when using the (commented
out) simpler code that sets just the rotation component of the matrix,
we get exactly the results Apple has for our test case. Of course this
code is wrong in the general case and I don't have any new idea left at
the moment. Any comments from somebody?
I also checked the Apple header file for NSView and didn't see an
instance variable where they could be storing the rotation values. Maybe
even that theory was wrong?
I added a few more tests to our NSView bounds scaling test case and will
commit this soon.
- (void) setBoundsRotation: (CGFloat)angle
{
if (_boundsMatrix != nil)
{
NSAffineTransformStruct ts = [_boundsMatrix transformStruct];
CGFloat sx, sy;
sx = sqrt(ts.m11 * ts.m11 + ts.m21 * ts.m21);
sy = sqrt(ts.m12 * ts.m12 + ts.m22 * ts.m22);
ts.m11 = sx * cos(M_PI * angle / 180);
ts.m12 = sy * sin(M_PI * angle / 180);
ts.m21 = -sx * sin(M_PI * angle / 180);
ts.m22 = sy * cos(M_PI * angle / 180);
// ts.m12 = ts.m22 * tan(M_PI * angle / 180);
// ts.m21 = -ts.m11 * tan(M_PI * angle / 180);
[_boundsMatrix setTransformStruct: ts];
// NSLog(@"new matrix %@", _boundsMatrix);
}
else
{
NSSize scale;
_boundsMatrix = [NSAffineTransform new];
scale = _computeScale(_frame.size, _bounds.size);
[_boundsMatrix scaleXBy: scale.width yBy: scale.height];
[_boundsMatrix translateXBy: -_bounds.origin.x
yBy: -_bounds.origin.y];
[_boundsMatrix rotateByDegrees: angle];
}
_boundsRotation = angle;
_is_rotated_from_base = _is_rotated_or_scaled_from_base = YES;
{
NSAffineTransform *matrix;
NSRect frame = _frame;
frame.origin = NSMakePoint(0, 0);
matrix = [_boundsMatrix copy];
[matrix invert];
[matrix boundingRectFor: frame result: &_bounds];
RELEASE(matrix);
}
if (_coordinates_valid)
{
(*invalidateImp)(self, invalidateSel);
}
[self resetCursorRects];
if (_post_bounds_changes)
{
[nc postNotificationName: NSViewBoundsDidChangeNotification
object: self];
}
}
On 14.07.2011 11:02, Fred Kiefer wrote:
I spend some time to find out why the NSView_bounds_scale.m test is
failing and now I am pretty sure I have the solution, although I don't
like it.
The problem here is the boundsRotation. Apple seems to have a very
specific concept here. The value returned by that method (and used
internally by methods like setBoundsRotation:) isn't what you would
expect. I think they store this value directly for the NSView and adjust
it only from the rotation methods. That is, this value has nothing to do
with the actual rotation value of the current bounds transformation
matrix, as we implemented it in GNUstep. The important difference is
when you combine a scale operation with a rotation. Independent of the
order of these operations Apple comes up with the same rotation value,
whereas GNUstep will end up with different values.
As there is no use in saying that our results are mathematically
correct, I suggest that we move over to the Apple way of doing things
and start storing the rotation in the NSView. This saves us one
expensive call to atan2() as well.
The same change may also be needed for the frame rotation, but I will
have to write more test code to verify this.