[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: 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);
      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];

  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.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]