Steps with LogarithmicAxis

Oystein Bjorke 10 years ago 0
This discussion was imported from CodePlex

WeirdNoise wrote at 2013-07-22 14:08:

Hello,
I'm trying to put together a little tool for displaying audio measurements. Such plots do usually have two logarithmic axes, though the amplitude axis is scaled linear by using dB.

The frequency axis, however, should be scaled logarithmic and has to cover approximately the human range of audibility from 20 Hz to 20 kHz. If I set up a plot using a logarithmic oxyPlot, I get something like this:
Image

I would like to have more displayed Steps (frequency labels) on the frequency axis, like this:
Image

Obviously, I can't just adjust the Axis' Base, as I would get rather odd numbers. Is there any way to achieve such an axis labeling with oxyPlot?

Thanks in advance

everytimer wrote at 2013-07-23 12:28:

I don't think it's possible right now. You should modify the GetTickValues method inside the LogarithmicAxis.cs file (particularly majorTickValues list). Please post the solution if you find out how to do it. Good luck.

willmoore88 wrote at 2013-08-07 09:15:

If you're not bothered about the minor ticks being the same size as major ticks then this is a bit of a hacky way to do this. Just change the minor ticks to major ticks.

Inside LogarithmicAxis.cs, GetTickValues()....
                    if (d2 >= this.ActualMinimum && d2 <= this.ActualMaximum)
                    {
                        //minorTickValues.Add(d2);
                        majorTickValues.Add(d2);
                    }

WeirdNoise wrote at 2013-08-11 17:20:

Hello there,
it's been a while... Finally got to work on this.
everytimer: Thanks for pointing me in the right direction!

Main differences between my implementation and original (assuming base=10, but works with any other base):
-You can zoom out and display many decades without messing up the labels
-A Decade is subdivided if there's enough space for the labels
-The IntervalLength property is actually used by the algorithm

Example:
Image

For several reasons, I did my fix in the form of a simple override.
In case anyone's interested and not bothered by a little VB:
Imports System.Math

Public Class MyLogAxis
    Inherits Axes.LogarithmicAxis

    Public Overrides Sub GetTickValues(ByRef majorLabelValues As IList(Of Double), ByRef majorTickValues As IList(Of Double), ByRef minorTickValues As IList(Of Double))

        majorTickValues = New List(Of Double)
        minorTickValues = New List(Of Double)

        Dim screensize As Integer
        If Me.IsHorizontal Then
            screensize = Me.ScreenMax.X - Me.ScreenMin.X
        Else
            screensize = Me.ScreenMax.Y - Me.ScreenMin.Y
        End If

        Dim totalBW As Double = Log(Me.ActualMaximum, Me.Base) - Log(Me.ActualMinimum, Me.Base)
        Dim BWratio As Double = width / totalBW

        If totalBW < 1 Then
            MyBase.GetTickValues(majorLabelValues, majorTickValues, minorTickValues)

        ElseIf BWratio < Me.IntervalLength Then
            Dim steps As Integer = Ceiling(Me.IntervalLength / BWratio)
            Dim c As Double = Ceiling(Log(Me.ActualMinimum, Me.Base))

            While c < Floor(Log(Me.ActualMaximum, Me.Base))
                minorTickValues.Add(Round(Pow(Me.Base, c), 10))
                If c Mod steps = 0 Then
                    majorTickValues.Add(Round(Pow(Me.Base, c), 10))
                End If
                c += 1
            End While

        Else
            Dim c As Double = Floor(Log(Me.ActualMinimum, Me.Base))
            Dim v As Double
            Dim c2 As Double
            Dim lower As Double
            While c < Ceiling(Log(Me.ActualMaximum, Me.Base)) + 1

                v = Pow(Me.Base, c)
                If v > Me.ActualMinimum And v < Me.ActualMaximum Then
                    majorTickValues.Add(Round(v, 10))
                End If

                Dim a As Integer = 1
                lower = c

                While a < Me.Base
                    c2 = c + Log(a, Me.Base)
                    v = Pow(Me.Base, c2)
                    If v + 0.00001 >= Me.ActualMinimum And v <= Me.ActualMaximum + 0.00001 Then
                        minorTickValues.Add(Round(Pow(Me.Base, c2), 10))
                        If BWratio * Min(c2 - lower, c + 1 - c2) > Me.IntervalLength Then
                            majorTickValues.Add(Round(v, 10))
                            lower = c2
                        End If
                    End If

                    a += 1
                End While
                c += 1
            End While
        End If
        majorLabelValues = majorTickValues
    End Sub
End Class

everytimer wrote at 2013-08-11 19:38:

Great stuff, and good looking graph =)