first example directed breadth bfs algorithms java algorithm math charts

java - example - Algoritmo de etiqueta agradable para gráficos con marcas mínimas



graph in c (13)

¡Aquí está en TypeScript!

/** * Calculate and update values for tick spacing and nice * minimum and maximum data points on the axis. */ function calculateTicks(maxTicks: number, minPoint: number, maxPoint: number): [number, number, number] { let range = niceNum(maxPoint - minPoint, false); let tickSpacing = niceNum(range / (maxTicks - 1), true); let niceMin = Math.floor(minPoint / tickSpacing) * tickSpacing; let niceMax = Math.ceil(maxPoint / tickSpacing) * tickSpacing; let tickCount = range / tickSpacing; return [tickCount, niceMin, niceMax]; } /** * Returns a "nice" number approximately equal to range Rounds * the number if round = true Takes the ceiling if round = false. * * @param range the data range * @param round whether to round the result * @return a "nice" number to be used for the data range */ function niceNum(range: number, round: boolean): number { let exponent: number; /** exponent of range */ let fraction: number; /** fractional part of range */ let niceFraction: number; /** nice, rounded fraction */ exponent = Math.floor(Math.log10(range)); fraction = range / Math.pow(10, exponent); if (round) { if (fraction < 1.5) niceFraction = 1; else if (fraction < 3) niceFraction = 2; else if (fraction < 7) niceFraction = 5; else niceFraction = 10; } else { if (fraction <= 1) niceFraction = 1; else if (fraction <= 2) niceFraction = 2; else if (fraction <= 5) niceFraction = 5; else niceFraction = 10; } return niceFraction * Math.pow(10, exponent); }

Necesito calcular los Ticklabels y el Tickrange para los gráficos manualmente.

