Skip to content

Commit 73ff2c0

Browse files
Color map and color legend in 'points' type plot #227
1 parent 9959c51 commit 73ff2c0

10 files changed

Lines changed: 320 additions & 83 deletions

File tree

js/src/providers/threejs/objects/PointsBillboard.js

Lines changed: 92 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
var THREE = require('three'),
44
buffer = require('./../../../core/lib/helpers/buffer'),
5+
colorMapHelper = require('./../../../core/lib/helpers/colorMap'),
56
Fn = require('./../helpers/Fn'),
67
commonUpdate = Fn.commonUpdate,
78
areAllChangesResolve = Fn.areAllChangesResolve,
@@ -21,9 +22,9 @@ module.exports = {
2122
pointPositions = config.positions.data,
2223
pointColors = (config.colors && config.colors.data) || null,
2324
shader = config.shader,
24-
colors,
25+
colors = null,
2526
opacities = (config.opacities && config.opacities.data &&
26-
config.opacities.data.length === pointPositions.length / 3) ? config.opacities.data : null,
27+
config.opacities.data.length === pointPositions.length / 3) ? config.opacities.data : null,
2728
object,
2829
material,
2930
colorsToFloat32Array = buffer.colorsToFloat32Array,
@@ -40,18 +41,56 @@ module.exports = {
4041
'flat': require('./shaders/Points.vertex.glsl'),
4142
'3d': require('./shaders/Points.vertex.glsl'),
4243
'3dspecular': require('./shaders/Points.vertex.glsl')
43-
};
44+
},
45+
colorMap = (config.color_map && config.color_map.data) || null,
46+
opacityFunction = (config.opacity_function && config.opacity_function.data) || null,
47+
colorRange = config.color_range,
48+
attribute = (config.attribute && config.attribute.data) || null,
49+
uniforms = {},
50+
useColorMap = 0;
4451

4552
fragmentShader = fragmentShaderMap[shader.toLowerCase()] || fragmentShaderMap.flat;
4653
vertexShader = vertexShaderMap[shader.toLowerCase()] || vertexShaderMap.flat;
4754

