CGAffineTranformRotate atan2 inaccuration

為{幸葍}努か 提交于 2019-12-10 17:12:10

问题


I'm making transition of my view and always happens something weird, I mean I calculate angle using this code: angle = atan2f(currentTouchPoint.y - center.y, currentTouchPoint.x - center.x) - atan2f(previousTouchPoint.y - center.y, previousTouchPoint.x - center.x);

And the view rotates but not properly. I mean it rotates in proper direction but the angle is always inaccurate about +/- 0.05 radians. And when I tap again, view rotates into proper position. Any adivices? It's important to me to get angle accurate to 5 places after comma.

Some NSLog to show you the problem:

First rotation first tap and second tap
2012-01-07 01:01:26.283 Wheel[9080:707] Angle: 0.598412 
2012-01-07 01:01:29.281 Wheel[9080:707] Angle: -0.070008
Second rotation first tap and second tap
2012-01-07 01:01:31.103 Wheel[9080:707] Angle: -0.679809 
2012-01-07 01:01:32.450 Wheel[9080:707] Angle: 0.092595
Third rotation first tap and second tap
2012-01-07 01:01:35.745 Wheel[9080:707] Angle: 0.607844 
2012-01-07 01:01:36.945 Wheel[9080:707] Angle: -0.064927 
Fourth rotation first tap and second tap
2012-01-07 01:01:41.073 Wheel[9080:707] Angle: -0.635756 
2012-01-07 01:01:41.920 Wheel[9080:707] Angle: 0.052361 

And I forget to tell you, condition, the difference between points is farther the inaccuration is bigger.

EDIT:

Circle *view = (Circle *) [self view];



for (CircleThumb *thumb in view.subviews) {


    CGPoint point = [thumb convertPoint:thumb.centerPoint toView:nil];
    CircleThumb *shadow = [[view.overlayView subviews] lastObject];
    CGPoint centralPoint = [shadow convertPoint:shadow.centerPoint toView:nil];
    CGRect shadowRect = [shadow.superview convertRect:shadow.frame toView:nil];

    if (CGRectContainsPoint(shadowRect, point) == YES) {
        CGPoint pointInShadowRect = [thumb convertPoint:thumb.centerPoint toView:shadow];
        if (CGPathContainsPoint(shadow.arc.CGPath, NULL, pointInShadowRect, NULL)) {


            CGAffineTransform current = view.transform;
            CGPoint center = view.window.center;
            CGPoint currentTouchPoint =centralPoint;
            CGPoint previousTouchPoint = point;

          long double angle = atan2f(currentTouchPoint.y - center.y, currentTouchPoint.x - center.x) - atan2f(previousTouchPoint.y - center.y, previousTouchPoint.x - center.x);

            [UIView animateWithDuration:0.3f animations:^{
                [view setTransform:CGAffineTransformRotate(current, angle)];
            }];
            [view.delegate circle:view didMoveToSegment:thumb.tag thumb:thumb];


            NSLog(@"Angle: %Lf ", angle);
            break;
        }
    }            
}

This is code which is part of '- touchesEnded: withEvent:' implementation

I'm making control similar to the one in Convert bot. the "wheels" from my app and from convertbot look similar but mine uses custom drawing.

So Circle is a UIView which we rotate. Circle has got subviews - CircleThumbs. thumb represents single segment of the circle. Points are calculated properly but I won't explain why, because there's no need.


回答1:


An atan calculation is never entirely correct. And you let iOS calculate cos and sin out of the angle again (cgaffinetransformRotate does that). So you're stacking up trigonometric inaccuracies. And since you calculate only the difference wi the previous angle, I imagine that you are also stacking up inaccuracies over the multiple touch events.

If it is to rotate something, then there is no reason to use trigonometry or angles. You can implement this completely using linear algebra. Something like this:

v.x = touch.x - center.x; v.y = touch.y - center.y;
float length = sqrt(v.x*v.x + v.y* v.y);
if (length < 5 ) break;
v.x /= length; v.y /= length;

// the rotation matrix is
//  v.x -v.y
//  v.y  v.x

rot.x = previous.x * v.x + previous.y * v.y;
rot.y = previous.x * v.y - previous.y * v.x;

CGAffineTransform rotate = CGAffineTransformMake( rot.x, rot.y, -rot.y, rot.x, 0,0);
...
[view SetTransform:CGAffineTransformConcat ( current, rotate )];
...

previous.x = v.x;
previous.y = v.y;

This pseudo code calculates a normalized vector for current point (the one for the previous point was also stored). Then we define a matrix that rotates the previous vector into the new vector. That matrix is concatenated with the existing transformation.

It would be better if the rotation wasn't always calculated from the previous state, but rather from an initial state. The trick is to calculate 'previous' only for the touchdown.



来源:https://stackoverflow.com/questions/8766128/cgaffinetranformrotate-atan2-inaccuration

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!