[chronojump] Refactorized cairo grid from xy to generic to be used on bars



commit c69eea68a224cc8eeb472b64208d08d154a229fa
Author: Xavier de Blas <xaviblas gmail com>
Date:   Fri Jul 16 19:24:32 2021 +0200

    Refactorized cairo grid from xy to generic to be used on bars

 src/gui/cairo/bars.cs               |  37 ++++++--
 src/gui/cairo/generic.cs            | 174 ++++++++++++++++++++++++++++++++++++
 src/gui/cairo/jumpsRunsEvolution.cs |   6 +-
 src/gui/cairo/raceAnalyzer.cs       |   2 +-
 src/gui/cairo/xy.cs                 | 172 +----------------------------------
 5 files changed, 211 insertions(+), 180 deletions(-)
---
diff --git a/src/gui/cairo/bars.cs b/src/gui/cairo/bars.cs
index a4f000ab5..eb28e3442 100644
--- a/src/gui/cairo/bars.cs
+++ b/src/gui/cairo/bars.cs
@@ -38,7 +38,6 @@ public abstract class CairoBars : CairoGeneric
        protected Cairo.Color colorBackground;
 
        protected Cairo.Context g;
-       protected const int textHeight = 14;
        protected int lineWidthDefault = 2;
        protected string xVariable = "";
        protected string yVariable = "Height";
@@ -49,8 +48,6 @@ public abstract class CairoBars : CairoGeneric
        protected double maxX = 0;
        protected double minY = 1000000;
        protected double maxY = 0;
-       protected int graphWidth;
-       protected int graphHeight;
 
        protected Cairo.Color black;
        protected Cairo.Color gray99;
@@ -127,13 +124,11 @@ public abstract class CairoBars : CairoGeneric
                g.LineTo(outerMargins, graphHeight - outerMargins);
                g.LineTo(graphWidth - outerMargins, graphHeight - outerMargins);
                g.Stroke ();
-               
-               g.SetFontSize(textHeight -2);
+
                printText(2, Convert.ToInt32(outerMargins/2), 0, textHeight, getYAxisLabel(), g, 
alignTypes.LEFT);
                printXAxisText();
                g.Stroke ();
 
-               g.SetFontSize(textHeight);
                g.LineWidth = lineWidthDefault;
        }
 
@@ -159,7 +154,14 @@ public abstract class CairoBars : CairoGeneric
                return string.Format("{0} ({1})", variable, units);
        }
 
-       protected double calculatePaintY (double realY)
+       //TODO: check if for one value this is /0
+       protected override double calculatePaintX (double realX)
+       {
+               return outerMargins + (realX - minX) * UtilAll.DivideSafe(
+                               graphWidth - 2*outerMargins,
+                               maxX - minX);
+        }
+       protected override double calculatePaintY (double realY)
        {
                 return graphHeight - outerMargins - UtilAll.DivideSafe(
                                (realY - minY) * (graphHeight - 2*outerMargins),
@@ -319,6 +321,18 @@ public abstract class CairoBars : CairoGeneric
                                title, g, alignTypes.LEFT);
        }
        */
+
+       //reccomended to 1st paint the grid, then the axis
+       protected void paintGrid(gridTypes gridType, bool niceAutoValues)
+       {
+               g.LineWidth = 1; //to allow to be shown the red arrows on jumpsWeightFVProfile
+
+               if(niceAutoValues)
+                       paintGridNiceAutoValues (g, minX, maxX, minY, maxY, 5, gridType);
+               else
+                       paintGridInt (g, minX, maxX, minY, maxY, 1, gridType);
+       }
+
 }
 
 public class CairoBarsJustTesting : CairoBars
@@ -329,7 +343,7 @@ public class CairoBarsJustTesting : CairoBars
                this.area = area;
 
                LogB.Information("area is null:" + (area == null).ToString());
-               initGraph(font, .8);
+               initGraph(font, 1); //.8 to have title at right
 
                endGraphDisposing(g);
        }