Conozco el algoritmo "estándar" para hacer tictac (consulte http://books.google.de/books?id=fvA7zLEFWZgC&pg=PA61&lpg=PA61&redir_esc=y#v=onepage&q&f=false ) y también conozco esta implementación de Java .

El problema es que con este algoritmo, los tics son "demasiado inteligentes". Eso significa que, el algoritmo decide la cantidad de marcas que se deben mostrar. Mi requisito es que siempre haya 5 Ticks, pero estos por supuesto deberían ser "bonitos". El enfoque ingenuo sería obtener el valor máximo, dividir con 5 y multiplicar con el número de tick. Los valores aquí son, por supuesto, no óptimos y las garrapatas son bastante feas.

¿Alguien sabe una solución para el problema o tiene una sugerencia para una descripción formal del algoritmo?


¡Aquí está la versión de Kotlin!

import java.lang.Math.* /** * Instantiates a new instance of the NiceScale class. * * @param min Double The minimum data point. * @param max Double The maximum data point. */ class NiceScale(private var minPoint: Double, private var maxPoint: Double) { private var maxTicks = 15.0 private var range: Double = 0.0 var niceMin: Double = 0.0 var niceMax: Double = 0.0 var tickSpacing: Double = 0.0 init { calculate() } /** * Calculate and update values for tick spacing and nice * minimum and maximum data points on the axis. */ private fun calculate() { range = niceNum(maxPoint - minPoint, false) tickSpacing = niceNum(range / (maxTicks - 1), true) niceMin = floor(minPoint / tickSpacing) * tickSpacing niceMax = ceil(maxPoint / tickSpacing) * tickSpacing } /** * Returns a "nice" number approximately equal to range. Rounds * the number if round = true. Takes the ceiling if round = false. * * @param range Double The data range. * @param round Boolean Whether to round the result. * @return Double A "nice" number to be used for the data range. */ private fun niceNum(range: Double, round: Boolean): Double { /** Exponent of range */ val exponent: Double = floor(log10(range)) /** Fractional part of range */ val fraction: Double /** Nice, rounded fraction */ val niceFraction: Double fraction = range / pow(10.0, exponent) niceFraction = if (round) { when { fraction < 1.5 -> 1.0 fraction < 3 -> 2.0 fraction < 7 -> 5.0 else -> 10.0 } } else { when { fraction <= 1 -> 1.0 fraction <= 2 -> 2.0 fraction <= 5 -> 5.0 else -> 10.0 } } return niceFraction * pow(10.0, exponent) } /** * Sets the minimum and maximum data points. * * @param minPoint Double The minimum data point. * @param maxPoint Double The maximum data point. */ fun setMinMaxPoints(minPoint: Double, maxPoint: Double) { this.minPoint = minPoint this.maxPoint = maxPoint calculate() } /** * Sets maximum number of tick marks we''re comfortable with. * * @param maxTicks Double The maximum number of tick marks. */ fun setMaxTicks(maxTicks: Double) { this.maxTicks = maxTicks calculate() } }


Aquí está la misma cosa en el Objetivo C

YFRNiceScale.h

#import <Foundation/Foundation.h> @interface YFRNiceScale : NSObject @property (nonatomic, readonly) CGFloat minPoint; @property (nonatomic, readonly) CGFloat maxPoint; @property (nonatomic, readonly) CGFloat maxTicks; @property (nonatomic, readonly) CGFloat tickSpacing; @property (nonatomic, readonly) CGFloat range; @property (nonatomic, readonly) CGFloat niceRange; @property (nonatomic, readonly) CGFloat niceMin; @property (nonatomic, readonly) CGFloat niceMax; - (id) initWithMin: (CGFloat) min andMax: (CGFloat) max; - (id) initWithNSMin: (NSDecimalNumber*) min andNSMax: (NSDecimalNumber*) max; @end

YFRNiceScale.m

#import "YFRNiceScale.h" @implementation YFRNiceScale @synthesize minPoint = _minPoint; @synthesize maxPoint = _maxPoint; @synthesize maxTicks = _maxTicks; @synthesize tickSpacing = _tickSpacing; @synthesize range = _range; @synthesize niceRange = _niceRange; @synthesize niceMin = _niceMin; @synthesize niceMax = _niceMax; - (id)init { self = [super init]; if (self) { } return self; } - (id) initWithMin: (CGFloat) min andMax: (CGFloat) max { if (self) { _maxTicks = 10; _minPoint = min; _maxPoint = max; [self calculate]; } return [self init]; } - (id) initWithNSMin: (NSDecimalNumber*) min andNSMax: (NSDecimalNumber*) max { if (self) { _maxTicks = 10; _minPoint = [min doubleValue]; _maxPoint = [max doubleValue]; [self calculate]; } return [self init]; } /** * Calculate and update values for tick spacing and nice minimum and maximum * data points on the axis. */ - (void) calculate { _range = [self niceNumRange: (_maxPoint-_minPoint) roundResult:NO]; _tickSpacing = [self niceNumRange: (_range / (_maxTicks - 1)) roundResult:YES]; _niceMin = floor(_minPoint / _tickSpacing) * _tickSpacing; _niceMax = ceil(_maxPoint / _tickSpacing) * _tickSpacing; _niceRange = _niceMax - _niceMin; } /** * Returns a "nice" number approximately equal to range Rounds the number if * round = true Takes the ceiling if round = false. * * @param range * the data range * @param round * whether to round the result * @return a "nice" number to be used for the data range */ - (CGFloat) niceNumRange:(CGFloat) aRange roundResult:(BOOL) round { CGFloat exponent; CGFloat fraction; CGFloat niceFraction; exponent = floor(log10(aRange)); fraction = aRange / pow(10, exponent); if (round) { if (fraction < 1.5) { niceFraction = 1; } else if (fraction < 3) { niceFraction = 2; } else if (fraction < 7) { niceFraction = 5; } else { niceFraction = 10; } } else { if (fraction <= 1) { niceFraction = 1; } else if (fraction <= 2) { niceFraction = 2; } else if (fraction <= 5) { niceFraction = 2; } else { niceFraction = 10; } } return niceFraction * pow(10, exponent); } - (NSString*) description { return [NSString stringWithFormat:@"NiceScale [minPoint=%.2f, maxPoint=%.2f, maxTicks=%.2f, tickSpacing=%.2f, range=%.2f, niceMin=%.2f, niceMax=%.2f]", _minPoint, _maxPoint, _maxTicks, _tickSpacing, _range, _niceMin, _niceMax ]; } @end

Uso:

YFRNiceScale* niceScale = [[YFRNiceScale alloc] initWithMin:0 andMax:500]; NSLog(@"Nice: %@", niceScale);


Aquí está la versión de C ++. Como beneficio adicional, obtiene una función que devuelve el número mínimo de puntos decimales para mostrar las etiquetas de marca en el eje.

El archivo de cabecera:

class NiceScale { public: float minPoint; float maxPoint; float maxTicks; float tickSpacing; float range; float niceMin; float niceMax; public: NiceScale() { maxTicks = 10; } /** * Instantiates a new instance of the NiceScale class. * * @param min the minimum data point on the axis * @param max the maximum data point on the axis */ NiceScale(float min, float max) { minPoint = min; maxPoint = max; calculate(); } /** * Calculate and update values for tick spacing and nice * minimum and maximum data points on the axis. */ void calculate() ; /** * Returns a "nice" number approximately equal to range Rounds * the number if round = true Takes the ceiling if round = false. * * @param range the data range * @param round whether to round the result * @return a "nice" number to be used for the data range */ float niceNum(float range, boolean round) ; /** * Sets the minimum and maximum data points for the axis. * * @param minPoint the minimum data point on the axis * @param maxPoint the maximum data point on the axis */ void setMinMaxPoints(float minPoint, float maxPoint) ; /** * Sets maximum number of tick marks we''re comfortable with * * @param maxTicks the maximum number of tick marks for the axis */ void setMaxTicks(float maxTicks) ; int decimals(void); };

Y el archivo CPP:

/** * Calculate and update values for tick spacing and nice * minimum and maximum data points on the axis. */ void NiceScale::calculate() { range = niceNum(maxPoint - minPoint, false); tickSpacing = niceNum(range / (maxTicks - 1), true); niceMin = floor(minPoint / tickSpacing) * tickSpacing; niceMax = ceil(maxPoint / tickSpacing) * tickSpacing; } /** * Returns a "nice" number approximately equal to range Rounds the number if round = true Takes the ceiling if round = false. * * @param range the data range * @param round whether to round the result * @return a "nice" number to be used for the data range */ float NiceScale::niceNum(float range, boolean round) { float exponent; /** exponent of range */ float fraction; /** fractional part of range */ float niceFraction; /** nice, rounded fraction */ exponent = floor(log10(range)); fraction = range / pow(10.f, exponent); if (round) { if (fraction < 1.5) niceFraction = 1; else if (fraction < 3) niceFraction = 2; else if (fraction < 7) niceFraction = 5; else niceFraction = 10; } else { if (fraction <= 1) niceFraction = 1; else if (fraction <= 2) niceFraction = 2; else if (fraction <= 5) niceFraction = 5; else niceFraction = 10; } return niceFraction * pow(10, exponent); } /** * Sets the minimum and maximum data points for the axis. * * @param minPoint the minimum data point on the axis * @param maxPoint the maximum data point on the axis */ void NiceScale::setMinMaxPoints(float minPoint, float maxPoint) { this->minPoint = minPoint; this->maxPoint = maxPoint; calculate(); } /** * Sets maximum number of tick marks we''re comfortable with * * @param maxTicks the maximum number of tick marks for the axis */ void NiceScale::setMaxTicks(float maxTicks) { this->maxTicks = maxTicks; calculate(); } // minimum number of decimals in tick labels // use in sprintf statement: // sprintf(buf, "%.*f", decimals(), tickValue); int NiceScale::decimals(void) { float logTickX = log10(tickSpacing); if(logTickX >= 0) return 0; return (int)(abs(floor(logTickX))); }


Aquí hay una versión de javascript:

var minPoint; var maxPoint; var maxTicks = 10; var tickSpacing; var range; var niceMin; var niceMax; /** * Instantiates a new instance of the NiceScale class. * * min the minimum data point on the axis * max the maximum data point on the axis */ function niceScale( min, max) { minPoint = min; maxPoint = max; calculate(); return { tickSpacing: tickSpacing, niceMinimum: niceMin, niceMaximum: niceMax }; } /** * Calculate and update values for tick spacing and nice * minimum and maximum data points on the axis. */ function calculate() { range = niceNum(maxPoint - minPoint, false); tickSpacing = niceNum(range / (maxTicks - 1), true); niceMin = Math.floor(minPoint / tickSpacing) * tickSpacing; niceMax = Math.ceil(maxPoint / tickSpacing) * tickSpacing; } /** * Returns a "nice" number approximately equal to range Rounds * the number if round = true Takes the ceiling if round = false. * * localRange the data range * round whether to round the result * a "nice" number to be used for the data range */ function niceNum( localRange, round) { var exponent; /** exponent of localRange */ var fraction; /** fractional part of localRange */ var niceFraction; /** nice, rounded fraction */ exponent = Math.floor(Math.log10(localRange)); fraction = localRange / Math.pow(10, exponent); if (round) { if (fraction < 1.5) niceFraction = 1; else if (fraction < 3) niceFraction = 2; else if (fraction < 7) niceFraction = 5; else niceFraction = 10; } else { if (fraction <= 1) niceFraction = 1; else if (fraction <= 2) niceFraction = 2; else if (fraction <= 5) niceFraction = 5; else niceFraction = 10; } return niceFraction * Math.pow(10, exponent); } /** * Sets the minimum and maximum data points for the axis. * * minPoint the minimum data point on the axis * maxPoint the maximum data point on the axis */ function setMinMaxPoints( localMinPoint, localMaxPoint) { minPoint = localMinPoint; maxPoint = localMaxoint; calculate(); } /** * Sets maximum number of tick marks we''re comfortable with * * maxTicks the maximum number of tick marks for the axis */ function setMaxTicks(localMaxTicks) { maxTicks = localMaxTicks; calculate(); }

¡Disfrutar!


Debería poder usar la implementación de Java con correcciones menores.

Cambia los valores máximos a 5.

Cambie el método de cálculo a esto:

private void calculate() { this.range = niceNum(maxPoint - minPoint, false); this.tickSpacing = niceNum(range / (maxTicks - 1), true); this.niceMin = Math.floor(minPoint / tickSpacing) * tickSpacing; this.niceMax = this.niceMin + tickSpacing * (maxticks - 1); // Always display maxticks }

Descargo de responsabilidad: tenga en cuenta que no he probado esto, por lo que es posible que deba modificarlo para que se vea bien. Mi solución sugerida agrega espacio adicional en la parte superior de la tabla para dejar siempre espacio para 5 tics. Esto puede parecer feo en algunos casos.


Encontré este hilo mientras escribía algo de php, ¡ahora el mismo código también está disponible en php!

class CNiceScale { private $minPoint; private $maxPoint; private $maxTicks = 10; private $tickSpacing; private $range; private $niceMin; private $niceMax; public function setScale($min, $max) { $this->minPoint = $min; $this->maxPoint = $max; $this->calculate(); } private function calculate() { $this->range = $this->niceNum($this->maxPoint - $this->minPoint, false); $this->tickSpacing = $this->niceNum($this->range / ($this->maxTicks - 1), true); $this->niceMin = floor($this->minPoint / $this->tickSpacing) * $this->tickSpacing; $this->niceMax = ceil($this->maxPoint / $this->tickSpacing) * $this->tickSpacing; } private function niceNum($range, $round) { $exponent; /** exponent of range */ $fraction; /** fractional part of range */ $niceFraction; /** nice, rounded fraction */ $exponent = floor(log10($range)); $fraction = $range / pow(10, $exponent); if ($round) { if ($fraction < 1.5) $niceFraction = 1; else if ($fraction < 3) $niceFraction = 2; else if ($fraction < 7) $niceFraction = 5; else $niceFraction = 10; } else { if ($fraction <= 1) $niceFraction = 1; else if ($fraction <= 2) $niceFraction = 2; else if ($fraction <= 5) $niceFraction = 5; else $niceFraction = 10; } return $niceFraction * pow(10, $exponent); } public function setMinMaxPoints($minPoint, $maxPoint) { $this->minPoint = $minPoint; $this->maxPoint = $maxPoint; $this->calculate(); } public function setMaxTicks($maxTicks) { $this->maxTicks = $maxTicks; $this->calculate(); } public function getTickSpacing() { return $this->tickSpacing; } public function getNiceMin() { return $this->niceMin; } public function getNiceMax() { return $this->niceMax; } }


Esta es la versión Swift:

class NiceScale { private var minPoint: Double private var maxPoint: Double private var maxTicks = 10 private(set) var tickSpacing: Double = 0 private(set) var range: Double = 0 private(set) var niceMin: Double = 0 private(set) var niceMax: Double = 0 init(min: Double, max: Double) { minPoint = min maxPoint = max calculate() } func setMinMaxPoints(min: Double, max: Double) { minPoint = min maxPoint = max calculate() } private func calculate() { range = niceNum(maxPoint - minPoint, round: false) tickSpacing = niceNum(range / Double((maxTicks - 1)), round: true) niceMin = floor(minPoint / tickSpacing) * tickSpacing niceMax = floor(maxPoint / tickSpacing) * tickSpacing } private func niceNum(range: Double, round: Bool) -> Double { let exponent = floor(log10(range)) let fraction = range / pow(10, exponent) let niceFraction: Double if round { if fraction <= 1.5 { niceFraction = 1 } else if fraction <= 3 { niceFraction = 2 } else if fraction <= 7 { niceFraction = 5 } else { niceFraction = 10 } } else { if fraction <= 1 { niceFraction = 1 } else if fraction <= 2 { niceFraction = 2 } else if fraction <= 5 { niceFraction = 5 } else { niceFraction = 10 } } return niceFraction * pow(10, exponent) } }


Esta es la versión VB.NET.

Public Class NiceScale Private minPoint As Double Private maxPoint As Double Private maxTicks As Double = 10 Private tickSpacing Private range As Double Private niceMin As Double Private niceMax As Double Public Sub New(min As Double, max As Double) minPoint = min maxPoint = max calculate() End Sub Private Sub calculate() range = niceNum(maxPoint - minPoint, False) tickSpacing = niceNum(range / (maxTicks - 1), True) niceMin = Math.Floor(minPoint / tickSpacing) * tickSpacing niceMax = Math.Ceiling(maxPoint / tickSpacing) * tickSpacing End Sub Private Function niceNum(range As Double, round As Boolean) As Double Dim exponent As Double ''/** exponent of range */ Dim fraction As Double ''/** fractional part of range */ Dim niceFraction As Double ''/** nice, rounded fraction */ exponent = Math.Floor(Math.Log10(range)) fraction = range / Math.Pow(10, exponent) If round Then If (fraction < 1.5) Then niceFraction = 1 ElseIf (fraction < 3) Then niceFraction = 2 ElseIf (fraction < 7) Then niceFraction = 5 Else niceFraction = 10 End If Else If (fraction <= 1) Then niceFraction = 1 ElseIf (fraction <= 2) Then niceFraction = 2 ElseIf (fraction <= 5) Then niceFraction = 5 Else niceFraction = 10 End If End If Return niceFraction * Math.Pow(10, exponent) End Function Public Sub setMinMaxPoints(minPoint As Double, maxPoint As Double) minPoint = minPoint maxPoint = maxPoint calculate() End Sub Public Sub setMaxTicks(maxTicks As Double) maxTicks = maxTicks calculate() End Sub Public Function getTickSpacing() As Double Return tickSpacing End Function Public Function getNiceMin() As Double Return niceMin End Function Public Function getNiceMax() As Double Return niceMax End Function End Class


He convertido el código de Java anterior a Python según mis requerimientos.

import math class NiceScale: def __init__(self, minv,maxv): self.maxTicks = 6 self.tickSpacing = 0 self.lst = 10 self.niceMin = 0 self.niceMax = 0 self.minPoint = minv self.maxPoint = maxv self.calculate() def calculate(self): self.lst = self.niceNum(self.maxPoint - self.minPoint, False) self.tickSpacing = self.niceNum(self.lst / (self.maxTicks - 1), True) self.niceMin = math.floor(self.minPoint / self.tickSpacing) * self.tickSpacing self.niceMax = math.ceil(self.maxPoint / self.tickSpacing) * self.tickSpacing def niceNum(self, lst, rround): self.lst = lst exponent = 0 # exponent of range */ fraction = 0 # fractional part of range */ niceFraction = 0 # nice, rounded fraction */ exponent = math.floor(math.log10(self.lst)); fraction = self.lst / math.pow(10, exponent); if (self.lst): if (fraction < 1.5): niceFraction = 1 elif (fraction < 3): niceFraction = 2 elif (fraction < 7): niceFraction = 5; else: niceFraction = 10; else : if (fraction <= 1): niceFraction = 1 elif (fraction <= 2): niceFraction = 2 elif (fraction <= 5): niceFraction = 5 else: niceFraction = 10 return niceFraction * math.pow(10, exponent) def setMinMaxPoints(self, minPoint, maxPoint): self.minPoint = minPoint self.maxPoint = maxPoint self.calculate() def setMaxTicks(self, maxTicks): self.maxTicks = maxTicks; self.calculate() a=NiceScale(14024, 17756) print "a.lst ", a.lst print "a.maxPoint ", a.maxPoint print "a.maxTicks ", a.maxTicks print "a.minPoint ", a.minPoint print "a.niceMax ", a.niceMax print "a.niceMin ", a.niceMin print "a.tickSpacing ", a.tickSpacing


Necesitaba que este algoritmo se convirtiera a C #, así que aquí está ...

public static class NiceScale { public static void Calculate(double min, double max, int maxTicks, out double range, out double tickSpacing, out double niceMin, out double niceMax) { range = niceNum(max - min, false); tickSpacing = niceNum(range / (maxTicks - 1), true); niceMin = Math.Floor(min / tickSpacing) * tickSpacing; niceMax = Math.Ceiling(max / tickSpacing) * tickSpacing; } private static double niceNum(double range, bool round) { double pow = Math.Pow(10, Math.Floor(Math.Log10(range))); double fraction = range / pow; double niceFraction; if (round) { if (fraction < 1.5) { niceFraction = 1; } else if (fraction < 3) { niceFraction = 2; } else if (fraction < 7) { niceFraction = 5; } else { niceFraction = 10; } } else { if (fraction <= 1) { niceFraction = 1; } else if (fraction <= 2) { niceFraction = 2; } else if (fraction <= 5) { niceFraction = 5; } else { niceFraction = 10; } } return niceFraction * pow; } }


Soy el autor de " Algoritmo para el escalamiento óptimo en un eje gráfico ". Solía ​​estar alojado en trollop.org, pero recientemente he movido motores de dominios / blogs. De todos modos, publicaré los contenidos aquí para facilitar el acceso.

He estado trabajando en una aplicación de gráficos de Android para una tarea y me encontré con un pequeño problema a la hora de presentar el gráfico en un formato bien escalado. Pasé un tiempo tratando de crear este algoritmo por mi cuenta y estuve muy cerca, pero al final encontré un ejemplo de pseudocódigo en un libro llamado "Graphics Gems, Volume 1" de Andrew S. Glassner. En el capítulo " Números agradables para etiquetas gráficas ", se proporciona una descripción excelente del problema:

Al crear un gráfico por computadora, es conveniente etiquetar los ejes x e y con números "agradables": números decimales simples. Por ejemplo, si el rango de datos es de 105 a 543, probablemente nos gustaría trazar el rango de 100 a 600 y poner marcas de verificación cada 100 unidades. O si el rango de datos es de 2.04 a 2.16, probablemente trazaríamos un rango de 2.00 a 2.20 con un espaciado de marca de 0.05. Los humanos son buenos para elegir tales números "buenos", pero los algoritmos simplistas no lo son. El algoritmo de selección de etiquetas ingenuo toma el rango de datos y lo divide en n intervalos iguales, pero esto generalmente resulta en etiquetas de tic feos. Aquí describimos un método simple para generar buenas etiquetas gráficas.

La observación principal es que los números "más bonitos" en decimal son 1, 2 y 5, y todos los múltiplos de poder de diez de estos números. Usaremos solo dichos números para el espaciado de marca y colocaremos marcas de verificación en múltiplos del espacio de marca ...

Usé el ejemplo de pseudocódigo en este libro para crear la siguiente clase en Java:

public class NiceScale { private double minPoint; private double maxPoint; private double maxTicks = 10; private double tickSpacing; private double range; private double niceMin; private double niceMax; /** * Instantiates a new instance of the NiceScale class. * * @param min the minimum data point on the axis * @param max the maximum data point on the axis */ public NiceScale(double min, double max) { this.minPoint = min; this.maxPoint = max; calculate(); } /** * Calculate and update values for tick spacing and nice * minimum and maximum data points on the axis. */ private void calculate() { this.range = niceNum(maxPoint - minPoint, false); this.tickSpacing = niceNum(range / (maxTicks - 1), true); this.niceMin = Math.floor(minPoint / tickSpacing) * tickSpacing; this.niceMax = Math.ceil(maxPoint / tickSpacing) * tickSpacing; } /** * Returns a "nice" number approximately equal to range Rounds * the number if round = true Takes the ceiling if round = false. * * @param range the data range * @param round whether to round the result * @return a "nice" number to be used for the data range */ private double niceNum(double range, boolean round) { double exponent; /** exponent of range */ double fraction; /** fractional part of range */ double niceFraction; /** nice, rounded fraction */ exponent = Math.floor(Math.log10(range)); fraction = range / Math.pow(10, exponent); if (round) { if (fraction < 1.5) niceFraction = 1; else if (fraction < 3) niceFraction = 2; else if (fraction < 7) niceFraction = 5; else niceFraction = 10; } else { if (fraction <= 1) niceFraction = 1; else if (fraction <= 2) niceFraction = 2; else if (fraction <= 5) niceFraction = 5; else niceFraction = 10; } return niceFraction * Math.pow(10, exponent); } /** * Sets the minimum and maximum data points for the axis. * * @param minPoint the minimum data point on the axis * @param maxPoint the maximum data point on the axis */ public void setMinMaxPoints(double minPoint, double maxPoint) { this.minPoint = minPoint; this.maxPoint = maxPoint; calculate(); } /** * Sets maximum number of tick marks we''re comfortable with * * @param maxTicks the maximum number of tick marks for the axis */ public void setMaxTicks(double maxTicks) { this.maxTicks = maxTicks; calculate(); } }

Entonces podemos hacer uso del código anterior como este:

NiceScale numScale = new NiceScale(-0.085, 0.173); System.out.println("Tick Spacing:/t" + numScale.getTickSpacing()); System.out.println("Nice Minimum:/t" + numScale.getNiceMin()); System.out.println("Nice Maximum:/t" + numScale.getNiceMax());

Que luego dará como resultado números bien formateados para su uso en cualquier aplicación para la que necesite crear escalas bonitas. = D

Tick Spacing: 0.05 Nice Minimum: -0.1 Nice Maximum: 0.2


Ya que todos y su perro publican una traducción a otros lenguajes populares, aquí está mi versión para el lenguaje de programación Nimrod . También agregué manejo de casos donde la cantidad de garrapatas es menos de dos:

import math, strutils const defaultMaxTicks = 10 type NiceScale = object minPoint: float maxPoint: float maxTicks: int tickSpacing: float niceMin: float niceMax: float proc ff(x: float): string = result = x.formatFloat(ffDecimal, 3) proc `$`*(x: NiceScale): string = result = "Input minPoint: " & x.minPoint.ff & "/nInput maxPoint: " & x.maxPoint.ff & "/nInput maxTicks: " & $x.maxTicks & "/nOutput niceMin: " & x.niceMin.ff & "/nOutput niceMax: " & x.niceMax.ff & "/nOutput tickSpacing: " & x.tickSpacing.ff & "/n" proc calculate*(x: var NiceScale) proc init*(x: var NiceScale; minPoint, maxPoint: float; maxTicks = defaultMaxTicks) = x.minPoint = minPoint x.maxPoint = maxPoint x.maxTicks = maxTicks x.calculate proc initScale*(minPoint, maxPoint: float; maxTicks = defaultMaxTicks): NiceScale = result.init(minPoint, maxPoint, maxTicks) proc niceNum(scaleRange: float; doRound: bool): float = var exponent: float ## Exponent of scaleRange. fraction: float ## Fractional part of scaleRange. niceFraction: float ## Nice, rounded fraction. exponent = floor(log10(scaleRange)); fraction = scaleRange / pow(10, exponent); if doRound: if fraction < 1.5: niceFraction = 1 elif fraction < 3: niceFraction = 2 elif fraction < 7: niceFraction = 5 else: niceFraction = 10 else: if fraction <= 1: niceFraction = 1 elif fraction <= 2: niceFraction = 2 elif fraction <= 5: niceFraction = 5 else: niceFraction = 10 return niceFraction * pow(10, exponent) proc calculate*(x: var NiceScale) = assert x.maxPoint > x.minPoint, "Wrong input range!" assert x.maxTicks >= 0, "Sorry, can''t have imaginary ticks!" let scaleRange = niceNum(x.maxPoint - x.minPoint, false) if x.maxTicks < 2: x.niceMin = floor(x.minPoint) x.niceMax = ceil(x.maxPoint) x.tickSpacing = (x.niceMax - x.niceMin) / (if x.maxTicks == 1: 2.0 else: 1.0) else: x.tickSpacing = niceNum(scaleRange / (float(x.maxTicks - 1)), true) x.niceMin = floor(x.minPoint / x.tickSpacing) * x.tickSpacing x.niceMax = ceil(x.maxPoint / x.tickSpacing) * x.tickSpacing when isMainModule: var s = initScale(57.2, 103.3) echo s

Esta es la versión despojada del comentario. El completo se puede leer en GitHub integrado en mi proyecto.