Skip to content

Commit 86d8a0f

Browse files
committed
Add demo for density-based opacity encoding
1 parent 99e92dc commit 86d8a0f

3 files changed

Lines changed: 216 additions & 0 deletions

File tree

example/dynamic-opacity.js

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/* eslint no-console: 0 */
2+
3+
import createScatterplot from '../src';
4+
import { saveAsPng } from './utils';
5+
6+
const canvas = document.querySelector('#canvas');
7+
const numPointsEl = document.querySelector('#num-points');
8+
const numPointsValEl = document.querySelector('#num-points-value');
9+
const pointSizeEl = document.querySelector('#point-size');
10+
const pointSizeValEl = document.querySelector('#point-size-value');
11+
const opacityEl = document.querySelector('#opacity');
12+
const opacityValEl = document.querySelector('#opacity-value');
13+
const clickLassoInitiatorEl = document.querySelector('#click-lasso-initiator');
14+
const resetEl = document.querySelector('#reset');
15+
const exportEl = document.querySelector('#export');
16+
const exampleEl = document.querySelector('#example-dynamic-opacity');
17+
18+
exampleEl.setAttribute('class', 'active');
19+
exampleEl.removeAttribute('href');
20+
21+
let points = [];
22+
let numPoints = 1000000;
23+
let pointSize = 2;
24+
let opacityByDensityFill = 0.15;
25+
let selection = [];
26+
27+
const lassoMinDelay = 10;
28+
const lassoMinDist = 2;
29+
const showRecticle = true;
30+
const recticleColor = [1, 1, 0.878431373, 0.33];
31+
32+
const selectHandler = ({ points: selectedPoints }) => {
33+
console.log('Selected:', selectedPoints);
34+
selection = selectedPoints;
35+
if (selection.length === 1) {
36+
const point = points[selection[0]];
37+
console.log(
38+
`X: ${point[0]}\nY: ${point[1]}\nCategory: ${point[2]}\nValue: ${point[3]}`
39+
);
40+
}
41+
};
42+
43+
const deselectHandler = () => {
44+
console.log('Deselected:', selection);
45+
selection = [];
46+
};
47+
48+
const scatterplot = createScatterplot({
49+
canvas,
50+
lassoMinDelay,
51+
lassoMinDist,
52+
pointSize,
53+
pointColor: '#fff',
54+
opacityBy: 'density',
55+
showRecticle,
56+
recticleColor,
57+
});
58+
59+
exportEl.addEventListener('click', () => saveAsPng(scatterplot));
60+
61+
console.log(`Scatterplot v${scatterplot.get('version')}`);
62+
63+
scatterplot.subscribe('select', selectHandler);
64+
scatterplot.subscribe('deselect', deselectHandler);
65+
66+
const generatePoints = (num) => {
67+
const newPoints = [];
68+
69+
let xn = 2.644838333129883;
70+
let yn = 4.060488700866699;
71+
let zn = 2.8982460498809814;
72+
let xn1;
73+
let yn1;
74+
let zn1;
75+
const a = 0.2;
76+
const b = 0.2;
77+
const c = 5.7;
78+
const dt = 0.006;
79+
80+
let minX = Infinity;
81+
let maxX = -Infinity;
82+
let minY = Infinity;
83+
let maxY = -Infinity;
84+
for (let i = 0; i <= num; i++) {
85+
let dx = -yn - zn;
86+
let dy = xn + a * yn;
87+
let dz = b + zn * (xn - c);
88+
89+
const xh = xn + 0.5 * dt * dx;
90+
const yh = yn + 0.5 * dt * dy;
91+
const zh = zn + 0.5 * dt * dz;
92+
93+
dx = -yh - zh;
94+
dy = xh + a * yh;
95+
dz = b + zh * (xh - c);
96+
97+
xn1 = xn + dt * dx;
98+
yn1 = yn + dt * dy;
99+
zn1 = zn + dt * dz;
100+
101+
newPoints.push([xn1, yn1, 0, 0]);
102+
103+
minX = xn1 < minX ? xn1 : minX;
104+
maxX = xn1 > maxX ? xn1 : maxX;
105+
minY = yn1 < minY ? yn1 : minY;
106+
maxY = yn1 > maxY ? yn1 : maxY;
107+
108+
xn = xn1;
109+
yn = yn1;
110+
zn = zn1;
111+
}
112+
113+
const dX = maxX - minX;
114+
const dY = maxY - minY;
115+
116+
for (let i = 0; i <= num; i++) {
117+
newPoints[i][0] -= minX;
118+
newPoints[i][0] /= dX / 2;
119+
newPoints[i][0] -= 1;
120+
newPoints[i][1] -= minY;
121+
newPoints[i][1] /= dY / 2;
122+
newPoints[i][1] -= 1;
123+
}
124+
125+
return newPoints;
126+
};
127+
128+
const setNumPoint = (newNumPoints) => {
129+
numPoints = newNumPoints;
130+
numPointsEl.value = numPoints;
131+
numPointsValEl.innerHTML = numPoints;
132+
points = generatePoints(numPoints);
133+
scatterplot.draw(points);
134+
};
135+
136+
const numPointsInputHandler = (event) => {
137+
numPointsValEl.innerHTML = `${+event.target
138+
.value} <em>release to redraw</em>`;
139+
};
140+
141+
numPointsEl.addEventListener('input', numPointsInputHandler);
142+
143+
const numPointsChangeHandler = (event) => setNumPoint(+event.target.value);
144+
145+
numPointsEl.addEventListener('change', numPointsChangeHandler);
146+
147+
const setPointSize = (newPointSize) => {
148+
pointSize = newPointSize;
149+
pointSizeEl.value = pointSize;
150+
pointSizeValEl.innerHTML = pointSize;
151+
scatterplot.set({ pointSize });
152+
};
153+
154+
const pointSizeInputHandler = (event) => setPointSize(+event.target.value);
155+
pointSizeEl.addEventListener('input', pointSizeInputHandler);
156+
157+
const opacityByDensityFillGroup = opacityEl.parentNode.cloneNode(true);
158+
opacityEl.parentNode.after(opacityByDensityFillGroup);
159+
160+
opacityEl.style.display = 'none';
161+
opacityValEl.innerHTML = 'Auto';
162+
163+
const opacityByDensityFillEl = opacityByDensityFillGroup.querySelector('input');
164+
const opacityByDensityFillLabEl = opacityByDensityFillGroup.querySelector(
165+
'.label'
166+
);
167+
const opacityByDensityFillValEl = opacityByDensityFillGroup.querySelector(
168+
'.value'
169+
);
170+
171+
opacityByDensityFillLabEl.innerHTML = 'Opacity Density Fill';
172+
173+
const setOpacityByDensityFill = (newOpacityByDensityFill, silent) => {
174+
opacityByDensityFill = newOpacityByDensityFill;
175+
opacityByDensityFillEl.value = opacityByDensityFill;
176+
opacityByDensityFillValEl.innerHTML = opacityByDensityFill;
177+
if (!silent) scatterplot.set({ opacityByDensityFill });
178+
};
179+
const opacityByDensityFillInputHandler = (event) =>
180+
setOpacityByDensityFill(+event.target.value);
181+
opacityByDensityFillEl.addEventListener(
182+
'input',
183+
opacityByDensityFillInputHandler
184+
);
185+
setOpacityByDensityFill(opacityByDensityFill, true);
186+
187+
const clickLassoInitiatorChangeHandler = (event) => {
188+
scatterplot.set({
189+
lassoInitiator: event.target.checked,
190+
});
191+
};
192+
193+
clickLassoInitiatorEl.addEventListener(
194+
'change',
195+
clickLassoInitiatorChangeHandler
196+
);
197+
198+
const resetClickHandler = () => {
199+
scatterplot.reset();
200+
};
201+
202+
resetEl.addEventListener('click', resetClickHandler);
203+
204+
setPointSize(pointSize);
205+
setNumPoint(numPoints);

example/index.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,11 @@ <h1 id="title">Regl Scatterplot</h1>
439439
>Size &amp; Opacity Encoding</a
440440
>
441441
</li>
442+
<li>
443+
<a id="example-dynamic-opacity" href="dynamic-opacity.html"
444+
>Dynamic Opacity</a
445+
>
446+
</li>
442447
<li>
443448
<a id="example-background" href="texture-background.html"
444449
>Background Image</a

webpack.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module.exports = (env, argv) => ({
1111
connectedPoints: './example/connected-points.js',
1212
connectedPointsBySegment: './example/connected-points-by-segments.js',
1313
performanceMode: './example/performance-mode.js',
14+
dynamicOpacity: './example/dynamic-opacity.js',
1415
},
1516
output: {
1617
path: `${__dirname}/docs`,
@@ -77,5 +78,10 @@ module.exports = (env, argv) => ({
7778
filename: 'performance-mode.html',
7879
chunks: ['performanceMode'],
7980
}),
81+
new HtmlWebpackPlugin({
82+
template: 'example/index.html',
83+
filename: 'dynamic-opacity.html',
84+
chunks: ['dynamicOpacity'],
85+
}),
8086
],
8187
});

0 commit comments

Comments
 (0)