@@ -348,10 +362,17 @@ public class CairoBarsJustTesting : CairoBars
        public override void Do(string font)
        {
                LogB.Information("at CairoBarsJustTesting.Do");
+
+               textHeight = 14;
+
                initGraph(font, 1); //.8 if writeTextAtRight
 
                 findPointMaximums();
+
+               g.SetFontSize(textHeight -2);
                paintAxis(2);
+               paintGrid(gridTypes.HORIZONTALLINES, true);
+               g.SetFontSize(textHeight);
 
                g.Color = black;
                plotBars();
diff --git a/src/gui/cairo/generic.cs b/src/gui/cairo/generic.cs
index 5c2e7a2ed..63ad2e5c8 100644
--- a/src/gui/cairo/generic.cs
+++ b/src/gui/cairo/generic.cs
@@ -25,11 +25,15 @@ using Cairo;
 
 public abstract class CairoGeneric
 {
+       protected int graphWidth;
+       protected int graphHeight;
+
        //for all 4 sides
        protected int outerMargins = 40; //blank space outside the axis.
        protected int innerMargins = 30; //space between the axis and the real coordinates.
 
        protected string font;
+       protected int textHeight = 12;
 
        /*
           need to dispose because Cairo does not clean ok on win and mac:
@@ -54,6 +58,16 @@ public abstract class CairoGeneric
                return new Cairo.Color(color.Red/65536.0, color.Green/65536.0, color.Blue/65536.0);
        }
 
+       //not abstract because on radial they are not defined
+       protected virtual double calculatePaintX (double realX)
+       {
+               return 0;
+       }
+       protected virtual double calculatePaintY (double realY)
+       {
+               return 0;
+       }
+
        protected enum alignTypes { LEFT, CENTER, RIGHT }
        protected void printText (int x, int y, int height, int textHeight, string text, Cairo.Context g, 
alignTypes align)
        {
@@ -73,5 +87,165 @@ public abstract class CairoGeneric
                g.ShowText(text);
        }
 
+       //TODO: fix if min == max (crashes)
+       protected enum gridTypes { BOTH, HORIZONTALLINES, VERTICALLINES }
+       protected void paintGridNiceAutoValues (Cairo.Context g, double minX, double maxX, double minY, 
double maxY, int seps, gridTypes gridType)
+       {
+               var gridXTuple = getGridStepAndBoundaries ((decimal) minX, (decimal) maxX, seps);
+               var gridYTuple = getGridStepAndBoundaries ((decimal) minY, (decimal) maxY, seps);
+
+               g.Save();
+               g.SetDash(new double[]{1, 2}, 0);
+               if(gridType != gridTypes.HORIZONTALLINES)
+                       for(double i = gridXTuple.Item1; i <= gridXTuple.Item2 ; i += gridXTuple.Item3)
+                       {
+                               int xtemp = Convert.ToInt32(calculatePaintX(i));
+                               if(xtemp <= outerMargins || xtemp >= graphWidth - outerMargins)
+                                       continue;
+
+                               paintVerticalGridLine(g, xtemp, Util.TrimDecimals(i, 2));
+                       }
+
+               if(gridType != gridTypes.VERTICALLINES)
+                       for(double i = gridYTuple.Item1; i <= gridYTuple.Item2 ; i += gridYTuple.Item3)
+                       {
+                               int ytemp = Convert.ToInt32(calculatePaintY(i));
+                               if(ytemp <= outerMargins || ytemp >= graphHeight - outerMargins)
+                                       continue;
+
+                               paintHorizontalGridLine(g, ytemp, Util.TrimDecimals(i, 2));
+                       }
+               g.Stroke ();
+               g.Restore();
+       }
+
+       //for a grid of integers
+       protected void paintGridInt (Cairo.Context g, double minX, double maxX, double minY, double maxY, int 
by, gridTypes gridType)
+       {
+               g.Save();
+               g.SetDash(new double[]{1, 2}, 0);
+               if(gridType != gridTypes.HORIZONTALLINES)
+                       for(double i = Math.Floor(minX); i <= Math.Ceiling(maxX) ; i += by)
+                       {
+                               int xtemp = Convert.ToInt32(calculatePaintX(i));
+                               if(xtemp <= outerMargins || xtemp >= graphWidth - outerMargins)
+                                       continue;
+
+                               paintVerticalGridLine(g, xtemp, Util.TrimDecimals(i, 2));
+                       }
+
+               if(gridType != gridTypes.VERTICALLINES)
+                       for(double i = Math.Floor(minX); i <= Math.Ceiling(maxY) ; i += by)
+                       {
+                               int ytemp = Convert.ToInt32(calculatePaintY(i));
+                               if(ytemp <= outerMargins || ytemp >= graphHeight - outerMargins)
+                                       continue;
+
+                               paintHorizontalGridLine(g, ytemp, Util.TrimDecimals(i, 2));
+                       }
+               g.Stroke ();
+               g.Restore();
+       }
+
+       protected void paintHorizontalGridLine(Cairo.Context g, int ytemp, string text)
+       {
+               g.MoveTo(outerMargins, ytemp);
+               g.LineTo(graphWidth - outerMargins, ytemp);
+               printText(Convert.ToInt32(outerMargins/2), ytemp, 0, textHeight, text, g, alignTypes.CENTER);
+       }
+       //this combined with printXAxisText is different on RaceAnalyzer
+       protected virtual void paintVerticalGridLine(Cairo.Context g, int xtemp, string text)
+       {
+               g.MoveTo(xtemp, graphHeight - outerMargins);
+               g.LineTo(xtemp, outerMargins);
+               printText(xtemp, graphHeight - Convert.ToInt32(outerMargins/2), 0, textHeight, text, g, 
alignTypes.CENTER);
+       }
+
+       /*
+        * adapted to not used LinQ from:
+        * https://stackoverflow.com/questions/237220/tickmark-algorithm-for-a-graph-axis
+        *
+        * thanks to: Andrew
+        */
+       //private static Tuple<decimal, decimal, decimal> getGridStepAndBoundaries (decimal min, decimal max, 
int stepCount)
+       private static Tuple<double, double, double> getGridStepAndBoundaries (decimal min, decimal max, int 
stepCount)
+       {
+               // Minimal increment to avoid round extreme values to be on the edge of the chart
+               decimal epsilon = (max - min) / 1e6m;
+               max += epsilon;
+               min -= epsilon;
+               decimal range = max - min;
+
+               // Target number of values to be displayed on the Y axis (it may be less)
+               //int stepCount = 10;
+               // First approximation
+               decimal roughStep = range / (stepCount - 1);
+
+               // Set best step for the range
+               decimal[] goodNormalizedSteps = { 1, 1.5m, 2, 2.5m, 5, 7.5m, 10 }; // keep the 10 at the end
+               // Or use these if you prefer:  { 1, 2, 5, 10 };
+
+               // Normalize rough step to find the normalized one that fits best
+               decimal stepPower = (decimal)Math.Pow(10, 
-Math.Floor(Math.Log10((double)Math.Abs(roughStep))));
+               var normalizedStep = roughStep * stepPower;
+
+               //this uses Linq
+               //var goodNormalizedStep = goodNormalizedSteps.First(n => n >= normalizedStep);
+
+               //without Linq
+               var goodNormalizedStep = LikeLinQFirst(goodNormalizedSteps, normalizedStep);
+
+               decimal step = goodNormalizedStep / stepPower;
+
+               // Determine the scale limits based on the chosen step.
+               decimal scaleMax = Math.Ceiling(max / step) * step;
+               decimal scaleMin = Math.Floor(min / step) * step;
+
+               //return new Tuple<decimal, decimal, decimal>(scaleMin, scaleMax, step);
+               return new Tuple<double, double, double>(Convert.ToDouble(scaleMin), 
Convert.ToDouble(scaleMax), Convert.ToDouble(step));
+       }
+       private static decimal LikeLinQFirst(decimal [] goodNormalizedSteps, decimal normalizedStep)
+       {
+               //Console.WriteLine(string.Format("normalizedStep: {0}", normalizedStep));
+               foreach(var item in goodNormalizedSteps)
+               {
+                       Console.WriteLine(item);
+                       if(item >= normalizedStep)
+                               return item;
+               }
+
+               return goodNormalizedSteps[0];
+       }
+
+       /*
+        * pathetical old method
+        *
+       private double getGridStep(double min, double max, int seps)
+       {
+               //show 5 steps positive, 5 negative (if possible)
+               double temp = UtilAll.DivideSafe(max - min, seps);
+               double step = temp;
+
+               //to have values multiples than 10, 100 ...
+               if(step == 0) //fix crash when no force
+                       step = 1;
+               else if(step <= 1) //do nothing
+                       step = .2;
+               else if(step <= 3)
+                       step = 1;
+               else if(step <= 10)
+                       step = 5;
+               else if(step <= 100)
+                       step = temp - (temp % 10);
+               else if(step <= 1000)
+                       step = temp - (temp % 100);
+               else if(step <= 10000)
+                       step = temp - (temp % 1000);
+               else //if(step <= 100000)
+                       step = temp - (temp % 10000);
+
+               return step;
+       }
+       */
 
 }
