Увеличьте масштаб повернутого изображения внутри прокрутки, чтобы уместить (заполнить) рамку наложения

Благодаря этому вопросу и ответу у меня теперь есть рабочие средства обнаружения, когда произвольно повернутое изображение не полностью выходит за обрезанный прямоугольник.

Следующим шагом будет выяснить, как правильно настроить масштабирование с прокруткой, чтобы убедиться, что внутри прямоугольника обрезки нет пустых мест. Чтобы уточнить, я хочу увеличить (увеличить) изображение; прямоугольник обрезки должен оставаться не трансформированным.

Структура иерархии выглядит следующим образом:

containing UIScrollView
    UIImageView (this gets arbitrarily rotated)
        crop rect overlay view

... где UIImageView также можно увеличивать и панорамировать внутри scrollView.

Происходят 4 события жестов, которые необходимо учитывать:

  1. Жест панорамирования (готово): выполняется путем обнаружения, если оно было панорамировано неправильно, и сбрасывает contentOffset.
  2. Вращение CGAffineTransform
  3. Прокрутка зум
  4. Регулировка прямоугольной рамки кадрирования

Насколько я могу судить, я должен иметь возможность использовать одну и ту же логику для 2, 3 и 4, чтобы отрегулировать zoomScale вида прокрутки, чтобы сделать изображение соответствующим образом.

Как правильно рассчитать коэффициент масштабирования, необходимый для идеального прилегания повернутого изображения к прямоугольнику обрезки?

Чтобы лучше проиллюстрировать то, что я пытаюсь выполнить, вот пример неправильного размера:

введите описание изображения здесь

Мне нужно рассчитать коэффициент масштабирования, необходимый для того, чтобы он выглядел так:

введите описание изображения здесь

Вот код, который я получил, используя решение Олусея ниже. Это работает, когда угол поворота незначителен (например, менее 1 радиан), но что-либо сверх этого, и это действительно шатко.

CGRect visibleRect = [_scrollView convertRect:_scrollView.bounds toView:_imageView];
CGRect cropRect = _cropRectView.frame;

CGFloat rotationAngle = fabs(self.rotationAngle);

CGFloat a = visibleRect.size.height * sinf(rotationAngle);
CGFloat b = visibleRect.size.width * cosf(rotationAngle);
CGFloat c = visibleRect.size.height * cosf(rotationAngle);
CGFloat d = visibleRect.size.width * sinf(rotationAngle);

CGFloat zoomDiff = MAX(cropRect.size.width / (a + b), cropRect.size.height / (c + d));
CGFloat newZoomScale = (zoomDiff > 1) ? zoomDiff : 1.0 / zoomDiff;

[UIView animateWithDuration:0.2
                      delay:0.05
                    options:NO
                 animations:^{
                         [self centerToCropRect:[self convertRect:cropRect toView:self.zoomingView]];
                         _scrollView.zoomScale = _scrollView.zoomScale * newZoomScale;
                 } completion:^(BOOL finished) {
                     if (![self rotatedView:_imageView containsViewCompletely:_cropRectView]) 
                     {
                         // Damn, it's still broken - this happens a lot
                     }
                     else
                     {
                         // Woo! Fixed
                     }
                     _didDetectBadRotation = NO;
                 }];

Обратите внимание, что я использую AutoLayout, который делает фреймы и границы глупыми.

3 ответа

Предположим, что прямоугольник вашего изображения (синий на диаграмме) и прямоугольник кадрирования (красный) имеют одинаковое соотношение сторон и центр. При повороте прямоугольник изображения теперь имеет ограничивающий прямоугольник (зеленый), который является тем, к чему вы хотите масштабировать свой урожай (эффективно, уменьшая изображение).

Для эффективного масштабирования вам необходимо знать размеры нового ограничивающего прямоугольника и использовать масштабный коэффициент, который вписывается в него. Размеры ограничивающего прямоугольника довольно очевидны

(a + b) x (c + d)

Обратите внимание, что каждый сегмент a, b, c, d является соседней или противоположной стороной прямоугольного треугольника, образованного ограничивающим прямоугольником и повернутым прямоугольником изображения.

a = image_rect_height * sin(rotation_angle)
b = image_rect_width * cos(rotation_angle)
c = image_rect_width * sin(rotation_angle)
d = image_rect_height * cos(rotation_angle)

Ваш масштабный коэффициент просто

MAX(crop_rect_width / (a + b), crop_rect_height / (c + d))

Вот справочная схема:

Заполнить рамку оверлея прямоугольником:

Для квадратной обрезки вам нужно знать новые границы повернутого изображения, которые будут заполнять вид обрезки.

Давайте посмотрим на справочную диаграмму:справочная схемаВам нужно найти высоту прямоугольного треугольника (изображение № 2). Обе высоты равны.

CGFloat sinAlpha = sin(alpha);
CGFloat cosAlpha = cos(alpha);
CGFloat hypotenuse = /* calculate */;
CGFloat altitude = hypotenuse * sinAlpha * cosAlpha;

Затем вам нужно рассчитать новую ширину для повернутого изображения и желаемый масштабный коэффициент следующим образом:

 CGFloat newWidth = previousWidth + altitude * 2;
 CGFloat scale = newWidth / previousWidth;

Я реализовал этот метод здесь.

Я отвечу, используя пример кода, но в основном эта проблема становится действительно простой, если вы будете думать в повернутой системе координат.

UIView* container = [[UIView alloc] initWithFrame:CGRectMake(80, 200, 100, 100)];
container.backgroundColor = [UIColor blueColor];

UIView* content2 = [[UIView alloc] initWithFrame:CGRectMake(-50, -50, 150, 150)];
content2.backgroundColor = [[UIColor greenColor] colorWithAlphaComponent:0.5];
[container addSubview:content2];

[self.view setBackgroundColor:[UIColor blackColor]];
[self.view addSubview:container];

[container.layer setSublayerTransform:CATransform3DMakeRotation(M_PI / 8.0, 0, 0, 1)];

//And now the calculations
CGRect containerFrameInContentCoordinates = [content2 convertRect:container.bounds fromView:container];
CGRect unionBounds = CGRectUnion(content2.bounds, containerFrameInContentCoordinates);

CGFloat midX = CGRectGetMidX(content2.bounds);
CGFloat midY = CGRectGetMidY(content2.bounds);

CGFloat scaleX1 = (-1 * CGRectGetMinX(unionBounds) + midX) / midX;
CGFloat scaleX2 = (CGRectGetMaxX(unionBounds) - midX) / midX;
CGFloat scaleY1 = (-1 * CGRectGetMinY(unionBounds) + midY) / midY;
CGFloat scaleY2 = (CGRectGetMaxY(unionBounds) - midY) / midY;

CGFloat scaleX = MAX(scaleX1, scaleX2);
CGFloat scaleY = MAX(scaleY1, scaleY2);
CGFloat scale = MAX(scaleX, scaleY);

content2.transform = CGAffineTransformScale(content2.transform, scale, scale);
Другие вопросы по тегам