Existen algunas situaciones en las que es probable que necesites un rango de números con intervalos regulares a partir de un valor mínimo y máximo, así como una cantidad máxima de intervalos o “marcas” (por ejemplo 10). Por ejemplo, esto resulta útil si estás diseñando un control de UI que muestre marcas para los ejes de una gráfica en la que se muestren valores cartesianos. Continúa leyendo para ver una técnica que podrías utilizar para ello.
Esta técnica está basada en el algoritmo NiceLabels que puedes encontrar en StackOverflow portado a varios lenguajes de programación; así que… ¿por qué no también a Xojo? De hecho ha sido pan comido convertirlo desde JavaScript a Xojo gracias a las similitudes entre ambos lenguajes de programación.
Por supuesto, si simplemente quieres usar la clase sin más, puedes descargar el proyecto de ejemplo desde este enlace.
Comencemos creando la clase propiamente dicha con el nombre NiceScale y todas las propiedades requeridas; por tanto, añade una nueva Clase a tu proyecto Xojo. Luego, con la clase seleccionada en el Navegador,. añade las siguientes propiedades:
- MaxPoint As Double, definiendo el Scope a Protected
- MinPoint As Double, definiendo el Scope a Protected
- Spacing As Double, definiendo el Scope a Public
- NiceMaximum As Double, definiendo el Scope a Public
- NiceMinimum As Double, definiendo el Scope a Public
A continuación, es el momento de añadir los método comenzando por el Constructor.
- Method Name: Constructor
- Parameters: min As Double, max As Double
- Scope: Public
Y escribiendo el siguiente fragmento de código en el Editor de Código asociado:
If Min = Max Then Max = Min + 1 Self.MinPoint = Min(Min,Max) Self.MaxPoint = Max(Max, Min) Self.Calculate
Como puedes ver, el Constructor llama al método Calculate; de modo que vamos a añadirlo:
- Method Name: Calculate
- Scope: Protected
Escribiendo el siguiente código en el Editor de código asociado:
Var range As Double range = niceNum(MaxPoint - MinPoint, False) Spacing = niceNum(range / 9, True) NiceMinimum = Floor(MinPoint / Spacing) * Spacing NiceMaximum = Ceiling(MaxPoint / Spacing) * Spacing
Nuevamente, este método está llamando al método NiceNum… de modo que añadamos este también a la clase NiceScale:
- Method Name: NiceNum
- Parameters: range As Double, round As Boolean
- Return Type: Double
- Scope: Protected
Escribiendo el siguiente código en el Editor de Código asociado:
Var exponent As Double Var fraction As Double Var niceFraction As Double exponent = Floor(Log(range) / Log(10)) fraction = range / 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 * Pow(10, exponent)
Por último, añadamos otro par de métodos a la clase: GetValues y SetminMaxPoints. El primero nos proporcionará un array con los valores de tipo doble ya convenientemente calculados, mientras que el segundo nos permitirá ajustar un nuevo par de valores mínimo y máximo sin necesidad de tener que crear una nueva instancia de la clase:
- Method Name: GetValues
- Return Type: Double()
- Scope: Public
Escribe a continuación el siguiente código en el Editor de Código asociado:
Var values() As Double For n As Double = Self._NiceMinimum To Self._NiceMaximum Step Self._mSpacing values.add n Next If values(0) > 0 Then While values(0) <> 0 values.AddAt(0, values(0)-Self._mSpacing) Wend End If Return values
- Method Name: SetMinMaxPoints
- Parameters: min As Double, max As Double
- Scope: Public
Y teclea estas líneas de código en el Editor de Código asociado:
If Min = Max Then Max = Min + 1 MinPoint = Min(Min,Max) MaxPoint = Max(Max, Min) Calculate
NiceScale en la Práctica
Vamos a crear ahora la interfaz de usuario para un proyecto Desktop, de modo que podamos ver en funcionamiento la clase NiceScale; y dado que vamos a necesitar algo de dibujado adicional, arrastra un Canvas desde la Librería al Navegador del proyecto. Esto creará una subclase del Canvas. Con la subclase de Canvas recién añadida seleccionada en el Navegador, utiliza el Panel Inspector asociado para definir los siguientes valores:
- Name: ScaleDrawing
Luego, añádele la siguiente propiedad:
- Name: range
- Type: NiceScale
- Scope: Private
Y un nuevo método a nuestra subclase ScaleDrawing usando los siguientes valores:
- Method Name: Constructor
Escribiendo a continuación estas líneas de código en el Editor de Código asociado:
Super.Constructor Self.range = New NiceScale( 0, 1 )
Añade luego un segundo método usando los siguientes valores:
- Method Name: Redraw
- Parameters: minValue As Double, maxValue As Double
Y escribiendo a continuación estas líneas de código en el Editor de Código asociado:
range.SetMinMaxPoints( minValue, maxValue ) Self.Refresh
Después de esto, es momento de dibujar algo en la superficie del canvas. Para ello necesitamos añadir el evento Paint a nuestra subclase ScaleDrawing. Cuando lo hayas hecho, escribe el siguiente código en el Editor de Código asociado:
g.DrawRectangle( 0, 0, g.Width, g.Height) Var values() As Double = range.GetValues Var offset As Double = (( g.Width ) / values.LastIndex) - 10 Var x As Double = 20 Var y As Double = g.Height / 2 - 5 Var tx As Double Var Val As Double For n As Integer = 0 To values.LastIndex Val = values(n) g.DrawLine( x, y, x, y + 10 ) tx = x - g.TextWidth( values(n).ToString ) / 2 g.DrawText( values(n).ToString, tx, y + 10 + g.FontAscent ) x = x + offset Next g.DrawLine( 20, y, x - offset, y )
Creando la UI del Ejemplo NiceScale
Selecciona la ventana Window1 en el Navegador de modo que se muestre en el Editor de Diseño. A continuación, arrastra la clase ScaleDrawing desde el Navegador y suéltala sobre el Editor de Diseño de modo que tenga un aspecto similar al siguiente (cierra los cuatro candados para ScaleDrawing1 en el Panel Inspector):
A continuación, añade un par de Label, un par de TextField y un Button bajo ScaleDrawing1, de modo que el diseño sea similar al mostrado en la siguiente imagen:
Renombra Label1 como MinimumLabel, Label2 como MaximumLabel, TextField1 como MinimumTF, TextField2 como MaximumTF, y Button1 como DrawBT. Luego, añade el evento Pressed a DrawBT y escribe el siguiente código en el Editor de Código asociado:
ScaleDrawing1.Redraw( MinimumTF.Text.ToDouble, maximumtf.Text.ToDouble )
¡Eso es todo!
Ejecutando la App
Ya está todo listo, de modo que puedes ejecutar la app y escribir algunos valores en los campos de texto Minimum y Maximum, haciendo clic a continuación en el botón para refrescar el dibujado. Estos son algunos ejemplos de lo que obtendrás con los siguientes rangos de valores:
- Por defecto:
- Mínimo: 12, Máximo: 112
- Mínimo: -345, Máximo: 835
- Mínimo: -816, Máximo: 25
Como puedes ver, es bueno contar con estas clases en tu bolsa de desarrollador en el caso de que necesites dibujar este tipo de escalas. Por supuesto, probablemente querrás utilizar la clase ScaleDrawing como punto de partida para que se adapte mejor a tus necesidades.