public void UpdateRotation(VirtualObject virtualObject, CGPoint span) { var midPointToFirstTouch = span.Divide(2f); var currentAngle = Math.Atan2(midPointToFirstTouch.X, midPointToFirstTouch.Y); var currentAngleToInitialFingerAngle = InitialFingerAngle - currentAngle; if (!RotationThresholdPassed) { var threshold = RotationThreshold; if (TranslationThresholdPassed || ScaleThresholdPassed) { threshold = RotationThresholdHarder; } if ((float)Math.Abs(currentAngleToInitialFingerAngle) > threshold) { RotationThresholdPassed = true; // Change the initial object angle to prevent a sudden jump after crossing the threshold. if (currentAngleToInitialFingerAngle > 0f) { InitialObjectAngle += threshold; } else { InitialObjectAngle -= threshold; } } } if (RotationThresholdPassed) { // Note: // For looking down on the object (99% of all use cases), we need to subtract the angle. // To make rotation also work correctly when looking from below the object one would have to // flip the sign of the angle depending on whether the object is above or below the camera... var x = virtualObject.EulerAngles.X; var y = InitialObjectAngle - currentAngleToInitialFingerAngle; var z = virtualObject.EulerAngles.Z; virtualObject.EulerAngles = new SCNVector3(x, (float)y, z); LastUsedObject = virtualObject; } }