[chronojump] Refactorized cairo grid from xy to generic to be used on bars
- From: Xavier de Blas <xaviblas src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [chronojump] Refactorized cairo grid from xy to generic to be used on bars
- Date: Fri, 16 Jul 2021 17:25:48 +0000 (UTC)
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]