Solved

Posted on 2011-04-25

Hello, this is kind of an oddball question. I'm rolling my own plot widget in .NET (I know, I know, but the UI needs for this particular plot are very application-specific) and I can plot the data just fine (this was actually surprisingly easy) but the only part I'm having trouble with (also surprising) is the calculation of the ticks to plot on the axis. All plot widgets calculate user friendly tick sizes based on two inputs

* data range (e.g., 0-10 or 0.0001 to 0.003 or 1e23 to 1e25)

* width of the plot (because of the purpose of the ticks are visual aids, you want a comfortable number of ticks in a given span of pixels)

And it will come up with good tick values (5,10,15; or 0.2, 0.4, 0.6, etc) at given locations

I'm having brain block and can't come up with the right equations to come up with user friendly tick values. And this is just esoteric enough so I can't seem to find good equations on google. This can't be too hard since every widget does it.

Does anyone have good equations, or have come across good equations in their travels?

Thanks for any help.

10 Comments

Concentrate on the x-axis.

Calculate your required range: Xmax-Xmin

and look at the mantissa in scientific notation.

If it's 1.00 to 1.59 you might want 10 to 17 divisions spacing 0.1

If it's 1.60 to 2.99 you might want 8 to 17 divisions spacing 0.2

If it's 3.00 to 5.99 you might want 6 to 14 divisions spacing 0.5

If it's 6.00 to 9.99 you might want 6 to 12 divisions spacing 1.0

To find the plotting limits, you would have round you Xmax up and Xmin down to the next tic.

Exactly. Although I think same algorithm will end up working for both X and Y. I hit several snags between that formulation and practice. I would feel bad asking someone to work it out -- I figured there *must* be some math published out there.

http://www.mathworks.com/h

So if you require at least 30 pixels for a tick, then if the graph is 200 pixels, you have room for at most floor(200/30) = 6 ticks (not counting the bottom as a tick since it helps the math). Then take (max - min)/ticks and round it up to the nearest 1, 2, or 5.

For example min = .97, max = 1.05 (max - min)/6 = .013333... rounds to .02 so we use .96, .98, etc.

```
Private Class cFriendlyAxisTickCalculator
Public m_Input_MinLogical As Single
Public m_Input_MaxLogical As Single
Public m_Input_PixelSpan As Single
Public m_Input_IdealPixelDistance As Single = 100
Public m_Output_ResultsAreValid As Boolean
Public m_Output_NumberOfTicks As Integer
Public m_Output_FirstTickLogicalValue As Single
Public m_Output_TickLogicalInterval As Single
Public m_Output_FirstTickPixelPosition As Single
Public m_Output_TickPixelInterval As Single
Public m_Output_DecimalPlacesToShow As Integer
Public Sub Calculate()
m_Output_ResultsAreValid = False
If m_Input_PixelSpan = 0 Then Return
If m_Input_MaxLogical <= m_Input_MinLogical Then Return
Dim deltalogical As Single = m_Input_MaxLogical - m_Input_MinLogical
Dim log10_deltalogical As Single = Math.Log10(deltalogical)
Dim fixed_log10_deltalogical As Single = Int(log10_deltalogical)
Dim logical_per_pixel As Single = deltalogical / m_Input_PixelSpan
Dim logical_for_ideal_pixel_distance As Single = logical_per_pixel * m_Input_IdealPixelDistance
Dim LFIPD_normalized_to_fixed_log10 As Single = logical_for_ideal_pixel_distance / 10 ^ (fixed_log10_deltalogical)
' fit to 0.1, 0.2, 1.0 (i.e., 1/10, 1/5, or whole integers)
Dim LFPID_norm_friendly As Single
If LFIPD_normalized_to_fixed_log10 < 0.15 Then
m_Output_DecimalPlacesToShow = -fixed_log10_deltalogical + 1
LFPID_norm_friendly = 0.1
ElseIf LFIPD_normalized_to_fixed_log10 < 0.6 Then
m_Output_DecimalPlacesToShow = -fixed_log10_deltalogical + 1
LFPID_norm_friendly = 0.2
Else
m_Output_DecimalPlacesToShow = -fixed_log10_deltalogical
LFPID_norm_friendly = 1.0
End If
If m_Output_DecimalPlacesToShow < 0 Then m_Output_DecimalPlacesToShow = 0
m_Output_TickLogicalInterval = LFPID_norm_friendly * 10 ^ (fixed_log10_deltalogical)
m_Output_TickPixelInterval = m_Output_TickLogicalInterval / logical_per_pixel
If m_Output_TickPixelInterval = 0 Then Return
Dim z As Single = Fix(m_Input_MinLogical / m_Output_TickLogicalInterval)
m_Output_FirstTickLogicalValue = (z + 1) * m_Output_TickLogicalInterval
m_Output_FirstTickPixelPosition = (m_Output_FirstTickLogicalValue - m_Input_MinLogical) / logical_per_pixel
m_Output_NumberOfTicks = 1 + Int((m_Input_PixelSpan - m_Output_FirstTickPixelPosition) / m_Output_TickPixelInterval)
m_Output_ResultsAreValid = True
End Sub
End Class
```

