@@ -17,6 +17,8 @@ var Axes = require('../../plots/cartesian/axes');
17
17
var Color = require ( '../color' ) ;
18
18
var Drawing = require ( '../drawing' ) ;
19
19
20
+ var dragElement = require ( '../dragelement' ) ;
21
+ var setCursor = require ( '../../lib/setcursor' ) ;
20
22
21
23
var shapes = module . exports = { } ;
22
24
@@ -299,15 +301,7 @@ function updateShape(gd, index, opt, value) {
299
301
var options = handleShapeDefaults ( optionsIn , gd . _fullLayout ) ;
300
302
gd . _fullLayout . shapes [ index ] = options ;
301
303
302
- var attrs = {
303
- 'data-index' : index ,
304
- 'fill-rule' : 'evenodd' ,
305
- d : shapePath ( gd , options )
306
- } ,
307
- clipAxes ;
308
-
309
- var lineColor = options . line . width ? options . line . color : 'rgba(0,0,0,0)' ;
310
-
304
+ var clipAxes ;
311
305
if ( options . layer !== 'below' ) {
312
306
clipAxes = ( options . xref + options . yref ) . replace ( / p a p e r / g, '' ) ;
313
307
drawShape ( gd . _fullLayout . _shapeUpperLayer ) ;
@@ -332,6 +326,14 @@ function updateShape(gd, index, opt, value) {
332
326
}
333
327
334
328
function drawShape ( shapeLayer ) {
329
+ var attrs = {
330
+ 'data-index' : index ,
331
+ 'fill-rule' : 'evenodd' ,
332
+ d : shapePath ( gd , options )
333
+ } ,
334
+ lineColor = options . line . width ?
335
+ options . line . color : 'rgba(0,0,0,0)' ;
336
+
335
337
var path = shapeLayer . append ( 'path' )
336
338
. attr ( attrs )
337
339
. style ( 'opacity' , options . opacity )
@@ -343,6 +345,72 @@ function updateShape(gd, index, opt, value) {
343
345
path . call ( Drawing . setClipUrl ,
344
346
'clip' + gd . _fullLayout . _uid + clipAxes ) ;
345
347
}
348
+
349
+ if ( ! gd . _context . editable ) return ;
350
+
351
+ var update ;
352
+ var x0 , y0 , x1 , y1 , astrX0 , astrY0 , astrX1 , astrY1 ;
353
+ var pathIn , astrPath ;
354
+ var xa , ya , x2p , y2p , p2x , p2y ;
355
+ dragElement . init ( {
356
+ element : path . node ( ) ,
357
+ prepFn : function ( ) {
358
+ setCursor ( path , 'move' ) ;
359
+
360
+ xa = Axes . getFromId ( gd , options . xref ) ;
361
+ ya = Axes . getFromId ( gd , options . yref ) ;
362
+
363
+ x2p = getDataToPixel ( gd , xa ) ;
364
+ y2p = getDataToPixel ( gd , ya , true ) ;
365
+ p2x = getPixelToData ( gd , xa ) ;
366
+ p2y = getPixelToData ( gd , ya , true ) ;
367
+
368
+ var astr = 'shapes[' + index + ']' ;
369
+ if ( options . type === 'path' ) {
370
+ pathIn = options . path ;
371
+ astrPath = astr + '.path' ;
372
+ }
373
+ else {
374
+ x0 = x2p ( options . x0 ) ;
375
+ y0 = y2p ( options . y0 ) ;
376
+ x1 = x2p ( options . x1 ) ;
377
+ y1 = y2p ( options . y1 ) ;
378
+
379
+ astrX0 = astr + '.x0' ;
380
+ astrY0 = astr + '.y0' ;
381
+ astrX1 = astr + '.x1' ;
382
+ astrY1 = astr + '.y1' ;
383
+ }
384
+
385
+ update = { } ;
386
+ } ,
387
+ moveFn : function ( dx , dy ) {
388
+ if ( options . type === 'path' ) {
389
+ var moveX = function moveX ( x ) { return p2x ( x2p ( x ) + dx ) ; } ;
390
+ if ( xa && xa . type === 'date' ) moveX = encodeDate ( moveX ) ;
391
+
392
+ var moveY = function moveY ( y ) { return p2y ( y2p ( y ) + dy ) ; } ;
393
+ if ( ya && ya . type === 'date' ) moveY = encodeDate ( moveY ) ;
394
+
395
+ options . path = movePath ( pathIn , moveX , moveY ) ;
396
+ update [ astrPath ] = options . path ;
397
+ }
398
+ else {
399
+ update [ astrX0 ] = options . x0 = p2x ( x0 + dx ) ;
400
+ update [ astrY0 ] = options . y0 = p2y ( y0 + dy ) ;
401
+ update [ astrX1 ] = options . x1 = p2x ( x1 + dx ) ;
402
+ update [ astrY1 ] = options . y1 = p2y ( y1 + dy ) ;
403
+ }
404
+
405
+ path . attr ( 'd' , shapePath ( gd , options ) ) ;
406
+ } ,
407
+ doneFn : function ( dragged ) {
408
+ setCursor ( path ) ;
409
+ if ( dragged ) {
410
+ Plotly . relayout ( gd , update ) ;
411
+ }
412
+ }
413
+ } ) ;
346
414
}
347
415
}
348
416
@@ -375,6 +443,51 @@ function decodeDate(convertToPx) {
375
443
return function ( v ) { return convertToPx ( v . replace ( '_' , ' ' ) ) ; } ;
376
444
}
377
445
446
+ function encodeDate ( convertToDate ) {
447
+ return function ( v ) { return convertToDate ( v ) . replace ( ' ' , '_' ) ; } ;
448
+ }
449
+
450
+ function getDataToPixel ( gd , axis , isVertical ) {
451
+ var gs = gd . _fullLayout . _size ,
452
+ dataToPixel ;
453
+
454
+ if ( axis ) {
455
+ var d2l = dataToLinear ( axis ) ;
456
+
457
+ dataToPixel = function ( v ) {
458
+ return axis . _offset + axis . l2p ( d2l ( v , true ) ) ;
459
+ } ;
460
+
461
+ if ( axis . type === 'date' ) dataToPixel = decodeDate ( dataToPixel ) ;
462
+ }
463
+ else if ( isVertical ) {
464
+ dataToPixel = function ( v ) { return gs . t + gs . h * ( 1 - v ) ; } ;
465
+ }
466
+ else {
467
+ dataToPixel = function ( v ) { return gs . l + gs . w * v ; } ;
468
+ }
469
+
470
+ return dataToPixel ;
471
+ }
472
+
473
+ function getPixelToData ( gd , axis , isVertical ) {
474
+ var gs = gd . _fullLayout . _size ,
475
+ pixelToData ;
476
+
477
+ if ( axis ) {
478
+ var l2d = linearToData ( axis ) ;
479
+ pixelToData = function ( p ) { return l2d ( axis . p2l ( p - axis . _offset ) ) ; } ;
480
+ }
481
+ else if ( isVertical ) {
482
+ pixelToData = function ( p ) { return 1 - ( p - gs . t ) / gs . h ; } ;
483
+ }
484
+ else {
485
+ pixelToData = function ( p ) { return ( p - gs . l ) / gs . w ; } ;
486
+ }
487
+
488
+ return pixelToData ;
489
+ }
490
+
378
491
function shapePath ( gd , options ) {
379
492
var type = options . type ,
380
493
xa = Axes . getFromId ( gd , options . xref ) ,
@@ -501,6 +614,29 @@ shapes.convertPath = function(pathIn, x2p, y2p) {
501
614
} ) ;
502
615
} ;
503
616
617
+ function movePath ( pathIn , moveX , moveY ) {
618
+ return pathIn . replace ( segmentRE , function ( segment ) {
619
+ var paramNumber = 0 ,
620
+ segmentType = segment . charAt ( 0 ) ,
621
+ xParams = paramIsX [ segmentType ] ,
622
+ yParams = paramIsY [ segmentType ] ,
623
+ nParams = numParams [ segmentType ] ;
624
+
625
+ var paramString = segment . substr ( 1 ) . replace ( paramRE , function ( param ) {
626
+ if ( paramNumber >= nParams ) return param ;
627
+
628
+ if ( xParams [ paramNumber ] ) param = moveX ( param ) ;
629
+ else if ( yParams [ paramNumber ] ) param = moveY ( param ) ;
630
+
631
+ paramNumber ++ ;
632
+
633
+ return param ;
634
+ } ) ;
635
+
636
+ return segmentType + paramString ;
637
+ } ) ;
638
+ }
639
+
504
640
shapes . calcAutorange = function ( gd ) {
505
641
var fullLayout = gd . _fullLayout ,
506
642
shapeList = fullLayout . shapes ,
0 commit comments