Skip to content

Commit a4f7b25

Browse files
committed
feat: Add initial projection support for interactors.
1 parent d63ee38 commit a4f7b25

4 files changed

Lines changed: 56 additions & 8 deletions

File tree

packages/plot/src/interactors/Interval1D.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { getField } from './util/get-field.js';
66
import { invert } from './util/invert.js';
77
import { patchScreenCTM } from './util/patchScreenCTM.js';
88
import { sanitizeStyles } from './util/sanitize-styles.js';
9+
import { getScale } from './util/scale.js';
910

1011
export class Interval1D {
1112
constructor(mark, {
@@ -62,11 +63,11 @@ export class Interval1D {
6263
}
6364

6465
init(svg) {
65-
const { brush, channel, style } = this;
66-
this.scale = svg.scale(channel);
66+
const { brush, channel, style, mark: { plot } } = this;
67+
this.scale = getScale(svg, channel, plot);
6768

68-
const rx = svg.scale('x').range;
69-
const ry = svg.scale('y').range;
69+
const rx = getScale(svg, 'x', plot).range;
70+
const ry = getScale(svg, 'y', plot).range;
7071
brush.extent([[min(rx), min(ry)], [max(rx), max(ry)]]);
7172

7273
const facets = select(svg).selectAll('g[aria-label="facet"]');

packages/plot/src/interactors/Interval2D.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { getField } from './util/get-field.js';
66
import { invert } from './util/invert.js';
77
import { patchScreenCTM } from './util/patchScreenCTM.js';
88
import { sanitizeStyles } from './util/sanitize-styles.js';
9+
import { getScale } from './util/scale.js';
910

1011
const asc = (a, b) => a - b;
1112

@@ -69,9 +70,9 @@ export class Interval2D {
6970
}
7071

7172
init(svg) {
72-
const { brush, style } = this;
73-
const xscale = this.xscale = svg.scale('x');
74-
const yscale = this.yscale = svg.scale('y');
73+
const { brush, style, mark: { plot } } = this;
74+
const xscale = this.xscale = getScale(svg, 'x', plot);
75+
const yscale = this.yscale = getScale(svg, 'y', plot);
7576
const rx = xscale.range;
7677
const ry = yscale.range;
7778
brush.extent([[min(rx), min(ry)], [max(rx), max(ry)]]);

packages/plot/src/interactors/Nearest.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { isSelection } from '@uwdata/mosaic-core';
22
import { eq, literal } from '@uwdata/mosaic-sql';
33
import { select, pointer } from 'd3';
44
import { getField } from './util/get-field.js';
5+
import { getScale } from './util/scale.js';
56

67
export class Nearest {
78
constructor(mark, {
@@ -36,7 +37,7 @@ export class Nearest {
3637

3738
const facets = select(svg).selectAll('g[aria-label="facet"]');
3839
const root = facets.size() ? facets : select(svg);
39-
const scale = svg.scale(channel);
40+
const scale = getScale(svg, channel, mark.plot);
4041
const param = !isSelection(selection);
4142

4243
root.on('pointerdown pointermove', function(evt) {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
export function getScale(fig, channel, plot) {
2+
const scale = fig.scale(channel);
3+
if (scale) return scale;
4+
5+
const proj = fig.projection();
6+
if (proj && (channel === 'x' || channel === 'y')) {
7+
const type = plot.getAttribute('projectionType');
8+
return projectionScale(proj, type, channel);
9+
}
10+
}
11+
12+
function projectionScale(proj, type, channel) {
13+
const { offset, translate: [tx, ty], scale, width, height } = proj;
14+
const planar = type === 'identity' || type === 'reflect-y';
15+
16+
let range;
17+
let apply;
18+
let invert;
19+
20+
if (channel === 'x') {
21+
range = [offset[0], offset[0] + width];
22+
apply = planar ? x => x * scale + tx
23+
: v => proj.stream([v, 0])[0];
24+
invert = planar ? x => (x - tx) / scale
25+
: proj.invert ? x => proj.invert([(x - tx) / scale, 0])[0]
26+
: null;
27+
} else {
28+
range = [offset[1], offset[1] + height];
29+
apply = type === 'identity' ? y => y * scale + ty
30+
: type === 'reflect-y' ? y => -y * scale + ty
31+
: v => proj.stream([0, v])[1];
32+
invert = type === 'identity' ? y => (y - ty) / scale
33+
: type === 'reflect-y' ? y => (ty - y) / scale
34+
: proj.invert ? y => proj.invert([0, (y - ty) / scale])[1]
35+
: null;
36+
}
37+
38+
return {
39+
type: planar || type === 'equirectangular' ? 'linear' : `${type}-x`,
40+
domain: range.map(v => invert(v)),
41+
range,
42+
apply,
43+
invert
44+
};
45+
}

0 commit comments

Comments
 (0)