Small fix for when low end is negative:

```
Private Class cFriendlyAxisTickCalculator
Public m_Input_MinLogical As Single
Public m_Input_MaxLogical As Single
Public m_Input_PixelSpan As Single
Public m_Input_IdealPixelDistance As Single = 80
Public m_Output_ResultsAreValid As Boolean
Public m_Output_NumberOfTicks As Integer
Public m_Output_FirstTickLogicalValue As Single
Public m_Output_TickLogicalInterval As Single
Public m_Output_FirstTickPixelPosition As Single
Public m_Output_TickPixelInterval As Single
Public m_Output_DecimalPlacesToShow As Integer
Public m_Temp As Single
Public Sub Calculate()
m_Output_ResultsAreValid = False
If m_Input_PixelSpan = 0 Then Return
If m_Input_MaxLogical <= m_Input_MinLogical Then Return
Dim deltalogical As Single = m_Input_MaxLogical - m_Input_MinLogical
Dim log10_deltalogical As Single = Math.Log10(deltalogical)
Dim fixed_log10_deltalogical As Single = Int(log10_deltalogical)
Dim logical_per_pixel As Single = deltalogical / m_Input_PixelSpan
Dim logical_for_ideal_pixel_distance As Single = logical_per_pixel * m_Input_IdealPixelDistance
Dim LFIPD_normalized_to_fixed_log10 As Single = logical_for_ideal_pixel_distance / 10 ^ (fixed_log10_deltalogical)
' fit to 0.1, 0.2, 1.0 (i.e., 1/10, 1/5, or whole integers)
m_Temp = LFIPD_normalized_to_fixed_log10
Dim LFPID_norm_friendly As Single
If LFIPD_normalized_to_fixed_log10 < 0.15 Then
m_Output_DecimalPlacesToShow = -fixed_log10_deltalogical + 1
LFPID_norm_friendly = 0.1
ElseIf LFIPD_normalized_to_fixed_log10 < 0.35 Then
m_Output_DecimalPlacesToShow = -fixed_log10_deltalogical + 1
LFPID_norm_friendly = 0.2
ElseIf LFIPD_normalized_to_fixed_log10 < 0.6 Then
m_Output_DecimalPlacesToShow = -fixed_log10_deltalogical + 1
LFPID_norm_friendly = 0.5
Else
m_Output_DecimalPlacesToShow = -fixed_log10_deltalogical
LFPID_norm_friendly = 1.0
End If
If m_Output_DecimalPlacesToShow < 0 Then m_Output_DecimalPlacesToShow = 0
m_Output_TickLogicalInterval = LFPID_norm_friendly * 10 ^ (fixed_log10_deltalogical)
m_Output_TickPixelInterval = m_Output_TickLogicalInterval / logical_per_pixel
If m_Output_TickPixelInterval = 0 Then Return
Dim z As Single = Fix(m_Input_MinLogical / m_Output_TickLogicalInterval)
m_Output_FirstTickLogicalValue = z * m_Output_TickLogicalInterval
If m_Output_FirstTickLogicalValue < m_Input_MinLogical Then m_Output_FirstTickLogicalValue += m_Output_TickLogicalInterval
m_Output_FirstTickPixelPosition = (m_Output_FirstTickLogicalValue - m_Input_MinLogical) / logical_per_pixel
m_Output_NumberOfTicks = 1 + Int((m_Input_PixelSpan - m_Output_FirstTickPixelPosition) / m_Output_TickPixelInterval)
m_Output_ResultsAreValid = True
End Sub
End Class
```

By clicking you are agreeing to Experts Exchange's Terms of Use.

Title | # Comments | Views | Activity |
---|---|---|---|

Submitting to a REST API via a CLR in C# | 12 | 49 | |

Voice recognition ASP or ASP.NET or JavaScript | 2 | 24 | |

Any reason why this might be bad? | 7 | 28 | |

import issue in bit values | 3 | 33 |

Join the community of 500,000 technology professionals and ask your questions.

Connect with top rated Experts

**19** Experts available now in Live!