diff --git a/src/gui/cairo/jumpsRunsEvolution.cs b/src/gui/cairo/jumpsRunsEvolution.cs
index 5d2992d37..947c67af9 100644
--- a/src/gui/cairo/jumpsRunsEvolution.cs
+++ b/src/gui/cairo/jumpsRunsEvolution.cs
@@ -81,9 +81,9 @@ public abstract class EvolutionGraph : CairoXY
                        if( ! (xtemp < outerMargins || xtemp > graphWidth - outerMargins) )
                        {
                                if(paintMonths)
-                                       paintVerticalGridLine(xtemp, string.Format("{0} {1}", year, 
UtilDate.GetMonthName(0, true)));
+                                       paintVerticalGridLine(g, xtemp, string.Format("{0} {1}", year, 
UtilDate.GetMonthName(0, true)));
                                else
-                                       paintVerticalGridLine(xtemp, year.ToString());
+                                       paintVerticalGridLine(g, xtemp, year.ToString());
                        }
 
                        if(! paintMonths)
@@ -103,7 +103,7 @@ public abstract class EvolutionGraph : CairoXY
                                if(xtemp < outerMargins || xtemp > graphWidth - outerMargins)
                                        continue;
 
-                               paintVerticalGridLine(xtemp, string.Format("{0} {1}", year, 
UtilDate.GetMonthName(month, true)));
+                               paintVerticalGridLine(g, xtemp, string.Format("{0} {1}", year, 
UtilDate.GetMonthName(month, true)));
                        }
                }
 
