Iphone 使用CATTransferM3D将矩形图像转换为四边形

Iphone 使用CATTransferM3D将矩形图像转换为四边形,iphone,ios,cocoa-touch,math,core-animation,Iphone,Ios,Cocoa Touch,Math,Core Animation,我有一个图像和一组四个点(描述一个四边形Q)。我想变换这幅图像,使其符合四边形Q。Photoshop称这种变换为“扭曲”。但根据这幅四边形的来源(图像在空间中移动的透视图),它实际上是比例、旋转和透视矩阵的组合 我想知道是否可以使用CATTransferM3D 4x4矩阵。你有什么关于如何做的提示吗?我试着取四个点并建立16个方程(从A'=A x u中),但没有成功:我不确定我应该使用什么作为z,z',w和w'系数 下图显示了我想做的事情: 以下是一些要点的示例: 276.523, 236.4

我有一个图像和一组四个点(描述一个四边形Q)。我想变换这幅图像,使其符合四边形Q。Photoshop称这种变换为“扭曲”。但根据这幅四边形的来源(图像在空间中移动的透视图),它实际上是比例、旋转和透视矩阵的组合

我想知道是否可以使用CATTransferM3D 4x4矩阵。你有什么关于如何做的提示吗?我试着取四个点并建立16个方程(从A'=A x u中),但没有成功:我不确定我应该使用什么作为z,z',w和w'系数

下图显示了我想做的事情:

以下是一些要点的示例:

276.523, 236.438,   517.656, 208.945,   275.984, 331.285,   502.23,  292.344
261.441, 235.059,   515.09,  211.5,     263.555, 327.066,   500.734, 295
229.031, 161.277,   427.125, 192.562,   229.16, 226,        416.48,  256

如果您的新四边形是平行四边形,则称为“剪切”,使用CGAffineTransform最容易实现。请参阅杰夫·拉马尔奇的优秀文章


如果您的新四边形不是平行四边形,请参阅下面的问题,了解如何应用CATTransformM3D:。

我们终于实现了这一点。我们尝试了几种不同的方法,但大多数都失败了。有些人甚至在获得与输入和输出相同的点时检索非单位矩阵(例如,来自KennyTM的一个……我们肯定遗漏了一些东西)

使用如下所示,我们得到一个
CATTransformM3D
可以在CAAnimation层上使用:

+ (CATransform3D)transformQuadrilateral:(Quadrilateral)origin toQuadrilateral:(Quadrilateral)destination {

    CvPoint2D32f *cvsrc = [self openCVMatrixWithQuadrilateral:origin]; 
    CvMat *src_mat = cvCreateMat( 4, 2, CV_32FC1 );
    cvSetData(src_mat, cvsrc, sizeof(CvPoint2D32f));

    CvPoint2D32f *cvdst = [self openCVMatrixWithQuadrilateral:destination]; 
    CvMat *dst_mat = cvCreateMat( 4, 2, CV_32FC1 );
    cvSetData(dst_mat, cvdst, sizeof(CvPoint2D32f));

    CvMat *H = cvCreateMat(3,3,CV_32FC1);
    cvFindHomography(src_mat, dst_mat, H);
    cvReleaseMat(&src_mat); 
    cvReleaseMat(&dst_mat); 

    CATransform3D transform = [self transform3DWithCMatrix:H->data.fl]; 
    cvReleaseMat(&H); 

    return transform; 
}

+ (CvPoint2D32f *)openCVMatrixWithQuadrilateral:(Quadrilateral)origin {

    CvPoint2D32f *cvsrc = (CvPoint2D32f *)malloc(4*sizeof(CvPoint2D32f)); 
    cvsrc[0].x = origin.upperLeft.x;
    cvsrc[0].y = origin.upperLeft.y;
    cvsrc[1].x = origin.upperRight.x;
    cvsrc[1].y = origin.upperRight.y;
    cvsrc[2].x = origin.lowerRight.x;
    cvsrc[2].y = origin.lowerRight.y;
    cvsrc[3].x = origin.lowerLeft.x;
    cvsrc[3].y = origin.lowerLeft.y; 
    return cvsrc; 
}

+ (CATransform3D)transform3DWithCMatrix:(float *)matrix {
    CATransform3D transform = CATransform3DIdentity; 

    transform.m11 = matrix[0];
    transform.m21 = matrix[1];
    transform.m41 = matrix[2];

    transform.m12 = matrix[3];
    transform.m22 = matrix[4];
    transform.m42 = matrix[5];

    transform.m14 = matrix[6];
    transform.m24 = matrix[7];
    transform.m44 = matrix[8];

    return transform; 
}

我已经为在iOS上执行此操作创建了一个工具包:


确保锚点位于左上方(
CGPointZero

+(CATTransformM3D)rectToQuad:(CGRect)rect
四边形:(CGPoint)左上角
quadTR:(CGPoint)右上角
quadBL:(CGPoint)左下角
quadBR:(CGPoint)右下角
{
return[self-rectToQuad:rect-quadTLX:topLeft.x-quadTLY:topLeft.y-quadTRX:topRight.x-quadTRY:topRight.y-quadBLX:bottomLeft.x-quadBRX:bottomRight.x-quadBRY:bottomRight.y];
}
+(CATTransferM3D)rectToQuad:(CGRect)rect
quadTLX:(CGFloat)x1a
四:(CGFloat)y1a
quadTRX:(CGFloat)x2a
四次:(CGFloat)y2a
quadBLX:(CGFloat)x3a
四:(CGFloat)y3a
quadBRX:(CGFloat)x4a
四次:(CGFloat)y4a
{
CGFloat X=rect.origin.X;
CGFloat Y=矩形原点Y;
CGFloat W=rect.size.width;
CGH=矩形尺寸高度;
CGY21=y2a-y1a;
CGY32=y3a-y2a;
CGY43=y4a-y3a;
CGY14=y1a-y4a;
CGY31=y3a-y1a;
CGY42=y4a-y2a;
CGA=-H*(x2a*x3a*y14+x2a*x4a*y31-x1a*x4a*y32+x1a*x3a*y42);
CGFloat b=W*(x2a*x3a*y14+x3a*x4a*y21+x1a*x4a*y32+x1a*x2a*y43);
CGFloat c=H*X*(x2a*x3a*y14+x2a*x4a*y31-x1a*x4a*y32+x1a*x3a*y42)-H*W*x1a*(x4a*y32-x3a*y42+x2a*y43)-W*Y*(x2a*x3a*y14+x3a*x4a*y21+x1a*x4a*y32+x1a*x2a*x2a*y43);
CGFloat d=H*(-x4a*y21*y3a+x2a*y1a*y43-x1a*y2a*y43-x3a*y1a*y4a+x3a*y2a*y4a);
cge=W*(x4a*y2a*y31-x3a*y1a*y42-x2a*y31*y4a+x1a*y3a*y42);
CGF=-(W*(x4a*(Y*y2a*y31+H*y1a*y32)-x3a*(H+Y)*y1a*y42+H*x2a*y1a*y43+x2a*Y*(y1a-y3a)*y4a+x1a*Y*y3a*(-y2a+y4a))-H*X*(x4a*y21*y3a-x2a*y1a*y43+x3a*(y1a-y2a)*y4a+x1a*y2a*(-y3a+y4a));
CGFloat g=H*(x3a*y21-x4a*y21+(-x1a+x2a)*y43);
CGH=W*(-x2a*y31+x4a*y31+(x1a-x3a)*y42);
CGFloat i=W*Y*(x2a*y31-x4a*y31-x1a*y42+x3a*y42)+H*(X*((x3a*y21)+x4a*y21+x1a*y43-x2a*y43)+W*((x3a*y2a)+x4a*y2a+x2a*y3a-x4a*y3a-x2a*y4a+x3a*y4a));
常数双kEpsilon=0.0001;
if(晶圆厂(i)0?1.0:-1.0);
}
CATTransferorM3D transform={a/i,d/i,0,g/i,b/i,e/i,0,h/i,0,0,0,1,0,c/i,f/i,0,1.0};
回归变换;
}

我不相信这个密码。我所做的只是浏览互联网,整理出各种不完整的答案。

下面是一个示例项目,它应用了上面hfossli答案中的代码,并在UIView上创建了一个类别,该类别在一次调用中设置框架并应用转换:

UIView+四边形代码:

#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>

@interface UIView (Quadrilateral)

//Sets frame to bounding box of quad and applies transform
- (void)transformToFitQuadTopLeft:(CGPoint)tl topRight:(CGPoint)tr bottomLeft:(CGPoint)bl bottomRight:(CGPoint)br;

@end    

@implementation UIView (Quadrilateral)

- (void)transformToFitQuadTopLeft:(CGPoint)tl topRight:(CGPoint)tr bottomLeft:(CGPoint)bl bottomRight:(CGPoint)br
{

    NSAssert(CGPointEqualToPoint(self.layer.anchorPoint, CGPointZero),@"Anchor point must be (0,0)!");
    CGRect boundingBox = [[self class] boundingBoxForQuadTR:tr tl:tl bl:bl br:br];
    self.frame = boundingBox;

    CGPoint frameTopLeft = boundingBox.origin;
    CATransform3D transform = [[self class] rectToQuad:self.bounds
                                                quadTL:CGPointMake(tl.x-frameTopLeft.x, tl.y-frameTopLeft.y)
                                                quadTR:CGPointMake(tr.x-frameTopLeft.x, tr.y-frameTopLeft.y)
                                                quadBL:CGPointMake(bl.x-frameTopLeft.x, bl.y-frameTopLeft.y)
                                                quadBR:CGPointMake(br.x-frameTopLeft.x, br.y-frameTopLeft.y)];

    self.layer.transform = transform;

}

+ (CGRect)boundingBoxForQuadTR:(CGPoint)tr tl:(CGPoint)tl bl:(CGPoint)bl br:(CGPoint)br
{
    CGRect boundingBox = CGRectZero;

    CGFloat xmin = MIN(MIN(MIN(tr.x, tl.x), bl.x),br.x);
    CGFloat ymin = MIN(MIN(MIN(tr.y, tl.y), bl.y),br.y);
    CGFloat xmax = MAX(MAX(MAX(tr.x, tl.x), bl.x),br.x);
    CGFloat ymax = MAX(MAX(MAX(tr.y, tl.y), bl.y),br.y);

    boundingBox.origin.x = xmin;
    boundingBox.origin.y = ymin;
    boundingBox.size.width = xmax - xmin;
    boundingBox.size.height = ymax - ymin;

    return boundingBox;
}

+ (CATransform3D)rectToQuad:(CGRect)rect
                     quadTL:(CGPoint)topLeft
                     quadTR:(CGPoint)topRight
                     quadBL:(CGPoint)bottomLeft
                     quadBR:(CGPoint)bottomRight
{
    return [self rectToQuad:rect quadTLX:topLeft.x quadTLY:topLeft.y quadTRX:topRight.x quadTRY:topRight.y quadBLX:bottomLeft.x quadBLY:bottomLeft.y quadBRX:bottomRight.x quadBRY:bottomRight.y];
}

+ (CATransform3D)rectToQuad:(CGRect)rect
                    quadTLX:(CGFloat)x1a
                    quadTLY:(CGFloat)y1a
                    quadTRX:(CGFloat)x2a
                    quadTRY:(CGFloat)y2a
                    quadBLX:(CGFloat)x3a
                    quadBLY:(CGFloat)y3a
                    quadBRX:(CGFloat)x4a
                    quadBRY:(CGFloat)y4a
{
    CGFloat X = rect.origin.x;
    CGFloat Y = rect.origin.y;
    CGFloat W = rect.size.width;
    CGFloat H = rect.size.height;

    CGFloat y21 = y2a - y1a;
    CGFloat y32 = y3a - y2a;
    CGFloat y43 = y4a - y3a;
    CGFloat y14 = y1a - y4a;
    CGFloat y31 = y3a - y1a;
    CGFloat y42 = y4a - y2a;

    CGFloat a = -H*(x2a*x3a*y14 + x2a*x4a*y31 - x1a*x4a*y32 + x1a*x3a*y42);
    CGFloat b = W*(x2a*x3a*y14 + x3a*x4a*y21 + x1a*x4a*y32 + x1a*x2a*y43);
    CGFloat c = H*X*(x2a*x3a*y14 + x2a*x4a*y31 - x1a*x4a*y32 + x1a*x3a*y42) - H*W*x1a*(x4a*y32 - x3a*y42 + x2a*y43) - W*Y*(x2a*x3a*y14 + x3a*x4a*y21 + x1a*x4a*y32 + x1a*x2a*y43);

    CGFloat d = H*(-x4a*y21*y3a + x2a*y1a*y43 - x1a*y2a*y43 - x3a*y1a*y4a + x3a*y2a*y4a);
    CGFloat e = W*(x4a*y2a*y31 - x3a*y1a*y42 - x2a*y31*y4a + x1a*y3a*y42);
    CGFloat f = -(W*(x4a*(Y*y2a*y31 + H*y1a*y32) - x3a*(H + Y)*y1a*y42 + H*x2a*y1a*y43 + x2a*Y*(y1a - y3a)*y4a + x1a*Y*y3a*(-y2a + y4a)) - H*X*(x4a*y21*y3a - x2a*y1a*y43 + x3a*(y1a - y2a)*y4a + x1a*y2a*(-y3a + y4a)));

    CGFloat g = H*(x3a*y21 - x4a*y21 + (-x1a + x2a)*y43);
    CGFloat h = W*(-x2a*y31 + x4a*y31 + (x1a - x3a)*y42);
    CGFloat i = W*Y*(x2a*y31 - x4a*y31 - x1a*y42 + x3a*y42) + H*(X*(-(x3a*y21) + x4a*y21 + x1a*y43 - x2a*y43) + W*(-(x3a*y2a) + x4a*y2a + x2a*y3a - x4a*y3a - x2a*y4a + x3a*y4a));

    const double kEpsilon = 0.0001;

    if(fabs(i) < kEpsilon)
    {
        i = kEpsilon* (i > 0 ? 1.0 : -1.0);
    }

    CATransform3D transform = {a/i, d/i, 0, g/i, b/i, e/i, 0, h/i, 0, 0, 1, 0, c/i, f/i, 0, 1.0};

    return transform;
}

@end
#导入
#进口
@界面视图(四边形)
//将框架设置为四边形的边界框并应用变换
-(void)transformToFitQuadTopLeft:(CGPoint)tl topRight:(CGPoint)tr bottomLeft:(CGPoint)bl bottomRight:(CGPoint)br;
@结束
@实现UIView(四边形)
-(void)transformToFitQuadTopLeft:(CGPoint)tl topRight:(CGPoint)tr bottomLeft:(CGPoint)bl bottomRight:(CGPoint)br
{
NSAssert(CGPointEqualToPoint(self.layer.anchorPoint,CGPointZero),@“锚点必须是(0,0)!”;
CGRect BONDINGBOX=[[self class]boundingBoxForQuadTR:tr tl:tl bl:bl br:br];
self.frame=boundingBox;
CGPoint frameTopLeft=boundingBox.origin;
CATTransformM3D transform=[[self class]rectToQuad:self.bounds
quadTL:CGPointMake(tl.x-frameTopLeft.x,tl.y-frameTopLeft.y)
quadTR:CGPointMake(tr.x-frameTopLeft.x,tr.y-frameTopLeft.y)
quadBL:CGPointMake(bl.x-frameTopLeft.x,bl.y-frameTopLeft.y)
quadBR:CGPointMake(br.x-frameTopLeft.x,br.y-frameTopLeft.y)];
self.layer.transform=变换;
}
+(CGRect)boundingBoxForQuadTR:(CGPoint)tr tl:(CGPoint)tl bl:(CGPoint)bl br:(CGPoint)br
{
CGRect boundingBox=CGRectZero;
CGFloat xmin=MIN(MIN(MIN(tr.x,tl.x),bl.x),br.x);
cgymin=MIN(MIN(MIN(tr.y,tl.y),bl.y),br.y);
CGFloat xmax=MAX(MAX(MAX(tr.x,tl.x),bl.x),br.x);
cgymax=MAX(MAX(MAX(MAX(tr.y,tl.y),bl.y),br.y);
boundingBox.origin.x=xmin;
boundingBox.origin.y=ymin;
boundingBox.size.widt
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>

@interface UIView (Quadrilateral)

//Sets frame to bounding box of quad and applies transform
- (void)transformToFitQuadTopLeft:(CGPoint)tl topRight:(CGPoint)tr bottomLeft:(CGPoint)bl bottomRight:(CGPoint)br;

@end    

@implementation UIView (Quadrilateral)

- (void)transformToFitQuadTopLeft:(CGPoint)tl topRight:(CGPoint)tr bottomLeft:(CGPoint)bl bottomRight:(CGPoint)br
{

    NSAssert(CGPointEqualToPoint(self.layer.anchorPoint, CGPointZero),@"Anchor point must be (0,0)!");
    CGRect boundingBox = [[self class] boundingBoxForQuadTR:tr tl:tl bl:bl br:br];
    self.frame = boundingBox;

    CGPoint frameTopLeft = boundingBox.origin;
    CATransform3D transform = [[self class] rectToQuad:self.bounds
                                                quadTL:CGPointMake(tl.x-frameTopLeft.x, tl.y-frameTopLeft.y)
                                                quadTR:CGPointMake(tr.x-frameTopLeft.x, tr.y-frameTopLeft.y)
                                                quadBL:CGPointMake(bl.x-frameTopLeft.x, bl.y-frameTopLeft.y)
                                                quadBR:CGPointMake(br.x-frameTopLeft.x, br.y-frameTopLeft.y)];

    self.layer.transform = transform;

}

+ (CGRect)boundingBoxForQuadTR:(CGPoint)tr tl:(CGPoint)tl bl:(CGPoint)bl br:(CGPoint)br
{
    CGRect boundingBox = CGRectZero;

    CGFloat xmin = MIN(MIN(MIN(tr.x, tl.x), bl.x),br.x);
    CGFloat ymin = MIN(MIN(MIN(tr.y, tl.y), bl.y),br.y);
    CGFloat xmax = MAX(MAX(MAX(tr.x, tl.x), bl.x),br.x);
    CGFloat ymax = MAX(MAX(MAX(tr.y, tl.y), bl.y),br.y);

    boundingBox.origin.x = xmin;
    boundingBox.origin.y = ymin;
    boundingBox.size.width = xmax - xmin;
    boundingBox.size.height = ymax - ymin;

    return boundingBox;
}

+ (CATransform3D)rectToQuad:(CGRect)rect
                     quadTL:(CGPoint)topLeft
                     quadTR:(CGPoint)topRight
                     quadBL:(CGPoint)bottomLeft
                     quadBR:(CGPoint)bottomRight
{
    return [self rectToQuad:rect quadTLX:topLeft.x quadTLY:topLeft.y quadTRX:topRight.x quadTRY:topRight.y quadBLX:bottomLeft.x quadBLY:bottomLeft.y quadBRX:bottomRight.x quadBRY:bottomRight.y];
}

+ (CATransform3D)rectToQuad:(CGRect)rect
                    quadTLX:(CGFloat)x1a
                    quadTLY:(CGFloat)y1a
                    quadTRX:(CGFloat)x2a
                    quadTRY:(CGFloat)y2a
                    quadBLX:(CGFloat)x3a
                    quadBLY:(CGFloat)y3a
                    quadBRX:(CGFloat)x4a
                    quadBRY:(CGFloat)y4a
{
    CGFloat X = rect.origin.x;
    CGFloat Y = rect.origin.y;
    CGFloat W = rect.size.width;
    CGFloat H = rect.size.height;

    CGFloat y21 = y2a - y1a;
    CGFloat y32 = y3a - y2a;
    CGFloat y43 = y4a - y3a;
    CGFloat y14 = y1a - y4a;
    CGFloat y31 = y3a - y1a;
    CGFloat y42 = y4a - y2a;

    CGFloat a = -H*(x2a*x3a*y14 + x2a*x4a*y31 - x1a*x4a*y32 + x1a*x3a*y42);
    CGFloat b = W*(x2a*x3a*y14 + x3a*x4a*y21 + x1a*x4a*y32 + x1a*x2a*y43);
    CGFloat c = H*X*(x2a*x3a*y14 + x2a*x4a*y31 - x1a*x4a*y32 + x1a*x3a*y42) - H*W*x1a*(x4a*y32 - x3a*y42 + x2a*y43) - W*Y*(x2a*x3a*y14 + x3a*x4a*y21 + x1a*x4a*y32 + x1a*x2a*y43);

    CGFloat d = H*(-x4a*y21*y3a + x2a*y1a*y43 - x1a*y2a*y43 - x3a*y1a*y4a + x3a*y2a*y4a);
    CGFloat e = W*(x4a*y2a*y31 - x3a*y1a*y42 - x2a*y31*y4a + x1a*y3a*y42);
    CGFloat f = -(W*(x4a*(Y*y2a*y31 + H*y1a*y32) - x3a*(H + Y)*y1a*y42 + H*x2a*y1a*y43 + x2a*Y*(y1a - y3a)*y4a + x1a*Y*y3a*(-y2a + y4a)) - H*X*(x4a*y21*y3a - x2a*y1a*y43 + x3a*(y1a - y2a)*y4a + x1a*y2a*(-y3a + y4a)));

    CGFloat g = H*(x3a*y21 - x4a*y21 + (-x1a + x2a)*y43);
    CGFloat h = W*(-x2a*y31 + x4a*y31 + (x1a - x3a)*y42);
    CGFloat i = W*Y*(x2a*y31 - x4a*y31 - x1a*y42 + x3a*y42) + H*(X*(-(x3a*y21) + x4a*y21 + x1a*y43 - x2a*y43) + W*(-(x3a*y2a) + x4a*y2a + x2a*y3a - x4a*y3a - x2a*y4a + x3a*y4a));

    const double kEpsilon = 0.0001;

    if(fabs(i) < kEpsilon)
    {
        i = kEpsilon* (i > 0 ? 1.0 : -1.0);
    }

    CATransform3D transform = {a/i, d/i, 0, g/i, b/i, e/i, 0, h/i, 0, 0, 1, 0, c/i, f/i, 0, 1.0};

    return transform;
}

@end
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>

@interface UIView (Quadrilateral)

//Sets frame to bounding box of quad and applies transform
- (void)transformToFitQuadTopLeft:(CGPoint)tl topRight:(CGPoint)tr bottomLeft:(CGPoint)bl bottomRight:(CGPoint)br;

@end    
#import "UIView+Quadrilateral.h"

@implementation UIView (Quadrilateral)

- (void)transformToFitQuadTopLeft:(CGPoint)tl topRight:(CGPoint)tr bottomLeft:(CGPoint)bl bottomRight:(CGPoint)br
{
    CGRect boundingBox = [[self class] boundingBoxForQuadTR:tr tl:tl bl:bl br:br];
    self.layer.transform = CATransform3DIdentity; // keeps current transform from interfering
    self.frame = boundingBox;

    CGPoint frameTopLeft = boundingBox.origin;
    CATransform3D transform = [[self class] rectToQuad:self.bounds
                                                quadTL:CGPointMake(tl.x-frameTopLeft.x, tl.y-frameTopLeft.y)
                                                quadTR:CGPointMake(tr.x-frameTopLeft.x, tr.y-frameTopLeft.y)
                                                quadBL:CGPointMake(bl.x-frameTopLeft.x, bl.y-frameTopLeft.y)
                                                quadBR:CGPointMake(br.x-frameTopLeft.x, br.y-frameTopLeft.y)];

    //  To account for anchor point, we must translate, transform, translate
    CGPoint anchorPoint = self.layer.position;
    CGPoint anchorOffset = CGPointMake(anchorPoint.x - boundingBox.origin.x, anchorPoint.y - boundingBox.origin.y);
    CATransform3D transPos = CATransform3DMakeTranslation(anchorOffset.x, anchorOffset.y, 0.);
    CATransform3D transNeg = CATransform3DMakeTranslation(-anchorOffset.x, -anchorOffset.y, 0.);
    CATransform3D fullTransform = CATransform3DConcat(CATransform3DConcat(transPos, transform), transNeg);

    //  Now we set our transform
    self.layer.transform = fullTransform;
}

+ (CGRect)boundingBoxForQuadTR:(CGPoint)tr tl:(CGPoint)tl bl:(CGPoint)bl br:(CGPoint)br
{
    CGRect boundingBox = CGRectZero;

    CGFloat xmin = MIN(MIN(MIN(tr.x, tl.x), bl.x),br.x);
    CGFloat ymin = MIN(MIN(MIN(tr.y, tl.y), bl.y),br.y);
    CGFloat xmax = MAX(MAX(MAX(tr.x, tl.x), bl.x),br.x);
    CGFloat ymax = MAX(MAX(MAX(tr.y, tl.y), bl.y),br.y);

    boundingBox.origin.x = xmin;
    boundingBox.origin.y = ymin;
    boundingBox.size.width = xmax - xmin;
    boundingBox.size.height = ymax - ymin;

    return boundingBox;
}

+ (CATransform3D)rectToQuad:(CGRect)rect
                     quadTL:(CGPoint)topLeft
                     quadTR:(CGPoint)topRight
                     quadBL:(CGPoint)bottomLeft
                     quadBR:(CGPoint)bottomRight
{
    return [self rectToQuad:rect quadTLX:topLeft.x quadTLY:topLeft.y quadTRX:topRight.x quadTRY:topRight.y quadBLX:bottomLeft.x quadBLY:bottomLeft.y quadBRX:bottomRight.x quadBRY:bottomRight.y];
}

+ (CATransform3D)rectToQuad:(CGRect)rect
                    quadTLX:(CGFloat)x1a
                    quadTLY:(CGFloat)y1a
                    quadTRX:(CGFloat)x2a
                    quadTRY:(CGFloat)y2a
                    quadBLX:(CGFloat)x3a
                    quadBLY:(CGFloat)y3a
                    quadBRX:(CGFloat)x4a
                    quadBRY:(CGFloat)y4a
{
    CGFloat X = rect.origin.x;
    CGFloat Y = rect.origin.y;
    CGFloat W = rect.size.width;
    CGFloat H = rect.size.height;

    CGFloat y21 = y2a - y1a;
    CGFloat y32 = y3a - y2a;
    CGFloat y43 = y4a - y3a;
    CGFloat y14 = y1a - y4a;
    CGFloat y31 = y3a - y1a;
    CGFloat y42 = y4a - y2a;

    CGFloat a = -H*(x2a*x3a*y14 + x2a*x4a*y31 - x1a*x4a*y32 + x1a*x3a*y42);
    CGFloat b = W*(x2a*x3a*y14 + x3a*x4a*y21 + x1a*x4a*y32 + x1a*x2a*y43);
    CGFloat c = H*X*(x2a*x3a*y14 + x2a*x4a*y31 - x1a*x4a*y32 + x1a*x3a*y42) - H*W*x1a*(x4a*y32 - x3a*y42 + x2a*y43) - W*Y*(x2a*x3a*y14 + x3a*x4a*y21 + x1a*x4a*y32 + x1a*x2a*y43);

    CGFloat d = H*(-x4a*y21*y3a + x2a*y1a*y43 - x1a*y2a*y43 - x3a*y1a*y4a + x3a*y2a*y4a);
    CGFloat e = W*(x4a*y2a*y31 - x3a*y1a*y42 - x2a*y31*y4a + x1a*y3a*y42);
    CGFloat f = -(W*(x4a*(Y*y2a*y31 + H*y1a*y32) - x3a*(H + Y)*y1a*y42 + H*x2a*y1a*y43 + x2a*Y*(y1a - y3a)*y4a + x1a*Y*y3a*(-y2a + y4a)) - H*X*(x4a*y21*y3a - x2a*y1a*y43 + x3a*(y1a - y2a)*y4a + x1a*y2a*(-y3a + y4a)));

    CGFloat g = H*(x3a*y21 - x4a*y21 + (-x1a + x2a)*y43);
    CGFloat h = W*(-x2a*y31 + x4a*y31 + (x1a - x3a)*y42);
    CGFloat i = W*Y*(x2a*y31 - x4a*y31 - x1a*y42 + x3a*y42) + H*(X*(-(x3a*y21) + x4a*y21 + x1a*y43 - x2a*y43) + W*(-(x3a*y2a) + x4a*y2a + x2a*y3a - x4a*y3a - x2a*y4a + x3a*y4a));

    const double kEpsilon = 0.0001;

    if(fabs(i) < kEpsilon)
    {
        i = kEpsilon* (i > 0 ? 1.0 : -1.0);
    }

    CATransform3D transform = {a/i, d/i, 0, g/i, b/i, e/i, 0, h/i, 0, 0, 1, 0, c/i, f/i, 0, 1.0};

    return transform;
}

@end
// JoshQuadView in Swift
// from: https://stackoverflow.com/a/18606029/294884

// NB: JoshRL uses the ordering convention
// "topleft, topright, bottomleft, bottomright"
// which is different from "clockwise from topleft".

// Note: is not meant to handle concave.

import UIKit

class JoshQuadView:UIView
    {
    func transformToFitQuadTopLeft(tl:CGPoint,tr:CGPoint,bl:CGPoint,br:CGPoint)
        {
        guard CGPointEqualToPoint(self.layer.anchorPoint, CGPointZero) else { print("suck");return }
        
        let b:CGRect = boundingBoxForQuadTR(tl, tr, bl, br)
        self.frame = b
        self.layer.transform = rectToQuad( self.bounds,
            CGPointMake(tl.x-b.origin.x, tl.y-b.origin.y),
            CGPointMake(tr.x-b.origin.x, tr.y-b.origin.y),
            CGPointMake(bl.x-b.origin.x, bl.y-b.origin.y),
            CGPointMake(br.x-b.origin.x, br.y-b.origin.y) )
        }
    
    func boundingBoxForQuadTR(
            tl:CGPoint, _ tr:CGPoint, _ bl:CGPoint, _ br:CGPoint    )->(CGRect)
        {
        var b:CGRect = CGRectZero

        let xmin:CGFloat = min(min(min(tr.x, tl.x), bl.x),br.x);
        let ymin:CGFloat = min(min(min(tr.y, tl.y), bl.y),br.y);
        let xmax:CGFloat = max(max(max(tr.x, tl.x), bl.x),br.x);
        let ymax:CGFloat = max(max(max(tr.y, tl.y), bl.y),br.y);

        b.origin.x = xmin
        b.origin.y = ymin
        b.size.width = xmax - xmin
        b.size.height = ymax - ymin

        return b;
        }

    func rectToQuad(
            rect:CGRect,
            _ topLeft:CGPoint,
            _ topRight:CGPoint,
            _ bottomLeft:CGPoint,
            _ bottomRight:CGPoint   )->(CATransform3D)
        {
        return rectToQuad(rect,
                  topLeft.x, topLeft.y,
                  topRight.x, topRight.y,
                  bottomLeft.x, bottomLeft.y,
                  bottomRight.x, bottomRight.y)
        }


    func rectToQuad(
            rect:CGRect,
            _ x1a:CGFloat, _ y1a:CGFloat,
            _ x2a:CGFloat, _ y2a:CGFloat,
            _ x3a:CGFloat, _ y3a:CGFloat,
            _ x4a:CGFloat, _ y4a:CGFloat    )->(CATransform3D)
        {
        let X = rect.origin.x;
        let Y = rect.origin.y;
        let W = rect.size.width;
        let H = rect.size.height;
        
        let y21 = y2a - y1a;
        let y32 = y3a - y2a;
        let y43 = y4a - y3a;
        let y14 = y1a - y4a;
        let y31 = y3a - y1a;
        let y42 = y4a - y2a;
        
        let a = -H*(x2a*x3a*y14 + x2a*x4a*y31 - x1a*x4a*y32 + x1a*x3a*y42);
        let b = W*(x2a*x3a*y14 + x3a*x4a*y21 + x1a*x4a*y32 + x1a*x2a*y43);
        
        // let c = H*X*(x2a*x3a*y14 + x2a*x4a*y31 - x1a*x4a*y32 + x1a*x3a*y42) - H*W*x1a*(x4a*y32 - x3a*y42 + x2a*y43) - W*Y*(x2a*x3a*y14 + x3a*x4a*y21 + x1a*x4a*y32 + x1a*x2a*y43);
        // Could be too long for Swift. Replaced with four lines:
        let c0 = -H*W*x1a*(x4a*y32 - x3a*y42 + x2a*y43)
        let cx = H*X*(x2a*x3a*y14 + x2a*x4a*y31 - x1a*x4a*y32 + x1a*x3a*y42)
        let cy = -W*Y*(x2a*x3a*y14 + x3a*x4a*y21 + x1a*x4a*y32 + x1a*x2a*y43)
        let c = c0 + cx + cy
        
        let d = H*(-x4a*y21*y3a + x2a*y1a*y43 - x1a*y2a*y43 - x3a*y1a*y4a + x3a*y2a*y4a);
        let e = W*(x4a*y2a*y31 - x3a*y1a*y42 - x2a*y31*y4a + x1a*y3a*y42);
        
        // let f = -(W*(x4a*(Y*y2a*y31 + H*y1a*y32) - x3a*(H + Y)*y1a*y42 + H*x2a*y1a*y43 + x2a*Y*(y1a - y3a)*y4a + x1a*Y*y3a*(-y2a + y4a)) - H*X*(x4a*y21*y3a - x2a*y1a*y43 + x3a*(y1a - y2a)*y4a + x1a*y2a*(-y3a + y4a)));
        // Is too long for Swift. Replaced with four lines:
        let f0 = -W*H*(x4a*y1a*y32 - x3a*y1a*y42 + x2a*y1a*y43)
        let fx = H*X*(x4a*y21*y3a - x2a*y1a*y43 - x3a*y21*y4a + x1a*y2a*y43)
        let fy = -W*Y*(x4a*y2a*y31 - x3a*y1a*y42 - x2a*y31*y4a + x1a*y3a*y42)
        let f = f0 + fx + fy
        
        let g = H*(x3a*y21 - x4a*y21 + (-x1a + x2a)*y43);
        let h = W*(-x2a*y31 + x4a*y31 + (x1a - x3a)*y42);
        
        // let i = W*Y*(x2a*y31 - x4a*y31 - x1a*y42 + x3a*y42) + H*(X*(-(x3a*y21) + x4a*y21 + x1a*y43 - x2a*y43) + W*(-(x3a*y2a) + x4a*y2a + x2a*y3a - x4a*y3a - x2a*y4a + x3a*y4a));
        // Is too long for Swift. Replaced with four lines:
        let i0 = H*W*(x3a*y42 - x4a*y32 - x2a*y43)
        let ix = H*X*(x4a*y21 - x3a*y21 + x1a*y43 - x2a*y43)
        let iy = W*Y*(x2a*y31 - x4a*y31 - x1a*y42 + x3a*y42)
        var i = i0 + ix + iy
        
        let kEpsilon:CGFloat = 0.0001;
        if(fabs(i) < kEpsilon) { i = kEpsilon * (i > 0 ? 1.0 : -1.0); }
        
        return CATransform3D(m11:a/i, m12:d/i, m13:0, m14:g/i,
                            m21:b/i, m22:e/i, m23:0, m24:h/i,
                            m31:0, m32:0, m33:1, m34:0,
                            m41:c/i, m42:f/i, m43:0, m44:1.0)
        }
    
    }
class QuadScreen:UIViewController
    {
    // sit your JoshQuadView in this view
    @IBOutlet var jqv:JoshQuadView!
    
    // simply have four small subview views, "handles"
    // with an icon on them (perhaps a small circle)
    // and put those over the four corners of the jqv
    
    // NOTE numbered CLOCKWISE from top left here:
    @IBOutlet var handle1:UIView!
    @IBOutlet var handle2:UIView!
    @IBOutlet var handle3:UIView!
    @IBOutlet var handle4:UIView!
    
    // put a pan recognizer on each handle, action goes to here
    // (for the pan recognizers, set cancels-in-view as needed
    // if you, example, highlight them on touch in their class)
    
    @IBAction func dragHandle(p:UIPanGestureRecognizer!)
        {
        let tr = p.translationInView(p.view)
        p.view!.center.x += tr.x
        p.view!.center.y += tr.y
        p.setTranslation(CGPointZero, inView: p.view)
        
        jqv.transformToFitQuadTopLeft(
            handle1.center, tr: handle2.center,
            bl: handle4.center, br: handle3.center )
        // it's that simple, there's nothing else to do
        
        p.setTranslation(CGPointZero, inView: p.view)
        }
    
    override func viewDidLayoutSubviews()
        {
        // don't forget to do this....is critical.
        jqv.layer.anchorPoint = CGPointMake(0, 0)
        }
    
import PerspectiveTransform

let destination = Perspective(
CGPoint(x: 108.315837, y: 80.1687782),
CGPoint(x: 377.282671, y: 41.4352201),
CGPoint(x: 193.321418, y: 330.023027),
CGPoint(x: 459.781253, y: 251.836131)
)

// Starting perspective is the current overlay frame or could be another 4 points.
let start = Perspective(overlayView.frame)

// Caclulate CATransform3D from start to destination
overlayView.layer.transform = start.projectiveTransform(destination: destination)
CATransform3D t = CATransform3DIdentity;
t = CATransform3DScale(t, .... )
t = CATransform3DRotate(t, ....)
t = CATransform3DTranslate(t, ....)
CATransform3D invertT = CATransform3DInvert(t);