HistogramPlot.cs

Go to the documentation of this file.
00001 /*
00002 NPlot - A charting library for .NET
00003 
00004 HistogramPlot.cs
00005 Copyright (C) 2003
00006 Matt Howlett, Paolo Pierini
00007 
00008 Redistribution and use of NPlot or parts there-of in source and
00009 binary forms, with or without modification, are permitted provided
00010 that the following conditions are met:
00011 
00012 1. Re-distributions in source form must retain at the head of each
00013    source file the above copyright notice, this list of conditions
00014    and the following disclaimer.
00015 
00016 2. Any product ("the product") that makes use NPlot or parts 
00017    there-of must either:
00018   
00019     (a) allow any user of the product to obtain a complete machine-
00020         readable copy of the corresponding source code for the 
00021         product and the version of NPlot used for a charge no more
00022         than your cost of physically performing source distribution,
00023         on a medium customarily used for software interchange, or:
00024 
00025     (b) reproduce the following text in the documentation, about 
00026         box or other materials intended to be read by human users
00027         of the product that is provided to every human user of the
00028         product: 
00029    
00030               "This product includes software developed as 
00031               part of the NPlot library project available 
00032               from: http://www.nplot.com/" 
00033 
00034         The words "This product" may optionally be replace with 
00035         the actual name of the product.
00036 
00037 ------------------------------------------------------------------------
00038 
00039 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00040 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00041 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00042 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00043 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00044 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00045 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00046 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00047 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00048 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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                                 // (1) determine the top left hand point of the bar (assuming not centered)
00109                                 PointD p1 = data[i];
00110                                 if ( double.IsNaN(p1.X) || double.IsNaN(p1.Y) )
00111                                         continue;
00112                                 
00113                                 // (2) determine the top right hand point of the bar (assuming not centered)
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                                 // (3) now account for plots this may be stacked on top of.
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                                 // (4) now account for centering
00158                                 if ( center_ )
00159                                 {
00160                                         double offset = ( p2.X - p1.X ) / 2.0f;
00161                                         p1.X -= offset;
00162                                         p2.X -= offset;
00163                                 }
00164 
00165                                 // (5) now account for BaseOffset (shift of bar sideways).
00166                 p1.X += baseOffset_;
00167                 p2.X += baseOffset_;
00168 
00169                                 // (6) now get physical coordinates of top two points.
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                                                 // room for optimization maybe.
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                                 // TODO make 0.08 a parameter.
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                 private double centerLine_ = 0.0;
00364                 public double CenterLine
00365                 {
00366                         get
00367                         {
00368                                 return centerLine_;
00369                         }
00370                         set
00371                         {
00372                                 centerLine_ = value;
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 }

Generated on Sat Nov 5 01:04:06 2005 for NPlot by  doxygen 1.4.5