diff --git a/src/gui/cairo/raceAnalyzer.cs b/src/gui/cairo/raceAnalyzer.cs
index d431a6ab8..679c15bb2 100644
--- a/src/gui/cairo/raceAnalyzer.cs
+++ b/src/gui/cairo/raceAnalyzer.cs
@@ -118,7 +118,7 @@ public class CairoGraphRaceAnalyzer : CairoXY
        {
                printText(graphWidth - outerMargins, graphHeight -Convert.ToInt32(.25 * outerMargins), 0, 
textHeight, getXAxisLabel(), g, alignTypes.CENTER);
        }
-       protected override void paintVerticalGridLine(int xtemp, string text)
+       protected override void paintVerticalGridLine(Cairo.Context g, int xtemp, string text)
        {
                g.MoveTo(xtemp, graphHeight - outerMargins);
                g.LineTo(xtemp, outerMargins);
diff --git a/src/gui/cairo/xy.cs b/src/gui/cairo/xy.cs
index 15e67b300..57f5eee0b 100644
--- a/src/gui/cairo/xy.cs
+++ b/src/gui/cairo/xy.cs
@@ -55,7 +55,6 @@ public abstract class CairoXY : CairoGeneric
        protected Cairo.Color colorBackground;
 
        protected Cairo.Context g;
-       protected const int textHeight = 12;
        protected int pointsRadius = 6;
        protected string xVariable = "";
        protected string yVariable = "";
@@ -69,8 +68,6 @@ public abstract class CairoXY : CairoGeneric
        double yAtMMaxY;
        protected double absoluteMaxX;
        protected double absoluteMaxY;
-       protected int graphWidth;
-       protected int graphHeight;
 
        protected Cairo.Color black;
        protected Cairo.Color gray99;
@@ -342,9 +339,9 @@ public abstract class CairoXY : CairoGeneric
                g.LineWidth = 1; //to allow to be shown the red arrows on jumpsWeightFVProfile
 
                if(niceAutoValues)
-                       paintGridNiceAutoValues (minX, absoluteMaxX, minY, absoluteMaxY, 5, gridType);
+                       paintGridNiceAutoValues (g, minX, absoluteMaxX, minY, absoluteMaxY, 5, gridType);
                else
-                       paintGridInt (minX, absoluteMaxX, minY, absoluteMaxY, 1, gridType);
+                       paintGridInt (g, minX, absoluteMaxX, minY, absoluteMaxY, 1, gridType);
        }
 
        protected void paintAxis()
