Files
ntpa/NPlot/Legend.cs
T

333 lines
9.2 KiB
C#
Raw Normal View History

2016-03-05 14:06:38 +01:00
/*
* NPlot - A charting library for .NET
*
* Legend.cs
* Copyright (C) 2003-2006 Matt Howlett and others.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of NPlot nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;
namespace NPlot
{
/// <summary>
/// Legend functionality specific to Legends associated with a PlotSurface2D.
/// </summary>
public class Legend : LegendBase
{
/// <summary>
/// Enumeration of possible Legend placements.
/// </summary>
public enum Placement
{
/// <summary>
/// Inside the plot area.
/// </summary>
Inside = 0,
/// <summary>
/// Outside the plot area.
/// </summary>
Outside = 1
}
private int xOffset_;
private int yOffset_;
private PlotSurface2D.XAxisPosition xAttach_;
private PlotSurface2D.YAxisPosition yAttach_;
private Placement horizontalEdgePlacement_;
private Placement verticalEdgePlacement_;
private bool neverShiftAxes_;
/// <summary>
/// Whether or not the positions of the Axes may be shifted to make
/// room for the Legend.
/// </summary>
public bool NeverShiftAxes
{
get
{
return neverShiftAxes_;
}
set
{
neverShiftAxes_ = value;
}
}
/// <summary>
/// Offset from the chosen Y-Axis. TODO: better description.
/// </summary>
public int XOffset
{
get
{
return xOffset_;
}
set
{
xOffset_ = value;
}
}
/// <summary>
/// Offset from the X-Axis. TODO: better description.
/// </summary>
public int YOffset
{
get
{
return yOffset_;
}
set
{
yOffset_ = value;
}
}
/// <summary>
/// Whether or not to attach the legend on the inside of the top
/// or bottom axis (which, is specified using the AttachTo method) or the
/// outside.
/// </summary>
public Legend.Placement VerticalEdgePlacement
{
get
{
return verticalEdgePlacement_;
}
set
{
verticalEdgePlacement_ = value;
}
}
/// <summary>
/// Whether or not to attach the legend on the inside of the
/// left or right axis (which, is specified using the AttachTo method)
/// or the outside.
/// </summary>
public Legend.Placement HorizontalEdgePlacement
{
get
{
return horizontalEdgePlacement_;
}
set
{
horizontalEdgePlacement_ = value;
}
}
/// <summary>
/// Specify the Axes to attach the legend to.
/// </summary>
/// <param name="xa">Specify which horizontal axis the legend should be attached to.</param>
/// <param name="ya">Specify which vertical axis the legend should be attached to.</param>
public void AttachTo( PlotSurface2D.XAxisPosition xa, PlotSurface2D.YAxisPosition ya )
{
xAttach_ = xa;
yAttach_ = ya;
}
/// <summary>
/// Default constructor.
/// </summary>
public Legend()
{
xAttach_ = PlotSurface2D.XAxisPosition.Top;
yAttach_ = PlotSurface2D.YAxisPosition.Right;
xOffset_ = 10;
yOffset_ = 1;
verticalEdgePlacement_ = Placement.Outside;
horizontalEdgePlacement_ = Placement.Inside;
neverShiftAxes_ = false;
}
/// <summary>
/// Updates the PlotSurface2D axes to compensate for the legend.
/// </summary>
/// <param name="pXAxis1">the bottom x axis</param>
/// <param name="pYAxis1">the left y axis</param>
/// <param name="pXAxis2">the top x axis</param>
/// <param name="pYAxis2">the right y axis</param>
/// <param name="plots">list of plots.</param>
/// <param name="scale">scale parameter (for text and other)</param>
/// <param name="padding">padding around plot within bounds.</param>
/// <param name="bounds">graphics surface bounds</param>
/// <param name="position">legend position</param>
public void UpdateAxesPositions(
PhysicalAxis pXAxis1,
PhysicalAxis pYAxis1,
PhysicalAxis pXAxis2,
PhysicalAxis pYAxis2,
ArrayList plots,
float scale, int padding, Rectangle bounds,
out Point position )
{
int leftIndent = 0;
int rightIndent = 0;
int bottomIndent = 0;
int topIndent = 0;
position = new Point(0,0);
// now determine if legend should change any of these (legend should be fully
// visible at all times), and draw legend.
Rectangle legendWidthHeight = this.GetBoundingBox( new Point(0,0), plots, scale );
if (legendWidthHeight.Width > bounds.Width)
{
legendWidthHeight.Width = bounds.Width;
}
// (1) calculate legend position.
// y
position.Y = this.yOffset_;
if ( this.xAttach_ == PlotSurface2D.XAxisPosition.Bottom )
{
position.Y += pYAxis1.PhysicalMin.Y;
if ( this.horizontalEdgePlacement_ == Legend.Placement.Inside )
{
position.Y -= legendWidthHeight.Height;
}
}
else
{
position.Y += pYAxis1.PhysicalMax.Y;
if ( this.horizontalEdgePlacement_ == Legend.Placement.Outside )
{
position.Y -= legendWidthHeight.Height;
}
}
// x
position.X = this.xOffset_;
if ( this.yAttach_ == PlotSurface2D.YAxisPosition.Left )
{
if ( this.verticalEdgePlacement_ == Legend.Placement.Outside )
{
position.X -= legendWidthHeight.Width;
}
position.X += pXAxis1.PhysicalMin.X;
}
else
{
if ( this.verticalEdgePlacement_ == Legend.Placement.Inside )
{
position.X -= legendWidthHeight.Width;
}
position.X += pXAxis1.PhysicalMax.X;
}
// determine update amounts for axes
if ( !this.neverShiftAxes_ )
{
if ( position.X < padding )
{
int changeAmount = -position.X + padding;
// only allow axes to move away from bounds.
if ( changeAmount > 0 )
{
leftIndent = changeAmount;
}
position.X += changeAmount;
}
if ( position.X + legendWidthHeight.Width > bounds.Right - padding )
{
int changeAmount = (position.X - bounds.Right + legendWidthHeight.Width + padding );
// only allow axes to move away from bounds.
if ( changeAmount > 0.0f )
{
rightIndent = changeAmount;
}
position.X -= changeAmount;
}
if ( position.Y < padding )
{
int changeAmount = -position.Y + padding;
// only allow axes to move away from bounds.
if ( changeAmount > 0.0f )
{
topIndent = changeAmount;
}
position.Y += changeAmount;
}
if ( position.Y + legendWidthHeight.Height > bounds.Bottom - padding )
{
int changeAmount = (position.Y - bounds.Bottom + legendWidthHeight.Height + padding );
// only allow axes to move away from bounds.
if ( changeAmount > 0.0f )
{
bottomIndent = changeAmount;
}
position.Y -= changeAmount;
}
// update axes.
pXAxis1.PhysicalMin = new Point( pXAxis1.PhysicalMin.X + leftIndent, pXAxis1.PhysicalMin.Y - bottomIndent );
pXAxis1.PhysicalMax = new Point( pXAxis1.PhysicalMax.X - rightIndent, pXAxis1.PhysicalMax.Y - bottomIndent );
pYAxis1.PhysicalMin = new Point( pYAxis1.PhysicalMin.X + leftIndent, pYAxis1.PhysicalMin.Y - bottomIndent );
pYAxis1.PhysicalMax = new Point( pYAxis1.PhysicalMax.X + leftIndent, pYAxis1.PhysicalMax.Y + topIndent );
pXAxis2.PhysicalMin = new Point( pXAxis2.PhysicalMin.X + leftIndent, pXAxis2.PhysicalMin.Y + topIndent );
pXAxis2.PhysicalMax = new Point( pXAxis2.PhysicalMax.X - rightIndent, pXAxis2.PhysicalMax.Y + topIndent );
pYAxis2.PhysicalMin = new Point( pYAxis2.PhysicalMin.X - rightIndent, pYAxis2.PhysicalMin.Y - bottomIndent );
pYAxis2.PhysicalMax = new Point( pYAxis2.PhysicalMax.X - rightIndent, pYAxis2.PhysicalMax.Y + topIndent );
}
}
}
}