@@ -295,63 +295,91 @@ const interaction_manager = {
295295
296296 // Execute pan operation and update axis limits
297297 execute_pan : ( ) => {
298- // Calculate visual movement for consistent dual y-axis behavior
298+ const pixelDx = interaction_manager . interaction . drag_end . x - interaction_manager . interaction . drag_start . x ;
299299 const pixelDy = interaction_manager . interaction . drag_end . y - interaction_manager . interaction . drag_start . y ;
300300
301- // Calculate data coordinate changes
301+ // Calculate data coordinate changes for each axis
302302 Object . keys ( vis . axes ) . forEach ( axis => {
303303 if ( vis . axes [ axis ] . scale && vis . axes [ axis ] . data_name ) {
304+ // Get current limits
305+ const currentMin = vis . axes [ axis ] . min || vis . axes [ axis ] . scale . domain ( ) [ 0 ] ;
306+ const currentMax = vis . axes [ axis ] . max || vis . axes [ axis ] . scale . domain ( ) [ 1 ] ;
307+
308+ // Validate current limits
309+ if ( ! isFinite ( currentMin ) || ! isFinite ( currentMax ) || isNaN ( currentMin ) || isNaN ( currentMax ) ) {
310+ console . warn ( `Invalid current limits for axis ${ axis } :` , currentMin , currentMax ) ;
311+ return ;
312+ }
313+
314+ const isLogScale = vis . axes [ axis ] . type === 'log' ;
315+ let workingMin , workingMax ;
316+
317+ // Step 1: Convert to working space (log space if logarithmic, regular space if linear)
318+ if ( isLogScale ) {
319+ if ( currentMin <= 0 || currentMax <= 0 ) {
320+ console . warn ( `Log axis ${ axis } has non-positive limits:` , currentMin , currentMax ) ;
321+ return ;
322+ }
323+ workingMin = Math . log10 ( currentMin ) ;
324+ workingMax = Math . log10 ( currentMax ) ;
325+ } else {
326+ workingMin = currentMin ;
327+ workingMax = currentMax ;
328+ }
329+
330+ // Step 2: Calculate pixel-to-data conversion using working space limits
304331 let deltaData ;
305332 if ( axis === 'x' ) {
306- deltaData = vis . axes [ axis ] . scale . invert ( interaction_manager . interaction . drag_start . x ) -
307- vis . axes [ axis ] . scale . invert ( interaction_manager . interaction . drag_end . x ) ;
333+ // For x-axis, use horizontal pixel movement
334+ const plotWidth = vis . axes [ axis ] . scale . range ( ) [ 1 ] - vis . axes [ axis ] . scale . range ( ) [ 0 ] ;
335+ const workingRange = workingMax - workingMin ;
336+ deltaData = - ( pixelDx / plotWidth ) * workingRange ; // Negative because right drag should move data left
308337 } else {
309- // For y-axes, calculate the visual percentage movement and apply to each axis's range
310- const currentMin = vis . axes [ axis ] . min || vis . axes [ axis ] . scale . domain ( ) [ 0 ] ;
311- const currentMax = vis . axes [ axis ] . max || vis . axes [ axis ] . scale . domain ( ) [ 1 ] ;
312- const currentRange = currentMax - currentMin ;
313-
314- // Calculate what percentage of the visual height we moved
338+ // For y-axes, use vertical pixel movement
315339 const plotHeight = vis . axes [ axis ] . scale . range ( ) [ 0 ] - vis . axes [ axis ] . scale . range ( ) [ 1 ] ; // range is [bottom, top]
316- const visualPercent = pixelDy / plotHeight ; // SVG coordinate to data coordinate mapping
317-
318- // Apply this percentage to the current data range
319- deltaData = visualPercent * currentRange ;
340+ const workingRange = workingMax - workingMin ;
341+ deltaData = ( pixelDy / plotHeight ) * workingRange ; // Positive because down drag should move data down
320342 }
321343
322- // Validate deltaData to prevent invalid values
344+ // Validate deltaData
323345 if ( ! isFinite ( deltaData ) || isNaN ( deltaData ) ) {
324346 console . warn ( `Invalid deltaData for axis ${ axis } :` , deltaData ) ;
325347 return ;
326348 }
327349
328- // Update axis limits
329- const currentMin = vis . axes [ axis ] . min || vis . axes [ axis ] . scale . domain ( ) [ 0 ] ;
330- const currentMax = vis . axes [ axis ] . max || vis . axes [ axis ] . scale . domain ( ) [ 1 ] ;
350+ // Step 3: Apply the change in working space
351+ const newWorkingMin = workingMin + deltaData ;
352+ const newWorkingMax = workingMax + deltaData ;
331353
332- // Validate current limits
333- if ( ! isFinite ( currentMin ) || ! isFinite ( currentMax ) || isNaN ( currentMin ) || isNaN ( currentMax ) ) {
334- console . warn ( `Invalid current limits for axis ${ axis } :` , currentMin , currentMax ) ;
354+ // Validate new working limits
355+ if ( ! isFinite ( newWorkingMin ) || ! isFinite ( newWorkingMax ) || isNaN ( newWorkingMin ) || isNaN ( newWorkingMax ) ) {
356+ console . warn ( `Invalid new working limits for axis ${ axis } :` , newWorkingMin , newWorkingMax ) ;
335357 return ;
336358 }
337359
338- const newMin = currentMin + deltaData ;
339- const newMax = currentMax + deltaData ;
340-
341- // Special validation for logarithmic axes
342- if ( vis . axes [ axis ] . type === 'log' ) {
343- if ( newMin <= 0 || newMax <= 0 ) {
344- console . warn ( `Log axis ${ axis } pan would create non-positive limits:` , newMin , newMax ) ;
345- return ; // Skip this axis - can't have non-positive values on log scale
360+ // Step 4: Convert back to real space
361+ let newMin , newMax ;
362+ if ( isLogScale ) {
363+ newMin = Math . pow ( 10 , newWorkingMin ) ;
364+ newMax = Math . pow ( 10 , newWorkingMax ) ;
365+
366+ // Additional validation for log scale
367+ if ( newMin <= 0 || newMax <= 0 || ! isFinite ( newMin ) || ! isFinite ( newMax ) ) {
368+ console . warn ( `Log axis ${ axis } pan would create invalid limits:` , newMin , newMax ) ;
369+ return ;
346370 }
371+ } else {
372+ newMin = newWorkingMin ;
373+ newMax = newWorkingMax ;
347374 }
348375
349- // Validate new limits
376+ // Final validation
350377 if ( ! isFinite ( newMin ) || ! isFinite ( newMax ) || isNaN ( newMin ) || isNaN ( newMax ) ) {
351- console . warn ( `Invalid new limits for axis ${ axis } :` , newMin , newMax ) ;
378+ console . warn ( `Invalid final limits for axis ${ axis } :` , newMin , newMax ) ;
352379 return ;
353380 }
354381
382+ // Step 5: Update axis limits
355383 vis . axes [ axis ] . min = newMin ;
356384 vis . axes [ axis ] . max = newMax ;
357385
0 commit comments