Git Product home page Git Product logo

react-three-flex's Introduction

@react-three/flex

Build Status Version Downloads Discord Shield

Placing content in THREE.js is hard. @react-three/flex brings the webs flexbox spec to react-three-fiber. It is based on Yoga, Facebook's open source layout engine for react-native.

npm install @react-three/flex

These demos are real, you can click them! They contain the full code, too.

Table of contents

Usage

Simply create layouts by wrapping your 3D objects in different <Box /> instances inside a <Flex /> container. This way they will be automatically placed in the 3D space following the flexbox specification just like in the DOM.

import { Flex, Box } from '@react-three/flex'

const Layout = () => (
  <Flex justifyContent="center" alignItems="center">
    <Box centerAnchor>
      <mesh geometry={box} />
    </Box>
    <Box centerAnchor flexGrow={1}>
      <mesh geometry={torus} />
    </Box>
  </Flex>
)

You can tweak the container and the boxes using standard CSS flex properties, like flexDirection or justifyContent for the container and flexGrow for the boxes. There are also shorthands, like align and justify. See the props docs below for more info.

Anchors

When positioning items, react-three-flex needs to know where the object anchor is: Yoga Layout expects the object position to be relative to the upper left corner, which is the same as the DOM expects.

Most THREE.js geometries, though, are positioned relative to the object center. To tell react-three-flex that your <Box /> positioning is relative to the center you need to set the centerAnchor prop to true.

<Box centerAnchor>
  <mesh geometry={sphere} />
</Box>

If you nest <Box /> elements, though, you need to set it to false. See Nesting.

Anchors

Stretching

By default @react-three/flex controls elements position only. In some cases you may want to control element sizing too. Since @react-three/flex has no information about how the inner content size works, you need to set your content size manually. To do so @react-three/flex provides you the container size in two ways:

  • Using a children render function:
<Flex>
  <Box width="auto" height="auto" flexGrow={1} centerAnchor>
    {(width, height) => <Plane args={[width, height]} />}
  </Box>
</Flex>
  • Using a hook:
function Inner() {
  const [width, height] = useFlexSize()
  return <Plane args={[width, height]} />
}

