diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..3ad02974 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,50 @@ +{ + "extends": [ + "airbnb", + "prettier", + "prettier/react" + ], + "parser": "babel-eslint", + "plugins": [ + "prettier" + ], + "env": { + "jasmine": true + }, + "globals": { + "__DEV__": true, + "ReactClass": true, + "fetch": true + }, + "rules": { + "prettier/prettier": ["error", { + "trailingComma": "all", + "singleQuote": true + }], + + "no-underscore-dangle": 0, + "no-use-before-define": 0, + "no-unused-expressions": 0, + "new-cap": 0, + "no-plusplus": 0, + "no-class-assign": 0, + "no-duplicate-imports": 0, + "no-console": 0, + + "import/extensions": 0, + "import/no-extraneous-dependencies": 0, + "import/no-unresolved": 0, + + "react/jsx-filename-extension": [ + 0, { "extensions": [".js", ".jsx"] } + ], + + "react/sort-comp": 0, + "react/prefer-stateless-function": 0, + + "react/forbid-prop-types": 1, + "react/prop-types": 0, + "react/require-default-props": 0, + "react/no-unused-prop-types": 0 + } +} diff --git a/.gitignore b/.gitignore index 00cbbdf5..619395b4 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,27 @@ typings/ # dotenv environment variables file .env + + +#### Xcode ignore #### +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..e5af7848 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "ios/PocketSVG"] + path = ios/PocketSVG + url = https://github.com/pocketsvg/PocketSVG diff --git a/ARKit.js b/ARKit.js new file mode 100644 index 00000000..f52b3698 --- /dev/null +++ b/ARKit.js @@ -0,0 +1,248 @@ +// +// index.js +// +// Created by HippoAR on 7/9/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +import { + StyleSheet, + View, + Text, + NativeModules, + requireNativeComponent, +} from 'react-native'; +import { keyBy, mapValues, isBoolean } from 'lodash'; +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; + +import { + deprecated, + detectionImages, + planeDetection, + position, + transition, +} from './components/lib/propTypes'; +import { pickColors, pickColorsFromFile } from './lib/pickColors'; +import generateId from './components/lib/generateId'; + +const ARKitManager = NativeModules.ARKitManager; + +const TRACKING_STATES = ['NOT_AVAILABLE', 'LIMITED', 'NORMAL']; + +const TRACKING_REASONS = [ + 'NONE', + 'INITIALIZING', + 'EXCESSIVE_MOTION', + 'INSUFFICIENT_FEATURES', +]; +const TRACKING_STATES_COLOR = ['red', 'orange', 'green']; + +class ARKit extends Component { + state = { + state: 0, + reason: 0, + floor: null, + }; + + componentDidMount() { + ARKitManager.resume(); + } + + componentWillUnmount() { + ARKitManager.pause(); + } + + getCallbackProps() { + return mapValues( + keyBy([ + 'onTapOnPlaneUsingExtent', + 'onTapOnPlaneNoExtent', + 'onPlaneDetected', + 'onPlaneRemoved', + 'onPlaneUpdated', + 'onAnchorDetected', + 'onAnchorUpdated', + 'onAnchorRemoved', + 'onTrackingState', + 'onARKitError', + ]), + name => this.callback(name), + ); + } + + render(AR = RCTARKit) { + let state = null; + if (this.props.debug) { + state = ( + + + + {TRACKING_REASONS[this.state.reason] || this.state.reason} + + + ); + } + return ( + + + {state} + + ); + } + + _onTrackingState = ({ + state = this.state.state, + reason = this.state.reason, + }) => { + if (this.props.onTrackingState) { + this.props.onTrackingState({ + state: TRACKING_STATES[state] || state, + reason: TRACKING_REASONS[reason] || reason, + }); + } + // TODO: check if we can remove this + if (this.props.debug) { + this.setState({ + state, + reason, + }); + } + }; + + _onEvent = event => { + let eventName = event.nativeEvent.event; + if (!eventName) { + return; + } + eventName = eventName.charAt(0).toUpperCase() + eventName.slice(1); + const eventListener = this.props[`on${eventName}`]; + if (eventListener) { + eventListener(event.nativeEvent); + } + }; + + // handle deprecated alias + _onPlaneUpdated = nativeEvent => { + if (this.props.onPlaneUpdate) { + this.props.onPlaneUpdate(nativeEvent); + } + if (this.props.onPlaneUpdated) { + this.props.onPlaneUpdated(nativeEvent); + } + }; + + callback(name) { + return event => { + if (this[`_${name}`]) { + this[`_${name}`](event.nativeEvent); + return; + } + if (!this.props[name]) { + return; + } + this.props[name](event.nativeEvent); + }; + } +} + +const styles = StyleSheet.create({ + statePanel: { + position: 'absolute', + top: 30, + left: 10, + height: 20, + borderRadius: 10, + padding: 4, + backgroundColor: 'black', + flexDirection: 'row', + }, + stateIcon: { + width: 12, + height: 12, + borderRadius: 6, + marginRight: 4, + }, + stateText: { + color: 'white', + fontSize: 10, + height: 12, + }, +}); + +// copy all ARKitManager properties to ARKit +Object.keys(ARKitManager).forEach(key => { + ARKit[key] = ARKitManager[key]; +}); + +const addDefaultsToSnapShotFunc = funcName => ( + { target = 'cameraRoll', format = 'png' } = {}, +) => ARKitManager[funcName]({ target, format }); + +ARKit.snapshot = addDefaultsToSnapShotFunc('snapshot'); +ARKit.snapshotCamera = addDefaultsToSnapShotFunc('snapshotCamera'); + +ARKit.exportModel = presetId => { + const id = presetId || generateId(); + const property = { id }; + return ARKitManager.exportModel(property).then(result => ({ ...result, id })); +}; + +ARKit.pickColors = pickColors; +ARKit.pickColorsFromFile = pickColorsFromFile; +ARKit.propTypes = { + debug: PropTypes.bool, + planeDetection, + origin: PropTypes.shape({ + position, + transition, + }), + lightEstimationEnabled: PropTypes.bool, + autoenablesDefaultLighting: PropTypes.bool, + worldAlignment: PropTypes.number, + detectionImages, + onARKitError: PropTypes.func, + + onFeaturesDetected: PropTypes.func, + // onLightEstimation is called rapidly, better poll with + // ARKit.getCurrentLightEstimation() + onLightEstimation: PropTypes.func, + + onPlaneDetected: PropTypes.func, + onPlaneRemoved: PropTypes.func, + onPlaneUpdated: PropTypes.func, + onPlaneUpdate: deprecated(PropTypes.func, 'Use `onPlaneUpdated` instead'), + + onAnchorDetected: PropTypes.func, + onAnchorRemoved: PropTypes.func, + onAnchorUpdated: PropTypes.func, + + onTrackingState: PropTypes.func, + onTapOnPlaneUsingExtent: PropTypes.func, + onTapOnPlaneNoExtent: PropTypes.func, + onEvent: PropTypes.func, + isMounted: PropTypes.func, + isInitialized: PropTypes.func, +}; + +const RCTARKit = requireNativeComponent('RCTARKit', ARKit); + +export default ARKit; diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..71e34db5 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ + + +see Releases diff --git a/DeviceMotion.js b/DeviceMotion.js new file mode 100644 index 00000000..aa8041fd --- /dev/null +++ b/DeviceMotion.js @@ -0,0 +1,19 @@ +import { NativeEventEmitter, NativeModules } from 'react-native'; + +const deviceMotionEmitter = new NativeEventEmitter(NativeModules.DeviceMotion); +let subscription; + +const DeviceMotion = { + start(callback, interval) { + NativeModules.DeviceMotion.setUpdateInterval(interval); + subscription = deviceMotionEmitter.addListener('MotionData', callback); + NativeModules.DeviceMotion.startUpdates(); + }, + + stop() { + subscription.remove(); + NativeModules.DeviceMotion.stopUpdates(); + }, +}; + +export default DeviceMotion; diff --git a/README.md b/README.md index 77a87e07..f2f07969 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,41 @@ -# react-native-arkit +⚠️ **LOOKING FOR MAINTAINERS - This Repo is currently not maintained. Give https://github.com/ViroCommunity/viro/ a try which has been open sourced and also supports android (ARCORE)** ⚠️ + +# react-native-arkit + + [![npm version](https://img.shields.io/npm/v/react-native-arkit.svg?style=flat)](https://www.npmjs.com/package/react-native-arkit) [![npm downloads](https://img.shields.io/npm/dm/react-native-arkit.svg?style=flat)](https://www.npmjs.com/package/react-native-arkit) React Native binding for iOS ARKit. -**Note**: ARKit is only supported by devices with A9 or later processors (iPhone 6s/7/SE, iPad 2017/Pro) on [iOS 11 beta](https://developer.apple.com/download/). You also need [Xcode 9 beta](https://developer.apple.com/download/) to build the project. +**Made with React Native Arkit**: + +- Homestory: An AI powered interior design assistant ([App store](https://itunes.apple.com/us/app/homestory-augmented-reality/id1292552232?ls=1&mt=8)) + +**Tutorial**: [How to make an ARKit app in 5 minutes using React Native](https://medium.com/@HippoAR/how-to-make-your-own-arkit-app-in-5-minutes-using-react-native-9d7ce109a4c2) + +**Sample Project**: https://github.com/HippoAR/ReactNativeARKit + +**Note**: ARKit is only supported by devices with A9 or later processors (iPhone 6s/7/SE/8/X, iPad 2017/Pro) on iOS 11. You also need Xcode 9 to build the project. + +There is a Slack group that anyone can join for help / support / general questions. + +[**Join Slack**](https://join.slack.com/t/react-native-ar/shared_invite/enQtMjUzMzg3MjM0MTQ5LWU3Nzg2YjI4MGRjMTM1ZDBlNmIwYTE4YmM0M2U0NmY2YjBiYzQ4YzlkODExMTA0NDkwMzFhYWY4ZDE2M2Q4NGY) ## Getting started -`$ npm install react-native-arkit --save` +`$ yarn add react-native-arkit` + +make sure to use the latest version of yarn (>=1.x.x) + +(npm does not work properly at the moment. See https://github.com/HippoAR/react-native-arkit/issues/103) + ### Mostly automatic installation +⚠️ **Currently automatic installation does not work as PocketSVG is missing. Follow the manual installation.** + `$ react-native link react-native-arkit` ### Manual installation @@ -21,18 +44,37 @@ React Native binding for iOS ARKit. #### iOS 1. In XCode, in the project navigator, right click `Libraries` ➜ `Add Files to [your project's name]` -2. Go to `node_modules` ➜ `react-native-arkit` and add `RCTARKit.xcodeproj` -3. In XCode, in the project navigator, select your project. Add `libRCTARKit.a` to your project's `Build Phases` ➜ `Link Binary With Libraries` -4. Run your project (`Cmd+R`)< +2. Go to `node_modules` ➜ add `react-native-arkit/ios/RCTARKit.xcodeproj` and `react-native-arkit/ios/PocketSVG/PocketSVG.xcodeproj` +3. In XCode, in the project navigator, select your project. Add `libRCTARKit.a` `and PocketSVG.framework` to your project's `Build Phases` ➜ `Link Binary With Libraries` +4. In Tab `General` ➜ `Embedded Binaries` ➜ `+` ➜ Add `PocketSVG.framework ios` +5. Run your project (`Cmd+R`)< + + +##### iOS Project configuration + +These steps are mandatory regardless of doing a manual or automatic installation: + +1. Give permissions for camera usage. In `Info.plist` add the following: + +``` +NSCameraUsageDescription +Your message to user when the camera is accessed for the first time +``` +2. ARKit only runs on arm64-ready devices so the default build architecture should be set to arm64: go to `Build settings` ➜ `Build Active Architecture Only` and change the value to `Yes`. + + ## Usage -Sample React Native ARKit App +A simple sample React Native ARKit App + ```javascript +// index.ios.js + import React, { Component } from 'react'; import { AppRegistry, View } from 'react-native'; -import ARKit from 'react-native-arkit'; +import { ARKit } from 'react-native-arkit'; export default class ReactNativeARKit extends Component { render() { @@ -41,11 +83,120 @@ export default class ReactNativeARKit extends Component { + // enable plane detection (defaults to Horizontal) + planeDetection={ARKit.ARPlaneDetection.Horizontal} + + // enable light estimation (defaults to true) + lightEstimationEnabled + // get the current lightEstimation (if enabled) + // it fires rapidly, so better poll it from outside with + // ARKit.getCurrentLightEstimation() + onLightEstimation={e => console.log(e.nativeEvent)} + + // event listener for (horizontal) plane detection + onPlaneDetected={anchor => console.log(anchor)} + + // event listener for plane update + onPlaneUpdated={anchor => console.log(anchor)} + + // arkit sometimes removes detected planes + onPlaneRemoved={anchor => console.log(anchor)} + + // event listeners for all anchors, see [Planes and Anchors](#planes-and-anchors) + onAnchorDetected={anchor => console.log(anchor)} + onAnchorUpdated={anchor => console.log(anchor)} + onAnchorRemoved={anchor => console.log(anchor)} + + // you can detect images and will get an anchor for these images + detectionImages={[{ resourceGroupName: 'DetectionImages' }]} + + + onARKitError={console.log} // if arkit could not be initialized (e.g. missing permissions), you will get notified here + > + + + + + + + + + + + + + + + + `, + pathFlatness: 0.1, + // it's also possible to specify a chamfer profile: + chamferRadius: 5, + chamferProfilePathSvg: ` + + `, + extrusion: 10, + }} + /> + ); } @@ -55,6 +206,503 @@ AppRegistry.registerComponent('ReactNativeARKit', () => ReactNativeARKit); ``` + + + + +### ``-Component + +#### Props + +| Prop | Type | Note | +|---|---|---| +| `debug` | `Boolean` | Debug mode will show the 3D axis and feature points detected. +| `planeDetection` | `ARKit.ARPlaneDetection.{ Horizontal \| Vertical \| HorizontalVertical \| None }` | ARKit plane detection. Defaults to `Horizontal`. `Vertical` is available with IOS 11.3 +| `lightEstimationEnabled` | `Boolean` | ARKit light estimation (defaults to false). +| `worldAlignment` | `ARKit.ARWorldAlignment.{ Gravity \| GravityAndHeading \| Camera }` | **ARWorldAlignmentGravity**
The coordinate system's y-axis is parallel to gravity, and its origin is the initial position of the device. **ARWorldAlignmentGravityAndHeading**
The coordinate system's y-axis is parallel to gravity, its x- and z-axes are oriented to compass heading, and its origin is the initial position of the device. **ARWorldAlignmentCamera**
The scene coordinate system is locked to match the orientation of the camera. Defaults to `ARKit.ARWorldAlignment.Gravity`. [See](https://developer.apple.com/documentation/arkit/arworldalignment)| +| `origin` | `{position, transition}` | Usually `{0,0,0}` is where you launched the app. If you want to have a different origin, you can set it here. E.g. if you set `origin={{position: {0,-1, 0}, transition: {duration: 1}}}` the new origin will be one meter below. If you have any objects already placed, they will get moved down using the given transition. All hit-test functions or similar will report coordinates relative to that new origin as `position`. You can get the original coordinates with `positionAbsolute` in these functions | +| `detectionImages` | `Array` | An Array of `DetectionImage` (see below), only available on IOS 11.3 | + +##### `DetectionImage` + +An `DetectionImage` is an image or image resource group that should be detected by ARKit. + +See https://developer.apple.com/documentation/arkit/arreferenceimage?language=objc how to add these images. + +You will then receive theses images in `onAnchorDetected/onAnchorUpdated`. See also [Planes and Anchors](#planes-and-anchors) for more details. + +`DetectionImage` has these properties + +| Prop | Type | Notes +|---|---|---| +| `resourceGroupName` | `String` | The name of the resource group | + +We probably will add the option to load images from other sources as well (PRs encouraged). + +#### Events + +| Event Name | Returns | Notes +|---|---|---| +| `onARKitError` | `ARKiterror` | will report whether an error occured while initializing ARKit. A common error is when the user has not allowed camera access. Another error is, if you use `worldAlignment=GravityAndHeading` and location service is turned off | +| `onLightEstimation` | `{ ambientColorTemperature, ambientIntensity }` | Light estimation on every frame. Called rapidly, better use polling. See `ARKit.getCurrentLightEstimation()` +| `onFeaturesDetected` | `{ featurePoints}` | Detected Features on every frame (currently also not throttled). Usefull to display custom dots for detected features. You can also poll this information with `ARKit.getCurrentDetectedFeaturePoints()` +| `onAnchorDetected` | `Anchor` | When an anchor (plane or image) is first detected. +| `onAnchorUpdated` | `Anchor` | When an anchor is updated +| `onAnchorRemoved` | `Anchor` | When an anchor is removed +| `onPlaneDetected` | `Anchor` | When a plane anchor is first detected. +| `onPlaneUpdated` | `Anchor` | When a detected plane is updated +| `onPlaneRemoved` | `Anchor` | When a detected plane is removed + +See [Planes and Anchors](#planes-and-anchors) for Details about anchors + + + +#### Planes and Anchors + +ARKit can detect different anchors in the real world: + +- `plane` horizontal and vertical planes +- `image`, image-anchors [See DetectionImage](#DetectionImage) +- face with iphone X or similar (not implemented yet) + +You then will receive anchor objects in the `onAnchorDetected`, `onAnchorUpdated`, `onAnchorRemoved` callbacks on your ``-component. + +You can use `onPlaneDetected`, `onPlaneUpdated`, `onPlaneRemoved` to only receive plane-anchors (may be deprecated later). + +The `Anchor` object has the following properties: + +| Property | Type | Description +|---|---|---| +| `id` | `String` | a unique id identifying the anchor | +| `type` | `String` | The type of the anchor (plane, image) | +| `position` | `{ x, y, z }` | the position of the anchor (relative to the origin) | +| `positionAbsolute` | `{ x, y, z }` | the absolute position of the anchor | +| `eulerAngles` | `{ x, y, z }` | the rotation of the plane | + +If its a `plane`-anchor, it will have these additional properties: + +| Property | Description +|---|---| +| `alignment` | `ARKit.ARPlaneAnchorAlignment.Horizontal` or `ARKit.ARPlaneAnchorAlignment.Vertical`
so you can check whether it was a horizontal or vertical plane | +| `extent` | see https://developer.apple.com/documentation/arkit/arplaneanchor?language=objc | +| `center` | see https://developer.apple.com/documentation/arkit/arplaneanchor?language=objc | + +`image`-Anchor: + +| Property | type | Description +|---|---|---| +| `image` | `{name}` | an object with the name of the image. + + + +### Static methods + +Static Methods can directly be used on the `ARKit`-export: + +``` +import { ARKit } from 'react-native-arkit' + +//... +const result = await ARKit.hitTestSceneObjects(point); + +``` + +All methods return a *promise* with the result. + +| Method Name | Arguments | Notes +|---|---|---| +| `snapshot` | | | Take a screenshot (will save to Photo Library) | +| `snapshotCamera` | | Take a screenshot without 3d models (will save to Photo Library) | +| `getCameraPosition` | | Get the current position of the `ARCamera` | +| `getCamera` | | Get all properties of the `ARCamera` | +| `getCurrentLightEstimation` | | Get current light estimation `{ ambientColorTemperature, ambientIntensity}` | +| `getCurrentDetectedFeaturePoints` | | Get current detected feature points (in last current frame) (array) | +| `focusScene` | | Sets the scene's position/rotation to where it was when first rendered (but now relative to your device's current position/rotation) | +| `hitTestPlanes` | point, type | check if a plane has ben hit by point (`{x,y}`) with detection type (any of `ARKit.ARHitTestResultType`). See https://developer.apple.com/documentation/arkit/arhittestresulttype?language=objc for further information | +| `hitTestSceneObjects` | point | check if a scene object has ben hit by point (`{x,y}`) | +| `isInitialized` | boolean | check whether arkit has been initialized (e.g. by mounting). See https://github.com/HippoAR/react-native-arkit/pull/152 for details | +| `isMounted` | boolean | check whether arkit has been mounted. See https://github.com/HippoAR/react-native-arkit/pull/152 for details | + +### 3D-Components + +This project allows you to work with 3d elements like with usual react-components. +We provide some primitive shapes like cubes, spheres, etc. as well as +a component to load model-files. + +You can also nest components to create new Components. Child-elements will +be relative to the parent: + +``` +const BigExclamationMark = ({ position, eulerAngles, color = '#ff0000' }) => ( + + + + +) + +// somewhere else + + + +``` + + +#### General props + +Most 3d object have these common properties + +| Prop | Type | Description | +|---|---|---| +| `position` | `{ x, y, z }` | The object's position (y is up) | +| `scale` | Number | The scale of the object. Defaults to 1 | +| `eulerAngles` | `{ x, y, z }` | The rotation in eulerAngles | +| `id` | String | a unique identifier. Only provide one, if you need to find the node later in hit-testing. | +| `shape` | depends on object | the shape of the object (will probably renamed to geometry in future versions) +| `material` | `{ diffuse, metalness, roughness, lightingModel, shaders }` | the material of the object | +| `transition` | `{duration: 1}` | Some property changes can be animated like in css transitions. Currently you can specify the duration (in seconds). | + +Advanced properties: + +| Prop | Type | Description | +|---|---|---| +| `rotation` | TODO | see scenkit documentation | +| `orientation` | TODO | see scenkit documentation | +| `renderingOrder` | Number | Order in which object is rendered. Usefull to place elements "behind" others, although they are nearer. | +| `categoryBitMask` | Number / bitmask | control which lights affect this object | +| `castsShadow` | `boolean` | whether this object casts shadows | +| `constraint` | `ARKit.Constraint.{ BillboardAxisAll \| BillboardAxisX \| BillboardAxisY \| BillboardAxisZ \| None }` | Constrains the node to always point to the camera | + +*New experimental feature:* + +You can switch properties on mount or onmount by specifying `propsOnMount` and `propsOnUnmount`. +E.g. you can scale an object on unmount: + +``` + +``` + +#### Material + +Most objects take a material property with these sub-props: + +| Prop | Type | Description | +|---|---|---| +| `diffuse` | `{ ...mapProperties }` (see below) | [diffuse](https://developer.apple.com/documentation/scenekit/scnmaterial/1462589-diffuse?language=objc) +| `specular` | `{ ...mapProperties }` (see below) | [specular](https://developer.apple.com/documentation/scenekit/scnmaterial/1462516-specular?language=objc) +| `displacement` | `{ ...mapProperties }` (see below) | [displacement](https://developer.apple.com/documentation/scenekit/scnmaterial/2867516-displacement?language=objc) +| `normal` | `{ ...mapProperties }` (see below) | [normal](https://developer.apple.com/documentation/scenekit/scnmaterial/1462542-normal) +| `metalness` | number | metalness of the object | +| `roughness` | number | roughness of the object | +| `doubleSided` | boolean | render both sides, default is `true` | +| `litPerPixel` | boolean | calculate lighting per-pixel or vertex [litPerPixel](https://developer.apple.com/documentation/scenekit/scnmaterial/1462580-litperpixel) | +| `lightingModel` | `ARKit.LightingModel.*` | [LightingModel](https://developer.apple.com/documentation/scenekit/scnmaterial.lightingmodel) | +| `blendMode` | `ARKit.BlendMode.*` | [BlendMode](https://developer.apple.com/documentation/scenekit/scnmaterial/1462585-blendmode) | +| `transparencyMode` | `ARKit.TransparencyMode.*` | [TransparencyMode](https://developer.apple.com/documentation/scenekit/scnmaterial/1462549-transparencymode?language=objc) | +| `fillMode` | `ARKit.FillMode.*` | [FillMode](https://developer.apple.com/documentation/scenekit/scnmaterial/2867442-fillmode) +| `shaders` | Object with keys from `ARKit.ShaderModifierEntryPoint.*` and shader strings as values | [Shader modifiers](https://developer.apple.com/documentation/scenekit/scnshadable) | +| `colorBufferWriteMask` | `ARKit.ColorMask.*` | [color mask](https://developer.apple.com/documentation/scenekit/scncolormask). Set to ARKit.ColorMask.None so that an object is transparent, but receives deferred shadows. | + +Map Properties: + +| Prop | Type | Description | +|---|---|---| +| `path` | string | Currently `require` is not supported, so this is an absolute link to a local resource placed for example in .xcassets | +| `color` | string | Color string, only used if path is not provided | +| `wrapS` | `ARKit.WrapMode.{ Clamp \| Repeat \| Mirror }` | [wrapS](https://developer.apple.com/documentation/scenekit/scnmaterialproperty/1395384-wraps?language=objc) | +| `wrapT` | `ARKit.WrapMode.{ Clamp \| Repeat \| Mirror }` |  [wrapT](https://developer.apple.com/documentation/scenekit/scnmaterialproperty/1395382-wrapt?language=objc) | +| `wrap` | `ARKit.WrapMode.{ Clamp \| Repeat \| Mirror }` |  Shorthand for setting both wrapS & wrapT | +| `translation` | `{ x, y, z }` | Translate the UVs, equivalent to applying a translation matrix to SceneKit's `transformContents` | +| `rotation` | `{ angle, x, y, z }` | Rotate the UVs, equivalent to applying a rotation matrix to SceneKit's `transformContents` | +| `scale` | `{ x, y, z }` | Scale the UVs, equivalent to applying a scale matrix to SceneKit's `transformContents` | + + +#### `` + +This Object has no geometry, but is simply a wrapper for other components. +It receives all common properties like position, eulerAngles, scale, opacity, etc. +but no shape or material. + + + +#### [``](https://developer.apple.com/documentation/scenekit/scnbox) + + +| Prop | Type | +|---|---| +| `shape` | `{ width, height, length, chamfer }` | + +And any common object property (position, material, etc.) + +#### [``](https://developer.apple.com/documentation/scenekit/scnsphere) + + +| Prop | Type | +|---|---| +| `shape` | `{ radius }` | + + + +#### [``](https://developer.apple.com/documentation/scenekit/scncylinder) + + +| Prop | Type | +|---|---| +| `shape` | `{ radius, height }` | + +#### [``](https://developer.apple.com/documentation/scenekit/scncone) + + +| Prop | Type | +|---|---| +| `shape` | `{ topR, bottomR, height }` | + +#### [``](https://developer.apple.com/documentation/scenekit/scnpyramid) + + +| Prop | Type | +|---|---| +| `shape` | `{ width, height, length }` | + +#### [``](https://developer.apple.com/documentation/scenekit/scntube) + + +| Prop | Type | +|---|---| +| `shape` | `{ innerR, outerR, height }` | + +#### [``](https://developer.apple.com/documentation/scenekit/scntorus) + + +| Prop | Type | +|---|---| +| `shape` | `{ ringR, pipeR }` | + +#### [``](https://developer.apple.com/documentation/scenekit/scncapsule) + + +| Prop | Type | +|---|---| +| `shape` | `{ capR, height }` | + +#### [``](https://developer.apple.com/documentation/scenekit/scnplane) + + +| Prop | Type | +|---|---| +| `shape` | `{ width, height }` | + +Notice: planes are veritcally aligned. If you want a horizontal plane, rotate it around the x-axis. + +*Example*: + +This is a horizontal plane that only receives shadows, but is invisible otherwise: + +``` + +``` + + +#### [``](https://developer.apple.com/documentation/scenekit/scntext) + +| Prop | Type | +|---|---| +| `text` | `String` | +| `font` | `{ name, size, depth, chamfer }` | + + + +#### `` + +SceneKit only supports `.scn` and `.dae` formats. + + +| Prop | Type | +|---|---| +| `model` | `{ file, node, scale, alpha }` | + +Objects currently don't take material property. + +#### `` + +Creates a extruded shape by an svg path. +See https://github.com/HippoAR/react-native-arkit/pull/89 for details + +| Prop | Type | +|---|---| +| `shape` | `{ pathSvg, extrusion, pathFlatness, chamferRadius, chamferProfilePathSvg, chamferProfilePathFlatness }` | + + + +#### [``](https://developer.apple.com/documentation/scenekit/scnlight) + +Place lights on the scene! + +You might set `autoenablesDefaultLighting={false}` on The `` component to disable default lighting. You can use `lightEstimationEnabled` and `ARKit.getCurrentLightEstimation()` to find values for intensity and temperature. This produces much nicer results then `autoenablesDefaultLighting`. + + + +| Prop | Type | Description | +|---|---|---| +| `position` | `{ x, y, z }` | | +| `eulerAngles` | `{ x, y, z }` | | +| `type` | any of `ARKit.LightType` | see [here for details](https://developer.apple.com/documentation/scenekit/scnlight.lighttype) | +| `color` | `string` | the color of the light | +| `temperature` | `Number` | The color temperature of the light | +| `intensity` | `Number` | The light intensity | +| `lightCategoryBitMask` | `Number`/`bitmask` | control which objects are lit by this light | +| `castsShadow` | `boolean` | whether to cast shadows on object | +| `shadowMode`| `ARKit.ShadowMode.* | Define the shadowmode. Set to `ARKit.ShadowMode.Deferred` to cast shadows on invisible objects (like an invisible floor plane) | + + + +Most properties described here are also supported: https://developer.apple.com/documentation/scenekit/scnlight + +This feature is new. If you experience any problem, please report an issue! + + +### HOCs (higher order components) + +#### withProjectedPosition() + +this hoc allows you to create 3D components where the position is always relative to the same point on the screen/camera, but sticks to a plane or object. + +Think about a 3D cursor that can be moved across your table or a 3D cursor on a wall. + +You can use the hoc like this: + +``` +const Cursor3D = withProjectedPosition()(({positionProjected, projectionResult}) => { + if(!projectionResult) { + // nothing has been hit, don't render it + return null; + } + return ( + + ) +}) + +``` + +It's recommended that you specify a transition duration (0.1s works nice), as the position gets updated rapidly, but slightly throttled. + +Now you can use your 3D cursor like this: + +##### Attach to a given detected horizontal plane + +Given you have detected a plane with onPlaneDetected, you can make the cursor stick to that plane: + +``` + + +``` + +If you don't have the id, but want to place the cursor on a certain plane (e.g. the first or last one), pass a function for plane. This function will get all hit-results and you can return the one you need: + +``` + results.length > 0 ? results[0] : null + }} +/> + +``` + +You can also add a property `onProjectedPosition` to your cursor which will be called with the hit result on every frame + +It uses https://developer.apple.com/documentation/arkit/arframe/2875718-hittest with some default options. Please file an issue or send a PR if you need more control over the options here! + +##### Attach to a given 3D object + +You can attach the cursor on a 3D object, e.g. a non-horizontal-plane or similar: + +Given there is some 3D object on your scene with `id="my-nodeId"` + +``` + +``` + +Like with planes, you can select the node with a function. + +E.gl you have several "walls" with ids "wall_1", "wall_2", etc. + +``` + results.find(r => r.id.startsWith('wall_')), + }} +/> +``` + + +It uses https://developer.apple.com/documentation/scenekit/scnscenerenderer/1522929-hittest with some default options. Please file an issue or send a PR if you need more control over the options here! + + +## FAQ: + +#### Which permissions does this use? + +- **camera access** (see section iOS Project configuration above). The user is asked for permission, as soon as you mount an `` component or use any of its API. If user denies access, you will get an error in `onARKitError` +- **location service**: only needed if you use `ARKit.ARWorldAlignment.GravityAndHeading`. + +#### Is there an Android / ARCore version? + +Not yet, but there has been a proof-of-concept: https://github.com/HippoAR/react-native-arkit/issues/14. We are looking for contributors to help backporting this proof-of-conept to react-native-arkit. + +#### I have another question... + +[**Join Slack!**](https://join.slack.com/t/react-native-ar/shared_invite/enQtMjUzMzg3MjM0MTQ5LWU3Nzg2YjI4MGRjMTM1ZDBlNmIwYTE4YmM0M2U0NmY2YjBiYzQ4YzlkODExMTA0NDkwMzFhYWY4ZDE2M2Q4NGY) + + ## Contributing -If you find a bug or would like to request a new feature, just [open an issue](https://github.com/HippoAR/react-native-arkit/issues/new). Your contributions are always welcome! Submit a pull request and see `contribution.md` for guidelines. +If you find a bug or would like to request a new feature, just [open an issue](https://github.com/HippoAR/react-native-arkit/issues/new). Your contributions are always welcome! Submit a pull request and see [`CONTRIBUTING.md`](CONTRIBUTING.md) for guidelines. diff --git a/components/ARBox.js b/components/ARBox.js new file mode 100644 index 00000000..b53350ce --- /dev/null +++ b/components/ARBox.js @@ -0,0 +1,23 @@ +// +// ARBox.js +// +// Created by HippoAR on 8/12/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +import PropTypes from 'prop-types'; + +import { material } from './lib/propTypes'; +import createArComponent from './lib/createArComponent'; + +const ARBox = createArComponent('addBox', { + shape: PropTypes.shape({ + width: PropTypes.number, + height: PropTypes.number, + length: PropTypes.number, + chamfer: PropTypes.number, + }), + material, +}); + +export default ARBox; diff --git a/components/ARCapsule.js b/components/ARCapsule.js new file mode 100644 index 00000000..b74df957 --- /dev/null +++ b/components/ARCapsule.js @@ -0,0 +1,21 @@ +// +// ARCapsule.js +// +// Created by HippoAR on 8/12/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +import PropTypes from 'prop-types'; + +import { material } from './lib/propTypes'; +import createArComponent from './lib/createArComponent'; + +const ARCapsule = createArComponent('addCapsule', { + shape: PropTypes.shape({ + capR: PropTypes.number, + height: PropTypes.number, + }), + material, +}); + +export default ARCapsule; diff --git a/components/ARCone.js b/components/ARCone.js new file mode 100644 index 00000000..01eef2b0 --- /dev/null +++ b/components/ARCone.js @@ -0,0 +1,22 @@ +// +// ARCone.js +// +// Created by HippoAR on 8/12/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +import PropTypes from 'prop-types'; + +import { material } from './lib/propTypes'; +import createArComponent from './lib/createArComponent'; + +const ARCone = createArComponent('addCone', { + shape: PropTypes.shape({ + topR: PropTypes.number, + bottomR: PropTypes.number, + height: PropTypes.number, + }), + material, +}); + +export default ARCone; diff --git a/components/ARCylinder.js b/components/ARCylinder.js new file mode 100644 index 00000000..ccbd1887 --- /dev/null +++ b/components/ARCylinder.js @@ -0,0 +1,21 @@ +// +// ARCylinder.js +// +// Created by HippoAR on 8/12/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +import PropTypes from 'prop-types'; + +import { material } from './lib/propTypes'; +import createArComponent from './lib/createArComponent'; + +const ARCylinder = createArComponent('addCylinder', { + shape: PropTypes.shape({ + radius: PropTypes.number, + height: PropTypes.number, + }), + material, +}); + +export default ARCylinder; diff --git a/components/ARGroup.js b/components/ARGroup.js new file mode 100644 index 00000000..87fbba16 --- /dev/null +++ b/components/ARGroup.js @@ -0,0 +1,12 @@ +// +// ARBox.js +// +// Created by HippoAR on 8/12/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +import createArComponent from './lib/createArComponent'; + +const ARBox = createArComponent('addGroup', {}); + +export default ARBox; diff --git a/components/ARLight.js b/components/ARLight.js new file mode 100644 index 00000000..0328e0e9 --- /dev/null +++ b/components/ARLight.js @@ -0,0 +1,28 @@ +import PropTypes from 'prop-types'; + +import { categoryBitMask, color, lightType, shadowMode } from './lib/propTypes'; +import createArComponent from './lib/createArComponent'; + +const ARLight = createArComponent('addLight', { + type: lightType, + color, + temperature: PropTypes.number, + intensity: PropTypes.number, + attenuationStartDistance: PropTypes.number, + attenuationEndDistance: PropTypes.number, + spotInnerAngle: PropTypes.number, + spotOuterAngle: PropTypes.number, + castsShadow: PropTypes.bool, + shadowRadius: PropTypes.number, + shadowColor: color, + // shadowMapSize: PropTypes.number, + shadowSampleCount: PropTypes.number, + shadowMode, + shadowBias: PropTypes.number, + orthographicScale: PropTypes.number, + zFar: PropTypes.number, + zNear: PropTypes.number, + lightCategoryBitMask: categoryBitMask, +}); + +export default ARLight; diff --git a/components/ARModel.js b/components/ARModel.js new file mode 100644 index 00000000..2103cc74 --- /dev/null +++ b/components/ARModel.js @@ -0,0 +1,31 @@ +// +// ARModel.js +// +// Created by HippoAR on 8/12/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +import PropTypes from 'prop-types'; + +import { NativeModules } from 'react-native'; + +import { material } from './lib/propTypes'; +import createArComponent from './lib/createArComponent'; + +const ARModel = createArComponent( + { + mount: NativeModules.ARModelManager.mount, + }, + { + model: PropTypes.shape({ + file: PropTypes.string, + node: PropTypes.string, + scale: PropTypes.number, + alpha: PropTypes.number, + }), + material, + }, + ['model'], +); + +export default ARModel; diff --git a/components/ARPlane.js b/components/ARPlane.js new file mode 100644 index 00000000..10cbe90e --- /dev/null +++ b/components/ARPlane.js @@ -0,0 +1,25 @@ +// +// ARPlane.js +// +// Created by HippoAR on 8/12/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +import PropTypes from 'prop-types'; + +import { material } from './lib/propTypes'; +import createArComponent from './lib/createArComponent'; + +const ARPlane = createArComponent('addPlane', { + shape: PropTypes.shape({ + width: PropTypes.number, + height: PropTypes.number, + cornerRadius: PropTypes.number, + cornerSegmentCount: PropTypes.number, + widthSegmentCount: PropTypes.number, + heightSegmentCount: PropTypes.number, + }), + material, +}); + +export default ARPlane; diff --git a/components/ARPyramid.js b/components/ARPyramid.js new file mode 100644 index 00000000..90f47acc --- /dev/null +++ b/components/ARPyramid.js @@ -0,0 +1,22 @@ +// +// ARPyramid.js +// +// Created by HippoAR on 8/12/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +import PropTypes from 'prop-types'; + +import { material } from './lib/propTypes'; +import createArComponent from './lib/createArComponent'; + +const ARPyramid = createArComponent('addPyramid', { + shape: PropTypes.shape({ + width: PropTypes.number, + length: PropTypes.number, + height: PropTypes.number, + }), + material, +}); + +export default ARPyramid; diff --git a/components/ARShape.js b/components/ARShape.js new file mode 100644 index 00000000..91e951f9 --- /dev/null +++ b/components/ARShape.js @@ -0,0 +1,19 @@ +import PropTypes from 'prop-types'; + +import { chamferMode, material } from './lib/propTypes'; +import createArComponent from './lib/createArComponent'; + +const ARShape = createArComponent('addShape', { + shape: PropTypes.shape({ + extrusion: PropTypes.number, + pathSvg: PropTypes.string, + pathFlatness: PropTypes.number, + chamferMode, + chamferRadius: PropTypes.number, + chamferProfilePathSvg: PropTypes.string, + chamferProfilePathFlatness: PropTypes.number, + }), + material, +}); + +export default ARShape; diff --git a/components/ARSphere.js b/components/ARSphere.js new file mode 100644 index 00000000..31e31269 --- /dev/null +++ b/components/ARSphere.js @@ -0,0 +1,20 @@ +// +// ARSphere.js +// +// Created by HippoAR on 8/12/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +import PropTypes from 'prop-types'; + +import { material } from './lib/propTypes'; +import createArComponent from './lib/createArComponent'; + +const ARSphere = createArComponent('addSphere', { + shape: PropTypes.shape({ + radius: PropTypes.number, + }), + material, +}); + +export default ARSphere; diff --git a/components/ARSprite.js b/components/ARSprite.js new file mode 100644 index 00000000..03e1029a --- /dev/null +++ b/components/ARSprite.js @@ -0,0 +1,72 @@ +import { + requireNativeComponent, + NativeModules, + findNodeHandle, +} from 'react-native'; +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; + +import { position, transition } from './lib/propTypes'; + +const { ARKitSpriteViewManager } = NativeModules; + +const DEFAULT_TRANSITION_DURATION = 0.05; +const ARSprite = class extends Component { + startAnimation() { + if (findNodeHandle(this.nativeRef)) { + ARKitSpriteViewManager.startAnimation(findNodeHandle(this.nativeRef)); + } + } + stopAnimation() { + if (findNodeHandle(this.nativeRef)) { + ARKitSpriteViewManager.stopAnimation(findNodeHandle(this.nativeRef)); + } + } + componentDidUpdate(oldProps) { + if (oldProps.disablePositionUpdate !== this.props.disablePositionUpdate) + this.handleAnimation(); + } + componentWillUnmount() { + this.stopAnimation(); + } + handleAnimation() { + if (this.props.disablePositionUpdate) { + this.stopAnimation(); + } else { + this.startAnimation(); + } + } + render() { + const { position, transition, ...props } = this.props; + + return ( + { + if (ref && !this.nativeRef) { + this.nativeRef = ref; + this.handleAnimation(); + } + }} + {...props} + position3D={position} + transitionDuration={ + transition ? transition.duration : DEFAULT_TRANSITION_DURATION + } + style={{ position: 'absolute', ...this.props.style }} + /> + ); + } +}; + +const RCTARKitARSprite = requireNativeComponent('RCTARKitSpriteView', null); +RCTARKitARSprite.propTypes = { + position3D: position, + transitionDuration: PropTypes.number, +}; +ARSprite.propTypes = { + position, + transition, + disablePositionUpdate: PropTypes.bool, +}; + +export default ARSprite; diff --git a/components/ARText.js b/components/ARText.js new file mode 100644 index 00000000..207d0ab8 --- /dev/null +++ b/components/ARText.js @@ -0,0 +1,31 @@ +// +// ARText.js +// +// Created by HippoAR on 8/12/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +import PropTypes from 'prop-types'; + +import { NativeModules } from 'react-native'; + +import { material } from './lib/propTypes'; +import createArComponent from './lib/createArComponent'; + +const ARText = createArComponent( + { mount: NativeModules.ARTextManager.mount, pick: ['id', 'text', 'font'] }, + { + text: PropTypes.string, + font: PropTypes.shape({ + name: PropTypes.string, + // weight: PropTypes.string, + size: PropTypes.number, + depth: PropTypes.number, + chamfer: PropTypes.number + }), + material + }, + ['text', 'font'] +); + +export default ARText; diff --git a/components/ARTorus.js b/components/ARTorus.js new file mode 100644 index 00000000..07c926eb --- /dev/null +++ b/components/ARTorus.js @@ -0,0 +1,21 @@ +// +// ARTorus.js +// +// Created by HippoAR on 8/12/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +import PropTypes from 'prop-types'; + +import { material } from './lib/propTypes'; +import createArComponent from './lib/createArComponent'; + +const ARTorus = createArComponent('addTorus', { + shape: PropTypes.shape({ + ringR: PropTypes.number, + pipeR: PropTypes.number, + }), + material, +}); + +export default ARTorus; diff --git a/components/ARTube.js b/components/ARTube.js new file mode 100644 index 00000000..ba203bb4 --- /dev/null +++ b/components/ARTube.js @@ -0,0 +1,22 @@ +// +// ARTube.js +// +// Created by HippoAR on 8/12/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +import PropTypes from 'prop-types'; + +import { material } from './lib/propTypes'; +import createArComponent from './lib/createArComponent'; + +const ARTube = createArComponent('addTube', { + shape: PropTypes.shape({ + innerR: PropTypes.number, + outerR: PropTypes.number, + height: PropTypes.number, + }), + material, +}); + +export default ARTube; diff --git a/components/lib/addAnimatedSupport.js b/components/lib/addAnimatedSupport.js new file mode 100644 index 00000000..2dbf4c65 --- /dev/null +++ b/components/lib/addAnimatedSupport.js @@ -0,0 +1,44 @@ +import { Animated } from 'react-native'; +import React from 'react'; +import get from 'lodash/get'; +import unset from 'lodash/unset'; + +export default ARComponent => { + // there is a strange issue: animatedvalues can't be child objects, + // so we have to flatten them + // we would need to have an Animated.ValueXYZ + const ANIMATEABLE3D = ['position', 'eulerAngles']; + + const ARComponentAnimatedInner = Animated.createAnimatedComponent( + class extends React.PureComponent { + render() { + // unflatten + const unflattened = {}; + const props = { ...this.props }; + ANIMATEABLE3D.forEach(key => { + unflattened[key] = { + x: get(props, `_flattened_${key}_x`), + y: get(props, `_flattened_${key}_y`), + z: get(props, `_flattened_${key}_z`), + }; + unset(props, `_flattened_${key}_x`); + unset(props, `_flattened_${key}_y`); + unset(props, `_flattened_${key}_z`); + }); + return ; + } + }, + ); + + return props => { + const flattenedProps = {}; + + ANIMATEABLE3D.forEach(key => { + flattenedProps[`_flattened_${key}_x`] = get(props, [key, 'x']); + flattenedProps[`_flattened_${key}_y`] = get(props, [key, 'y']); + flattenedProps[`_flattened_${key}_z`] = get(props, [key, 'z']); + }); + + return ; + }; +}; diff --git a/components/lib/createArComponent.js b/components/lib/createArComponent.js new file mode 100644 index 00000000..21552962 --- /dev/null +++ b/components/lib/createArComponent.js @@ -0,0 +1,256 @@ +import { NativeModules, processColor } from 'react-native'; +import PropTypes from 'prop-types'; +import React, { PureComponent, Fragment } from 'react'; +import filter from 'lodash/filter'; +import isDeepEqual from 'fast-deep-equal'; +import isEmpty from 'lodash/isEmpty'; +import keys from 'lodash/keys'; +import pick from 'lodash/pick'; +import some from 'lodash/some'; + +import { + castsShadow, + categoryBitMask, + eulerAngles, + opacity, + orientation, + position, + renderingOrder, + rotation, + scale, + constraint, + transition, +} from './propTypes'; +import addAnimatedSupport from './addAnimatedSupport'; +import generateId from './generateId'; +import processMaterial from './processMaterial'; + +const DEBUG = false; + +const { ARGeosManager } = NativeModules; + +const PROP_TYPES_IMMUTABLE = { + id: PropTypes.string, + frame: PropTypes.string, +}; +const MOUNT_UNMOUNT_ANIMATION_PROPS = { + propsOnMount: PropTypes.any, + propsOnUnMount: PropTypes.any, +}; +const PROP_TYPES_NODE = { + position, + transition, + orientation, + eulerAngles, + rotation, + scale, + categoryBitMask, + castsShadow, + renderingOrder, + opacity, + constraint, +}; + +const NODE_PROPS = keys(PROP_TYPES_NODE); +const IMMUTABLE_PROPS = keys(PROP_TYPES_IMMUTABLE); + +const TIMERS = {}; + +/** +mountConfig, +propTypes, +nonUpdateablePropKeys: if a prop key is in this list, +the property will be updated on scenekit, instead of beeing remounted. + +this excludes at the moment: model, font, text, (???) +* */ +export default (mountConfig, propTypes = {}, nonUpdateablePropKeys = []) => { + const allPropTypes = { + ...MOUNT_UNMOUNT_ANIMATION_PROPS, + ...PROP_TYPES_IMMUTABLE, + ...PROP_TYPES_NODE, + ...propTypes, + }; + // any custom props (material, shape, ...) + const nonNodePropKeys = keys(propTypes); + + const parseMaterials = props => ({ + ...props, + ...(props.shadowColor + ? { shadowColor: processColor(props.shadowColor) } + : {}), + ...(props.color ? { color: processColor(props.color) } : {}), + ...(props.material ? { material: processMaterial(props.material) } : {}), + }); + + const getNonNodeProps = props => parseMaterials(pick(props, nonNodePropKeys)); + + const mountFunc = + typeof mountConfig === 'string' + ? ARGeosManager[mountConfig] + : mountConfig.mount; + + const mount = (id, props, parentId) => { + if (DEBUG) console.log(`[${id}] [${new Date().getTime()}] mount`, props); + return mountFunc( + getNonNodeProps(props), + { + id, + ...pick(props, NODE_PROPS), + }, + props.frame, + parentId, + ); + }; + + const update = (id, props) => { + if (DEBUG) console.log(`[${id}] [${new Date().getTime()}] update`, props); + return ARGeosManager.updateNode(id, props); + }; + + const unmount = id => { + if (DEBUG) console.log(`[${id}] [${new Date().getTime()}] unmount`); + return ARGeosManager.unmount(id); + }; + + const ARComponent = class extends PureComponent { + constructor(props) { + super(props); + this.identifier = props.id || generateId(); + } + identifier = null; + componentDidMount() { + const { propsOnMount, ...props } = this.props; + if (propsOnMount) { + const fullPropsOnMount = { ...props, ...propsOnMount }; + const { + transition: transitionOnMount = { duration: 0 }, + } = fullPropsOnMount; + + this.doPendingTimers(); + this.mountWithProps(fullPropsOnMount).then(() => { + this.props = propsOnMount; + this.componentWillUpdate({ ...props, transition: transitionOnMount }); + }); + } else { + this.mountWithProps(props); + } + } + + async mountWithProps(props) { + return mount(this.identifier, props, this.context.arkitParentId); + } + + componentWillUpdate(props) { + const changedKeys = filter( + keys(this.props), + key => key !== 'children' && !isDeepEqual(props[key], this.props[key]), + ); + if (isEmpty(changedKeys)) { + return; + } + + if (__DEV__) { + const nonAllowedUpdates = filter(changedKeys, k => + IMMUTABLE_PROPS.includes(k), + ); + if (nonAllowedUpdates.length > 0) { + throw new Error( + `[${this + .identifier}] prop can't be updated: '${nonAllowedUpdates.join( + ', ', + )}'`, + ); + } + } + + if (some(changedKeys, k => nonUpdateablePropKeys.includes(k))) { + if (DEBUG) + console.log( + `[${this.identifier}] need to remount node because of `, + changedKeys, + ); + this.mountWithProps({ ...this.props, ...props }); + } else { + // every property is updateable + // send only these changed property to the native part + + const propsToupdate = { + // always inclue transition + transition: { + ...this.props.transition, + ...props.transition, + }, + ...parseMaterials(pick(props, changedKeys)), + }; + update(this.identifier, propsToupdate).catch(() => { + // sometimes calls are out of order, so this node has been unmounted + // we therefore mount again + + this.mountWithProps({ ...this.props, ...props }); + }); + } + } + + componentWillUnmount() { + const { propsOnUnmount, ...props } = this.props; + if (propsOnUnmount) { + const fullProps = { ...props, ...propsOnUnmount }; + const { transition: { duration = 0 } = {} } = fullProps; + + this.componentWillUpdate(fullProps); + this.delayed(() => { + unmount(this.identifier); + }, duration * 1000); + } else { + this.doPendingTimers(); + unmount(this.identifier); + } + } + /** + do something delayed, but keep order of events per id + * */ + delayed(callback, duration) { + this.doPendingTimers(); + TIMERS[this.identifier] = { + handle: global.setTimeout(() => { + callback.call(this); + delete TIMERS[this.identifier]; + }, duration), + callback, + }; + } + + doPendingTimers() { + if (TIMERS[this.identifier]) { + // timer is running, do it now, otherwise we might change order + // e.g. it could be that an unmount happens after a remount + global.clearTimeout(TIMERS[this.identifier].handle); + TIMERS[this.identifier].callback.call(this); + delete TIMERS[this.identifier]; + } + } + getChildContext() { + return { + arkitParentId: this.identifier, + }; + } + + render() { + return this.props.children ? ( + {this.props.children} + ) : null; + } + }; + ARComponent.childContextTypes = { + arkitParentId: PropTypes.string, + }; + ARComponent.contextTypes = { + arkitParentId: PropTypes.string, + }; + + const ARComponentAnimated = addAnimatedSupport(ARComponent); + ARComponentAnimated.propTypes = allPropTypes; + + return ARComponentAnimated; +}; diff --git a/components/lib/generateId.js b/components/lib/generateId.js new file mode 100644 index 00000000..50956e66 --- /dev/null +++ b/components/lib/generateId.js @@ -0,0 +1,71 @@ +// Unique id generator + +const digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + +function toSex(num, base) { + /* eslint no-param-reassign:0 */ + if (base) { + num = parseInt(num, base); + } + let [integer, decimal] = num.toString().split('.'); + + integer = parseInt(integer, 10); + const sex = []; + do { + sex.push(digits[integer % 60]); + integer = Math.floor(integer / 60); + } while (integer > 0); + + let result = sex.reverse().join(''); + + if (decimal) { + decimal = parseFloat(`.${decimal}`); + const rem = []; + let precision = 4; + do { + decimal *= 60; + const x = Math.floor(decimal); + rem.push(digits[x]); + decimal -= x; + precision--; + } while (precision); + + result += `.${rem.join('')}`; + } + + return result; +} + +function toDigits(str, n) { + if (str.length === n) { + return str; + } + + if (str.length > n) { + return str.substr(0, n); + } + + do { + str = `0${str}`; + } while (str.length < n); + + return str; +} + +let random = Math.floor(Math.random() * 60 * 60 * 60 * 60); + +// export default function() { +// const firstEightDigits = Math.random().toString(36).substr(2, 8); +// const secondEightDigits = Math.random().toString(36).substr(2, 8); +// return `${firstEightDigits}${secondEightDigits}`; +// } + +export default function(idfv) { + random++; + const time = toSex(Math.floor(Date.now() / 1000)); + const device = idfv ? toSex(idfv.split('-')[4], 16) : ''; + return `${toDigits(time, 6)}${device && toDigits(device, 10)}${toDigits( + toSex(random), + 4, + )}`; +} diff --git a/components/lib/processMaterial.js b/components/lib/processMaterial.js new file mode 100644 index 00000000..0790c7dd --- /dev/null +++ b/components/lib/processMaterial.js @@ -0,0 +1,27 @@ +import { processColor } from 'react-native'; +import { isObject, isString, mapValues, set } from 'lodash'; + +// https://developer.apple.com/documentation/scenekit/scnmaterial +const propsWithMaps = ['normal', 'diffuse', 'displacement', 'specular']; + +export default function processMaterial(materialOrg) { + const material = { ...materialOrg }; + // previously it was possible to set { material: { color:'colorstring'}}... translate this to { material: { diffuse: { color: 'colorstring'}}} + if (material.color) { + set(material, 'diffuse.color', material.color); + } + + return mapValues( + material, + (prop, key) => + propsWithMaps.includes(key) + ? { + ...(isObject(prop) ? prop : {}), + color: processColor( + // allow for setting a diffuse colorstring { diffuse: 'colorstring'} + key === 'diffuse' && isString(prop) ? prop : prop.color, + ), + } + : prop, + ); +} diff --git a/components/lib/propTypes.js b/components/lib/propTypes.js new file mode 100644 index 00000000..b16127de --- /dev/null +++ b/components/lib/propTypes.js @@ -0,0 +1,152 @@ +import { NativeModules } from 'react-native'; +import { values } from 'lodash'; +import PropTypes from 'prop-types'; + +const ARKitManager = NativeModules.ARKitManager; + +const animatableNumber = PropTypes.oneOfType([ + PropTypes.number, + PropTypes.object, +]); + +export const deprecated = (propType, hint = null) => ( + props, + propName, + componentName, +) => { + if (props[propName]) { + console.warn( + `Prop \`${propName}\` supplied to` + + ` \`${componentName}\` is deprecated. ${hint}`, + ); + } + return PropTypes.checkPropTypes( + { [propName]: propType }, + props, + propName, + componentName, + ); +}; +export const position = PropTypes.shape({ + x: animatableNumber, + y: animatableNumber, + z: animatableNumber, +}); + +export const scale = animatableNumber; +export const categoryBitMask = PropTypes.number; +export const transition = PropTypes.shape({ + duration: PropTypes.number, +}); + +export const planeDetection = PropTypes.oneOf( + values(ARKitManager.ARPlaneDetection), +); +export const eulerAngles = PropTypes.shape({ + x: animatableNumber, + y: animatableNumber, + z: animatableNumber, +}); + +export const rotation = PropTypes.shape({ + x: animatableNumber, + y: animatableNumber, + z: animatableNumber, + w: animatableNumber, +}); + +export const orientation = PropTypes.shape({ + x: animatableNumber, + y: animatableNumber, + z: animatableNumber, + w: animatableNumber, +}); + +export const textureTranslation = PropTypes.shape({ + x: PropTypes.number, + y: PropTypes.number, + z: PropTypes.number, +}); + +export const textureRotation = PropTypes.shape({ + angle: PropTypes.number, + x: PropTypes.number, + y: PropTypes.number, + z: PropTypes.number, +}); + +export const textureScale = PropTypes.shape({ + x: PropTypes.number, + y: PropTypes.number, + z: PropTypes.number, +}); + +export const shaders = PropTypes.shape({ + [ARKitManager.ShaderModifierEntryPoint.Geometry]: PropTypes.string, + [ARKitManager.ShaderModifierEntryPoint.Surface]: PropTypes.string, + [ARKitManager.ShaderModifierEntryPoint.LightingModel]: PropTypes.string, + [ARKitManager.ShaderModifierEntryPoint.Fragment]: PropTypes.string, +}); + +export const lightingModel = PropTypes.oneOf( + values(ARKitManager.LightingModel), +); + +export const castsShadow = PropTypes.bool; +export const renderingOrder = PropTypes.number; +export const blendMode = PropTypes.oneOf(values(ARKitManager.BlendMode)); +export const transparencyMode = PropTypes.oneOf( + values(ARKitManager.TransparencyMode), +); +export const chamferMode = PropTypes.oneOf(values(ARKitManager.ChamferMode)); +export const color = PropTypes.string; +export const fillMode = PropTypes.oneOf(values(ARKitManager.FillMode)); + +export const lightType = PropTypes.oneOf(values(ARKitManager.LightType)); +export const shadowMode = PropTypes.oneOf(values(ARKitManager.ShadowMode)); +export const colorBufferWriteMask = PropTypes.oneOf( + values(ARKitManager.ColorMask), +); + +export const opacity = animatableNumber; + +export const constraint = PropTypes.oneOf(values(ARKitManager.Constraint)); + +export const wrapMode = PropTypes.oneOf(values(ARKitManager.WrapMode)); + +export const materialProperty = PropTypes.shape({ + path: PropTypes.string, + color: PropTypes.string, + intensity: PropTypes.number, + wrapS: wrapMode, + wrapT: wrapMode, + wrap: wrapMode, + translation: textureTranslation, + scale: textureScale, + rotation: textureRotation, +}); + +export const material = PropTypes.shape({ + color, + normal: materialProperty, + specular: materialProperty, + displacement: materialProperty, + diffuse: PropTypes.oneOfType([PropTypes.string, materialProperty]), + metalness: PropTypes.number, + roughness: PropTypes.number, + blendMode, + transparencyMode, + lightingModel, + shaders, + writesToDepthBuffer: PropTypes.bool, + colorBufferWriteMask, + doubleSided: PropTypes.bool, + litPerPixel: PropTypes.bool, + transparency: PropTypes.number, + fillMode, +}); + +const detectionImage = PropTypes.shape({ + resourceGroupName: PropTypes.string, +}); +export const detectionImages = PropTypes.arrayOf(detectionImage); diff --git a/examples/animation.md b/examples/animation.md new file mode 100644 index 00000000..a500204c --- /dev/null +++ b/examples/animation.md @@ -0,0 +1,6 @@ +# DRAFT Animations + +- react-native Animated library +- propsOnMount / propsOnUnmount +- scenekit transitions +- animated models diff --git a/examples/measure-distance.md b/examples/measure-distance.md new file mode 100644 index 00000000..c97ce57c --- /dev/null +++ b/examples/measure-distance.md @@ -0,0 +1,6 @@ +# DRAFT Measure distance + +- geometry, pythagoras, vectors +- hitTestPlanes with features +- hitTestPlanes with planes +- hitTestSceneObjects diff --git a/examples/models.md b/examples/models.md new file mode 100644 index 00000000..5f3d36db --- /dev/null +++ b/examples/models.md @@ -0,0 +1,4 @@ +# DRAFT loading models + +- loading from network +- convertion with xcode diff --git a/hocs/withProjectedPosition.js b/hocs/withProjectedPosition.js new file mode 100644 index 00000000..cc7938c3 --- /dev/null +++ b/hocs/withProjectedPosition.js @@ -0,0 +1,105 @@ +import React, { Component } from 'react'; +import withAnimationFrame from '@panter/react-animation-frame'; +import { round, isFunction } from 'lodash'; +import { NativeModules } from 'react-native'; + +const ARKitManager = NativeModules.ARKitManager; + +const rountPosition = ({ x, y, z }, precision) => ({ + x: round(x, precision), + y: round(y, precision), + z: round(z, precision), +}); + +export default ({ throttleMs = 33, overwritePosition = {} } = {}) => C => + withAnimationFrame( + class extends Component { + projectionRunning = true; + _isMounted = false; + constructor(props) { + super(props); + this.state = { + positionProjected: props.position || { x: 0, y: 0, z: 0 }, + projectionResult: null, + }; + this.handleAnimation(props); + } + componentWillMount() { + this._isMounted = true; + } + handleAnimation({ projectionEnabled }) { + if (projectionEnabled) { + if (!this.projectionRunning) { + this.projectionRunning = true; + this.props.startAnimation(); + } + } else if (this.projectionRunning) { + this.projectionRunning = false; + this.props.endAnimation(); + } + } + componentWillUpdate({ projectionEnabled = true }) { + this.handleAnimation({ projectionEnabled }); + } + + componentWillUnmount() { + this._isMounted = false; + } + + onResult(result) { + if (this._isMounted) { + if (result) { + this.setState({ + positionProjected: rountPosition(result.position, 3), + projectionResult: result, + }); + } else { + this.setState({ + positionProjected: null, + projectionResult: null, + }); + } + if (this.props.onProjectedPosition) { + this.props.onProjectedPosition(result); + } + } + } + + onAnimationFrame() { + const { x, y, plane, node } = this.props.projectPosition || {}; + + if (plane) { + ARKitManager.hitTestPlanes( + { x, y }, + ARKitManager.ARHitTestResultType.ExistingPlane, + ).then(({ results }) => { + const result = isFunction(plane) + ? plane(results) + : results.find(r => r.id === plane); + this.onResult(result); + }); + } else if (node) { + ARKitManager.hitTestSceneObjects({ x, y }).then(({ results }) => { + const result = isFunction(node) + ? node(results) + : results.find(r => r.id === node); + this.onResult(result); + }); + } + } + + render() { + return ( + + ); + } + }, + throttleMs, + ); diff --git a/index.js b/index.js index a7188e15..0bdb4b4d 100644 --- a/index.js +++ b/index.js @@ -5,43 +5,63 @@ // Copyright © 2017 HippoAR. All rights reserved. // -import PropTypes from 'prop-types'; -import React from 'react'; -import { NativeModules, requireNativeComponent } from 'react-native'; +import ARBox from './components/ARBox'; +import ARCapsule from './components/ARCapsule'; +import ARCone from './components/ARCone'; +import ARCylinder from './components/ARCylinder'; +import ARGroup from './components/ARGroup'; +import ARKit from './ARKit'; +import ARLight from './components/ARLight'; +import ARModel from './components/ARModel'; +import ARPlane from './components/ARPlane'; +import ARPyramid from './components/ARPyramid'; +import ARShape from './components/ARShape'; +import ARSphere from './components/ARSphere'; +import ARSprite from './components/ARSprite'; +import ARText from './components/ARText'; +import ARTorus from './components/ARTorus'; +import ARTube from './components/ARTube'; +import DeviceMotion from './DeviceMotion'; +import startup from './startup'; +import withProjectedPosition from './hocs/withProjectedPosition'; -const ARKitManager = NativeModules.ARKitManager; +import * as colorUtils from './lib/colorUtils'; -class ARKit extends React.Component { - render() { - return ; - } +ARKit.Box = ARBox; +ARKit.Sphere = ARSphere; +ARKit.Cylinder = ARCylinder; +ARKit.Cone = ARCone; +ARKit.Pyramid = ARPyramid; +ARKit.Tube = ARTube; +ARKit.Torus = ARTorus; +ARKit.Capsule = ARCapsule; +ARKit.Plane = ARPlane; +ARKit.Text = ARText; +ARKit.Model = ARModel; +ARKit.Sprite = ARSprite; +ARKit.Group = ARGroup; +ARKit.Shape = ARShape; +ARKit.Light = ARLight; - getCameraPosition() { - return ARKitManager.getCameraPosition(); - } +startup(); - callback(name) { - return event => { - if (!this.props[name]) { - return; - } - this.props[name](event.nativeEvent); - }; - } -} - -ARKit.propTypes = { - debug: PropTypes.bool, - planeDetection: PropTypes.bool, - lightEstimation: PropTypes.bool, - onPlaneDetected: PropTypes.func, - onPlaneUpdate: PropTypes.func, +export { + colorUtils, + ARKit, + DeviceMotion, + ARBox, + ARSphere, + ARSprite, + ARCylinder, + ARCone, + ARPyramid, + ARTube, + ARTorus, + ARCapsule, + ARPlane, + ARText, + ARModel, + ARLight, + ARGroup, + withProjectedPosition, }; - -const RCTARKit = requireNativeComponent('RCTARKit', ARKit); - -module.exports = ARKit; diff --git a/ios/DeviceMotion.h b/ios/DeviceMotion.h new file mode 100644 index 00000000..8951ecd6 --- /dev/null +++ b/ios/DeviceMotion.h @@ -0,0 +1,23 @@ +// +// DeviceMotion.h +// RCTARKit +// +// Created by Zehao Li on 7/29/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +#import +#import +#import + +@interface DeviceMotion : RCTEventEmitter + +@property (nonatomic) CMMotionManager *motionManager; + +- (void)setUpdateInterval:(double) interval; +- (void)getUpdateInterval:(RCTResponseSenderBlock) cb; +- (void)getData:(RCTResponseSenderBlock) cb; +- (void)startUpdates; +- (void)stopUpdates; + +@end diff --git a/ios/DeviceMotion.m b/ios/DeviceMotion.m new file mode 100644 index 00000000..4b49b398 --- /dev/null +++ b/ios/DeviceMotion.m @@ -0,0 +1,103 @@ +// +// DeviceMotion.m +// RCTARKit +// +// Created by Zehao Li on 7/29/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +#import +#import +#import "DeviceMotion.h" + +@implementation DeviceMotion + +@synthesize bridge = _bridge; + +RCT_EXPORT_MODULE(); + +- (id)init { + self = [super init]; + return self; +} + + ++ (BOOL)requiresMainQueueSetup +{ + return YES; +} + +- (CMMotionManager *)motionManager { + if (_motionManager) { + return _motionManager; + } + + _motionManager = [[CMMotionManager alloc] init]; + if (![_motionManager isDeviceMotionAvailable]) { + NSLog(@"DeviceMotion not Available!"); + return _motionManager; + } + + NSLog(@"DeviceMotion available"); + if ([_motionManager isDeviceMotionActive] == NO) { + NSLog(@"DeviceMotion active"); + } else { + NSLog(@"DeviceMotion not active"); + } + return _motionManager; +} + +- (NSArray *)supportedEvents { + return @[@"MotionData"]; +} + +RCT_EXPORT_METHOD(setUpdateInterval:(double) interval) { + NSLog(@"setDeviceMotionUpdateInterval: %f", interval); + [self.motionManager setDeviceMotionUpdateInterval:interval]; +} + +RCT_EXPORT_METHOD(getUpdateInterval:(RCTResponseSenderBlock) cb) { + double interval = self.motionManager.deviceMotionUpdateInterval; + NSLog(@"getDeviceMotionUpdateInterval: %f", interval); + cb(@[[NSNull null], [NSNumber numberWithDouble:interval]]); +} + +RCT_EXPORT_METHOD(getData:(RCTResponseSenderBlock) cb) { + CMAcceleration gravity = self.motionManager.deviceMotion.gravity; + CMRotationMatrix m = self.motionManager.deviceMotion.attitude.rotationMatrix; + + cb(@[[NSNull null], @{ + @"gravity": @{ @"x" : @(gravity.x), @"y" : @(gravity.y), @"z" : @(gravity.z) }, + @"rotationMatrix": @{ + @"m11" : @(m.m11), @"m12" : @(m.m12), @"m13" : @(m.m13), + @"m21" : @(m.m21), @"m22" : @(m.m22), @"m23" : @(m.m23), + @"m31" : @(m.m31), @"m32" : @(m.m32), @"m33" : @(m.m33) }, + }]); +} + +RCT_EXPORT_METHOD(startUpdates) { + NSLog(@"startDeviceMotionUpdates"); + [self.motionManager startDeviceMotionUpdates]; + + /* Receive the DeviceMotion data on this block */ + [self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] + withHandler:^(CMDeviceMotion *deviceMotion, NSError *error) { + CMAcceleration gravity = deviceMotion.gravity; + CMRotationMatrix m = deviceMotion.attitude.rotationMatrix; + [self sendEventWithName:@"MotionData" + body:@{ + @"gravity": @{ @"x" : @(gravity.x), @"y" : @(gravity.y), @"z" : @(gravity.z) }, + @"rotationMatrix": @{ + @"m11" : @(m.m11), @"m12" : @(m.m12), @"m13" : @(m.m13), + @"m21" : @(m.m21), @"m22" : @(m.m22), @"m23" : @(m.m23), + @"m31" : @(m.m31), @"m32" : @(m.m32), @"m33" : @(m.m33) }, + }]; + }]; +} + +RCT_EXPORT_METHOD(stopUpdates) { + NSLog(@"stopDeviceMotionUpdates"); + [self.motionManager stopDeviceMotionUpdates]; +} + +@end diff --git a/ios/Plane.h b/ios/Plane.h deleted file mode 100644 index 5fff9353..00000000 --- a/ios/Plane.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Plane.h -// RCTARKit -// -// Created by Zehao Li on 7/10/17. -// Copyright © 2017 Facebook. All rights reserved. -// - -#import -#import - -@interface Plane : SCNNode -- (instancetype)initWithAnchor:(ARPlaneAnchor *)anchor isHidden:(BOOL)hidden; -- (void)update:(ARPlaneAnchor *)anchor; -- (void)setTextureScale; -- (void)hide; -@property (nonatomic, retain) ARPlaneAnchor *anchor; -@property (nonatomic, retain) SCNBox *planeGeometry; -@end diff --git a/ios/Plane.m b/ios/Plane.m deleted file mode 100644 index b072dc92..00000000 --- a/ios/Plane.m +++ /dev/null @@ -1,81 +0,0 @@ -// -// Plane.m -// RCTARKit -// -// Created by Zehao Li on 7/10/17. -// Copyright © 2017 Facebook. All rights reserved. -// - -#import "Plane.h" - -@implementation Plane - -- (instancetype)initWithAnchor:(ARPlaneAnchor *)anchor isHidden:(BOOL)hidden { - self = [super init]; - - self.anchor = anchor; - float width = anchor.extent.x; - float length = anchor.extent.z; - - float planeHeight = 0.01; - - self.planeGeometry = [SCNBox boxWithWidth:width height:planeHeight length:length chamferRadius:0]; - - SCNMaterial *material = [SCNMaterial new]; - UIImage *img = [UIImage imageNamed:@"tron_grid.png"]; - - material.diffuse.contents = img; - - SCNMaterial *transparentMaterial = [SCNMaterial new]; - transparentMaterial.diffuse.contents = [UIColor colorWithWhite:1.0 alpha:0.0]; - - if (hidden) { - self.planeGeometry.materials = @[transparentMaterial, transparentMaterial, transparentMaterial, transparentMaterial, transparentMaterial, transparentMaterial]; - } else { - self.planeGeometry.materials = @[transparentMaterial, transparentMaterial, transparentMaterial, transparentMaterial, material, transparentMaterial]; - } - - SCNNode *planeNode = [SCNNode nodeWithGeometry:self.planeGeometry]; - - planeNode.position = SCNVector3Make(0, -planeHeight / 2, 0); - - planeNode.physicsBody = [SCNPhysicsBody - bodyWithType:SCNPhysicsBodyTypeKinematic - shape: [SCNPhysicsShape shapeWithGeometry:self.planeGeometry options:nil]]; - - [self setTextureScale]; - [self addChildNode:planeNode]; - return self; -} - -- (void)update:(ARPlaneAnchor *)anchor { - self.planeGeometry.width = anchor.extent.x; - self.planeGeometry.length = anchor.extent.z; - - self.position = SCNVector3Make(anchor.center.x, 0, anchor.center.z); - - SCNNode *node = [self.childNodes firstObject]; - //self.physicsBody = nil; - node.physicsBody = [SCNPhysicsBody - bodyWithType:SCNPhysicsBodyTypeKinematic - shape: [SCNPhysicsShape shapeWithGeometry:self.planeGeometry options:nil]]; - [self setTextureScale]; -} - -- (void)setTextureScale { - CGFloat width = self.planeGeometry.width; - CGFloat height = self.planeGeometry.length; - - SCNMaterial *material = self.planeGeometry.materials[4]; - material.diffuse.contentsTransform = SCNMatrix4MakeScale(width, height, 1); - material.diffuse.wrapS = SCNWrapModeRepeat; - material.diffuse.wrapT = SCNWrapModeRepeat; -} - -- (void)hide { - SCNMaterial *transparentMaterial = [SCNMaterial new]; - transparentMaterial.diffuse.contents = [UIColor colorWithWhite:1.0 alpha:0.0]; - self.planeGeometry.materials = @[transparentMaterial, transparentMaterial, transparentMaterial, transparentMaterial, transparentMaterial, transparentMaterial]; -} - -@end diff --git a/ios/PocketSVG b/ios/PocketSVG new file mode 160000 index 00000000..5405d0f5 --- /dev/null +++ b/ios/PocketSVG @@ -0,0 +1 @@ +Subproject commit 5405d0f5838c1242e79b490c6ddf738779e4d5c0 diff --git a/ios/RCTARKit.h b/ios/RCTARKit.h index 6479df94..de5c7819 100644 --- a/ios/RCTARKit.h +++ b/ios/RCTARKit.h @@ -10,21 +10,98 @@ #import #import -#import +#import "RCTARKitDelegate.h" +#import "RCTARKitNodes.h" -@interface RCTARKit : ARSCNView +typedef void (^RCTBubblingEventBlock)(NSDictionary *body); +typedef void (^RCTARKitResolve)(id result); +typedef void (^RCTARKitReject)(NSString *code, NSString *message, NSError *error); + + +@interface RCTARKit : UIView + ++ (instancetype)sharedInstance; ++ (bool)isInitialized; +- (instancetype)initWithARView:(ARSCNView *)arView; + + +@property (nonatomic, strong) NSMutableArray> *touchDelegates; +@property (nonatomic, strong) NSMutableArray> *rendererDelegates; +@property (nonatomic, strong) NSMutableArray> *sessionDelegates; + + +#pragma mark - Properties +@property (nonatomic, strong) ARSCNView *arView; +@property (nonatomic, strong) RCTARKitNodes *nodeManager; @property (nonatomic, assign) BOOL debug; -@property (nonatomic, assign) BOOL planeDetection; -@property (nonatomic, assign) BOOL lightEstimation; -@property (nonatomic, readonly) NSDictionary *cameraPosition; +@property (nonatomic, assign) ARPlaneDetection planeDetection; +@property (nonatomic, assign) BOOL lightEstimationEnabled; +@property (nonatomic, assign) BOOL autoenablesDefaultLighting; +@property (nonatomic, assign) NSDictionary* origin; +@property (nonatomic, assign) ARWorldAlignment worldAlignment; +@property (nonatomic, assign) NSArray* detectionImages; @property (nonatomic, copy) RCTBubblingEventBlock onPlaneDetected; -@property (nonatomic, copy) RCTBubblingEventBlock onPlaneUpdate; +@property (nonatomic, copy) RCTBubblingEventBlock onPlaneRemoved; +@property (nonatomic, copy) RCTBubblingEventBlock onPlaneUpdated; -@property NSMutableDictionary *planes; -@property NSMutableArray *boxes; +@property (nonatomic, copy) RCTBubblingEventBlock onAnchorDetected; +@property (nonatomic, copy) RCTBubblingEventBlock onAnchorRemoved; +@property (nonatomic, copy) RCTBubblingEventBlock onAnchorUpdated; -+ (instancetype)sharedInstance; + +@property (nonatomic, copy) RCTBubblingEventBlock onFeaturesDetected; +@property (nonatomic, copy) RCTBubblingEventBlock onLightEstimation; + +@property (nonatomic, copy) RCTBubblingEventBlock onTrackingState; +@property (nonatomic, copy) RCTBubblingEventBlock onTapOnPlaneUsingExtent; +@property (nonatomic, copy) RCTBubblingEventBlock onTapOnPlaneNoExtent; +@property (nonatomic, copy) RCTBubblingEventBlock onEvent; +@property (nonatomic, copy) RCTBubblingEventBlock onARKitError; + + +@property NSMutableDictionary *planes; // plane detected + + + +#pragma mark - Public Method +- (void)pause; +- (void)resume; +- (void)reset; +- (void)hitTestPlane:(CGPoint)tapPoint types:(ARHitTestResultType)types resolve:(RCTARKitResolve)resolve reject:(RCTARKitReject)reject; +- (void)hitTestSceneObjects:(CGPoint)tapPoint resolve:(RCTARKitResolve) resolve reject:(RCTARKitReject)reject; +- (SCNVector3)projectPoint:(SCNVector3)point; +- (float)getCameraDistanceToPoint:(SCNVector3)point; +- (UIImage *)getSnapshot:(NSDictionary*)selection; +- (UIImage *)getSnapshotCamera:(NSDictionary*)selection; +- (void)focusScene; +- (void)clearScene; +- (NSDictionary *)readCameraPosition; +- (NSDictionary *)readCamera; +- (NSDictionary* )getCurrentLightEstimation; +- (NSArray * )getCurrentDetectedFeaturePoints; +- (bool)isMounted; +- (void)addRendererDelegates:(id)delegate; +- (void)removeRendererDelegates:(id)delegate; + +#pragma mark - Delegates +- (void)renderer:(id )renderer didRenderScene:(SCNScene *)scene atTime:(NSTimeInterval)time; +- (void)renderer:(id )renderer didAddNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor; +- (void)renderer:(id )renderer willUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor; +- (void)renderer:(id )renderer didUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor; +- (void)renderer:(id )renderer didRemoveNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor; + +- (void)session:(ARSession *)session didUpdateFrame:(ARFrame *)frame; +- (void)session:(ARSession *)session cameraDidChangeTrackingState:(ARCamera *)camera; @end + + + +#if __has_include("RCTARKitARCL.h") +#import "RCTARKitARCL.h" +@compatibility_alias ARKit RCTARKitARCL; +#else +@compatibility_alias ARKit RCTARKit; +#endif diff --git a/ios/RCTARKit.m b/ios/RCTARKit.m index 8a4e5be1..36105fe1 100644 --- a/ios/RCTARKit.m +++ b/ios/RCTARKit.m @@ -7,188 +7,740 @@ // #import "RCTARKit.h" -#import "Plane.h" +#import "RCTConvert+ARKit.h" -@interface RCTARKit () +@import CoreLocation; -@property (nonatomic, strong) ARWorldTrackingSessionConfiguration *configuration; +@interface RCTARKit () { + RCTARKitResolve _resolve; +} + +@property (nonatomic, strong) ARSession* session; +@property (nonatomic, strong) ARWorldTrackingConfiguration *configuration; @end +void dispatch_once_on_main_thread(dispatch_once_t *predicate, + dispatch_block_t block) { + if ([NSThread isMainThread]) { + dispatch_once(predicate, block); + } else { + if (DISPATCH_EXPECT(*predicate == 0L, NO)) { + dispatch_sync(dispatch_get_main_queue(), ^{ + dispatch_once(predicate, block); + }); + } + } +} + + @implementation RCTARKit +static RCTARKit *instance = nil; + ++ (bool)isInitialized { + return instance !=nil; +} + (instancetype)sharedInstance { - static RCTARKit *arView = nil; + static dispatch_once_t onceToken; - - dispatch_once(&onceToken, ^{ - if (arView == nil) { - arView = [[self alloc] init]; + + dispatch_once_on_main_thread(&onceToken, ^{ + if (instance == nil) { + ARSCNView *arView = [[ARSCNView alloc] init]; + instance = [[self alloc] initWithARView:arView]; } }); + + return instance; +} - return arView; +- (bool)isMounted { + + return self.superview != nil; } -- (instancetype)init { +- (instancetype)initWithARView:(ARSCNView *)arView { if ((self = [super init])) { - self.delegate = self; - [self.session runWithConfiguration:self.configuration]; + self.arView = arView; + + // delegates + arView.delegate = self; + arView.session.delegate = self; + + UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapFrom:)]; + tapGestureRecognizer.numberOfTapsRequired = 1; + [self.arView addGestureRecognizer:tapGestureRecognizer]; + + self.touchDelegates = [NSMutableArray array]; + self.rendererDelegates = [NSMutableArray array]; + self.sessionDelegates = [NSMutableArray array]; + + // nodeManager + self.nodeManager = [RCTARKitNodes sharedInstance]; + self.nodeManager.arView = arView; + [self.sessionDelegates addObject:self.nodeManager]; + + // configuration(s) + arView.autoenablesDefaultLighting = YES; + arView.scene.rootNode.name = @"root"; + + #if TARGET_IPHONE_SIMULATOR + // allow for basic orbit gestures if we're running in the simulator + arView.allowsCameraControl = YES; + arView.defaultCameraController.interactionMode = SCNInteractionModeOrbitTurntable; + arView.defaultCameraController.maximumVerticalAngle = 45; + arView.defaultCameraController.inertiaEnabled = YES; + [arView.defaultCameraController translateInCameraSpaceByX:(float) 0.0 Y:(float) 0.0 Z:(float) 3.0]; + + #endif + // start ARKit + [self addSubview:arView]; + [self resume]; + } + return self; +} + + + - self.autoenablesDefaultLighting = YES; - self.scene = [SCNScene new]; +- (void)layoutSubviews { + [super layoutSubviews]; + //NSLog(@"setting view bounds %@", NSStringFromCGRect(self.bounds)); + self.arView.frame = self.bounds; +} + +- (void)pause { + [self.session pause]; +} + +- (void)resume { + [self.session runWithConfiguration:self.configuration]; +} - self.planes = [NSMutableDictionary new]; +- (void)session:(ARSession *)session didFailWithError:(NSError *)error { + if(self.onARKitError) { + self.onARKitError(RCTJSErrorFromNSError(error)); + } else { + NSLog(@"Initializing ARKIT failed with Error: %@ %@", error, [error userInfo]); + + } + +} +- (void)reset { + if (ARWorldTrackingConfiguration.isSupported) { + [self.session runWithConfiguration:self.configuration options:ARSessionRunOptionRemoveExistingAnchors | ARSessionRunOptionResetTracking]; } - return self; +} + +- (void)focusScene { + [self.nodeManager.localOrigin setPosition:self.nodeManager.cameraOrigin.position]; + [self.nodeManager.localOrigin setRotation:self.nodeManager.cameraOrigin.rotation]; +} + +- (void)clearScene { + [self.nodeManager clear]; } #pragma mark - setter-getter +- (ARSession*)session { + return self.arView.session; +} + - (BOOL)debug { - return self.showsStatistics; + return self.arView.showsStatistics; } - (void)setDebug:(BOOL)debug { if (debug) { - self.showsStatistics = YES; - self.debugOptions = ARSCNDebugOptionShowWorldOrigin | ARSCNDebugOptionShowFeaturePoints; + self.arView.showsStatistics = YES; + self.arView.debugOptions = ARSCNDebugOptionShowWorldOrigin | ARSCNDebugOptionShowFeaturePoints; } else { - self.showsStatistics = NO; - self.debugOptions = SCNDebugOptionNone; + self.arView.showsStatistics = NO; + self.arView.debugOptions = SCNDebugOptionNone; } } -- (BOOL)planeDetection { - return self.configuration.planeDetection == ARPlaneDetectionHorizontal; +- (ARPlaneDetection)planeDetection { + ARWorldTrackingConfiguration *configuration = (ARWorldTrackingConfiguration *) self.configuration; + return configuration.planeDetection; } -- (void)setPlaneDetection:(BOOL)planeDetection { - if (planeDetection) { - NSLog(@"detect plane"); - self.configuration.planeDetection = ARPlaneDetectionHorizontal; +- (void)setPlaneDetection:(ARPlaneDetection)planeDetection { + ARWorldTrackingConfiguration *configuration = (ARWorldTrackingConfiguration *) self.configuration; + + configuration.planeDetection = planeDetection; + [self resume]; +} + +-(NSDictionary*)origin { + return @{ + @"position": vectorToJson(self.nodeManager.localOrigin.position) + }; +} + +-(void)setOrigin:(NSDictionary*)json { + + if(json[@"transition"]) { + NSDictionary * transition =json[@"transition"]; + if(transition[@"duration"]) { + [SCNTransaction setAnimationDuration:[transition[@"duration"] floatValue]]; + } else { + [SCNTransaction setAnimationDuration:0.0]; + } + } else { - NSLog(@"do not detect plane"); - self.configuration.planeDetection = ARPlaneDetectionNone; + [SCNTransaction setAnimationDuration:0.0]; } + SCNVector3 position = [RCTConvert SCNVector3:json[@"position"]]; + [self.nodeManager.localOrigin setPosition:position]; +} - [self.session runWithConfiguration:self.configuration]; +- (BOOL)lightEstimationEnabled { + ARConfiguration *configuration = self.configuration; + return configuration.lightEstimationEnabled; +} + + +- (void)setLightEstimationEnabled:(BOOL)lightEstimationEnabled { + ARConfiguration *configuration = self.configuration; + configuration.lightEstimationEnabled = lightEstimationEnabled; + [self resume]; +} +- (void)setAutoenablesDefaultLighting:(BOOL)autoenablesDefaultLighting { + self.arView.autoenablesDefaultLighting = autoenablesDefaultLighting; } -- (BOOL)lightEstimation { - return self.configuration.lightEstimationEnabled; +- (BOOL)autoenablesDefaultLighting { + return self.arView.autoenablesDefaultLighting; } -- (void)setLightEstimation:(BOOL)lightEstimation { - self.configuration.lightEstimationEnabled = lightEstimation; - [self.session runWithConfiguration:self.configuration]; +- (ARWorldAlignment)worldAlignment { + ARConfiguration *configuration = self.configuration; + return configuration.worldAlignment; +} + +- (void)setWorldAlignment:(ARWorldAlignment)worldAlignment { + ARConfiguration *configuration = self.configuration; + if (worldAlignment == ARWorldAlignmentGravityAndHeading) { + configuration.worldAlignment = ARWorldAlignmentGravityAndHeading; + } else if (worldAlignment == ARWorldAlignmentCamera) { + configuration.worldAlignment = ARWorldAlignmentCamera; + } else { + configuration.worldAlignment = ARWorldAlignmentGravity; + } + [self resume]; +} + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110300 +- (void)setDetectionImages:(NSArray*) detectionImages { + + if (@available(iOS 11.3, *)) { + ARWorldTrackingConfiguration *configuration = self.configuration; + NSSet *detectionImagesSet = [[NSSet alloc] init]; + for (id config in detectionImages) { + if(config[@"resourceGroupName"]) { + // TODO: allow bundle to be defined + detectionImagesSet = [detectionImagesSet setByAddingObjectsFromSet:[ARReferenceImage referenceImagesInGroupNamed:config[@"resourceGroupName"] bundle:nil]]; + } + } + configuration.detectionImages = detectionImagesSet; + [self resume];; + } +} +#endif +- (NSDictionary *)readCameraPosition { + // deprecated + SCNVector3 cameraPosition = self.nodeManager.cameraOrigin.position; + return vectorToJson(cameraPosition); } -- (NSDictionary *)cameraPosition { - simd_float4 position = self.session.currentFrame.camera.transform.columns[3]; +static NSDictionary * vectorToJson(const SCNVector3 v) { + return @{ @"x": @(v.x), @"y": @(v.y), @"z": @(v.z) }; +} +static NSDictionary * vector_float3ToJson(const simd_float3 v) { + return @{ @"x": @(v.x), @"y": @(v.y), @"z": @(v.z) }; +} +static NSDictionary * vector4ToJson(const SCNVector4 v) { + return @{ @"x": @(v.x), @"y": @(v.y), @"z": @(v.z), @"w": @(v.w) }; +} + + +- (NSDictionary *)readCamera { + SCNVector3 position = self.nodeManager.cameraOrigin.position; + SCNVector4 rotation = self.nodeManager.cameraOrigin.rotation; + SCNVector4 orientation = self.nodeManager.cameraOrigin.orientation; + SCNVector3 eulerAngles = self.nodeManager.cameraOrigin.eulerAngles; + SCNVector3 direction = self.nodeManager.cameraDirection; return @{ - @"x": [NSNumber numberWithFloat:position.x], - @"y": [NSNumber numberWithFloat:position.y], - @"z": [NSNumber numberWithFloat:position.z] + @"position":vectorToJson(position), + @"rotation":vector4ToJson(rotation), + @"orientation":vector4ToJson(orientation), + @"eulerAngles":vectorToJson(eulerAngles), + @"direction":vectorToJson(direction), }; } +- (SCNVector3)projectPoint:(SCNVector3)point { + return [self.arView projectPoint:[self.nodeManager getAbsolutePositionToOrigin:point]]; + +} + + + +- (float)getCameraDistanceToPoint:(SCNVector3)point { + return [self.nodeManager getCameraDistanceToPoint:point]; + +} + + #pragma mark - Lazy loads --(ARWorldTrackingSessionConfiguration *)configuration { +-(ARWorldTrackingConfiguration *)configuration { if (_configuration) { return _configuration; } - -// if (!ARWorldTrackingSessionConfiguration.isSupported) { -// } - - _configuration = [ARWorldTrackingSessionConfiguration new]; + + if (!ARWorldTrackingConfiguration.isSupported) {} + + _configuration = [ARWorldTrackingConfiguration new]; _configuration.planeDetection = ARPlaneDetectionHorizontal; return _configuration; } -#pragma mark - methods + +#pragma mark - snapshot methods + +- (void)hitTestSceneObjects:(const CGPoint)tapPoint resolve:(RCTARKitResolve)resolve reject:(RCTARKitReject)reject { + + resolve([self.nodeManager getSceneObjectsHitResult:tapPoint]); +} + + +- (UIImage *)getSnapshot:(NSDictionary *)selection { + UIImage *image = [self.arView snapshot]; + + + return [self cropImage:image toSelection:selection]; + +} + + + + + +- (UIImage *)getSnapshotCamera:(NSDictionary *)selection { + CVPixelBufferRef pixelBuffer = self.arView.session.currentFrame.capturedImage; + CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer]; + + CIContext *temporaryContext = [CIContext contextWithOptions:nil]; + CGImageRef videoImage = [temporaryContext + createCGImage:ciImage + fromRect:CGRectMake(0, 0, + CVPixelBufferGetWidth(pixelBuffer), + CVPixelBufferGetHeight(pixelBuffer))]; + + UIImage *image = [UIImage imageWithCGImage:videoImage scale: 1.0 orientation:UIImageOrientationRight]; + CGImageRelease(videoImage); + + UIImage *cropped = [self cropImage:image toSelection:selection]; + return cropped; + +} + + + +- (UIImage *)cropImage:(UIImage *)imageToCrop toRect:(CGRect)rect +{ + //CGRect CropRect = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height+15); + + CGImageRef imageRef = CGImageCreateWithImageInRect([imageToCrop CGImage], rect); + UIImage *cropped = [UIImage imageWithCGImage:imageRef]; + CGImageRelease(imageRef); + + return cropped; +} + +static inline double radians (double degrees) {return degrees * M_PI/180;} +UIImage* rotate(UIImage* src, UIImageOrientation orientation) +{ + UIGraphicsBeginImageContext(src.size); + + CGContextRef context = UIGraphicsGetCurrentContext(); + [src drawAtPoint:CGPointMake(0, 0)]; + if (orientation == UIImageOrientationRight) { + CGContextRotateCTM (context, radians(90)); + } else if (orientation == UIImageOrientationLeft) { + CGContextRotateCTM (context, radians(-90)); + } else if (orientation == UIImageOrientationDown) { + // NOTHING + } else if (orientation == UIImageOrientationUp) { + CGContextRotateCTM (context, radians(90)); + } + + + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return image; +} +- (UIImage *)cropImage:(UIImage *)imageToCrop toSelection:(NSDictionary *)selection +{ + + // selection is in view-coordinate system + // where as the image is a camera picture with arbitary size + // also, the camera picture is cut of so that it "covers" the self.bounds + // if selection is nil, crop to the viewport + + UIImage * image = rotate(imageToCrop, imageToCrop.imageOrientation); + + float arViewWidth = self.bounds.size.width; + float arViewHeight = self.bounds.size.height; + float imageWidth = image.size.width; + float imageHeight = image.size.height; + + float arViewRatio = arViewHeight/arViewWidth; + float imageRatio = imageHeight/imageWidth; + float imageToArWidth = imageWidth/arViewWidth; + float imageToArHeight = imageHeight/arViewHeight; + + float finalHeight; + float finalWidth; + + + if (arViewRatio > imageRatio) + { + finalHeight = arViewHeight*imageToArHeight; + finalWidth = arViewHeight*imageToArHeight /arViewRatio; + } + else + { + finalWidth = arViewWidth*imageToArWidth; + finalHeight = arViewWidth * imageToArWidth * arViewRatio; + } + + float topOffset = (image.size.height - finalHeight)/2; + float leftOffset = (image.size.width - finalWidth)/2; + + + float x = leftOffset; + float y = topOffset; + float width = finalWidth; + float height = finalHeight; + if(selection && selection != [NSNull null]) { + x = leftOffset+ [selection[@"x"] floatValue]*imageToArWidth; + y = topOffset+[selection[@"y"] floatValue]*imageToArHeight; + width = [selection[@"width"] floatValue]*imageToArWidth; + height = [selection[@"height"] floatValue]*imageToArHeight; + } + CGRect rect = CGRectMake(x, y, width, height); + + UIImage *cropped = [self cropImage:image toRect:rect]; + return cropped; +} + + +#pragma mark - plane hit detection + +- (void)hitTestPlane:(const CGPoint)tapPoint types:(ARHitTestResultType)types resolve:(RCTARKitResolve)resolve reject:(RCTARKitReject)reject { + + resolve([self getPlaneHitResult:tapPoint types:types]); +} + + + +static NSDictionary * getPlaneHitResult(NSMutableArray *resultsMapped, const CGPoint tapPoint) { + return @{ + @"results": resultsMapped + }; +} + + +- (NSDictionary *)getPlaneHitResult:(const CGPoint)tapPoint types:(ARHitTestResultType)types; { + NSArray *results = [self.arView hitTest:tapPoint types:types]; + NSMutableArray * resultsMapped = [self.nodeManager mapHitResults:results]; + NSDictionary *planeHitResult = getPlaneHitResult(resultsMapped, tapPoint); + return planeHitResult; +} + +- (void)handleTapFrom: (UITapGestureRecognizer *)recognizer { + // Take the screen space tap coordinates and pass them to the hitTest method on the ARSCNView instance + CGPoint tapPoint = [recognizer locationInView:self.arView]; + // + if(self.onTapOnPlaneUsingExtent) { + // Take the screen space tap coordinates and pass them to the hitTest method on the ARSCNView instance + NSDictionary * planeHitResult = [self getPlaneHitResult:tapPoint types:ARHitTestResultTypeExistingPlaneUsingExtent]; + self.onTapOnPlaneUsingExtent(planeHitResult); + } + + if(self.onTapOnPlaneNoExtent) { + // Take the screen space tap coordinates and pass them to the hitTest method on the ARSCNView instance + NSDictionary * planeHitResult = [self getPlaneHitResult:tapPoint types:ARHitTestResultTypeExistingPlane]; + self.onTapOnPlaneNoExtent(planeHitResult); + } +} + + #pragma mark - ARSCNViewDelegate -/** - Called when a new node has been added. - */ -- (void)renderer:(id )renderer didAddNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor { - if (![anchor isKindOfClass:[ARPlaneAnchor class]]) { - return; +- (void)renderer:(id)renderer updateAtTime:(NSTimeInterval)time { + for (id rendererDelegate in self.rendererDelegates) { + if ([rendererDelegate respondsToSelector:@selector(renderer:updateAtTime:)]) { + [rendererDelegate renderer:renderer updateAtTime:time]; + } } +} + - ARPlaneAnchor *planeAnchor = (ARPlaneAnchor *)anchor; - if (self.onPlaneDetected) { - self.onPlaneDetected(@{ - @"id": planeAnchor.identifier.UUIDString, - @"alignment": @(planeAnchor.alignment), - @"center": @{ @"x": @(planeAnchor.center.x), @"y": @(planeAnchor.center.y), @"z": @(planeAnchor.center.z) }, - @"extent": @{ @"x": @(planeAnchor.extent.x), @"y": @(planeAnchor.extent.y), @"z": @(planeAnchor.extent.z) } - }); +- (void)renderer:(id )renderer didRenderScene:(SCNScene *)scene atTime:(NSTimeInterval)time { + for (id rendererDelegate in self.rendererDelegates) { + if ([rendererDelegate respondsToSelector:@selector(renderer:didRenderScene:atTime:)]) { + [rendererDelegate renderer:renderer didRenderScene:scene atTime:time]; + } } +} + - Plane *plane = [[Plane alloc] initWithAnchor: (ARPlaneAnchor *)anchor isHidden: NO]; - [self.planes setObject:plane forKey:anchor.identifier]; - [node addChildNode:plane]; + + +- (NSDictionary *)makeAnchorDetectionResult:(SCNNode *)node anchor:(ARAnchor *)anchor { + NSDictionary* baseProps = @{ + @"id": anchor.identifier.UUIDString, + @"type": @"unkown", + @"eulerAngles":vectorToJson(node.eulerAngles), + @"position": vectorToJson([self.nodeManager getRelativePositionToOrigin:node.position]), + @"positionAbsolute": vectorToJson(node.position) + }; + NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithDictionary:baseProps]; + + if([anchor isKindOfClass:[ARPlaneAnchor class]]) { + ARPlaneAnchor *planeAnchor = (ARPlaneAnchor *)anchor; + NSDictionary * planeProperties = [self makePlaneAnchorProperties:planeAnchor]; + [dict addEntriesFromDictionary:planeProperties]; + } else if (@available(iOS 11.3, *)) { + #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110300 + if([anchor isKindOfClass:[ARImageAnchor class]]) { + ARImageAnchor *imageAnchor = (ARImageAnchor *)anchor; + NSDictionary * imageProperties = [self makeImageAnchorProperties:imageAnchor]; + [dict addEntriesFromDictionary:imageProperties]; + } + #endif + } else { + // Fallback on earlier versions + } + return dict; } -/** - Called when a node will be updated. - */ + +- (NSDictionary *)makePlaneAnchorProperties:(ARPlaneAnchor *)planeAnchor { + return @{ + @"type": @"plane", + @"alignment": @(planeAnchor.alignment), + @"center": vector_float3ToJson(planeAnchor.center), + @"extent": vector_float3ToJson(planeAnchor.extent) + }; + +} + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110300 +- (NSDictionary *)makeImageAnchorProperties:(ARImageAnchor *)imageAnchor API_AVAILABLE(ios(11.3)){ + return @{ + @"type": @"image", + @"image": @{ + @"name": imageAnchor.referenceImage.name + } + + }; + +} + #endif + +- (void)addRendererDelegates:(id) delegate { + [self.rendererDelegates addObject:delegate]; + NSLog(@"added, number of renderer delegates %d", [self.rendererDelegates count]); +} + +- (void)removeRendererDelegates:(id) delegate { + [self.rendererDelegates removeObject:delegate]; + NSLog(@"removed, number of renderer delegates %d", [self.rendererDelegates count]); +} - (void)renderer:(id )renderer willUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor { } -/** - Called when a node has been updated. - */ -- (void)renderer:(id )renderer didUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor { - ARPlaneAnchor *planeAnchor = (ARPlaneAnchor *)anchor; - if (self.onPlaneUpdate) { - self.onPlaneUpdate(@{ - @"id": planeAnchor.identifier.UUIDString, - @"alignment": @(planeAnchor.alignment), - @"center": @{ @"x": @(planeAnchor.center.x), @"y": @(planeAnchor.center.y), @"z": @(planeAnchor.center.z) }, - @"extent": @{ @"x": @(planeAnchor.extent.x), @"y": @(planeAnchor.extent.y), @"z": @(planeAnchor.extent.z) } - }); +- (void)renderer:(id )renderer didAddNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor { + + NSDictionary *anchorDict = [self makeAnchorDetectionResult:node anchor:anchor]; + + if (self.onPlaneDetected && [anchor isKindOfClass:[ARPlaneAnchor class]]) { + self.onPlaneDetected(anchorDict); + } else if (self.onAnchorDetected) { + self.onAnchorDetected(anchorDict); } + +} - Plane *plane = [self.planes objectForKey:anchor.identifier]; - if (plane == nil) { - return; +- (void)renderer:(id )renderer didUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor { + NSDictionary *anchorDict = [self makeAnchorDetectionResult:node anchor:anchor]; + + if (self.onPlaneUpdated && [anchor isKindOfClass:[ARPlaneAnchor class]]) { + self.onPlaneUpdated(anchorDict); + }else if (self.onAnchorUpdated) { + self.onAnchorUpdated(anchorDict); } + +} - [plane update:(ARPlaneAnchor *)anchor]; +- (void)renderer:(id)renderer didRemoveNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor { + NSDictionary *anchorDict = [self makeAnchorDetectionResult:node anchor:anchor]; + + if (self.onPlaneRemoved && [anchor isKindOfClass:[ARPlaneAnchor class]]) { + self.onPlaneRemoved(anchorDict); + } else if (self.onAnchorRemoved) { + self.onAnchorRemoved(anchorDict); + } } -/** - Called when a mapped node has been removed. - */ -- (void)renderer:(id )renderer didRemoveNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor { - [self.planes removeObjectForKey:anchor.identifier]; + + + +#pragma mark - ARSessionDelegate + +- (ARFrame * _Nullable)currentFrame { + return self.arView.session.currentFrame; } -#pragma mark - session +- (NSDictionary *)getCurrentLightEstimation { + return [self wrapLightEstimation:[self currentFrame].lightEstimate]; +} + +- (NSMutableArray *)getCurrentDetectedFeaturePoints { + NSMutableArray * featurePoints = [NSMutableArray array]; + for (int i = 0; i < [self currentFrame].rawFeaturePoints.count; i++) { + vector_float3 positionV = [self currentFrame].rawFeaturePoints.points[i]; + SCNVector3 position = [self.nodeManager getRelativePositionToOrigin:SCNVector3Make(positionV[0],positionV[1],positionV[2])]; + NSString * pointId = [NSString stringWithFormat:@"featurepoint_%lld",[self currentFrame].rawFeaturePoints.identifiers[i]]; + + [featurePoints addObject:@{ + @"x": @(position.x), + @"y": @(position.y), + @"z": @(position.z), + @"id":pointId, + }]; + + } + return featurePoints; +} - (void)session:(ARSession *)session didUpdateFrame:(ARFrame *)frame { - // simd_float4 position = frame.camera.transform.columns[3]; + for (id sessionDelegate in self.sessionDelegates) { + if ([sessionDelegate respondsToSelector:@selector(session:didUpdateFrame:)]) { + [sessionDelegate session:session didUpdateFrame:frame]; + } + } + if (self.onFeaturesDetected) { + NSArray * featurePoints = [self getCurrentDetectedFeaturePoints]; + dispatch_async(dispatch_get_main_queue(), ^{ + + + if(self.onFeaturesDetected) { + self.onFeaturesDetected(@{ + @"featurePoints":featurePoints + }); + } + }); + } + + if (self.lightEstimationEnabled && self.onLightEstimation) { + /** this is called rapidly and is therefore demanding, better poll it from outside with getCurrentLightEstimation **/ + + + + dispatch_async(dispatch_get_main_queue(), ^{ + if(self.onLightEstimation) { + NSDictionary *estimate = [self getCurrentLightEstimation]; + self.onLightEstimation(estimate); + } + }); + + } + } -- (void)session:(ARSession *)session didFailWithError:(NSError *)error { +- (NSDictionary *)wrapLightEstimation:(ARLightEstimate *)estimate { + if(!estimate) { + return nil; + } + return @{ + @"ambientColorTemperature":@(estimate.ambientColorTemperature), + @"ambientIntensity":@(estimate.ambientIntensity), + }; +} + + + +- (void)session:(ARSession *)session cameraDidChangeTrackingState:(ARCamera *)camera { + if (self.onTrackingState) { + dispatch_async(dispatch_get_main_queue(), ^{ + self.onTrackingState(@{ + @"state": @(camera.trackingState), + @"reason": @(camera.trackingStateReason) + }); + }); + } } -- (void)sessionWasInterrupted:(ARSession *)session { + + +#pragma mark - RCTARKitTouchDelegate + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + [super touchesBegan:touches withEvent:event]; + for (id touchDelegate in self.touchDelegates) { + if ([touchDelegate respondsToSelector:@selector(touches:beganWithEvent:)]) { + [touchDelegate touches:touches beganWithEvent:event]; + } + } } -- (void)sessionInterruptionEnded:(ARSession *)session { +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + [super touchesMoved:touches withEvent:event]; + for (id touchDelegate in self.touchDelegates) { + if ([touchDelegate respondsToSelector:@selector(touches:movedWithEvent:)]) { + [touchDelegate touches:touches movedWithEvent:event]; + } + } +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + [super touchesEnded:touches withEvent:event]; + for (id touchDelegate in self.touchDelegates) { + if ([touchDelegate respondsToSelector:@selector(touches:endedWithEvent:)]) { + [touchDelegate touches:touches endedWithEvent:event]; + } + } +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { + [super touchesCancelled:touches withEvent:event]; + for (id touchDelegate in self.touchDelegates) { + if ([touchDelegate respondsToSelector:@selector(touches:cancelledWithEvent:)]) { + [touchDelegate touches:touches cancelledWithEvent:event]; + } + } +} + + + +#pragma mark - dealloc +-(void) dealloc { } @end diff --git a/ios/RCTARKit.xcodeproj/project.pbxproj b/ios/RCTARKit.xcodeproj/project.pbxproj index 29b10b9b..78c04cc0 100644 --- a/ios/RCTARKit.xcodeproj/project.pbxproj +++ b/ios/RCTARKit.xcodeproj/project.pbxproj @@ -8,7 +8,16 @@ /* Begin PBXBuildFile section */ 10062C831F127572009AE974 /* RCTARKitManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10062C821F127572009AE974 /* RCTARKitManager.m */; }; - 10E553291F1391350059B7EC /* Plane.m in Sources */ = {isa = PBXBuildFile; fileRef = 10E553281F1391350059B7EC /* Plane.m */; }; + 1021FE1C1F3EDB98000E7339 /* ARModelManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1021FE181F3EDB90000E7339 /* ARModelManager.m */; }; + 1021FE1D1F3EDB9B000E7339 /* ARTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1021FE191F3EDB90000E7339 /* ARTextManager.m */; }; + 105F124E1F7C0719006D4BA3 /* RCTConvert+ARKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 105F124D1F7C0718006D4BA3 /* RCTConvert+ARKit.m */; }; + 10DCBC4B1F7CE836008C89E7 /* ARGeosManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10DCBC4A1F7CE836008C89E7 /* ARGeosManager.m */; }; + 10ED47A71F38BC01004DF043 /* DeviceMotion.m in Sources */ = {isa = PBXBuildFile; fileRef = 10ED47A61F38BC00004DF043 /* DeviceMotion.m */; }; + 10FEF6141F774C9000EC21AE /* RCTARKitIO.m in Sources */ = {isa = PBXBuildFile; fileRef = 10FEF6101F774C8F00EC21AE /* RCTARKitIO.m */; }; + 10FEF6151F774C9000EC21AE /* RCTARKitNodes.m in Sources */ = {isa = PBXBuildFile; fileRef = 10FEF6121F774C9000EC21AE /* RCTARKitNodes.m */; }; + B1990B221FCEEBD60001AE2F /* color-grabber.m in Sources */ = {isa = PBXBuildFile; fileRef = B1990B211FCEEBD60001AE2F /* color-grabber.m */; }; + B1CEA29F204C14090025C1B8 /* RCTARKitSpriteView.m in Sources */ = {isa = PBXBuildFile; fileRef = B1CEA29E204C14090025C1B8 /* RCTARKitSpriteView.m */; }; + B1CEA2A3204C160D0025C1B8 /* RCTARKitSpriteViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B1CEA2A2204C160D0025C1B8 /* RCTARKitSpriteViewManager.m */; }; B3E7B58A1CC2AC0600A0062D /* RCTARKit.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RCTARKit.m */; }; /* End PBXBuildFile section */ @@ -22,14 +31,44 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + B14C363A1F9504D70047CB67 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 10062C811F127566009AE974 /* RCTARKitManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTARKitManager.h; sourceTree = ""; }; 10062C821F127572009AE974 /* RCTARKitManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTARKitManager.m; sourceTree = ""; }; - 10E553271F1391350059B7EC /* Plane.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Plane.h; sourceTree = ""; }; - 10E553281F1391350059B7EC /* Plane.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Plane.m; sourceTree = ""; }; + 1021FE181F3EDB90000E7339 /* ARModelManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ARModelManager.m; sourceTree = ""; }; + 1021FE191F3EDB90000E7339 /* ARTextManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ARTextManager.m; sourceTree = ""; }; + 1021FE1A1F3EDB90000E7339 /* ARTextManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ARTextManager.h; sourceTree = ""; }; + 1021FE1B1F3EDB90000E7339 /* ARModelManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ARModelManager.h; sourceTree = ""; }; + 105F124C1F7C0718006D4BA3 /* RCTConvert+ARKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+ARKit.h"; sourceTree = ""; }; + 105F124D1F7C0718006D4BA3 /* RCTConvert+ARKit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+ARKit.m"; sourceTree = ""; }; + 10DCBC491F7CE836008C89E7 /* ARGeosManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ARGeosManager.h; sourceTree = ""; }; + 10DCBC4A1F7CE836008C89E7 /* ARGeosManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ARGeosManager.m; sourceTree = ""; }; + 10ED47A51F38BC00004DF043 /* DeviceMotion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeviceMotion.h; sourceTree = ""; }; + 10ED47A61F38BC00004DF043 /* DeviceMotion.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DeviceMotion.m; sourceTree = ""; }; + 10FEF60F1F774C8F00EC21AE /* RCTARKitNodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTARKitNodes.h; sourceTree = ""; }; + 10FEF6101F774C8F00EC21AE /* RCTARKitIO.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTARKitIO.m; sourceTree = ""; }; + 10FEF6111F774C8F00EC21AE /* RCTARKitIO.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTARKitIO.h; sourceTree = ""; }; + 10FEF6121F774C9000EC21AE /* RCTARKitNodes.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTARKitNodes.m; sourceTree = ""; }; + 10FEF6131F774C9000EC21AE /* RCTARKitDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTARKitDelegate.h; sourceTree = ""; }; 134814201AA4EA6300B7C361 /* libRCTARKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTARKit.a; sourceTree = BUILT_PRODUCTS_DIR; }; + B14C36631F960C500047CB67 /* PocketSVG.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PocketSVG.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B14C36651F960C6E0047CB67 /* PocketSVG.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PocketSVG.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B1990B201FCEEBD60001AE2F /* color-grabber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "color-grabber.h"; sourceTree = ""; }; + B1990B211FCEEBD60001AE2F /* color-grabber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "color-grabber.m"; sourceTree = ""; }; + B1CEA29E204C14090025C1B8 /* RCTARKitSpriteView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTARKitSpriteView.m; sourceTree = ""; }; + B1CEA2A0204C15500025C1B8 /* RCTARKitSpriteView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTARKitSpriteView.h; sourceTree = ""; }; + B1CEA2A1204C15FE0025C1B8 /* RCTARKitSpriteViewManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTARKitSpriteViewManager.h; sourceTree = ""; }; + B1CEA2A2204C160D0025C1B8 /* RCTARKitSpriteViewManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTARKitSpriteViewManager.m; sourceTree = ""; }; B3E7B5881CC2AC0600A0062D /* RCTARKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTARKit.h; sourceTree = ""; }; B3E7B5891CC2AC0600A0062D /* RCTARKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTARKit.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -45,6 +84,23 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 106999E21F3EC2FB00032829 /* components */ = { + isa = PBXGroup; + children = ( + B1CEA2A2204C160D0025C1B8 /* RCTARKitSpriteViewManager.m */, + B1CEA2A1204C15FE0025C1B8 /* RCTARKitSpriteViewManager.h */, + B1CEA2A0204C15500025C1B8 /* RCTARKitSpriteView.h */, + B1CEA29E204C14090025C1B8 /* RCTARKitSpriteView.m */, + 10DCBC491F7CE836008C89E7 /* ARGeosManager.h */, + 10DCBC4A1F7CE836008C89E7 /* ARGeosManager.m */, + 1021FE1A1F3EDB90000E7339 /* ARTextManager.h */, + 1021FE191F3EDB90000E7339 /* ARTextManager.m */, + 1021FE1B1F3EDB90000E7339 /* ARModelManager.h */, + 1021FE181F3EDB90000E7339 /* ARModelManager.m */, + ); + path = components; + sourceTree = ""; + }; 134814211AA4EA7D00B7C361 /* Products */ = { isa = PBXGroup; children = ( @@ -56,14 +112,42 @@ 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( - 10E553271F1391350059B7EC /* Plane.h */, - 10E553281F1391350059B7EC /* Plane.m */, + B1990B1F1FCEEBD60001AE2F /* color-grabber */, + 106999E21F3EC2FB00032829 /* components */, + 10ED47A51F38BC00004DF043 /* DeviceMotion.h */, + 10ED47A61F38BC00004DF043 /* DeviceMotion.m */, B3E7B5881CC2AC0600A0062D /* RCTARKit.h */, B3E7B5891CC2AC0600A0062D /* RCTARKit.m */, + 10FEF6131F774C9000EC21AE /* RCTARKitDelegate.h */, + 10FEF6111F774C8F00EC21AE /* RCTARKitIO.h */, + 10FEF6101F774C8F00EC21AE /* RCTARKitIO.m */, + 10FEF60F1F774C8F00EC21AE /* RCTARKitNodes.h */, + 10FEF6121F774C9000EC21AE /* RCTARKitNodes.m */, 10062C811F127566009AE974 /* RCTARKitManager.h */, 10062C821F127572009AE974 /* RCTARKitManager.m */, + 105F124C1F7C0718006D4BA3 /* RCTConvert+ARKit.h */, + 105F124D1F7C0718006D4BA3 /* RCTConvert+ARKit.m */, 134814211AA4EA7D00B7C361 /* Products */, + B13FF7601F94F72400A6C92B /* Frameworks */, + ); + sourceTree = ""; + }; + B13FF7601F94F72400A6C92B /* Frameworks */ = { + isa = PBXGroup; + children = ( + B14C36651F960C6E0047CB67 /* PocketSVG.framework */, + B14C36631F960C500047CB67 /* PocketSVG.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + B1990B1F1FCEEBD60001AE2F /* color-grabber */ = { + isa = PBXGroup; + children = ( + B1990B201FCEEBD60001AE2F /* color-grabber.h */, + B1990B211FCEEBD60001AE2F /* color-grabber.m */, ); + path = "color-grabber"; sourceTree = ""; }; /* End PBXGroup section */ @@ -76,6 +160,7 @@ 58B511D71A9E6C8500147676 /* Sources */, 58B511D81A9E6C8500147676 /* Frameworks */, 58B511D91A9E6C8500147676 /* CopyFiles */, + B14C363A1F9504D70047CB67 /* CopyFiles */, ); buildRules = ( ); @@ -93,7 +178,7 @@ isa = PBXProject; attributes = { LastUpgradeCheck = 0610; - ORGANIZATIONNAME = Facebook; + ORGANIZATIONNAME = HippoAR; TargetAttributes = { 58B511DA1A9E6C8500147676 = { CreatedOnToolsVersion = 6.1.1; @@ -122,9 +207,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 10E553291F1391350059B7EC /* Plane.m in Sources */, + 10FEF6141F774C9000EC21AE /* RCTARKitIO.m in Sources */, + 10DCBC4B1F7CE836008C89E7 /* ARGeosManager.m in Sources */, + 10FEF6151F774C9000EC21AE /* RCTARKitNodes.m in Sources */, + B1990B221FCEEBD60001AE2F /* color-grabber.m in Sources */, + 1021FE1C1F3EDB98000E7339 /* ARModelManager.m in Sources */, + 1021FE1D1F3EDB9B000E7339 /* ARTextManager.m in Sources */, + 105F124E1F7C0719006D4BA3 /* RCTConvert+ARKit.m in Sources */, B3E7B58A1CC2AC0600A0062D /* RCTARKit.m in Sources */, 10062C831F127572009AE974 /* RCTARKitManager.m in Sources */, + B1CEA29F204C14090025C1B8 /* RCTARKitSpriteView.m in Sources */, + B1CEA2A3204C160D0025C1B8 /* RCTARKitSpriteViewManager.m in Sources */, + 10ED47A71F38BC01004DF043 /* DeviceMotion.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -164,7 +258,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -198,7 +292,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; @@ -208,12 +302,18 @@ 58B511F01A9E6C8500147676 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + FRAMEWORK_SEARCH_PATHS = ""; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/../../../React/**", "$(SRCROOT)/../../react-native/React/**", + "$(SRCROOT)/../../react-native-arcl/ios", + ../../../ios/Pods/Headers/Public/, + ../../../ios/Pods/Headers/Public/React, + "$(SRCROOT)/PocketSVG", ); + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTARKit; @@ -224,12 +324,18 @@ 58B511F11A9E6C8500147676 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + FRAMEWORK_SEARCH_PATHS = ""; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/../../../React/**", "$(SRCROOT)/../../react-native/React/**", + "$(SRCROOT)/../../react-native-arcl/ios", + ../../../ios/Pods/Headers/Public/, + ../../../ios/Pods/Headers/Public/React, + "$(SRCROOT)/PocketSVG", ); + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTARKit; diff --git a/ios/RCTARKit.xcodeproj/project.xcworkspace/xcuserdata/q.xcuserdatad/UserInterfaceState.xcuserstate b/ios/RCTARKit.xcodeproj/project.xcworkspace/xcuserdata/q.xcuserdatad/UserInterfaceState.xcuserstate index 33342893..04e912a1 100644 Binary files a/ios/RCTARKit.xcodeproj/project.xcworkspace/xcuserdata/q.xcuserdatad/UserInterfaceState.xcuserstate and b/ios/RCTARKit.xcodeproj/project.xcworkspace/xcuserdata/q.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/ios/RCTARKitDelegate.h b/ios/RCTARKitDelegate.h new file mode 100644 index 00000000..c864ae4d --- /dev/null +++ b/ios/RCTARKitDelegate.h @@ -0,0 +1,37 @@ +// +// RCTARKitDelegate.h +// RCTARKit +// +// Created by Zehao Li on 9/7/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +#import +#import +#import + +@protocol RCTARKitTouchDelegate +@optional +- (void)touches:(NSSet *)touches beganWithEvent:(UIEvent *)event; +- (void)touches:(NSSet *)touches movedWithEvent:(UIEvent *)event; +- (void)touches:(NSSet *)touches endedWithEvent:(UIEvent *)event; +- (void)touches:(NSSet *)touches cancelledWithEvent:(UIEvent *)event; +@end + + + +@protocol RCTARKitRendererDelegate +@optional +- (void)renderer:(id)renderer updateAtTime:(NSTimeInterval)time; +- (void)renderer:(id)renderer didRenderScene:(SCNScene *)scene atTime:(NSTimeInterval)time; +@end + + + +@protocol RCTARKitSessionDelegate +@optional +- (void)session:(ARSession *)session didUpdateFrame:(ARFrame *)frame; +- (void)session:(ARSession *)session didAddAnchors:(NSArray*)anchors; +- (void)session:(ARSession *)session didUpdateAnchors:(NSArray*)anchors; +- (void)session:(ARSession *)session didRemoveAnchors:(NSArray*)anchors; +@end diff --git a/ios/RCTARKitIO.h b/ios/RCTARKitIO.h new file mode 100644 index 00000000..1ddde7d9 --- /dev/null +++ b/ios/RCTARKitIO.h @@ -0,0 +1,24 @@ +// +// RCTARKitIO.h +// RCTARKit +// +// Created by Zehao Li on 9/9/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +#import +#import + +typedef void (^ARKitIOExportHandler)(NSString* filename, NSString* path); + + +@interface RCTARKitIO : NSObject + ++ (instancetype)sharedInstance; + +- (SCNNode *)loadModel:(NSString *)path nodeName:(NSString *)nodeName withAnimation:(BOOL)withAnimation; +- (SCNNode *)loadMDLModel:(NSString *)path nodeName:(NSString *)nodeName withAnimation:(BOOL)withAnimation; + +- (void)saveScene:(SCNScene *)scene as:(NSString *)filename finishHandler:(nullable ARKitIOExportHandler)finishHandler; + +@end diff --git a/ios/RCTARKitIO.m b/ios/RCTARKitIO.m new file mode 100644 index 00000000..4aa53e4a --- /dev/null +++ b/ios/RCTARKitIO.m @@ -0,0 +1,167 @@ +// +// RCTARKitIO.m +// RCTARKit +// +// Created by Zehao Li on 9/9/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +#import "RCTARKitIO.h" +#import +#import +#import + +@implementation RCTARKitIO + ++ (instancetype)sharedInstance { + static RCTARKitIO *instance = nil; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + if (instance == nil) { + instance = [[self alloc] init]; + } + }); + return instance; +} + +- (instancetype)init { + if ((self = [super init])) { + } + return self; +} + + +- (SCNNode *)loadModel:(NSString *)path nodeName:(NSString *)nodeName withAnimation:(BOOL)withAnimation { + NSURL *url = [self urlFromPath:path]; + NSError* error; + + SCNScene *scene = [SCNScene sceneWithURL:url options:nil error:&error]; + if(error) { + NSLog(@"%@",[error localizedDescription]); + } + + SCNNode *node; + if (nodeName) { + node = [scene.rootNode childNodeWithName:nodeName recursively:YES]; + } else { + node = [[SCNNode alloc] init]; + NSArray *nodeArray = [scene.rootNode childNodes]; + for (SCNNode *eachChild in nodeArray) { + [node addChildNode:eachChild]; + } + } + + if (withAnimation) { + NSMutableArray *animationMutableArray = [NSMutableArray array]; + SCNSceneSource *sceneSource = [SCNSceneSource sceneSourceWithURL:url options:@{SCNSceneSourceAnimationImportPolicyKey:SCNSceneSourceAnimationImportPolicyPlayRepeatedly}]; + + NSArray *animationIds = [sceneSource identifiersOfEntriesWithClass:[CAAnimation class]]; + for (NSString *eachId in animationIds){ + CAAnimation *animation = [sceneSource entryWithIdentifier:eachId withClass:[CAAnimation class]]; + [animationMutableArray addObject:animation]; + + } + NSArray *animationArray = [NSArray arrayWithArray:animationMutableArray]; + + int i = 1; + for (CAAnimation *animation in animationArray) { + NSString *key = [NSString stringWithFormat:@"ANIM_%d", i]; + [node addAnimation:animation forKey:key]; + i++; + } + } + + return node; +} + + + +- (SCNNode *)loadMDLModel:(NSString *)path nodeName:(NSString *)nodeName withAnimation:(BOOL)withAnimation { + NSURL *url = [self urlFromPath:path]; + MDLAsset *asset = [[MDLAsset alloc] initWithURL:url]; + SCNScene *scene = [SCNScene sceneWithMDLAsset:asset]; + + SCNNode *node; + if (nodeName) { + node = [scene.rootNode childNodeWithName:nodeName recursively:YES]; + } else { + node = [[SCNNode alloc] init]; + NSArray *nodeArray = [scene.rootNode childNodes]; + for (SCNNode *eachChild in nodeArray) { + [node addChildNode:eachChild]; + } + } + + if (withAnimation) { + NSMutableArray *animationMutableArray = [NSMutableArray array]; + SCNSceneSource *sceneSource = [SCNSceneSource sceneSourceWithURL:url options:@{SCNSceneSourceAnimationImportPolicyKey:SCNSceneSourceAnimationImportPolicyPlayRepeatedly}]; + + NSArray *animationIds = [sceneSource identifiersOfEntriesWithClass:[CAAnimation class]]; + for (NSString *eachId in animationIds){ + CAAnimation *animation = [sceneSource entryWithIdentifier:eachId withClass:[CAAnimation class]]; + [animationMutableArray addObject:animation]; + + } + NSArray *animationArray = [NSArray arrayWithArray:animationMutableArray]; + + int i = 1; + for (CAAnimation *animation in animationArray) { + NSString *key = [NSString stringWithFormat:@"ANIM_%d", i]; + [node addAnimation:animation forKey:key]; + i++; + } + } + + return node; +} + + +- (NSURL *)urlFromPath:(NSString *)path { + + NSURL *url; + + if([path hasPrefix: @"/"]) { + url = [NSURL fileURLWithPath: path]; + } else if ([path rangeOfString:@"scnassets"].location == NSNotFound) { + NSString *assetPath = [self getAppLibraryCachesPathWithSubDirectory:nil]; + NSString *modelPath = [NSString stringWithFormat:@"file://%@", [assetPath stringByAppendingPathComponent:path]]; + url = [NSURL URLWithString:[modelPath stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]]; + } else { + url = [[NSBundle mainBundle] URLForResource:path withExtension:nil]; + } + + return url; +} + + +- (void)saveScene:(SCNScene *)scene as:(NSString *)filename finishHandler:(nullable ARKitIOExportHandler)finishHandler { + NSString *assetPath = [self getAppLibraryCachesPathWithSubDirectory:nil]; + NSString *exportPath = [NSString stringWithFormat:@"file://%@/%@", assetPath, filename]; + NSLog(@"exportModel to ===> %@", exportPath); + NSURL *url = [NSURL URLWithString:[exportPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + + [scene writeToURL:url options:nil delegate:nil progressHandler:^(float totalProgress, NSError * _Nullable error, BOOL * _Nonnull stop) { + if (totalProgress == 1.0 && finishHandler) { + finishHandler(filename, exportPath); + } + }]; +} + + +- (NSString *)getAppLibraryCachesPathWithSubDirectory:(NSString *)directory { + NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] + stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]]; + if (directory) { + path = [path stringByAppendingPathComponent:directory]; + } + + NSFileManager *fileManager = [NSFileManager defaultManager]; + if (![fileManager fileExistsAtPath:path]) { + [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil]; + } + + return path; +} + +@end diff --git a/ios/RCTARKitManager.h b/ios/RCTARKitManager.h index 40959e2c..3f9d8fc4 100644 --- a/ios/RCTARKitManager.h +++ b/ios/RCTARKitManager.h @@ -11,7 +11,4 @@ #import @interface RCTARKitManager : RCTViewManager - -- (void)getCameraPosition:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject; - @end diff --git a/ios/RCTARKitManager.m b/ios/RCTARKitManager.m index 7c8a7eb5..2df43c4c 100644 --- a/ios/RCTARKitManager.m +++ b/ios/RCTARKitManager.m @@ -8,27 +8,394 @@ #import "RCTARKitManager.h" #import "RCTARKit.h" - -@interface RCTARKitManager () -@end +#import "RCTARKitNodes.h" +#import +#import +#import "color-grabber.h" @implementation RCTARKitManager RCT_EXPORT_MODULE() + + - (UIView *)view { - return [RCTARKit sharedInstance]; + return [ARKit sharedInstance]; +} + ++ (BOOL)requiresMainQueueSetup +{ + return YES; +} + + +- (NSDictionary *)constantsToExport +{ + + NSMutableDictionary * arHitTestResultType = + [NSMutableDictionary dictionaryWithDictionary: + @{ + @"FeaturePoint": @(ARHitTestResultTypeFeaturePoint), + @"EstimatedHorizontalPlane": @(ARHitTestResultTypeEstimatedHorizontalPlane), + @"ExistingPlane": @(ARHitTestResultTypeExistingPlane), + @"ExistingPlaneUsingExtent": @(ARHitTestResultTypeExistingPlaneUsingExtent), + }]; + NSMutableDictionary * arAnchorAligment = + [NSMutableDictionary + dictionaryWithDictionary:@{ + @"Horizontal": @(ARPlaneAnchorAlignmentHorizontal) + }]; + NSMutableDictionary * arPlaneDetection = + [NSMutableDictionary + dictionaryWithDictionary:@{ + @"Horizontal": @(ARPlaneDetectionHorizontal), + @"None": @(ARPlaneDetectionNone), + }]; + #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110300 + if (@available(iOS 11.3, *)) { + [arHitTestResultType + addEntriesFromDictionary:@{ + @"ExistingPlaneUsingGeometry": @(ARHitTestResultTypeExistingPlaneUsingGeometry), + @"EstimatedVerticalPlane": @(ARHitTestResultTypeEstimatedVerticalPlane) + }]; + [arPlaneDetection + addEntriesFromDictionary:@{ + @"Vertical": @(ARPlaneDetectionVertical), + @"HorizontalVertical": @(ARPlaneDetectionHorizontal + ARPlaneDetectionVertical), + }]; + [arAnchorAligment + addEntriesFromDictionary:@{ + @"Vertical": @(ARPlaneAnchorAlignmentVertical) + }]; + } + #endif + + + + + return @{ + @"ARHitTestResultType": arHitTestResultType, + @"ARPlaneDetection": arPlaneDetection, + @"ARPlaneAnchorAlignment": arAnchorAligment, + @"LightingModel": @{ + @"Constant": SCNLightingModelConstant, + @"Blinn": SCNLightingModelBlinn, + @"Lambert": SCNLightingModelLambert, + @"Phong": SCNLightingModelPhong, + @"PhysicallyBased": SCNLightingModelPhysicallyBased + }, + @"LightType": @{ + @"Ambient": SCNLightTypeAmbient, + @"Directional": SCNLightTypeDirectional, + @"Omni": SCNLightTypeOmni, + @"Probe": SCNLightTypeProbe, + @"Spot": SCNLightTypeSpot, + @"IES": SCNLightTypeIES + }, + @"ShadowMode": @{ + @"Forward": [@(SCNShadowModeForward) stringValue], + @"Deferred": [@(SCNShadowModeDeferred) stringValue], + @"ModeModulated": [@(SCNShadowModeModulated) stringValue], + }, + @"ColorMask": @{ + @"All": [@(SCNColorMaskAll) stringValue], + @"None": [@(SCNColorMaskNone) stringValue], + @"Alpha": [@(SCNColorMaskAlpha) stringValue], + @"Blue": [@(SCNColorMaskBlue) stringValue], + @"Red": [@(SCNColorMaskRed) stringValue], + @"Green": [@(SCNColorMaskGreen) stringValue], + }, + + @"ShaderModifierEntryPoint": @{ + @"Geometry": SCNShaderModifierEntryPointGeometry, + @"Surface": SCNShaderModifierEntryPointSurface, + @"LighingModel": SCNShaderModifierEntryPointLightingModel, + @"Fragment": SCNShaderModifierEntryPointFragment + }, + @"BlendMode": @{ + @"Alpha": [@(SCNBlendModeAlpha) stringValue], + @"Add": [@(SCNBlendModeAdd) stringValue], + @"Subtract": [@(SCNBlendModeSubtract) stringValue], + @"Multiply": [@(SCNBlendModeMultiply) stringValue], + @"Screen": [@(SCNBlendModeScreen) stringValue], + @"Replace": [@(SCNBlendModeReplace) stringValue], + }, + @"TransparencyMode": @{ + @"Default": [@(SCNTransparencyModeAOne) stringValue], + @"RGBZero": [@(SCNTransparencyModeRGBZero) stringValue], + @"SingleLayer": [@(SCNTransparencyModeSingleLayer) stringValue], + @"DualLayer": [@(SCNTransparencyModeDualLayer) stringValue], + }, + @"ChamferMode": @{ + @"Both": [@(SCNChamferModeBoth) stringValue], + @"Back": [@(SCNChamferModeBack) stringValue], + @"Front": [@(SCNChamferModeBack) stringValue], + + }, + @"ARWorldAlignment": @{ + @"Gravity": @(ARWorldAlignmentGravity), + @"GravityAndHeading": @(ARWorldAlignmentGravityAndHeading), + @"Camera": @(ARWorldAlignmentCamera), + }, + @"FillMode": @{ + @"Fill": [@(SCNFillModeFill) stringValue], + @"Lines": [@(SCNFillModeLines) stringValue], + }, + @"WrapMode": @{ + @"Clamp": [@(SCNWrapModeClamp) stringValue], + @"Repeat": [@(SCNWrapModeRepeat) stringValue], + @"Mirror": [@(SCNWrapModeMirror) stringValue], + }, + @"Constraint": @{ + @"None": @"0", + @"BillboardAxisAll": [@(SCNBillboardAxisAll) stringValue], + @"BillboardAxisX": [@(SCNBillboardAxisX) stringValue], + @"BillboardAxisY": [@(SCNBillboardAxisY) stringValue], + @"BillboardAxisZ": [@(SCNBillboardAxisZ) stringValue], + } + }; } RCT_EXPORT_VIEW_PROPERTY(debug, BOOL) -RCT_EXPORT_VIEW_PROPERTY(planeDetection, BOOL) -RCT_EXPORT_VIEW_PROPERTY(lightEstimation, BOOL) +RCT_EXPORT_VIEW_PROPERTY(planeDetection, ARPlaneDetection) +RCT_EXPORT_VIEW_PROPERTY(origin, NSDictionary *) +RCT_EXPORT_VIEW_PROPERTY(lightEstimationEnabled, BOOL) +RCT_EXPORT_VIEW_PROPERTY(autoenablesDefaultLighting, BOOL) +RCT_EXPORT_VIEW_PROPERTY(worldAlignment, NSInteger) +RCT_EXPORT_VIEW_PROPERTY(detectionImages, NSArray *) RCT_EXPORT_VIEW_PROPERTY(onPlaneDetected, RCTBubblingEventBlock) -RCT_EXPORT_VIEW_PROPERTY(onPlaneUpdate, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onPlaneUpdated, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onPlaneRemoved, RCTBubblingEventBlock) + +RCT_EXPORT_VIEW_PROPERTY(onAnchorDetected, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onAnchorUpdated, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onAnchorRemoved, RCTBubblingEventBlock) + +RCT_EXPORT_VIEW_PROPERTY(onTrackingState, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onFeaturesDetected, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onLightEstimation, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onTapOnPlaneUsingExtent, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onTapOnPlaneNoExtent, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onEvent, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onARKitError, RCTBubblingEventBlock) + +RCT_EXPORT_METHOD(pause:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + [[ARKit sharedInstance] pause]; + resolve(@{}); +} + +RCT_EXPORT_METHOD(resume:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + [[ARKit sharedInstance] resume]; + resolve(@{}); +} + +RCT_EXPORT_METHOD(reset:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + [[ARKit sharedInstance] reset]; + resolve(@{}); +} + +RCT_EXPORT_METHOD(isInitialized:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + resolve(@([ARKit isInitialized])); +} + +RCT_EXPORT_METHOD(isMounted:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + if( [ARKit isInitialized]) { + dispatch_async(dispatch_get_main_queue(), ^{ + resolve(@([[ARKit sharedInstance] isMounted])); + }); + } else { + resolve(@(NO)); + } +} + +RCT_EXPORT_METHOD( + hitTestPlanes: (NSDictionary *)pointDict + types:(NSUInteger)types + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject + ) { + CGPoint point = CGPointMake( [pointDict[@"x"] floatValue], [pointDict[@"y"] floatValue] ); + [[ARKit sharedInstance] hitTestPlane:point types:types resolve:resolve reject:reject]; +} + +RCT_EXPORT_METHOD( + hitTestSceneObjects: (NSDictionary *)pointDict + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject + ) { + CGPoint point = CGPointMake( [pointDict[@"x"] floatValue], [pointDict[@"y"] floatValue] ); + [[ARKit sharedInstance] hitTestSceneObjects:point resolve:resolve reject:reject]; +} + + + + +- (NSString *)getAssetUrl:(NSString *)localID { + // thx https://stackoverflow.com/a/34788748/1463534 + // heck, objective c is such a mess + NSString * assetID = [localID stringByReplacingOccurrencesOfString:@"/.*" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, [localID length])]; + NSString * ext = @"JPG"; + NSString * assetURLStr = [NSString stringWithFormat:@"assets-library://asset/asset.%@?id=%@&ext=%@", ext, assetID, ext]; + return assetURLStr; +} + +- (void)storeImageInPhotoAlbum:(UIImage *)image cameraProperties:(NSDictionary *) cameraProperties reject:(RCTPromiseRejectBlock)reject resolve:(RCTPromiseResolveBlock)resolve { + __block PHObjectPlaceholder *placeholder; + + [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ + PHAssetChangeRequest* createAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:image]; + placeholder = [createAssetRequest placeholderForCreatedAsset]; + + } completionHandler:^(BOOL success, NSError *error) { + if (success) + { + + NSString * localID = placeholder.localIdentifier; + + NSString * assetURLStr = [self getAssetUrl:localID]; + + resolve(@{@"url": assetURLStr, @"width":@(image.size.width), @"height": @(image.size.height), @"camera":cameraProperties}); + } + else + { + reject(@"snapshot_error", @"Could not store snapshot", error); + } + }]; +} + + +- (void)storeImageInDirectory:(UIImage *)image directory:(NSString *)directory format:(NSString *)format cameraProperties:(NSDictionary *) cameraProperties reject:(RCTPromiseRejectBlock)reject resolve:(RCTPromiseResolveBlock)resolve { + NSData *data; + if([format isEqualToString:@"jpg"]) { + data = UIImageJPEGRepresentation(image, 0.9); + } else if([format isEqualToString:@"png"]) { + data = UIImagePNGRepresentation(image); + } else { + reject(@"snapshot_error", [NSString stringWithFormat:@"unkown file format '%@'", format], nil); + return; + } + NSString *prefixString = @"capture"; + + NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString] ; + NSString *uniqueFileName = [NSString stringWithFormat:@"%@_%@.%@", prefixString, guid, format]; + + NSString *filePath = [directory stringByAppendingPathComponent:uniqueFileName]; //Add the file name + bool success = [data writeToFile:filePath atomically:YES]; //Write the file + if(success) { + resolve(@{@"url": filePath, @"width":@(image.size.width), @"height": @(image.size.height), @"camera":cameraProperties}); + } else { + // TODO use NSError from writeToFile + reject(@"snapshot_error", [NSString stringWithFormat:@"could not save to '%@'", filePath], nil); + } + +} + +- (void)storeImage:(UIImage *)image options:(NSDictionary *)options reject:(RCTPromiseRejectBlock)reject resolve:(RCTPromiseResolveBlock)resolve cameraProperties:(NSDictionary *)cameraProperties { + NSString * target = @"cameraRoll"; + NSString * format = @"png"; + + if(options[@"target"]) { + target = options[@"target"]; + } + if(options[@"format"]) { + format = options[@"format"]; + } + if([target isEqualToString:@"cameraRoll"]) { + // camera roll / photo album + [self storeImageInPhotoAlbum:image cameraProperties:cameraProperties reject:reject resolve:resolve ]; + } else { + NSString * dir; + if([target isEqualToString:@"cache"]) { + dir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; + } else if([target isEqualToString:@"documents"]) { + dir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; + + } else { + dir = target; + } + [self storeImageInDirectory:image directory:dir format:format cameraProperties:cameraProperties reject:reject resolve:resolve ]; + } +} + +RCT_EXPORT_METHOD(snapshot:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSDictionary * selection = options[@"selection"]; + NSDictionary * cameraProperties = [[ARKit sharedInstance] readCamera]; + UIImage *image = [[ARKit sharedInstance] getSnapshot:selection]; + + [self storeImage:image options:options reject:reject resolve:resolve cameraProperties:cameraProperties ]; + }); +} + + + + +RCT_EXPORT_METHOD(snapshotCamera:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSDictionary * selection = options[@"selection"]; + NSDictionary * cameraProperties = [[ARKit sharedInstance] readCamera]; + UIImage *image = [[ARKit sharedInstance] getSnapshotCamera:selection]; + [self storeImage:image options:options reject:reject resolve:resolve cameraProperties:cameraProperties]; + }); +} + +RCT_EXPORT_METHOD(pickColorsRaw:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + + NSDictionary * selection = options[@"selection"]; + UIImage *image = [[ARKit sharedInstance] getSnapshotCamera:selection]; + resolve([[ColorGrabber sharedInstance] getColorsFromImage:image options:options]); + }); +} + +RCT_EXPORT_METHOD(pickColorsRawFromFile:(NSString * )filePath options:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + UIImage *image = [UIImage imageWithContentsOfFile:filePath]; + resolve([[ColorGrabber sharedInstance] getColorsFromImage:image options:options]); + }); +} + +RCT_EXPORT_METHOD(getCamera:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + resolve([[ARKit sharedInstance] readCamera]); +} RCT_EXPORT_METHOD(getCameraPosition:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { - resolve([[RCTARKit sharedInstance] cameraPosition]); + resolve([[ARKit sharedInstance] readCameraPosition]); +} + +RCT_EXPORT_METHOD(getCurrentLightEstimation:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + resolve([[ARKit sharedInstance] getCurrentLightEstimation]); +} + +RCT_EXPORT_METHOD(getCurrentDetectedFeaturePoints:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + resolve([[ARKit sharedInstance] getCurrentDetectedFeaturePoints]); +} + +RCT_EXPORT_METHOD(projectPoint: + (NSDictionary *)pointDict + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + SCNVector3 point = SCNVector3Make( [pointDict[@"x"] floatValue], [pointDict[@"y"] floatValue], [pointDict[@"z"] floatValue] ); + SCNVector3 pointProjected = [[ARKit sharedInstance] projectPoint:point]; + float distance = [[ARKit sharedInstance] getCameraDistanceToPoint:point]; + resolve(@{ + @"x": @(pointProjected.x), + @"y": @(pointProjected.y), + @"z": @(pointProjected.z), + @"distance": @(distance) + }); + +} + +RCT_EXPORT_METHOD(focusScene:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + [[ARKit sharedInstance] focusScene]; + resolve(@{}); +} + +RCT_EXPORT_METHOD(clearScene:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + [[ARKit sharedInstance] clearScene]; + resolve(@{}); } @end diff --git a/ios/RCTARKitNodes.h b/ios/RCTARKitNodes.h new file mode 100644 index 00000000..a6ad4be5 --- /dev/null +++ b/ios/RCTARKitNodes.h @@ -0,0 +1,49 @@ +// +// RCTARKitNodes.h +// RCTARKit +// +// Created by Zehao Li on 9/9/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +#import +#import +#import +#import "RCTARKitDelegate.h" + +typedef NS_OPTIONS(NSUInteger, RFReferenceFrame) { + RFReferenceFrameLocal = 0, + RFReferenceFrameCamera = 1, + RFReferenceFrameFrontOfCamera = 2, + RFReferenceFrameWorld = 3, // to be implemented +}; + +@interface SCNNode (ReferenceFrame) +@property (nonatomic, assign) RFReferenceFrame referenceFrame; +@end + + +@interface RCTARKitNodes : NSObject + +@property (nonatomic, strong) ARSCNView *arView; + +@property (nonatomic, strong) SCNNode *localOrigin; +@property (nonatomic, strong) SCNNode *cameraOrigin; +@property (nonatomic, strong) SCNNode *frontOfCamera; +@property (nonatomic, assign) SCNVector3 cameraDirection; + + ++ (instancetype)sharedInstance; + +- (void)addNodeToScene:(SCNNode *)node inReferenceFrame:(NSString *)referenceFrame withParentId:(NSString *)parentId; +- (bool)updateNode:(NSString *)nodeId properties:(NSDictionary *) properties; +- (float)getCameraDistanceToPoint:(SCNVector3)point; +- (void)registerNode:(SCNNode *)node withId:(NSString *)key; +- (SCNNode *)getNodeWithId:(NSString *)key; +- (void)removeNode:(NSString *)key; +- (NSDictionary *)getSceneObjectsHitResult:(const CGPoint)tapPoint; +- (void)clear; +- (NSMutableArray *) mapHitResults:(NSArray *)results; +- (SCNVector3)getAbsolutePositionToOrigin:(const SCNVector3)positionRelative; +- (SCNVector3)getRelativePositionToOrigin:(const SCNVector3)positionAbsolute; +@end diff --git a/ios/RCTARKitNodes.m b/ios/RCTARKitNodes.m new file mode 100644 index 00000000..6379fb8a --- /dev/null +++ b/ios/RCTARKitNodes.m @@ -0,0 +1,407 @@ +// +// RCTARKitNodes.m +// RCTARKit +// +// Created by Zehao Li on 9/9/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +#import "RCTARKit.h" +#import "RCTARKitNodes.h" +#import "RCTConvert+ARKit.h" + + +@implementation SCNNode (ReferenceFrame) +@dynamic referenceFrame; +@end + +CGFloat focDistance = 0.2f; + + +@interface RCTARKitNodes () + + +@property (nonatomic, strong) SCNNode* rootNode; + +@property NSMutableDictionary *nodes; +@property NSMutableDictionary *orphans; + +@end + + + +@implementation RCTARKitNodes + ++ (instancetype)sharedInstance { + static RCTARKitNodes *instance = nil; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + if (instance == nil) { + instance = [[self alloc] init]; + } + }); + return instance; +} + +- (instancetype)init { + if ((self = [super init])) { + // local reference frame origin + self.localOrigin = [[SCNNode alloc] init]; + self.localOrigin.name = @"localOrigin"; + + // camera reference frame origin + self.cameraOrigin = [[SCNNode alloc] init]; + self.cameraOrigin.name = @"cameraOrigin"; + + // front-of-camera frame origin + self.frontOfCamera = [[SCNNode alloc] init]; + self.frontOfCamera.name = @"frontOfCamera"; + + // init caches + self.nodes = [NSMutableDictionary new]; + self.orphans = [NSMutableDictionary new]; + } + return self; +} + +- (void)setArView:(ARSCNView *)arView { + //NSLog(@"setArView"); + _arView = arView; + self.rootNode = arView.scene.rootNode; + + self.rootNode.name = @"root"; + + [self.rootNode addChildNode:self.localOrigin]; + [self.rootNode addChildNode:self.cameraOrigin]; + [self.rootNode addChildNode:self.frontOfCamera]; +} + +#pragma mark + + + + +/** + add a node to scene on a frame (defaults to Local) or to a parentNode (if parentId is given) + */ +- (void)addNodeToScene:(SCNNode *)node inReferenceFrame:(NSString *)referenceFrame withParentId:(NSString *)parentId { + //NSLog(@"addNodeToScene node: %@ ", node.name); + if(parentId) { + [self addNodeToParent:node parentId:parentId]; + } else { + [self addNodeToFrame:node referenceFrame:referenceFrame]; + } + +} +/** + add a node to scene in a reference frame + */ +- (void)addNodeToFrame:(SCNNode *)node referenceFrame:(NSString *)referenceFrame { + [self registerNode:node withId:node.name]; + if (!referenceFrame) { + referenceFrame = @"Local"; // default to Local frame + } + NSString *selectorString = [NSString stringWithFormat:@"addNodeTo%@Frame:", referenceFrame]; + SEL selector = NSSelectorFromString(selectorString); + if ([self respondsToSelector:selector]) { + // check https://stackoverflow.com/questions/7017281/performselector-may-cause-a-leak-because-its-selector-is-unknown + IMP imp = [self methodForSelector:selector]; + void (*func)(id, SEL, SCNNode*) = (void *)imp; + func(self, selector, node); + } +} + + +- (void)addNodeToParent:(SCNNode *)node parentId:(NSString *)parentId { + SCNNode * parentNode = [self getNodeWithId:parentId]; + + if(!parentNode) { + NSMutableSet * orphansLookingForParent = [self.orphans objectForKey:parentId]; + if(!orphansLookingForParent) { + orphansLookingForParent = [NSMutableSet setWithObject:node]; + } else { + [orphansLookingForParent addObject:node]; + } + [self.orphans setObject:orphansLookingForParent forKey:parentId]; + + } else { + [parentNode addChildNode:node]; + + } + [self registerNode:node withId:node.name]; + + + +} + +- (void)clear { + // clear scene + NSArray *keys = [self.nodes allKeys]; + + for (id key in keys) { + id node = [self.nodes objectForKey:key]; + if (node) { + [node removeFromParentNode]; + } + + } + [self.nodes removeAllObjects]; +} + +- (void)addNodeToLocalFrame:(SCNNode *)node { + node.referenceFrame = RFReferenceFrameLocal; + + [self.localOrigin addChildNode:node]; + //NSLog(@"[RCTARKitNodes] Add node %@ to Local frame at (%.2f, %.2f, %.2f)", node.name, node.position.x, node.position.y, node.position.z); + +} + +- (void)addNodeToCameraFrame:(SCNNode *)node { + node.referenceFrame = RFReferenceFrameCamera; + //NSLog(@"[RCTARKitNodes] Add node %@ to Camera frame at (%.2f, %.2f, %.2f)", node.name, node.position.x, node.position.y, node.position.z); + + [self.cameraOrigin addChildNode:node]; +} + +- (void)addNodeToFrontOfCameraFrame:(SCNNode *)node { + node.referenceFrame = RFReferenceFrameFrontOfCamera; + + //NSLog(@"[RCTARKitNodes] Add node %@ to FrontOfCamera frame at (%.2f, %.2f, %.2f)", node.name, node.position.x, node.position.y, node.position.z); + + [self.frontOfCamera addChildNode:node]; +} + + +- (NSDictionary *)getSceneObjectsHitResult:(const CGPoint)tapPoint { + NSDictionary *options = @{ + SCNHitTestRootNodeKey: self.localOrigin, + SCNHitTestSortResultsKey: @(YES), + SCNHitTestOptionSearchMode: @(SCNHitTestSearchModeAll) + }; + NSArray *results = [_arView hitTest:tapPoint options:options]; + NSMutableArray * resultsMapped = [self mapHitResultsWithSceneResults:results]; + NSDictionary *result = getSceneObjectHitResult(resultsMapped, tapPoint); + return result; +} + + +static NSDictionary * getSceneObjectHitResult(NSMutableArray *resultsMapped, const CGPoint tapPoint) { + return @{ + @"results": resultsMapped + }; +} + + +static SCNVector3 toSCNVector3(simd_float4 float4) { + SCNVector3 positionAbsolute = SCNVector3Make(float4.x, float4.y, float4.z); + return positionAbsolute; +} + +- (SCNVector3)getRelativePositionToOrigin:(const SCNVector3)positionAbsolute { + SCNVector3 originPosition = self.localOrigin.position; + SCNVector3 position = SCNVector3Make(positionAbsolute.x - originPosition.x, positionAbsolute.y- originPosition.y, positionAbsolute.z - originPosition.z); + return position; +} + +- (SCNVector3)getAbsolutePositionToOrigin:(const SCNVector3)positionRelative { + SCNVector3 originPosition = self.localOrigin.position; + SCNVector3 position = SCNVector3Make(positionRelative.x + originPosition.x, positionRelative.y+ originPosition.y, positionRelative.z + originPosition.z); + return position; +} + + + +- (NSMutableArray *) mapHitResultsWithSceneResults: (NSArray *)results { + + NSMutableArray *resultsMapped = [NSMutableArray arrayWithCapacity:[results count]]; + + [results enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) { + SCNHitTestResult *result = (SCNHitTestResult *) obj; + SCNNode * node = result.node; + + NSString * nodeId = [self findNodeId:node]; + if(nodeId) { + + SCNVector3 positionAbsolute = result.worldCoordinates; + SCNVector3 position = [self getRelativePositionToOrigin:positionAbsolute]; + SCNVector3 normal = result.worldNormal; + float distance = [self getCameraDistanceToPoint:positionAbsolute]; + NSDictionary *result = @{ + @"id": nodeId, + @"distance": @(distance), + @"positionAbsolute": @{ + @"x": @(positionAbsolute.x), + @"y": @(positionAbsolute.y), + @"z": @(positionAbsolute.z) + }, + @"position": @{ + @"x": @(position.x), + @"y": @(position.y), + @"z": @(position.z) + }, + @"normal": @{ + @"x": @(normal.x), + @"y": @(normal.y), + @"z": @(normal.z) + } + }; + [resultsMapped addObject:(result )]; + } + + }]; + + return resultsMapped; + +} + + + +static id ObjectOrNull(id object) +{ + return object ?: [NSNull null]; +} + +- (NSMutableArray *) mapHitResults:(NSArray *)results { + NSMutableArray *resultsMapped = [NSMutableArray arrayWithCapacity:[results count]]; + + [results enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) { + ARHitTestResult *result = (ARHitTestResult *) obj; + + SCNVector3 positionAbsolute = toSCNVector3(result.worldTransform.columns[3]); + SCNVector3 position = [self getRelativePositionToOrigin:positionAbsolute]; + [resultsMapped addObject:(@{ + @"distance": @(result.distance), + @"id": ObjectOrNull(result.anchor.identifier.UUIDString), + @"positionAbsolute": @{ + @"x": @(positionAbsolute.x), + @"y": @(positionAbsolute.y), + @"z": @(positionAbsolute.z) + }, + @"position": @{ + @"x": @(position.x), + @"y": @(position.y), + @"z": @(position.z) + } + } )]; + }]; + return resultsMapped; +} + +#pragma mark - node register +- (void)registerNode:(SCNNode *)node withId:(NSString *)nodeId { + [self removeNode:nodeId]; + if (node) { + //NSLog(@"registering node %@", nodeId); + [self.nodes setObject:node forKey:nodeId]; + + // are there any orphans? (3d objects that have a parent, but parent has not yet been mounted + // this seems to be the default case as react mounts first the children + NSSet * orphans = [self.orphans objectForKey:nodeId]; + if(orphans) { + for (SCNNode * child in [orphans allObjects]) { + [node addChildNode:child]; + } + [self.orphans removeObjectForKey:nodeId]; + } + } +} + + +- (NSString *) findNodeId:(SCNNode *)nodeWithParents { + + SCNNode* _node = nodeWithParents; + while(_node) { + if(_node.name && [self.nodes objectForKey:_node.name]) { + return _node.name; + } + _node = _node.parentNode; + } + return nil; + +} + + +- (SCNNode *)getNodeWithId:(NSString *)nodeId { + return [self.nodes objectForKey:nodeId]; +} + +- (void)removeNode:(NSString *)nodeId { + + SCNNode *node = [self getNodeWithId:nodeId]; + if (node) { + //NSLog(@"removing node: %@ ", key); + + if(node.parentNode) { + if(node.light) { + // see https://stackoverflow.com/questions/47270056/how-to-remove-a-light-with-shadowmode-deferred-in-scenekit-arkit?noredirect=1#comment81491270_47270056 + node.hidden = YES; + [node removeFromParentNode]; + } else { + [node removeFromParentNode]; + } + } + [self.nodes removeObjectForKey:nodeId]; + } +} + +- (bool)updateNode:(NSString *)nodeId + properties:(NSDictionary *) properties { + + SCNNode *node = [self getNodeWithId:nodeId]; + //NSLog(@"updating node %@ :%@", nodeId, properties); + if(node) { + [RCTConvert setNodeProperties:node properties:properties]; + if(node.geometry && properties[@"shape"]) { + [RCTConvert setShapeProperties:node.geometry properties:properties[@"shape"]]; + } + if(properties[@"material"]) { + for (id material in node.geometry.materials) { + [RCTConvert setMaterialProperties:material properties:properties[@"material"]]; + } + } + if(node.light) { + [RCTConvert setLightProperties:node.light properties:properties]; + } + return true; + } else { + NSLog(@"WARNING: node does not exists: %@. This means that the node has not been mounted yet, so native calls got out of order", nodeId); + + return false; + } + +} + + + + +#pragma mark - RCTARKitSessionDelegate +- (void)session:(ARSession *)session didUpdateFrame:(ARFrame *)frame { + simd_float4 pos = frame.camera.transform.columns[3]; + self.cameraOrigin.position = SCNVector3Make(pos.x, pos.y, pos.z); + simd_float4 z = frame.camera.transform.columns[2]; + self.cameraDirection = SCNVector3Make(-z.x, -z.y, -z.z); + self.cameraOrigin.eulerAngles = SCNVector3Make(0, atan2f(z.x, z.z), 0); + self.frontOfCamera.position = SCNVector3Make(pos.x - focDistance * z.x, pos.y - focDistance * z.y, pos.z - focDistance * z.z); + self.frontOfCamera.eulerAngles = self.cameraOrigin.eulerAngles; + +} + +- (float)getCameraDistanceToPoint:(SCNVector3)point { + return getDistance(self.cameraOrigin.position, point); +} + +static float getDistance(const SCNVector3 pointA, const SCNVector3 pointB) { + float xd = pointB.x - pointA.x; + float yd = pointB.y - pointA.y; + float zd = pointB.z - pointA.z; + float distance = sqrt(xd * xd + yd * yd + zd * zd); + + if (distance < 0){ + return (distance * -1); + } else { + return (distance); + } +} + +@end diff --git a/ios/RCTConvert+ARKit.h b/ios/RCTConvert+ARKit.h new file mode 100644 index 00000000..60beee71 --- /dev/null +++ b/ios/RCTConvert+ARKit.h @@ -0,0 +1,48 @@ +// +// RCTConvert+ARKit.h +// RCTARKit +// +// Created by Zehao Li on 9/28/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +#import +#import +#import +#import + +@interface SCNTextNode : SCNNode +@end + + +@interface RCTConvert (ARKit) + ++ (SCNMaterial *)SCNMaterial:(id)json; + ++ (SCNVector3)SCNVector3:(id)json; ++ (SCNVector4)SCNVector4:(id)json; ++ (SCNNode *)SCNNode:(id)json; + ++ (SCNBox *)SCNBox:(id)json; ++ (SCNSphere *)SCNSphere:(id)json; ++ (SCNCylinder *)SCNCylinder:(id)json; ++ (SCNCone *)SCNCone:(id)json; ++ (SCNPyramid *)SCNPyramid:(id)json; ++ (SCNTube *)SCNTube:(id)json; ++ (SCNTorus *)SCNTorus:(id)json; ++ (SCNCapsule *)SCNCapsule:(id)json; ++ (SCNPlane *)SCNPlane:(id)json; ++ (SCNShape * )SCNShape:(id)json; ++ (SCNLight *)SCNLight:(id)json; + ++ (SCNTextNode *)SCNTextNode:(id)json; + ++ (void)setNodeProperties:(SCNNode *)node properties:(id)json; ++ (void)setMaterialProperties:(SCNMaterial *)material properties:(id)json; ++ (void)setShapeProperties:(SCNGeometry *)geometry properties:(id)json; ++ (void)setLightProperties:(SCNLight *)light properties:(id)json; + ++ (ARPlaneDetection)ARPlaneDetection:(id)number; + +@end + diff --git a/ios/RCTConvert+ARKit.m b/ios/RCTConvert+ARKit.m new file mode 100644 index 00000000..16c1f133 --- /dev/null +++ b/ios/RCTConvert+ARKit.m @@ -0,0 +1,619 @@ +// +// RCTConvert+ARKit.m +// RCTARKit +// +// Created by Zehao Li on 9/28/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +#import "RCTConvert+ARKit.h" +#import "SVGBezierPath.h" + +@implementation RCTConvert (ARKit) + ++ (SCNMaterial *)SCNMaterial:(id)json { + SCNMaterial *material = [SCNMaterial new]; + [self setMaterialProperties:material properties:json]; + + return material; +} + + + ++ (SCNVector3)SCNVector3:(id)json { + CGFloat x = [json[@"x"] floatValue]; + CGFloat y = [json[@"y"] floatValue]; + CGFloat z = [json[@"z"] floatValue]; + return SCNVector3Make(x, y, z); +} + ++ (SCNVector4)SCNVector4:(id)json { + CGFloat x = [json[@"x"] floatValue]; + CGFloat y = [json[@"y"] floatValue]; + CGFloat z = [json[@"z"] floatValue]; + CGFloat w = [json[@"w"] floatValue]; + return SCNVector4Make(x, y, z, w); +} + + ++ (SCNNode *)SCNNode:(id)json { + SCNNode *node = [SCNNode new]; + + node.name = [NSString stringWithFormat:@"%@", json[@"id"]]; + [self setNodeProperties:node properties:json]; + + return node; +} + + ++ (void)addMaterials:(SCNGeometry *)geometry json:(id)json sides:(int) sides { + SCNMaterial *material = [self SCNMaterial:json[@"material"]]; + + NSMutableArray *materials = [NSMutableArray array]; + for (int i = 0; i < sides; i++) + [materials addObject: material]; + geometry.materials = materials; +} + ++ (SCNBox *)SCNBox:(id)json { + NSDictionary *shape = json[@"shape"]; + + + CGFloat width = [shape[@"width"] floatValue]; + CGFloat height = [shape[@"height"] floatValue]; + CGFloat length = [shape[@"length"] floatValue]; + CGFloat chamfer = [shape[@"chamfer"] floatValue]; + SCNBox *geometry = [SCNBox boxWithWidth:width height:height length:length chamferRadius:chamfer]; + + [self addMaterials:geometry json:json sides:6]; + + return geometry; +} + + ++ (SCNSphere *)SCNSphere:(id)json { + NSDictionary* shape = json[@"shape"]; + CGFloat radius = [shape[@"radius"] floatValue]; + SCNSphere *geometry = [SCNSphere sphereWithRadius:radius]; + + [self addMaterials:geometry json:json sides:1]; + + return geometry; +} + ++ (SCNCylinder *)SCNCylinder:(id)json { + NSDictionary* shape = json[@"shape"]; + CGFloat radius = [shape[@"radius"] floatValue]; + CGFloat height = [shape[@"height"] floatValue]; + SCNCylinder *geometry = [SCNCylinder cylinderWithRadius:radius height:height]; + + [self addMaterials:geometry json:json sides:3]; + + return geometry; +} + ++ (SCNCone *)SCNCone:(id)json { + NSDictionary* shape = json[@"shape"]; + CGFloat topR = [shape[@"topR"] floatValue]; + CGFloat bottomR = [shape[@"bottomR"] floatValue]; + CGFloat height = [shape[@"height"] floatValue]; + SCNCone *geometry = [SCNCone coneWithTopRadius:topR bottomRadius:bottomR height:height]; + + [self addMaterials:geometry json:json sides:2]; + + return geometry; +} + ++ (SCNPyramid *)SCNPyramid:(id)json { + NSDictionary* shape = json[@"shape"]; + CGFloat width = [shape[@"width"] floatValue]; + CGFloat length = [shape[@"length"] floatValue]; + CGFloat height = [shape[@"height"] floatValue]; + SCNPyramid *geometry = [SCNPyramid pyramidWithWidth:width height:height length:length]; + + [self addMaterials:geometry json:json sides:5]; + + return geometry; +} + ++ (SCNTube *)SCNTube:(id)json { + NSDictionary* shape = json[@"shape"]; + CGFloat innerR = [shape[@"innerR"] floatValue]; + CGFloat outerR = [shape[@"outerR"] floatValue]; + CGFloat height = [shape[@"height"] floatValue]; + SCNTube *geometry = [SCNTube tubeWithInnerRadius:innerR outerRadius:outerR height:height]; + + [self addMaterials:geometry json:json sides:4]; + + return geometry; +} + ++ (SCNTorus *)SCNTorus:(id)json { + NSDictionary* shape = json[@"shape"]; + CGFloat ringR = [shape[@"ringR"] floatValue]; + CGFloat pipeR = [shape[@"pipeR"] floatValue]; + SCNTorus *geometry = [SCNTorus torusWithRingRadius:ringR pipeRadius:pipeR]; + + [self addMaterials:geometry json:json sides:1]; + + return geometry; +} + ++ (SCNCapsule *)SCNCapsule:(id)json { + NSDictionary* shape = json[@"shape"]; + CGFloat capR = [shape[@"capR"] floatValue]; + CGFloat height = [shape[@"height"] floatValue]; + SCNCapsule *geometry = [SCNCapsule capsuleWithCapRadius:capR height:height]; + + [self addMaterials:geometry json:json sides:1]; + + return geometry; +} + ++ (SCNPlane *)SCNPlane:(id)json { + NSDictionary* shape = json[@"shape"]; + CGFloat width = [shape[@"width"] floatValue]; + CGFloat height = [shape[@"height"] floatValue]; + SCNPlane *geometry = [SCNPlane planeWithWidth:width height:height]; + if(shape[@"cornerRadius"]) { + geometry.cornerRadius = [shape[@"cornerRadius"] floatValue]; + } + if(shape[@"cornerSegmentCount"]) { + geometry.cornerSegmentCount = [shape[@"cornerSegmentCount"] intValue]; + } + if(shape[@"widthSegmentCount"]) { + geometry.widthSegmentCount = [shape[@"widthSegmentCount"] intValue]; + } + if(shape[@"heightSegmentCount"]) { + geometry.heightSegmentCount = [shape[@"heightSegmentCount"] intValue]; + } + [self addMaterials:geometry json:json sides:1]; + + return geometry; +} + ++ (SVGBezierPath *)svgStringToBezier:(NSString *)pathString { + NSArray * paths = [SVGBezierPath pathsFromSVGString:pathString]; + SVGBezierPath * fullPath; + for(SVGBezierPath *path in paths) { + if(!fullPath) { + fullPath = path; + } else { + [fullPath appendPath:path]; + } + } + return fullPath; +} + ++ (void)setChamferProfilePathSvg:(SCNShape *)geometry properties:(NSDictionary *)shape { + if (shape[@"chamferProfilePathSvg"]) { + + + SVGBezierPath * path = [self svgStringToBezier:shape[@"chamferProfilePathSvg"]]; + if(shape[@"chamferProfilePathFlatness"]) { + path.flatness = [shape[@"chamferProfilePathFlatness"] floatValue]; + } + // normalize path + CGRect boundingBox = path.bounds; + if(path.bounds.size.width !=0 && path.bounds.size.height != 0) { + CGFloat scaleX = 1/boundingBox.size.width; + CGFloat scaleY = scaleY = 1/boundingBox.size.height; + + CGAffineTransform transform = CGAffineTransformMakeScale(scaleX, scaleY); + [path applyTransform:transform]; + geometry.chamferProfile = path; + } else { + NSLog(@"Invalid chamferProfilePathFlatness"); + } + } + +} + ++ (SCNShape * )SCNShape:(id)json { + NSDictionary* shape = json[@"shape"]; + + + NSString * pathString = shape[@"pathSvg"]; + + SVGBezierPath * path = [self svgStringToBezier:pathString]; + + if (shape[@"pathFlatness"]) { + path.flatness = [shape[@"pathFlatness"] floatValue]; + } + + CGFloat extrusion = [shape[@"extrusion"] floatValue]; + SCNShape *geometry = [SCNShape shapeWithPath:path extrusionDepth:extrusion]; + + if (shape[@"chamferMode"]) { + geometry.chamferMode = (SCNChamferMode) [shape[@"chamferMode"] integerValue]; + } + if (shape[@"chamferRadius"]) { + geometry.chamferRadius = [shape[@"chamferRadius"] floatValue]; + } + if (shape[@"chamferProfilePathSvg"]) { + [self setChamferProfilePathSvg:geometry properties:shape]; + } + + [self addMaterials:geometry json:json sides:1]; + + return geometry; +} + + ++ (SCNTextNode *)SCNTextNode:(id)json { + // init SCNText + NSString *text = [NSString stringWithFormat:@"%@", json[@"text"]]; + if (!text) { + text = @"(null)"; + } + + NSDictionary* font = json[@"font"]; + CGFloat depth = [font[@"depth"] floatValue]; + if (!depth) { + depth = 0.0f; + } + CGFloat fontSize = [font[@"size"] floatValue]; + CGFloat size = fontSize / 12; + SCNText *scnText = [SCNText textWithString:text extrusionDepth:depth / size]; + + scnText.flatness = 0.1; + + // font + NSString *fontName = font[@"name"]; + if (fontName) { + scnText.font = [UIFont fontWithName:fontName size:12]; + } else { + scnText.font = [UIFont systemFontOfSize:12]; + } + + // chamfer + CGFloat chamfer = [font[@"chamfer"] floatValue]; + if (!chamfer) { + chamfer = 0.0f; + } + scnText.chamferRadius = chamfer / size; + + // material + // scnText.materials = @[face, face, border, border, border]; + [self addMaterials:scnText json:json sides:5]; + + + // SCNTextNode + SCNTextNode *textNode = [SCNNode nodeWithGeometry:scnText]; + textNode.name = [NSString stringWithFormat:@"%@", json[@"id"]]; + + + textNode.scale = SCNVector3Make(size, size, size); + + // position textNode + SCNVector3 min = SCNVector3Zero; + SCNVector3 max = SCNVector3Zero; + [textNode getBoundingBoxMin:&min max:&max]; + + textNode.position = SCNVector3Make(-(min.x + max.x) / 2 * size, + -(min.y + max.y) / 2 * size, + -(min.z + max.z) / 2 * size); + + return textNode; +} + + ++ (SCNLight *)SCNLight:(id)json { + SCNLight * light = [SCNLight light]; + [self setLightProperties:light properties:json]; + return light; +} + + ++ (void)setMaterialPropertyContents:(id)property material:(SCNMaterialProperty *)material { + + if (property[@"path"]) { + SCNMatrix4 m = SCNMatrix4Identity; + + // scenekit has an issue with indexed-colour png's on some devices, so we redraw the image. See for more details: https://stackoverflow.com/questions/40058359/scenekit-some-textures-have-a-red-hue/45824190#45824190 + + UIImage *correctedImage; + UIImage *inputImage = [UIImage imageNamed:property[@"path"]]; + CGFloat width = inputImage.size.width; + CGFloat height = inputImage.size.height; + + UIGraphicsBeginImageContext(inputImage.size); + [inputImage drawInRect:(CGRectMake(0, 0, width, height))]; + correctedImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + material.contents = correctedImage; + + + if (property[@"wrapS"]) { + material.wrapS = (SCNWrapMode) [property[@"wrapS"] integerValue]; + } + + if (property[@"wrapT"]) { + material.wrapT = (SCNWrapMode) [property[@"wrapT"] integerValue]; + } + + if (property[@"wrap"]) { + material.wrapT = (SCNWrapMode) [property[@"wrapT"] integerValue]; + material.wrapS = (SCNWrapMode) [property[@"wrapS"] integerValue]; + } + + if (property[@"scale"]) { + float x = [property[@"scale"][@"x"] floatValue]; + float y = [property[@"scale"][@"y"] floatValue]; + float z = [property[@"scale"][@"z"] floatValue]; + + m = SCNMatrix4Mult(m, SCNMatrix4MakeScale(x, y, z)); + } + + if (property[@"rotation"]) { + float a = [property[@"rotation"][@"angle"] floatValue]; + float x = [property[@"rotation"][@"x"] floatValue]; + float y = [property[@"rotation"][@"y"] floatValue]; + float z = [property[@"rotation"][@"z"] floatValue]; + + m = SCNMatrix4Mult(m, SCNMatrix4MakeRotation(a, x, y, z)); + } + + if (property[@"translation"]) { + float x = [property[@"translation"][@"x"] floatValue]; + float y = [property[@"translation"][@"y"] floatValue]; + float z = [property[@"translation"][@"z"] floatValue]; + + m = SCNMatrix4Mult(m, SCNMatrix4MakeTranslation(x, y, z)); + } + + material.contentsTransform = m; + + + } else if (property[@"color"]) { + material.contents = [self UIColor:property[@"color"]]; + } + if (property[@"intensity"]) { + material.intensity = [property[@"intensity"] floatValue]; + } +} + ++ (void)setMaterialProperties:(SCNMaterial *)material properties:(id)json { + if (json[@"doubleSided"]) { + material.doubleSided = [json[@"doubleSided"] boolValue]; + } else { + material.doubleSided = YES; + } + + if (json[@"blendMode"]) { + material.blendMode = (SCNBlendMode) [json[@"blendMode"] integerValue]; + } + + if (json[@"transparencyMode"]) { + material.transparencyMode = (SCNTransparencyMode) [json[@"transparencyMode"] integerValue]; + } + + if (json[@"lightingModel"]) { + material.lightingModelName = json[@"lightingModel"]; + } + + if (json[@"diffuse"]) { + [self setMaterialPropertyContents:json[@"diffuse"] material:material.diffuse]; + } + + if (json[@"normal"]) { + [self setMaterialPropertyContents:json[@"normal"] material:material.normal]; + } + + if (json[@"displacement"]) { + [self setMaterialPropertyContents:json[@"displacement"] material:material.displacement]; + } + + if (json[@"specular"]) { + [self setMaterialPropertyContents:json[@"specular"] material:material.specular]; + } + + if (json[@"transparency"]) { + material.transparency = [json[@"transparency"] floatValue]; + } + + if (json[@"metalness"]) { + material.lightingModelName = SCNLightingModelPhysicallyBased; + material.metalness.contents = @([json[@"metalness"] floatValue]); + } + + if (json[@"roughness"]) { + material.lightingModelName = SCNLightingModelPhysicallyBased; + material.roughness.contents = @([json[@"roughness"] floatValue]); + } + + if(json[@"shaders"] ) { + material.shaderModifiers = json[@"shaders"]; + } + + if(json[@"writesToDepthBuffer"] ) { + material.writesToDepthBuffer = [json[@"writesToDepthBuffer"] boolValue]; + } + + if(json[@"colorBufferWriteMask"] ) { + material.colorBufferWriteMask = [json[@"colorBufferWriteMask"] integerValue]; + } + + if(json[@"fillMode"] ) { + material.fillMode = [json[@"fillMode"] integerValue]; + } + + if(json[@"doubleSided"]) { + material.doubleSided = [json[@"doubleSided"] boolValue]; + } + + if(json[@"litPerPixel"]) { + material.litPerPixel = [json[@"litPerPixel"] boolValue]; + } +} + ++ (void)setNodeProperties:(SCNNode *)node properties:(id)json { + + if (json[@"categoryBitMask"]) { + node.categoryBitMask = [json[@"categoryBitMask"] integerValue]; + } + if (json[@"renderingOrder"]) { + node.renderingOrder = [json[@"renderingOrder"] integerValue]; + } + if (json[@"castsShadow"]) { + node.castsShadow = [json[@"castsShadow"] boolValue]; + } + if (json[@"constraint"]) { + SCNBillboardConstraint *constraint = [SCNBillboardConstraint billboardConstraint]; + constraint.freeAxes = [json[@"constraint"] integerValue]; + node.constraints = @[constraint]; + } + if(json[@"transition"]) { + NSDictionary * transition =json[@"transition"]; + if(transition[@"duration"]) { + [SCNTransaction setAnimationDuration:[transition[@"duration"] floatValue]]; + } else { + [SCNTransaction setAnimationDuration:0.0]; + } + + } else { + [SCNTransaction setAnimationDuration:0.0]; + } + if (json[@"position"]) { + node.position = [self SCNVector3:json[@"position"]]; + } + + if (json[@"scale"]) { + + CGFloat scale = [json[@"scale"] floatValue]; + node.scale = SCNVector3Make(scale, scale, scale); + + } + + if (json[@"eulerAngles"]) { + node.eulerAngles = [self SCNVector3:json[@"eulerAngles"]]; + } + + if (json[@"orientation"]) { + node.orientation = [self SCNVector4:json[@"orientation"]]; + } + + if (json[@"rotation"]) { + node.rotation = [self SCNVector4:json[@"rotation"]]; + } + + if (json[@"opacity"]) { + node.opacity = [json[@"opacity"] floatValue]; + } +} + + ++ (NSSet *) specialShapeProperties { + return [[NSSet alloc] initWithArray: + @[@"pathSvg", @"chamferProfilePathSvg"]]; +} + + ++ (void)setShapeProperties:(SCNGeometry *)geometry properties:(id)shapeJson { + + // most properties are strings + for (NSString* key in shapeJson) { + if(![self.specialShapeProperties containsObject:key]) { + id value = [NSNumber numberWithFloat:[shapeJson[key] floatValue]]; + [geometry setValue:value forKey:key]; + } + } + + if([geometry isKindOfClass:[SCNShape class]]) { + SCNShape * shapeGeometry = (SCNShape * ) geometry; + if(shapeJson[@"pathSvg"]) { + NSString * pathString = shapeJson[@"pathSvg"]; + SVGBezierPath * path = [self svgStringToBezier:pathString]; + if (shapeJson[@"pathFlatness"]) { + path.flatness = [shapeJson[@"pathFlatness"] floatValue]; + } + shapeGeometry.path = path; + } + if (shapeJson[@"chamferProfilePathSvg"]) { + [self setChamferProfilePathSvg: shapeGeometry properties:shapeJson]; + } + + } +} + + + ++ (void)setLightProperties:(SCNLight *)light properties:(id)json { + if (json[@"lightCategoryBitMask"]) { + light.categoryBitMask = [json[@"lightCategoryBitMask"] integerValue]; + } + if(json[@"type"]) { + light.type = json[@"type"]; + } + if(json[@"color"]) { + light.color = (__bridge id _Nonnull)([RCTConvert CGColor:json[@"color"]]); + } + if(json[@"temperature"]) { + light.temperature = [json[@"temperature"] floatValue]; + } + + if(json[@"intensity"]) { + light.intensity = [json[@"intensity"] floatValue]; + } + + if(json[@"attenuationStartDistance"]) { + light.attenuationStartDistance = [json[@"attenuationStartDistance"] floatValue]; + } + + if(json[@"attenuationEndDistance"]) { + light.attenuationEndDistance = [json[@"attenuationEndDistance"] floatValue]; + } + + if(json[@"spotInnerAngle"]) { + light.spotInnerAngle = [json[@"spotInnerAngle"] floatValue]; + } + + if(json[@"spotOuterAngle"]) { + light.spotOuterAngle = [json[@"spotOuterAngle"] floatValue]; + } + + if(json[@"castsShadow"]) { + light.castsShadow = [json[@"castsShadow"] boolValue]; + } + + if(json[@"shadowRadius"]) { + light.shadowRadius = [json[@"shadowRadius"] floatValue]; + } + + if(json[@"shadowColor"]) { + light.shadowColor = (__bridge id _Nonnull)([RCTConvert CGColor:json[@"shadowColor"]]); + } + + + if(json[@"shadowSampleCount"]) { + light.shadowSampleCount = [json[@"shadowSampleCount"] integerValue]; + } + + if(json[@"shadowBias"]) { + light.shadowBias = [json[@"shadowBias"] floatValue]; + } + + if(json[@"shadowMode"]) { + light.shadowMode = [json[@"shadowMode"] integerValue]; + } + if(json[@"orthographicScale"]) { + light.orthographicScale = [json[@"orthographicScale"] floatValue]; + } + + if(json[@"zFar"]) { + light.zFar = [json[@"zFar"] floatValue]; + } + + if(json[@"zNear"]) { + light.zNear = [json[@"zNear"] floatValue]; + } +} + ++ (ARPlaneDetection)ARPlaneDetection:(id)number { + return (ARPlaneDetection) [number integerValue]; +} + + + +@end diff --git a/ios/color-grabber/color-grabber.h b/ios/color-grabber/color-grabber.h new file mode 100644 index 00000000..69cb1eaf --- /dev/null +++ b/ios/color-grabber/color-grabber.h @@ -0,0 +1,13 @@ +// +// color-grabber.h +// + +#import +#import +#import + +@interface ColorGrabber : NSObject + ++ (instancetype)sharedInstance; +- (NSArray *)getColorsFromImage:(UIImage *)image options:(NSDictionary *)options; +@end diff --git a/ios/color-grabber/color-grabber.m b/ios/color-grabber/color-grabber.m new file mode 100644 index 00000000..3c334535 --- /dev/null +++ b/ios/color-grabber/color-grabber.m @@ -0,0 +1,250 @@ + +#import +#import +#import "color-grabber.h" + +#define CLAMP(x, low, high) ({\ +__typeof__(x) __x = (x); \ +__typeof__(low) __low = (low);\ +__typeof__(high) __high = (high);\ +__x > __high ? __high : (__x < __low ? __low : __x);\ +}) + +@implementation ColorGrabber + + + + +RCT_EXPORT_MODULE(); + +- (NSArray *)getColorsFromImage:(UIImage *)image options:(NSDictionary *)options { + + float dimension = [RCTConvert float:options[@"dimension"]]; // 4 + float flexibility = [RCTConvert float:options[@"flexibility"]]; // 5; + float range = [RCTConvert float:options[@"range"]]; // 40; + + NSMutableArray * colours = [NSMutableArray new]; + CGImageRef imageRef = [image CGImage]; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + unsigned char *rawData = (unsigned char*) calloc(dimension * dimension * 4, sizeof(unsigned char)); + NSUInteger bytesPerPixel = 4; + NSUInteger bytesPerRow = bytesPerPixel * dimension; + NSUInteger bitsPerComponent = 8; + CGContextRef context = CGBitmapContextCreate(rawData, dimension, dimension, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); + CGColorSpaceRelease(colorSpace); + CGContextDrawImage(context, CGRectMake(0, 0, dimension, dimension), imageRef); + CGContextRelease(context); + + float x = 0; + float y = 0; + for (int n = 0; n<(dimension*dimension); n++){ + + int index = (bytesPerRow * y) + x * bytesPerPixel; + int red = rawData[index]; + int green = rawData[index + 1]; + int blue = rawData[index + 2]; + int alpha = rawData[index + 3]; + NSArray * a = [NSArray arrayWithObjects:[NSString stringWithFormat:@"%i",red],[NSString stringWithFormat:@"%i",green],[NSString stringWithFormat:@"%i",blue],[NSString stringWithFormat:@"%i",alpha], nil]; + [colours addObject:a]; + + y++; + if (y==dimension){ + y=0; + x++; + } + } + free(rawData); + + // Add some colour flexibility (adds more colours either side of the colours in the image) + NSArray * copyColours = [NSArray arrayWithArray:colours]; + NSMutableArray * flexibleColours = [NSMutableArray new]; + + float flexFactor = flexibility * 2 + 1; + float factor = flexFactor * flexFactor * 3; //(r,g,b) == *3 + for (int n = 0; n<(dimension * dimension); n++){ + + NSArray * pixelColours = copyColours[n]; + NSMutableArray * reds = [NSMutableArray new]; + NSMutableArray * greens = [NSMutableArray new]; + NSMutableArray * blues = [NSMutableArray new]; + + for (int p = 0; p<3; p++){ + + NSString * rgbStr = pixelColours[p]; + int rgb = [rgbStr intValue]; + + for (int f = -flexibility; f= ranged_r-range && r<= ranged_r+range){ + if (g>= ranged_g-range && g<= ranged_g+range){ + if (b>= ranged_b-range && b<= ranged_b+range){ + exclude = true; + } + } + } + } + + if (!exclude){ [ranges addObject:key]; } + } + + + + // If you want percentages to colours continue below + NSMutableDictionary * temp = [NSMutableDictionary new]; + float totalCount = 0.0f; + for (NSString * rangeKey in ranges){ + NSNumber * count = colourCounter[rangeKey]; + totalCount += [count intValue]; + temp[rangeKey]=count; + } + + // Set percentages + NSMutableArray * colors = [NSMutableArray new]; + for (NSString * key in temp){ + float count = [temp[key] floatValue]; + float percentage = count/totalCount; + //NSLog(@"%f",percentage); + NSArray * rgb = [key componentsSeparatedByString:@","]; + float r = [rgb[0] floatValue]; + float g = [rgb[1] floatValue]; + float b = [rgb[2] floatValue]; + + + + [colors addObject:@{ + @"color": @{ + @"r": @(r), + @"g": @(g), + @"b": @(b) + }, + @"percentage":@(percentage) + }]; + + } + return colors; +} + +RCT_EXPORT_METHOD(getColors:(NSString *)path options:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) +{ + + NSURL* aURL = [NSURL URLWithString:path]; + + if([path hasPrefix: @"/"]) { + UIImage *image = [UIImage imageWithContentsOfFile:path]; + + NSArray * colors = [self getColorsFromImage:image options:options]; + resolve(colors); + } else { + ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; + + [library assetForURL:aURL resultBlock:^(ALAsset *asset) { + UIImage *image = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullScreenImage] scale:0.5 orientation:UIImageOrientationUp]; + NSArray * colors = [self getColorsFromImage:image options:options]; + resolve(colors); + + } + failureBlock:^(NSError *error) { + reject(@"asset error", @"cant't get colors from asset", error ); + }]; + } + + + + +} + +- (NSString *)hexStringFromColor:(UIColor *)color { + const CGFloat *components = CGColorGetComponents(color.CGColor); + + CGFloat r = components[0]; + CGFloat g = components[1]; + CGFloat b = components[2]; + + return [NSString stringWithFormat:@"#%02lX%02lX%02lX", + lroundf(r * 255), + lroundf(g * 255), + lroundf(b * 255)]; +} + + ++ (instancetype)sharedInstance { + static ColorGrabber *instance = nil; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + if (instance == nil) { + instance = [[self alloc] init]; + } + }); + return instance; +} + + +@end + + + diff --git a/ios/components/ARGeosManager.h b/ios/components/ARGeosManager.h new file mode 100644 index 00000000..76595137 --- /dev/null +++ b/ios/components/ARGeosManager.h @@ -0,0 +1,13 @@ +// +// ARGeosManager.h +// RCTARKit +// +// Created by Zehao Li on 9/28/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +#import + +@interface ARGeosManager : NSObject + +@end diff --git a/ios/components/ARGeosManager.m b/ios/components/ARGeosManager.m new file mode 100644 index 00000000..d5d7d9e3 --- /dev/null +++ b/ios/components/ARGeosManager.m @@ -0,0 +1,105 @@ +// +// ARGeosManager.m +// RCTARKit +// +// Created by Zehao Li on 9/28/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +#import "ARGeosManager.h" +#import "RCTARKitNodes.h" + +@implementation ARGeosManager + +RCT_EXPORT_MODULE() + + +RCT_EXPORT_METHOD(addBox:(SCNBox *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject ) { + node.geometry = geometry; + [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId]; + resolve(nil); +} + +RCT_EXPORT_METHOD(addGroup:(id)bla node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId]; + resolve(nil); +} + +RCT_EXPORT_METHOD(addSphere:(SCNSphere *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + node.geometry = geometry; + [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId]; + resolve(nil); +} + +RCT_EXPORT_METHOD(addCylinder:(SCNCylinder *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + node.geometry = geometry; + [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId]; + resolve(nil); +} + +RCT_EXPORT_METHOD(addCone:(SCNCone *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + node.geometry = geometry; + [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId]; + resolve(nil); +} + +RCT_EXPORT_METHOD(addPyramid:(SCNPyramid *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + node.geometry = geometry; + [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId]; + resolve(nil); +} + +RCT_EXPORT_METHOD(addTube:(SCNTube *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + node.geometry = geometry; + [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId]; + resolve(nil); +} + +RCT_EXPORT_METHOD(addTorus:(SCNTorus *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + node.geometry = geometry; + [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId]; + resolve(nil); +} + +RCT_EXPORT_METHOD(addCapsule:(SCNCapsule *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + node.geometry = geometry; + [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId]; + resolve(nil); +} + +RCT_EXPORT_METHOD(addPlane:(SCNPlane *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + node.geometry = geometry; + [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId]; + resolve(nil); +} + +RCT_EXPORT_METHOD(addShape:(SCNShape *)geometry node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + node.geometry = geometry; + [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId]; + resolve(nil); +} + +RCT_EXPORT_METHOD(addLight:(SCNLight *)light node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + node.light = light; + [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId]; + resolve(nil); +} + + +RCT_EXPORT_METHOD(unmount:(NSString *)identifier) { + //NSLog(@"unmounting node: %@ ", identifier); + [[RCTARKitNodes sharedInstance] removeNode:identifier]; +} + +RCT_EXPORT_METHOD(updateNode:(NSString *)identifier properties:(NSDictionary *) properties resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + //NSLog(@"updating node: %@ ", identifier); + bool success = [[RCTARKitNodes sharedInstance] updateNode:identifier properties:properties]; + if(success) { + resolve(nil); + } else { + reject(@"updateNodeError", @"could not update node", nil); + } +} + +@end + diff --git a/ios/components/ARModelManager.h b/ios/components/ARModelManager.h new file mode 100644 index 00000000..7966fd1c --- /dev/null +++ b/ios/components/ARModelManager.h @@ -0,0 +1,13 @@ +// +// ARModelManager.h +// RCTARKit +// +// Created by Zehao Li on 8/12/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +#import + +@interface ARModelManager : NSObject + +@end diff --git a/ios/components/ARModelManager.m b/ios/components/ARModelManager.m new file mode 100644 index 00000000..c7cb54ce --- /dev/null +++ b/ios/components/ARModelManager.m @@ -0,0 +1,72 @@ +// +// ARModelManager.m +// RCTARKit +// +// Created by Zehao Li on 8/12/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +#import "ARModelManager.h" +#import "RCTARKitNodes.h" +#import "RCTARKitIO.h" +#import "RCTConvert+ARKit.h" + +@implementation ARModelManager + +RCT_EXPORT_MODULE() + +RCT_EXPORT_METHOD(mount:(NSDictionary *)property node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId) { + //NSLog(@"mounting node: %@ ", node.name); + // we need to mount first, otherwise, if the loading of the model is slow, it will be registered too late + [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId]; + // we need to do the model loading in its own queue, otherwise it can block, so that react-to-native-calls get out of order + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSDictionary *model = property[@"model"]; + CGFloat scale = 1; + if(model[@"scale"]) { + // deprecated + scale = [model[@"scale"] floatValue]; + } + + NSString *path = [NSString stringWithFormat:@"%@", model[@"file"]]; + //NSLog(@"mounting model: %@ %@", node.name, path); + SCNNode *modelNode = [[RCTARKitIO sharedInstance] loadModel:path nodeName:model[@"node"] withAnimation:YES]; + modelNode.scale = SCNVector3Make(scale, scale, scale); + // transfer some properties to modeNode like "castsShadow" + modelNode.castsShadow = node.castsShadow; + + + NSDictionary* materialJson; + if(property[@"material"] ) { + materialJson = property[@"material"]; + } + + + if(materialJson) { + for(id idx in modelNode.geometry.materials) { + SCNMaterial* material = (SCNMaterial* )idx; + [RCTConvert setMaterialProperties:material properties:materialJson]; + } + } + + for(id idx in modelNode.childNodes) { + // iterate over all childnodes and apply shaders + + SCNNode* childNode = (SCNNode *)idx; + childNode.castsShadow = node.castsShadow; + if(materialJson) { + for(id idx in childNode.geometry.materials) { + SCNMaterial* material = (SCNMaterial* )idx; + [RCTConvert setMaterialProperties:material properties:materialJson]; + } + } + + } + [node addChildNode:modelNode]; + //NSLog(@"load model finished: %@", node.name); + }); + + +} + +@end diff --git a/ios/components/ARTextManager.h b/ios/components/ARTextManager.h new file mode 100644 index 00000000..59b01a75 --- /dev/null +++ b/ios/components/ARTextManager.h @@ -0,0 +1,13 @@ +// +// ARTextManager.h +// RCTARKit +// +// Created by Zehao Li on 8/12/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +#import + +@interface ARTextManager : NSObject + +@end diff --git a/ios/components/ARTextManager.m b/ios/components/ARTextManager.m new file mode 100644 index 00000000..54f00884 --- /dev/null +++ b/ios/components/ARTextManager.m @@ -0,0 +1,22 @@ +// +// ARTextManager.m +// RCTARKit +// +// Created by Zehao Li on 8/12/17. +// Copyright © 2017 HippoAR. All rights reserved. +// + +#import "ARTextManager.h" +#import "RCTARKitNodes.h" +#import "RCTConvert+ARKit.h" + +@implementation ARTextManager + +RCT_EXPORT_MODULE() + +RCT_EXPORT_METHOD(mount:(SCNTextNode *)textNode node:(SCNNode *)node frame:(NSString *)frame parentId:(NSString *)parentId) { + [node addChildNode:textNode]; + [[RCTARKitNodes sharedInstance] addNodeToScene:node inReferenceFrame:frame withParentId:parentId]; +} + +@end diff --git a/ios/components/RCTARKitSpriteView.h b/ios/components/RCTARKitSpriteView.h new file mode 100644 index 00000000..aa6813b4 --- /dev/null +++ b/ios/components/RCTARKitSpriteView.h @@ -0,0 +1,22 @@ +// +// RCTARKitSpriteView.h +// RCTARKit +// +// Created by Marco Wettstein on 04.03.18. +// Copyright © 2018 HippoAR. All rights reserved. +// + +#import +#import "RCTARKit.h" +#import +#import "RCTARKitDelegate.h" + +@interface RCTARKitSpriteView : RCTView + +@property (nonatomic, assign) SCNVector3 position3D; +@property (nonatomic, assign) float transitionDuration; + +@end + + + diff --git a/ios/components/RCTARKitSpriteView.m b/ios/components/RCTARKitSpriteView.m new file mode 100644 index 00000000..44813438 --- /dev/null +++ b/ios/components/RCTARKitSpriteView.m @@ -0,0 +1,52 @@ +// +// RCTARKitSpriteView.m +// RCTARKit +// +// Created by Marco Wettstein on 04.03.18. +// Copyright © 2018 HippoAR. All rights reserved. +// + +#import + + +#import "RCTARKitSpriteView.h" +#import "RCTConvert+ARKit.h" +#import +#import + +@implementation RCTARKitSpriteView { + +} + + + +- (CGAffineTransform)getTransform { + SCNVector3 point = [[ARKit sharedInstance] projectPoint:self.position3D]; + + // the sprite is behind the camera so push it off screen + float yTransform = point.z < 1 ? point.y : 10000; + + CGAffineTransform t = CGAffineTransformMakeTranslation(point.x, yTransform); + return t; +} + +- (void)didMoveToSuperview { + // set it once + CGAffineTransform t = [self getTransform]; + [self setTransform:t]; +} + +- (void)renderer:(id)renderer updateAtTime:(NSTimeInterval)time { + [self performSelectorOnMainThread:@selector(setTransformByProject) withObject:nil waitUntilDone:NO]; + +} + +- (void)setTransformByProject { + CGAffineTransform t = [self getTransform]; + [UIView beginAnimations:nil context:NULL]; + [UIView setAnimationDuration:self.transitionDuration]; + [self setTransform:t]; + [UIView commitAnimations]; +} + +@end diff --git a/ios/components/RCTARKitSpriteViewManager.h b/ios/components/RCTARKitSpriteViewManager.h new file mode 100644 index 00000000..5a705b90 --- /dev/null +++ b/ios/components/RCTARKitSpriteViewManager.h @@ -0,0 +1,5 @@ +#import + +@interface RCTARKitSpriteViewManager : RCTViewManager + +@end diff --git a/ios/components/RCTARKitSpriteViewManager.m b/ios/components/RCTARKitSpriteViewManager.m new file mode 100644 index 00000000..39add846 --- /dev/null +++ b/ios/components/RCTARKitSpriteViewManager.m @@ -0,0 +1,46 @@ +#import "RCTARKitSpriteViewManager.h" +#import "RCTARKitSpriteView.h" +#import +#import +#import "RCTARKit.h" + +@implementation RCTARKitSpriteViewManager + +RCT_EXPORT_MODULE(); + +@synthesize bridge = _bridge; + +RCT_EXPORT_VIEW_PROPERTY(position3D, SCNVector3) +RCT_EXPORT_VIEW_PROPERTY(transitionDuration, float) + +- (UIView *)view +{ + UIView * view = [[RCTARKitSpriteView alloc] init]; + + return view; +} + + +RCT_EXPORT_METHOD(startAnimation:(nonnull NSNumber *)reactTag) +{ + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { + RCTARKitSpriteView *view = viewRegistry[reactTag]; + if ([view isKindOfClass:[RCTARKitSpriteView class]]) { + [[ARKit sharedInstance] addRendererDelegates:view]; + } + }]; +} + +RCT_EXPORT_METHOD(stopAnimation:(nonnull NSNumber *)reactTag) +{ + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { + RCTARKitSpriteView *view = viewRegistry[reactTag]; + if ([view isKindOfClass:[RCTARKitSpriteView class]]) { + [[ARKit sharedInstance] removeRendererDelegates:view]; + } + }]; +} + + + +@end diff --git a/lib/colorUtils.js b/lib/colorUtils.js new file mode 100644 index 00000000..d41e81e9 --- /dev/null +++ b/lib/colorUtils.js @@ -0,0 +1,43 @@ +// kudos to to https://github.com/jverhoelen/camanjs-whitebalance/blob/master/src/caman.whitebalance.js +import { NativeModules } from 'react-native'; + +const ARKitManager = NativeModules.ARKitManager; + +export const colorTemperatureToRgb = temperature => { + const m = global.Math; + const temp = temperature / 100; + let r; + let g; + let b; + + if (temp <= 66) { + r = 255; + g = m.min(m.max(99.4708025861 * m.log(temp) - 161.1195681661, 0), 255); + } else { + r = m.min(m.max(329.698727446 * m.pow(temp - 60, -0.1332047592), 0), 255); + g = m.min(m.max(288.1221695283 * m.pow(temp - 60, -0.0755148492), 0), 255); + } + + if (temp >= 66) { + b = 255; + } else if (temp <= 19) { + b = 0; + } else { + b = temp - 10; + b = m.min(m.max(138.5177312231 * m.log(b) - 305.0447927307, 0), 255); + } + + return { + r, + g, + b, + }; +}; +export const whiteBalanceWithTemperature = ({ r, g, b }, temperature) => { + const temperatureRgb = colorTemperatureToRgb(temperature); + return { + r: r * 255 / temperatureRgb.r, + g: g * 255 / temperatureRgb.g, + b: b * 255 / temperatureRgb.b, + }; +}; diff --git a/lib/pickColors.js b/lib/pickColors.js new file mode 100644 index 00000000..2d83a1fc --- /dev/null +++ b/lib/pickColors.js @@ -0,0 +1,65 @@ +import { NativeModules } from 'react-native'; + +import { whiteBalanceWithTemperature } from './colorUtils'; + +const ARKitManager = NativeModules.ARKitManager; + +const doWhiteBalance = async (colors, { includeRawColors }) => { + const lightEstimation = await ARKitManager.getCurrentLightEstimation(); + + if (!lightEstimation) { + return colors; + } + + return colors.map(({ color, ...p }) => ({ + color: whiteBalanceWithTemperature( + color, + lightEstimation.ambientColorTemperature, + ), + ...p, + ...(includeRawColors ? { colorRaw: color } : {}), + })); +}; +export const pickColorsFromFile = async ( + filePath, + { + whiteBalance = true, + includeRawColors = false, + // color grabber options, currently undocumented + range = 40, + dimension = 4, + flexibility = 5, + } = {}, +) => { + const colors = await ARKitManager.pickColorsRawFromFile(filePath, { + range, + dimension, + flexibility, + }); + if (!whiteBalance) { + return colors; + } + return doWhiteBalance(colors, { includeRawColors }); +}; +export const pickColors = async ( + { + whiteBalance = true, + includeRawColors = false, + selection = null, + // color grabber options, currently undocumented + range = 40, + dimension = 4, + flexibility = 5, + } = {}, +) => { + const colors = await ARKitManager.pickColorsRaw({ + selection, + range, + dimension, + flexibility, + }); + if (!whiteBalance) { + return colors; + } + return doWhiteBalance(colors, { includeRawColors }); +}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..8ea2eda5 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2490 @@ +{ + "name": "react-native-arkit", + "version": "0.8.0-beta.2", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@panter/react-animation-frame": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@panter/react-animation-frame/-/react-animation-frame-0.3.7.tgz", + "integrity": "sha1-MaDXaD5KjS4bKf9RLK02UTvUPQA=", + "requires": { + "react": "15.4.2" + }, + "dependencies": { + "react": { + "version": "15.4.2", + "resolved": "https://registry.npmjs.org/react/-/react-15.4.2.tgz", + "integrity": "sha1-QfeZGyYYU5K6m66WyIiefgGDl+8=", + "requires": { + "fbjs": "0.8.15", + "loose-envify": "1.3.1", + "object-assign": "4.1.1" + } + } + } + }, + "acorn": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.2.tgz", + "integrity": "sha512-o96FZLJBPY1lvTuJylGA9Bk3t/GKPPJG8H0ydQQl01crzwJgspa4AEIq/pVTXigmK0PHVQhiAtn8WMBLL9D2WA==", + "dev": true + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "3.3.0" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, + "ajv": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.2.tgz", + "integrity": "sha1-R8aNaehvXZUxA7AHSpQw3GPaXjk=", + "dev": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "json-schema-traverse": "0.3.1", + "json-stable-stringify": "1.0.1" + } + }, + "ajv-keywords": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", + "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", + "dev": true + }, + "ansi-escapes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.0.0.tgz", + "integrity": "sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "argparse": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", + "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, + "aria-query": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-0.7.0.tgz", + "integrity": "sha512-/r2lHl09V3o74+2MLKEdewoj37YZqiQZnfen1O4iNlrOjUgeKuu1U2yF3iKh6HJxqF+OXkLMfQv65Z/cvxD6vA==", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7" + } + }, + "array-includes": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "dev": true, + "requires": { + "define-properties": "1.1.2", + "es-abstract": "1.8.2" + } + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "1.0.3" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "dev": true + }, + "axobject-query": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-0.1.0.tgz", + "integrity": "sha1-YvWdvFnJ+SQnWco0mWDnov48NsA=", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, + "babel-eslint": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-7.2.3.tgz", + "integrity": "sha1-sv4tgBJkcPXBlELcdXJTqJdxCCc=", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0" + } + }, + "babel-helper-builder-react-jsx": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz", + "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "esutils": "2.0.2" + } + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "6.24.1", + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "dev": true, + "requires": { + "babel-helper-function-name": "6.24.1", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "lodash": "4.17.4" + } + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "dev": true, + "requires": { + "babel-helper-get-function-arity": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "dev": true, + "requires": { + "babel-helper-optimise-call-expression": "6.24.1", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-react-transform": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-react-transform/-/babel-plugin-react-transform-2.0.2.tgz", + "integrity": "sha1-UVu/qZaJOYEULZCx+bFjXeKZUQk=", + "dev": true, + "requires": { + "lodash": "4.17.4" + } + }, + "babel-plugin-syntax-async-functions": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", + "dev": true + }, + "babel-plugin-syntax-class-properties": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", + "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=", + "dev": true + }, + "babel-plugin-syntax-flow": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", + "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=", + "dev": true + }, + "babel-plugin-syntax-jsx": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=", + "dev": true + }, + "babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", + "dev": true + }, + "babel-plugin-syntax-trailing-function-commas": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", + "dev": true + }, + "babel-plugin-transform-class-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", + "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", + "dev": true, + "requires": { + "babel-helper-function-name": "6.24.1", + "babel-plugin-syntax-class-properties": "6.13.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" + } + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "lodash": "4.17.4" + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "dev": true, + "requires": { + "babel-helper-define-map": "6.26.0", + "babel-helper-function-name": "6.24.1", + "babel-helper-optimise-call-expression": "6.24.1", + "babel-helper-replace-supers": "6.24.1", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "dev": true, + "requires": { + "babel-helper-function-name": "6.24.1", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz", + "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=", + "dev": true, + "requires": { + "babel-plugin-transform-strict-mode": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "dev": true, + "requires": { + "babel-helper-call-delegate": "6.24.1", + "babel-helper-get-function-arity": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-flow-strip-types": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", + "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", + "dev": true, + "requires": { + "babel-plugin-syntax-flow": "6.18.0", + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-object-assign": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-assign/-/babel-plugin-transform-object-assign-6.22.0.tgz", + "integrity": "sha1-+Z0vZvGgsNSY40bFNZaEdAyqILo=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-object-rest-spread": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", + "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", + "dev": true, + "requires": { + "babel-plugin-syntax-object-rest-spread": "6.13.0", + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-react-display-name": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz", + "integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-react-jsx": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz", + "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=", + "dev": true, + "requires": { + "babel-helper-builder-react-jsx": "6.26.0", + "babel-plugin-syntax-jsx": "6.18.0", + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-react-jsx-source": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz", + "integrity": "sha1-ZqwSFT9c0tF7PBkmj0vwGX9E7NY=", + "dev": true, + "requires": { + "babel-plugin-syntax-jsx": "6.18.0", + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "dev": true, + "requires": { + "regenerator-transform": "0.10.1" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-preset-react-native": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-react-native/-/babel-preset-react-native-2.1.0.tgz", + "integrity": "sha1-kBPr2C2hyIECv1iIEP9Z4gnKK4o=", + "dev": true, + "requires": { + "babel-plugin-check-es2015-constants": "6.22.0", + "babel-plugin-react-transform": "2.0.2", + "babel-plugin-syntax-async-functions": "6.13.0", + "babel-plugin-syntax-class-properties": "6.13.0", + "babel-plugin-syntax-flow": "6.18.0", + "babel-plugin-syntax-jsx": "6.18.0", + "babel-plugin-syntax-trailing-function-commas": "6.22.0", + "babel-plugin-transform-class-properties": "6.24.1", + "babel-plugin-transform-es2015-arrow-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoping": "6.26.0", + "babel-plugin-transform-es2015-classes": "6.24.1", + "babel-plugin-transform-es2015-computed-properties": "6.24.1", + "babel-plugin-transform-es2015-destructuring": "6.23.0", + "babel-plugin-transform-es2015-for-of": "6.23.0", + "babel-plugin-transform-es2015-function-name": "6.24.1", + "babel-plugin-transform-es2015-literals": "6.22.0", + "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", + "babel-plugin-transform-es2015-parameters": "6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", + "babel-plugin-transform-es2015-spread": "6.22.0", + "babel-plugin-transform-es2015-template-literals": "6.22.0", + "babel-plugin-transform-flow-strip-types": "6.22.0", + "babel-plugin-transform-object-assign": "6.22.0", + "babel-plugin-transform-object-rest-spread": "6.26.0", + "babel-plugin-transform-react-display-name": "6.25.0", + "babel-plugin-transform-react-jsx": "6.24.1", + "babel-plugin-transform-react-jsx-source": "6.22.0", + "babel-plugin-transform-regenerator": "6.26.0", + "react-transform-hmr": "1.0.4" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" + }, + "dependencies": { + "core-js": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", + "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=", + "dev": true + } + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "lodash": "4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "debug": "2.6.9", + "globals": "9.18.0", + "invariant": "2.2.2", + "lodash": "4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "esutils": "2.0.2", + "lodash": "4.17.4", + "to-fast-properties": "1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "color-convert": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", + "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "typedarray": "0.0.6" + } + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-react-class": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.0.tgz", + "integrity": "sha1-q0SEl8JlZuHilBPogyB9V8/nvtQ=", + "dev": true, + "requires": { + "fbjs": "0.8.15", + "loose-envify": "1.3.1", + "object-assign": "4.1.1" + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" + } + }, + "damerau-levenshtein": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz", + "integrity": "sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "dev": true, + "requires": { + "foreach": "2.0.5", + "object-keys": "1.0.11" + } + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "5.0.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.0", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.6.2" + } + }, + "doctrine": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", + "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=", + "dev": true, + "requires": { + "esutils": "2.0.2", + "isarray": "1.0.0" + } + }, + "dom-walk": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", + "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=", + "dev": true + }, + "emoji-regex": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.5.1.tgz", + "integrity": "sha512-PAHp6TxrCy7MGMFidro8uikr+zlJJKJ/Q6mm2ExZ7HwkyR9lSVFfE3kt36qcwa24BQL7y0G9axycGjK1A/0uNQ==", + "dev": true + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "0.4.19" + } + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "dev": true, + "requires": { + "is-arrayish": "0.2.1" + } + }, + "es-abstract": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.8.2.tgz", + "integrity": "sha512-dvhwFL3yjQxNNsOWx6exMlaDrRHCRGMQlnx5lsXDCZ/J7G/frgIIl94zhZSp/galVAYp7VzPi1OrAHta89/yGQ==", + "dev": true, + "requires": { + "es-to-primitive": "1.1.1", + "function-bind": "1.1.1", + "has": "1.0.1", + "is-callable": "1.1.3", + "is-regex": "1.0.4" + } + }, + "es-to-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "dev": true, + "requires": { + "is-callable": "1.1.3", + "is-date-object": "1.0.1", + "is-symbol": "1.0.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.7.2.tgz", + "integrity": "sha1-/29fUZOEiifum2J74+c/ucteZi4=", + "dev": true, + "requires": { + "ajv": "5.2.2", + "babel-code-frame": "6.26.0", + "chalk": "2.1.0", + "concat-stream": "1.6.0", + "cross-spawn": "5.1.0", + "debug": "3.0.1", + "doctrine": "2.0.0", + "eslint-scope": "3.7.1", + "espree": "3.5.1", + "esquery": "1.0.0", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "file-entry-cache": "2.0.0", + "functional-red-black-tree": "1.0.1", + "glob": "7.1.2", + "globals": "9.18.0", + "ignore": "3.3.5", + "imurmurhash": "0.1.4", + "inquirer": "3.3.0", + "is-resolvable": "1.0.0", + "js-yaml": "3.10.0", + "json-stable-stringify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.4", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "optionator": "0.8.2", + "path-is-inside": "1.0.2", + "pluralize": "7.0.0", + "progress": "2.0.0", + "require-uncached": "1.0.3", + "semver": "5.4.1", + "strip-ansi": "4.0.0", + "strip-json-comments": "2.0.1", + "table": "4.0.1", + "text-table": "0.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.0" + } + }, + "chalk": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", + "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.4.0" + } + }, + "debug": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.0.1.tgz", + "integrity": "sha512-6nVc6S36qbt/mutyt+UGMnawAMrPDZUPQjRZI3FS9tCtDRhvxJbK79unYBLPi+z5SLXQ3ftoVBFCblQtNSls8w==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } + } + }, + "eslint-config-airbnb": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-15.1.0.tgz", + "integrity": "sha512-m0q9fiMBzDAIbirlGnpJNWToIhdhJmXXnMG+IFflYzzod9231ZhtmGKegKg8E9T8F1YuVaDSU1FnCm5b9iXVhQ==", + "dev": true, + "requires": { + "eslint-config-airbnb-base": "11.3.2" + } + }, + "eslint-config-airbnb-base": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-11.3.2.tgz", + "integrity": "sha512-/fhjt/VqzBA2SRsx7ErDtv6Ayf+XLw9LIOqmpBuHFCVwyJo2EtzGWMB9fYRFBoWWQLxmNmCpenNiH0RxyeS41w==", + "dev": true, + "requires": { + "eslint-restricted-globals": "0.1.1" + } + }, + "eslint-config-prettier": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-2.6.0.tgz", + "integrity": "sha1-8h2w67Q4rWePuYlGCXxLsZi+/Mw=", + "dev": true, + "requires": { + "get-stdin": "5.0.1" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.1.tgz", + "integrity": "sha512-yUtXS15gIcij68NmXmP9Ni77AQuCN0itXbCc/jWd8C6/yKZaSNXicpC8cgvjnxVdmfsosIXrjpzFq7GcDryb6A==", + "dev": true, + "requires": { + "debug": "2.6.9", + "resolve": "1.4.0" + } + }, + "eslint-module-utils": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz", + "integrity": "sha512-jDI/X5l/6D1rRD/3T43q8Qgbls2nq5km5KSqiwlyUbGo5+04fXhMKdCPhjwbqAa6HXWaMxj8Q4hQDIh7IadJQw==", + "dev": true, + "requires": { + "debug": "2.6.9", + "pkg-dir": "1.0.0" + } + }, + "eslint-plugin-flowtype": { + "version": "2.35.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.35.1.tgz", + "integrity": "sha512-YTCeVsMOi3ga8PJjdAV97FaTNXNknzrO+4ZDMHJN65i4uMjL5KgfgQZpyVsBirWOfgXMKRduxpsyM64K/0LbXw==", + "dev": true, + "requires": { + "lodash": "4.17.4" + } + }, + "eslint-plugin-import": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.7.0.tgz", + "integrity": "sha512-HGYmpU9f/zJaQiKNQOVfHUh2oLWW3STBrCgH0sHTX1xtsxYlH1zjLh8FlQGEIdZSdTbUMaV36WaZ6ImXkenGxQ==", + "dev": true, + "requires": { + "builtin-modules": "1.1.1", + "contains-path": "0.1.0", + "debug": "2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "0.3.1", + "eslint-module-utils": "2.1.1", + "has": "1.0.1", + "lodash.cond": "4.5.2", + "minimatch": "3.0.4", + "read-pkg-up": "2.0.0" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "2.0.2", + "isarray": "1.0.0" + } + } + } + }, + "eslint-plugin-jsx-a11y": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.1.1.tgz", + "integrity": "sha512-5I9SpoP7gT4wBFOtXT8/tXNPYohHBVfyVfO17vkbC7r9kEIxYJF12D3pKqhk8+xnk12rfxKClS3WCFpVckFTPQ==", + "dev": true, + "requires": { + "aria-query": "0.7.0", + "array-includes": "3.0.3", + "ast-types-flow": "0.0.7", + "axobject-query": "0.1.0", + "damerau-levenshtein": "1.0.4", + "emoji-regex": "6.5.1", + "jsx-ast-utils": "1.4.1" + } + }, + "eslint-plugin-prettier": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.3.1.tgz", + "integrity": "sha512-AV8shBlGN9tRZffj5v/f4uiQWlP3qiQ+lh+BhTqRLuKSyczx+HRWVkVZaf7dOmguxghAH1wftnou/JUEEChhGg==", + "dev": true, + "requires": { + "fast-diff": "1.1.2", + "jest-docblock": "21.1.0" + } + }, + "eslint-plugin-react": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.3.0.tgz", + "integrity": "sha512-7L6QEOxm7XhcDoe+U9Qt7GJjU6KeQOX9jCLGE8EPGF6FQbwZ9LgcBzsjXIZv9oYvNQlvQZmLjJs76xEeWsI4QA==", + "dev": true, + "requires": { + "doctrine": "2.0.0", + "has": "1.0.1", + "jsx-ast-utils": "2.0.1", + "prop-types": "15.5.10" + }, + "dependencies": { + "jsx-ast-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz", + "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=", + "dev": true, + "requires": { + "array-includes": "3.0.3" + } + } + } + }, + "eslint-restricted-globals": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz", + "integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=", + "dev": true + }, + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "dev": true, + "requires": { + "esrecurse": "4.2.0", + "estraverse": "4.2.0" + } + }, + "espree": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.1.tgz", + "integrity": "sha1-DJiLirRttTEAoZVK5LqZXd0n2H4=", + "dev": true, + "requires": { + "acorn": "5.1.2", + "acorn-jsx": "3.0.1" + } + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "esquery": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", + "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", + "dev": true, + "requires": { + "estraverse": "4.2.0" + } + }, + "esrecurse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", + "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", + "dev": true, + "requires": { + "estraverse": "4.2.0", + "object-assign": "4.1.1" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "external-editor": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.0.5.tgz", + "integrity": "sha512-Msjo64WT5W+NhOpQXh0nOHm+n0RfU1QUwDnKYvJ8dEJ8zlwLrqXNTv5mSUTJpepf41PDJGyhueTw2vNZW+Fr/w==", + "dev": true, + "requires": { + "iconv-lite": "0.4.19", + "jschardet": "1.5.1", + "tmp": "0.0.33" + } + }, + "fast-deep-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" + }, + "fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fbjs": { + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.15.tgz", + "integrity": "sha1-TwaV/fzBbDfAsH+s7Iy0xAkWhbk=", + "requires": { + "core-js": "1.2.7", + "isomorphic-fetch": "2.2.1", + "loose-envify": "1.3.1", + "object-assign": "4.1.1", + "promise": "7.3.1", + "setimmediate": "1.0.5", + "ua-parser-js": "0.7.14" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "1.0.5" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "1.2.2", + "object-assign": "4.1.1" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "flat-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", + "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=", + "dev": true, + "requires": { + "circular-json": "0.3.3", + "del": "2.2.2", + "graceful-fs": "4.1.11", + "write": "0.2.1" + } + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-stdin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", + "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "global": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", + "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "dev": true, + "requires": { + "min-document": "2.19.0", + "process": "0.5.2" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "arrify": "1.0.1", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "has": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "dev": true, + "requires": { + "function-bind": "1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "hosted-git-info": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", + "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "ignore": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.5.tgz", + "integrity": "sha512-JLH93mL8amZQhh/p6mfQgVBH3M6epNq3DfsXsTSuSrInVjwyYlFE1nv2AgfRCC8PoOhM0jwQ5v8s9LgbK7yGDw==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "requires": { + "ansi-escapes": "3.0.0", + "chalk": "2.1.0", + "cli-cursor": "2.1.0", + "cli-width": "2.2.0", + "external-editor": "2.0.5", + "figures": "2.0.0", + "lodash": "4.17.4", + "mute-stream": "0.0.7", + "run-async": "2.3.0", + "rx-lite": "4.0.8", + "rx-lite-aggregates": "4.0.8", + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "through": "2.3.8" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.0" + } + }, + "chalk": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", + "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.4.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } + } + }, + "invariant": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", + "dev": true, + "requires": { + "loose-envify": "1.3.1" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "1.1.1" + } + }, + "is-callable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", + "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", + "dev": true, + "requires": { + "is-path-inside": "1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", + "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", + "dev": true, + "requires": { + "path-is-inside": "1.0.2" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "1.0.1" + } + }, + "is-resolvable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", + "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", + "dev": true, + "requires": { + "tryit": "1.0.3" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "1.7.3", + "whatwg-fetch": "2.0.3" + } + }, + "jest-docblock": { + "version": "21.1.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-21.1.0.tgz", + "integrity": "sha512-ai3olFJ/3cSd60klaRIcC/Cb44/RsJ69qS8uXxfWXEbnbDqjcbl5K8Z5ekfY15hgVZGSAiLz7pOwfjIBE/yJVw==", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, + "js-yaml": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", + "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", + "dev": true, + "requires": { + "argparse": "1.0.9", + "esprima": "4.0.0" + } + }, + "jschardet": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-1.5.1.tgz", + "integrity": "sha512-vE2hT1D0HLZCLLclfBSfkfTTedhVj0fubHpJBHKwwUWX0nSbhPAfk+SG9rTX95BYNmau8rGFfCeaT6T5OW1C2A==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsx-ast-utils": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", + "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2", + "type-check": "0.3.2" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "strip-bom": "3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "2.0.0", + "path-exists": "3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + }, + "lodash.cond": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz", + "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=", + "dev": true + }, + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "requires": { + "js-tokens": "3.0.2" + } + }, + "lru-cache": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", + "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", + "dev": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "mimic-fn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", + "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", + "dev": true + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "dev": true, + "requires": { + "dom-walk": "0.1.1" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "0.1.12", + "is-stream": "1.1.0" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "2.5.0", + "is-builtin-module": "1.0.0", + "semver": "5.4.1", + "validate-npm-package-license": "3.0.1" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-keys": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "1.1.0" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-limit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz", + "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=", + "dev": true + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "1.1.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "1.3.1" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "2.0.1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "2.3.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, + "pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "dev": true, + "requires": { + "find-up": "1.1.2" + } + }, + "pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prettier": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.7.0.tgz", + "integrity": "sha512-kIbA3UE9sYGiVCxlWg92EOHWZqte6EAkC7DS5je6NaL8g3Uw1rWe0eH+UX4Hy5OEiR9aql2vVMHeg6lR4YGxYg==", + "dev": true + }, + "private": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", + "integrity": "sha1-aM5eih7woju1cMwoU3tTMqumPvE=", + "dev": true + }, + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "progress": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "2.0.6" + } + }, + "prop-types": { + "version": "15.5.10", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz", + "integrity": "sha1-J5ffwxJhguOpXj37suiT3ddFYVQ=", + "requires": { + "fbjs": "0.8.15", + "loose-envify": "1.3.1" + } + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "react": { + "version": "16.0.0-alpha.12", + "resolved": "https://registry.npmjs.org/react/-/react-16.0.0-alpha.12.tgz", + "integrity": "sha1-jFlIUoFIXfMZtvd2gtjdBiHAgZQ=", + "dev": true, + "requires": { + "create-react-class": "15.6.0", + "fbjs": "0.8.15", + "loose-envify": "1.3.1", + "object-assign": "4.1.1", + "prop-types": "15.5.10" + } + }, + "react-deep-force-update": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/react-deep-force-update/-/react-deep-force-update-1.1.1.tgz", + "integrity": "sha1-vNMUeAJ7ZLMznxCJIatSC0MT3Cw=", + "dev": true + }, + "react-proxy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/react-proxy/-/react-proxy-1.1.8.tgz", + "integrity": "sha1-nb/Z2SdSjDqp9ETkVYw3gwq4wmo=", + "dev": true, + "requires": { + "lodash": "4.17.4", + "react-deep-force-update": "1.1.1" + } + }, + "react-transform-hmr": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/react-transform-hmr/-/react-transform-hmr-1.0.4.tgz", + "integrity": "sha1-4aQL0Krvxy6N/Xp82gmvhQZjl7s=", + "dev": true, + "requires": { + "global": "4.3.2", + "react-proxy": "1.1.8" + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "2.0.0", + "normalize-package-data": "2.4.0", + "path-type": "2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "2.1.0", + "read-pkg": "2.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "2.0.0" + } + } + } + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "regenerator-runtime": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", + "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==", + "dev": true + }, + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "private": "0.1.7" + } + }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "0.1.0", + "resolve-from": "1.0.1" + } + }, + "resolve": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", + "integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==", + "dev": true, + "requires": { + "path-parse": "1.0.5" + } + }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "2.0.1", + "signal-exit": "3.0.2" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "2.1.0" + } + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "dev": true + }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "requires": { + "rx-lite": "4.0.8" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "spdx-correct": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", + "dev": true, + "requires": { + "spdx-license-ids": "1.2.2" + } + }, + "spdx-expression-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", + "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", + "dev": true + }, + "spdx-license-ids": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", + "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "table": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.1.tgz", + "integrity": "sha1-qBFsEz+sLGH0pCCrbN9cTWHw5DU=", + "dev": true, + "requires": { + "ajv": "4.11.8", + "ajv-keywords": "1.5.1", + "chalk": "1.1.3", + "lodash": "4.17.4", + "slice-ansi": "0.0.4", + "string-width": "2.1.1" + }, + "dependencies": { + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "1.0.2" + } + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "tryit": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", + "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "ua-parser-js": { + "version": "0.7.14", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.14.tgz", + "integrity": "sha1-EQ1T+kw/MmwSEpK76skE0uAzh8o=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", + "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", + "dev": true, + "requires": { + "spdx-correct": "1.0.2", + "spdx-expression-parse": "1.0.4" + } + }, + "whatwg-fetch": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz", + "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=" + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "dev": true, + "requires": { + "isexe": "2.0.0" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "0.5.1" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } +} diff --git a/package.json b/package.json index 2a845b90..111b5b8b 100644 --- a/package.json +++ b/package.json @@ -4,26 +4,40 @@ "type": "git", "url": "https://github.com/HippoAR/react-native-arkit.git" }, - "version": "0.0.6", + "version": "0.9.0", "description": "React Native binding for iOS ARKit", "author": "Zehao Li ", "license": "MIT", "homepage": "https://github.com/HippoAR/react-native-arkit", - "keywords": [ - "react-native", - "react", - "native", - "ARKit", - "AR" - ], + "keywords": ["react-native", "react", "native", "ARKit", "AR"], "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, + "peerDependencies": { + "react-native": ">=0.46.0", + "react": "*", + "prop-types": "*" + }, "dependencies": { - "prop-types": "^15.5.7" + "@panter/react-animation-frame": "^0.3.7", + "fast-deep-equal": "^1.0.0", + "lodash": "^4.17.4", + "prop-types": "^15.6.0" }, - "peerDependencies": { - "react": "^15.5.0" + "devDependencies": { + "babel-eslint": "^7.2.3", + "babel-preset-react-native": "^2.1.0", + "eslint": "^4.2.0", + "eslint-config-airbnb": "^15.0.1", + "eslint-config-prettier": "^2.1.1", + "eslint-plugin-flowtype": "^2.33.0", + "eslint-plugin-import": "^2.3.0", + "eslint-plugin-jsx-a11y": "^5.0.3", + "eslint-plugin-prettier": "^2.1.1", + "eslint-plugin-react": "^7.0.1", + "prettier": "^1.3.1", + "react": "16.2.0", + "prop-types": "^15.6.0" } } diff --git a/screenshots/geometries.jpg b/screenshots/geometries.jpg new file mode 100644 index 00000000..796eca55 Binary files /dev/null and b/screenshots/geometries.jpg differ diff --git a/startup.js b/startup.js new file mode 100644 index 00000000..6817eebb --- /dev/null +++ b/startup.js @@ -0,0 +1,15 @@ +import { NativeModules } from 'react-native'; + +const ARKitManager = NativeModules.ARKitManager; + +export default () => { + // when reloading the app, the scene should be cleared. + // on prod, this usually does not happen, but you can reload the app in develop mode + // without clearing, this would result in inconsistency + // do this only when arkit was already initialized + ARKitManager.isInitialized().then(isInitialized => { + if (isInitialized) { + ARKitManager.clearScene(); + } + }); +}; diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..9033fa07 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1721 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@panter/react-animation-frame@^0.3.7": + version "0.3.7" + resolved "https://registry.yarnpkg.com/@panter/react-animation-frame/-/react-animation-frame-0.3.7.tgz#31a0d7683e4a8d2e1b29ff512cad36513bd43d00" + dependencies: + react "15.4.2" + +acorn-jsx@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" + dependencies: + acorn "^3.0.4" + +acorn@^3.0.4: + version "3.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" + +acorn@^5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.2.tgz#911cb53e036807cf0fa778dc5d370fbd864246d7" + +ajv-keywords@^1.0.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" + +ajv@^4.7.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +ajv@^5.2.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.2.tgz#47c68d69e86f5d953103b0074a9430dc63da5e39" + dependencies: + co "^4.6.0" + fast-deep-equal "^1.0.0" + json-schema-traverse "^0.3.0" + json-stable-stringify "^1.0.1" + +ansi-escapes@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-2.0.0.tgz#5bae52be424878dd9783e8910e3fc2922e83c81b" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansi-styles@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" + dependencies: + color-convert "^1.9.0" + +argparse@^1.0.7: + version "1.0.9" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" + dependencies: + sprintf-js "~1.0.2" + +aria-query@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-0.7.0.tgz#4af10a1e61573ddea0cf3b99b51c52c05b424d24" + dependencies: + ast-types-flow "0.0.7" + +array-includes@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.7.0" + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + +arrify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + +ast-types-flow@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" + +axobject-query@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-0.1.0.tgz#62f59dbc59c9f9242759ca349960e7a2fe3c36c0" + dependencies: + ast-types-flow "0.0.7" + +babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-eslint@^7.2.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.2.3.tgz#b2fe2d80126470f5c19442dc757253a897710827" + dependencies: + babel-code-frame "^6.22.0" + babel-traverse "^6.23.1" + babel-types "^6.23.0" + babylon "^6.17.0" + +babel-helper-builder-react-jsx@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz#39ff8313b75c8b65dceff1f31d383e0ff2a408a0" + dependencies: + babel-runtime "^6.26.0" + babel-types "^6.26.0" + esutils "^2.0.2" + +babel-helper-call-delegate@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-define-map@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" + dependencies: + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-get-function-arity@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-hoist-variables@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-optimise-call-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-replace-supers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" + dependencies: + babel-helper-optimise-call-expression "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-check-es2015-constants@^6.5.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-react-transform@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/babel-plugin-react-transform/-/babel-plugin-react-transform-2.0.2.tgz#515bbfa996893981142d90b1f9b1635de2995109" + dependencies: + lodash "^4.6.1" + +babel-plugin-syntax-async-functions@^6.5.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" + +babel-plugin-syntax-class-properties@^6.5.0, babel-plugin-syntax-class-properties@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" + +babel-plugin-syntax-flow@^6.18.0, babel-plugin-syntax-flow@^6.5.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a2af26aa20cd25995c398c4eb70310c8d" + +babel-plugin-syntax-jsx@^6.5.0, babel-plugin-syntax-jsx@^6.8.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" + +babel-plugin-syntax-object-rest-spread@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" + +babel-plugin-syntax-trailing-function-commas@^6.5.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" + +babel-plugin-transform-class-properties@^6.5.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac" + dependencies: + babel-helper-function-name "^6.24.1" + babel-plugin-syntax-class-properties "^6.8.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-arrow-functions@^6.5.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoping@^6.5.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f" + dependencies: + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-plugin-transform-es2015-classes@^6.5.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" + dependencies: + babel-helper-define-map "^6.24.1" + babel-helper-function-name "^6.24.1" + babel-helper-optimise-call-expression "^6.24.1" + babel-helper-replace-supers "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-computed-properties@^6.5.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-destructuring@^6.5.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-for-of@^6.5.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-function-name@^6.5.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-literals@^6.5.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-modules-commonjs@^6.5.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz#0d8394029b7dc6abe1a97ef181e00758dd2e5d8a" + dependencies: + babel-plugin-transform-strict-mode "^6.24.1" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-types "^6.26.0" + +babel-plugin-transform-es2015-parameters@^6.5.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" + dependencies: + babel-helper-call-delegate "^6.24.1" + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-shorthand-properties@^6.5.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-spread@^6.5.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-template-literals@^6.5.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-flow-strip-types@^6.5.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz#84cb672935d43714fdc32bce84568d87441cf7cf" + dependencies: + babel-plugin-syntax-flow "^6.18.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-object-assign@^6.5.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-assign/-/babel-plugin-transform-object-assign-6.22.0.tgz#f99d2f66f1a0b0d498e346c5359684740caa20ba" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-object-rest-spread@^6.5.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06" + dependencies: + babel-plugin-syntax-object-rest-spread "^6.8.0" + babel-runtime "^6.26.0" + +babel-plugin-transform-react-display-name@^6.5.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz#67e2bf1f1e9c93ab08db96792e05392bf2cc28d1" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-react-jsx-source@^6.5.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz#66ac12153f5cd2d17b3c19268f4bf0197f44ecd6" + dependencies: + babel-plugin-syntax-jsx "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-react-jsx@^6.5.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz#840a028e7df460dfc3a2d29f0c0d91f6376e66a3" + dependencies: + babel-helper-builder-react-jsx "^6.24.1" + babel-plugin-syntax-jsx "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-regenerator@^6.5.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" + dependencies: + regenerator-transform "^0.10.0" + +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-preset-react-native@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/babel-preset-react-native/-/babel-preset-react-native-2.1.0.tgz#9013ebd82da1c88102bf588810ff59e209ca2b8a" + dependencies: + babel-plugin-check-es2015-constants "^6.5.0" + babel-plugin-react-transform "2.0.2" + babel-plugin-syntax-async-functions "^6.5.0" + babel-plugin-syntax-class-properties "^6.5.0" + babel-plugin-syntax-flow "^6.5.0" + babel-plugin-syntax-jsx "^6.5.0" + babel-plugin-syntax-trailing-function-commas "^6.5.0" + babel-plugin-transform-class-properties "^6.5.0" + babel-plugin-transform-es2015-arrow-functions "^6.5.0" + babel-plugin-transform-es2015-block-scoping "^6.5.0" + babel-plugin-transform-es2015-classes "^6.5.0" + babel-plugin-transform-es2015-computed-properties "^6.5.0" + babel-plugin-transform-es2015-destructuring "^6.5.0" + babel-plugin-transform-es2015-for-of "^6.5.0" + babel-plugin-transform-es2015-function-name "^6.5.0" + babel-plugin-transform-es2015-literals "^6.5.0" + babel-plugin-transform-es2015-modules-commonjs "^6.5.0" + babel-plugin-transform-es2015-parameters "^6.5.0" + babel-plugin-transform-es2015-shorthand-properties "^6.5.0" + babel-plugin-transform-es2015-spread "^6.5.0" + babel-plugin-transform-es2015-template-literals "^6.5.0" + babel-plugin-transform-flow-strip-types "^6.5.0" + babel-plugin-transform-object-assign "^6.5.0" + babel-plugin-transform-object-rest-spread "^6.5.0" + babel-plugin-transform-react-display-name "^6.5.0" + babel-plugin-transform-react-jsx "^6.5.0" + babel-plugin-transform-react-jsx-source "^6.5.0" + babel-plugin-transform-regenerator "^6.5.0" + react-transform-hmr "^1.0.4" + +babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.24.1, babel-template@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.23.1, babel-traverse@^6.24.1, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.19.0, babel-types@^6.23.0, babel-types@^6.24.1, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon@^6.17.0, babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +brace-expansion@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +builtin-modules@^1.0.0, builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + +caller-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" + dependencies: + callsites "^0.2.0" + +callsites@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" + +chalk@^1.1.1, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.0, chalk@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" + dependencies: + ansi-styles "^3.1.0" + escape-string-regexp "^1.0.5" + supports-color "^4.0.0" + +circular-json@^0.3.1: + version "0.3.3" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +color-convert@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" + dependencies: + color-name "^1.1.1" + +color-name@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + +core-js@^1.0.0: + version "1.2.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + +core-js@^2.4.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cross-spawn@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +damerau-levenshtein@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz#03191c432cb6eea168bb77f3a55ffdccb8978514" + +debug@^2.6.8: + version "2.6.8" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" + dependencies: + ms "2.0.0" + +debug@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.0.1.tgz#0564c612b521dc92d9f2988f0549e34f9c98db64" + dependencies: + ms "2.0.0" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +define-properties@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" + dependencies: + foreach "^2.0.5" + object-keys "^1.0.8" + +del@^2.0.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" + dependencies: + globby "^5.0.0" + is-path-cwd "^1.0.0" + is-path-in-cwd "^1.0.0" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + rimraf "^2.2.8" + +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +doctrine@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63" + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +dom-walk@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" + +emoji-regex@^6.1.0: + version "6.5.1" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.5.1.tgz#9baea929b155565c11ea41c6626eaa65cef992c2" + +encoding@^0.1.11: + version "0.1.12" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + dependencies: + iconv-lite "~0.4.13" + +error-ex@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.7.0: + version "1.8.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.8.2.tgz#25103263dc4decbda60e0c737ca32313518027ee" + dependencies: + es-to-primitive "^1.1.1" + function-bind "^1.1.1" + has "^1.0.1" + is-callable "^1.1.3" + is-regex "^1.0.4" + +es-to-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" + dependencies: + is-callable "^1.1.1" + is-date-object "^1.0.1" + is-symbol "^1.0.1" + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +eslint-config-airbnb-base@^11.3.0: + version "11.3.2" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-11.3.2.tgz#8703b11abe3c88ac7ec2b745b7fdf52e00ae680a" + dependencies: + eslint-restricted-globals "^0.1.1" + +eslint-config-airbnb@^15.0.1: + version "15.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-15.1.0.tgz#fd432965a906e30139001ba830f58f73aeddae8e" + dependencies: + eslint-config-airbnb-base "^11.3.0" + +eslint-config-prettier@^2.1.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-2.5.0.tgz#9ecb9296bae4e2e59a3ce361a96c9f825fe67b75" + dependencies: + get-stdin "^5.0.1" + +eslint-import-resolver-node@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.1.tgz#4422574cde66a9a7b099938ee4d508a199e0e3cc" + dependencies: + debug "^2.6.8" + resolve "^1.2.0" + +eslint-module-utils@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz#abaec824177613b8a95b299639e1b6facf473449" + dependencies: + debug "^2.6.8" + pkg-dir "^1.0.0" + +eslint-plugin-flowtype@^2.33.0: + version "2.35.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.35.1.tgz#9ad98181b467a3645fbd2a8d430393cc17a4ea63" + dependencies: + lodash "^4.15.0" + +eslint-plugin-import@^2.3.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.7.0.tgz#21de33380b9efb55f5ef6d2e210ec0e07e7fa69f" + dependencies: + builtin-modules "^1.1.1" + contains-path "^0.1.0" + debug "^2.6.8" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.1" + eslint-module-utils "^2.1.1" + has "^1.0.1" + lodash.cond "^4.3.0" + minimatch "^3.0.3" + read-pkg-up "^2.0.0" + +eslint-plugin-jsx-a11y@^5.0.3: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.1.1.tgz#5c96bb5186ca14e94db1095ff59b3e2bd94069b1" + dependencies: + aria-query "^0.7.0" + array-includes "^3.0.3" + ast-types-flow "0.0.7" + axobject-query "^0.1.0" + damerau-levenshtein "^1.0.0" + emoji-regex "^6.1.0" + jsx-ast-utils "^1.4.0" + +eslint-plugin-prettier@^2.1.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-2.3.1.tgz#e7a746c67e716f335274b88295a9ead9f544e44d" + dependencies: + fast-diff "^1.1.1" + jest-docblock "^21.0.0" + +eslint-plugin-react@^7.0.1: + version "7.3.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.3.0.tgz#ca9368da36f733fbdc05718ae4e91f778f38e344" + dependencies: + doctrine "^2.0.0" + has "^1.0.1" + jsx-ast-utils "^2.0.0" + prop-types "^15.5.10" + +eslint-restricted-globals@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7" + +eslint-scope@^3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint@^4.2.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.7.0.tgz#d35fc07c472520be3de85b3da11e99c576afd515" + dependencies: + ajv "^5.2.0" + babel-code-frame "^6.22.0" + chalk "^2.1.0" + concat-stream "^1.6.0" + cross-spawn "^5.1.0" + debug "^3.0.1" + doctrine "^2.0.0" + eslint-scope "^3.7.1" + espree "^3.5.1" + esquery "^1.0.0" + estraverse "^4.2.0" + esutils "^2.0.2" + file-entry-cache "^2.0.0" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^9.17.0" + ignore "^3.3.3" + imurmurhash "^0.1.4" + inquirer "^3.0.6" + is-resolvable "^1.0.0" + js-yaml "^3.9.1" + json-stable-stringify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.4" + minimatch "^3.0.2" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + pluralize "^7.0.0" + progress "^2.0.0" + require-uncached "^1.0.3" + semver "^5.3.0" + strip-ansi "^4.0.0" + strip-json-comments "~2.0.1" + table "^4.0.1" + text-table "~0.2.0" + +espree@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.1.tgz#0c988b8ab46db53100a1954ae4ba995ddd27d87e" + dependencies: + acorn "^5.1.1" + acorn-jsx "^3.0.0" + +esprima@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" + +esquery@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa" + dependencies: + estraverse "^4.0.0" + +esrecurse@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" + dependencies: + estraverse "^4.1.0" + object-assign "^4.0.1" + +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +external-editor@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.0.4.tgz#1ed9199da9cbfe2ef2f7a31b2fde8b0d12368972" + dependencies: + iconv-lite "^0.4.17" + jschardet "^1.4.2" + tmp "^0.0.31" + +fast-deep-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" + +fast-diff@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.1.tgz#0aea0e4e605b6a2189f0e936d4b7fbaf1b7cfd9b" + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +fbjs@^0.8.16, fbjs@^0.8.4: + version "0.8.16" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.9" + +fbjs@^0.8.9: + version "0.8.15" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.15.tgz#4f0695fdfcc16c37c0b07facec8cb4c4091685b9" + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.9" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" + dependencies: + flat-cache "^1.2.1" + object-assign "^4.0.1" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + dependencies: + locate-path "^2.0.0" + +flat-cache@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96" + dependencies: + circular-json "^0.3.1" + del "^2.0.2" + graceful-fs "^4.1.2" + write "^0.2.1" + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +function-bind@^1.0.2, function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + +get-stdin@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" + +glob@^7.0.3, glob@^7.0.5, glob@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global@^4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" + dependencies: + min-document "^2.19.0" + process "~0.5.1" + +globals@^9.17.0, globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + +globby@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" + dependencies: + array-union "^1.0.1" + arrify "^1.0.0" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +graceful-fs@^4.1.2: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + +has@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" + dependencies: + function-bind "^1.0.2" + +hosted-git-info@^2.1.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" + +iconv-lite@^0.4.17, iconv-lite@~0.4.13: + version "0.4.19" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" + +ignore@^3.3.3: + version "3.3.5" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.5.tgz#c4e715455f6073a8d7e5dae72d2fc9d71663dba6" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +inquirer@^3.0.6: + version "3.2.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.2.3.tgz#1c7b1731cf77b934ec47d22c9ac5aa8fe7fbe095" + dependencies: + ansi-escapes "^2.0.0" + chalk "^2.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^2.0.4" + figures "^2.0.0" + lodash "^4.3.0" + mute-stream "0.0.7" + run-async "^2.2.0" + rx-lite "^4.0.8" + rx-lite-aggregates "^4.0.8" + string-width "^2.1.0" + strip-ansi "^4.0.0" + through "^2.3.6" + +invariant@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" + dependencies: + loose-envify "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + +is-callable@^1.1.1, is-callable@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-path-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" + +is-path-in-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" + dependencies: + is-path-inside "^1.0.0" + +is-path-inside@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" + dependencies: + path-is-inside "^1.0.1" + +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + dependencies: + has "^1.0.1" + +is-resolvable@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" + dependencies: + tryit "^1.0.1" + +is-stream@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +is-symbol@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" + +isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +isomorphic-fetch@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + dependencies: + node-fetch "^1.0.1" + whatwg-fetch ">=0.10.0" + +jest-docblock@^21.0.0: + version "21.2.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414" + +js-tokens@^3.0.0, js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + +js-yaml@^3.9.1: + version "3.10.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jschardet@^1.4.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-1.5.1.tgz#c519f629f86b3a5bedba58a88d311309eec097f9" + +json-schema-traverse@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + dependencies: + jsonify "~0.0.0" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsx-ast-utils@^1.4.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1" + +jsx-ast-utils@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz#e801b1b39985e20fffc87b40e3748080e2dcac7f" + dependencies: + array-includes "^3.0.3" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +lodash.cond@^4.3.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" + +lodash@^4.0.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.6.1: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" + dependencies: + js-tokens "^3.0.0" + +lru-cache@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +mimic-fn@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + dependencies: + dom-walk "^0.1.0" + +minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + +node-fetch@^1.0.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +normalize-package-data@^2.3.2: + version "2.4.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-keys@^1.0.8: + version "1.0.11" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + dependencies: + mimic-fn "^1.0.0" + +optionator@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +os-tmpdir@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +p-limit@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + dependencies: + p-limit "^1.1.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-is-inside@^1.0.1, path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + dependencies: + pify "^2.0.0" + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +pkg-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" + dependencies: + find-up "^1.0.0" + +pluralize@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +prettier@^1.3.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.7.0.tgz#47481588f41f7c90f63938feb202ac82554e7150" + +private@^0.1.6: + version "0.1.7" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +process@~0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" + +progress@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" + +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + dependencies: + asap "~2.0.3" + +prop-types@^15.5.10: + version "15.5.10" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" + dependencies: + fbjs "^0.8.9" + loose-envify "^1.3.1" + +prop-types@^15.6.0: + version "15.6.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" + dependencies: + fbjs "^0.8.16" + loose-envify "^1.3.1" + object-assign "^4.1.1" + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +react-deep-force-update@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/react-deep-force-update/-/react-deep-force-update-1.1.1.tgz#bcd31478027b64b3339f108921ab520b4313dc2c" + +react-proxy@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/react-proxy/-/react-proxy-1.1.8.tgz#9dbfd9d927528c3aa9f444e4558c37830ab8c26a" + dependencies: + lodash "^4.6.1" + react-deep-force-update "^1.0.0" + +react-transform-hmr@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/react-transform-hmr/-/react-transform-hmr-1.0.4.tgz#e1a40bd0aaefc72e8dfd7a7cda09af85066397bb" + dependencies: + global "^4.3.0" + react-proxy "^1.1.7" + +react@15.4.2: + version "15.4.2" + resolved "https://registry.yarnpkg.com/react/-/react-15.4.2.tgz#41f7991b26185392ba9bae96c8889e7e018397ef" + dependencies: + fbjs "^0.8.4" + loose-envify "^1.1.0" + object-assign "^4.1.0" + +react@16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba" + dependencies: + fbjs "^0.8.16" + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.0" + +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + +readable-stream@^2.2.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" + util-deprecate "~1.0.1" + +regenerator-runtime@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1" + +regenerator-transform@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" + dependencies: + babel-runtime "^6.18.0" + babel-types "^6.19.0" + private "^0.1.6" + +require-uncached@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" + dependencies: + caller-path "^0.1.0" + resolve-from "^1.0.0" + +resolve-from@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" + +resolve@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86" + dependencies: + path-parse "^1.0.5" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +rimraf@^2.2.8: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + dependencies: + is-promise "^2.1.0" + +rx-lite-aggregates@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" + dependencies: + rx-lite "*" + +rx-lite@*, rx-lite@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +"semver@2 || 3 || 4 || 5", semver@^5.3.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + +signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + +spdx-correct@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" + dependencies: + spdx-license-ids "^1.0.2" + +spdx-expression-parse@~1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" + +spdx-license-ids@^1.0.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +string-width@^2.0.0, string-width@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string_decoder@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^4.0.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e" + dependencies: + has-flag "^2.0.0" + +table@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/table/-/table-4.0.1.tgz#a8116c133fac2c61f4a420ab6cdf5c4d61f0e435" + dependencies: + ajv "^4.7.0" + ajv-keywords "^1.0.0" + chalk "^1.1.1" + lodash "^4.0.0" + slice-ansi "0.0.4" + string-width "^2.0.0" + +text-table@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +tmp@^0.0.31: + version "0.0.31" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" + dependencies: + os-tmpdir "~1.0.1" + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + +tryit@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + +ua-parser-js@^0.7.9: + version "0.7.14" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.14.tgz#110d53fa4c3f326c121292bbeac904d2e03387ca" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +validate-npm-package-license@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + dependencies: + spdx-correct "~1.0.0" + spdx-expression-parse "~1.0.0" + +whatwg-fetch@>=0.10.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" + +which@^1.2.9: + version "1.3.0" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" + dependencies: + isexe "^2.0.0" + +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +write@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" + dependencies: + mkdirp "^0.5.1" + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"