그래프 그리는 프로그램을 보면 눈금자가 적당히 그려진다.
가령, 그래프가 출력되는 범위가 13부터 30까지라면, 눈금자가 15, 20, 25, 30의 위치에 그려져 있는 뭐 그런거. 격자(grid)를 그린다고도 한다.

이런건 어떻게 구현 할 수 있을까?
Gnuplot같은 오픈소스 프로그램을 보면 알 수 있겠지만, 난 다른 사람이 만든 프로그램을 뜯어볼 정도로 고수도 아니고 의지도 없으므로 직접 만들어 보기로 했다.
아래의 소스는 구간의 길이를 적당히 판단해서, 1단위, 2단위, 5단위로 눈금의 위치를 출력하는 알고리즘이다.

비주얼 베이직으로 소스를 만들어 보았다. 물론 이 소스는 아마 제대로 작동하지 않을 것이며 (보안상 그렇다. 난 당연히 작동하는 소스를 이용하고 있다.) 공부한 후 직접 작동하도록 고쳐서 쓰는 것은 숙제이다.

    Function determineTics(min, max) As Double()
        d = max - min 일단 양쪽 끝 값의 차이를 알아서, 얼마나 긴 구간을 그려야 하는지 조사한다.
        tics(0) = 0 첫 값은 그냥 0이라고 해 두자. 아무 의미 없다.
        If d < 0 Then
            Return tics 만약 구간 길이가 음수라면, 최대값이 최소값보다 작은 경우이므로 아무 의미 없는 값을 돌려준다.
        End If
        digit = CInt(System.Math.Floor(System.Math.Log10(d))) 자리수가 몇자리인지 세야 한다. 자리수는 상용로그에서 지표값이 된다. 따라서 상용로그를 계산한 후 올린다. 가령, 10은 상용로그값이 1인데 2자리수이며, 0.1은 상용로그값이 -1인데 이런건 0자리수라고 치자.
이제, 가수를 계산하자.
        added = System.Math.Log10(d) - CDbl(digit) 이건 가수를 뜻한다. 가수는 상용로그 값의 소수부분을 말한다. 잘 모르겠으면 고등학교 수학 교과서에서 로그함수 부분을 찾아보도록 하자. 어떤 수의 소수부분은 당연히 그 수에서 정수부분을 빼면 된다.
        If added >= 0 And added < System.Math.Log10(2) Then 만약 가수가 0보다 크고 2의 상용로그값보다 크면 눈금의 크기는 2눈금이 된다.
            s = System.Math.Log10(2)
        ElseIf added > System.Math.Log10(2) And added < System.Math.Log10(5) Then 마찬가지로 2의 상용로그값보다 크고 5의 상용로그값보다 작으면 5눈금이 된다.
            s = System.Math.Log10(5)
        Else
            s = 1 나머지는 1눈금으로 가자.
        End If
        diff = System.Math.Pow(10, s + CDbl(digit - 1)) 이것은 눈금자 사이의 간격을 말한다.
        beginning = System.Math.Floor(min / System.Math.Pow(10, digit)) * System.Math.Pow(10, digit) 이것은 최초 시작하는 값이다. 전체 구간의 시작부분보다는 큰 값이면서 눈금에 해당하는 곳을 찾아낸다.
        If beginning < min Then
            beginning += diff 만약을 위해, 혹시 눈금의 시작값이 구간의 시작부분보다 작다면 한칸 더해준다. 아마 한칸만 더하면 될 것이다. 두칸이나 틀릴수는 없다고 생각한다.
        End If
        tics(0) = beginning 이제 되돌려줄 배열의 최초값을 설정한다.
        Dim i As Integer = 1 원래는 for문을 쓰고 싶었지만 구간이 도대체 얼마나 될지 알수가 없으니까 무한반복문을 쓰고 중간에 빠져나오도록 했다.
        Do
            ReDim Preserve tics(i) 한칸 늘려주고
            tics(i) = tics(i - 1) + diff 그 다음칸의 값은 앞에 있던 칸에다가 눈금의 간격만큼을 더한 값이다
            If tics(i) + diff >= max Then 만약 그 다음값이 구간의 끝값을 넘어갈 것 같으면
                Exit Do 끝낸다
            End If
            i += 1 아니면 한단계 더 올리고 다시 반복하자.
        Loop
        Return tics 이제 배열을 되돌려 주면 된다.
    End Function


문제 : 그럼, 로그 눈금으로 그리고 싶으면 어떻게 하면 될까?
답 :            
newtic = determineTics(System.Math.Floor(System.Math.Log10(min)), System.Math.Ceiling(System.Math.Log10(max)))

by snowall 2009. 9. 5. 03:15