Skip to content

Commit af7a597

Browse files
committed
Fix DataTexture use with buffers
1 parent 2757698 commit af7a597

File tree

4 files changed

+121
-67
lines changed

4 files changed

+121
-67
lines changed

examples/Textures.ipynb

Lines changed: 34 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
{
44
"cell_type": "code",
55
"execution_count": null,
6-
"metadata": {
7-
"collapsed": false
8-
},
6+
"metadata": {},
97
"outputs": [],
108
"source": [
119
"from pythreejs import *\n",
@@ -16,9 +14,7 @@
1614
{
1715
"cell_type": "code",
1816
"execution_count": null,
19-
"metadata": {
20-
"collapsed": true
21-
},
17+
"metadata": {},
2218
"outputs": [],
2319
"source": [
2420
"checker_tex = ImageTexture(imageUri='img/checkerboard.png')\n",
@@ -28,9 +24,7 @@
2824
{
2925
"cell_type": "code",
3026
"execution_count": null,
31-
"metadata": {
32-
"collapsed": true
33-
},
27+
"metadata": {},
3428
"outputs": [],
3529
"source": [
3630
"checker_tex"
@@ -39,9 +33,7 @@
3933
{
4034
"cell_type": "code",
4135
"execution_count": null,
42-
"metadata": {
43-
"collapsed": true
44-
},
36+
"metadata": {},
4537
"outputs": [],
4638
"source": [
4739
"earth_tex"
@@ -50,41 +42,39 @@
5042
{
5143
"cell_type": "code",
5244
"execution_count": null,
53-
"metadata": {
54-
"collapsed": false
55-
},
45+
"metadata": {},
5646
"outputs": [],
5747
"source": [
5848
"#\n",
5949
"# Create checkerboard pattern\n",
6050
"#\n",
6151
"\n",
62-
"data = []\n",
63-
"\n",
6452
"# tex dims need to be power of two.\n",
6553
"arr_w = 256\n",
6654
"arr_h = 256\n",
6755
"\n",
68-
"# num checkers in checkerboard pattern\n",
69-
"n_checkers_x = 4\n",
70-
"n_checkers_y = 4\n",
56+
"import numpy as np\n",
7157
"\n",
72-
"# width in texels of each checker\n",
73-
"checker_w = arr_w / n_checkers_x\n",
74-
"checker_h = arr_h / n_checkers_y\n",
58+
"def gen_checkers(width, height, n_checkers_x, n_checkers_y):\n",
59+
" array = np.ones((width, height, 3))\n",
60+
" \n",
61+
" # width in texels of each checker\n",
62+
" checker_w = width / n_checkers_x\n",
63+
" checker_h = height / n_checkers_y\n",
64+
" \n",
7565
"\n",
76-
"for y in range(arr_h):\n",
77-
" for x in range(arr_w):\n",
78-
" color_key = int(x / checker_w) + int(y / checker_h)\n",
79-
" if color_key % 2 == 0:\n",
80-
" data += [ 0, 0, 0 ]\n",
81-
" else:\n",
82-
" data += [ 1, 1, 1 ]\n",
66+
" for y in range(arr_h):\n",
67+
" for x in range(arr_w):\n",
68+
" color_key = int(x / checker_w) + int(y / checker_h)\n",
69+
" if color_key % 2 == 0:\n",
70+
" array[x, y, :] = [ 0, 0, 0 ]\n",
71+
" else:\n",
72+
" array[x, y, :] = [ 1, 1, 1 ]\n",
73+
" return array\n",
74+
" \n",
8375
"\n",
8476
"data_tex = DataTexture(\n",
85-
" data=data,\n",
86-
" width=arr_w,\n",
87-
" height=arr_h,\n",
77+
" data=gen_checkers(arr_w, arr_h, 4, 4),\n",
8878
" format=\"RGBFormat\",\n",
8979
" type=\"FloatType\"\n",
9080
")"
@@ -93,9 +83,7 @@
9383
{
9484
"cell_type": "code",
9585
"execution_count": null,
96-
"metadata": {
97-
"collapsed": false
98-
},
86+
"metadata": {},
9987
"outputs": [],
10088
"source": [
10189
"data_tex"
@@ -104,33 +92,33 @@
10492
{
10593
"cell_type": "code",
10694
"execution_count": null,
107-
"metadata": {
108-
"collapsed": true
109-
},
95+
"metadata": {},
11096
"outputs": [],
111-
"source": []
97+
"source": [
98+
"data_tex.data = gen_checkers(arr_w, arr_h, 12, 20)"
99+
]
112100
}
113101
],
114102
"metadata": {
115103
"anaconda-cloud": {},
116104
"kernelspec": {
117-
"display_name": "Python [py27]",
105+
"display_name": "Python 3",
118106
"language": "python",
119-
"name": "Python [py27]"
107+
"name": "python3"
120108
},
121109
"language_info": {
122110
"codemirror_mode": {
123111
"name": "ipython",
124-
"version": 2
112+
"version": 3
125113
},
126114
"file_extension": ".py",
127115
"mimetype": "text/x-python",
128116
"name": "python",
129117
"nbconvert_exporter": "python",
130-
"pygments_lexer": "ipython2",
131-
"version": "2.7.12"
118+
"pygments_lexer": "ipython3",
119+
"version": "3.5.3"
132120
}
133121
},
134122
"nbformat": 4,
135-
"nbformat_minor": 0
123+
"nbformat_minor": 1
136124
}

js/scripts/three-class-config.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,14 +1086,14 @@ module.exports = {
10861086
properties: {
10871087
// this.image = { data: data, width: width, height: height };
10881088
data: new Types.ArrayBuffer(),
1089-
width: new Types.Int(0),
1090-
height: new Types.Int(0),
1089+
// width: new Types.Int(0), // inferred from data
1090+
// height: new Types.Int(0), // inferred from data
10911091
minFilter: new Types.Enum('Filters', 'NearestFilter'), // override default
10921092
magFilter: new Types.Enum('Filters', 'NearestFilter'), // override default
10931093
flipY: new Types.Bool(false), // override default
10941094
generateMipmaps: new Types.Bool(false),
10951095
},
1096-
constructorArgs: [ 'data', 'width', 'height', 'format', 'type', 'mapping', 'wrapS', 'wrapT', 'magFilter', 'minFilter', 'anisotropy' ],
1096+
constructorArgs: [ 'data', 'format', 'type', 'mapping', 'wrapS', 'wrapT', 'magFilter', 'minFilter', 'anisotropy' ],
10971097
},
10981098
DepthTexture: {
10991099
relativePath: './textures/DepthTexture',

js/src/textures/DataTexture.js

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,81 @@
11
var _ = require('underscore');
2-
var DataTextureBase = require('./DataTexture.autogen');
2+
var datawidgets = require('jupyter-datawidgets');
3+
var ndarray = require('ndarray');
4+
var DataTextureBase = require('./DataTexture.autogen').DataTextureModel;
35

4-
var DataTextureModel = DataTextureBase.DataTextureModel.extend({
6+
var DataTextureModel = DataTextureBase.extend({
57

68
createPropertiesArrays: function() {
7-
DataTextureBase.DataTextureModel.prototype.createPropertiesArrays.call(this);
9+
DataTextureBase.prototype.createPropertiesArrays.call(this);
810

911
// three.js DataTexture stores the data, width, and height props together in a dict called 'image'
1012
this.property_mappers['DataTextureData'] = 'mapDataTextureData';
13+
delete this.property_converters['data'];
1114
},
1215

16+
decodeData() {
17+
var rawData = this.get('data');
18+
if (rawData.dimension < 2 || rawData.dimension > 3) {
19+
throw Error('DataTexture data dimensions need to be 2 or 3, got:', rawData.dimension)
20+
}
21+
var data = this.convertArrayBufferModelToThree(rawData, 'data');
22+
var width = rawData.shape[0];
23+
var height = rawData.shape[1];
1324

14-
mapDataTextureDataModelToThree: function() {
15-
var data = new Float32Array(this.get('data'));
16-
var width = this.get('width');
17-
var height = this.get('height');
18-
this.obj.image = { data: data, width: width, height: height };
19-
this.obj.needsUpdate = true;
25+
return {
26+
data: data,
27+
width: rawData.shape[0],
28+
height: rawData.shape[1],
29+
}
2030
},
2131

22-
mapDataTextureDataThreeToModel: function() {
23-
var imageRecord = this.obj.image;
32+
constructThreeObject: function() {
33+
var data = this.decodeData();
34+
35+
// Make a copy of buffer
36+
var buffer = new data.data.constructor(data.data.length);
37+
buffer.set(data.data);
38+
39+
var result = new THREE.DataTexture(
40+
buffer,
41+
data.width,
42+
data.height,
43+
this.convertEnumModelToThree(this.get('format'), 'format'),
44+
this.convertEnumModelToThree(this.get('type'), 'type'),
45+
this.convertEnumModelToThree(this.get('mapping'), 'mapping'),
46+
this.convertEnumModelToThree(this.get('wrapS'), 'wrapS'),
47+
this.convertEnumModelToThree(this.get('wrapT'), 'wrapT'),
48+
this.convertEnumModelToThree(this.get('magFilter'), 'magFilter'),
49+
this.convertEnumModelToThree(this.get('minFilter'), 'minFilter'),
50+
this.convertFloatModelToThree(this.get('anisotropy'), 'anisotropy')
51+
);
52+
result.needsUpdate = true;
53+
return Promise.resolve(result);
54+
55+
},
2456

25-
// this.image = { data: data, width: width, height: height };
26-
var dataArr = imageRecord.data;
27-
var dataWidth = imageRecord.width;
28-
var dataHeight = imageRecord.height;
2957

30-
this.set('data', this.convertArrayBufferThreeToModel(dataArr));
31-
this.set('width', dataWidth);
32-
this.set('height', dataHeight);
58+
mapDataTextureDataModelToThree: function() {
59+
var imageRecord = this.obj.image;
60+
var data = this.decodeData();
61+
if (imageRecord.width !== data.width || imageRecord.height !== imageRecord.height) {
62+
throw new Error('Cannot change the dimensions of a DataTexture!');
63+
}
64+
this.obj.image.data.set(data.data);
65+
this.obj.needsUpdate = true;
66+
this.set({ version: this.obj.version }, 'pushFromThree');
67+
},
68+
69+
mapDataTextureDataThreeToModel: function() {
70+
var textureData = this.obj.image.data;
71+
var modelNDArray = this.get('data');
72+
modelNDArray.data.set(textureData);
3373
},
3474

75+
}, {
76+
serializers: _.extend({
77+
data: datawidgets.array_serialization,
78+
}, DataTextureBase.serializers),
3579
});
3680

3781
module.exports = {

pythreejs/textures/DataTexture.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import numpy as np
2+
from traitlets import validate, TraitError
3+
4+
from .DataTexture_autogen import DataTexture as BaseDataTexture
5+
6+
7+
class DataTexture(BaseDataTexture):
8+
9+
@validate('data')
10+
def _valid_data(self, proposal):
11+
# Validate shape
12+
if np.ndim(proposal) > 2:
13+
raise TraitError('Data needs to have two or three dimensions. Given shape was: %r'
14+
% np.shape(proposal))
15+
old = self._trait_values.get(proposal['trait'].name, None)
16+
value = proposal['value']
17+
if old is not None and np.shape(old) != np.shape(value):
18+
raise TraitError('Cannot change shape of previously initialized DataTexture.')
19+
if value.dtype == np.float64:
20+
# 64-bit not supported, coerce to 32-bit
21+
value = value.astype(np.float32)
22+
return value

0 commit comments

Comments
 (0)