Add crop gesture.

pull/1/head
Matthew Chen
parent 03cbeb5fee
commit 57f888a447

@ -201,15 +201,6 @@ public class ImageEditorView: UIView, ImageEditorModelDelegate {
self.currentStrokeSamples.removeAll()
}
let referenceView = layersView
let unitSampleForGestureLocation = { () -> CGPoint in
// TODO: Smooth touch samples before converting into stroke samples.
let location = gestureRecognizer.location(in: referenceView)
let x = CGFloatClamp01(CGFloatInverseLerp(location.x, 0, referenceView.bounds.width))
let y = CGFloatClamp01(CGFloatInverseLerp(location.y, 0, referenceView.bounds.height))
return CGPoint(x: x, y: y)
}
// TODO: Color picker.
let strokeColor = UIColor.blue
// TODO: Tune stroke width.
@ -219,14 +210,14 @@ public class ImageEditorView: UIView, ImageEditorModelDelegate {
case .began:
removeCurrentStroke()
currentStrokeSamples.append(unitSampleForGestureLocation())
currentStrokeSamples.append(unitSampleForGestureLocation(gestureRecognizer))
let stroke = ImageEditorStrokeItem(color: strokeColor, unitSamples: currentStrokeSamples, unitStrokeWidth: unitStrokeWidth)
model.append(item: stroke)
currentStroke = stroke
case .changed, .ended:
currentStrokeSamples.append(unitSampleForGestureLocation())
currentStrokeSamples.append(unitSampleForGestureLocation(gestureRecognizer))
guard let lastStroke = self.currentStroke else {
owsFailDebug("Missing last stroke.")
@ -250,67 +241,104 @@ public class ImageEditorView: UIView, ImageEditorModelDelegate {
}
}
private func unitSampleForGestureLocation(_ gestureRecognizer: UIGestureRecognizer) -> CGPoint {
let referenceView = layersView
// TODO: Smooth touch samples before converting into stroke samples.
let location = gestureRecognizer.location(in: referenceView)
let x = CGFloatClamp01(CGFloatInverseLerp(location.x, 0, referenceView.bounds.width))
let y = CGFloatClamp01(CGFloatInverseLerp(location.y, 0, referenceView.bounds.height))
return CGPoint(x: x, y: y)
}
// MARK: - Crop
private var cropStartUnit = CGPoint.zero
private var cropEndUnit = CGPoint.zero
private var cropLayer1 = CAShapeLayer()
private var cropLayer2 = CAShapeLayer()
private var cropLayers: [CAShapeLayer] {
return [cropLayer1, cropLayer2]
}
@objc
public func handleCropGesture(_ gestureRecognizer: UIGestureRecognizer) {
AssertIsOnMainThread()
// let removeCurrentStroke = {
// if let stroke = self.currentStroke {
// self.model.remove(item: stroke)
// }
// self.currentStroke = nil
// self.currentStrokeSamples.removeAll()
// }
//
// let referenceView = self
// let unitSampleForGestureLocation = { () -> CGPoint in
// // TODO: Smooth touch samples before converting into stroke samples.
// let location = gestureRecognizer.location(in: referenceView)
// let x = CGFloatClamp01(CGFloatInverseLerp(location.x, 0, referenceView.bounds.width))
// let y = CGFloatClamp01(CGFloatInverseLerp(location.y, 0, referenceView.bounds.height))
// return CGPoint(x: x, y: y)
// }
//
// // TODO: Color picker.
// let strokeColor = UIColor.blue
// // TODO: Tune stroke width.
// let unitStrokeWidth = ImageEditorStrokeItem.defaultUnitStrokeWidth()
//
// switch gestureRecognizer.state {
// case .began:
// removeCurrentStroke()
//
// currentStrokeSamples.append(unitSampleForGestureLocation())
//
// let stroke = ImageEditorStrokeItem(color: strokeColor, unitSamples: currentStrokeSamples, unitStrokeWidth: unitStrokeWidth)
// model.append(item: stroke)
// currentStroke = stroke
//
// case .changed, .ended:
// currentStrokeSamples.append(unitSampleForGestureLocation())
//
// guard let lastStroke = self.currentStroke else {
// owsFailDebug("Missing last stroke.")
// removeCurrentStroke()
// return
// }
//
// // Model items are immutable; we _replace_ the
// // stroke item rather than modify it.
// let stroke = ImageEditorStrokeItem(itemId: lastStroke.itemId, color: strokeColor, unitSamples: currentStrokeSamples, unitStrokeWidth: unitStrokeWidth)
// model.replace(item: stroke, suppressUndo: true)
//
// if gestureRecognizer.state == .ended {
// currentStroke = nil
// currentStrokeSamples.removeAll()
// } else {
// currentStroke = stroke
// }
// default:
// removeCurrentStroke()
// }
let kCropDashLength: CGFloat = 3
let cancelCrop = {
for cropLayer in self.cropLayers {
cropLayer.removeFromSuperlayer()
cropLayer.removeAllAnimations()
}
}
let updateCropLayer = { (cropLayer: CAShapeLayer) in
cropLayer.fillColor = nil
cropLayer.lineWidth = 1.0
cropLayer.lineDashPattern = [NSNumber(value: Double(kCropDashLength)), NSNumber(value: Double(kCropDashLength))]
let viewSize = self.layersView.bounds.size
cropLayer.frame = CGRect(origin: .zero, size: viewSize)
// Find the upper-left and bottom-right corners of the
// crop rectangle, in unit coordinates.
let unitMin = CGPointMin(self.cropStartUnit, self.cropEndUnit)
let unitMax = CGPointMax(self.cropStartUnit, self.cropEndUnit)
let transformSampleToPoint = { (unitSample: CGPoint) -> CGPoint in
return CGPoint(x: viewSize.width * unitSample.x,
y: viewSize.height * unitSample.y)
}
// Convert from unit coordinates to view coordinates.
let pointMin = transformSampleToPoint(unitMin)
let pointMax = transformSampleToPoint(unitMax)
let cropRect = CGRect(x: pointMin.x,
y: pointMin.y,
width: pointMax.x - pointMin.x,
height: pointMax.y - pointMin.y)
let bezierPath = UIBezierPath(rect: cropRect)
cropLayer.path = bezierPath.cgPath
}
let updateCrop = {
updateCropLayer(self.cropLayer1)
updateCropLayer(self.cropLayer2)
self.cropLayer1.strokeColor = UIColor.white.cgColor
self.cropLayer2.strokeColor = UIColor.black.cgColor
self.cropLayer1.lineDashPhase = 0
self.cropLayer2.lineDashPhase = self.cropLayer1.lineDashPhase + kCropDashLength
}
let startCrop = {
for cropLayer in self.cropLayers {
self.layersView.layer.addSublayer(cropLayer)
}
updateCrop()
}
let endCrop = {
updateCrop()
for cropLayer in self.cropLayers {
cropLayer.removeFromSuperlayer()
cropLayer.removeAllAnimations()
}
}
switch gestureRecognizer.state {
case .began:
let unitSample = unitSampleForGestureLocation(gestureRecognizer)
cropStartUnit = unitSample
cropEndUnit = unitSample
startCrop()
case .changed:
cropEndUnit = unitSampleForGestureLocation(gestureRecognizer)
updateCrop()
case .ended:
cropEndUnit = unitSampleForGestureLocation(gestureRecognizer)
endCrop()
default:
cancelCrop()
}
}
// MARK: - ImageEditorModelDelegate
@ -515,6 +543,7 @@ public class ImageEditorView: UIView, ImageEditorModelDelegate {
shapeLayer.path = bezierPath.cgPath
shapeLayer.fillColor = nil
shapeLayer.lineCap = kCALineCapRound
shapeLayer.lineJoin = kCALineJoinRound
return shapeLayer
}

@ -194,6 +194,16 @@ CG_INLINE CGFloat CGPointDistance(CGPoint left, CGPoint right)
return sqrt(delta.x * delta.x + delta.y * delta.y);
}
CG_INLINE CGPoint CGPointMin(CGPoint left, CGPoint right)
{
return CGPointMake(MIN(left.x, right.x), MIN(left.y, right.y));
}
CG_INLINE CGPoint CGPointMax(CGPoint left, CGPoint right)
{
return CGPointMake(MAX(left.x, right.x), MAX(left.y, right.y));
}
CG_INLINE CGSize CGSizeScale(CGSize size, CGFloat factor)
{
return CGSizeMake(size.width * factor, size.height * factor);
@ -204,6 +214,14 @@ CG_INLINE CGSize CGSizeAdd(CGSize left, CGSize right)
return CGSizeMake(left.width + right.width, left.height + right.height);
}
CG_INLINE CGRect CGRectScale(CGRect rect, CGFloat factor)
{
CGRect result;
result.origin = CGPointScale(rect.origin, factor);
result.size = CGSizeScale(rect.size, factor);
return result;
}
CGFloat CGHairlineWidth(void);
NS_ASSUME_NONNULL_END

Loading…
Cancel
Save