Skip to content

Commit e0d3b99

Browse files
authored
Merge pull request #180 from vidartf/doc-extend
Docs: extending pythreejs
2 parents 7ad3639 + d173bf6 commit e0d3b99

File tree

3 files changed

+186
-0
lines changed

3 files changed

+186
-0
lines changed

docs/source/extending.rst

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
2+
Extending pythreejs
3+
===================
4+
5+
While you can do a lot with pythreejs out of the box, you might have
6+
some custom rendering you want to do, that would be more efficient
7+
to configure as a separate widget. To be able to integrate such
8+
objects with pythreejs, the following extension guide can be helpful.
9+
10+
11+
Blackbox object
12+
---------------
13+
14+
Pythreejs exports a :py:class:`~pythreejs.Blackbox` Widget,
15+
which inherits :py:class:`~pythreejs.Object3D`. The intention is for
16+
third-party widget libraries to inherit from it on both the Python
17+
and JS side. You would add the traits needed to set up your object,
18+
and have the JS side set up the corresponding three.js object. The
19+
three.js object itself would not be synced across the wire, which is
20+
why it is called a blackbox, but you can still manipulate it in a
21+
scene (transforming, putting it as a child, etc.). This can be
22+
very efficient e.g. for complex, generated objects, where the
23+
final three.js data would be prohibitively expensive to synchronize.
24+
25+
26+
Example implementation
27+
**********************
28+
29+
Below is an example implementation for rendering a crystal lattice.
30+
It takes a basis structure, and then tiles copies of this basis
31+
in x/y/z, potentially generating thousands of spheres.
32+
33+
34+
.. note::
35+
36+
This example is not a good/optimized crystal structure viewer. It is
37+
merely used to convey the concept of a widget with a few parameters
38+
translating to something with potentially hugh amounts of data/objects.
39+
40+
41+
Python::
42+
43+
import traitlets
44+
import pythreejs
45+
46+
class CubicLattice(pythreejs.Blackbox):
47+
_model_module = traitlets.Unicode('my_module_name').tag(sync=True)
48+
49+
basis = traitlets.List(
50+
trait=pythreejs.Vector3(),
51+
default_value=[[0, 0, 0]],
52+
max_length=5
53+
).tag(sync=True)
54+
55+
repetitions = traitlets.List(
56+
trait=traitlets.Int(),
57+
default_value=[5, 5, 5],
58+
min_length=3,
59+
max_length=3
60+
).tag(sync=True)
61+
62+
63+
JavaScript:
64+
65+
.. code-block:: javascript
66+
67+
import * as THREE from "three";
68+
69+
import {
70+
BlackboxModel
71+
} from 'jupyter-threejs';
72+
73+
74+
const atomGeometry = new THREE.SphereBufferGeometry(0.2, 16, 8);
75+
const atomMaterials = [
76+
new THREE.MeshLambertMaterial({color: 'red'}),
77+
new THREE.MeshLambertMaterial({color: 'green'}),
78+
new THREE.MeshLambertMaterial({color: 'yellow'}),
79+
new THREE.MeshLambertMaterial({color: 'blue'}),
80+
new THREE.MeshLambertMaterial({color: 'cyan'}),
81+
];
82+
83+
export class CubicLatticeModel extends BlackboxModel {
84+
defaults() {
85+
return {...super.defaults(), ...{
86+
_model_name: 'CubicLatticeModel',
87+
_model_module: 'my_module_name',
88+
basis: [[0, 0, 0]],
89+
repetitions: [5, 5, 5],
90+
}};
91+
}
92+
93+
// This method is called to create the three.js object of the model:
94+
constructThreeObject() {
95+
const root = new THREE.Group();
96+
// Create the children of this group:
97+
// This is the part that is specific to this example
98+
this.createLattice(root);
99+
return root;
100+
}
101+
102+
// This method is called whenever the model changes:
103+
onChange(model, options) {
104+
super.onChange(model, options);
105+
// If any of the parameters change, simply rebuild children:
106+
this.createLattice();
107+
}
108+
109+
// Our custom method to build the lattice:
110+
createLattice(obj) {
111+
obj = obj || this.obj;
112+
113+
// Set up the basis to tile:
114+
const basisInput = this.get('basis');
115+
const basis = new THREE.Group();
116+
for (let i=0; i < basisInput.length; ++i) {
117+
let mesh = new THREE.Mesh(atomGeometry, atomMaterials[i]);
118+
mesh.position.fromArray(basisInput[i]);
119+
basis.add(mesh);
120+
}
121+
122+
// Tile in x, y, z:
123+
const [nx, ny, nz] = this.get('repetitions');
124+
const children = [];
125+
for (let x = 0; x < nx; ++x) {
126+
for (let y = 0; y < ny; ++y) {
127+
for (let z = 0; z < nz; ++z) {
128+
let copy = basis.clone();
129+
copy.position.set(x, y, z);
130+
children.push(copy);
131+
}
132+
}
133+
}
134+
135+
obj.remove(...obj.children);
136+
obj.add(...children);
137+
}
138+
}
139+
140+
141+
This code should then be wrapped up in a widget extension (see
142+
documentation from ipywidgets on how to do this).
143+
144+
Usage::
145+
146+
import pythreejs
147+
from IPython.display import display
148+
from my_module import CubicLattice
149+
150+
lattice = CubicLattice(basis=[[0,0,0], [0.5, 0.5, 0.5]])
151+
152+
# Preview the lattice directly:
153+
display(lattice)
154+
155+
# Or put it in a scene:
156+
width=600
157+
height=400
158+
key_light = pythreejs.DirectionalLight(position=[-5, 5, 3], intensity=0.7)
159+
ambient_light = pythreejs.AmbientLight(color='#777777')
160+
161+
camera = pythreejs.PerspectiveCamera(
162+
position=[-5, 0, -5],
163+
children=[
164+
# Have the key light follow the camera:
165+
key_light
166+
],
167+
aspect=width/height,
168+
)
169+
control = pythreejs.OrbitControls(controlling=camera)
170+
171+
scene = pythreejs.Scene(children=[lattice, camera, ambient_light])
172+
173+
renderer = pythreejs.Renderer(camera=camera,
174+
scene=scene,
175+
controls=[control],
176+
width=width, height=height)
177+
178+
display(renderer)
179+
180+
181+
182+
.. figure:: images/extension-example.png
183+
:alt: rendered output example
184+
185+
Figure: Example view of the rendered lattice object.
56.2 KB
Loading

docs/source/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ Contents
5757
:maxdepth: 2
5858
:caption: Development
5959

60+
extending
6061
develop-install
6162

6263

0 commit comments

Comments
 (0)