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.Drawing.Drawing2D;
00053 using System.Drawing;
00054 using System;
00055 using System.Collections;
00056
00057 namespace NPlot
00058 {
00059
00068 public class Axis : System.ICloneable
00069 {
00070
00075 public bool TicksCrossAxis
00076 {
00077 get
00078 {
00079 return ticksCrossAxis_;
00080 }
00081 set
00082 {
00083 ticksCrossAxis_ = value;
00084 }
00085 }
00086 bool ticksCrossAxis_ = false;
00087
00088
00098 public virtual double WorldMax
00099 {
00100 get
00101 {
00102 return worldMax_;
00103 }
00104 set
00105 {
00106 this.worldMax_ = value;
00107
00108
00109
00110
00111
00112
00113 }
00114 }
00115 private double worldMax_;
00116
00117
00127 public virtual double WorldMin
00128 {
00129 get
00130 {
00131 return this.worldMin_;
00132 }
00133 set
00134 {
00135 this.worldMin_ = value;
00136
00137
00138
00139
00140
00141
00142 }
00143 }
00144 private double worldMin_;
00145
00146
00151 public int LargeTickSize
00152 {
00153 get
00154 {
00155 return largeTickSize_;
00156 }
00157 set
00158 {
00159 largeTickSize_ = value;
00160 }
00161 }
00162 private int largeTickSize_;
00163
00164
00168 public int SmallTickSize
00169 {
00170 get
00171 {
00172 return smallTickSize_;
00173 }
00174 set
00175 {
00176 smallTickSize_ = value;
00177 }
00178 }
00179 private int smallTickSize_;
00180
00181
00185 public string Label
00186 {
00187 get
00188 {
00189 return label_;
00190 }
00191 set
00192 {
00193 label_ = value;
00194 }
00195 }
00196 private string label_;
00197
00198
00204 public bool TickTextNextToAxis
00205 {
00206 get
00207 {
00208 return tickTextNextToAxis_;
00209 }
00210 set
00211 {
00212 tickTextNextToAxis_ = value;
00213 }
00214 }
00215 bool tickTextNextToAxis_;
00216
00217
00222 public bool Hidden
00223 {
00224 get
00225 {
00226 return hidden_;
00227 }
00228 set
00229 {
00230 hidden_ = value;
00231 }
00232 }
00233 private bool hidden_;
00234
00235
00240 public bool Reversed
00241 {
00242 get
00243 {
00244 return reversed_;
00245 }
00246 set
00247 {
00248 reversed_ = value;
00249 }
00250 }
00251 private bool reversed_;
00252
00253
00257 public bool HideTickText
00258 {
00259 get
00260 {
00261 return hideTickText_;
00262 }
00263 set
00264 {
00265 hideTickText_ = value;
00266 }
00267 }
00268 private bool hideTickText_;
00269
00270
00274 public Font TickTextFont
00275 {
00276 get
00277 {
00278 return this.tickTextFont_;
00279 }
00280 set
00281 {
00282 this.tickTextFont_ = value;
00283 UpdateScale();
00284 }
00285 }
00286 private Font tickTextFont_;
00287 private Font tickTextFontScaled_;
00288
00289
00293 public Font LabelFont
00294 {
00295 get
00296 {
00297 return labelFont_;
00298 }
00299 set
00300 {
00301 labelFont_ = value;
00302 UpdateScale();
00303 }
00304 }
00305 private Font labelFont_;
00306 private Font labelFontScaled_;
00307
00308
00314 public string NumberFormat
00315 {
00316 get
00317 {
00318 return numberFormat_;
00319 }
00320 set
00321 {
00322 numberFormat_ = value;
00323 }
00324 }
00325 private string numberFormat_;
00326
00327
00333 public int MinPhysicalLargeTickStep
00334 {
00335 get
00336 {
00337 return minPhysicalLargeTickStep_;
00338 }
00339 set
00340 {
00341 minPhysicalLargeTickStep_ = value;
00342 }
00343 }
00344 private int minPhysicalLargeTickStep_ = 30;
00345
00346
00350 public System.Drawing.Color AxisColor
00351 {
00352 get
00353 {
00354 return linePen_.Color;
00355 }
00356 set
00357 {
00358 linePen_ = new Pen( (Color)value );
00359 }
00360 }
00361
00362
00366 public System.Drawing.Pen AxisPen
00367 {
00368 get
00369 {
00370 return linePen_;
00371 }
00372 set
00373 {
00374 linePen_ = value;
00375 }
00376 }
00377 private System.Drawing.Pen linePen_;
00378
00379
00391 public bool TicksIndependentOfPhysicalExtent
00392 {
00393 get
00394 {
00395 return ticksIndependentOfPhysicalExtent_;
00396 }
00397 set
00398 {
00399 ticksIndependentOfPhysicalExtent_ = value;
00400 }
00401 }
00402 private bool ticksIndependentOfPhysicalExtent_ = false;
00403
00404
00408 public bool FlipTicksLabel
00409 {
00410 get
00411 {
00412 return flipTicksLabel_;
00413 }
00414 set
00415 {
00416 flipTicksLabel_ = value;
00417 }
00418 }
00419 private bool flipTicksLabel_ = false;
00420
00421
00425 public float TicksAngle
00426 {
00427 get
00428 {
00429 return ticksAngle_;
00430 }
00431 set
00432 {
00433 ticksAngle_ = value;
00434 }
00435 }
00436 private float ticksAngle_ = (float)Math.PI / 2.0f;
00437
00438
00443 public float TicksLabelAngle
00444 {
00445 get
00446 {
00447 return ticksLabelAngle_;
00448 }
00449 set
00450 {
00451 ticksLabelAngle_ = value;
00452 }
00453 }
00454 private float ticksLabelAngle_ = 0.0f;
00455
00456
00460 public Color LabelColor
00461 {
00462 set
00463 {
00464 labelBrush_ = new SolidBrush( value );
00465 }
00466 }
00467
00468
00472 public Brush LabelBrush
00473 {
00474 get
00475 {
00476 return labelBrush_;
00477 }
00478 set
00479 {
00480 labelBrush_ = value;
00481 }
00482 }
00483 private Brush labelBrush_;
00484
00485
00489 public Color TickTextColor
00490 {
00491 set
00492 {
00493 tickTextBrush_ = new SolidBrush( value );
00494 }
00495 }
00496
00497
00501 public Brush TickTextBrush
00502 {
00503 get
00504 {
00505 return tickTextBrush_;
00506 }
00507 set
00508 {
00509 tickTextBrush_ = value;
00510 }
00511 }
00512 private Brush tickTextBrush_;
00513
00514
00520 public bool AutoScaleText
00521 {
00522 get
00523 {
00524 return autoScaleText_;
00525 }
00526 set
00527 {
00528 autoScaleText_ = value;
00529 }
00530 }
00531 private bool autoScaleText_;
00532
00533
00539 public bool AutoScaleTicks
00540 {
00541 get
00542 {
00543 return autoScaleTicks_;
00544 }
00545 set
00546 {
00547 autoScaleTicks_ = value;
00548 }
00549 }
00550 private bool autoScaleTicks_;
00551
00552
00563 public virtual object Clone()
00564 {
00565
00566
00567 if (this.GetType() != typeof(Axis))
00568 {
00569 throw new NPlotException( "Clone not defined in derived type." );
00570 }
00571
00572 Axis a = new Axis();
00573 DoClone( this, a );
00574 return a;
00575 }
00576
00577
00583 protected static void DoClone( Axis b, Axis a )
00584 {
00585
00586
00587 a.autoScaleText_ = b.autoScaleText_;
00588 a.autoScaleTicks_ = b.autoScaleTicks_;
00589 a.worldMax_ = b.worldMax_;
00590 a.worldMin_ = b.worldMin_;
00591 a.tickTextNextToAxis_ = b.tickTextNextToAxis_;
00592 a.hidden_ = b.hidden_;
00593 a.hideTickText_ = b.hideTickText_;
00594 a.reversed_ = b.reversed_;
00595 a.ticksAngle_ = b.ticksAngle_;
00596 a.ticksLabelAngle_ = b.ticksLabelAngle_;
00597 a.minPhysicalLargeTickStep_ = b.minPhysicalLargeTickStep_;
00598 a.ticksIndependentOfPhysicalExtent_ = b.ticksIndependentOfPhysicalExtent_;
00599 a.largeTickSize_ = b.largeTickSize_;
00600 a.smallTickSize_ = b.smallTickSize_;
00601 a.ticksCrossAxis_ = b.ticksCrossAxis_;
00602 a.labelOffset_ = b.labelOffset_;
00603 a.labelOffsetAbsolute_ = b.labelOffsetAbsolute_;
00604 a.labelOffsetScaled_ = b.labelOffsetScaled_;
00605
00606
00607 a.tickTextFont_ = (Font)b.tickTextFont_.Clone();
00608 a.label_ = (string)b.label_.Clone();
00609 if (b.numberFormat_ != null)
00610 {
00611 a.numberFormat_ = (string)b.numberFormat_.Clone();
00612 }
00613 else
00614 {
00615 a.numberFormat_ = null;
00616 }
00617
00618 a.labelFont_ = (Font)b.labelFont_.Clone();
00619 a.linePen_ = (Pen)b.linePen_.Clone();
00620 a.tickTextBrush_ = (Brush)b.tickTextBrush_.Clone();
00621 a.labelBrush_ = (Brush)b.labelBrush_.Clone();
00622
00623 a.FontScale = b.FontScale;
00624 a.TickScale = b.TickScale;
00625
00626 }
00627
00628
00629
00634 private void Init()
00635 {
00636 this.worldMax_ = double.NaN;
00637 this.worldMin_ = double.NaN;
00638 this.Hidden = false;
00639 this.SmallTickSize = 2;
00640 this.LargeTickSize = 6;
00641 this.FontScale = 1.0f;
00642 this.TickScale = 1.0f;
00643 this.AutoScaleTicks = false;
00644 this.AutoScaleText = false;
00645 this.TickTextNextToAxis = true;
00646 this.HideTickText = false;
00647 this.TicksCrossAxis = false;
00648 this.LabelOffset = 0.0f;
00649 this.LabelOffsetAbsolute = false;
00650 this.LabelOffsetScaled = true;
00651
00652 this.Label = "" ;
00653 this.NumberFormat = null;
00654 this.Reversed = false;
00655
00656 FontFamily fontFamily = new FontFamily( "Arial" );
00657 this.TickTextFont = new Font( fontFamily, 10, FontStyle.Regular, GraphicsUnit.Pixel );
00658 this.LabelFont = new Font( fontFamily, 12, FontStyle.Regular, GraphicsUnit.Pixel );
00659 this.LabelColor = System.Drawing.Color.Black;
00660 this.TickTextColor = System.Drawing.Color.Black;
00661 this.linePen_ = new Pen( System.Drawing.Color.Black );
00662 this.linePen_.Width = 1.0f;
00663 this.FontScale = 1.0f;
00664
00665
00666 drawFormat_ = new StringFormat();
00667 drawFormat_.Alignment = StringAlignment.Center;
00668 }
00669
00670 StringFormat drawFormat_;
00671
00672
00676 public Axis( )
00677 {
00678 this.Init();
00679 }
00680
00681
00687 public Axis( double worldMin, double worldMax )
00688 {
00689 this.Init();
00690 this.WorldMin = worldMin;
00691 this.WorldMax = worldMax;
00692 }
00693
00694
00699 public Axis( Axis a )
00700 {
00701 Axis.DoClone( a, this );
00702 }
00703
00704
00710 public bool OutOfRange( double coord )
00711 {
00712 if (double.IsNaN(WorldMin) || double.IsNaN(WorldMax))
00713 {
00714 throw new NPlotException( "world min / max not set" );
00715 }
00716
00717 if (coord > this.WorldMax || coord < this.WorldMin)
00718 {
00719 return true;
00720 }
00721 else
00722 {
00723 return false;
00724 }
00725 }
00726
00727
00734 public void LUB( Axis a )
00735 {
00736 if (a == null)
00737 {
00738 return;
00739 }
00740
00741
00742 if (!double.IsNaN(a.worldMin_))
00743 {
00744 if (double.IsNaN(worldMin_))
00745 {
00746 WorldMin = a.WorldMin;
00747 }
00748 else
00749 {
00750 if (a.WorldMin < WorldMin)
00751 {
00752 WorldMin = a.WorldMin;
00753 }
00754 }
00755 }
00756
00757
00758 if (!double.IsNaN(a.worldMax_))
00759 {
00760 if (double.IsNaN(worldMax_))
00761 {
00762 WorldMax = a.WorldMax;
00763 }
00764 else
00765 {
00766 if (a.WorldMax > WorldMax)
00767 {
00768 WorldMax = a.WorldMax;
00769 }
00770 }
00771 }
00772 }
00773
00774
00785 public virtual PointF WorldToPhysical(
00786 double coord,
00787 PointF physicalMin,
00788 PointF physicalMax,
00789 bool clip )
00790 {
00791
00792
00793
00794
00795 PointF _physicalMin;
00796 PointF _physicalMax;
00797
00798 if ( this.Reversed )
00799 {
00800 _physicalMin = physicalMax;
00801 _physicalMax = physicalMin;
00802 }
00803 else
00804 {
00805 _physicalMin = physicalMin;
00806 _physicalMax = physicalMax;
00807 }
00808
00809
00810
00811
00812 if ( clip )
00813 {
00814 if ( WorldMin < WorldMax )
00815 {
00816 if ( coord > WorldMax )
00817 {
00818 return _physicalMax;
00819 }
00820 if ( coord < WorldMin )
00821 {
00822 return _physicalMin;
00823 }
00824 }
00825 else
00826 {
00827 if ( coord < WorldMax )
00828 {
00829 return _physicalMax;
00830 }
00831 if ( coord > WorldMin )
00832 {
00833 return _physicalMin;
00834 }
00835 }
00836 }
00837
00838
00839
00840
00841 double range = WorldMax - WorldMin;
00842 double prop = (double)((coord - WorldMin) / range);
00843
00844
00845
00846 const double largeClip = 100.0;
00847 if (prop > largeClip && clip)
00848 prop = largeClip;
00849
00850 if (prop < -largeClip && clip)
00851 prop = -largeClip;
00852
00853 if (range == 0)
00854 {
00855 if (coord >= WorldMin)
00856 prop = largeClip;
00857
00858 if (coord < WorldMin)
00859 prop = -largeClip;
00860 }
00861
00862
00863 PointF offset = new PointF(
00864 (float)(prop * (_physicalMax.X - _physicalMin.X)),
00865 (float)(prop * (_physicalMax.Y - _physicalMin.Y)) );
00866
00867 return new Point( (int)(_physicalMin.X + offset.X), (int)(_physicalMin.Y + offset.Y) );
00868 }
00869
00870
00880 public virtual double PhysicalToWorld(
00881 PointF p,
00882 PointF physicalMin,
00883 PointF physicalMax,
00884 bool clip )
00885 {
00886
00887
00888
00889
00890 PointF _physicalMin;
00891 PointF _physicalMax;
00892
00893 if ( this.Reversed )
00894 {
00895 _physicalMin = physicalMax;
00896 _physicalMax = physicalMin;
00897 }
00898 else
00899 {
00900 _physicalMin = physicalMin;
00901 _physicalMax = physicalMax;
00902 }
00903
00904
00905 float axis_X = _physicalMax.X - _physicalMin.X;
00906 float axis_Y = _physicalMax.Y - _physicalMin.Y;
00907 float len = (float)Math.Sqrt( axis_X * axis_X + axis_Y * axis_Y );
00908 axis_X /= len;
00909 axis_Y /= len;
00910
00911
00912 PointF posRel = new PointF( p.X - _physicalMin.X, p.Y - _physicalMin.Y );
00913
00914
00915 float prop = ( axis_X * posRel.X + axis_Y * posRel.Y ) / len;
00916
00917 double world = prop * (this.WorldMax - this.WorldMin) + this.WorldMin;
00918
00919
00920 if (clip)
00921 {
00922 world = Math.Max( world, this.WorldMin );
00923 world = Math.Min( world, this.WorldMax );
00924 }
00925
00926 return world;
00927 }
00928
00929
00938 public object DrawLabel(
00939 Graphics g,
00940 Point offset,
00941 Point axisPhysicalMin,
00942 Point axisPhysicalMax )
00943 {
00944
00945 if ( Label != "" )
00946 {
00947
00948
00949 float extraOffsetAmount = this.LabelOffset;
00950 extraOffsetAmount += 2.0f;
00951
00952 if (this.AutoScaleText)
00953 {
00954 if (this.LabelOffsetScaled)
00955 {
00956 extraOffsetAmount *= this.FontScale;
00957 }
00958 }
00959
00960
00961 float offsetLength = (float)Math.Sqrt( offset.X*offset.X + offset.Y*offset.Y );
00962 if (offsetLength > 0.01)
00963 {
00964 float x_component = offset.X / offsetLength;
00965 float y_component = offset.Y / offsetLength;
00966
00967 x_component *= extraOffsetAmount;
00968 y_component *= extraOffsetAmount;
00969
00970 if (this.LabelOffsetAbsolute)
00971 {
00972 offset.X = (int)x_component;
00973 offset.Y = (int)y_component;
00974 }
00975 else
00976 {
00977 offset.X += (int)x_component;
00978 offset.Y += (int)y_component;
00979 }
00980 }
00981
00982
00983 double theta = Math.Atan2(
00984 axisPhysicalMax.Y - axisPhysicalMin.Y,
00985 axisPhysicalMax.X - axisPhysicalMin.X );
00986 theta = theta * 180.0f / Math.PI;
00987
00988 PointF average = new PointF(
00989 (axisPhysicalMax.X + axisPhysicalMin.X)/2.0f,
00990 (axisPhysicalMax.Y + axisPhysicalMin.Y)/2.0f );
00991
00992 g.TranslateTransform( offset.X , offset.Y );
00993 g.TranslateTransform( average.X, average.Y );
00994 g.RotateTransform( (float)theta );
00995
00996 SizeF labelSize = g.MeasureString( Label, labelFontScaled_);
00997
00998
00999 RectangleF drawRect = new RectangleF(
01000 -labelSize.Width/2.0f,
01001 -labelSize.Height/2.0f,
01002 labelSize.Width,
01003 labelSize.Height );
01004
01005 g.DrawString(
01006 Label,
01007 labelFontScaled_,
01008 labelBrush_,
01009 drawRect,
01010 drawFormat_ );
01011
01012
01013 Matrix m = g.Transform;
01014 PointF[] recPoints = new PointF[2];
01015 recPoints[0] = new PointF( -labelSize.Width/2.0f, -labelSize.Height/2.0f );
01016 recPoints[1] = new PointF( labelSize.Width/2.0f, labelSize.Height/2.0f );
01017 m.TransformPoints( recPoints );
01018
01019 int x1 = (int)Math.Min( recPoints[0].X, recPoints[1].X );
01020 int x2 = (int)Math.Max( recPoints[0].X, recPoints[1].X );
01021 int y1 = (int)Math.Min( recPoints[0].Y, recPoints[1].Y );
01022 int y2 = (int)Math.Max( recPoints[0].Y, recPoints[1].Y );
01023
01024 g.ResetTransform();
01025
01026
01027 return new Rectangle( x1, y1, (x2-x1), (y2-y1) );
01028 }
01029
01030 return null;
01031 }
01032
01033
01046 public virtual void DrawTick(
01047 Graphics g,
01048 double w,
01049 float size,
01050 string text,
01051 Point textOffset,
01052 Point axisPhysMin,
01053 Point axisPhysMax,
01054 out Point labelOffset,
01055 out Rectangle boundingBox )
01056 {
01057
01058
01059 PointF tickStart = WorldToPhysical( w, axisPhysMin, axisPhysMax, true );
01060
01061
01062 PointF axisDir = Utils.UnitVector( axisPhysMin, axisPhysMax );
01063
01064
01065 float x1 = (float)(Math.Cos( -this.TicksAngle ) * axisDir.X + Math.Sin( -this.TicksAngle ) * axisDir.Y);
01066 float y1 = (float)(-Math.Sin( -this.TicksAngle ) * axisDir.X + Math.Cos( -this.TicksAngle ) * axisDir.Y);
01067
01068
01069 PointF tickVector = new PointF( this.TickScale * size * x1, this.TickScale * size * y1 );
01070
01071 if (this.TicksCrossAxis)
01072 {
01073 tickStart = new PointF(
01074 tickStart.X - tickVector.X / 2.0f,
01075 tickStart.Y - tickVector.Y / 2.0f );
01076 }
01077
01078
01079 PointF tickEnd = new PointF( tickStart.X + tickVector.X, tickStart.Y + tickVector.Y );
01080
01081
01082 if (g != null)
01083 g.DrawLine( this.linePen_, (int)tickStart.X, (int)tickStart.Y, (int)tickEnd.X, (int)tickEnd.Y );
01084
01085
01086
01087
01088 int minX = (int)Math.Min( tickStart.X, tickEnd.X );
01089 int minY = (int)Math.Min( tickStart.Y, tickEnd.Y );
01090 int maxX = (int)Math.Max( tickStart.X, tickEnd.X );
01091 int maxY = (int)Math.Max( tickStart.Y, tickEnd.Y );
01092 boundingBox = new Rectangle( minX, minY, maxX-minX, maxY-minY );
01093
01094
01095 labelOffset = new Point(
01096 -(int)tickVector.X,
01097 -(int)tickVector.Y );
01098
01099
01100
01101
01102
01103
01104
01105
01106
01107
01108 if (text != "" && !HideTickText && g != null )
01109 {
01110 SizeF textSize = g.MeasureString( text, tickTextFontScaled_ );
01111
01112
01113 float textCenterX;
01114 float textCenterY;
01115
01116
01117 if (!this.TickTextNextToAxis)
01118 {
01119
01120 textCenterX = tickStart.X + tickVector.X*1.2f;
01121 textCenterY = tickStart.Y + tickVector.Y*1.2f;
01122
01123
01124 textCenterX += 0.5f * x1 * textSize.Width;
01125 textCenterY += 0.5f * y1 * textSize.Height;
01126 }
01127
01128 else
01129 {
01130
01131 textCenterX = tickStart.X;
01132 textCenterY = tickStart.Y;
01133
01134
01135 textCenterX -= 0.5f * x1 * textSize.Width;
01136 textCenterY -= 0.5f * y1 * textSize.Height;
01137
01138
01139 textCenterX -= x1*(2.0f+FontScale);
01140 textCenterY -= y1*(2.0f+FontScale);
01141 }
01142
01143
01144 if (this.TicksLabelAngle != 0.0f)
01145 {
01146
01147
01148
01149 PointF textScaledTickVector = new PointF( this.TickScale * x1 * (textSize.Height/2.0f), this.TickScale * y1 * (textSize.Height/2.0f) );
01150
01151 PointF rotatePoint;
01152 if (this.TickTextNextToAxis)
01153 {
01154 rotatePoint = new PointF( tickStart.X - textScaledTickVector.X, tickStart.Y - textScaledTickVector.Y );
01155 }
01156 else
01157 {
01158 rotatePoint = new PointF( tickEnd.X + textScaledTickVector.X, tickEnd.Y + textScaledTickVector.Y );
01159 }
01160
01161 float actualAngle;
01162 if (flipTicksLabel_)
01163 {
01164 double radAngle = (Math.PI / 180) * this.TicksLabelAngle;
01165 rotatePoint.X += textSize.Width * (float)Math.Cos(radAngle);
01166 rotatePoint.Y += textSize.Width * (float)Math.Sin(radAngle);
01167 actualAngle = this.TicksLabelAngle + 180;
01168 }
01169 else
01170 {
01171 actualAngle = this.TicksLabelAngle;
01172 }
01173
01174
01175 g.TranslateTransform( rotatePoint.X, rotatePoint.Y );
01176
01177 g.RotateTransform( actualAngle );
01178
01179 Matrix m = g.Transform;
01180 PointF[] recPoints = new PointF[2];
01181 recPoints[0] = new PointF( 0.0f, -(textSize.Height / 2) );
01182 recPoints[1] = new PointF( textSize.Width, textSize.Height );
01183 m.TransformPoints( recPoints );
01184
01185 float t_x1 = Math.Min( recPoints[0].X, recPoints[1].X );
01186 float t_x2 = Math.Max( recPoints[0].X, recPoints[1].X );
01187 float t_y1 = Math.Min( recPoints[0].Y, recPoints[1].Y );
01188 float t_y2 = Math.Max( recPoints[0].Y, recPoints[1].Y );
01189
01190 boundingBox = Rectangle.Union(boundingBox, new Rectangle( (int)t_x1, (int)t_y1, (int)(t_x2-t_x1), (int)(t_y2-t_y1) ) );
01191 RectangleF drawRect = new RectangleF( 0.0f, -(textSize.Height / 2), textSize.Width, textSize.Height );
01192
01193 g.DrawString(
01194 text,
01195 tickTextFontScaled_,
01196 tickTextBrush_,
01197 drawRect,
01198 drawFormat_ );
01199
01200 t_x2 -= tickStart.X;
01201 t_y2 -= tickStart.Y;
01202 t_x2 *= 1.25f;
01203 t_y2 *= 1.25f;
01204
01205 labelOffset = new Point( (int)t_x2, (int)t_y2 );
01206
01207 g.ResetTransform();
01208
01209
01210
01211 }
01212 else
01213 {
01214
01215 float bx1 = (textCenterX - textSize.Width/2.0f);
01216 float by1 = (textCenterY - textSize.Height/2.0f);
01217 float bx2 = textSize.Width;
01218 float by2 = textSize.Height;
01219
01220 RectangleF drawRect = new RectangleF( bx1, by1, bx2, by2 );
01221 Rectangle drawRect_int = new Rectangle( (int)bx1, (int)by1, (int)bx2, (int)by2 );
01222
01223
01224 boundingBox = Rectangle.Union( boundingBox, drawRect_int );
01225
01226
01227
01228 g.DrawString(
01229 text,
01230 tickTextFontScaled_,
01231 tickTextBrush_,
01232 drawRect,
01233 drawFormat_ );
01234
01235 textCenterX -= tickStart.X;
01236 textCenterY -= tickStart.Y;
01237 textCenterX *= 2.3f;
01238 textCenterY *= 2.3f;
01239
01240 labelOffset = new Point( (int)textCenterX, (int)textCenterY );
01241 }
01242 }
01243
01244 }
01245
01246
01257 public virtual void Draw(
01258 System.Drawing.Graphics g,
01259 Point physicalMin,
01260 Point physicalMax,
01261 out Rectangle boundingBox )
01262 {
01263
01264 int x1 = Math.Min( physicalMin.X, physicalMax.X );
01265 int x2 = Math.Max( physicalMin.X, physicalMax.X );
01266 int y1 = Math.Min( physicalMin.Y, physicalMax.Y );
01267 int y2 = Math.Max( physicalMin.Y, physicalMax.Y );
01268 Rectangle bounds = new Rectangle( x1, y1, x2-x1, y2-y1 );
01269
01270 if (!Hidden)
01271 {
01272
01273
01274 g.DrawLine( this.linePen_, physicalMin.X, physicalMin.Y, physicalMax.X, physicalMax.Y );
01275
01276
01277
01278 object labelOffset;
01279 object tickBounds;
01280 this.DrawTicks( g, physicalMin, physicalMax, out labelOffset, out tickBounds );
01281
01282
01283 object labelBounds = null;
01284 if (!this.HideTickText)
01285 {
01286 labelBounds = this.DrawLabel( g, (Point)labelOffset, physicalMin, physicalMax );
01287 }
01288
01289
01290 if (labelBounds != null)
01291 bounds = Rectangle.Union( bounds, (Rectangle)labelBounds );
01292
01293 if (tickBounds != null)
01294 bounds = Rectangle.Union( bounds, (Rectangle)tickBounds );
01295
01296 }
01297
01298 boundingBox = bounds;
01299 }
01300
01301
01311 protected static void UpdateOffsetAndBounds(
01312 ref object labelOffset, ref object boundingBox,
01313 Point mergeLabelOffset, Rectangle mergeBoundingBox )
01314 {
01315
01316 Point lo = (Point)labelOffset;
01317 double norm1 = Math.Sqrt( lo.X*lo.X + lo.Y*lo.Y );
01318 double norm2 = Math.Sqrt( mergeLabelOffset.X*mergeLabelOffset.X + mergeLabelOffset.Y*mergeLabelOffset.Y );
01319 if (norm1 < norm2)
01320 {
01321 labelOffset = mergeLabelOffset;
01322 }
01323
01324
01325 Rectangle b = mergeBoundingBox;
01326 if (boundingBox == null)
01327 {
01328 boundingBox = b;
01329 }
01330 else
01331 {
01332 boundingBox = Rectangle.Union( (Rectangle)boundingBox, b );
01333 }
01334 }
01335
01336
01345 protected virtual void DrawTicks(
01346 Graphics g,
01347 Point physicalMin,
01348 Point physicalMax,
01349 out object labelOffset,
01350 out object boundingBox )
01351 {
01352 labelOffset = null;
01353 boundingBox = null;
01354
01355
01356
01357 }
01358
01359
01360
01364 public double WorldLength
01365 {
01366 get
01367 {
01368 return Math.Abs( worldMax_ - worldMin_ );
01369 }
01370 }
01371
01385 internal virtual void WorldTickPositions_FirstPass(
01386 Point physicalMin,
01387 Point physicalMax,
01388 out ArrayList largeTickPositions,
01389 out ArrayList smallTickPositions
01390 )
01391 {
01392 largeTickPositions = new ArrayList();
01393 smallTickPositions = null;
01394 }
01395
01396
01408 internal virtual void WorldTickPositions_SecondPass(
01409 Point physicalMin,
01410 Point physicalMax,
01411 ArrayList largeTickPositions,
01412 ref ArrayList smallTickPositions )
01413 {
01414 if (smallTickPositions == null)
01415 smallTickPositions = new ArrayList();
01416 }
01417
01418
01426 public void WorldTickPositions(
01427 Point physicalMin,
01428 Point physicalMax,
01429 out ArrayList largeTickPositions,
01430 out ArrayList smallTickPositions
01431 )
01432 {
01433 WorldTickPositions_FirstPass( physicalMin, physicalMax, out largeTickPositions, out smallTickPositions );
01434 WorldTickPositions_SecondPass( physicalMin, physicalMax, largeTickPositions, ref smallTickPositions );
01435 }
01436
01437
01447 public void IncreaseRange( double percent )
01448 {
01449 double range = WorldMax - WorldMin;
01450
01451 if ( !Utils.DoubleEqual( range, 0.0 ) )
01452 {
01453 range *= percent;
01454 }
01455 else
01456 {
01457
01458
01459 range = 0.01;
01460 }
01461
01462 WorldMax += range;
01463 WorldMin -= range;
01464 }
01465
01466
01471 internal float FontScale
01472 {
01473 get
01474 {
01475 return fontScale_;
01476 }
01477
01478 set
01479 {
01480 fontScale_ = value;
01481 UpdateScale();
01482 }
01483 }
01484 private float fontScale_;
01485
01486
01491 internal float TickScale
01492 {
01493 get
01494 {
01495 return tickScale_;
01496 }
01497 set
01498 {
01499 tickScale_ = value;
01500 }
01501 }
01502 private float tickScale_;
01503
01504
01505 private void UpdateScale()
01506 {
01507 if (labelFont_ != null)
01508 this.labelFontScaled_ = Utils.ScaleFont( labelFont_, FontScale );
01509
01510 if (tickTextFont_ != null)
01511 this.tickTextFontScaled_ = Utils.ScaleFont( tickTextFont_, FontScale );
01512 }
01513
01514
01518 public virtual bool IsLinear
01519 {
01520 get
01521 {
01522 return true;
01523 }
01524 }
01525
01526 private float labelOffset_ = 0;
01535 public float LabelOffset
01536 {
01537 get
01538 {
01539 return labelOffset_;
01540 }
01541 set
01542 {
01543 labelOffset_ = value;
01544 }
01545 }
01546
01547 private bool labelOffsetAbsolute_ = false;
01555 public bool LabelOffsetAbsolute
01556 {
01557 get
01558 {
01559 return labelOffsetAbsolute_;
01560 }
01561 set
01562 {
01563 labelOffsetAbsolute_ = value;
01564 }
01565 }
01566
01567 private bool labelOffsetScaled_ = true;
01572 public bool LabelOffsetScaled
01573 {
01574 get
01575 {
01576 return labelOffsetScaled_;
01577 }
01578 set
01579 {
01580 labelOffsetScaled_ = value;
01581 }
01582 }
01583
01584
01592 protected Point getDefaultLabelOffset( Point physicalMin, Point physicalMax )
01593 {
01594 System.Drawing.Rectangle tBoundingBox;
01595 System.Drawing.Point tLabelOffset;
01596
01597 this.DrawTick( null, this.WorldMax, this.LargeTickSize,
01598 "",
01599 new Point(0,0),
01600 physicalMin, physicalMax,
01601 out tLabelOffset, out tBoundingBox );
01602
01603 return tLabelOffset;
01604 }
01605
01606
01610 public Color Color
01611 {
01612 set
01613 {
01614 this.AxisColor = value;
01615 this.TickTextColor = value;
01616 this.LabelColor = value;
01617 }
01618 }
01619
01620
01621
01622
01623
01624
01625
01626
01627
01628
01629
01630
01631
01632
01633
01634
01635
01636
01637
01638
01639
01640
01641
01642
01643
01644
01645
01646
01647
01648
01649
01650
01651
01652
01653 }
01654 }