Skip to content

Commit

Permalink
feat(Align): add component, demo, docs (#544)
Browse files Browse the repository at this point in the history
Co-authored-by: alvarosabu <[email protected]>
  • Loading branch information
andretchen0 and alvarosabu authored Dec 10, 2024
1 parent e71cdad commit e3ee52c
Show file tree
Hide file tree
Showing 8 changed files with 283 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ export default defineConfig({
{ text: 'Precipitation', link: '/guide/staging/precipitation' },
{ text: 'Sparkles', link: '/guide/staging/sparkles' },
{ text: 'Ocean', link: '/guide/staging/ocean' },
{ text: 'Align', link: '/guide/staging/align' },
{ text: 'SoftShadows', link: '/guide/staging/soft-shadows' },
{ text: 'Grid', link: '/guide/staging/grid' },
],
Expand Down
20 changes: 20 additions & 0 deletions docs/.vitepress/theme/components/AlignDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script setup lang="ts">
import { Align, OrbitControls } from '@tresjs/cientos'
import { TresCanvas } from '@tresjs/core'
</script>

<template>
<TresCanvas clear-color="#222">
<TresPerspectiveCamera />
<OrbitControls />

<TresAxesHelper :scale="2" />

<Align top right back>
<TresMesh>
<TresBoxGeometry />
<TresMeshNormalMaterial />
</TresMesh>
</Align>
</TresCanvas>
</template>
1 change: 1 addition & 0 deletions docs/component-list/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export default [
{ text: 'Sparkles', link: '/guide/staging/sparkles' },
{ text: 'Ocean', link: '/guide/staging/ocean' },
{ text: 'Fit', link: '/guide/staging/fit' },
{ text: 'Align', link: '/guide/staging/align' },
{ text: 'SoftShadows', link: '/guide/staging/soft-shadows' },
{ text: 'Grid', link: '/guide/staging/grid' },
],
Expand Down
51 changes: 51 additions & 0 deletions docs/guide/staging/align.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Align

<DocsDemo>
<AlignDemo />
</DocsDemo>

Calculates a bounding box around its children and aligns them as a group within their parent. The component measures its contents and realigns on every frame unless `cacheKey` is set.

## Usage

<<< @/.vitepress/theme/components/AlignDemo.vue

## Props

All props are optional.

| Prop | Description |
| ------------ | ----------------------------------- |
| `top` | If `true`, aligns bounding box bottom to `0` on the y-axis |
| `bottom` | If `true`, aligns bounding box top to `0` on the y-axis. |
| `left` | If `true`, aligns bounding box right to `0` on the x-axis. |
| `right` | If `true`, aligns bounding box left to `0` on the x-axis. |
| `front` | If `true`, aligns bounding box back to `0` on the z-axis. |
| `back` | If `true`, aligns bounding box front to `0` on the z-axis. |
| `disable` | If `true`, disables alignment on all axes. |
| `disableX` | If `true`, disables alignment on the x-axis. |
| `disableY` | If `true`, disables alignment on the y-axis. |
| `disableZ` | If `true`, disables alignment on the z-axis. |
| `precise` | See [Box3.setFromObject](https://threejs.org/docs/index.html?q=box3#api/en/math/Box3.setFromObject). |
| `onAlign` | Callback that fires when updating, after measurement. |
| `cacheKey` | If set, component will only update when `cacheKey`'s value changes. If unset, component will update every frame. |

## `OnAlignCallbackProps`

```ts
export interface OnAlignCallbackProps {
/** The next parent above <Align /> */
parent: Object3D
/** The outmost container group of the <Align /> component */
container: Object3D
width: number
height: number
depth: number
boundingBox: Box3
boundingSphere: Sphere
center: Vector3
verticalAlignment: number
horizontalAlignment: number
depthAlignment: number
}
```
61 changes: 61 additions & 0 deletions playground/vue/src/pages/staging/AlignDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<script setup lang="ts">
import { Align, OrbitControls } from '@tresjs/cientos'
import { TresCanvas } from '@tresjs/core'
import { TresLeches, useControls } from '@tresjs/leches'
import '@tresjs/leches/styles'
const elapsed = shallowRef(0)
let intervalId: ReturnType<typeof setInterval>
onMounted(() => {
intervalId = setInterval(() => {
elapsed.value += 1000 / 30
}, 1000 / 30)
})
onUnmounted(() => {
clearInterval(intervalId)
})
const ctrl = useControls({
top: false,
left: false,
front: false,
bottom: false,
right: false,
back: false,
disable: false,
disableX: false,
disableY: false,
disableZ: false,
useCacheKey: false,
vIf: true,
})
</script>

<template>
<TresLeches />
<TresCanvas>
<TresPerspectiveCamera :position="[0, 0, 5]" />
<OrbitControls />
<TresAxesHelper :scale="2" :position="[0, 0, 0]" />
<Align
v-if="ctrl.vIf.value.value"
:left="ctrl.left.value.value"
:right="ctrl.right.value.value"
:top="ctrl.top.value.value"
:bottom="ctrl.bottom.value.value"
:front="ctrl.front.value.value"
:back="ctrl.back.value.value"
:disable="ctrl.disable.value.value"
:disable-x="ctrl.disableX.value.value"
:disable-y="ctrl.disableY.value.value"
:disable-z="ctrl.disableZ.value.value"
:cache-key="ctrl.useCacheKey.value.value ? (() => Math.floor(elapsed * 0.001)) : undefined"
>
<TresMesh :position-x="Math.cos(elapsed * 0.001) * 2" :position-y="Math.sin(elapsed * 0.001) * 2" :scale-z="Math.cos(elapsed * 0.001) * 2">
<TresBoxGeometry />
<TresMeshNormalMaterial />
</TresMesh>
</Align>
</TresCanvas>
</template>
5 changes: 5 additions & 0 deletions playground/vue/src/router/routes/staging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ export const stagingRoutes = [
name: 'Fit',
component: () => import('../../pages/staging/fit/index.vue'),
},
{
path: '/staging/align',
name: 'Align',
component: () => import('../../pages/staging/AlignDemo.vue'),
},
{
path: '/staging/soft-shadows',
name: 'SoftShadows',
Expand Down
142 changes: 142 additions & 0 deletions src/core/staging/Align.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<script setup lang="ts">
import { useLoop } from '@tresjs/core'
import type { Group, Object3D } from 'three'
import { Box3, Sphere, Vector3 } from 'three'
import type { MaybeRefOrGetter } from 'vue'
import { shallowRef, toValue, watchEffect } from 'vue'
// NOTE: Sources
// https://github.com/pmndrs/drei/blob/master/src/core/Center.tsx
export interface OnAlignCallbackProps {
/** The next parent above <Align /> */
parent: Object3D
/** The outmost container group of the <Align/> component */
container: Object3D
width: number
height: number
depth: number
boundingBox: Box3
boundingSphere: Sphere
center: Vector3
verticalAlignment: number
horizontalAlignment: number
depthAlignment: number
}
export interface AlignProps {
top?: boolean
right?: boolean
bottom?: boolean
left?: boolean
front?: boolean
back?: boolean
/** Disable all axes */
disable?: boolean
/** Disable x-axis alignment */
disableX?: boolean
/** Disable y-axis alignment */
disableY?: boolean
/** Disable z-axis alignment */
disableZ?: boolean
/** See https://threejs.org/docs/index.html?q=box3#api/en/math/Box3.setFromObject */
precise?: boolean
/** Callback, fires when updating, after measurement */
onAlign?: (props: OnAlignCallbackProps) => void
/** Optional cacheKey to keep the component from recalculating on every render */
cacheKey?: MaybeRefOrGetter<any>
}
const props = withDefaults(defineProps<AlignProps>(), {
precise: true,
cacheKey: undefined,
})
const ref = shallowRef<Group>()
const outer = shallowRef<Group>()
const inner = shallowRef<Group>()
const box3 = new Box3()
const center = new Vector3()
const sphere = new Sphere()
function update() {
if (!outer.value || !inner.value || !ref.value) { return }
outer.value.matrixWorld.identity()
box3.setFromObject(inner.value, props.precise)
const width = box3.max.x - box3.min.x
const height = box3.max.y - box3.min.y
const depth = box3.max.z - box3.min.z
box3.getCenter(center)
box3.getBoundingSphere(sphere)
const yAlign = props.top ? height / 2 : props.bottom ? -height / 2 : 0
const xAlign = props.left ? -width / 2 : props.right ? width / 2 : 0
const zAlign = props.front ? depth / 2 : props.back ? -depth / 2 : 0
outer.value.position.set(
props.disable || props.disableX ? 0 : -center.x + xAlign,
props.disable || props.disableY ? 0 : -center.y + yAlign,
props.disable || props.disableZ ? 0 : -center.z + zAlign,
)
// Only fire onCentered if the bounding box has changed
if (typeof props.onAlign !== 'undefined') {
props.onAlign({
parent: ref.value.parent!,
container: ref.value,
width,
height,
depth,
boundingBox: box3,
boundingSphere: sphere,
center,
verticalAlignment: yAlign,
horizontalAlignment: xAlign,
depthAlignment: zAlign,
})
}
}
let off: (() => void) | null = null
let cacheKey: any = null
// NOTE: `useLoop` requires TresContext, which is only
// availble in setup. We need a handle in useEffect,
// so keep a reference.
const loop = useLoop()
watchEffect(() => {
// NOTE: update whenever cachekey changes.
// NOTE: We might already always be updating, so `off`.
off?.()
off = null
// NOTE: Don't update if we resolve to the previous cacheKey value,
// unless cacheKey is null or undefined.
const nextKey = toValue(props.cacheKey)
if (nextKey === cacheKey && cacheKey !== null && cacheKey !== undefined) { return }
cacheKey = nextKey
if (props.cacheKey === null || props.cacheKey === undefined) {
off = loop.onBeforeRender(() => {
update()
}).off
}
else {
update()
}
})
defineExpose({ instance: ref, update })
</script>

<template>
<TresGroup ref="ref">
<TresGroup ref="outer">
<TresGroup ref="inner">
<slot></slot>
</TresGroup>
</TresGroup>
</TresGroup>
</template>
2 changes: 2 additions & 0 deletions src/core/staging/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Align from './Align.vue'
import Backdrop from './Backdrop.vue'
import ContactShadows from './ContactShadows.vue'
import Fit from './Fit.vue'
Expand All @@ -13,6 +14,7 @@ import Environment from './useEnvironment/component.vue'
import Lightformer from './useEnvironment/lightformer/index.vue'

export {
Align,
Backdrop,
ContactShadows,
Environment,
Expand Down

0 comments on commit e3ee52c

Please sign in to comment.