00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052 using System;
00053 using System.Drawing;
00054 using System.Collections;
00055 using System.Text;
00056
00057 namespace NPlot
00058 {
00062 public class LogAxis : Axis
00063 {
00064
00069 public override object Clone()
00070 {
00071 LogAxis a = new LogAxis();
00072 if (this.GetType() != a.GetType())
00073 {
00074 throw new NPlotException("Clone not defined in derived type. Help!");
00075 }
00076 this.DoClone( this, a );
00077 return a;
00078 }
00079
00080
00086 protected void DoClone(LogAxis b, LogAxis a)
00087 {
00088 Axis.DoClone(b,a);
00089
00090 a.numberSmallTicks_ = b.numberSmallTicks_;
00091 a.largeTickValue_ = b.largeTickValue_;
00092 a.largeTickStep_ = b.largeTickStep_;
00093 }
00094
00095
00099 public LogAxis()
00100 : base()
00101 {
00102 Init();
00103 }
00104
00105
00110 public LogAxis(Axis a)
00111 : base(a)
00112 {
00113 Init();
00114 }
00115
00116
00122 public LogAxis(double worldMin, double worldMax)
00123 : base( worldMin, worldMax )
00124 {
00125 Init();
00126 }
00127
00128
00129 private void Init()
00130 {
00131 this.NumberFormat = "{0:g5}";
00132 }
00133
00134
00145 protected override void DrawTicks(
00146 Graphics g,
00147 Point physicalMin,
00148 Point physicalMax,
00149 out object labelOffset,
00150 out object boundingBox )
00151 {
00152
00153 Point tLabelOffset;
00154 Rectangle tBoundingBox;
00155
00156 labelOffset = this.getDefaultLabelOffset( physicalMin, physicalMax );
00157 boundingBox = null;
00158
00159 ArrayList largeTickPositions;
00160 ArrayList smallTickPositions;
00161 this.WorldTickPositions( physicalMin, physicalMax, out largeTickPositions, out smallTickPositions );
00162
00163 Point offset = new Point( 0, 0 );
00164 object bb = null;
00165
00166 if (largeTickPositions.Count > 0)
00167 {
00168 for (int i=0; i<largeTickPositions.Count; ++i)
00169 {
00170 StringBuilder label = new StringBuilder();
00171
00172 label.AppendFormat(this.NumberFormat, (double)largeTickPositions[i]);
00173 this.DrawTick( g, (double)largeTickPositions[i], this.LargeTickSize, label.ToString(),
00174 new Point(0,0), physicalMin, physicalMax, out tLabelOffset, out tBoundingBox );
00175
00176 Axis.UpdateOffsetAndBounds( ref labelOffset, ref boundingBox, tLabelOffset, tBoundingBox );
00177 }
00178 }
00179 else
00180 {
00181
00182 PointF dir = Utils.UnitVector(physicalMin,physicalMax);
00183 Rectangle rr = new Rectangle( physicalMin.X,
00184 (int)((physicalMax.X-physicalMin.X)*dir.X),
00185 physicalMin.Y,
00186 (int)((physicalMax.Y-physicalMin.Y)*dir.Y) );
00187 bb = rr;
00188 }
00189
00190
00191
00192 if (smallTickPositions.Count > 0)
00193 {
00194 for (int i=0; i<smallTickPositions.Count; ++i)
00195 {
00196 this.DrawTick( g, (double)smallTickPositions[i], this.SmallTickSize,
00197 "", new Point(0,0), physicalMin, physicalMax, out tLabelOffset, out tBoundingBox );
00198
00199 }
00200 }
00201
00202 }
00203
00204
00213 internal override void WorldTickPositions_SecondPass(
00214 Point physicalMin,
00215 Point physicalMax,
00216 ArrayList largeTickPositions,
00217 ref ArrayList smallTickPositions )
00218 {
00219
00220 if (smallTickPositions != null)
00221 {
00222 throw new NPlotException( "not expecting smallTickPositions to be set already." );
00223 }
00224
00225 smallTickPositions = new ArrayList();
00226
00227
00228 double bigTickSpacing = this.DetermineTickSpacing();
00229 int nSmall = this.DetermineNumberSmallTicks( bigTickSpacing );
00230
00231
00232
00233
00234 if ( bigTickSpacing > 1.0f )
00235 {
00236 if (largeTickPositions.Count > 0)
00237 {
00238
00239
00240 double pos1 = (double)largeTickPositions[0];
00241 while (pos1 > this.WorldMin)
00242 {
00243 pos1 = pos1 / 10.0f;
00244 smallTickPositions.Add( pos1 );
00245 }
00246
00247 for (int i=0; i<largeTickPositions.Count; ++i )
00248 {
00249 double pos = (double)largeTickPositions[i];
00250 for (int j=1; j<=nSmall; ++j )
00251 {
00252 pos=pos*10.0F;
00253
00254 if (pos < WorldMax)
00255 {
00256 smallTickPositions.Add( pos );
00257 }
00258 }
00259 }
00260 }
00261 }
00262 else
00263 {
00264
00265 double [] m = { 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f };
00266
00267 if (largeTickPositions.Count > 0)
00268 {
00269
00270
00271 double pos1=(double)largeTickPositions[0]/10.0f;
00272 for (int i=0; i<m.Length; i++)
00273 {
00274 double pos=pos1*m[i];
00275 if (pos>this.WorldMin)
00276 {
00277 smallTickPositions.Add(pos);
00278 }
00279 }
00280
00281 for (int i=0; i<largeTickPositions.Count; ++i )
00282 {
00283 pos1=(double)largeTickPositions[i];
00284 for (int j=0; j<m.Length; ++j )
00285 {
00286 double pos=pos1*m[j];
00287
00288 if (pos < WorldMax)
00289 {
00290 smallTickPositions.Add( pos );
00291 }
00292 }
00293 }
00294 }
00295 else
00296 {
00297
00298
00299 double dec=Math.Floor(Math.Log10(WorldMin));
00300 double pos1=Math.Pow(10.0,dec);
00301 for (int i=0; i<m.Length; i++)
00302 {
00303 double pos=pos1*m[i];
00304 if (pos>this.WorldMin && pos< this.WorldMax )
00305 {
00306 smallTickPositions.Add(pos);
00307 }
00308 }
00309 }
00310 }
00311
00312 }
00313
00314 private static double m_d5Log = -Math.Log10(0.5);
00315 private static double m_d5RegionPos = Math.Abs(m_d5Log + ((1 - m_d5Log) / 2));
00316 private static double m_d5RegionNeg = Math.Abs(m_d5Log / 2);
00317
00318 private void CalcGrids( double dLenAxis, int nNumDivisions, ref double dDivisionInterval)
00319 {
00320 double dMyInterval = dLenAxis / nNumDivisions;
00321 double dPower = Math.Log10(dMyInterval);
00322 dDivisionInterval = 10 ^ (int)dPower;
00323 double dFixPower = dPower - (int)dPower;
00324 double d5Region = Math.Abs(dPower - dFixPower);
00325 double dMyMult;
00326 if (dPower < 0)
00327 {
00328 d5Region = -(dPower - dFixPower);
00329 dMyMult = 0.5;
00330 }
00331 else
00332 {
00333 d5Region = 1 - (dPower - dFixPower);
00334 dMyMult = 5;
00335 }
00336 if ((d5Region >= m_d5RegionNeg) && (d5Region <= m_d5RegionPos))
00337 {
00338 dDivisionInterval = dDivisionInterval * dMyMult;
00339 }
00340 }
00341
00349 internal override void WorldTickPositions_FirstPass(
00350 Point physicalMin,
00351 Point physicalMax,
00352 out ArrayList largeTickPositions,
00353 out ArrayList smallTickPositions
00354 )
00355 {
00356
00357 smallTickPositions = null;
00358 largeTickPositions = new ArrayList();
00359
00360 if ( double.IsNaN(WorldMin) || double.IsNaN(WorldMax) )
00361 {
00362 throw new NPlotException( "world extent of axis not set." );
00363 }
00364
00365 double roundTickDist = this.DetermineTickSpacing( );
00366
00367
00368 double first = 0.0f;
00369
00370
00371 if (double.IsNaN(largeTickValue_))
00372 {
00373 if( WorldMin > 0.0 )
00374 {
00375
00376 double nToFirst = Math.Floor(Math.Log10(WorldMin) / roundTickDist)+1.0f;
00377 first = nToFirst * roundTickDist;
00378 }
00379
00380
00381 if (first-roundTickDist >= Math.Log10(WorldMin))
00382 {
00383 first -= roundTickDist;
00384 }
00385 }
00386
00387
00388 else
00389 {
00390 first = Math.Log10( this.LargeTickValue );
00391
00392
00393
00394 while (first < Math.Log10(WorldMin))
00395 {
00396 first += roundTickDist;
00397 }
00398
00399 while (first > Math.Log10(WorldMin)+roundTickDist)
00400 {
00401 first -= roundTickDist;
00402 }
00403 }
00404
00405 double mark = first;
00406 while (mark <= Math.Log10(WorldMax))
00407 {
00408
00409
00410 double val;
00411 val = Math.Pow( 10.0, mark );
00412 largeTickPositions.Add( val );
00413 mark += roundTickDist;
00414 }
00415
00416 }
00417
00418
00423 private double DetermineTickSpacing( )
00424 {
00425 if ( double.IsNaN(WorldMin) || double.IsNaN(WorldMax) )
00426 {
00427 throw new NPlotException( "world extent of axis is not set." );
00428 }
00429
00430
00431 if ( !double.IsNaN( this.largeTickStep_) )
00432 {
00433 if ( this.largeTickStep_ <= 0.0f )
00434 {
00435 throw new NPlotException( "can't have negative tick step - reverse WorldMin WorldMax instead." );
00436 }
00437
00438 return this.largeTickStep_;
00439 }
00440
00441 double MagRange = (double)(Math.Floor(Math.Log10(WorldMax)) - Math.Floor(Math.Log10(WorldMin))+1.0);
00442
00443 if ( MagRange > 0.0 )
00444 {
00445
00446
00447
00448
00449 double roundTickDist=1.0F;
00450 int nticks=(int)(MagRange/roundTickDist);
00451 while (nticks > 10)
00452 {
00453 roundTickDist++;
00454 nticks=(int)(MagRange/roundTickDist);
00455 }
00456 return roundTickDist;
00457 }
00458 else
00459 {
00460 return 0.0f;
00461 }
00462 }
00463
00464
00470 private int DetermineNumberSmallTicks( double bigTickDist )
00471 {
00472
00473
00474 if (this.numberSmallTicks_ != null && bigTickDist == 1.0f)
00475 {
00476 return (int)this.numberSmallTicks_+1;
00477 }
00478
00479
00480
00481
00482 if (bigTickDist == 1.0f)
00483 {
00484 return 8;
00485 }
00486
00487 else if (bigTickDist > 1.0f)
00488 {
00489 return (int)bigTickDist - 1;
00490 }
00491 else
00492 {
00493 throw new NPlotException("Wrong Major tick distance setting");
00494 }
00495 }
00496
00497
00501 public double LargeTickStep
00502 {
00503 set
00504 {
00505 largeTickStep_ = value;
00506 }
00507 get
00508 {
00509 return largeTickStep_;
00510 }
00511 }
00512
00513
00517 public double LargeTickValue
00518 {
00519 set
00520 {
00521 largeTickValue_ = value;
00522 }
00523 get
00524 {
00525 return largeTickValue_;
00526 }
00527 }
00528
00529
00533 public int NumberSmallTicks
00534 {
00535 set
00536 {
00537 numberSmallTicks_ = value;
00538 }
00539 }
00540
00541
00542
00543 private object numberSmallTicks_;
00544 private double largeTickValue_ = double.NaN;
00545 private double largeTickStep_ = double.NaN;
00546
00556 public override PointF WorldToPhysical(
00557 double coord,
00558 PointF physicalMin,
00559 PointF physicalMax,
00560 bool clip )
00561 {
00562
00563 if (clip)
00564 {
00565 if (coord > WorldMax)
00566 {
00567 return physicalMax;
00568 }
00569 if (coord < WorldMin)
00570 {
00571 return physicalMin;
00572 }
00573 }
00574
00575 if (coord < 0.0f)
00576 {
00577 throw new NPlotException( "Cannot have negative values for data using Log Axis" );
00578 }
00579
00580
00581 double lrange = (double)(Math.Log10(WorldMax) - Math.Log10(WorldMin));
00582 double prop = (double)((Math.Log10(coord) - Math.Log10(WorldMin)) / lrange);
00583 PointF offset = new PointF( (float)(prop * (physicalMax.X - physicalMin.X)),
00584 (float)(prop * (physicalMax.Y - physicalMin.Y)) );
00585
00586 return new PointF( physicalMin.X + offset.X, physicalMin.Y + offset.Y );
00587 }
00588
00589
00599 public override double PhysicalToWorld( PointF p, PointF physicalMin, PointF physicalMax, bool clip )
00600 {
00601
00602 double t = base.PhysicalToWorld( p, physicalMin, physicalMax, clip );
00603
00604
00605 double v = (t - this.WorldMin) / (this.WorldMax - this.WorldMin);
00606
00607 double ret = WorldMin*Math.Pow( WorldMax / WorldMin, v );
00608
00609
00610 if (clip)
00611 {
00612 ret = Math.Max( ret, WorldMin );
00613 ret = Math.Min( ret, WorldMax );
00614 }
00615
00616 return ret;
00617
00618 }
00619
00620
00624 public override double WorldMin
00625 {
00626 get
00627 {
00628 return (double)base.WorldMin;
00629 }
00630 set
00631 {
00632 if (value > 0.0f)
00633 {
00634 base.WorldMin = value;
00635 }
00636 else
00637 {
00638 throw new NPlotException("Cannot have negative values in Log Axis");
00639 }
00640 }
00641 }
00642
00643
00647 public override double WorldMax
00648 {
00649 get
00650 {
00651 return (double)base.WorldMax;
00652 }
00653 set
00654 {
00655 if (value > 0.0F)
00656 {
00657 base.WorldMax = value;
00658 }
00659 else
00660 {
00661 throw new NPlotException("Cannot have negative values in Log Axis");
00662 }
00663 }
00664 }
00665
00669 public override bool IsLinear
00670 {
00671 get
00672 {
00673 return false;
00674 }
00675 }
00676
00677 }
00678 }