Skip to content

Commit e612a9e

Browse files
committed
Add methods for programmatic zooming
1 parent 1469311 commit e612a9e

8 files changed

Lines changed: 465 additions & 24 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
## Next
22

3+
## v1.4.0
4+
5+
- Add zooming via the following four methods. All four methods supported animated transition just like `draw()`.
6+
1. `scatterplot.zoomToLocation(target, distance)` for zooming to a specific point location
7+
2. `scatterplot.zoomToArea(rectangle)` for zooming to an area specified by a rectangular bounding box
8+
3. `scatterplot.zoomToPoints(pointIndices)` for zooming to a set of points
9+
4. `scatterplot.zoomToOrigin()` for zooming to the origin
10+
311
## v1.3.2
412

513
- Add `scatterplot.isSupported` and `renderer.isSupported` as read-only properties to expose if all GL extensions are supported and enabled in the user's browser (#90)

README.md

Lines changed: 123 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ You can encode a point data value in multiple ways. For instance, as you can see
122122

123123
**Why can't I specify a range function instead of a map?** Until we have implemented enough scale functions in the shader it's easier to let _you_ pre-compute the map. For instance, if you wanted to encode a continuous values on a log scale of point size, you can simply do `pointSize: Array(100).fill().map((v, i) => Math.log(i + 1) + 1)`.
124124

