@@ -13,7 +13,10 @@ Date Author Change
1313
1414using EPPlusImageRenderer . RenderItems ;
1515using OfficeOpenXml . Drawing . Chart ;
16+ using OfficeOpenXml . FormulaParsing . Excel . Functions . Finance ;
17+ using OfficeOpenXml . FormulaParsing . Excel . Functions . MathFunctions ;
1618using OfficeOpenXml . Utils . TypeConversion ;
19+ using System ;
1720using System . Collections . Generic ;
1821using System . Linq ;
1922
@@ -27,17 +30,20 @@ public DrawingChart(ExcelChart chart) : base(chart)
2730 }
2831 public ExcelChart Chart { get ; set ; }
2932
30- protected List < object > GetAxisValue ( ExcelChartAxisStandard ax , RenderItem rect )
33+ protected List < object > GetAxisValue ( ExcelChartAxisStandard ax , RenderItem rect , out double ? min , out double ? max , out double ? majorUnit )
3134 {
32- rect . GetBounds ( out double x , out double y , out double w , out double h ) ;
33- var values = ax . GetAxisValues ( ) ;
34- if ( ax . AxisType == eAxisType . Cat )
35+ var values = ax . GetAxisValues ( out bool isCount ) ;
36+ if ( ax . AxisType == eAxisType . Cat &&
37+ isCount == false )
3538 {
39+ min = 0 ;
40+ max = values . Length ;
41+ majorUnit = 1 ;
3642 return values . ToList ( ) ;
3743 }
3844 var l = new List < object > ( ) ;
39- var min = double . MaxValue ;
40- var max = double . MinValue ;
45+ min = double . MaxValue ;
46+ max = double . MinValue ;
4147 foreach ( var v in values )
4248 {
4349 var d = ConvertUtil . GetValueDouble ( v , false , true ) ;
@@ -54,26 +60,122 @@ protected List<object> GetAxisValue(ExcelChartAxisStandard ax, RenderItem rect)
5460 max = d ;
5561 }
5662 }
57- var majorUnit = ax . MajorUnit ?? GetAutoUnit ( min , max ) ;
58-
63+ var maxMajorTickmarks = 10 ; //TODO: Calculate based on rect size
64+ GetAutoMinMaxValue ( ax , maxMajorTickmarks , isCount , ref min , ref max , out majorUnit ) ;
65+ for ( var v = min ; v <= max ; v += majorUnit )
66+ {
67+ l . Add ( v ) ;
68+ }
5969 return l ;
70+ }
71+
72+ private void GetAutoMinMaxValue ( ExcelChartAxisStandard ax , int maxMajorTickmarks , bool isCount , ref double ? min , ref double ? max , out double ? majorUnit )
73+ {
74+ if ( ax . MinValue . HasValue )
75+ {
76+ min = ax . MinValue ;
77+ }
78+ else
79+ {
80+ if ( isCount )
81+ {
82+ min = 1 ;
83+ }
84+ else
85+ {
86+ var diffFromZero = ( max - min ) / max ;
87+ if ( diffFromZero > 0.091 )
88+ {
89+ min = 0 ;
90+ }
91+ }
92+ }
93+
94+ if ( isCount )
95+ {
96+ majorUnit = 1 ;
97+ }
98+ else
99+ {
100+ if ( ax . MaxValue . HasValue )
101+ {
102+ max = ax . MaxValue ;
103+ majorUnit = ax . MajorUnit ?? GetAutoUnit ( min . Value , max . Value ) ;
104+ if ( ax . MinValue . HasValue == false )
105+ {
106+ var newMin = max - majorUnit ;
107+ while ( newMin > min )
108+ {
109+ newMin -= majorUnit . Value ;
110+ }
111+ min = newMin ;
112+ }
113+ }
114+ else
115+ {
116+ majorUnit = ax . MajorUnit ?? GetAutoUnit ( min . Value , max . Value ) ;
117+ if ( isCount == false )
118+ {
119+ var diff = max . Value - min . Value ;
120+ var newMax = min . Value + majorUnit ;
121+ while ( ( newMax - min ) < ( diff * 1.05 ) )
122+ {
123+ newMax += majorUnit . Value ;
124+ }
125+ max = newMax ;
126+ }
127+ if ( min != 0 && max - min < 9 )
128+ {
129+ min -= 2 ;
130+ }
131+ }
132+ var newUnit = majorUnit ;
133+ while ( newUnit >= 2 && ( max - min ) / newUnit > maxMajorTickmarks )
134+ {
135+ newUnit /= 2 ;
136+ }
137+ }
60138 }
61139
62140 private double GetAutoUnit ( double min , double max )
63141 {
64142 var diff = max - min ;
65-
66- var unit = diff / 10 ;
67- var rest = unit % 10d ;
68- if ( rest < 5 )
143+ if ( diff < 8 )
69144 {
70- unit -= rest ;
145+ return 1 ;
71146 }
72147 else
73148 {
74- unit += 10 - rest ;
149+ var rawMajorUnit = diff ;
150+ var exponent = Math . Floor ( Math . Log10 ( rawMajorUnit ) ) ;
151+ var fraction = rawMajorUnit / ( Math . Pow ( 10 , exponent ) ) ;
152+ double unit ;
153+ if ( fraction <= 1 )
154+ {
155+ unit = 1D ;
156+ }
157+ else if ( fraction <= 2 )
158+ {
159+ unit = 2 ;
160+ }
161+ else if ( fraction <= 2.5 )
162+ {
163+ unit = 2.5 ;
164+ }
165+ else if ( fraction <= 5 )
166+ {
167+ unit = 5 ;
168+ }
169+ else
170+ {
171+ unit = 10 ;
172+ }
173+
174+ var axMax = unit * Math . Pow ( 10 , exponent ) ;
175+ var axMin = Math . Floor ( min / axMax ) * axMax ;
176+ axMax = Math . Ceiling ( max / axMax ) * axMax ;
177+ return axMax / 10 ;
75178 }
76- return unit ;
77179 }
78180 }
79181}
0 commit comments