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.Drawing.Drawing2D;
00055 using System.Collections;
00056
00057 namespace NPlot
00058 {
00059
00063 public class HistogramPlot : BaseSequencePlot, IPlot, ISequencePlot
00064 {
00065
00069 public IRectangleBrush RectangleBrush
00070 {
00071 get
00072 {
00073 return rectangleBrush_;
00074 }
00075 set
00076 {
00077 rectangleBrush_ = value;
00078 }
00079
00080 }
00081 private IRectangleBrush rectangleBrush_ = new RectangleBrushes.Solid( Color.Black );
00082
00083
00087 public HistogramPlot()
00088 {
00089 }
00090
00091
00098 public void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
00099 {
00100 SequenceAdapter data =
00101 new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
00102
00103 float yoff;
00104
00105 for ( int i=0; i<data.Count; ++i )
00106 {
00107
00108
00109 PointD p1 = data[i];
00110 if ( double.IsNaN(p1.X) || double.IsNaN(p1.Y) )
00111 continue;
00112
00113
00114 PointD p2;
00115 if (i+1 != data.Count)
00116 {
00117 p2 = data[i+1];
00118 if ( double.IsNaN(p2.X) || double.IsNaN(p2.Y) )
00119 continue;
00120 p2.Y = p1.Y;
00121 }
00122 else if (i != 0)
00123 {
00124 p2 = data[i-1];
00125 if ( double.IsNaN(p2.X) || double.IsNaN(p2.Y) )
00126 continue;
00127 double offset = p1.X - p2.X;
00128 p2.X = p1.X + offset;
00129 p2.Y = p1.Y;
00130 }
00131 else
00132 {
00133 double offset = 1.0f;
00134 p2.X = p1.X + offset;
00135 p2.Y = p1.Y;
00136 }
00137
00138
00139 HistogramPlot currentPlot = this;
00140 yoff = 0.0f;
00141 double yval = 0.0f;
00142 while (currentPlot.isStacked_)
00143 {
00144 SequenceAdapter stackedToData = new SequenceAdapter(
00145 currentPlot.stackedTo_.DataSource,
00146 currentPlot.stackedTo_.DataMember,
00147 currentPlot.stackedTo_.OrdinateData,
00148 currentPlot.stackedTo_.AbscissaData );
00149
00150 yval += stackedToData[i].Y;
00151 yoff = yAxis.WorldToPhysical( yval, false ).Y;
00152 p1.Y += stackedToData[i].Y;
00153 p2.Y += stackedToData[i].Y;
00154 currentPlot = currentPlot.stackedTo_;
00155 }
00156
00157
00158 if ( center_ )
00159 {
00160 double offset = ( p2.X - p1.X ) / 2.0f;
00161 p1.X -= offset;
00162 p2.X -= offset;
00163 }
00164
00165
00166 p1.X += baseOffset_;
00167 p2.X += baseOffset_;
00168
00169
00170 PointF xPos1 = xAxis.WorldToPhysical( p1.X, false );
00171 PointF yPos1 = yAxis.WorldToPhysical( p1.Y, false );
00172 PointF xPos2 = xAxis.WorldToPhysical( p2.X, false );
00173 PointF yPos2 = yAxis.WorldToPhysical( p2.Y, false );
00174
00175 if (isStacked_)
00176 {
00177 currentPlot = this;
00178 while (currentPlot.isStacked_)
00179 {
00180 currentPlot = currentPlot.stackedTo_;
00181 }
00182 this.baseWidth_ = currentPlot.baseWidth_;
00183 }
00184
00185 float width = xPos2.X - xPos1.X;
00186 float height;
00187 if (isStacked_)
00188 {
00189 height = -yPos1.Y+yoff;
00190 }
00191 else
00192 {
00193 height = -yPos1.Y+yAxis.PhysicalMin.Y;
00194 }
00195
00196 float xoff = (1.0f - baseWidth_)/2.0f*width;
00197 Rectangle r = new Rectangle( (int)(xPos1.X+xoff), (int)yPos1.Y, (int)(width-2*xoff), (int)height );
00198
00199 if (this.Filled)
00200 {
00201 if (r.Height != 0 && r.Width != 0)
00202 {
00203
00204 g.FillRectangle( rectangleBrush_.Get(r), r );
00205 }
00206 }
00207
00208 g.DrawRectangle( Pen, r.X, r.Y, r.Width, r.Height );
00209
00210 }
00211 }
00212
00213
00217 public bool Filled
00218 {
00219 get
00220 {
00221 return filled_;
00222 }
00223 set
00224 {
00225 filled_ = value;
00226 }
00227 }
00228 private bool filled_ = false;
00229
00230
00231 private float baseWidth_ = 1.0f;
00236 public float BaseWidth
00237 {
00238 get
00239 {
00240 return baseWidth_;
00241 }
00242 set
00243 {
00244 if (value > 0.0 && value <= 1.0)
00245 {
00246 baseWidth_ = value;
00247 }
00248 else
00249 {
00250 throw new NPlotException( "Base width must be between 0.0 and 1.0" );
00251 }
00252 }
00253 }
00254
00255
00260 public Axis SuggestXAxis()
00261 {
00262 SequenceAdapter data =
00263 new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
00264
00265 Axis a = data.SuggestXAxis();
00266
00267 PointD p1;
00268 PointD p2;
00269 PointD p3;
00270 PointD p4;
00271 if (data.Count < 2)
00272 {
00273 p1 = data[0];
00274 p1.X -= 1.0;
00275 p2 = data[0];
00276 p3 = p1;
00277 p4 = p2;
00278 }
00279 else
00280 {
00281 p1 = data[0];
00282 p2 = data[1];
00283 p3 = data[data.Count-2];
00284 p4 = data[data.Count-1];
00285 }
00286
00287 double offset1;
00288 double offset2;
00289
00290 if (!center_)
00291 {
00292 offset1 = 0.0f;
00293 offset2 = p4.X - p3.X;
00294 }
00295 else
00296 {
00297 offset1 = (p2.X - p1.X)/2.0f;
00298 offset2 = (p4.X - p3.X)/2.0f;
00299 }
00300
00301 a.WorldMin -= offset1;
00302 a.WorldMax += offset2;
00303
00304 return a;
00305 }
00306
00307
00312 public Axis SuggestYAxis()
00313 {
00314
00315 if ( this.isStacked_ )
00316 {
00317 double tmpMax = 0.0f;
00318 ArrayList adapterList = new ArrayList();
00319
00320 HistogramPlot currentPlot = this;
00321 do
00322 {
00323 adapterList.Add( new SequenceAdapter(
00324 currentPlot.DataSource,
00325 currentPlot.DataMember,
00326 currentPlot.OrdinateData,
00327 currentPlot.AbscissaData )
00328 );
00329 } while ((currentPlot = currentPlot.stackedTo_) != null);
00330
00331 SequenceAdapter[] adapters =
00332 (SequenceAdapter[])adapterList.ToArray(typeof(SequenceAdapter));
00333
00334 for (int i=0; i<adapters[0].Count; ++i)
00335 {
00336 double tmpHeight = 0.0f;
00337 for (int j=0; j<adapters.Length; ++j)
00338 {
00339 tmpHeight += adapters[j][i].Y;
00340 }
00341 tmpMax = Math.Max(tmpMax, tmpHeight);
00342 }
00343
00344 Axis a = new LinearAxis(0.0f,tmpMax);
00345
00346 a.IncreaseRange( 0.08 );
00347 return a;
00348 }
00349 else
00350 {
00351 SequenceAdapter data =
00352 new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
00353
00354 return data.SuggestYAxis();
00355 }
00356 }
00357
00358
00359
00360
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378 private bool center_ = true;
00384 public bool Center
00385 {
00386 set
00387 {
00388 center_ = value;
00389 }
00390 get
00391 {
00392 return center_;
00393 }
00394 }
00395
00396
00400 public bool IsStacked
00401 {
00402 get
00403 {
00404 return isStacked_;
00405 }
00406 }
00407 private bool isStacked_;
00408
00409
00410 private HistogramPlot stackedTo_;
00414 public void StackedTo(HistogramPlot hp)
00415 {
00416 SequenceAdapter data =
00417 new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
00418
00419 SequenceAdapter hpData =
00420 new SequenceAdapter( hp.DataSource, hp.DataMember, hp.OrdinateData, hp.AbscissaData );
00421
00422 if ( hp != null )
00423 {
00424 isStacked_ = true;
00425 if ( hpData.Count != data.Count )
00426 {
00427 throw new NPlotException("Can stack HistogramPlot data only with the same number of datapoints.");
00428 }
00429 for ( int i=0; i < data.Count; ++i )
00430 {
00431 if ( data[i].X != hpData[i].X )
00432 {
00433 throw new NPlotException("Can stack HistogramPlot data only with the same X coordinates.");
00434 }
00435 if ( hpData[i].Y < 0.0f)
00436 {
00437 throw new NPlotException("Can stack HistogramPlot data only with positive Y coordinates.");
00438 }
00439 }
00440 }
00441 stackedTo_ = hp;
00442 }
00443
00444
00450 public void DrawInLegend( Graphics g, Rectangle startEnd )
00451 {
00452
00453 if (Filled)
00454 {
00455 g.FillRectangle( rectangleBrush_.Get(startEnd), startEnd );
00456 }
00457
00458 g.DrawRectangle( Pen, startEnd.X, startEnd.Y, startEnd.Width, startEnd.Height );
00459
00460 }
00461
00462
00466 public System.Drawing.Pen Pen
00467 {
00468 get
00469 {
00470 return pen_;
00471 }
00472 set
00473 {
00474 pen_ = value;
00475 }
00476 }
00477 private System.Drawing.Pen pen_ = new Pen(Color.Black);
00478
00479
00483 public System.Drawing.Color Color
00484 {
00485 set
00486 {
00487 if (pen_ != null)
00488 {
00489 pen_.Color = value;
00490 }
00491 else
00492 {
00493 pen_ = new Pen(value);
00494 }
00495 }
00496 get
00497 {
00498 return pen_.Color;
00499 }
00500 }
00501
00502
00506 public double BaseOffset
00507 {
00508 set
00509 {
00510 baseOffset_ = value;
00511 }
00512 get
00513 {
00514 return baseOffset_;
00515 }
00516 }
00517 private double baseOffset_;
00518
00519
00520 }
00521 }