function Outer() {
  return (
    <Flex>
      <Box width="auto" height="auto" flexGrow={1} centerAnchor>
        <Inner />

Remember that the useFlexSize hook works ONLY if your <Box/> is outside the component.

Invalidation and Reflow

While the DOM's Flexbox has full control over all the changes of the tree, @react-three/flex runs on React, hence it has no way to know if a children size or shape has changed. For performance reasons Flex layout calculation does not run every frame, and it has to be triggered manually in some cases.

What will trigger a reflow:

  • <Flexbox/> props changes (alignItems, size, ...)
  • <Box/> props changes (flexGrow, margin, ...)
  • <Flexbox/> and <Box/> rerenders with children differences
function AnimatedBox() {
  // Since <Box/> is inside the component, setting the state will rerender it, thus causing a reflow.
  // ⚠️ If <Box/> were outside this component, this would NOT cause a reflow!
  const [state, setState] = useState(true)
  useInterval(() => setState((s) => !s), 1000)
  return (
    <Box centerAnchor>
      <mesh>
        <boxBufferGeometry attach="geometry" args={[state ? 10 : 30, 10, 10]} />

This will NOT cause a reflow!

function AnimatedBox() {
  // ⚠️ Setting state does not rerender <Box/> since it's in the parent
  // ‼️ No Reflow!!
  const [state, setState] = useState(true)
  useInterval(() => setState((s) => !s), 1000)
  return (
    <mesh scale={[state ? 1 : 3, 1, 1]}>
      <boxBufferGeometry attach="geometry" />
    </mesh>
  )
}

function Layout() {
  return (
    <Flex>
      <Box centerAnchor>
        <AnimatedBox />

For every other case (setting size with the useFrame hook, performing react-spring animation, or <Box/> are not rerendered) you'll need to manually cause a reflow, using the useReflow() hook. Reflows requests are batched every frame so you can call it from hundreds of components without performance issues.

Animation with useFrame():

function AnimatedBox() {
  const ref = useRef()
  const reflow = useReflow()
  useFrame(({ clock }) => {
    ref.current.scale.x = 1 + Math.sin(clock.getElapsed())
    reflow()
  })
  return (
    <Box centerAnchor>
      <mesh ref={ref}>

<Box/> outside of component:

function AnimatedBox() {
  const [state, setState] = useState(true)
  useInterval(() => setState((s) => !s), 1000)
  const reflow = useReflow()
  useEffect(reflow, [state])
  return (
    <mesh ref={ref} scale={[state ? 1 : 3, 1, 1]}>

Sizing

@react-three/flex differs from DOM Flexbox in that it relies on a parent container for the root flex. It is required to specify its dimensions using size prop for wrapping and to be responsive.

<Flex flexDirection="row" flexWrap="wrap" size={[300, 200, 0]}>
  {/* ... */}
</Flex>

⚠️ WATCH OUT! Yoga flexbox engine uses whole integer numbers to perform layout calculation to preserve precision - @react-three/flex multiplies every element size and flex prop by the scaleFactor of the root flex container. By default it's 100, and works well for small scenes. If you use a different scene scale, make sure to tweak it accordingly.

Bounds

Axis Orientation

Another important difference with DOM Flexbox is that you have to specify the plane of the container in 3D. The elements will be positioned in the 2D plane given by the two axes, using width and height calculated along the two axes.

The 2D flex container width and height will be calculated by looking at the size prop with respect of the chosen axes (100 for x and 200 for y in this example).

The default plane is xy, the other possibilites are yz and xz.

<Flex plane="xy" size={[100, 200, 0]}>
  {/* ... */}
</Flex>

Axes Orientation

Margin and Padding

For every <Flex /> and <Box /> component you can specify the margin and padding like in DOM elements.

<Flex flexDirection="row" size={[300, 200, 0]} padding={30} margin={5}>
  <Box padding={5} marginTop={5} centerAnchor>
    <mesh geometry={sphere} />
  </Box>
</Flex>

Margin

Nesting

Since a <Flex /> component works the same way as a DOM one, you can easily make complex layouts by nesting flex containers.

<Flex flexDirection="row" flexWrap="wrap" size={[50, 0, 0]}>
  <Box centerAnchor>
    <mesh geometry={sphere} />
  </Box>
  <Box flexDirection="column" flexWrap="no-wrap">
    <Box centerAnchor>
      <mesh geometry={sphere} />
    </Box>
    <Box centerAnchor>
      <mesh geometry={box} />
    </Box>
  </Box>
</Flex>

Measuring the container

When building responsive layouts you might need to synchronize the size of the 3D Flex container with the DOM, for example to synchronize scroll position or to modify the height of a scroll container. To make it easier, you can use the onReflow prop on the root <Flex> component that will be called every time the flex layout is recalculated - e.g. when any content changes.

<Flex onReflow={(totalWidth, totalHeight) => ...}>
 {/* ... */}
</Flex>

API

You can find a full list of props here.

<Flex
  size={[1, 1, 1]} // Total size of the flex container, see above
  position={[0, 0, 0]} // Default - position for the flex container in the scene
  direction="ltr" // Default - right to left or right to left
  plane="xy" // Default - plane axes, see above
  scaleFactor={100} // Default - integer scale factor, see above (Sizing)
  onReflow={fn} // Called everytime the layout is recalculated
  {...R3FlexProps} // Standard Flexbox props, described below
>
  <Box>{/* ... */}</Box>
</Flex>
<Box
  centerAnchor // If the inner content position is relative to its center, see above (Anchors)
  {...R3FlexProps} // Standard Flexbox props, described below
>
  <mesh geometry={box} />
</Box>

Or you can pass a function as children:

<Box>{(width, height) => <Model width={width} height={height} />}</Box>

Flexbox props

Both <Flex/> and <Box /> components share the same Flexbox props API from Yoga. The library also provides string and number inputs for convenience and shorthands.

Example:

// Flex with padding top set to 10, alignItems to 'center', justifyContent to 'space-around' and flexWrap to 'wrap'
<Flex pt={10} align="center" justify="space-around" wrap="wrap">
  {/* ... */}
</Flex>

react-three-flex's People

Contributors

chnicoloso avatar chriscrosscrash avatar codyjasonbennett avatar dai-shi avatar dependabot[bot] avatar drcmda avatar giulioz avatar gsimone avatar iinfin avatar inokawa avatar madou avatar pavel-mazhuga avatar saitonakamura avatar stephencorwin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-three-flex's Issues

Hook to get the position, similar to useFlexSize()

Essentially I need this:

const node = useFlexNode();
const { left, top } = node.getComputedLayout();

but

  1. This is in yoga-units (e.g, scaled by default x100)
  2. I don't think this is proper code for hooks, right? I'm not supposed to imperatively call getComputedLayout like this in a render function?

Why do I need this? My flex-items are buttons. Behind those buttons is the nested "page". The buttons use masks to show the page as you hover, since the page is actually "physically" behind the button. If you click the button you essentially fly through the button to the nested page. For buttons that are not perfectly in the center on the viewport I need to move the nested page. Right now it's only slightly offset by the flex style of the button, but since this is farther way I need to scale the position with the distance so you can properly look through the button and see the page (essentially a line from camera, through the center of the button to the center of the next page). Hence I need the position of each box item.

Better Reflow Performance

We should avoid recalculate the sizes of all the components during a reflow, and also allow manual size passing

Update to a newer version of react-pdf/yoga

Newer features introduced into yoga have been released, including row/column gap. React-PDF has had updates, with react-pdf/yoga at v4.1.2, while r3flex is still using v2.0.4.

I would suggest an audit of new API changes and a ticket which covers the upgrade and implementation of the new API. I'd love to work on this though I don't know if I have the bandwidth to take the lead on it, so for now just a suggestion 😁

Boundingbox for Flex

@drcmda
I'm trying to get a boundingbox for the Flex container.

See sandbox here: https://codesandbox.io/s/hardcore-fog-0eqrh?file=/src/App.js

I've added a wireframe box with the same position and size as the Flex container. I would expect that the first flex item would start at the side of the container (because justify = start), however it starts at the center of the container. The container wraps correctly (hit 4... it wraps after 3 items), but I would expect all the items to be contained inside the wireframe.

Why is this not happening? Is my current method not correct? Is there any other way to retrieve the bounding box for a flex container?

Initial bounding box calculation are wrong when using nested boxes

I used a message and codesandbox from a discord server
https://discord.com/channels/740090768164651008/745777196911689748/860103131593310238
https://codesandbox.io/s/playing-with-react-three-flex-forked-05wo8?file=/src/App.js

The problem is that initial bounding box for a group that contains other boxes is reported incorrectly (width should be 200, but it's 100). This probably has to do with that until flex is calculated first time, boxes inside other boxes positioned in the same place

(Beginner) Faulty behaviour with changing Content and on Load

Running into faulty behaviour trying out flex. Sadly I couldn't find a fix for it just yet.
I first mapped the data to the content but because of the faulty behaviour I tried rewriting it resulting in the code how it is now.

There are 2 issues I can't seem to find a fix for.:
First, on load flex is not executed, it needs a reflow to wake up.
Second, larger text causes most of the time wrong positioning and needs sometimes several switching through other content to get it shown properly. Going through several times more can cause it to show faulty again.
Am I missing something?
I am causing reflow, by changing the slide Position that is kept in a store.

https://codesandbox.io/s/lively-browser-hz0yj?file=/src/App.js Working Code here. The 3 cubes are "back", "reset", "next"

A small imageroll for a quick overview of the issue. The last 3 images basically show the behaviour that should be happening but somehow isn't. All images are one sessions and going through them with the cubes.

chrome_2I5VuVj9RI
chrome_QSjwohOL9I
chrome_qL2a4ihPDW
chrome_oL1aNvaBj3
chrome_sVovOpU9Lf
chrome_fnO2KwDN1F

with
const [state, setState] = useState(true) useInterval(() => setState((s) => !s), 1000)
the called reflow fixes the issue. But why doesn't it properly show it to begin with?

Use scrollarea and mesh events

Hello,

I'm currently using a scrollarea to position some of the content in my scene.

I would like to use react-three-fiber events on meshes like onPointerEnter also, but the scrollarea is blocking events to the canvas.

Any ideas?

Feature request: ignore invisible

I have some hidden elements I use for raycasting, but don't want them to affect the spacing of things. These are hidden with visible=false. It would be great if there was a flag: <Box ignoreInvisible />

that would ignore those invisible elements when computing the box size.

Axis orientation

Hi all!

First of all, thanks for this awesome library! It is really really cool!

I've had a hard time figuring out how the axis orientation works. I've created a sandbox here to get some more grip on it: https://codesandbox.io/s/hardcore-fog-0eqrh?file=/src/App.js

Note that I've added the AxesHelper to help me know which axis is what.

While typing this everything started making a lot more sense... hopefully this process also helps others...

I've concluded that - when I choose flexDirection="row" - it first layouts the boxes (you pick how many boxes you want, but after 4 boxes it should wrap) on the axis of the first character of the plane. So say, for plane XY the boxes layout in the direction of the X axis, but after 3 boxes, the wrapping occurs on the Y axis. Hence, XY. When I pick flexDirection="column" it does the same thing, but now it layouts on Y first (second character) and then wraps on X.

This is consistent for all planes. The difference between XY and XZ is that the first one will wrap on the Y axis, and the second will wrap on the Z axis.

When there is no wrapping configured, there is no difference between selecting XY or XZ - when selected flexDirection="row".

I do wonder why it layouts like this:

  1. XY and row: on the X axis, from the origin (of the scene) TO the direction of the AxesHelper X line
  2. XY and column: on the Y axis, from the origin (of the scene) AWAY from the direction of the AxesHelper Y line
  3. YZ and row: on the Y axis, from the origin (of the scene) TO the direction of the AxesHelper Y line
  4. YZ and column: on the Z axis, from the origin (of the scene) AWAY from the direction of the AxesHelper Z line
  5. XZ and row: on the X axis, from the origin (of the scene) TO the direction of the AxesHelper X line (same as 1.)
  6. XZ and column: on the Z axis, from the origin (of the scene) AWAY from the direction of the AxesHelper Z line (same as 4.)

So:
A) I cannot layout AWAY from the direction of the AxesHelper X line. Only TO (1. and 5.)
B) I can layout TO and AWAY to/from the direction of the AxesHelper Y line (2. and 3.)
C) I cannot layout TO the direction of the AxesHelper Z line. Only AWAY (4. and 6.)

Is this by design? If so, how can I do A or C? Should I flip something, so that it goes to the correct direction?

Stretch Elements

Right now flex layout can only reposition element, but in DOM flex you can also resize them, using align-items: stretch.
Should we implement this as well?

Sticky

Hey there! :)

Awesome project, thanks for that!

I'm wondering if there is any "simple" logic to get a sticky effect on an element that scrolls into view.
E.g. an image thats scroll into view stays there for some scrolls and then it just continues?

Thanks & br, sebi

Center of flex element is not aligned with parent object center.

I want to be able to align items outside of a Flex element with the Flex element. However this seems challenging to do because the flex element itself has a center that is not aligned with the parent center.

Take this code, for example, viewable in this sandbox:

    <group position-z={-1}>
      <Flex flexDirection="row" alignItems="baseline">
        <Box>
          <mesh scale-y={1.5} >
            <meshBasicMaterial color="green" />
            <boxBufferGeometry />
          </mesh>
        </Box>
        <Box>
          <mesh>
            <meshBasicMaterial color="red" />
            <boxBufferGeometry />
          </mesh>
        </Box>
      </Flex>
      <mesh scale-y={1.5} >
        <meshBasicMaterial color="yellow"/>
        <planeBufferGeometry args={[2,2]} />
      </mesh>
    </group>

This results in:
Screen Shot 2021-10-22 at 11 16 52 AM

I would expect the rendered plane to be center aligned both vertically and horizontally with the Flex element, but the horizontal alignment is off. Is there a recommended way to achieve this?

Scaled box results in child elements collapsing on each other.

Given this code, tryable in this sandbox where I want to scale a box and all of it's children down:

     <Flex flexDirection="row">
        <Box flexDirection="column" scale-x={0.5} scale-y={0.5}>
          <Box>
            <mesh>
              <meshBasicMaterial color="yellow" />
              <boxBufferGeometry />
            </mesh>
          </Box>
          <Box>
            <mesh>
              <meshBasicMaterial color="red" />
              <boxBufferGeometry />
            </mesh>
          </Box>
        </Box>
      </Flex>

The resulting scene looks like:

Screen Shot 2021-10-25 at 2 34 29 PM

When I'd expect the boxes to not be collapsed on each other, but stacked vertically.

Is there a way to scale down a box properly and have its children properly aligned?

Unexpected positioning of Box object

Issue description:
When placing a Box object within a Flex object, unexpected positional values are observed. For flex-direction="column", the x position is 0.5 and for flex-direction="row", the y position is -0.5 when justifyContent="center" and alignItems="center" are applied. This is inconsistent with the expected values of 0.

Expected behavior:
The Box object, when placed inside a Flex object with justifyContent="center" and alignItems="center" applied, should result in x and y position values of 0.

Actual behavior:
The actual x position for flex-direction="column" is 0.5, and the y position for flex-direction="row" is -0.5.

Steps to reproduce:

  1. Create a Box object inside a Flex object.
  2. Apply justifyContent="center" and alignItems="center" to the Flex object.
  3. Log the x position value for flex-direction: column and y position value for flex-direction: row.

Minimal reproduction of the problem with instructions:
You can view and interact with a minimal reproduction of the issue here: https://stackblitz.com/edit/stackblitz-starters-n7vn5a?file=src%2FApp.tsx. In this example, clicking on the canvas will log the positional values.

Impact:
This issue is causing the Box object to not center properly within the Flex object and is also affecting the usage example in the documentation.

Additional notes:
Uncertain if this issue is a bug or a misunderstanding of the library's expected functionality. Any clarification or guidance would be highly appreciated.

Broken Release GH action

> [email protected] prepublishOnly .
> npm run build


> [email protected] build /home/runner/work/react-three-flex/react-three-flex

> rollup -c

src/index.ts → dist/index.js, dist/index.cjs...
[!] (plugin esbuild) Error: The service was stopped
Error: The service was stopped
    at /home/runner/work/react-three-flex/react-three-flex/node_modules/esbuild/lib/main.js:405:31
    at Socket.afterClose (/home/runner/work/react-three-flex/react-three-flex/node_modules/esbuild/lib/main.js:331:7)
    at Socket.emit (events.js:327:22)
    at endReadableNT (_stream_readable.js:1220:12)
    at processTicksAndRejections (internal/process/task_queues.js:84:21)

Debug view

What is the best way to debug why Boxes/children ended in places where they ended. I'm trying yo understand what gone wrong in my markup and having wireframe of flex container/highlight of the margins would be very helpful. But I don't understand how to do this properly (because of center anchor, for instance you can't set this on a flex). Is there any example I can look up?

For instance I can do this, but I can't really figure out whether it mesh inside flex positioned incorrectly or items themselves
https://codesandbox.io/s/eager-elgamal-hbqz3?file=/src/index.tsx

Vite compatibility

Hey,

The problem

I was trying to use @react-three/flex in a vite based project (instead of the current example that uses CRA) but I landed on multiple issues.
**EDIT: Actually I fixed it while writing this, so I guess I should still post it for anyone that is in the same situation.
Scroll to the bottom for the solution. **

Naive installation

Uncaught ReferenceError: _a is not defined ... at node_modules/yoga-layout-prebuilt/yoga-layout/dist/entry-browser.js (:3000/node_modules/.vite/@react-three_flex.js?v=63c167e0:27914)

Which can be solved either by replacing the string of the problematic file or by adding <script>window._a = undefined;</script> in the index.html

Then there is an issue with ‘@babel/runtime/helpers/esm/extends’

react-dom.development.js:11340 Uncaught TypeError: e[h(...)] is not a function
    at index.js:formatted:1

Used in those lines

default:
        return e[`set${h(n2)}`](i2);

And I couldn't find a solution for that, so I tried looking for issues/PR and stumbled upon this one #37

yoga wasm PR

Then I

  • added it as package.json dependency with "react-three-flex": "pmndrs/react-three-flex#37/head"
  • added the corresponding vite.resolve.alias { find: "@react-three/flex", replacement: "./node_modules/react-three-flex/src/index.ts" },
  • included it in deps vite optimizeDeps: { include: ["@react-three/flex"] },

Which spawns another problem

index.js:1 Uncaught (in promise) RuntimeError: abort(RuntimeError: abort(both async and sync fetching of the wasm failed). Build with -s ASSERTIONS=1 for more info.). Build with -s ASSERTIONS=1 for more info.
    at z (index.js:1)
    at index.js:1

Something that might be worth noting is that this PR seems quite outdated so that might just be the reason (since i'm getting this log index.js:1 npm install react-three-fiber is deprecated, use @react-three/fiber instead!)

repro repository here, using the Text component from https://codesandbox.io/embed/7psew

Solution

**EDIT: follow the steps from the yoga wasm PR section and then add this file in /public/yoga.wasm **

Codesandbox examples not working on iOS

Expected Behavior

On my iphone i'm trying to see the examples from codesandbox.io directly from safari ios.

Actual Behavior

After initialisation of codesandbox transpiling, sometimes can display a blank page or refresh automatically.

Steps to Reproduce the Problem

Try to use any examples on Safari iOS with iphone directly to see behavior (tried in iOS 13.6.1 and didn't worked as well) .

Specifications

Don't know if it's a codesanbox problem or just react-three-flex not working on mobile. Can be a reproduction of this issue
Codesanbox.io #839

  • Version: iOS 13.6
  • Platform: Safari iOS``

Box size control

I've tried to implement a UI with react-three-flex that is bound to the camera.
After rotating the camera and in case of content change in the UI a BoundingBox had to be recalculated.
Since the recalculation is based on setFromObject the global orientation is included in this calculation leading to a change in the flex's box size.

My solution would be to have an option for explicit sizing of Boxes.
A FlexText component f.e. could then calculate its size on every content change and explicitly notify the box with the exact and correct size.

I already made a POC implementation, which worked very well and made the camera bound UI possible.
If the solution is viable I could make my POC into a more generalizing feature and submit a PR.

Hope to hear your feedback about this :)
I really like this library since I see it playing a crucial role (at least in my development) in building good dynamic user interfaces in R3F, where HTML can't do the job (e.g. WebXR).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.