Skip to content

Commit

Permalink
Only invoke mouse event on affected layers (#384)
Browse files Browse the repository at this point in the history
- only invoke onHover/onClick on affected layers
- update docs
  • Loading branch information
Pessimistress authored Mar 1, 2017
1 parent 4a9f9c4 commit c3eae5b
Show file tree
Hide file tree
Showing 14 changed files with 130 additions and 84 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Ref: http://keepachangelog.com/en/0.3.0/

#### [v4.0.0-beta.3]
- Add PointCloudLayer
- FIX: `onHover` and `onClick` are no longer called on layers that are not affected
- BREAKING: `layer.pick()` is renamed to `layer.getPickingInfo()`, must return info object

#### [v4.0.0-beta.2]
Expand Down
3 changes: 0 additions & 3 deletions demo/src/components/demos/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,6 @@ export default class PlotDemo extends Component {
}

@autobind _onHover(info) {
if (info.layer.id === 'PlotLayer') {
return;
}
const hoverInfo = info.sample ? info : null;
if (hoverInfo !== this.state.hoverInfo) {
this.setState({hoverInfo});
Expand Down
17 changes: 8 additions & 9 deletions docs/layer-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,17 @@ The default implementation looks for a variable `model` in the layer's
state (which is expected to be an instance of the luma.gl `Model` class)
and calls `draw` on that model.

##### Picking: pick({uniforms, deviceX, deviceY})
##### Picking: getPickingInfo({info, mode})

The pick method should return an object with optional fields about
what was picked. This `info` object is then populated with additional
information by deck.gl and finally passed to the layer's `onHover` or
`onPick` callbacks.
what was picked. This `info` object is then passed to the layer's `onHover`
or `onPick` callbacks.

The default implementation looks for a variable `model` in the layer's
state (which is expected to be an instance of the luma.gl `Model` class)
uses that model for picking, and renders that model with attributes set
that allow the layer shaders to render picking colors instead of normal
colors.
The received `info` argument contains fields `color` as the picked pixel
and `index` calculated using `layer.decodePickingColor()`.

The default implementation populates the `info` object with an `object` field
that is indexed from `layer.props.data`.

### Comparison with React's Lifecycle

Expand Down
13 changes: 10 additions & 3 deletions docs/upgrade-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,22 @@ For API consistency reasons these have all been renamed with the suffix `..Scale

| scatterplotLayer | radius | radiusScale |

## Modified Layers
### Modified Layers


### Upgrading Layers from deck.gl v3 to v4
## Upgrading Layers from deck.gl v3 to v4

While v3 layers should work without changes in v4, there are some improvements
you may want to take advantage of.

## updateTriggers
#### updateTriggers

To enable applications to use accessor names in updateTriggers, you need to
add an 'accessor' field when you register your attributes.

#### getPickingInfo

To override the default picking behavior, implement the `getPickingInfo()` method
that returns an info object to be passed to `onHover` and `onClick` handlers. If
this methods returns `null`, no event will be fired.

13 changes: 13 additions & 0 deletions docs/whats-new.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ name of the vertex attribute controlled by an accessor. It is now possible
to supply names of `accessors`.


### More intuitive mouse events

`onHover` is now only fired on entering/exiting an object instead of on mouse move.

`onClick` is now only fired on the picked layer instead of all pickable layers.


## New Features for Layer Subclassing

### **Overridable shaders
Expand All @@ -100,6 +107,12 @@ Can be a string or a an array of strings. Will be used to match
`updateTriggers` accessor names with instance attributes.


### `getPickingInfo()`

This method replaces the old `pick()` method and is expected to return an info
object. Layers can block the execution of callback functions by returning `null`.


## Performance

A number of performance improvements and fixes have been gradually introduced
Expand Down
4 changes: 2 additions & 2 deletions examples/sample-layers/plot-layer/plot-layer.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Layer} from 'deck.gl';
import {CompositeLayer} from 'deck.gl';
import AxesLayer from './axes-layer';
import SurfaceLayer from './surface-layer';

Expand Down Expand Up @@ -44,7 +44,7 @@ const defaultProps = {
* @param {Number} [props.fontSize] - size of the labels
* @param {Array} [props.axesColor] - color of the gridlines, in [r,g,b,a]
*/
export default class PlotLayer extends Layer {
export default class PlotLayer extends CompositeLayer {

initializeState() {
this.state = {
Expand Down
11 changes: 6 additions & 5 deletions examples/sample-layers/plot-layer/surface-layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,23 +136,24 @@ export default class SurfaceLayer extends Layer {

decodePickingColor([r, g, b]) {
if (b === 0) {
return null;
return -1;
}
return [r / 255, g / 255];
}

pick(props) {
super.pick(props);
const {info} = props;
getPickingInfo(opts) {
const {info} = opts;

if (info && info.index) {
if (info && info.index !== -1) {
const {xMin, xMax, yMin, yMax, getZ} = this.props;
const x = info.index[0] * (xMax - xMin) + xMin;
const y = info.index[1] * (yMax - yMin) + yMin;
const z = getZ(x, y);

info.sample = [x, y, z];
}

return info;
}

_setBounds(bounds) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "deck.gl",
"description": "A suite of 3D-enabled data visualization overlays, suitable for react-map-gl",
"license": "MIT",
"version": "4.0.0-beta.2",
"version": "4.0.0-beta.3",
"keywords": [
"webgl",
"visualization",
Expand Down
67 changes: 25 additions & 42 deletions src/layers/core/geojson-layer/geojson-layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ const getCoordinates = f => get(f, 'geometry.coordinates');
export default class GeoJsonLayer extends CompositeLayer {
initializeState() {
this.state = {
subLayers: null,
subLayerCount: 0,
pickInfos: []
subLayers: null
};
}

Expand All @@ -72,32 +70,14 @@ export default class GeoJsonLayer extends CompositeLayer {
}
}

_onPickSublayer(mode, info) {
const {pickInfos, subLayerCount} = this.state;
pickInfos.push(info);

if (pickInfos.length === subLayerCount) {
// all sublayers have been accounted for
let pickInfo = pickInfos.find(i => i.index >= 0) || pickInfos[0];

pickInfo = Object.assign({}, pickInfo, {
layer: this,
feature: get(pickInfo, 'object.feature') || pickInfo.object
});

switch (mode) {
case 'click': this.props.onClick(pickInfo); break;
case 'hover': this.props.onHover(pickInfo); break;
default: throw new Error('unknown pick type');
}
}
_onHoverSubLayer(info) {
info.object = (info.object && info.object.feature) || info.object;
this.props.onHover(info);
}

getPickingInfo(opts) {
// this is called before the onHover/onClick of sublayers
// pickInfo is used to collect the pick results of all sublayers
this.state.pickInfos.length = 0;
return null;
_onClickSubLayer(info) {
info.object = (info.object && info.object.feature) || info.object;
this.props.onClick(info);
}

renderLayers() {
Expand All @@ -113,22 +93,21 @@ export default class GeoJsonLayer extends CompositeLayer {
drawPolygons = drawPolygons && polygonOutlineFeatures && polygonOutlineFeatures.length > 0;
fillPolygons = fillPolygons && polygonFeatures && polygonFeatures.length > 0;

// Override user's onHover and onClick props
const handlers = {
onHover: this._onPickSublayer.bind(this, 'hover'),
onClick: this._onPickSublayer.bind(this, 'click')
};
const onHover = this._onHoverSubLayer.bind(this);
const onClick = this._onClickSubLayer.bind(this);

// Filled Polygon Layer
const polygonFillLayer = fillPolygons && new PolygonLayer(Object.assign({},
this.props, handlers, {
this.props, {
id: `${id}-polygon-fill`,
data: polygonFeatures,
getPolygon: getCoordinates,
getElevation,
getColor: getFillColor,
extruded,
wireframe: false,
onHover,
onClick,
updateTriggers: {
getElevation: this.props.updateTriggers.getElevation,
getColor: this.props.updateTriggers.getFillColor
Expand All @@ -138,25 +117,29 @@ export default class GeoJsonLayer extends CompositeLayer {
// Polygon outline or wireframe
let polygonOutlineLayer = null;
if (drawPolygons && extruded && wireframe) {
polygonOutlineLayer = new PolygonLayer(Object.assign({}, this.props, handlers, {
polygonOutlineLayer = new PolygonLayer(Object.assign({}, this.props, {
id: `${id}-polygon-wireframe`,
data: polygonFeatures,
getPolygon: getCoordinates,
getElevation,
getColor: getStrokeColor,
extruded: true,
wireframe: true,
onHover,
onClick,
updateTriggers: {
getColor: this.props.updateTriggers.getStrokeColor
}
}));
} else if (drawPolygons) {
polygonOutlineLayer = new PathLayer(Object.assign({}, this.props, handlers, {
polygonOutlineLayer = new PathLayer(Object.assign({}, this.props, {
id: `${id}-polygon-outline`,
data: polygonOutlineFeatures,
getPath: getCoordinates,
getColor: getStrokeColor,
getStrokeWidth,
onHover,
onClick,
updateTriggers: {
getColor: this.props.updateTriggers.getStrokeColor,
getStrokeWidth: this.props.updateTriggers.getStrokeWidth
Expand All @@ -165,42 +148,42 @@ export default class GeoJsonLayer extends CompositeLayer {
}

const lineLayer = drawLines && new PathLayer(Object.assign({},
this.props, handlers, {
this.props, {
id: `${id}-line-paths`,
data: lineFeatures,
getPath: getCoordinates,
getColor: getStrokeColor,
getStrokeWidth,
onHover,
onClick,
updateTriggers: {
getColor: this.props.updateTriggers.getStrokeColor,
getStrokeWidth: this.props.updateTriggers.getStrokeWidth
}
}));

const pointLayer = drawPoints && new ScatterplotLayer(Object.assign({},
this.props, handlers, {
this.props, {
id: `${id}-points`,
data: pointFeatures,
getPosition: getCoordinates,
getColor: getPointColor,
getRadius: getPointSize,
onHover,
onClick,
updateTriggers: {
getColor: this.props.updateTriggers.getPointColor,
getRadius: this.props.updateTriggers.getPointSize
},
fp64: this.props.fp64
}));

const layers = [
return [
polygonFillLayer,
polygonOutlineLayer,
lineLayer,
pointLayer
].filter(Boolean);

this.state.subLayerCount = layers.length;

return layers;
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/lib/composite-layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@ export default class CompositeLayer extends Layer {
constructor(props) {
super(props);
}

getPickingInfo(opts) {
// do not call onHover/onClick on the container
return null;
}
}
Loading

0 comments on commit c3eae5b

Please sign in to comment.