-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
34 changed files
with
2,705 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// | ||
// HeaderScan.h | ||
// RNWescan | ||
// | ||
// Created by OSX on 11/09/18. | ||
// Copyright © 2018 Facebook. All rights reserved. | ||
// | ||
|
||
#ifndef HeaderScan_h | ||
#define HeaderScan_h | ||
|
||
|
||
#endif /* HeaderScan_h */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// | ||
// SwiftHeader.swift | ||
// RNWescan | ||
// | ||
// Created by OSX on 11/09/18. | ||
// Copyright © 2018 Facebook. All rights reserved. | ||
// | ||
|
||
import Foundation |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// | ||
// EditScanCornerView.swift | ||
// WeScan | ||
// | ||
// Created by Boris Emorine on 3/5/18. | ||
// Copyright © 2018 WeTransfer. All rights reserved. | ||
// | ||
|
||
import UIKit | ||
|
||
/// A UIView used by corners of a quadrilateral that is aware of its position. | ||
final class EditScanCornerView: UIView { | ||
|
||
let position: CornerPosition | ||
|
||
/// The image to display when the corner view is highlighted. | ||
private var image: UIImage? | ||
private(set) var isHighlighted = false | ||
|
||
lazy private var circleLayer: CAShapeLayer = { | ||
let layer = CAShapeLayer() | ||
layer.fillColor = UIColor.clear.cgColor | ||
layer.strokeColor = UIColor.white.cgColor | ||
layer.lineWidth = 1.0 | ||
return layer | ||
}() | ||
|
||
init(frame: CGRect, position: CornerPosition) { | ||
self.position = position | ||
super.init(frame: frame) | ||
backgroundColor = UIColor.clear | ||
clipsToBounds = true | ||
layer.addSublayer(circleLayer) | ||
} | ||
|
||
required init?(coder aDecoder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
|
||
override func layoutSubviews() { | ||
super.layoutSubviews() | ||
layer.cornerRadius = bounds.width / 2.0 | ||
} | ||
|
||
override func draw(_ rect: CGRect) { | ||
super.draw(rect) | ||
|
||
let bezierPath = UIBezierPath(ovalIn: rect.insetBy(dx: circleLayer.lineWidth, dy: circleLayer.lineWidth)) | ||
circleLayer.frame = rect | ||
circleLayer.path = bezierPath.cgPath | ||
|
||
image?.draw(in: rect) | ||
} | ||
|
||
func highlightWithImage(_ image: UIImage) { | ||
isHighlighted = true | ||
self.image = image | ||
self.setNeedsDisplay() | ||
} | ||
|
||
func reset() { | ||
isHighlighted = false | ||
image = nil | ||
setNeedsDisplay() | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// | ||
// Error.swift | ||
// WeScan | ||
// | ||
// Created by Boris Emorine on 2/28/18. | ||
// Copyright © 2018 WeTransfer. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
/// Errors related to the `ImageScannerController` | ||
public enum ImageScannerControllerError: Error { | ||
/// The user didn't grant permission to use the camera. | ||
case authorization | ||
/// An error occured when setting up the user's device. | ||
case inputDevice | ||
/// An error occured when trying to capture a picture. | ||
case capture | ||
/// Error when creating the CIImage. | ||
case ciImageCreation | ||
} | ||
|
||
extension ImageScannerControllerError: LocalizedError { | ||
|
||
public var errorDescription: String? { | ||
switch self { | ||
case .authorization: | ||
return "Failed to get the user's authorization for camera." | ||
case .inputDevice: | ||
return "Could not setup input device." | ||
case .capture: | ||
return "Could not capture pitcure." | ||
case .ciImageCreation: | ||
return "Internal Error - Could not create CIImage" | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
// | ||
// Quadrilateral.swift | ||
// WeScan | ||
// | ||
// Created by Boris Emorine on 2/8/18. | ||
// Copyright © 2018 WeTransfer. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import AVFoundation | ||
import UIKit | ||
import CoreImage | ||
import CoreGraphics | ||
|
||
/// A data structure representing a quadrilateral and its position. This class exists to bypass the fact that CIRectangleFeature is read-only. | ||
public struct Quadrilateral: Transformable { | ||
|
||
/// A point that specifies the top left corner of the quadrilateral. | ||
var topLeft: CGPoint | ||
|
||
/// A point that specifies the top right corner of the quadrilateral. | ||
var topRight: CGPoint | ||
|
||
/// A point that specifies the bottom right corner of the quadrilateral. | ||
var bottomRight: CGPoint | ||
|
||
/// A point that specifies the bottom left corner of the quadrilateral. | ||
var bottomLeft: CGPoint | ||
|
||
init(rectangleFeature: CIRectangleFeature) { | ||
self.topLeft = rectangleFeature.topLeft | ||
self.topRight = rectangleFeature.topRight | ||
self.bottomLeft = rectangleFeature.bottomLeft | ||
self.bottomRight = rectangleFeature.bottomRight | ||
} | ||
|
||
init(topLeft: CGPoint, topRight: CGPoint, bottomRight: CGPoint, bottomLeft: CGPoint) { | ||
self.topLeft = topLeft | ||
self.topRight = topRight | ||
self.bottomRight = bottomRight | ||
self.bottomLeft = bottomLeft | ||
} | ||
|
||
/// Generates a `UIBezierPath` of the quadrilateral. | ||
func path() -> UIBezierPath { | ||
let path = UIBezierPath() | ||
path.move(to: topLeft) | ||
path.addLine(to: topRight) | ||
path.addLine(to: bottomRight) | ||
path.addLine(to: bottomLeft) | ||
path.close() | ||
|
||
return path | ||
} | ||
|
||
/// Applies a `CGAffineTransform` to the quadrilateral. | ||
/// | ||
/// - Parameters: | ||
/// - t: the transform to apply. | ||
/// - Returns: The transformed quadrilateral. | ||
func applying(_ transform: CGAffineTransform) -> Quadrilateral { | ||
let quadrilateral = Quadrilateral(topLeft: topLeft.applying(transform), topRight: topRight.applying(transform), bottomRight: bottomRight.applying(transform), bottomLeft: bottomLeft.applying(transform)) | ||
|
||
return quadrilateral | ||
} | ||
|
||
/// Reorganizes the current quadrilateal, making sure that the points are at their appropriate positions. For example, it ensures that the top left point is actually the top and left point point of the quadrilateral. | ||
mutating func reorganize() { | ||
let points = [topLeft, topRight, bottomRight, bottomLeft] | ||
let ySortedPoints = sortPointsByYValue(points) | ||
|
||
guard ySortedPoints.count == 4 else { | ||
return | ||
} | ||
|
||
let topMostPoints = Array(ySortedPoints[0..<2]) | ||
let bottomMostPoints = Array(ySortedPoints[2..<4]) | ||
let xSortedTopMostPoints = sortPointsByXValue(topMostPoints) | ||
let xSortedBottomMostPoints = sortPointsByXValue(bottomMostPoints) | ||
|
||
guard xSortedTopMostPoints.count > 1, | ||
xSortedBottomMostPoints.count > 1 else { | ||
return | ||
} | ||
|
||
topLeft = xSortedTopMostPoints[0] | ||
topRight = xSortedTopMostPoints[1] | ||
bottomRight = xSortedBottomMostPoints[1] | ||
bottomLeft = xSortedBottomMostPoints[0] | ||
} | ||
|
||
/// Scales the quadrilateral based on the ratio of two given sizes, and optionnaly applies a rotation. | ||
/// | ||
/// - Parameters: | ||
/// - fromSize: The size the quadrilateral is currently related to. | ||
/// - toSize: The size to scale the quadrilateral to. | ||
/// - rotationAngle: The optional rotation to apply. | ||
/// - Returns: The newly scaled and potentially rotated quadrilateral. | ||
func scale(_ fromSize: CGSize, _ toSize: CGSize, withRotationAngle rotationAngle: CGFloat = 0.0) -> Quadrilateral { | ||
var invertedfromSize = fromSize | ||
let rotated = rotationAngle != 0.0 | ||
|
||
if rotated && rotationAngle != CGFloat.pi { | ||
invertedfromSize = CGSize(width: fromSize.height, height: fromSize.width) | ||
} | ||
|
||
var transformedQuad = self | ||
let invertedFromSizeWidth = invertedfromSize.width == 0 ? .leastNormalMagnitude : invertedfromSize.width | ||
|
||
let scale = toSize.width / invertedFromSizeWidth | ||
let scaledTransform = CGAffineTransform(scaleX: scale, y: scale) | ||
transformedQuad = transformedQuad.applying(scaledTransform) | ||
|
||
if rotated { | ||
let rotationTransform = CGAffineTransform(rotationAngle: rotationAngle) | ||
|
||
let fromImageBounds = CGRect(x: 0.0, y: 0.0, width: fromSize.width, height: fromSize.height).applying(scaledTransform).applying(rotationTransform) | ||
|
||
let toImageBounds = CGRect(x: 0.0, y: 0.0, width: toSize.width, height: toSize.height) | ||
let translationTransform = CGAffineTransform.translateTransform(fromCenterOfRect: fromImageBounds, toCenterOfRect: toImageBounds) | ||
|
||
transformedQuad = transformedQuad.applyTransforms([rotationTransform, translationTransform]) | ||
} | ||
|
||
return transformedQuad | ||
} | ||
|
||
// Convenience functions | ||
|
||
/// Sorts the given `CGPoints` based on their y value. | ||
/// - Parameters: | ||
/// - points: The poinmts to sort. | ||
/// - Returns: The points sorted based on their y value. | ||
private func sortPointsByYValue(_ points: [CGPoint]) -> [CGPoint] { | ||
return points.sorted { (point1, point2) -> Bool in | ||
point1.y < point2.y | ||
} | ||
} | ||
|
||
/// Sorts the given `CGPoints` based on their x value. | ||
/// - Parameters: | ||
/// - points: The poinmts to sort. | ||
/// - Returns: The points sorted based on their x value. | ||
private func sortPointsByXValue(_ points: [CGPoint]) -> [CGPoint] { | ||
return points.sorted { (point1, point2) -> Bool in | ||
point1.x < point2.x | ||
} | ||
} | ||
|
||
} | ||
|
||
extension Quadrilateral { | ||
|
||
/// Converts the current to the cartesian coordinate system (where 0 on the y axis is at the bottom). | ||
/// | ||
/// - Parameters: | ||
/// - height: The height of the rect containing the quadrilateral. | ||
/// - Returns: The same quadrilateral in the cartesian corrdinate system. | ||
func toCartesian(withHeight height: CGFloat) -> Quadrilateral { | ||
let topLeft = self.topLeft.cartesian(withHeight: height) | ||
let topRight = self.topRight.cartesian(withHeight: height) | ||
let bottomRight = self.bottomRight.cartesian(withHeight: height) | ||
let bottomLeft = self.bottomLeft.cartesian(withHeight: height) | ||
|
||
return Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) | ||
} | ||
|
||
} | ||
|
||
extension Quadrilateral: Equatable { | ||
|
||
public static func == (lhs: Quadrilateral, rhs: Quadrilateral) -> Bool { | ||
return lhs.topLeft == rhs.topLeft && lhs.topRight == rhs.topRight && lhs.bottomRight == rhs.bottomRight && lhs.bottomLeft == rhs.bottomLeft | ||
} | ||
|
||
} |
Oops, something went wrong.