感谢JoshRL,以下是他的类的Swift版本。
这个版本已经完全调试,所有受到"太长在Swift中"问题影响的行都已经重新设计和测试。它在高流量生产环境中运行得非常完美。
使用起来非常简单。下面是如何在Swift中使用的示例。
2016年发布的Swift版本... 完整、可用的复制粘贴解决方案。
为2022年更新!直接插入带有当前语法等的代码。
import UIKit
class JoshQuadView: UIImageView { // or UIView, as preferred
func transformToFitQuadTopLeft(tl: CGPoint, tr: CGPoint, bl: CGPoint, br: CGPoint) {
guard self.layer.anchorPoint == .zero else {
print("suck")
return
}
let b = boundingBoxForQuadTR(tl, tr, bl, br)
self.frame = b
self.layer.transform = rectToQuad(bounds,
.init(x: tl.x-b.origin.x, y: tl.y-b.origin.y),
.init(x: tr.x-b.origin.x, y: tr.y-b.origin.y),
.init(x: bl.x-b.origin.x, y: bl.y-b.origin.y),
.init(x: br.x-b.origin.x, y: br.y-b.origin.y))
}
func boundingBoxForQuadTR(_ tl: CGPoint, _ tr: CGPoint, _ bl: CGPoint, _ br: CGPoint) -> CGRect {
var b: CGRect = .zero
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 {
rectToQuadCalculation(rect,
topLeft.x, topLeft.y,
topRight.x, topRight.y,
bottomLeft.x, bottomLeft.y,
bottomRight.x, bottomRight.y)
}
func rectToQuadCalculation(_ rect: CGRect,
_ x1a: CGFloat, _ y1a: CGFloat,
_ x2a: CGFloat, _ y2a: CGFloat,
_ x3a: CGFloat, _ y3a: CGFloat,
_ x4a: CGFloat, _ y4a: CGFloat) -> CATransform3D {
let XX = rect.origin.x
let YY = rect.origin.y
let WW = rect.size.width
let HH = 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 = -HH * (x2a*x3a*y14 + x2a*x4a*y31 - x1a*x4a*y32 + x1a*x3a*y42)
let b = WW * (x2a*x3a*y14 + x3a*x4a*y21 + x1a*x4a*y32 + x1a*x2a*y43)
let c0 = -HH * WW * x1a * (x4a*y32 - x3a*y42 + x2a*y43)
let cx = HH * XX * (x2a*x3a*y14 + x2a*x4a*y31 - x1a*x4a*y32 + x1a*x3a*y42)
let cy = -WW * YY * (x2a*x3a*y14 + x3a*x4a*y21 + x1a*x4a*y32 + x1a*x2a*y43)
let c = c0 + cx + cy
let d = HH * (-x4a*y21*y3a + x2a*y1a*y43 - x1a*y2a*y43 - x3a*y1a*y4a + x3a*y2a*y4a)
let e = WW * (x4a*y2a*y31 - x3a*y1a*y42 - x2a*y31*y4a + x1a*y3a*y42)
let f0 = -WW * HH * (x4a * y1a * y32 - x3a * y1a * y42 + x2a * y1a * y43)
let fx = HH * XX * (x4a * y21 * y3a - x2a * y1a * y43 - x3a * y21 * y4a + x1a * y2a * y43)
let fy = -WW * YY * (x4a * y2a * y31 - x3a * y1a * y42 - x2a * y31 * y4a + x1a * y3a * y42)
let f = f0 + fx + fy
let g = HH * (x3a * y21 - x4a * y21 + (-x1a + x2a) * y43)
let h = WW * (-x2a * y31 + x4a * y31 + (x1a - x3a) * y42)
let iy = WW * YY * (x2a * y31 - x4a * y31 - x1a * y42 + x3a * y42)
let ix = HH * XX * (x4a * y21 - x3a * y21 + x1a * y43 - x2a * y43)
let i0 = HH * WW * (x3a * y42 - x4a * y32 - x2a * y43)
var i = i0 + ix + iy
let kEpsilon: CGFloat = 0.0001
if abs(i) < kEpsilon {
i = kEpsilon * (i > 0 ? 1 : -1)
}
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)
}
}
快速测试:
@IBOutlet var someImage: JoshQuadView!
someImage.transformToFitQuadTopLeft(
tl: CGPoint(x: 0, y: 0),
tr: CGPoint(x: 400, y: 0),
bl: CGPoint(x: 0, y: 400),
br: CGPoint(x: 400, y: 400))
它将呈现普通的方形图像。
someImage.transformToFitQuadTopLeft(
tl: CGPoint(x: -50, y: -20),
tr: CGPoint(x: 400, y: 0),
bl: CGPoint(x: 0, y: 400),
br: CGPoint(x: 400, y: 400))
它将弯曲掉左上角。
重要提醒。JoshRL 最初使用的顺序是 tl tr bl br。因此这里保持不变。在处理 verts 时,更常见的做法是按照顺时针方向使用 tl(即 tl tr br bl),所以请记住这一点!
在 Swift 中使用可拖动角落手柄:
假设您有一个容器视图 "QuadScreen"。
您想要拉伸的视图将是 JoshQuadView。将其放入场景中。将其连接到 IBOutlet,在此示例中为 "jqv"。
只需在场景中放置四个角落手柄(即图片),作为您的句柄图标的 PNG。将这些链接到四个句柄的 IBOutlets 上。代码完全处理这些句柄。 (按照代码中的注释轻松设置 storyboard。)
然后,只需一行代码即可完成拉伸:
class QuadScreen: UIViewController {
@IBOutlet var jqv: JoshQuadView!
@IBOutlet var handle1: UIView!
@IBOutlet var handle2: UIView!
@IBOutlet var handle3: UIView!
@IBOutlet var handle4: UIView!
@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(.zero, inView: p.view)
jqv.transformToFitQuadTopLeft( handle1.center, tr: handle2.center, bl: handle4.center, br: handle3.center)
p.setTranslation(.zero, inView: p.view)
}
override func viewDidLayoutSubviews() {
jqv.layer.anchorPoint = .zero
}
}
有趣的是,对于Google来说,使用Android内置命令来重塑多边形非常容易。
Android
他们有一个用于重新塑造多边形的内置命令。这个出色的答案包含复制和粘贴代码:https://dev59.com/UGzXa4cB1Zd3GeqPRTgn#34667015
cvFindHomography()
OpenCV函数也可以用于这种方式。 - MonsieurDart