@lukekaalim/act-three 2.0.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/components.js CHANGED
@@ -1,26 +1,51 @@
1
1
  // @flow strict
2
- /*:: import type { Mesh } from 'three'; */
3
- /*:: import type { Component } from '@lukekaalim/act'; */
4
-
5
- import { h } from "@lukekaalim/act";
2
+ /*:: import type { Component as ActComponent } from '@lukekaalim/act'; */
3
+ /*:: import type {
4
+ Vector3,
5
+ Object3D,
6
+ Quaternion,
7
+ BufferGeometry,
8
+ Material,
9
+ } from "three"; */
10
+ /*:: import * as Three from 'three'; */
6
11
 
7
12
  /*::
8
- export type SharedProps<T> = {|
9
- ref: { current: ?T }
10
- |}
11
-
13
+ export type Object3DProps<T> = {
14
+ ref?: ((reference: T) => mixed) | { current: T | any },
12
15
 
13
- export type Props = {|
14
- width: number,
15
- height: number,
16
- updateStyle?: boolean,
17
- |};
16
+ name?: string,
17
+ position?: Vector3,
18
+ quaternion?: Quaternion,
19
+ visible?: boolean,
20
+ scale?: Vector3,
21
+ };
22
+ export type MeshProps = {
23
+ ...Object3DProps<Three.Mesh>,
24
+ geometry?: BufferGeometry,
25
+ material?: Material,
26
+ };
27
+ export type InstanceMeshProps = {
28
+ ...MeshProps,
29
+ ...Object3DProps<Three.InstanceMesh>,
30
+ };
31
+ export type PointLightsProps = {
32
+ ...Object3DProps<Three.PointLight>,
33
+ distance?: number,
34
+ decay?: number,
35
+ intensity?: number,
36
+ power?: number,
37
+ };
38
+ export type PointsProps = {
39
+ ...Object3DProps<Three.Points>,
40
+ geometry?: BufferGeometry,
41
+ material?: Material,
42
+ };
18
43
  */
19
44
 
