0

I am trying to modify fisheye this project so that I can use radius function to increase fisheye size. My aim is to see more cells bigger around mouse. Current implementation does not support radius function. If I use circular instead of scale, I can use radius function. But in this case, I dont know how to use circular.

Either way, help is appreciated :)

Thanks!

hakan
  • 2,564
  • 2
  • 15
  • 25

1 Answers1

3

The radius parameter on the circular fisheye puts a boundary to the magnification effects. In contrast, in the scale/Cartesian fisheye, the entire graph is modified. The focus cell is enlarged, and other cells are compressed according to how far away they are from the focus. There is no boundary, the compression continues smoothly (getting progressively more compressed) until the edge of the plot. See http://bost.ocks.org/mike/fisheye/#cartesian

If what you want is that cells near to the focus aren't compressed as much (so you can still compare adjacent cells effectively), then the parameter to change is the distortion parameter. Lower distortion will reduce the amount by which the focus cell is magnified, and therefore leave more room for adjacent cells. The default distortion parameter is 3, you're using higher values, which increases the magnification of the focus cell at the expense of all the others.

If changing the distortion doesn't satisfy you, try changing the scale type by using d3.fisheye.scale(d3.scale.sqrt); this will change the function determining how the image magnification changes as you move away from the focus point. (I couldn't get other scale types to work -- log gives an error, and with power scales there is no way to set the exponent.)

Edit

After additional playing around, I'm not satisfied with the results from changing the input scale type. I misunderstood how that would affect it: it doesn't change the scale function for the distortion, but for the raw data, so that changes are different for points above the focus compared to point below the focus. The scale type you give as a parameter to the fisheye scale should be the underlying scale type that makes sense for the data, and is distinct from the fisheye effects.

Instead, I've tried some custom code to add an exponent into the calculation. To understand how it works, you need to first break down the original function:

The original code for the fisheye scale is:

function fisheye(_) {
  var x = scale(_),
      left = x < a,
      range = d3.extent(scale.range()),
      min = range[0],
      max = range[1],
      m = left ? a - min : max - a;
  if (m == 0) m = max - min;
  return a + (left ? -1 : 1) * m * (d + 1) / (d + (m / Math.abs(x - a)));
}

The _ is the input value, scale is usually a linear scale for which domain and range have been set, a is the focus point in the output range, and d is the distortion parameter.

In other words: to determine the point at which a value is drawn on the distorted scale:

  • calculate the range position of the value based on the default/undistorted scale;
  • calculate it's distance from the focal point ({distance}, Math.abs(x-a));
  • calculate the distance between edge of the graph and the focal point ({total distance}, m);
  • the returned value is offset from the focal point by {total distance} multiplied by
    (d + 1) / (d + ({total distance} / {distance}) );
  • adjust as appropriate depending on whether the value is below or above the focal point.

For an input point that is half-way between the focal point and the edge of the graph on the undistorted scale, the inner fraction {total distance}/{distance} will equal 2. The outer fraction will therefore be (d+1)/(d+2). If d is 0 (no distortion), this will equal 1/2, and the output point will also be half-way between the focal point and the edge of the graph. As the distortion parameter, d, increases, that fraction also increases: at d=1, the output point would be 2/3 of the way from the focal point to the edge of the graph; at d=2, it would be 3/4 of the way to the edge of the graph; and so on.

In contrast, when the input value is very close to the focal point, {distance} is nearly 0, so the inner fraction approaches infinity and the outer fraction approaches 0, so the returned point will be very close to the focal point.

Finally, when the input value is very close to the edge of the graph, {distance} is nearly {total distance}, and both the inner and outer fractions will be nearly 1, so the returned point will also be very close to the edge of the graph.

Those last two identities we want to keep. We just want to change the relationship in between -- how the offset from focal point changes as the input point gets farther away from the focal point and closer to the edge of the graph. Changing the distortion parameter changes the amount of distortion in both near and far values equally. If you reduce the distortion parameter you also reduce the overall magnification, since all the data still has to fit in the same space.

The OP wanted to reduce the rate of change in magnification between cells near the focal point. Reducing the distortion parameter does this, but only by reducing the magnification overall. The ideal approach would be to change the relationship between distance from the focal point and degree of distortion.

