Synced Major Gridlines

May 28, 2014 at 4:31 PM
Hi and thanks for an awesome component.

I'm trying to accomplish the following:
  • One main y-axis on the left side.
  • Two secondary y-axes on the right side.
  • All three axes are at different scales and connected to different curves.
I would like to arrange the two right side axes to have the same number of major steps as the main left axis and I would like them to be zoom/panned in such a way that the major grid lines line up. The goal is to be able to follow the grid line from either side of the graph and have it match up against a major grid value on all axis.

Does anyone have any experience doing this and can give me a nudge in the right direction?
Jun 6, 2014 at 8:18 PM
I would like this too! I added https://oxyplot.codeplex.com/workitem/10217
I think it can be related to "synchronized axes" - https://oxyplot.codeplex.com/workitem/9945
but I have no idea on how to solve it!
Jun 21, 2014 at 2:03 PM
Edited Jun 21, 2014 at 2:05 PM
I kinda solved this problem, unfortuntely I did not have time to do it in a completely generic fashion.
You are welcome to use any of the follwing to make it generic and add it to Oxyplot though!

Background:
My graph contains 4 y-axis, 2 on each side.

Implementation:
I first had to decide how many major grid lines I was going to use for all the axes.
In my case, I constrained it to 20, because it works out well enough.
But one Idea I had that I never had time to implement was to let oxyplot create the axis natuarally by it self,
then find the one which naturally had the most grid marks and use that number of gridmarks as my gridmarkCount.

Then I had to make sure each axis started on a value and ended on a value, so first grid mark is always at the very bottom and last gridmark is always at the very top. This is simple enough if you set the minimum and maxium values to proper values.

Then I had to calculate a gridStep value that was evenly divisible by (maximum - minumum) / gridmarkCount.
This can be a bit tricky because the step values have to follow 1, 2, 5 , 10 etc, but its doable.

Finally, I needed a function to simply add x amount of gridSteps to the maximum of all other axis except the main, to make sure we ended up with gridmarkCount.

In my specific example it worked out like this:
  • MainAxis predetermined, not dynamic.
  • GridmarkCount = 20, not dynamic.
  • Minimum and Maximum always are 0 and 100 on my main axis, not dynamic.
  • Thus gridmarkStep becomes 5 on my main axis.
  • Calculate minimum and maximum for all other axis.
    If other axis turned out to have less than 20 gridmarks, I simply added x gridSteps to the maximum, thus changing the maximum until that axis gridmarkcount became 20.
  • Ensure each axis has an appropriate minimum and maximum to ensure first value is the absolute bottom and highest value is at the absolute top.
Hope this helps somehow!
Jul 2, 2014 at 7:40 PM
Edited Jul 2, 2014 at 7:42 PM
I've been syncing the axes using Zoom, although imo it's messy and I'm not really happy with it. As far as major grid lines I just leave the primary vertical and horizontal axes to handle them >_> again not a good overall solution. I mentioned in another post that it'd also be nice to have AxisChangeTypes include the type of zoom (by scale, factor or values) and the actual values sent, to make it easier to transform them into screen points and use them with the other axes.

Here's how I'm handling the axes, pretty sure I based it off an example:
(Quick note, this is done for multiple vertical and one horizontal axes)
private void Axis_OnAxisChanged(object sender, AxisChangedEventArgs args)
{
    if (_syncChange || ((Axis) sender).IsHorizontal())
        return;
    
    _syncChange = true;
    switch (args.ChangeType)
    {
        case AxisChangeTypes.Zoom:
        case AxisChangeTypes.Pan:
            _chartIsZoomed = true;
            foreach (var a in uiChartPlot.Model.Axes)
            {
                if (a.Key == ((Axis) sender).Key || a.IsHorizontal())
                    continue;

                double min =
                    a.InverseTransform(
                        ((Axis) sender).Transform(((Axis) sender).ActualMinimum));
                double max =
                    a.InverseTransform(
                        ((Axis) sender).Transform(((Axis) sender).ActualMaximum));

                a.Zoom(min, max);
            }
            break;

//      case AxisChangeTypes.Zoom:
//          break;

        case AxisChangeTypes.Reset:
            _chartIsZoomed = false;
            break;
    }

    uiChartPlot.InvalidatePlot(false);
    _syncChange = false;
}