Skip to content

Commit

Permalink
Working completely
Browse files Browse the repository at this point in the history
  • Loading branch information
rf1804 committed Sep 13, 2018
1 parent c5f4a75 commit 8768cb2
Show file tree
Hide file tree
Showing 34 changed files with 2,705 additions and 10 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
```javascript
import RNWescan from 'react-native-wescan';

// TODO: What to do with the module?
RNWescan;

RNWescan.scanDocument((response) => {
console.log(response) //It will give the private path of the final image
})
```

13 changes: 13 additions & 0 deletions ios/HeaderScan.h
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 */
6 changes: 5 additions & 1 deletion ios/RNWescan.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
#import "RCTBridgeModule.h"
#endif

@class ImageScannerController;
@class ImageScannerControllerDelegate;

@interface RNWescan : NSObject <RCTBridgeModule>
@property (nonatomic, strong) RCTResponseSenderBlock callback;

@end


29 changes: 28 additions & 1 deletion ios/RNWescan.m
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@

#import "RNWescan.h"
#import "RNWescan-Swift.h"
//#import <RNWescan.xcworkspace/RNWescan-Swift.h>

//@interface RNWescan() <ImageScannerControllerDelegate>
//
//@end


@implementation RNWescan

Expand All @@ -8,6 +15,26 @@ - (dispatch_queue_t)methodQueue
return dispatch_get_main_queue();
}
RCT_EXPORT_MODULE()
RCT_EXPORT_METHOD(scanDocument:(RCTResponseSenderBlock)callback) {

self.callback = callback;

ImageScannerController *scannerVC = [[ImageScannerController alloc] init];

UIViewController *root = [[[UIApplication sharedApplication] delegate] window].rootViewController;
[root presentViewController:scannerVC animated:true completion:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(weScanPostData:) name:@"WEScanImagePost" object:nil];
}


-(void)weScanPostData:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] removeObserver:self];

NSLog(@"asdfsf - %@", [notification userInfo]);
self.callback(@[[notification userInfo]]); // Return callback for 'cancel' action (if is required)
}

@end


213 changes: 210 additions & 3 deletions ios/RNWescan.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions ios/SwiftHeader.swift
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
67 changes: 67 additions & 0 deletions ios/WeScan/Common/EditScanCornerView.swift
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()
}

}
38 changes: 38 additions & 0 deletions ios/WeScan/Common/Error.swift
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"
}
}

}
176 changes: 176 additions & 0 deletions ios/WeScan/Common/Quadrilateral.swift
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
}

}
Loading

0 comments on commit 8768cb2

Please sign in to comment.