@@ -6,6 +6,15 @@ var Lib = require('../../lib');
66var Drawing = require ( '../../components/drawing' ) ;
77var colorscaleStroke = require ( './style' ) . colorscaleStroke ;
88
9+ // Length (px) of each arrowhead arm per unit of marker.line.width at
10+ // arrowsize = 1. With the head's half-angle of PI/12, this yields an opening
11+ // roughly 3x the line width, matching the `marker.arrowsize` spec
12+ var HEAD_LEN_PER_WIDTH = 5.8 ;
13+
14+ // Max arrowhead length as a fraction of the body length, so the head stays
15+ // slightly shorter than the body for very short arrows
16+ var MAX_HEAD_FRAC = 0.7 ;
17+
918module . exports = function plot ( gd , plotinfo , cdscatter , scatterLayer , transitionOpts , makeOnCompleteCallback ) {
1019 var join , onComplete ;
1120
@@ -90,7 +99,7 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
9099
91100 // Update line segments
92101 lineSegments . each ( function ( cdi ) {
93- var path = d3 . select ( this ) ;
102+ const path = d3 . select ( this ) ;
94103
95104 // Skip invalid points
96105 if ( cdi . x === undefined || cdi . y === undefined ) {
@@ -100,25 +109,22 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
100109
101110 // Compute arrow in data space
102111 // Derive pixel-per-data scaling from axes at this point
103- var pxPerX = Math . abs ( xa . c2p ( cdi . x + 1 ) - xa . c2p ( cdi . x ) ) ;
104- var pxPerY = Math . abs ( ya . c2p ( cdi . y + 1 ) - ya . c2p ( cdi . y ) ) ;
105- var scaleRatio = ( pxPerX && pxPerY ) ? ( pxPerY / pxPerX ) : 1 ;
106- var baseHeadScale = 0.2 ;
107- var markerArrowsize = ( trace . marker || { } ) . arrowsize ;
108- var arrowScale = ( markerArrowsize !== undefined )
109- ? ( baseHeadScale * markerArrowsize )
110- : baseHeadScale ;
111- // Fixed arrowhead wedge angle (radians).
112- // Arrow direction is fully determined by u,v (see barbAng below);
113- // this constant only controls the opening of the head.
114- var headAngle = Math . PI / 12 ;
115-
116- var u = ( trace . u && trace . u [ cdi . i ] ) || 0 ;
117- var v = ( trace . v && trace . v [ cdi . i ] ) || 0 ;
118-
119- var norm = Math . sqrt ( u * u + v * v ) ;
120- var unitx = norm ? ( u / norm ) : 0 ;
121- var unity = norm ? ( v / norm ) : 0 ;
112+ const pxPerX = Math . abs ( xa . c2p ( cdi . x + 1 ) - xa . c2p ( cdi . x ) ) ;
113+ const pxPerY = Math . abs ( ya . c2p ( cdi . y + 1 ) - ya . c2p ( cdi . y ) ) ;
114+ const scaleRatio = ( pxPerX && pxPerY ) ? ( pxPerY / pxPerX ) : 1 ;
115+ const markerArrowsize = ( trace . marker || { } ) . arrowsize ;
116+ const arrowSizeVal = ( markerArrowsize !== undefined ) ? markerArrowsize : 1 ;
117+ // Fixed arrowhead wedge angle (radians). Arrow direction is fully
118+ // determined by u,v (see angPx below); this constant only controls the
119+ // relative angle of the point of the arrowhead
120+ const headAngle = Math . PI / 12 ;
121+
122+ const u = ( trace . u && trace . u [ cdi . i ] ) || 0 ;
123+ const v = ( trace . v && trace . v [ cdi . i ] ) || 0 ;
124+
125+ const norm = Math . sqrt ( u * u + v * v ) ;
126+ const unitx = norm ? ( u / norm ) : 0 ;
127+ const unity = norm ? ( v / norm ) : 0 ;
122128 var baseLen ;
123129 if ( sizemode === 'scaled' ) {
124130 var n = maxNorm ? ( norm / maxNorm ) : 0 ;
@@ -127,16 +133,10 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
127133 baseLen = norm * sizeref ;
128134 }
129135
130- var dxBase = unitx * baseLen ;
131- var dyBase = unity * baseLen ;
132- var dx = dxBase * scaleRatio ;
133- var dy = dyBase ;
134- var barbLen = Math . sqrt ( ( dx * dx ) / scaleRatio + dy * dy ) ;
135- var arrowLen = barbLen * arrowScale ;
136- var barbAng = Math . atan2 ( dy , dx / scaleRatio ) ;
137-
138- var ang1 = barbAng + headAngle ;
139- var ang2 = barbAng - headAngle ;
136+ const dxBase = unitx * baseLen ;
137+ const dyBase = unity * baseLen ;
138+ const dx = dxBase * scaleRatio ;
139+ const dy = dyBase ;
140140
141141 var x0 , y0 , x1 , y1 ;
142142 if ( anchor === 'tip' ) {
@@ -156,22 +156,28 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
156156 y1 = y0 + dy ;
157157 }
158158
159- var xh1 = x1 - arrowLen * Math . cos ( ang1 ) * scaleRatio ;
160- var yh1 = y1 - arrowLen * Math . sin ( ang1 ) ;
161- var xh2 = x1 - arrowLen * Math . cos ( ang2 ) * scaleRatio ;
162- var yh2 = y1 - arrowLen * Math . sin ( ang2 ) ;
163-
164- // Convert to pixels
165- var p0x = xa . c2p ( x0 ) ;
166- var p0y = ya . c2p ( y0 ) ;
167- var p1x = xa . c2p ( x1 ) ;
168- var p1y = ya . c2p ( y1 ) ;
169- var ph1x = xa . c2p ( xh1 ) ;
170- var ph1y = ya . c2p ( yh1 ) ;
171- var ph2x = xa . c2p ( xh2 ) ;
172- var ph2y = ya . c2p ( yh2 ) ;
173-
174- var pathData = 'M' + p0x + ',' + p0y + 'L' + p1x + ',' + p1y + 'L' + ph1x + ',' + ph1y + 'L' + p1x + ',' + p1y + 'L' + ph2x + ',' + ph2y ;
159+ // Arrow body endpoints (px)
160+ const p0x = xa . c2p ( x0 ) ;
161+ const p0y = ya . c2p ( y0 ) ;
162+ const p1x = xa . c2p ( x1 ) ;
163+ const p1y = ya . c2p ( y1 ) ;
164+
165+ // Arrowhead is sized in pixels (relative to the line width)
166+ // so it remains the same regardless of zoom, rather than scaling with the data space
167+ // Set max head size so the head stays slightly shorter than the arrow body
168+ // (e.g. for very short arrows when zoomed out). No head for zero-length arrows.
169+ const lineWidth = trace . marker . line . width ;
170+ const bodyLenPx = Math . sqrt ( ( p1x - p0x ) * ( p1x - p0x ) + ( p1y - p0y ) * ( p1y - p0y ) ) ;
171+ const maxHeadPx = MAX_HEAD_FRAC * bodyLenPx ;
172+ const headLenPx = Math . min ( HEAD_LEN_PER_WIDTH * arrowSizeVal * lineWidth , maxHeadPx ) ;
173+ const angPx = Math . atan2 ( p1y - p0y , p1x - p0x ) ;
174+
175+ const ph1x = p1x - headLenPx * Math . cos ( angPx - headAngle ) ;
176+ const ph1y = p1y - headLenPx * Math . sin ( angPx - headAngle ) ;
177+ const ph2x = p1x - headLenPx * Math . cos ( angPx + headAngle ) ;
178+ const ph2y = p1y - headLenPx * Math . sin ( angPx + headAngle ) ;
179+
180+ const pathData = 'M' + p0x + ',' + p0y + 'L' + p1x + ',' + p1y + 'L' + ph1x + ',' + ph1y + 'L' + p1x + ',' + p1y + 'L' + ph2x + ',' + ph2y ;
175181 path . attr ( 'd' , pathData ) ;
176182 } ) ;
177183
0 commit comments