55+
if (attribute && colorRange && colorMap && attribute.length > 0 &&
56+
colorRange.length > 0 && colorMap.length > 0) {
57+
58+
useColorMap = 1;
59+
60+
if (opacityFunction === null || opacityFunction.length === 0) {
61+
opacityFunction = [colorMap[0], 1.0, colorMap[colorMap.length - 4], 1.0];
62+
63+
config.opacity_function = {
64+
data: opacityFunction,
65+
shape: [4]
66+
};
67+
}
68+
69+
var canvas = colorMapHelper.createCanvasGradient(colorMap, 1024, opacityFunction);
70+
var colormap = new THREE.CanvasTexture(canvas, THREE.UVMapping, THREE.ClampToEdgeWrapping,
71+
THREE.ClampToEdgeWrapping, THREE.NearestFilter, THREE.NearestFilter);
72+
colormap.needsUpdate = true;
73+
74+
uniforms = {
75+
low: {value: colorRange[0]},
76+
high: {value: colorRange[1]},
77+
colormap: {type: 't', value: colormap}
78+
}
79+
} else {
80+
colors = (pointColors && pointColors.length === pointPositions.length / 3 ?
81+
colorsToFloat32Array(pointColors) : getColorsArray(color, pointPositions.length / 3)
82+
);
83+
}
84+
4885
material = new THREE.ShaderMaterial({
4986
uniforms: THREE.UniformsUtils.merge([
5087
THREE.UniformsLib.lights,
51-
THREE.UniformsLib.points
88+
THREE.UniformsLib.points,
89+
uniforms
5290
]),
5391
defines: {
5492
USE_SPECULAR: (shader === '3dSpecular' ? 1 : 0),
93+
USE_COLOR_MAP: useColorMap,
5594
USE_PER_POINT_OPACITY: (opacities !== null ? 1 : 0)
5695
},
5796
vertexShader: vertexShader,
@@ -72,12 +111,11 @@ module.exports = {
72111
material.map = null;
73112
material.isPointsMaterial = true;
74113

75-
colors = (pointColors && pointColors.length === pointPositions.length / 3 ?
76-
colorsToFloat32Array(pointColors) : getColorsArray(color, pointPositions.length / 3)
114+
object = new THREE.Points(
115+
getGeometry(pointPositions, colors, opacities, useColorMap ? attribute : null),
116+
material
77117
);
78118

79-
object = new THREE.Points(getGeometry(pointPositions, colors, opacities), material);
80-
81119
Fn.expandBoundingBox(object.geometry.boundingBox, config.point_size * 0.5);
82120

83121
modelMatrix.set.apply(modelMatrix, config.model_matrix.data);
@@ -90,7 +128,7 @@ module.exports = {
90128
update: function (config, changes, obj) {
91129
var resolvedChanges = {};
92130

93-
if (typeof(changes.positions) !== 'undefined' && !changes.positions.timeSeries &&
131+
if (typeof (changes.positions) !== 'undefined' && !changes.positions.timeSeries &&
94132
changes.positions.data.length === obj.geometry.attributes.position.array.length) {
95133
obj.geometry.attributes.position.array.set(changes.positions.data);
96134
obj.geometry.attributes.position.needsUpdate = true;
@@ -101,13 +139,40 @@ module.exports = {
101139
resolvedChanges.positions = null;
102140
}
103141

104-
// if (typeof(changes.opacity) !== 'undefined' && !changes.opacity.timeSeries) {
105-
// obj.material.uniforms.opacity.value = changes.opacity;
106-
// obj.material.depthWrite = config.opacity === 1.0;
107-
// obj.material.transparent = config.opacity !== 1.0;
108-
//
109-
// resolvedChanges.opacity = null;
110-
// }
142+
if (typeof (changes.color_range) !== 'undefined' && !changes.color_range.timeSeries &&
143+
obj.geometry.attributes.attributes) {
144+
obj.material.uniforms.low.value = changes.color_range[0];
145+
obj.material.uniforms.high.value = changes.color_range[1];
146+
147+
resolvedChanges.color_range = null;
148+
}
149+
150+
if (((typeof (changes.color_map) !== 'undefined' && !changes.color_map.timeSeries) ||
151+
(typeof (changes.opacity_function) !== 'undefined' && !changes.opacity_function.timeSeries)) &&
152+
obj.geometry.attributes.attributes) {
153+
154+
var canvas = colorMapHelper.createCanvasGradient(
155+
(changes.color_map && changes.color_map.data) || config.color_map.data,
156+
1024,
157+
(changes.opacity_function && changes.opacity_function.data) || config.opacity_function.data
158+
);
159+
160+
obj.material.uniforms.colormap.value.image = canvas;
161+
obj.material.uniforms.colormap.value.needsUpdate = true;
162+
163+
resolvedChanges.color_map = null;
164+
resolvedChanges.opacity_function = null;
165+
}
166+
167+
if (typeof (changes.attribute) !== 'undefined' && !changes.attribute.timeSeries &&
168+
obj.geometry.attributes.attattributesribute &&
169+
changes.attribute.data.length === obj.geometry.attributes.attributes.array.length) {
170+
171+
obj.geometry.attributes.attributes.array.set(changes.attribute.data);
172+
obj.geometry.attributes.attributes.needsUpdate = true;
173+
174+
resolvedChanges.attribute = null;
175+
}
111176

112177
commonUpdate(config, changes, resolvedChanges, obj);
113178

@@ -126,18 +191,26 @@ module.exports = {
126191
* @param {Float32Array} positions
127192
* @param {Float32Array} colors
128193
* @param {Float32Array} opacities
194+
* @param {Float32Array} attribute
129195
* @return {THREE.BufferGeometry}
130196
*/
131-
function getGeometry(positions, colors, opacities) {
197+
function getGeometry(positions, colors, opacities, attribute) {
132198
var geometry = new THREE.BufferGeometry();
133199

134200
geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), 3));
135-
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
136201

137-
if (opacities) {
202+
if (colors && colors.length > 0) {
203+
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
204+
}
205+
206+
if (opacities && opacities.length > 0) {
138207
geometry.setAttribute('opacities', new THREE.BufferAttribute(opacities, 1).setUsage(THREE.DynamicDrawUsage));
139208
}
140209

210+
if (attribute && attribute.length > 0) {
211+
geometry.setAttribute('attributes', new THREE.BufferAttribute(attribute, 1).setUsage(THREE.DynamicDrawUsage));
212+
}
213+
141214
geometry.computeBoundingSphere();
142215
geometry.computeBoundingBox();
143216

js/src/providers/threejs/objects/PointsMesh.js

Lines changed: 105 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
var THREE = require('three'),
44
buffer = require('./../../../core/lib/helpers/buffer'),
5+
colorMapHelper = require('./../../../core/lib/helpers/colorMap'),
56
Fn = require('./../helpers/Fn'),
67
areAllChangesResolve = Fn.areAllChangesResolve,
78
commonUpdate = Fn.commonUpdate,
@@ -23,33 +24,52 @@ module.exports = {
2324
meshDetail = typeof (config.mesh_detail) !== 'undefined' ? config.mesh_detail : 2,
2425
colors,
2526
opacities = (config.opacities && config.opacities.data &&
26-
config.opacities.data.length === positions.length / 3) ? config.opacities.data : null,
27+
config.opacities.data.length === positions.length / 3) ? config.opacities.data : null,
2728
object,
2829
colorsToFloat32Array = buffer.colorsToFloat32Array,
2930
phongShader = THREE.ShaderLib.phong,
30-
material = new THREE.ShaderMaterial({
31-
uniforms: THREE.UniformsUtils.merge([phongShader.uniforms, {
32-
shininess: {value: 50},
33-
opacity: {value: config.opacity}
34-
}]),
35-
defines: {
36-
USE_PER_POINT_OPACITY: (opacities !== null ? 1 : 0)
37-
},
38-
vertexShader: require('./shaders/PointsMesh.vertex.glsl'),
39-
fragmentShader: require('./shaders/PointsMesh.fragment.glsl'),
40-
depthWrite: (config.opacity === 1.0 && opacities === null),
41-
transparent: (config.opacity !== 1.0 || opacities !== null),
42-
lights: true,
43-
clipping: true,
44-
vertexColors: THREE.VertexColors
45-
}),
31+
material,
4632
i,
4733
boundingBoxGeometry = new THREE.BufferGeometry(),
48-
geometry = new THREE.IcosahedronBufferGeometry(config.point_size * 0.5, meshDetail);
34+
geometry = new THREE.IcosahedronBufferGeometry(config.point_size * 0.5, meshDetail),
35+
colorMap = (config.color_map && config.color_map.data) || null,
36+
opacityFunction = (config.opacity_function && config.opacity_function.data) || null,
37+
colorRange = config.color_range,
38+
attribute = (config.attribute && config.attribute.data) || null,
39+
uniforms = {},
40+
useColorMap = 0;
4941

50-
colors = (pointColors && pointColors.length === positions.length / 3 ?
51-
colorsToFloat32Array(pointColors) : getColorsArray(color, positions.length / 3)
52-
);
42+
if (attribute && colorRange && colorMap && attribute.length > 0 &&
43+
colorRange.length > 0 && colorMap.length > 0) {
44+
45+
useColorMap = 1;
46+
47+
if (opacityFunction === null || opacityFunction.length === 0) {
48+
opacityFunction = [colorMap[0], 1.0, colorMap[colorMap.length - 4], 1.0];
49+
50+
config.opacity_function = {
51+
data: opacityFunction,
52+
shape: [4]
53+
};
54+
}
55+
56+
var canvas = colorMapHelper.createCanvasGradient(colorMap, 1024, opacityFunction);
57+
var colormap = new THREE.CanvasTexture(canvas, THREE.UVMapping, THREE.ClampToEdgeWrapping,
58+
THREE.ClampToEdgeWrapping, THREE.NearestFilter, THREE.NearestFilter);
59+
colormap.needsUpdate = true;
60+
61+
uniforms = {
62+
low: {value: colorRange[0]},
63+
high: {value: colorRange[1]},
64+
colormap: {type: 't', value: colormap}
65+
}
66+
geometry.setAttribute('attributes',
67+
new THREE.InstancedBufferAttribute(attribute, 1).setUsage(THREE.DynamicDrawUsage));
68+
} else {
69+
colors = (pointColors && pointColors.length === positions.length / 3 ?
70+
colorsToFloat32Array(pointColors) : getColorsArray(color, positions.length / 3)
71+
);
72+
}
5373

5474
geometry.setAttribute('color', new THREE.InstancedBufferAttribute(new Float32Array(colors), 3));
5575

@@ -65,6 +85,25 @@ module.exports = {
6585
Fn.expandBoundingBox(boundingBoxGeometry.boundingBox, config.point_size * 0.5);
6686

6787
geometry.boundingBox = boundingBoxGeometry.boundingBox.clone();
88+
89+
material = new THREE.ShaderMaterial({
90+
uniforms: THREE.UniformsUtils.merge([phongShader.uniforms, {
91+
shininess: {value: 50},
92+
opacity: {value: config.opacity}
93+
}, uniforms]),
94+
defines: {
95+
USE_PER_POINT_OPACITY: (opacities !== null ? 1 : 0),
96+
USE_COLOR_MAP: useColorMap
97+
},
98+
vertexShader: require('./shaders/PointsMesh.vertex.glsl'),
99+
fragmentShader: require('./shaders/PointsMesh.fragment.glsl'),
100+
depthWrite: (config.opacity === 1.0 && opacities === null),
101+
transparent: (config.opacity !== 1.0 || opacities !== null),
102+
lights: true,
103+
clipping: true,
104+
vertexColors: THREE.VertexColors
105+
})
106+
68107
object = new THREE.InstancedMesh(geometry, material, positions.length / 3);
69108
object.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
70109

@@ -83,11 +122,51 @@ module.exports = {
83122
update: function (config, changes, obj) {
84123
var resolvedChanges = {};
85124

86-
// if (typeof(changes.opacity) !== 'undefined' && !changes.opacity.timeSeries) {
87-
// obj.material.uniforms.opacity.value = changes.opacity;
88-
//
89-
// resolvedChanges.opacity = null;
90-
// }
125+
if (typeof (changes.positions) !== 'undefined' && !changes.positions.timeSeries &&
126+
changes.positions.data.length === obj.geometry.attributes.position.array.length) {
127+
obj.geometry.attributes.position.array.set(changes.positions.data);
128+
obj.geometry.attributes.position.needsUpdate = true;
129+
130+
obj.geometry.computeBoundingSphere();
131+
obj.geometry.computeBoundingBox();
132+
133+
resolvedChanges.positions = null;
134+
}
135+
136+
if (((typeof (changes.color_map) !== 'undefined' && !changes.color_map.timeSeries) ||
137+
(typeof (changes.opacity_function) !== 'undefined' && !changes.opacity_function.timeSeries)) &&
138+
obj.geometry.attributes.attributes) {
139+
140+
var canvas = colorMapHelper.createCanvasGradient(
141+
(changes.color_map && changes.color_map.data) || config.color_map.data,
142+
1024,
143+
(changes.opacity_function && changes.opacity_function.data) || config.opacity_function.data
144+
);
145+
146+
obj.material.uniforms.colormap.value.image = canvas;
147+
obj.material.uniforms.colormap.value.needsUpdate = true;
148+
149+
resolvedChanges.color_map = null;
150+
resolvedChanges.opacity_function = null;
151+
}
152+
153+
if (typeof (changes.color_range) !== 'undefined' && !changes.color_range.timeSeries &&
154+
obj.geometry.attributes.attributes) {
155+
obj.material.uniforms.low.value = changes.color_range[0];
156+
obj.material.uniforms.high.value = changes.color_range[1];
157+
158+
resolvedChanges.color_range = null;
159+
}
160+
161+
if (typeof (changes.attribute) !== 'undefined' && !changes.attribute.timeSeries &&
162+
obj.geometry.attributes.attributes &&
163+
changes.attribute.data.length === obj.geometry.attributes.attributes.array.length) {
164+
165+
obj.geometry.attributes.attributes.array.set(changes.attribute.data);
166+
obj.geometry.attributes.attributes.needsUpdate = true;
167+
168+
resolvedChanges.attribute = null;
169+
}
91170

92171
commonUpdate(config, changes, resolvedChanges, obj);
93172

js/src/providers/threejs/objects/TextureData.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ module.exports = {
2727

2828
config.interpolation = typeof (config.interpolation) !== 'undefined' ? config.interpolation : true;
2929

30-
if (opacityFunction === null) {
30+
if (opacityFunction === null || opacityFunction.length === 0) {
3131
opacityFunction = [colorMap[0], 1.0, colorMap[colorMap.length - 4], 1.0];
3232

3333
config.opacity_function = {

js/src/providers/threejs/objects/shaders/Points.3d.fragment.glsl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ void main (void)
3737
pos = pos / pos.w;
3838
float depth = (pos.z + 1.0) / 2.0;
3939

40-
if(depth < gl_FragDepthEXT) discard;
40+
if (depth < gl_FragDepthEXT) discard;
4141
gl_FragDepthEXT = depth;
4242
#endif
4343

@@ -48,7 +48,7 @@ void main (void)
4848

4949
finalSphereColor.a *= opacity;
5050

51-
for(int l = 0; l <NUM_DIR_LIGHTS; l++) {
51+
for (int l = 0; l <NUM_DIR_LIGHTS; l++) {
5252
vec3 lightDirection = -directionalLights[l].direction;
5353
float lightingIntensity = clamp(dot(-lightDirection, normal), 0.0, 1.0);
5454
addedLights.rgb += directionalLights[l].color * (0.05 + 0.95 * lightingIntensity);

0 commit comments

Comments
 (0)