Skip to content

Commit

Permalink
feat: Add initial projection support for interactors.
Browse files Browse the repository at this point in the history
  • Loading branch information
jheer committed Mar 21, 2024
1 parent d63ee38 commit a4f7b25
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 8 deletions.
9 changes: 5 additions & 4 deletions packages/plot/src/interactors/Interval1D.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getField } from './util/get-field.js';
import { invert } from './util/invert.js';
import { patchScreenCTM } from './util/patchScreenCTM.js';
import { sanitizeStyles } from './util/sanitize-styles.js';
import { getScale } from './util/scale.js';

export class Interval1D {
constructor(mark, {
Expand Down Expand Up @@ -62,11 +63,11 @@ export class Interval1D {
}

init(svg) {
const { brush, channel, style } = this;
this.scale = svg.scale(channel);
const { brush, channel, style, mark: { plot } } = this;
this.scale = getScale(svg, channel, plot);

const rx = svg.scale('x').range;
const ry = svg.scale('y').range;
const rx = getScale(svg, 'x', plot).range;
const ry = getScale(svg, 'y', plot).range;
brush.extent([[min(rx), min(ry)], [max(rx), max(ry)]]);

const facets = select(svg).selectAll('g[aria-label="facet"]');
Expand Down
7 changes: 4 additions & 3 deletions packages/plot/src/interactors/Interval2D.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getField } from './util/get-field.js';
import { invert } from './util/invert.js';
import { patchScreenCTM } from './util/patchScreenCTM.js';
import { sanitizeStyles } from './util/sanitize-styles.js';
import { getScale } from './util/scale.js';

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

Expand Down Expand Up @@ -69,9 +70,9 @@ export class Interval2D {
}

init(svg) {
const { brush, style } = this;
const xscale = this.xscale = svg.scale('x');
const yscale = this.yscale = svg.scale('y');
const { brush, style, mark: { plot } } = this;
const xscale = this.xscale = getScale(svg, 'x', plot);
const yscale = this.yscale = getScale(svg, 'y', plot);
const rx = xscale.range;
const ry = yscale.range;
brush.extent([[min(rx), min(ry)], [max(rx), max(ry)]]);
Expand Down
3 changes: 2 additions & 1 deletion packages/plot/src/interactors/Nearest.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { isSelection } from '@uwdata/mosaic-core';
import { eq, literal } from '@uwdata/mosaic-sql';
import { select, pointer } from 'd3';
import { getField } from './util/get-field.js';
import { getScale } from './util/scale.js';

export class Nearest {
constructor(mark, {
Expand Down Expand Up @@ -36,7 +37,7 @@ export class Nearest {

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

root.on('pointerdown pointermove', function(evt) {
Expand Down
45 changes: 45 additions & 0 deletions packages/plot/src/interactors/util/scale.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
export function getScale(fig, channel, plot) {
const scale = fig.scale(channel);
if (scale) return scale;

const proj = fig.projection();
if (proj && (channel === 'x' || channel === 'y')) {
const type = plot.getAttribute('projectionType');
return projectionScale(proj, type, channel);
}
}

function projectionScale(proj, type, channel) {
const { offset, translate: [tx, ty], scale, width, height } = proj;
const planar = type === 'identity' || type === 'reflect-y';

let range;
let apply;
let invert;

if (channel === 'x') {
range = [offset[0], offset[0] + width];
apply = planar ? x => x * scale + tx
: v => proj.stream([v, 0])[0];
invert = planar ? x => (x - tx) / scale
: proj.invert ? x => proj.invert([(x - tx) / scale, 0])[0]
: null;
} else {
range = [offset[1], offset[1] + height];
apply = type === 'identity' ? y => y * scale + ty
: type === 'reflect-y' ? y => -y * scale + ty
: v => proj.stream([0, v])[1];
invert = type === 'identity' ? y => (y - ty) / scale
: type === 'reflect-y' ? y => (ty - y) / scale
: proj.invert ? y => proj.invert([0, (y - ty) / scale])[1]
: null;
}

return {
type: planar || type === 'equirectangular' ? 'linear' : `${type}-x`,
domain: range.map(v => invert(v)),
range,
apply,
invert
};
}

0 comments on commit a4f7b25

Please sign in to comment.