My changed code for the same function is:

function fisheye(_) {
    var x = scale(_),
        left = x < a,
        range = d3.extent(scale.range()),
        min = range[0],
        max = range[1],
        m = left ? a - min : max - a,
        dp = Math.pow(d, p);
    if (m == 0) return left? min : max;
    return a + (left ? -1 : 1) *  m  * 
      Math.pow( 
        (dp + 1)
      / (dp + (m / Math.abs(x-a) ) )
      , p);
}

I've changed two things: I raise the fraction (d + 1)/(d + {total distance}/{distance}) to a power, and I also replace the original d value with it raised to the same exponent (dp). The first change is what changes the relationship, the second is just an adjustment so that a given distortion parameter will have approximately the same overall magnification effect regardless of the power parameter.

The fraction raised to the power will still be close to zero if the fraction is close to zero, and will still be close to one if the fraction is close to one, so the basic identities remain the same. However, when the power parameter is less than one, the rate of change will be shallower at the edges, and steeper in the middle. For a power parameter greater than 1, the rate of change will be quite steep at the edges and shallower near the focal point.

Example here: http://codepen.io/AmeliaBR/pen/zHqac
The horizontal fisheye scale has a square-root power function (p = 0.5), while the vertical has a square function (p = 2). Both have the same unadjusted distortion parameter (d = 6).

The effect of the square root function is that even the farthest columns still have some visible width, but the change in column width near the focal point is significant. The effect of the power of 2 function is that the rows far away from the focal point are compressed to nearly invisible height, but the rows above and below the focus are still of significant size. I think this latter version achieves what @piedpiper was hoping for.

I've of course also added a .power function to the fisheye scale in order to set the p parameter, and have set the default value for p to 1, which will give the same results as the original fisheye scale. I use the name power for the method to distinguish from the exponent method of power scales, which would be used if they underlying scale (before distortion) had a power relationship.

AmeliaBR
  • 26,169
  • 6
  • 76
  • 110
  • hi @AmeliaBR, my eyes cannot tell much difference, if any, when lowering the distortion on the cartesian fisheye, although your explanation makes sense...perhaps the logical width/height are relatively too large. I wonder if applying a mild distortion with a large radius using the circular fisheye (see second chart in Mike's work where the circular fisheye is applied to a uniform grid) might not produce results that are more in tune with piedpiper's objectives...although I must say I am not completely clear on what those are. – FernOfTheAndes Feb 10 '14 at 01:08
  • @FernOfTheAndes Were you changing the default distortion parameter in the fisheye script, or the actual parameter used when initializing the fisheye scales? – AmeliaBR Feb 10 '14 at 01:28
  • In the latter...actual parameter. – FernOfTheAndes Feb 10 '14 at 01:31
  • @FernOfTheAndes Not sure why not; with distortion 10 I see the focus cell taking up 1/4 of the page, with distortion 2 or 3 it is only slightly magnified. – AmeliaBR Feb 10 '14 at 01:39
  • @AmeliaBR...jeepers, I guess I was making changes back and forth too quickly...there is quite a lag in my system for CodePen changes to take effect. So, yes, now I see, changed distortion from 2 to 20 and back and now I see. – FernOfTheAndes Feb 10 '14 at 01:44
  • d3.fisheye.scale(d3.scale.sqrt) somehow not working. It is making topleft corner to be bigger than to right bottom corner. If focus is closer to topleft corner, scaling is bigger. What actually want is, without increasing distortion and magnifying cell then its normal size, to be able to view more cells close to their normal size. In other words, I want cells less scaled down when they are close to focus. – hakan Feb 10 '14 at 08:49
  • @piedpiper Yes, I noticed that later. Looking at the code, I think that option is only for when your data is plotted using a non-linear scale. I've been trying some custom code to implement the changes I was actually trying to create. I'll update the answer when I get them figured out. – AmeliaBR Feb 10 '14 at 15:41
  • @AmeliaBR I very much appriciate your effort! I am examining your solution and ideas. Making different experiments by changing coefficients. I will add comment later. – hakan Feb 11 '14 at 09:21