20
- export const Three/*: Component<Props>*/ = (props) => {
21
- return h('Three', props, props.children);
45
+ export const Component = {
46
+ mesh: ('mesh'/*: ActComponent<MeshProps>*/),
47
+ instanceMesh: ('instanceMesh'/*: ActComponent<InstanceMeshProps>*/),
48
+ pointLight: ('pointLight'/*: ActComponent<PointLightsProps>*/),
49
+ points: ('points'/*: ActComponent<PointsProps>*/),
22
50
  };
23
-
24
- export const Cube/*: Component<{ ...SharedProps<Mesh> }>*/ = () => {
25
- return h('Cube');
26
- };
51
+ export const C = Component;
package/main.js CHANGED
@@ -1,12 +1,14 @@
1
1
  // @flow strict
2
2
  /*:: import type { Element } from '@lukekaalim/act'; */
3
+ /*:: import type { ThreeRenderer } from "./render.js"; */
4
+
3
5
  import { createTree } from "@lukekaalim/act-reconciler";
4
6
  import { createWebRenderService } from "@lukekaalim/act-web";
5
7
  import { attachNodes } from "@lukekaalim/act-web/node.js";
6
8
  import { createThreeRenderer } from "./render.js";
7
9
 
8
- export const render = (element/*: Element*/, node/*: HTMLElement*/) => {
9
- const { renderRoot } = createThreeRenderer();
10
+ export const render = (element/*: Element*/, node/*: HTMLElement*/, renderer/*: ThreeRenderer*/ = createThreeRenderer()) => {
11
+ const { renderRoot } = renderer;
10
12
  const web = createWebRenderService(new Map([['Three', { render: renderRoot }]]));
11
13
 
12
14
  const onDiff = diff => attachNodes(node, web.render(diff));
@@ -18,4 +20,6 @@ export const render = (element/*: Element*/, node/*: HTMLElement*/) => {
18
20
  const tree = createTree(element, options);
19
21
  };
20
22
 
23
+ export * from './render.js';
24
+ export * from './node.js';
21
25
  export * from './components.js';
package/node.js ADDED
@@ -0,0 +1,54 @@
1
+ // @flow strict
2
+ /*:: import type { Object3D } from "three"; */
3
+ /*:: import type { PropDiff } from '@lukekaalim/act-reconciler'; */
4
+ import { Mesh, PointLight, Points } from "three";
5
+
6
+ /*::
7
+ export type NodeInstance = {
8
+ object: Object3D,
9
+ update: (props: PropDiff[]) => void,
10
+ dispose: () => void,
11
+ };
12
+
13
+ export type NodeDefinition = {
14
+ type: string,
15
+ create: () => NodeInstance,
16
+ };
17
+ */
18
+
19
+ const createNodeDefinitionFromConstructor = ([type, constructor])/*: NodeDefinition*/ => {
20
+ const create = () => {
21
+ const object = new constructor();
22
+ const update = (props) => {
23
+ for (const { key, next } of props)
24
+ switch (key) {
25
+ case 'position':
26
+ object.position.copy((next/*: any*/)); break;
27
+ case 'quaternion':
28
+ object.quaternion.copy((next/*: any*/)); break;
29
+ default:
30
+ (object/*: any*/)[key] = next; break;
31
+ }
32
+ };
33
+ const dispose = () => {
34
+ return;
35
+ };
36
+ const instance = {
37
+ object,
38
+ update,
39
+ dispose,
40
+ };
41
+ return instance;
42
+ };
43
+ return {
44
+ type,
45
+ create,
46
+ }
47
+ };
48
+
49
+ export const threeNodes/*: NodeDefinition[]*/ = [
50
+ ['mesh', Mesh],
51
+ ['pointLight', PointLight],
52
+ ['points', Points],
53
+ ].map(createNodeDefinitionFromConstructor);
54
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukekaalim/act-three",
3
- "version": "2.0.2",
3
+ "version": "3.0.0",
4
4
  "description": "Render threejs content using act",
5
5
  "main": "main.js",
6
6
  "type": "module",
package/render.js CHANGED
@@ -1,7 +1,10 @@
1
1
  // @flow strict
2
- /*:: import type { CommitDiff } from '@lukekaalim/act-reconciler'; */
2
+ /*:: import type { CommitDiff, PropDiff } from '@lukekaalim/act-reconciler'; */
3
3
  /*:: import type { Object3D } from 'three'; */
4
- import { BoxGeometry, Mesh, MeshBasicMaterial, PerspectiveCamera, Scene, WebGLRenderer } from 'three';
4
+ /*:: import type { NodeDefinition } from './node.js'; */
5
+ import { PerspectiveCamera, Scene, WebGLRenderer } from 'three';
6
+ import { threeNodes } from './node.js';
7
+ import { calculatePropsDiff } from '@lukekaalim/act-reconciler'
5
8
 
6
9
  /*::
7
10
  export type ThreeRenderer = {
@@ -9,7 +12,9 @@ export type ThreeRenderer = {
9
12
  };
10
13
  */
11
14
 
12
- export const createThreeRenderer = ()/*: ThreeRenderer*/ => {
15
+ export const createThreeRenderer = (nodeDefs/*: NodeDefinition[]*/ = threeNodes)/*: ThreeRenderer*/ => {
16
+ const nodeDefsByType = new Map(nodeDefs.map(def => [def.type, def]));
17
+
13
18
  const roots = new Map();
14
19
  const objects = new Map();
15
20
 
@@ -43,23 +48,43 @@ export const createThreeRenderer = ()/*: ThreeRenderer*/ => {
43
48
  };
44
49
 
45
50
  const createObject = (diff) => {
46
- const geometry = new BoxGeometry(1, 1, 1);
47
- const material = new MeshBasicMaterial( { color: '#00ff00' } );
48
- const cube = new Mesh( geometry, material );
51
+ const { type } = diff.next.element;
52
+ if (typeof type !== 'string')
53
+ return null;
54
+
55
+ const nodeDef = nodeDefsByType.get(type);
56
+ if (!nodeDef)
57
+ throw new Error(`Unknown type ${type}`);
49
58
 
50
- objects.set(diff.next.id, cube);
59
+ const nodeInstance = nodeDef.create();
60
+ objects.set(diff.next.id, nodeInstance);
51
61
 
52
- return cube;
62
+ return nodeInstance;
53
63
  }
54
64
 
65
+ const removeObject = (nodeInstance) => {
66
+ nodeInstance.dispose();
67
+ };
68
+
55
69
  const renderObject = (diff/*: CommitDiff*/)/*: Object3D[]*/ => {
56
- const object = objects.get(diff.next.id) || createObject(diff);
70
+ const nodeInstance = objects.get(diff.next.id) || createObject(diff);
57
71
 
58
72
  const { ref } = (diff.next.element.props/*: any*/);
59
73
  if (diff.prev.pruned && typeof ref === 'object' && ref)
60
- ref.current = object;
61
-
62
- return [object];
74
+ ref.current = nodeInstance && nodeInstance.object;
75
+
76
+ const children = diff.diffs.map(renderObject).flat(1);
77
+
78
+ if (!nodeInstance)
79
+ return children;
80
+
81
+ if (diff.next.pruned)
82
+ return (removeObject(nodeInstance), []);
83
+
84
+ const props = calculatePropsDiff(diff.prev.element.props, diff.next.element.props);
85
+ nodeInstance.update([...props.values()]);
86
+
87
+ return [nodeInstance.object];
63
88
  };
64
89
  const attachObjects = (scene, children) => {
65
90
  scene.add(...children);