-
Notifications
You must be signed in to change notification settings - Fork 79
/
Brush.js
147 lines (135 loc) · 4.55 KB
/
Brush.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/**
* Define a brush to select elements in a map.
* @param {D3 Selection} selection - A d3 selection to place the brush in.
* @param {Boolean} is_enabled - Whether to turn the brush on.
* @param {escher.Map} map - The map where the brush will be active.
* @param {String} insert_after - A d3 selector string to choose the svg element
* that the brush will be inserted after. Often a
* canvas element (e.g. '.canvas-group').
*/
var utils = require('./utils')
var d3_brush = require('d3-brush').brush
var d3_brushSelection = require('d3-brush').brushSelection
var d3_scaleIdentity = require('d3-scale').scaleIdentity
var d3_selection = require('d3-selection')
var d3_select = require('d3-selection').select
var Brush = utils.make_class()
Brush.prototype = {
init: init,
toggle: toggle,
setup_selection_brush: setup_selection_brush,
}
module.exports = Brush
/**
* Initialize the brush.
* @param {D3 Selection} selection - The selection for the brush.
* @param {Boolean} is_enabled - Whether to enable right away.
* @param {escher.Map} map - The Escher Map object.
* @param {Node} insert_after - A node within selection to insert after.
*/
function init (selection, is_enabled, map, insert_after) {
this.brush_sel = selection.append('g').attr('id', 'brush-container')
var node = this.brush_sel.node()
var insert_before_node = selection.select(insert_after).node().nextSibling
if (node !== insert_before_node) {
node.parentNode.insertBefore(node, insert_before_node)
}
this.enabled = is_enabled
this.map = map
}
/**
* Returns a boolean for the on/off status of the brush
* @return {Boolean}
*/
function brush_is_enabled () {
return this.map.sel.select('.brush').empty()
}
/**
* Turn the brush on or off
* @param {Boolean} on_off
*/
function toggle (onOff) {
if (onOff === undefined) {
onOff = !this.enabled
}
if (onOff) {
this.setup_selection_brush()
} else {
this.brush_sel.selectAll('*').remove()
}
}
/**
* Turn off the mouse crosshair
*/
function turn_off_crosshair (sel) {
sel.selectAll('rect').attr('cursor', null)
}
function setup_selection_brush () {
var map = this.map
var selection = this.brush_sel
var selectable_selection = map.sel.selectAll('#nodes,#text-labels')
var size_and_location = map.canvas.size_and_location()
var width = size_and_location.width
var height = size_and_location.height
var x = size_and_location.x
var y = size_and_location.y
// Clear existing brush
selection.selectAll('*').remove()
// Set a flag so we know that the brush is being cleared at the end of a
// successful brush
var clearing_flag = false
var brush = d3_brush()
.extent([ [ x, y ], [ x + width, y + height ] ])
.on('start', function () {
turn_off_crosshair(selection)
// unhide secondary metabolites if they are hidden
if (map.settings.get('hide_secondary_metabolites')) {
map.settings.set('hide_secondary_metabolites', false)
map.draw_everything()
map.set_status('Showing secondary metabolites. You can hide them ' +
'again in Settings.', 2000)
}
})
.on('brush', function () {
var shift_key_on = d3_selection.event.sourceEvent.shiftKey
var rect = d3_brushSelection(this)
// Check for no selection (e.g. after clearing brush)
if (rect !== null) {
// When shift is pressed, ignore the currently selected nodes.
// Otherwise, brush all nodes.
var selection = (
shift_key_on ?
selectable_selection.selectAll('.node:not(.selected),.text-label:not(.selected)') :
selectable_selection.selectAll('.node,.text-label')
)
selection.classed('selected', (d) => {
var sx = d.x
var sy = d.y
return (rect[0][0] <= sx && sx < rect[1][0] &&
rect[0][1] <= sy && sy < rect[1][1])
})
}
})
.on('end', function () {
turn_off_crosshair(selection)
// Clear brush
var rect = d3_brushSelection(this)
if (rect === null) {
if (clearing_flag) {
clearing_flag = false
} else {
// Empty selection, deselect all
map.select_none()
}
} else {
// Not empty, then clear the box
clearing_flag = true
selection.call(brush.move, null)
}
})
selection
// Initialize brush
.call(brush)
// Turn off the pan grab icons
turn_off_crosshair(selection)
}