125-
For a complete example see [example/index.js](example/index.js) and [example/size-encoding.js](example/size-encoding.js).
125+
[Code Example](example/index.js) | [Demo](https://flekschas.github.io/regl-scatterplot/index.html)
126126

127127
### Connecting points
128128

@@ -170,7 +170,7 @@ would lead tp the following line segment ordering:
170170

171171
Note, to visualize the point connections, make sure `scatterplot.set({ showPointConnection: true })` is set!
172172

173-
For an example see [example/connected-points.js](example/connected-points.js).
173+
[Code Example](example/connected-points.js) | [Demo](https://flekschas.github.io/regl-scatterplot/connected-points.html)
174174

175175
### Synchronize D3 x and y scales with the scatterplot view
176176

@@ -190,7 +190,7 @@ const scatterplot = createScatterplot({
190190

191191
Now whenever you pan or zoom, the domains of `xScale` and `yScale` will be updated according to your current view. Note, the ranges are automatically set to the width and height of your `canvas` object.
192192

193-
For a complete example with D3 axes see [example/axes.js](example/axes.js).
193+
[Code Example](example/axes.js) | [Demo](https://flekschas.github.io/regl-scatterplot/axes.html)
194194

195195
### Translating Point Coordinates to Screen Coordinates
196196

@@ -211,7 +211,45 @@ scatterplot.subscribe('view', ({ xScale, yScale }) => {
211211
});
212212
```
213213

214-
For a complete example see [example/text-labels.js](example/text-labels.js).
214+
[Code Example](example/text-labels.js) | [Demo](https://flekschas.github.io/regl-scatterplot/text-labels.html)
215+
216+
### Transition Points
217+
218+
To make sense of two different states of points, it can help to show an animation by transitioning the points from their first to their second location. To do so, simple `draw()` the new points as follows:
219+
220+
```javascript
221+
const initialPoints = Array.from({ length: 100 }, () => [Math.random() * 42, Math.random()]);
222+
const finalPoints = Array.from({ length: 100 }, () => [Math.random() * 42, Math.random()]);
223+
224+
const scatterplot = createScatterplot({ ... });
225+
scatterplot.draw(initialPoints).then(() => {
226+
scatterplot.draw(finalPoints, { transition: true });
227+
})
228+
```
229+
230+
It's important that the number of points is the same for the two `draw()` calls. Also note that the point correspondence is determined by their index.
231+
232+
[Code Example](example/transition.js) | [Demo](https://flekschas.github.io/regl-scatterplot/transition.html)
233+
234+
### Zoom to Points
235+
236+
Sometimes it can be useful to programmatically zoom to a set of points. In regl-scatterplot you can do this with the `zoomToPoints()` method as follows:
237+
238+
```javascript
239+
const points = Array.from({ length: 100 }, () => [Math.random() * 42, Math.random()]);
240+
241+
const scatterplot = createScatterplot({ ... });
242+
scatterplot.draw(initialPoints).then(() => {
243+
// We'll select the first five points...
244+
scatterplot.select([0, 1, 2, 3, 4]);
245+
// ...and zoom into them
246+
scatterplot.zoomToPoints([0, 1, 2, 3, 4], { transition: true })
247+
})
248+
```
249+
250+
Note that the zooming can be smoothly transitioned when `{ transition: true }` is passed to the function.
251+
252+
[Code Example](example/multiple-instances.js) | [Demo](https://flekschas.github.io/regl-scatterplot/multiple-instances.html)
215253

216254
## API
217255

@@ -378,7 +416,87 @@ scatterplot.hover(1); // To hover the second point
378416
**Arguments:**
379417

380418
- `options` [optional] is an object with the following properties:
381-
- `preventEvent`: if `true` the `deselect` will not be published.
419+
- `preventEvent`: if `true` the `deselect` will not be published.
420+
421+
<a name="scatterplot.zoomToPoints" href="#scatterplot.zoomToPoints">#</a> scatterplot.<b>zoomToPoints</b>(<i>points</i>, <i>options = {}</i>)
422+
423+
Zoom to a set of points
424+
425+
**Arguments:**
426+
427+
- `points` is an array of point indices.
428+
- `options` [optional] is an object with the following properties:
429+
- `padding`: [default: `0`]: relative padding around the bounding box of the points to zoom to
430+
- `transition` [default: `false`]: if `true`, the camera will smoothly transition to its new position
431+
- `transitionDuration` [default: `500`]: the duration in milliseconds over which the transition should occur
432+
- `transitionEasing` [default: `cubicInOut`]: the easing function, which determines how intermediate values of the transition are calculated
433+
434+
**Examples:**
435+
436+
```javascript
437+
// Let's say we have three points
438+
scatterplot.draw([
439+
[0.1, 0.1],
440+
[0.2, 0.2],
441+
[0.3, 0.3],
442+
]);
443+
444+
// To zoom to the first and second point we have to do
445+
scatterplot.zoomToPoints([0, 1]);
446+
```
447+
448+
<a name="scatterplot.zoomToOrigin" href="#scatterplot.zoomToOrigin">#</a> scatterplot.<b>zoomToOrigin</b>(<i>options = {}</i>)
449+
450+
Zoom to the original camera position. This is similar to resetting the view
451+
452+
**Arguments:**
453+
454+
- `options` [optional] is an object with the following properties:
455+
- `transition` [default: `false`]: if `true`, the camera will smoothly transition to its new position
456+
- `transitionDuration` [default: `500`]: the duration in milliseconds over which the transition should occur
457+
- `transitionEasing` [default: `cubicInOut`]: the easing function, which determines how intermediate values of the transition are calculated
458+
459+
<a name="scatterplot.zoomToLocation" href="#scatterplot.zoomToLocation">#</a> scatterplot.<b>zoomToLocation</b>(<i>target</i>, <i>distance</i>, <i>options = {}</i>)
460+
461+
Zoom to a specific location, specified in normalized device coordinates. This function is similar to [`scatterplot.lookAt()`](#scatterplot.lookAt), however, it allows to smoothly transition the camera position.
462+
463+
**Arguments:**
464+
465+
- `target` the camera target given as a `[x, y]` tuple.
466+
- `distance` the camera distance to the target given as a number between `]0, Infinity]`. The smaller the number the closer moves the camera, i.e., the more the view is zoomed in.
467+
- `options` [optional] is an object with the following properties:
468+
- `transition` [default: `false`]: if `true`, the camera will smoothly transition to its new position
469+
- `transitionDuration` [default: `500`]: the duration in milliseconds over which the transition should occur
470+
- `transitionEasing` [default: `cubicInOut`]: the easing function, which determines how intermediate values of the transition are calculated
471+
472+
**Examples:**
473+
474+
```javascript
475+
scatterplot.zoomToLocation([0.5, 0.5], 0.5, { transition: true });
476+
// => This will make the camera zoom into the top-right corner of the scatter plot
477+
```
478+
479+
<a name="scatterplot.zoomToArea" href="#scatterplot.zoomToArea">#</a> scatterplot.<b>zoomToArea</b>(<i>rectangle</i>, <i>options = {}</i>)
480+
481+
Zoom to a specific area specified by a recangle in normalized device coordinates.
482+
483+
**Arguments:**
484+
485+
- `rectangle` the rectangle must come in the form of `{ x, y, width, height }`.
486+
- `options` [optional] is an object with the following properties:
487+
- `transition` [default: `false`]: if `true`, the camera will smoothly transition to its new position
488+
- `transitionDuration` [default: `500`]: the duration in milliseconds over which the transition should occur
489+
- `transitionEasing` [default: `cubicInOut`]: the easing function, which determines how intermediate values of the transition are calculated
490+
491+
**Examples:**
492+
493+
```javascript
494+
scatterplot.zoomToArea(
495+
{ x: 0, y: 0, width: 1, height: 1 },
496+
{ transition: true }
497+
);
498+
// => This will make the camera zoom into the top-right corner of the scatter plot
499+
```
382500

383501
<a name="scatterplot.lookAt" href="#scatterplot.lookAt">#</a> scatterplot.<b>lookAt</b>(<i>view</i>, <i>options = {}</i>)
384502

example/multiple-instances.js

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -229,21 +229,21 @@ resetEl.addEventListener('click', resetClickHandler);
229229
/**
230230
* Link scatter plots
231231
*/
232-
const syncView = (source, cameraView) => {
233-
scatterplots
234-
.filter((sp) => sp !== source)
235-
.forEach((sp) => {
236-
sp.view(cameraView, { preventEvent: true });
237-
});
238-
};
239-
240232
const syncSelection = (source, selectedPoints, classIds) => {
241233
scatterplots
242234
.filter((sp) => sp !== source)
243235
.forEach((sp) => {
244236
sp.select(selectedPoints, { preventEvent: true });
245237
});
246238

239+
scatterplots.forEach((sp) => {
240+
sp.zoomToPoints(selectedPoints, {
241+
padding: 0.2,
242+
transition: true,
243+
transitionDuration: 1500,
244+
});
245+
});
246+
247247
const pointId =
248248
selectedPoints[Math.floor(Math.random() * (selectedPoints.length - 1))];
249249
showImg(pointId, classIds[pointId]);
@@ -256,6 +256,13 @@ const syncDeselection = (source) => {
256256
sp.deselect({ preventEvent: true });
257257
});
258258

259+
scatterplots.forEach((sp) => {
260+
sp.zoomToOrigin({
261+
transition: true,
262+
transitionDuration: 1500,
263+
});
264+
});
265+
259266
hideImg();
260267
};
261268

@@ -288,7 +295,6 @@ whenData
288295
);
289296
});
290297
sp.subscribe('pointout', hideNote);
291-
sp.subscribe('view', ({ view }) => syncView(sp, view));
292298
sp.subscribe('select', ({ points }) =>
293299
syncSelection(sp, points, classIds)
294300
);

public/index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,9 @@ <h1 id="title">Regl Scatterplot</h1>
616616
</li>
617617
<li>
618618
<a id="example-multiple-instances" href="multiple-instances.html"
619-
>Multiple Instances</a
619+
>Multiple Instances<br /><span class="smaller"
620+
>(with linked point selection and animated zoom)</span
621+
></a
620622
>
621623
</li>
622624
</ul>

0 commit comments

Comments
 (0)