Skip to content

Commit

Permalink
docs: start using mkdocs for assembling a site from markdown, and fil…
Browse files Browse the repository at this point in the history
…l in several core docs
  • Loading branch information
lojjic committed Feb 12, 2021
1 parent 42170e2 commit cfe5d27
Show file tree
Hide file tree
Showing 33 changed files with 268 additions and 78 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ Developing interactive 3D and 2D graphics should be as straightforward as develo

Troika was developed starting in 2016 by the [ProtectWise](https://protectwise.com) front end engineering team as an internal tool to enable rapid development of interactive 3D and 2D data visualizations in the ProtectWise Visualizer. As it matured and became an essential part of our toolkit, we realized it could be useful to the broader web developer community and decided to release it as an open source project.

Our initial public release can be considered mature/stable enough to use in production code. However you will find that documentation is still very incomplete. We're working on improving that, and you can see [the documentation](./docs/overview.md) that we have so far. We also recommend checking out the various examples ([live version](https://troika-examples.netlify.com/) and [source code](./packages/troika-examples)) and the inline code documentation for the [core facade classes](./packages/troika-core/src/facade/) as good places to start.
Our initial public release can be considered mature/stable enough to use in production code. However you will find that documentation is still very incomplete. We're working on improving that, and you can see [the documentation](docs/index.md) that we have so far. We also recommend checking out the various examples ([live version](https://troika-examples.netlify.com/) and [source code](./packages/troika-examples)) and the inline code documentation for the [core facade classes](./packages/troika-core/src/facade/) as good places to start.


----

[![Build Status](https://travis-ci.org/protectwise/troika.svg?branch=master)](https://travis-ci.org/protectwise/troika)

[![Netlify Status](https://api.netlify.com/api/v1/badges/523722ef-0c71-4bdc-935d-575c73ec1838/deploy-status)](https://app.netlify.com/sites/troika-examples/deploys)
[![Netlify Status](https://api.netlify.com/api/v1/badges/523722ef-0c71-4bdc-935d-575c73ec1838/deploy-status)](https://app.netlify.com/sites/troika-examples/deploys)
6 changes: 0 additions & 6 deletions docs/animations-and-transitions.md

This file was deleted.

2 changes: 1 addition & 1 deletion docs/examples.md → docs/getting-started/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Examples
---


The `packages/troika-examples` directory contains several example scenes. These are intended to demonstrate a wide range of Troika's capabilities and some interesting techniques that it enables.
The [troika-examples package](https://github.com/protectwise/troika/tree/master/packages/troika-examples) contains several example scenes. These are intended to demonstrate a wide range of Troika's capabilities and some interesting techniques that it enables.


## Viewing the examples
Expand Down
File renamed without changes.
15 changes: 7 additions & 8 deletions docs/overview.md → docs/index.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
---
id: overview
title: What is Troika?
title: Overview
---


Troika is a JavaScript framework that simplifies the creation of interactive graphics in the browser, with a focus on 3D/WebGL, and optimized for data visualization use cases.

It provides:
Expand All @@ -19,11 +18,11 @@ Troika's main goal is to take aspects of developing interactive graphics that yo

## Troika core

At its core, Troika manages a simple mapping from a declarative scene descriptor to a tree of special objects called [_Facades_](facade-basics.md). Each `Facade` is a component that knows how to synchronize its state, set as flat properties by the scene descriptor, to a more complex underlying API.
At its core, Troika manages a simple mapping from a declarative scene descriptor to a tree of special objects called [_Facades_](troika-core/facades.md). Each `Facade` is a component that knows how to synchronize its state, set as flat properties by the scene descriptor, to a more complex underlying API.

On top of that, Troika builds in some things like an event subscription model and declarative animations and transitions for facade properties.

[Learn more about Troika core concepts.](facade-basics.md)
[Learn more about Troika core concepts.](troika-core/facades.md)


## Troika for 3D
Expand All @@ -34,9 +33,9 @@ Troika manages the Three.js renderer and common things like world matrix calcula

As a very rough analogy: if Three.js provides a DOM for WebGL, then you could consider Troika to be like ReactJS for managing that DOM. It simplifies things, but you still need to know the DOM.

Troika 3D also provides some more advanced capabilities like: position-synced [HTML overlays](3d-html-overlays.md), an easy-to-use [GPU instancing](3d-instancing.md) abstraction, high quality [3D text rendering](3d-text.md), utilities for [manipulating shaders](3d-shader-utils.md), and [WebXR support](3d-webxr.md).
Troika 3D also provides some more advanced capabilities like: position-synced [HTML overlays](troika-3d/3d-html-overlays.md), an easy-to-use [GPU instancing](troika-3d/3d-instancing.md) abstraction, high quality [3D text rendering](troika-3d/3d-text.md), utilities for [manipulating shaders](troika-3d/3d-shader-utils.md), and [WebXR support](troika-xr/3d-webxr.md).

[Learn more about Troika 3D.](3d-overview.md)
[Learn more about Troika 3D.](troika-3d/3d-overview.md)


## Troika for 2D
Expand All @@ -45,12 +44,12 @@ As a separate package, Troika also provides the ability to define using the 2D C

This can be nice on its own when you don't need 3D, but is also useful as a graceful fallback for when WebGL isn't available in the browser.

[Learn more about Troika 2D.](2d-overview.md)
[Learn more about Troika 2D.](troika-2d/2d-overview.md)


## Framework integration

Currently Troika provides React components (`Canvas3D` and `Canvas2D`) that allow you to describe your scenes within the standard React lifecycle, using the state and event handlers from the parent React component.
Currently, Troika provides React components (`Canvas3D` and `Canvas2D`) that allow you to describe your scenes within the standard React lifecycle, using the state and event handlers from the parent React component.

The responsibilities of the wrapper layer have been kept as narrow as possible, so it should be simple to write wrappers for other frameworks as needed. Contributions are welcome.

Expand Down
6 changes: 0 additions & 6 deletions docs/pointer-states.md

This file was deleted.

3 changes: 3 additions & 0 deletions docs/three-instanced-uniforms-mesh/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{%
include-markdown "../../packages/three-instanced-uniforms-mesh/README.md"
%}
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ The [typical setup for Three.js rendering](https://threejs.org/docs/index.html#m
Troika controls when the Three.js scene is re-rendered, only doing so when:

* The scene is updated
* A facade [`animation` or `transition`](animations-and-transitions.md) is running
* A facade changes properties due to [`pointerStates`](interactivity-and-events.md#pointer-states) interactions
* A facade [`animation` or `transition`](../troika-core/animations-and-transitions.md) is running
* A facade changes properties due to [`pointerStates`](../troika-core/interactivity-and-events.md#pointer-states) interactions
* A facade indicates that it requires a re-render by calling `this.notifyWorld('needsRender')`


Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
149 changes: 149 additions & 0 deletions docs/troika-core/animations-and-transitions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
---
id: animations-and-transitions
title: Animations and Transitions
---

Troika has built-in support for declarative transitions and animations on the properties of _any_ Facade, controlled via `transition` and `animation` properties in the [scene descriptors](./scene-descriptors.md). These generally work very much like [CSS transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions) and [CSS animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations), so they should be very familiar to web developers.

> Note: These transitions and animations are driven by the [troika-animation package](https://github.com/protectwise/troika/tree/master/packages/troika-animation). Facade classes are automatically [extended to intercept property changes](https://github.com/protectwise/troika/blob/master/packages/troika-core/src/facade/Animatable.js) and apply transitions when needed.
## Transitions

Any descriptor can be given a `transition` property to automatically intercept changes to certain properties and animate from their previous values to their new values.

The `transition` property should be an object with transitionable property names as keys, and transition specs as values. Those specs can either be objects describing the transition parameters, or `true` for a default transition.

Troika supports both traditional duration-based transitions, like CSS, but also supports "spring" animations ala [react-spring](https://www.react-spring.io/). Spring animations can often give a more natural feel and behave better when target values change repeatedly during transition, but duration-based transitions can be more predictable. Use the approach that fits your case.

A basic example of a descriptor object that defines transitions for its `x`, `y`, and `z` properties:

```js
{
facade: MyObject,
x: xVal,
y: yVal,
z: zVal,
transition: {
x: true, // uses a default duration-based transition
y: 'spring', //uses a default spring-based transition
z: { //detailed transition parameters:
duration: 500,
easing: 'easeOutExpo'
}
}
}
```

When this descriptor is next applied with new values for `x`, `y`, and `z`, those values will be individually transitioned based on their configured parameters. That simple!

The custom transition config object can take one of two forms for duration- vs. spring-based animations:

### Duration-based

```js
transition: {
z: {
duration: 1234, //in ms, defaults to 750
easing: 'easeInOutBounce', //easing function, defaults to 'easeOutCubic'
delay: 123, //in ms, defaults to 0
interpolate: 'number' //see "Interpolation" below
}
}
```

### Spring-based

```js
transition: {
myProp: {
spring: true,
// or spring: 'wobbly',
// or spring: {mass, tension, friction},
delay: 250 //in ms, defaults to 0
}
}
```

The meanings of the spring configuration parameters, and the named presets, match those from [react-spring](https://www.react-spring.io/docs/hooks/api).

> Note: Spring-based transitions do not currently support custom interpolations so they should only be used for numeric values.

## Animations

Any descriptor can be given an `animation` property to define one or more keyframe animations for certain properties. Any animations will start running when the Facade is created and added to the scene. If any aspect of the `animation` is changed later, the old animation will be stopped and the new one will be started.

Here's an example of an animation that will rotate the object indefinitely, looping every 1.5 seconds:

```js
{
facade: MyObject,
animation: {
0: {rotateY: 0},
100: {rotateY: Math.PI * 2},
duration: 1500,
iterations: Infinity
}
}
```

### Animation Spec Structure

#### keyframes

All animations need at least two keyframes. They are defined by numeric properties from `0` for the first frame to `100` for the last frame, or any number in between. Also the special property names `'from'` and `'to'` are aliases for `0` and `100` respectively.

Each keyframe value is an object holding a set of properties and their target values at that keyframe. The values will be interpolated between the keyframes as the animation runs, applying them to the Facade instance.

#### duration

The number of milliseconds over which the animation's keyframes are run. If the animation loops (see `iterations`), this is the length of one iteration.

#### delay

A number of milliseconds to wait before starting the animation's first frame.

#### easing

An easing function for the animation, defaulting to "linear". This is applied to the whole animation's progression of keyframes, not individual keyframe segments.

#### iterations

The number of times the animation should loop, defaulting to `1`. To loop endlessly, give it the value `Infinity`.

#### direction

Which direction the animation should progress: `"forward"` (the default), `"backward"`, or `"alternate"` to toggle between forward and backward every other iteration.

#### interpolate

Defines how non-numeric animated values should be interpolated between keyframes. It takes an object whose keys are property names and values are [interpolators](#Interpolation). For example:

```js
interpolate: {
emissive: 'color'
}
```

#### paused

If `true`, the animation will be paused at its current keyframe. This can be toggled on and off to pause and unpause the animation.


## Exit Animations

Since they start when a Facade instance "enters" the scene, it's often convenient to think of `animation` as defining "entrance animations." Troika also supports "exit animations" which are applied when an object is removed from the scene. It will temporarily keep that object present in the scene long enough to run its exit animation, before fully destroying it. This allows you to do things like a nice smooth fade-out, scale-out, or fly-away rather than having objects abruptly disappear.

To define an exit animation, simply give the descriptor an `exitAnimation` property. Its value matches the structure of a regular `animation`.


## Interpolation

Transitions and animations will by default treat property values as numbers and interpolate their "tweened" values numerically. But some certain values, such as colors, require a different interpolation strategy.

Troika supports the following named interpolations:

- `"number"` - simple linear interpolation between two numeric values (the default).
- `"color"` - interprets the start/end values as RGB colors, and interpolates each color channel independently. The start/end values can be 24-bit integers or any CSS color string value, and the interpolated values will always be returned as 24-bit integers (8 bits red, 8 bits green, 8 bits blue.)

If you need a different interpolation, you can provide a custom function. Your function will take three parameters: the start value, the end value, and the progress between them (the output of the easing function) in the range from `0` to `1`. It must return an in-between value of the appropriate type.
29 changes: 15 additions & 14 deletions docs/facade-basics.md → docs/troika-core/facades.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The **`Facade`** is the central concept in the Troika framework. It serves as th

In your app, you will define Facades to represent different types of objects in your scene. You will also make use of built-in Facade types that are specialized for specific purposes.

Each Facade type is defined as a JavaScript `class`, extending the base `Facade` class. It has a few base methods, but otherwise each Facade class is free to define its own shape in the form of its public instance properties. Those properties will receive values from a [scene descriptor](./scene-descriptors.md) or manual updates later on.
Each Facade type is defined as a JavaScript `class`, extending the base `Facade` class. It has a few base methods, but otherwise each Facade class is free to define its own shape in the form of its public instance properties. Those properties will receive values from a [scene descriptor](scene-descriptors.md) or manual updates later on.

See the [base `Facade` class source](https://github.com/protectwise/troika/blob/master/packages/troika-core/src/facade/Facade.js) for some additional class and method JSDoc.

Expand All @@ -32,7 +32,7 @@ The Facade class's `constructor` is called, and is always passed a single argume

### 2. Update

This is where the facade instance receives its state. Since "state" is defined as the facade object's properties, updating that state simply consists of assigning a set of property values. This is usually done by copying a [scene descriptor](./scene-descriptors.md)'s values directly onto the facade instance during a scene update pass. It can also be triggered manually via the facade's `.update({...values})` method.
This is where the facade instance receives its state. Since "state" is defined as the facade object's properties, updating that state simply consists of assigning a set of property values. This is usually done by copying a [scene descriptor](scene-descriptors.md)'s values directly onto the facade instance during a scene update pass. It can also be triggered manually via the facade's `.update({...values})` method.

This part of the lifecycle is also usually when the facade synchronizes its new state properties to its more complex backing object model.

Expand All @@ -57,15 +57,15 @@ The base `Facade` class is a superclass of all facades, but you will seldom exte

### ParentFacade

[[Source]](https://github.com/protectwise/troika/blob/master/packages/troika-core/src/facade/ParentFacade.js) - This extends `Facade` with the ability to manage not only itself but also a set of child facades. At the end of its update phase it will recursively synchronize a set of child facade instances, based on an array of [descriptor objects](./scene-descriptors.md) returned by its `describeChildren()` method (which by default returns the value of its `.children` property.)
[[Source]](https://github.com/protectwise/troika/blob/master/packages/troika-core/src/facade/ParentFacade.js) - This extends `Facade` with the ability to manage not only itself but also a set of child facades. At the end of its update phase it will recursively synchronize a set of child facade instances, based on an array of [descriptor objects](scene-descriptors.md) returned by its `describeChildren()` method (which by default returns the value of its `.children` property.)

### ListFacade

[[Source]](https://github.com/protectwise/troika/blob/master/packages/troika-core/src/facade/ListFacade.js) - Inspired by [D3](https://d3js.org/), this is an optimized way to update many of the same type of object that skips creating intermediate descriptor objects for each item. For details see [Data Lists](./scene-descriptors.md#data-lists).
[[Source]](https://github.com/protectwise/troika/blob/master/packages/troika-core/src/facade/ListFacade.js) - Inspired by [D3](https://d3js.org/), this is an optimized way to update many of the same type of object that skips creating intermediate descriptor objects for each item. For details see [Data Lists](scene-descriptors.md#data-lists).

### Object3DFacade, Object2DFacade

These are base facades for the [`troika-3d`](./3d-overview.md) and [`troika-2d`](./2d-overview.md) packages. If you are creating a 3D/2D graphical scene, you'll likely be extending these for most of your objects. See the docs for those packages for details.
These are base facades for the [`troika-3d`](../troika-3d/3d-overview.md) and [`troika-2d`](../troika-2d/2d-overview.md) packages. If you are creating a 3D/2D graphical scene, you'll likely be extending these for most of your objects. See the docs for those packages for details.


## Example
Expand All @@ -77,35 +77,36 @@ import { Facade } from 'troika-core'

export class MyThingFacade extends Facade {
// Instantiation:
constructor(parent) {
constructor (parent) {
super(parent)

// Init backing object:
this._impl = new SuperComplicatedObject()

// Define state properties with initial values:
this.width = 1
this.height = 1
this.depth = 1
this.color = '#123456'
}

// Getter/setter for directly syncing a standalone property:
set color(value) {
set color (value) {
this._impl.setColor(value)
}
get color() {

get color () {
return this._impl.getColor()
}

// Handler for syncing interdependent properties:
afterUpdate() {
afterUpdate () {
this._impl.setDimensions(this.width, this.height, this.depth)
super.afterUpdate() //don't forget the super call!
}

// Cleanup:
destructor() {
destructor () {
this._impl.teardown()
delete this._impl
super.destructor()
Expand Down
File renamed without changes.
Loading

0 comments on commit cfe5d27

Please sign in to comment.