Skip to content

Commit f3bba1a

Browse files
authored
feat(Center): Center[object] (#2511)
* [box3] * Center[object] * tweaks * doc * doc
1 parent 2eca82b commit f3bba1a

File tree

3 files changed

+164
-41
lines changed

3 files changed

+164
-41
lines changed
Lines changed: 120 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
1-
import * as React from 'react'
2-
import { Vector3 } from 'three'
31
import { Meta, StoryObj } from '@storybook/react-vite'
42

53
import { Setup } from '../Setup'
6-
import { useTurntable } from '../useTurntable'
74

8-
import { Box, Center, useGLTF } from '../../src'
9-
import { ComponentProps } from 'react'
5+
import { Box, Center, Cylinder, Gltf } from '../../src'
6+
import { Ref, useMemo, useState } from 'react'
7+
import { Box3, Vector3 } from 'three'
108

119
export default {
1210
title: 'Staging/Center',
1311
component: Center,
1412
decorators: [
1513
(Story) => (
16-
<Setup cameraPosition={new Vector3(0, 0, -10)}>
14+
<Setup cameraPosition={new Vector3(2, 2, 2)}>
1715
<Story />
1816
</Setup>
1917
),
@@ -22,30 +20,130 @@ export default {
2220

2321
type Story = StoryObj<typeof Center>
2422

25-
const SimpleExample = (props: ComponentProps<typeof Center>) => {
26-
const { scene } = useGLTF('LittlestTokyo.glb')
23+
function LittlestTokyo({ catMeshRef }: { catMeshRef?: Ref<THREE.Mesh> }) {
24+
return (
25+
<group>
26+
<Gltf src="LittlestTokyo.glb" scale={0.002} />
27+
28+
{catMeshRef && (
29+
// we draw a box around the cat
30+
<Box ref={catMeshRef} position={[0.095, 0.35, 0.25]} args={[0.2, 0.2, 0.2]}>
31+
<meshStandardMaterial color="green" transparent opacity={0.5} />
32+
</Box>
33+
)}
34+
</group>
35+
)
36+
}
37+
38+
//
39+
40+
/**
41+
* `children` are centered, by default at (0,0,0)
42+
*/
43+
export const St1 = {
44+
render: () => (
45+
<>
46+
<axesHelper />
47+
<Center>
48+
<group
49+
position={[100, 100, 100]} // whatever inner position
50+
>
51+
<LittlestTokyo />
52+
</group>
53+
</Center>
54+
</>
55+
),
56+
name: 'Default',
57+
} satisfies Story
58+
59+
//
60+
61+
/**
62+
* if `position` is set, children are centered at that position
63+
*/
64+
export const St2 = {
65+
render: () => (
66+
<>
67+
<axesHelper />
68+
<Center position={[0, 1, 0]}>
69+
<group
70+
position={[100, 100, 100]} // whatever inner position
71+
>
72+
<LittlestTokyo />
73+
</group>
74+
</Center>
75+
</>
76+
),
77+
name: '[position]',
78+
} satisfies Story
2779

28-
const ref = useTurntable()
80+
//
81+
82+
/**
83+
* At `top` of centered position
84+
*/
85+
export const St3 = {
86+
render: () => (
87+
<>
88+
<axesHelper />
89+
<Center position={[0, 1, 0]} top>
90+
<group position={[100, 100, 100]}>
91+
<LittlestTokyo />
92+
</group>
93+
</Center>
94+
</>
95+
),
96+
name: '[position][top]',
97+
} satisfies Story
98+
99+
//
100+
101+
function St4Scene() {
102+
const [catMesh, setCatMesh] = useState<THREE.Mesh | null>(null)
29103

30104
return (
31-
<Center position={[5, 5, 10]} {...props}>
32-
<Box args={[10, 10, 10]}>
33-
<meshNormalMaterial wireframe />
34-
</Box>
35-
<primitive ref={ref} object={scene} scale={[0.01, 0.01, 0.01]} />
36-
</Center>
105+
<>
106+
<axesHelper />
107+
<Center object={catMesh}>
108+
<group position={[100, 100, 100]}>
109+
<LittlestTokyo catMeshRef={setCatMesh} />
110+
</group>
111+
</Center>
112+
</>
37113
)
38114
}
39115

40-
function CenterScene(props: ComponentProps<typeof Center>) {
116+
/**
117+
* An inner `object` can be specified: its bounding box will be used to center (instead of the one of `children`, by default).
118+
*/
119+
120+
export const St4 = {
121+
render: () => <St4Scene />,
122+
name: '[object]',
123+
} satisfies Story
124+
125+
//
126+
127+
function St5Scene() {
128+
const [catMesh, setCatMesh] = useState<THREE.Mesh | null>(null)
129+
41130
return (
42-
<React.Suspense fallback={null}>
43-
<SimpleExample {...props} />
44-
</React.Suspense>
131+
<>
132+
<axesHelper />
133+
<Center object={catMesh} position={[0, 1, 0]} top>
134+
<group position={[100, 100, 100]}>
135+
<LittlestTokyo catMeshRef={setCatMesh} />
136+
</group>
137+
</Center>
138+
</>
45139
)
46140
}
47141

48-
export const CenterSt = {
49-
render: (args) => <CenterScene {...args} />,
50-
name: 'Default',
142+
/**
143+
* Inner "cat mesh" centered at `top` of (0,1,0) position
144+
*/
145+
146+
export const St5 = {
147+
render: () => <St5Scene />,
148+
name: '[object][position][top]',
51149
} satisfies Story

docs/staging/center.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ export type Props = ThreeElements['group'] & {
3232
disableY?: boolean
3333
/** Disable z-axis centering */
3434
disableZ?: boolean
35+
/** object to compute box3 from */
36+
object?: THREE.Object3D | null
3537
/** Precision, defaults to true, see https://threejs.org/docs/index.html?q=box3#api/en/math/Box3.setFromObject */
3638
precise?: boolean
3739
/** Callback, fires in the useLayoutEffect phase, after measurement */

src/core/Center.tsx

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint react-hooks/exhaustive-deps: 1 */
12
import { Box3, Vector3, Sphere, Group, Object3D } from 'three'
23
import * as React from 'react'
34
import { ThreeElements, useThree } from '@react-three/fiber'
@@ -34,6 +35,8 @@ export type CenterProps = Omit<ThreeElements['group'], 'ref'> & {
3435
disableY?: boolean
3536
/** Disable z-axis centering */
3637
disableZ?: boolean
38+
/** object to compute box3 from */
39+
object?: THREE.Object3D | null
3740
/** See https://threejs.org/docs/index.html?q=box3#api/en/math/Box3.setFromObject */
3841
precise?: boolean
3942
/** Callback, fires in the useLayoutEffect phase, after measurement */
@@ -46,6 +49,7 @@ export const Center: ForwardRefComponent<CenterProps, Group> = /* @__PURE__ */ R
4649
function Center(
4750
{
4851
children,
52+
object,
4953
disable,
5054
disableX,
5155
disableY,
@@ -66,11 +70,14 @@ export const Center: ForwardRefComponent<CenterProps, Group> = /* @__PURE__ */ R
6670
const ref = React.useRef<Group>(null!)
6771
const outer = React.useRef<Group>(null!)
6872
const inner = React.useRef<Group>(null!)
73+
74+
const [box3] = React.useState(() => new Box3())
75+
const [center] = React.useState(() => new Vector3())
76+
const [sphere] = React.useState(() => new Sphere())
77+
6978
React.useLayoutEffect(() => {
7079
outer.current.matrixWorld.identity()
71-
const box3 = new Box3().setFromObject(inner.current, precise)
72-
const center = new Vector3()
73-
const sphere = new Sphere()
80+
box3.setFromObject(object ?? inner.current, precise)
7481
const width = box3.max.x - box3.min.x
7582
const height = box3.max.y - box3.min.y
7683
const depth = box3.max.z - box3.min.z
@@ -87,22 +94,38 @@ export const Center: ForwardRefComponent<CenterProps, Group> = /* @__PURE__ */ R
8794
)
8895

8996
// Only fire onCentered if the bounding box has changed
90-
if (typeof onCentered !== 'undefined') {
91-
onCentered({
92-
parent: ref.current.parent!,
93-
container: ref.current,
94-
width,
95-
height,
96-
depth,
97-
boundingBox: box3,
98-
boundingSphere: sphere,
99-
center: center,
100-
verticalAlignment: vAlign,
101-
horizontalAlignment: hAlign,
102-
depthAlignment: dAlign,
103-
})
104-
}
105-
}, [cacheKey, onCentered, top, left, front, disable, disableX, disableY, disableZ, precise, right, bottom, back])
97+
onCentered?.({
98+
parent: ref.current.parent!,
99+
container: ref.current,
100+
width,
101+
height,
102+
depth,
103+
boundingBox: box3,
104+
boundingSphere: sphere,
105+
center: center,
106+
verticalAlignment: vAlign,
107+
horizontalAlignment: hAlign,
108+
depthAlignment: dAlign,
109+
})
110+
}, [
111+
cacheKey,
112+
onCentered,
113+
top,
114+
left,
115+
front,
116+
disable,
117+
disableX,
118+
disableY,
119+
disableZ,
120+
object,
121+
precise,
122+
right,
123+
bottom,
124+
back,
125+
box3,
126+
center,
127+
sphere,
128+
])
106129

107130
React.useImperativeHandle(fRef, () => ref.current, [])
108131

0 commit comments

Comments
 (0)