@@ -666,175 +663,14 @@ public abstract class CairoXY : CairoGeneric
                writeTextAtRight(line +2, string.Format("- {0}: {1} {2}", yVariable, 
Util.TrimDecimals(pClosest.Y, 2), yUnits), false);
        }
 
-       //TODO: fix if min == max (crashes)
-       protected enum gridTypes { BOTH, HORIZONTALLINES, VERTICALLINES }
-       protected void paintGridNiceAutoValues (double minX, double maxX, double minY, double maxY, int seps, 
gridTypes gridType)
-       {
-               var gridXTuple = getGridStepAndBoundaries ((decimal) minX, (decimal) maxX, seps);
-               var gridYTuple = getGridStepAndBoundaries ((decimal) minY, (decimal) maxY, seps);
-
-               g.Save();
-               g.SetDash(new double[]{1, 2}, 0);
-               if(gridType != gridTypes.HORIZONTALLINES)
-                       for(double i = gridXTuple.Item1; i <= gridXTuple.Item2 ; i += gridXTuple.Item3)
-                       {
-                               int xtemp = Convert.ToInt32(calculatePaintX(i));
-                               if(xtemp <= outerMargins || xtemp >= graphWidth - outerMargins)
-                                       continue;
-
-                               paintVerticalGridLine(xtemp, Util.TrimDecimals(i, 2));
-                       }
-
-               if(gridType != gridTypes.VERTICALLINES)
-                       for(double i = gridYTuple.Item1; i <= gridYTuple.Item2 ; i += gridYTuple.Item3)
-                       {
-                               int ytemp = Convert.ToInt32(calculatePaintY(i));
-                               if(ytemp <= outerMargins || ytemp >= graphHeight - outerMargins)
-                                       continue;
-
-                               paintHorizontalGridLine(ytemp, Util.TrimDecimals(i, 2));
-                       }
-               g.Stroke ();
-               g.Restore();
-       }
-
-       //for a grid of integers
-       protected void paintGridInt (double minX, double maxX, double minY, double maxY, int by, gridTypes 
gridType)
-       {
-               g.Save();
-               g.SetDash(new double[]{1, 2}, 0);
-               if(gridType != gridTypes.HORIZONTALLINES)
-                       for(double i = Math.Floor(minX); i <= Math.Ceiling(maxX) ; i += by)
-                       {
-                               int xtemp = Convert.ToInt32(calculatePaintX(i));
-                               if(xtemp <= outerMargins || xtemp >= graphWidth - outerMargins)
-                                       continue;
-
-                               paintVerticalGridLine(xtemp, Util.TrimDecimals(i, 2));
-                       }
-
-               if(gridType != gridTypes.VERTICALLINES)
-                       for(double i = Math.Floor(minX); i <= Math.Ceiling(maxY) ; i += by)
-                       {
-                               int ytemp = Convert.ToInt32(calculatePaintY(i));
-                               if(ytemp <= outerMargins || ytemp >= graphHeight - outerMargins)
-                                       continue;
-
-                               paintHorizontalGridLine(ytemp, Util.TrimDecimals(i, 2));
-                       }
-               g.Stroke ();
-               g.Restore();
-       }
-
-       protected void paintHorizontalGridLine(int ytemp, string text)
-       {
-               g.MoveTo(outerMargins, ytemp);
-               g.LineTo(graphWidth - outerMargins, ytemp);
-               printText(Convert.ToInt32(outerMargins/2), ytemp, 0, textHeight, text, g, alignTypes.CENTER);
-       }
-       //this combined with printXAxisText is different on RaceAnalyzer
-       protected virtual void paintVerticalGridLine(int xtemp, string text)
-       {
-               g.MoveTo(xtemp, graphHeight - outerMargins);
-               g.LineTo(xtemp, outerMargins);
-               printText(xtemp, graphHeight - Convert.ToInt32(outerMargins/2), 0, textHeight, text, g, 
alignTypes.CENTER);
-       }
-
-
-       /*
-        * adapted to not used LinQ from:
-        * https://stackoverflow.com/questions/237220/tickmark-algorithm-for-a-graph-axis
-        *
-        * thanks to: Andrew
-        */
-       //private static Tuple<decimal, decimal, decimal> getGridStepAndBoundaries (decimal min, decimal max, 
int stepCount)
-       private static Tuple<double, double, double> getGridStepAndBoundaries (decimal min, decimal max, int 
stepCount)
-       {
-               // Minimal increment to avoid round extreme values to be on the edge of the chart
-               decimal epsilon = (max - min) / 1e6m;
-               max += epsilon;
-               min -= epsilon;
-               decimal range = max - min;
-
-               // Target number of values to be displayed on the Y axis (it may be less)
-               //int stepCount = 10;
-               // First approximation
-               decimal roughStep = range / (stepCount - 1);
-
-               // Set best step for the range
-               decimal[] goodNormalizedSteps = { 1, 1.5m, 2, 2.5m, 5, 7.5m, 10 }; // keep the 10 at the end
-               // Or use these if you prefer:  { 1, 2, 5, 10 };
-
-               // Normalize rough step to find the normalized one that fits best
-               decimal stepPower = (decimal)Math.Pow(10, 
-Math.Floor(Math.Log10((double)Math.Abs(roughStep))));
-               var normalizedStep = roughStep * stepPower;
-
-               //this uses Linq
-               //var goodNormalizedStep = goodNormalizedSteps.First(n => n >= normalizedStep);
-
-               //without Linq
-               var goodNormalizedStep = LikeLinQFirst(goodNormalizedSteps, normalizedStep);
-
-               decimal step = goodNormalizedStep / stepPower;
-
-               // Determine the scale limits based on the chosen step.
-               decimal scaleMax = Math.Ceiling(max / step) * step;
-               decimal scaleMin = Math.Floor(min / step) * step;
-
-               //return new Tuple<decimal, decimal, decimal>(scaleMin, scaleMax, step);
-               return new Tuple<double, double, double>(Convert.ToDouble(scaleMin), 
Convert.ToDouble(scaleMax), Convert.ToDouble(step));
-       }
-       private static decimal LikeLinQFirst(decimal [] goodNormalizedSteps, decimal normalizedStep)
-       {
-               //Console.WriteLine(string.Format("normalizedStep: {0}", normalizedStep));
-               foreach(var item in goodNormalizedSteps)
-               {
-                       Console.WriteLine(item);
-                       if(item >= normalizedStep)
-                               return item;
-               }
-
-               return goodNormalizedSteps[0];
-       }
-
-       /*
-        * pathetical old method
-        *
-       private double getGridStep(double min, double max, int seps)
-       {
-               //show 5 steps positive, 5 negative (if possible)
-               double temp = UtilAll.DivideSafe(max - min, seps);
-               double step = temp;
-
-               //to have values multiples than 10, 100 ...
-               if(step == 0) //fix crash when no force
-                       step = 1;
-               else if(step <= 1) //do nothing
-                       step = .2;
-               else if(step <= 3)
-                       step = 1;
-               else if(step <= 10)
-                       step = 5;
-               else if(step <= 100)
-                       step = temp - (temp % 10);
-               else if(step <= 1000)
-                       step = temp - (temp % 100);
-               else if(step <= 10000)
-                       step = temp - (temp % 1000);
-               else //if(step <= 100000)
-                       step = temp - (temp % 10000);
-
-               return step;
-       }
-       */
 
        //TODO: check if for one value this is /0
-       protected double calculatePaintX (double realX)
+       protected override double calculatePaintX (double realX)
        {
                 return totalMargins + (realX - minX) * (graphWidth - totalMargins - totalMargins) / 
(absoluteMaxX - minX);
         }
        //TODO: check if for one value this is /0
-       protected double calculatePaintY (double realY)
+       protected override double calculatePaintY (double realY)
        {
                 return graphHeight - totalMargins - ((realY - minY) * (graphHeight - totalMargins - 
totalMargins) / (absoluteMaxY - minY));
         }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]