Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/pmndrs/drei
Browse files Browse the repository at this point in the history
  • Loading branch information
drcmda committed Nov 21, 2022
2 parents 2d2d2e8 + b77d6fc commit b47417b
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 20 deletions.
14 changes: 11 additions & 3 deletions README.md
Expand Up @@ -2312,7 +2312,13 @@ function Effects() {

Views use gl.scissor to cut the viewport into segments. You tie a view to a tracking div which then controls the position and bounds of the viewport. This allows you to have multiple views with a single, performant canvas. These views will follow their tracking elements, scroll along, resize, etc.

It is advisable to re-connect the event system to a parent that contains both the canvas and the html content. This ensures that both are accessible/selectable and even allows you to mount controls or other deeper integrations into your view.
It is advisable to re-connect the event system to a parent that contains both the canvas and the html content.
This ensures that both are accessible/selectable and even allows you to mount controls or other deeper
integrations into your view.

> Note that `@react-three/fiber` newer than `^8.1.0` is required for `View` to work correctly if the
> canvas/react three fiber root is not fullscreen. A warning will be logged if drei is used with older
> versions of `@react-three/fiber`.

```tsx
<View
Expand Down Expand Up @@ -2558,19 +2564,21 @@ Calculates a boundary box and centers the camera accordingly. If you are using c
<mesh />
</Bounds>
```

The Bounds component also acts as a context provider, use the `useBounds` hook to refresh the bounds, fit the camera, clip near/far planes or focus objects. `refresh(object?: THREE.Object3D | THREE.Box3)` will recalculate bounds. Since this can be expensive only call it when you know the view has changed. `clip` sets the cameras near/far planes. `fit` zooms and centers the view.
The Bounds component also acts as a context provider, use the `useBounds` hook to refresh the bounds, fit the camera, clip near/far planes, go to camera orientations or focus objects. `refresh(object?: THREE.Object3D | THREE.Box3)` will recalculate bounds, since this can be expensive only call it when you know the view has changed. `clip` sets the cameras near/far planes. `to` sets a position and target for the camera. `fit` zooms and centers the view.

```jsx
function Foo() {
const bounds = useBounds()
useEffect(() => {
// Calculate scene bounds
bounds.refresh().clip().fit()
// Or, focus a specific object or box3
// bounds.refresh(ref.current).clip().fit()
// bounds.refresh(new THREE.Box3()).clip().fit()
// Or, send the camera to a specific orientatin
// bounds.to({position: [0, 10, 10], target: {[5, 5, 0]}})
<Bounds>
<Foo />
```
Expand Down
21 changes: 20 additions & 1 deletion src/core/Bounds.tsx
Expand Up @@ -94,7 +94,7 @@ export function Bounds({ children, damping = 6, fit, clip, observe, margin = 1.2
}

if (controls?.constructor.name === 'OrthographicTrackballControls') {
// Put camera on a sphere along which it should moves
// Put camera on a sphere along which it should move
const { distance } = getSize()
const direction = camera.position.clone().sub(controls.target).normalize().multiplyScalar(distance)
const newPos = controls.target.clone().add(direction)
Expand All @@ -113,6 +113,25 @@ export function Bounds({ children, damping = 6, fit, clip, observe, margin = 1.2
invalidate()
return this
},
to({ position, target }: { position: [number, number, number]; target?: [number, number, number] }) {
current.camera.copy(camera.position)
const { center } = getSize()
goal.camera.set(...position)

if (target) {
goal.focus.set(...target)
} else {
goal.focus.copy(center)
}

if (damping) {
current.animating = true
} else {
camera.position.set(...position)
}

return this
},
fit() {
current.camera.copy(camera.position)
if (controls) current.focus.copy(controls.target)
Expand Down
68 changes: 62 additions & 6 deletions src/web/View.tsx
Expand Up @@ -6,14 +6,34 @@ const isOrthographicCamera = (def: any): def is THREE.OrthographicCamera =>
def && (def as THREE.OrthographicCamera).isOrthographicCamera
const col = new THREE.Color()

/**
* In `@react-three/fiber` after `v8.0.0` but prior to `v8.1.0`, `state.size` contained only dimension
* information. After `v8.1.0`, position information (`top`, `left`) was added
*
* @todo remove this when drei supports v9 and up
*/
type LegacyCanvasSize = {
height: number
width: number
}

type CanvasSize = LegacyCanvasSize & {
top: number
left: number
}

function isNonLegacyCanvasSize(size: Record<string, number>): size is CanvasSize {
return 'top' in size
}

export type ContainerProps = {
scene: THREE.Scene
index: number
children?: React.ReactNode
frames: number
rect: React.MutableRefObject<DOMRect>
track: React.MutableRefObject<HTMLElement>
canvasSize: Size
canvasSize: LegacyCanvasSize | CanvasSize
}

export type ViewProps = {
Expand All @@ -27,6 +47,30 @@ export type ViewProps = {
children?: React.ReactNode
}

function computeContainerPosition(
canvasSize: LegacyCanvasSize | CanvasSize,
trackRect: DOMRect
): {
position: CanvasSize & { bottom: number, right: number }
isOffscreen: boolean
} {
const { right, top, left: trackLeft, bottom: trackBottom, width, height } = trackRect
const isOffscreen = trackRect.bottom < 0 || top > canvasSize.height || right < 0 || trackRect.left > canvasSize.width

if (isNonLegacyCanvasSize(canvasSize)) {
const canvasBottom = canvasSize.top + canvasSize.height
const bottom = canvasBottom - trackBottom
const left = trackLeft - canvasSize.left

return { position: { width, height, left, top, bottom, right }, isOffscreen }
}

// Fall back on old behavior if r3f < 8.1.0
const bottom = canvasSize.height - trackBottom

return { position: { width, height, top, left: trackLeft, bottom, right }, isOffscreen }
}

function Container({ canvasSize, scene, index, children, frames, rect, track }: ContainerProps) {
const get = useThree((state) => state.get)
const camera = useThree((state) => state.camera)
Expand All @@ -41,9 +85,11 @@ function Container({ canvasSize, scene, index, children, frames, rect, track }:
}

if (rect.current) {
const { left, right, top, bottom, width, height } = rect.current
const isOffscreen = bottom < 0 || top > canvasSize.height || right < 0 || left > canvasSize.width
const positiveYUpBottom = canvasSize.height - bottom
const {
position: { left, bottom, width, height },
isOffscreen,
} = computeContainerPosition(canvasSize, rect.current)

const aspect = width / height

if (isOrthographicCamera(camera)) {
Expand All @@ -61,8 +107,8 @@ function Container({ canvasSize, scene, index, children, frames, rect, track }:
camera.updateProjectionMatrix()
}

state.gl.setViewport(left, positiveYUpBottom, width, height)
state.gl.setScissor(left, positiveYUpBottom, width, height)
state.gl.setViewport(left, bottom, width, height)
state.gl.setScissor(left, bottom, width, height)
state.gl.setScissorTest(true)

if (isOffscreen) {
Expand All @@ -84,6 +130,16 @@ function Container({ canvasSize, scene, index, children, frames, rect, track }:
return () => setEvents({ connected: old })
}, [])

React.useEffect(() => {
if (isNonLegacyCanvasSize(canvasSize)) {
return
}
console.warn(
'Detected @react-three/fiber canvas size does not include position information. <View /> may not work as expected. ' +
'Upgrade to @react-three/fiber ^8.1.0 for support.\n See https://github.com/pmndrs/drei/issues/944'
)
}, [])

return <>{children}</>
}

Expand Down
25 changes: 15 additions & 10 deletions yarn.lock
Expand Up @@ -5043,9 +5043,9 @@ deep-is@^0.1.3, deep-is@~0.1.3:
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==

deep-object-diff@^1.1.0:
version "1.1.7"
resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.7.tgz#348b3246f426427dd633eaa50e1ed1fc2eafc7e4"
integrity sha512-QkgBca0mL08P6HiOjoqvmm6xOAl2W6CT2+34Ljhg0OeFan8cwlcdq8jrLKsBBuUFAZLsN5b6y491KdKEoSo9lg==
version "1.1.9"
resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.9.tgz#6df7ef035ad6a0caa44479c536ed7b02570f4595"
integrity sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==

deepmerge@^4.2.2:
version "4.2.2"
Expand Down Expand Up @@ -8199,9 +8199,9 @@ loader-runner@^2.4.0:
integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==

loader-utils@^1.1.0, loader-utils@^1.2.3:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613"
integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==
version "1.4.2"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3"
integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==
dependencies:
big.js "^5.2.2"
emojis-list "^3.0.0"
Expand Down Expand Up @@ -8634,11 +8634,16 @@ minimist-options@4.1.0:
is-plain-obj "^1.1.0"
kind-of "^6.0.3"

minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6:
minimist@^1.1.1, minimist@^1.2.5, minimist@^1.2.6:
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==

minimist@^1.2.0:
version "1.2.7"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18"
integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==

minipass-collect@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"
Expand Down Expand Up @@ -11291,9 +11296,9 @@ terser-webpack-plugin@^4.2.3:
webpack-sources "^1.4.3"

terser@^4.1.2, terser@^4.6.3:
version "4.8.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17"
integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==
version "4.8.1"
resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.1.tgz#a00e5634562de2239fd404c649051bf6fc21144f"
integrity sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==
dependencies:
commander "^2.20.0"
source-map "~0.6.1"
Expand Down

1 comment on commit b47417b

@vercel
Copy link

@vercel vercel bot commented on b47417b Nov 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.