plotly.js
Version:
The open source javascript graphing library that powers plotly
192 lines (151 loc) • 6.02 kB
JavaScript
'use strict';
var readPaths = require('../shapes/draw_newshape/helpers').readPaths;
var displayOutlines = require('../shapes/display_outlines');
var clearOutlineControllers = require('../shapes/handle_outline').clearOutlineControllers;
var Color = require('../color');
var Drawing = require('../drawing');
var arrayEditor = require('../../plot_api/plot_template').arrayEditor;
var helpers = require('../shapes/helpers');
var getPathString = helpers.getPathString;
// Selections are stored in gd.layout.selections, an array of objects
// index can point to one item in this array,
// or non-numeric to simply add a new one
// or -1 to modify all existing
// opt can be the full options object, or one key (to be set to value)
// or undefined to simply redraw
// if opt is blank, val can be 'add' or a full options object to add a new
// annotation at that point in the array, or 'remove' to delete this one
module.exports = {
draw: draw,
drawOne: drawOne,
activateLastSelection: activateLastSelection
};
function draw(gd) {
var fullLayout = gd._fullLayout;
clearOutlineControllers(gd);
// Remove previous selections before drawing new selections in fullLayout.selections
fullLayout._selectionLayer.selectAll('path').remove();
for(var k in fullLayout._plots) {
var selectionLayer = fullLayout._plots[k].selectionLayer;
if(selectionLayer) selectionLayer.selectAll('path').remove();
}
for(var i = 0; i < fullLayout.selections.length; i++) {
drawOne(gd, i);
}
}
function couldHaveActiveSelection(gd) {
return gd._context.editSelection;
}
function drawOne(gd, index) {
// remove the existing selection if there is one.
// because indices can change, we need to look in all selection layers
gd._fullLayout._paperdiv
.selectAll('.selectionlayer [data-index="' + index + '"]')
.remove();
var o = helpers.makeSelectionsOptionsAndPlotinfo(gd, index);
var options = o.options;
var plotinfo = o.plotinfo;
// this selection is gone - quit now after deleting it
// TODO: use d3 idioms instead of deleting and redrawing every time
if(!options._input) return;
drawSelection(gd._fullLayout._selectionLayer);
function drawSelection(selectionLayer) {
var d = getPathString(gd, options);
var attrs = {
'data-index': index,
'fill-rule': 'evenodd',
d: d
};
var opacity = options.opacity;
var fillColor = 'rgba(0,0,0,0)';
var lineColor = options.line.color || Color.contrast(gd._fullLayout.plot_bgcolor);
var lineWidth = options.line.width;
var lineDash = options.line.dash;
if(!lineWidth) {
// ensure invisible border to activate the selection
lineWidth = 5;
lineDash = 'solid';
}
var isActiveSelection = couldHaveActiveSelection(gd) &&
gd._fullLayout._activeSelectionIndex === index;
if(isActiveSelection) {
fillColor = gd._fullLayout.activeselection.fillcolor;
opacity = gd._fullLayout.activeselection.opacity;
}
var allPaths = [];
for(var sensory = 1; sensory >= 0; sensory--) {
var path = selectionLayer.append('path')
.attr(attrs)
.style('opacity', sensory ? 0.1 : opacity)
.call(Color.stroke, lineColor)
.call(Color.fill, fillColor)
// make it easier to select senory background path
.call(Drawing.dashLine,
sensory ? 'solid' : lineDash,
sensory ? 4 + lineWidth : lineWidth
);
setClipPath(path, gd, options);
if(isActiveSelection) {
var editHelpers = arrayEditor(gd.layout, 'selections', options);
path.style({
cursor: 'move',
});
var dragOptions = {
element: path.node(),
plotinfo: plotinfo,
gd: gd,
editHelpers: editHelpers,
isActiveSelection: true // i.e. to enable controllers
};
var polygons = readPaths(d, gd);
// display polygons on the screen
displayOutlines(polygons, path, dragOptions);
} else {
path.style('pointer-events', sensory ? 'all' : 'none');
}
allPaths[sensory] = path;
}
var forePath = allPaths[0];
var backPath = allPaths[1];
backPath.node().addEventListener('click', function() { return activateSelection(gd, forePath); });
}
}
function setClipPath(selectionPath, gd, selectionOptions) {
var clipAxes = selectionOptions.xref + selectionOptions.yref;
Drawing.setClipUrl(
selectionPath,
'clip' + gd._fullLayout._uid + clipAxes,
gd
);
}
function activateSelection(gd, path) {
if(!couldHaveActiveSelection(gd)) return;
var element = path.node();
var id = +element.getAttribute('data-index');
if(id >= 0) {
// deactivate if already active
if(id === gd._fullLayout._activeSelectionIndex) {
deactivateSelection(gd);
return;
}
gd._fullLayout._activeSelectionIndex = id;
gd._fullLayout._deactivateSelection = deactivateSelection;
draw(gd);
}
}
function activateLastSelection(gd) {
if(!couldHaveActiveSelection(gd)) return;
var id = gd._fullLayout.selections.length - 1;
gd._fullLayout._activeSelectionIndex = id;
gd._fullLayout._deactivateSelection = deactivateSelection;
draw(gd);
}
function deactivateSelection(gd) {
if(!couldHaveActiveSelection(gd)) return;
var id = gd._fullLayout._activeSelectionIndex;
if(id >= 0) {
clearOutlineControllers(gd);
delete gd._fullLayout._activeSelectionIndex;
draw(gd);
}
}