Skip to content

Commit 741f958

Browse files
authored
Merge pull request #3438 from plotly/isosurface-volume
iso-surface finalist with caps, slices & spaceframe display on uniform or non-uniform coordinates
2 parents 7652b90 + ea8dccd commit 741f958

32 files changed

+37095
-1
lines changed

lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Plotly.register([
2525

2626
require('./scatter3d'),
2727
require('./surface'),
28+
require('./isosurface'),
2829
require('./mesh3d'),
2930
require('./cone'),
3031
require('./streamtube'),

lib/isosurface.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Copyright 2012-2019, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
module.exports = require('../src/traces/isosurface');

src/plots/gl3d/scene.js

+24-1
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ function render(scene) {
8888
}
8989

9090
var tx;
91+
var vectorTx = [];
9192

9293
if(trace.type === 'cone' || trace.type === 'streamtube') {
93-
var vectorTx = [];
9494
if(isHoverinfoAll || hoverinfoParts.indexOf('u') !== -1) {
9595
vectorTx.push('u: ' + formatter('xaxis', selection.traceCoordinate[3]));
9696
}
@@ -110,6 +110,12 @@ function render(scene) {
110110
vectorTx.push(selection.textLabel);
111111
}
112112
tx = vectorTx.join('<br>');
113+
} else if(trace.type === 'isosurface') {
114+
vectorTx.push('value: ' + Axes.tickText(scene.mockAxis, scene.mockAxis.d2l(selection.traceCoordinate[3]), 'hover').text);
115+
if(selection.textLabel) {
116+
vectorTx.push(selection.textLabel);
117+
}
118+
tx = vectorTx.join('<br>');
113119
} else {
114120
tx = selection.textLabel;
115121
}
@@ -271,6 +277,8 @@ function initializeGLPlot(scene, canvas, gl) {
271277
// List of scene objects
272278
scene.traces = {};
273279

280+
scene.make4thDimension();
281+
274282
return true;
275283
}
276284

@@ -855,4 +863,19 @@ proto.setConvert = function() {
855863
}
856864
};
857865

866+
proto.make4thDimension = function() {
867+
868+
var _this = this;
869+
var gd = _this.graphDiv;
870+
var fullLayout = gd._fullLayout;
871+
872+
// mock axis for hover formatting
873+
_this.mockAxis = {
874+
type: 'linear',
875+
showexponent: 'all',
876+
exponentformat: 'B'
877+
};
878+
Axes.setConvert(_this.mockAxis, fullLayout);
879+
};
880+
858881
module.exports = Scene;

src/traces/isosurface/attributes.js

+272
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
/**
2+
* Copyright 2012-2019, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
var colorscaleAttrs = require('../../components/colorscale/attributes');
12+
var colorbarAttrs = require('../../components/colorbar/attributes');
13+
var surfaceAtts = require('../surface/attributes');
14+
var meshAttrs = require('../mesh3d/attributes');
15+
var baseAttrs = require('../../plots/attributes');
16+
17+
var extendFlat = require('../../lib/extend').extendFlat;
18+
var overrideAll = require('../../plot_api/edit_types').overrideAll;
19+
20+
function makeSliceAttr(axLetter) {
21+
return {
22+
show: {
23+
valType: 'boolean',
24+
role: 'info',
25+
dflt: false,
26+
description: [
27+
'Determines whether or not slice planes about the', axLetter,
28+
'dimension are drawn.'
29+
].join(' ')
30+
},
31+
locations: {
32+
valType: 'data_array',
33+
dflt: [],
34+
role: 'info',
35+
description: [
36+
'Specifies the location(s) of slices on the axis.',
37+
'When not locations specified slices would be created for',
38+
'all points of the axis', axLetter, 'except start and end.'
39+
].join(' ')
40+
},
41+
fill: {
42+
valType: 'number',
43+
role: 'style',
44+
min: 0,
45+
max: 1,
46+
dflt: 1,
47+
description: [
48+
'Sets the fill ratio of the `slices`. The default fill value of the',
49+
'`slices` is 1 meaning that they are entirely shaded. On the other hand',
50+
'Applying a `fill` ratio less than one would allow the creation of',
51+
'openings parallel to the edges.'
52+
].join(' ')
53+
}
54+
};
55+
}
56+
57+
function makeCapAttr(axLetter) {
58+
return {
59+
show: {
60+
valType: 'boolean',
61+
role: 'info',
62+
dflt: true,
63+
description: [
64+
'Sets the fill ratio of the `slices`. The default fill value of the', axLetter,
65+
'`slices` is 1 meaning that they are entirely shaded. On the other hand',
66+
'Applying a `fill` ratio less than one would allow the creation of',
67+
'openings parallel to the edges.'
68+
].join(' ')
69+
},
70+
fill: {
71+
valType: 'number',
72+
role: 'style',
73+
min: 0,
74+
max: 1,
75+
dflt: 1,
76+
description: [
77+
'Sets the fill ratio of the `caps`. The default fill value of the',
78+
'`caps` is 1 meaning that they are entirely shaded. On the other hand',
79+
'Applying a `fill` ratio less than one would allow the creation of',
80+
'openings parallel to the edges.'
81+
].join(' ')
82+
}
83+
};
84+
}
85+
86+
var attrs = module.exports = overrideAll(extendFlat({
87+
x: {
88+
valType: 'data_array',
89+
role: 'info',
90+
description: [
91+
'Sets the X coordinates of the vertices on X axis.'
92+
].join(' ')
93+
},
94+
y: {
95+
valType: 'data_array',
96+
role: 'info',
97+
description: [
98+
'Sets the Y coordinates of the vertices on Y axis.'
99+
].join(' ')
100+
},
101+
z: {
102+
valType: 'data_array',
103+
role: 'info',
104+
description: [
105+
'Sets the Z coordinates of the vertices on Z axis.'
106+
].join(' ')
107+
},
108+
value: {
109+
valType: 'data_array',
110+
role: 'info',
111+
description: [
112+
'Sets the 4th dimension (value) of the vertices.'
113+
].join(' ')
114+
},
115+
isomin: {
116+
valType: 'number',
117+
role: 'info',
118+
description: [
119+
'Sets the minimum boundary for iso-surface plot.'
120+
].join(' ')
121+
},
122+
isomax: {
123+
valType: 'number',
124+
role: 'info',
125+
description: [
126+
'Sets the maximum boundary for iso-surface plot.'
127+
].join(' ')
128+
},
129+
130+
surface: {
131+
show: {
132+
valType: 'boolean',
133+
role: 'info',
134+
dflt: true,
135+
description: [
136+
'Hides/displays surfaces between minimum and maximum iso-values.'
137+
].join(' ')
138+
},
139+
count: {
140+
valType: 'integer',
141+
role: 'info',
142+
dflt: 2,
143+
min: 1,
144+
description: [
145+
'Sets the number of iso-surfaces between minimum and maximum iso-values.',
146+
'By default this value is 2 meaning that only minimum and maximum surfaces',
147+
'would be drawn.'
148+
].join(' ')
149+
},
150+
fill: {
151+
valType: 'number',
152+
role: 'style',
153+
min: 0,
154+
max: 1,
155+
dflt: 1,
156+
description: [
157+
'Sets the fill ratio of the iso-surface. The default fill value of the',
158+
'surface is 1 meaning that they are entirely shaded. On the other hand',
159+
'Applying a `fill` ratio less than one would allow the creation of',
160+
'openings parallel to the edges.'
161+
].join(' ')
162+
},
163+
pattern: {
164+
valType: 'flaglist',
165+
flags: ['A', 'B', 'C', 'D', 'E'],
166+
extras: ['all', 'odd', 'even'],
167+
dflt: 'all',
168+
role: 'style',
169+
description: [
170+
'Sets the surface pattern of the iso-surface 3-D sections. The default pattern of',
171+
'the surface is `all` meaning that the rest of surface elements would be shaded.',
172+
'The check options (either 1 or 2) could be used to draw half of the squares',
173+
'on the surface. Using various combinations of capital `A`, `B`, `C`, `D` and `E`',
174+
'may also be used to reduce the number of triangles on the iso-surfaces and',
175+
'creating other patterns of interest.'
176+
].join(' ')
177+
}
178+
},
179+
180+
spaceframe: {
181+
show: {
182+
valType: 'boolean',
183+
role: 'info',
184+
dflt: false,
185+
description: [
186+
'Displays/hides tetrahedron shapes between minimum and',
187+
'maximum iso-values. Often useful when either caps or',
188+
'surfaces are disabled or filled with values less than 1.'
189+
].join(' ')
190+
},
191+
fill: {
192+
valType: 'number',
193+
role: 'style',
194+
min: 0,
195+
max: 1,
196+
dflt: 0.15,
197+
description: [
198+
'Sets the fill ratio of the `spaceframe` elements. The default fill value',
199+
'is 0.15 meaning that only 15% of the area of every faces of tetras would be',
200+
'shaded. Applying a greater `fill` ratio would allow the creation of stronger',
201+
'elements or could be sued to have entirely closed areas (in case of using 1).'
202+
].join(' ')
203+
}
204+
},
205+
206+
slices: {
207+
x: makeSliceAttr('x'),
208+
y: makeSliceAttr('y'),
209+
z: makeSliceAttr('z')
210+
},
211+
212+
caps: {
213+
x: makeCapAttr('x'),
214+
y: makeCapAttr('y'),
215+
z: makeCapAttr('z')
216+
},
217+
218+
text: {
219+
valType: 'string',
220+
role: 'info',
221+
dflt: '',
222+
arrayOk: true,
223+
description: [
224+
'Sets the text elements associated with the vertices.',
225+
'If trace `hoverinfo` contains a *text* flag and *hovertext* is not set,',
226+
'these elements will be seen in the hover labels.'
227+
].join(' ')
228+
},
229+
},
230+
231+
colorscaleAttrs('', {
232+
colorAttr: '`value`',
233+
showScaleDflt: true,
234+
editTypeOverride: 'calc'
235+
}), {
236+
237+
colorbar: colorbarAttrs,
238+
239+
// Flat shaded mode
240+
flatshading: {
241+
valType: 'boolean',
242+
role: 'style',
243+
dflt: false,
244+
description: [
245+
'Determines whether or not normal smoothing is applied to the isosurfaces,',
246+
'creating isosurfaces with an angular, low-poly look via flat reflections.'
247+
].join(' ')
248+
},
249+
250+
contour: {
251+
show: extendFlat({}, surfaceAtts.contours.x.show, {
252+
description: [
253+
'Sets whether or not dynamic contours are shown on hover.',
254+
'Contours are more useful when hovering on caps and slices.'
255+
].join(' ')
256+
}),
257+
color: surfaceAtts.contours.x.color,
258+
width: surfaceAtts.contours.x.width
259+
},
260+
261+
lightposition: {
262+
x: extendFlat({}, surfaceAtts.lightposition.x, {dflt: 1e5}),
263+
y: extendFlat({}, surfaceAtts.lightposition.y, {dflt: 1e5}),
264+
z: extendFlat({}, surfaceAtts.lightposition.z, {dflt: 0})
265+
},
266+
lighting: meshAttrs.lighting,
267+
268+
hoverinfo: extendFlat({}, baseAttrs.hoverinfo)
269+
}), 'calc', 'nested');
270+
271+
attrs.x.editType = attrs.y.editType = attrs.z.editType = attrs.value.editType = 'calc+clearAxisTypes';
272+
attrs.transforms = undefined;

src/traces/isosurface/calc.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* Copyright 2012-2019, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
var colorscaleCalc = require('../../components/colorscale/calc');
12+
13+
module.exports = function calc(gd, trace) {
14+
15+
trace._len = Math.min(trace.x.length, trace.y.length, trace.z.length, trace.value.length);
16+
17+
var min = Infinity;
18+
var max = -Infinity;
19+
var len = trace.value.length;
20+
for(var i = 0; i < len; i++) {
21+
var v = trace.value[i];
22+
min = Math.min(min, v);
23+
max = Math.max(max, v);
24+
}
25+
26+
trace._minValues = min;
27+
trace._maxValues = max;
28+
29+
trace._vMin = (trace.isomin === undefined || trace.isomin === null) ? min : trace.isomin;
30+
trace._vMax = (trace.isomax === undefined || trace.isomin === null) ? max : trace.isomax;
31+
32+
colorscaleCalc(gd, trace, {
33+
vals: [trace._vMin, trace._vMax],
34+
containerStr: '',
35+
cLetter: 'c'
36+
});
37+
};

0 commit comments

Comments
 (0)