2008-10-25 30 views
19

Estoy buscando un algoritmo que coloque marcas en un eje, dado un rango para mostrar, un ancho para mostrarlo y una función para medir el ancho de una cuerda para una marca.Algoritmo de Tickmark para un eje gráfico

Por ejemplo, dado que necesito mostrar entre 1e-6 y 5e-6 y un ancho para mostrar en píxeles, el algoritmo determinaría que debo poner marcas (por ejemplo) en 1e-6, 2e-6 , 3e-6, 4e-6 y 5e-6. Dado un ancho menor, podría decidir que la ubicación óptima solo esté en las posiciones pares, es decir, 2e-6 y 4e-6 (ya que poner más marcas haría que se superpongan).

Un algoritmo inteligente daría preferencia a marcas en múltiplos de 10, 5 y 2. Además, un algoritmo inteligente sería simétrico en torno a cero.

Respuesta

15

Consulte el artículo de Paul Heckbert "Bonitos números para las etiquetas de gráficos" en Graphics Gems.

Google book preview

+0

Esto se ve bien, puedo comprar el libro. – Nick

+15

¿Cómo diablos puede un libro sin un libro electrónico disponible ser una respuesta tan aceptada? – geotheory

+0

Puede leer el artículo de Heckbert en [Amazon] (https://www.amazon.com/dp/0122861663/) (vea la función "Mirar dentro"). La implementación de C está en el Apéndice. –

1

Tome el segmento más largo alrededor de cero (o el gráfico completo, si el cero no está en el rango) - por ejemplo, si tiene algo en el rango [-5, 1], tome [-5,0 ]

Calcule aproximadamente cuánto tiempo durará este segmento, en tics. Esto solo está dividiendo la longitud por el ancho de una marca. Entonces supongamos que el método dice que podemos poner 11 ticks de -5 a 0. Este es nuestro límite superior. Por el lado más corto, simplemente reflejaremos el resultado en el lado más largo.

Ahora intente poner tantos (hasta 11) ticks en, de modo que el marcador para cada tic en el formulario i * 10 * 10^n, i * 5 * 10^n, i * 2 * 10^n, donde n es un número entero, ei es el índice del tic. Ahora es un problema de optimización: queremos maximizar la cantidad de tics que podemos poner, al mismo tiempo que minimizamos la distancia entre el último tic y el final del resultado. Asigne un puntaje para obtener tantos ticks como podamos, menos que nuestro límite superior, y asigne un puntaje para obtener el último tick cercano a n - tendrá que experimentar aquí.

En el ejemplo anterior, intente n = 1. Obtenemos 1 tilde (en i = 0). n = 2 nos da 1 tick, y estamos más lejos del límite inferior, por lo que sabemos que tenemos que ir hacia el otro lado. n = 0 nos da 6 tics, en cada punto de punto entero. n = -1 nos da 12 tics (0, -0.5, ..., -5.0). n = -2 nos da 24 tics, y así sucesivamente. El algoritmo de puntuación les dará a cada uno una puntuación; más alto significa un mejor método.

Repita esto para i * 5 * 10^n, y i * 2 * 10^n, y tome el que tenga el mejor puntaje.

(como un algoritmo de puntuación de ejemplo, digamos que el puntaje es la distancia al último tic multiplicado por el número máximo de ticks menos el número necesario. Esto probablemente será malo, pero servirá como un punto de partida decente) .

0

He estado utilizando la biblioteca de gráficos jQuery flot. Es de código abierto y hace bastante bien la generación de ejes/tics. Sugeriría mirar su código y pellizcar algunas ideas desde allí.

-3

¿Cuál es su lengua de desarrollo? Tengo un control gráfico en C++ que resuelve este problema fácilmente usando una combinación de logaritmo, cellos, etc. Si quieres puedes explicar el código por ti.

+0

Mi idioma dev es C#, pero no le importaría ver una implementación en C++ - I puede traducir – Nick

0

Este sencillo algoritmo produce un intervalo que es múltiplo de 1, 2, o 5 veces a la potencia de 10. Y el rango del eje consigue divididos en al menos 5 intervalos.La muestra es el código de lenguaje Java:

protected double calculateInterval(double range) { 
    double x = Math.pow(10.0, Math.floor(Math.log10(range))); 
    if (range/x >= 5) 
     return x; 
    else if (range/(x/2.0) >= 5) 
     return x/2.0; 
    else 
     return x/5.0; 
} 

Esta es una alternativa, por 10 intervalos mínimos:

protected double calculateInterval(double range) { 
    double x = Math.pow(10.0, Math.floor(Math.log10(range))); 
    if (range/(x/2.0) >= 10) 
     return x/2.0; 
    else if (range/(x/5.0) >= 10) 
     return x/5.0; 
    else 
     return x/10.0; 
} 
Cuestiones relacionadas