@@ -118,26 +118,34 @@ function drawOne(gd, index) {
118
118
null
119
119
) ;
120
120
121
- if ( gd . _context . edits . shapePosition ) setupDragElement ( gd , path , options , index ) ;
121
+ if ( gd . _context . edits . shapePosition ) setupDragElement ( gd , path , options , index , shapeLayer ) ;
122
122
}
123
123
}
124
124
125
- function setupDragElement ( gd , shapePath , shapeOptions , index ) {
125
+ function setupDragElement ( gd , shapePath , shapeOptions , index , shapeLayer ) {
126
126
var MINWIDTH = 10 ,
127
127
MINHEIGHT = 10 ;
128
128
129
129
var xPixelSized = shapeOptions . xsizemode === 'pixel' ,
130
- yPixelSized = shapeOptions . ysizemode === 'pixel' ;
130
+ yPixelSized = shapeOptions . ysizemode === 'pixel' ,
131
+ isLine = shapeOptions . type === 'line' ;
131
132
132
133
var update ;
133
134
var x0 , y0 , x1 , y1 , xAnchor , yAnchor , astrX0 , astrY0 , astrX1 , astrY1 , astrXAnchor , astrYAnchor ;
134
135
var n0 , s0 , w0 , e0 , astrN , astrS , astrW , astrE , optN , optS , optW , optE ;
135
136
var pathIn , astrPath ;
136
137
137
- var xa , ya , x2p , y2p , p2x , p2y ;
138
+ // setup conversion functions
139
+ var xa = Axes . getFromId ( gd , shapeOptions . xref ) ,
140
+ ya = Axes . getFromId ( gd , shapeOptions . yref ) ,
141
+ x2p = helpers . getDataToPixel ( gd , xa ) ,
142
+ y2p = helpers . getDataToPixel ( gd , ya , true ) ,
143
+ p2x = helpers . getPixelToData ( gd , xa ) ,
144
+ p2y = helpers . getPixelToData ( gd , ya , true ) ;
138
145
146
+ var sensoryElement = obtainSensoryElement ( ) ;
139
147
var dragOptions = {
140
- element : shapePath . node ( ) ,
148
+ element : sensoryElement . node ( ) ,
141
149
gd : gd ,
142
150
prepFn : startDrag ,
143
151
doneFn : endDrag
@@ -146,39 +154,90 @@ function setupDragElement(gd, shapePath, shapeOptions, index) {
146
154
147
155
dragElement . init ( dragOptions ) ;
148
156
149
- shapePath . node ( ) . onmousemove = updateDragMode ;
157
+ sensoryElement . node ( ) . onmousemove = updateDragMode ;
150
158
151
- function updateDragMode ( evt ) {
152
- // element might not be on screen at time of setup,
153
- // so obtain bounding box here
154
- var dragBBox = dragOptions . element . getBoundingClientRect ( ) ;
155
-
156
- // choose 'move' or 'resize'
157
- // based on initial position of cursor within the drag element
158
- var w = dragBBox . right - dragBBox . left ,
159
- h = dragBBox . bottom - dragBBox . top ,
160
- x = evt . clientX - dragBBox . left ,
161
- y = evt . clientY - dragBBox . top ,
162
- cursor = ( w > MINWIDTH && h > MINHEIGHT && ! evt . shiftKey ) ?
163
- dragElement . getCursor ( x / w , 1 - y / h ) :
164
- 'move' ;
165
-
166
- setCursor ( shapePath , cursor ) ;
167
-
168
- // possible values 'move', 'sw', 'w', 'se', 'e', 'ne', 'n', 'nw' and 'w'
169
- dragMode = cursor . split ( '-' ) [ 0 ] ;
159
+ function obtainSensoryElement ( ) {
160
+ return isLine ? createLineDragHandles ( ) : shapePath ;
170
161
}
171
162
172
- function startDrag ( evt ) {
173
- // setup conversion functions
174
- xa = Axes . getFromId ( gd , shapeOptions . xref ) ;
175
- ya = Axes . getFromId ( gd , shapeOptions . yref ) ;
163
+ function createLineDragHandles ( ) {
164
+ var minSensoryWidth = 10 ,
165
+ sensoryWidth = Math . max ( shapeOptions . line . width , minSensoryWidth ) ;
166
+
167
+ // Helper shapes group
168
+ // Note that by setting the `data-index` attr, it is ensured that
169
+ // the helper group is purged in this modules `draw` function
170
+ var g = shapeLayer . append ( 'g' )
171
+ . attr ( 'data-index' , index ) ;
172
+
173
+ // Helper path for moving
174
+ g . append ( 'path' )
175
+ . attr ( 'd' , shapePath . attr ( 'd' ) )
176
+ . style ( {
177
+ 'cursor' : 'move' ,
178
+ 'stroke-width' : sensoryWidth ,
179
+ 'stroke-opacity' : '0' // ensure not visible
180
+ } ) ;
181
+
182
+ // Helper circles for resizing
183
+ var circleStyle = {
184
+ 'cursor' : 'default' ,
185
+ 'fill-opacity' : '0' // ensure not visible
186
+ } ;
187
+ var circleRadius = sensoryWidth / 2 > minSensoryWidth ? sensoryWidth / 2 : minSensoryWidth ;
188
+
189
+ g . append ( 'circle' )
190
+ . attr ( {
191
+ 'data-line-point' : 'start-point' ,
192
+ 'cx' : xPixelSized ? x2p ( shapeOptions . xanchor ) + shapeOptions . x0 : x2p ( shapeOptions . x0 ) ,
193
+ 'cy' : yPixelSized ? y2p ( shapeOptions . yanchor ) - shapeOptions . y0 : y2p ( shapeOptions . y0 ) ,
194
+ 'r' : circleRadius
195
+ } )
196
+ . style ( circleStyle ) ;
197
+
198
+ g . append ( 'circle' )
199
+ . attr ( {
200
+ 'data-line-point' : 'end-point' ,
201
+ 'cx' : xPixelSized ? x2p ( shapeOptions . xanchor ) + shapeOptions . x1 : x2p ( shapeOptions . x1 ) ,
202
+ 'cy' : yPixelSized ? y2p ( shapeOptions . yanchor ) - shapeOptions . y1 : y2p ( shapeOptions . y1 ) ,
203
+ 'r' : circleRadius
204
+ } )
205
+ . style ( circleStyle ) ;
206
+
207
+ return g ;
208
+ }
176
209
177
- x2p = helpers . getDataToPixel ( gd , xa ) ;
178
- y2p = helpers . getDataToPixel ( gd , ya , true ) ;
179
- p2x = helpers . getPixelToData ( gd , xa ) ;
180
- p2y = helpers . getPixelToData ( gd , ya , true ) ;
210
+ function updateDragMode ( evt ) {
211
+ if ( isLine ) {
212
+ if ( evt . target . tagName === 'path' ) {
213
+ dragMode = 'move' ;
214
+ } else {
215
+ dragMode = evt . target . attributes [ 'data-line-point' ] . value === 'start-point' ?
216
+ 'resize-over-start-point' : 'resize-over-end-point' ;
217
+ }
218
+ } else {
219
+ // element might not be on screen at time of setup,
220
+ // so obtain bounding box here
221
+ var dragBBox = dragOptions . element . getBoundingClientRect ( ) ;
222
+
223
+ // choose 'move' or 'resize'
224
+ // based on initial position of cursor within the drag element
225
+ var w = dragBBox . right - dragBBox . left ,
226
+ h = dragBBox . bottom - dragBBox . top ,
227
+ x = evt . clientX - dragBBox . left ,
228
+ y = evt . clientY - dragBBox . top ,
229
+ cursor = ( w > MINWIDTH && h > MINHEIGHT && ! evt . shiftKey ) ?
230
+ dragElement . getCursor ( x / w , 1 - y / h ) :
231
+ 'move' ;
232
+
233
+ setCursor ( shapePath , cursor ) ;
234
+
235
+ // possible values 'move', 'sw', 'w', 'se', 'e', 'ne', 'n', 'nw' and 'w'
236
+ dragMode = cursor . split ( '-' ) [ 0 ] ;
237
+ }
238
+ }
181
239
240
+ function startDrag ( evt ) {
182
241
// setup update strings and initial values
183
242
var astr = 'shapes[' + index + ']' ;
184
243
@@ -305,6 +364,19 @@ function setupDragElement(gd, shapePath, shapeOptions, index) {
305
364
shapeOptions . path = movePath ( pathIn , moveX , moveY ) ;
306
365
update [ astrPath ] = shapeOptions . path ;
307
366
}
367
+ else if ( isLine ) {
368
+ if ( dragMode === 'resize-over-start-point' ) {
369
+ var newX0 = x0 + dx ;
370
+ var newY0 = yPixelSized ? y0 - dy : y0 + dy ;
371
+ update [ astrX0 ] = shapeOptions . x0 = xPixelSized ? newX0 : p2x ( newX0 ) ;
372
+ update [ astrY0 ] = shapeOptions . y0 = yPixelSized ? newY0 : p2y ( newY0 ) ;
373
+ } else if ( dragMode === 'resize-over-end-point' ) {
374
+ var newX1 = x1 + dx ;
375
+ var newY1 = yPixelSized ? y1 - dy : y1 + dy ;
376
+ update [ astrX1 ] = shapeOptions . x1 = xPixelSized ? newX1 : p2x ( newX1 ) ;
377
+ update [ astrY1 ] = shapeOptions . y1 = yPixelSized ? newY1 : p2y ( newY1 ) ;
378
+ }
379
+ }
308
380
else {
309
381
var newN = ( ~ dragMode . indexOf ( 'n' ) ) ? n0 + dy : n0 ,
310
382
newS = ( ~ dragMode . indexOf ( 's' ) ) ? s0 + dy : s0 ,
0 commit comments