Skip to content

Commit 57c3d2e

Browse files
authored
Merge pull request #91 from panter/with-projected-position
add hoc withProjectedPosition
2 parents 710c9c6 + 45cceca commit 57c3d2e

File tree

15 files changed

+346
-117
lines changed

15 files changed

+346
-117
lines changed

ARKit.js

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
View,
1414
Text,
1515
NativeModules,
16-
requireNativeComponent
16+
requireNativeComponent,
1717
} from 'react-native';
1818

1919
import generateId from './components/lib/generateId';
@@ -26,15 +26,15 @@ const TRACKING_REASONS = [
2626
'NONE',
2727
'INITIALIZING',
2828
'EXCESSIVE_MOTION',
29-
'INSUFFICIENT_FEATURES'
29+
'INSUFFICIENT_FEATURES',
3030
];
3131
const TRACKING_STATES_COLOR = ['red', 'orange', 'green'];
3232

3333
class ARKit extends Component {
3434
state = {
3535
state: 0,
3636
reason: 0,
37-
floor: null
37+
floor: null,
3838
};
3939
componentWillMount() {
4040
ARKitManager.clearScene();
@@ -55,7 +55,7 @@ class ARKit extends Component {
5555
<View
5656
style={[
5757
styles.stateIcon,
58-
{ backgroundColor: TRACKING_STATES_COLOR[this.state.state] }
58+
{ backgroundColor: TRACKING_STATES_COLOR[this.state.state] },
5959
]}
6060
/>
6161
<Text style={styles.stateText}>
@@ -84,21 +84,21 @@ class ARKit extends Component {
8484
_onTrackingState = ({
8585
state = this.state.state,
8686
reason = this.state.reason,
87-
floor
87+
floor,
8888
}) => {
8989
if (this.props.onTrackingState) {
9090
this.props.onTrackingState({
9191
state: TRACKING_STATES[state] || state,
9292
reason: TRACKING_REASONS[reason] || reason,
93-
floor
93+
floor,
9494
});
9595
}
9696

9797
if (this.props.debug) {
9898
this.setState({
9999
state,
100100
reason,
101-
floor: floor ? floor.toFixed(2) : this.state.floor
101+
floor: floor ? floor.toFixed(2) : this.state.floor,
102102
});
103103
}
104104
};
@@ -138,19 +138,19 @@ const styles = StyleSheet.create({
138138
borderRadius: 10,
139139
padding: 4,
140140
backgroundColor: 'black',
141-
flexDirection: 'row'
141+
flexDirection: 'row',
142142
},
143143
stateIcon: {
144144
width: 12,
145145
height: 12,
146146
borderRadius: 6,
147-
marginRight: 4
147+
marginRight: 4,
148148
},
149149
stateText: {
150150
color: 'white',
151151
fontSize: 10,
152-
height: 12
153-
}
152+
height: 12,
153+
},
154154
});
155155

156156
// copy all ARKitManager properties to ARKit
@@ -160,7 +160,7 @@ Object.keys(ARKitManager).forEach(key => {
160160

161161
const addDefaultsToSnapShotFunc = funcName => ({
162162
target = 'cameraRoll',
163-
format = 'png'
163+
format = 'png',
164164
}) => ARKitManager[funcName]({ target, format });
165165

166166
ARKit.snapshot = addDefaultsToSnapShotFunc('snapshot');
@@ -182,7 +182,7 @@ ARKit.propTypes = {
182182
onTrackingState: PropTypes.func,
183183
onTapOnPlaneUsingExtent: PropTypes.func,
184184
onTapOnPlaneNoExtent: PropTypes.func,
185-
onEvent: PropTypes.func
185+
onEvent: PropTypes.func,
186186
};
187187

188188
const RCTARKit = requireNativeComponent('RCTARKit', ARKit);

README.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,96 @@ See https://github.com/HippoAR/react-native-arkit/pull/89 for details
311311
| `shape` | `{ pathSvg, extrusion, pathFlatness, chamferRadius, chamferProfilePathSvg, chamferProfilePathFlatness }` |
312312

313313

314+
### HOCs (higher order components)
315+
316+
#### withProjectedPosition()
317+
318+
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.
319+
320+
Think about a 3D cursor that can be moved across your table or a 3D cursor on a wall.
321+
322+
You can use the hoc like this:
323+
324+
```
325+
const Cursor3D = withProjectedPosition()(({positionProjected, projectionResult}) => {
326+
if(!projectionResult) {
327+
// nothing has been hit, don't render it
328+
return null;
329+
}
330+
return (
331+
<ARKit.Sphere
332+
position={positionProjected}
333+
shape={{
334+
radius: 0.1
335+
}}
336+
/>
337+
)
338+
})
339+
340+
```
341+
342+
Now you can your 3D cursor like this:
343+
344+
##### Attach to a given detected horizontal plane
345+
346+
Given you have detected a plane with onPlaneDetected, you can make the cursor stick to that plane:
347+
348+
```
349+
<Cursor3D projectPosition={{
350+
x: windowWidth / 2,
351+
y: windowHeight / 2,
352+
plane: "my-planeId"
353+
}}
354+
/>
355+
356+
```
357+
358+
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:
359+
360+
```
361+
<Cursor3D projectPosition={{
362+
x: windowWidth / 2,
363+
y: windowHeight / 2,
364+
plane: (results) => results.length > 0 ? results[0] : null
365+
}}
366+
/>
367+
368+
```
369+
370+
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!
371+
372+
##### Attach to a given 3D object
373+
374+
You can attach the cursor on a 3D object, e.g. a non-horizontal-plane or similar:
375+
376+
Given there is some 3D object on your scene with `id="my-nodeId"`
377+
378+
```
379+
<Cursor3D projectPosition={{
380+
x: windowWidth / 2,
381+
y: windowHeight / 2,
382+
node: "my-nodeId"
383+
}}
384+
/>
385+
```
386+
387+
Like with planes, you can select the node with a function.
388+
389+
E.gl you have several "walls" with ids "wall_1", "wall_2", etc.
390+
391+
```
392+
<Cursor3D projectPosition={{
393+
x: windowWidth / 2,
394+
y: windowHeight / 2,
395+
node: results => results.find(r => r.id.startsWith('wall_')),
396+
}}
397+
/>
398+
```
399+
400+
401+
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!
402+
403+
314404

315405
## Contributing
316406

components/ARSprite.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const ARSprite = withAnimationFrame(
1313
super(props);
1414
this.state = {
1515
zIndex: new Animated.Value(),
16-
pos2D: new Animated.ValueXY() // inits to zero
16+
pos2D: new Animated.ValueXY(), // inits to zero
1717
};
1818
}
1919
onAnimationFrame() {
@@ -22,9 +22,9 @@ const ARSprite = withAnimationFrame(
2222
{
2323
x: this.state.pos2D.x,
2424
y: this.state.pos2D.y,
25-
z: this.state.zIndex
26-
}
27-
])
25+
z: this.state.zIndex,
26+
},
27+
]),
2828
);
2929
}
3030

