123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- //
- // 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));
- }
|