K3D tools
Technical perspective
Artur Trzęsiok
Sage Days 74
CIAS, Observatoire de Paris, Meudon
Technology stack
- ThreeJS (15.000+ commits)
- Travis
- Grunt tool
- Karma runner
- Pako
- Json-patch
- Vagrant + Puppet
- Bower/NPM
Project decomposition
K3D standalone
K3D jupyter
- ThreeJS as abstraction layer to WebGL
- Viewer for internal json-based format
- Package delivered as Bower/NPM module with semantic versioning
- E2E testing done on screenshot level
- Based on IPython widgets
- Internal communication Based on json-patch format
- Based on NumPy package
- Provide high-level function that will produce json format for K3D standalone
Library details
- Size: 2.5 MB
- Proof of concept -
3 months of development - Low level Javascript performance improvements
- TypedArray
- Avoid prototype
- Flat structure
- High level performance improvements
- Greedy Mesh
- Octree
- etc.
- Every object generator as plugin
- Line
- Marching Cubes
- Points Clouds
- STL
- Surface
- Text
- Texture
- Vectors
- Vectors Field
- Voxles
Culled Mesh

Greedy Mesh

Performance over readability
for (y = 0, i = 0, p = 0; y < height - 1; y++) {
for (x = 0; x < width - 1; x++, p++, i += 18) {
// Performance over readability
vertices[i] = vertices[i + 9] = x / (width - 1);
vertices[i + 1] = vertices[i + 10] = y / (height - 1);
vertices[i + 2] = vertices[i + 11] = heights[p];
vertices[i + 3] = vertices[i + 15] = (x + 1) / (width - 1);
vertices[i + 4] = vertices[i + 16] = (y + 1) / (height - 1);
vertices[i + 5] = vertices[i + 17] = heights[p + 1 + width];
vertices[i + 6] = (x + 1) / (width - 1);
vertices[i + 7] = y / (height - 1);
vertices[i + 8] = heights[p + 1];
vertices[i + 12] = x / (width - 1);
vertices[i + 13] = (y + 1) / (height - 1);
vertices[i + 14] = heights[p + width];
}
//skip last column
p++;
}
Communication scheme
Browser
Python
plot = K3D()
field = K3D.marching_cubes(field, level=0.8)
plot += field
plot.display()
Send K3D browser widget
Send and store internal json data
Load K3D standalone widget + small helper to dispatch Jupyter commands
Display object + store json
field.color = 0xFFFF00
- Update Python object
- Compute new exported json
- Compute difference beetwen new and stored version
- Send difference (json-diff format)
- Apply patch on stored json
- Update scene
Example internal json
{
"metadata" : {},
"objects" : [{
"type" : "Line",
"modelViewMatrix" : [
1.5, 0.0, 0.0, 1.5,
0.0, 1.5, 0.0, 0.0,
0.0, 0.0, 1.5, 0.0,
0.0, 0.0, 0.0, 1.0
],
"color" : 16711680,
"pointsPositions" : [
-3.0, 2.0, 0.0,
-1.0, 2.0, 0.0,
-1.0, 1.0, 0.0,
-2.0, 0.0, 0.0,
-1.0, 0.0, 0.0,
-1.0, -2.0, 0.0,
-3.0, -2.0, 0.0
]
}, {
"type" : "Line",
"pointsPositions" : [
1.0, 2.0, 0.0,
2.0, 2.0, 0.0,
3.0, 1.0, 0.0,
3.0, -1.0, 0.0,
2.0, -2.0, 0.0,
1.0, -2.0, 0.0,
1.0, 2.0, 0.0
]
}
]
}
Example internal json
{
"metadata" : {},
"objects" : [{
"type" : "Line",
"modelViewMatrix" : [
1.5, 0.0, 0.0, 1.5,
0.0, 1.5, 0.0, 0.0,
0.0, 0.0, 1.5, 0.0,
0.0, 0.0, 0.0, 1.0
],
"color" : 16711680,
"pointsPositions" : "AABAwAAAAEAAAAAAAACAvwAAAEAAAAAAAACAvwAAgD8AAAAAAAAA
wAAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAMAAAAAAAABAwAAA
AMAAAAAA"
}, {
"type" : "Line",
"pointsPositions" : "AACAPwAAAEAAAAAAAAAAQAAAAEAAAAAAAABAQAAAgD8AAAAAAABA
QAAAgL8AAAAAAAAAQAAAAMAAAAAAAACAPwAAAMAAAAAAAACAPwAA
AEAAAAAA"
}
]
}
Example json patch
(rfc 6902)
[
{ "op": "replace", "path": "/baz", "value": "boo" },
{ "op": "add", "path": "/hello", "value": ["world"] },
{ "op": "remove", "path": "/foo"}
]
K3D
By Artur Trzesiok
K3D
- 1,127