Увеличьте масштаб повернутого изображения внутри прокрутки, чтобы уместить (заполнить) рамку наложения
Благодаря этому вопросу и ответу у меня теперь есть рабочие средства обнаружения, когда произвольно повернутое изображение не полностью выходит за обрезанный прямоугольник.
Следующим шагом будет выяснить, как правильно настроить масштабирование с прокруткой, чтобы убедиться, что внутри прямоугольника обрезки нет пустых мест. Чтобы уточнить, я хочу увеличить (увеличить) изображение; прямоугольник обрезки должен оставаться не трансформированным.
Структура иерархии выглядит следующим образом:
containing UIScrollView
UIImageView (this gets arbitrarily rotated)
crop rect overlay view
... где UIImageView также можно увеличивать и панорамировать внутри scrollView.
Происходят 4 события жестов, которые необходимо учитывать:
- Жест панорамирования (готово): выполняется путем обнаружения, если оно было панорамировано неправильно, и сбрасывает contentOffset.
- Вращение CGAffineTransform
- Прокрутка зум
- Регулировка прямоугольной рамки кадрирования
Насколько я могу судить, я должен иметь возможность использовать одну и ту же логику для 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);