204 lines
7.0 KiB
Objective-C
204 lines
7.0 KiB
Objective-C
//
|
|
// CGGeometry+RSKImageCropper.m
|
|
//
|
|
// Copyright (c) 2015 Ruslan Skorb, http://ruslanskorb.com/
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
//
|
|
|
|
#import "CGGeometry+RSKImageCropper.h"
|
|
|
|
// K is a constant such that the accumulated error of our floating-point computations is definitely bounded by K units in the last place.
|
|
#ifdef CGFLOAT_IS_DOUBLE
|
|
static const CGFloat kK = 9;
|
|
#else
|
|
static const CGFloat kK = 0;
|
|
#endif
|
|
|
|
const CGPoint RSKPointNull = { INFINITY, INFINITY };
|
|
|
|
CGPoint RSKRectCenterPoint(CGRect rect)
|
|
{
|
|
return CGPointMake(CGRectGetMinX(rect) + CGRectGetWidth(rect) / 2,
|
|
CGRectGetMinY(rect) + CGRectGetHeight(rect) / 2);
|
|
}
|
|
|
|
CGRect RSKRectNormalize(CGRect rect)
|
|
{
|
|
CGPoint origin = rect.origin;
|
|
|
|
CGFloat x = origin.x;
|
|
CGFloat y = origin.y;
|
|
CGFloat ceilX = ceil(x);
|
|
CGFloat ceilY = ceil(y);
|
|
|
|
if (fabs(ceilX - x) < pow(10, kK) * RSK_EPSILON * fabs(ceilX + x) || fabs(ceilX - x) < RSK_MIN ||
|
|
fabs(ceilY - y) < pow(10, kK) * RSK_EPSILON * fabs(ceilY + y) || fabs(ceilY - y) < RSK_MIN) {
|
|
|
|
origin.x = ceilX;
|
|
origin.y = ceilY;
|
|
} else {
|
|
origin.x = floor(x);
|
|
origin.y = floor(y);
|
|
}
|
|
|
|
CGSize size = rect.size;
|
|
|
|
CGFloat width = size.width;
|
|
CGFloat height = size.height;
|
|
CGFloat ceilWidth = ceil(width);
|
|
CGFloat ceilHeight = ceil(height);
|
|
|
|
if (fabs(ceilWidth - width) < pow(10, kK) * RSK_EPSILON * fabs(ceilWidth + width) || fabs(ceilWidth - width) < RSK_MIN ||
|
|
fabs(ceilHeight - height) < pow(10, kK) * RSK_EPSILON * fabs(ceilHeight + height) || fabs(ceilHeight - height) < RSK_MIN) {
|
|
|
|
size.width = ceilWidth;
|
|
size.height = ceilHeight;
|
|
} else {
|
|
size.width = floor(width);
|
|
size.height = floor(height);
|
|
}
|
|
|
|
return CGRectMake(origin.x, origin.y, size.width, size.height);
|
|
}
|
|
|
|
CGRect RSKRectScaleAroundPoint(CGRect rect, CGPoint point, CGFloat sx, CGFloat sy)
|
|
{
|
|
CGAffineTransform translationTransform, scaleTransform;
|
|
translationTransform = CGAffineTransformMakeTranslation(-point.x, -point.y);
|
|
rect = CGRectApplyAffineTransform(rect, translationTransform);
|
|
scaleTransform = CGAffineTransformMakeScale(sx, sy);
|
|
rect = CGRectApplyAffineTransform(rect, scaleTransform);
|
|
translationTransform = CGAffineTransformMakeTranslation(point.x, point.y);
|
|
rect = CGRectApplyAffineTransform(rect, translationTransform);
|
|
return rect;
|
|
}
|
|
|
|
bool RSKPointIsNull(CGPoint point)
|
|
{
|
|
return CGPointEqualToPoint(point, RSKPointNull);
|
|
}
|
|
|
|
CGPoint RSKPointRotateAroundPoint(CGPoint point, CGPoint pivot, CGFloat angle)
|
|
{
|
|
CGAffineTransform translationTransform, rotationTransform;
|
|
translationTransform = CGAffineTransformMakeTranslation(-pivot.x, -pivot.y);
|
|
point = CGPointApplyAffineTransform(point, translationTransform);
|
|
rotationTransform = CGAffineTransformMakeRotation(angle);
|
|
point = CGPointApplyAffineTransform(point, rotationTransform);
|
|
translationTransform = CGAffineTransformMakeTranslation(pivot.x, pivot.y);
|
|
point = CGPointApplyAffineTransform(point, translationTransform);
|
|
return point;
|
|
}
|
|
|
|
CGFloat RSKPointDistance(CGPoint p1, CGPoint p2)
|
|
{
|
|
CGFloat dx = p1.x - p2.x;
|
|
CGFloat dy = p1.y - p2.y;
|
|
return sqrt(pow(dx, 2) + pow(dy, 2));
|
|
}
|
|
|
|
RSKLineSegment RSKLineSegmentMake(CGPoint start, CGPoint end)
|
|
{
|
|
return (RSKLineSegment){ start, end };
|
|
}
|
|
|
|
RSKLineSegment RSKLineSegmentRotateAroundPoint(RSKLineSegment line, CGPoint pivot, CGFloat angle)
|
|
{
|
|
return RSKLineSegmentMake(RSKPointRotateAroundPoint(line.start, pivot, angle),
|
|
RSKPointRotateAroundPoint(line.end, pivot, angle));
|
|
}
|
|
|
|
/*
|
|
Equations of line segments:
|
|
|
|
pA = ls1.start + uA * (ls1.end - ls1.start)
|
|
pB = ls2.start + uB * (ls2.end - ls2.start)
|
|
|
|
In the case when `pA` is equal `pB` we have:
|
|
|
|
x1 + uA * (x2 - x1) = x3 + uB * (x4 - x3)
|
|
y1 + uA * (y2 - y1) = y3 + uB * (y4 - y3)
|
|
|
|
uA = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3) / (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
|
|
uB = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3) / (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
|
|
|
|
numeratorA = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)
|
|
denominatorA = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
|
|
|
|
numeratorA = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)
|
|
denominatorB = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
|
|
|
|
[1] Denominators are equal.
|
|
[2] If numerators and denominator are zero, then the line segments are coincident. The point of intersection is the midpoint of the line segment.
|
|
|
|
x = (x1 + x2) * 0.5
|
|
y = (y1 + y2) * 0.5
|
|
|
|
or
|
|
|
|
x = (x3 + x4) * 0.5
|
|
y = (y3 + y4) * 0.5
|
|
|
|
[3] If denominator is zero, then the line segments are parallel. There is no point of intersection.
|
|
[4] If `uA` and `uB` is included into the interval [0, 1], then the line segments intersects in the point (x, y).
|
|
|
|
x = x1 + uA * (x2 - x1)
|
|
y = y1 + uA * (y2 - y1)
|
|
|
|
or
|
|
|
|
x = x3 + uB * (x4 - x3)
|
|
y = y3 + uB * (y4 - y3)
|
|
*/
|
|
CGPoint RSKLineSegmentIntersection(RSKLineSegment ls1, RSKLineSegment ls2)
|
|
{
|
|
CGFloat x1 = ls1.start.x;
|
|
CGFloat y1 = ls1.start.y;
|
|
CGFloat x2 = ls1.end.x;
|
|
CGFloat y2 = ls1.end.y;
|
|
CGFloat x3 = ls2.start.x;
|
|
CGFloat y3 = ls2.start.y;
|
|
CGFloat x4 = ls2.end.x;
|
|
CGFloat y4 = ls2.end.y;
|
|
|
|
CGFloat numeratorA = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
|
|
CGFloat numeratorB = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3);
|
|
CGFloat denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
|
|
|
|
// Check the coincidence.
|
|
if (fabs(numeratorA) < RSK_EPSILON && fabs(numeratorB) < RSK_EPSILON && fabs(denominator) < RSK_EPSILON) {
|
|
return CGPointMake((x1 + x2) * 0.5, (y1 + y2) * 0.5);
|
|
}
|
|
|
|
// Check the parallelism.
|
|
if (fabs(denominator) < RSK_EPSILON) {
|
|
return RSKPointNull;
|
|
}
|
|
|
|
// Check the intersection.
|
|
CGFloat uA = numeratorA / denominator;
|
|
CGFloat uB = numeratorB / denominator;
|
|
if (uA < 0 || uA > 1 || uB < 0 || uB > 1) {
|
|
return RSKPointNull;
|
|
}
|
|
|
|
return CGPointMake(x1 + uA * (x2 - x1), y1 + uA * (y2 - y1));
|
|
}
|