@@ -34,18 +34,18 @@ const ARSprite = withAnimationFrame(
3434
style={{
3535
position: 'absolute',
3636
transform: this.state.pos2D.getTranslateTransform(),
37-
...this.props.style
37+
...this.props.style,
3838
}}
3939
>
4040
{this.props.children}
4141
</Animated.View>
4242
);
4343
}
44-
}
44+
},
4545
);
4646

4747
ARSprite.propTypes = {
48-
position
48+
position,
4949
};
5050

5151
module.exports = ARSprite;

components/lib/createArComponent.js

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
orientation,
1616
position,
1717
rotation,
18-
transition
18+
transition,
1919
} from './propTypes';
2020
import { processColorInMaterial } from './parseColor';
2121
import generateId from './generateId';
@@ -27,25 +27,25 @@ const NODE_PROPS = [
2727
'rotation',
2828
'scale',
2929
'orientation',
30-
'transition'
30+
'transition',
3131
];
3232
const KEYS_THAT_NEED_REMOUNT = ['material', 'shape', 'model'];
3333

3434
const nodeProps = (id, props) => ({
3535
id,
36-
...pick(props, NODE_PROPS)
36+
...pick(props, NODE_PROPS),
3737
});
3838

3939
export default (mountConfig, propTypes = {}) => {
4040
const getShapeAndMaterialProps = props =>
4141
typeof mountConfig === 'string'
4242
? {
4343
shape: props.shape,
44-
material: processColorInMaterial(props.material)
44+
material: processColorInMaterial(props.material),
4545
}
4646
: {
4747
...pick(props, mountConfig.pick),
48-
material: processColorInMaterial(props.material)
48+
material: processColorInMaterial(props.material),
4949
};
5050

5151
const mountFunc =
@@ -57,22 +57,22 @@ export default (mountConfig, propTypes = {}) => {
5757
mountFunc(
5858
getShapeAndMaterialProps(props),
5959
nodeProps(id, props),
60-
props.frame
60+
props.frame,
6161
);
6262
};
6363

6464
const ARComponent = class extends Component {
6565
identifier = null;
66-
6766
componentDidMount() {
6867
this.identifier = this.props.id || generateId();
68+
6969
mount(this.identifier, this.props);
7070
}
7171

7272
componentWillUpdate(props) {
7373
const changedKeys = filter(
7474
keys(this.props),
75-
key => !isDeepEqual(props[key], this.props[key])
75+
key => !isDeepEqual(props[key], this.props[key]),
7676
);
7777

7878
if (isEmpty(changedKeys)) {
@@ -88,7 +88,7 @@ export default (mountConfig, propTypes = {}) => {
8888
// always include transition
8989
ARGeosManager.update(
9090
this.identifier,
91-
pick(props, ['transition', ...changedKeys])
91+
pick(props, ['transition', ...changedKeys]),
9292
);
9393
}
9494
}
@@ -101,6 +101,7 @@ export default (mountConfig, propTypes = {}) => {
101101
return null;
102102
}
103103
};
104+
104105
ARComponent.propTypes = {
105106
frame: PropTypes.string,
106107
position,
@@ -109,7 +110,7 @@ export default (mountConfig, propTypes = {}) => {
109110
rotation,
110111
orientation,
111112
material,
112-
...propTypes
113+
...propTypes,
113114
};
114115

115116
return ARComponent;

components/lib/generateId.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
44

55
function toSex(num, base) {
6+
/* eslint no-param-reassign:0 */
67
if (base) {
78
num = parseInt(num, base);
89
}

components/lib/parseColor.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { processColor } from 'react-native';
22

3+
/* eslint import/prefer-default-export: 0 */
34
export function processColorInMaterial(material) {
45
if (!material) {
56
return material;

0 commit comments

Comments
 (0)