679 строки
20 KiB
C#
679 строки
20 KiB
C#
/*
|
|
NPlot - A charting library for .NET
|
|
|
|
LogAxis.cs
|
|
Copyright (C) 2003
|
|
Paolo Pierini, Matt Howlett
|
|
|
|
Redistribution and use of NPlot or parts there-of in source and
|
|
binary forms, with or without modification, are permitted provided
|
|
that the following conditions are met:
|
|
|
|
1. Re-distributions in source form must retain at the head of each
|
|
source file the above copyright notice, this list of conditions
|
|
and the following disclaimer.
|
|
|
|
2. Any product ("the product") that makes use NPlot or parts
|
|
there-of must either:
|
|
|
|
(a) allow any user of the product to obtain a complete machine-
|
|
readable copy of the corresponding source code for the
|
|
product and the version of NPlot used for a charge no more
|
|
than your cost of physically performing source distribution,
|
|
on a medium customarily used for software interchange, or:
|
|
|
|
(b) reproduce the following text in the documentation, about
|
|
box or other materials intended to be read by human users
|
|
of the product that is provided to every human user of the
|
|
product:
|
|
|
|
"This product includes software developed as
|
|
part of the NPlot library project available
|
|
from: http://www.nplot.com/"
|
|
|
|
The words "This product" may optionally be replace with
|
|
the actual name of the product.
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.Collections;
|
|
using System.Text;
|
|
|
|
namespace NPlot
|
|
{
|
|
/// <summary>
|
|
/// The class implementing logarithmic axes.
|
|
/// </summary>
|
|
public class LogAxis : Axis
|
|
{
|
|
|
|
/// <summary>
|
|
/// Deep Copy of the LogAxis.
|
|
/// </summary>
|
|
/// <returns>A Copy of the LogAxis Class.</returns>
|
|
public override object Clone()
|
|
{
|
|
LogAxis a = new LogAxis();
|
|
if (this.GetType() != a.GetType())
|
|
{
|
|
throw new NPlotException("Clone not defined in derived type. Help!");
|
|
}
|
|
this.DoClone( this, a );
|
|
return a;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Helper method for Clone (actual implementation)
|
|
/// </summary>
|
|
/// <param name="a">The original object to clone.</param>
|
|
/// <param name="b">The cloned object.</param>
|
|
protected void DoClone(LogAxis b, LogAxis a)
|
|
{
|
|
Axis.DoClone(b,a);
|
|
// add specific elemtents of the class for the deep copy of the object
|
|
a.numberSmallTicks_ = b.numberSmallTicks_;
|
|
a.largeTickValue_ = b.largeTickValue_;
|
|
a.largeTickStep_ = b.largeTickStep_;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Default constructor.
|
|
/// </summary>
|
|
public LogAxis()
|
|
: base()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Copy Constructor
|
|
/// </summary>
|
|
/// <param name="a">The Axis to clone.</param>
|
|
public LogAxis(Axis a)
|
|
: base(a)
|
|
{
|
|
Init();
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="worldMin">Minimum World value for the axis.</param>
|
|
/// <param name="worldMax">Maximum World value for the axis.</param>
|
|
public LogAxis(double worldMin, double worldMax)
|
|
: base( worldMin, worldMax )
|
|
{
|
|
Init();
|
|
}
|
|
|
|
|
|
private void Init()
|
|
{
|
|
this.NumberFormat = "{0:g5}";
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Draw the ticks.
|
|
/// </summary>
|
|
/// <param name="g">The drawing surface on which to draw.</param>
|
|
/// <param name="physicalMin">The minimum physical extent of the axis.</param>
|
|
/// <param name="physicalMax">The maximum physical extent of the axis.</param>
|
|
/// <param name="boundingBox">out: smallest box that completely encompasses all of the ticks and tick labels.</param>
|
|
/// <param name="labelOffset">out: a suitable offset from the axis to draw the axis label.</param>
|
|
/// <returns> An ArrayList containing the offset from the axis required for an axis label
|
|
/// to miss this tick, followed by a bounding rectangle for the tick and tickLabel drawn.</returns>
|
|
protected override void DrawTicks(
|
|
Graphics g,
|
|
Point physicalMin,
|
|
Point physicalMax,
|
|
out object labelOffset,
|
|
out object boundingBox )
|
|
{
|
|
|
|
Point tLabelOffset;
|
|
Rectangle tBoundingBox;
|
|
|
|
labelOffset = this.getDefaultLabelOffset( physicalMin, physicalMax );
|
|
boundingBox = null;
|
|
|
|
ArrayList largeTickPositions;
|
|
ArrayList smallTickPositions;
|
|
this.WorldTickPositions( physicalMin, physicalMax, out largeTickPositions, out smallTickPositions );
|
|
|
|
Point offset = new Point( 0, 0 );
|
|
object bb = null;
|
|
// Missed this protection
|
|
if (largeTickPositions.Count > 0)
|
|
{
|
|
for (int i=0; i<largeTickPositions.Count; ++i)
|
|
{
|
|
StringBuilder label = new StringBuilder();
|
|
// do google search for "format specifier writeline" for help on this.
|
|
label.AppendFormat(this.NumberFormat, (double)largeTickPositions[i]);
|
|
this.DrawTick( g, (double)largeTickPositions[i], this.LargeTickSize, label.ToString(),
|
|
new Point(0,0), physicalMin, physicalMax, out tLabelOffset, out tBoundingBox );
|
|
|
|
Axis.UpdateOffsetAndBounds( ref labelOffset, ref boundingBox, tLabelOffset, tBoundingBox );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// just get the axis bounding box)
|
|
PointF dir = Utils.UnitVector(physicalMin,physicalMax);
|
|
Rectangle rr = new Rectangle( physicalMin.X,
|
|
(int)((physicalMax.X-physicalMin.X)*dir.X),
|
|
physicalMin.Y,
|
|
(int)((physicalMax.Y-physicalMin.Y)*dir.Y) );
|
|
bb = rr;
|
|
}
|
|
|
|
|
|
// missed protection for zero ticks
|
|
if (smallTickPositions.Count > 0)
|
|
{
|
|
for (int i=0; i<smallTickPositions.Count; ++i)
|
|
{
|
|
this.DrawTick( g, (double)smallTickPositions[i], this.SmallTickSize,
|
|
"", new Point(0,0), physicalMin, physicalMax, out tLabelOffset, out tBoundingBox );
|
|
// ignore r for now - assume bb unchanged by small tick bounds.
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Determines the positions, in world coordinates, of the small ticks
|
|
/// if they have not already been generated.
|
|
/// </summary>
|
|
/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
|
|
/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
|
|
/// <param name="largeTickPositions">The positions of the large ticks, unchanged</param>
|
|
/// <param name="smallTickPositions">If null, small tick positions are returned via this parameter. Otherwise this function does nothing.</param>
|
|
internal override void WorldTickPositions_SecondPass(
|
|
Point physicalMin,
|
|
Point physicalMax,
|
|
ArrayList largeTickPositions,
|
|
ref ArrayList smallTickPositions )
|
|
{
|
|
|
|
if (smallTickPositions != null)
|
|
{
|
|
throw new NPlotException( "not expecting smallTickPositions to be set already." );
|
|
}
|
|
|
|
smallTickPositions = new ArrayList();
|
|
|
|
// retrieve the spacing of the big ticks. Remember this is decades!
|
|
double bigTickSpacing = this.DetermineTickSpacing();
|
|
int nSmall = this.DetermineNumberSmallTicks( bigTickSpacing );
|
|
|
|
// now we have to set the ticks
|
|
// let us start with the easy case where the major tick distance
|
|
// is larger than a decade
|
|
if ( bigTickSpacing > 1.0f )
|
|
{
|
|
if (largeTickPositions.Count > 0)
|
|
{
|
|
// deal with the smallticks preceding the
|
|
// first big tick
|
|
double pos1 = (double)largeTickPositions[0];
|
|
while (pos1 > this.WorldMin)
|
|
{
|
|
pos1 = pos1 / 10.0f;
|
|
smallTickPositions.Add( pos1 );
|
|
}
|
|
// now go on for all other Major ticks
|
|
for (int i=0; i<largeTickPositions.Count; ++i )
|
|
{
|
|
double pos = (double)largeTickPositions[i];
|
|
for (int j=1; j<=nSmall; ++j )
|
|
{
|
|
pos=pos*10.0F;
|
|
// check to see if we are still in the range
|
|
if (pos < WorldMax)
|
|
{
|
|
smallTickPositions.Add( pos );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// guess what...
|
|
double [] m = { 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f };
|
|
// Then we deal with the other ticks
|
|
if (largeTickPositions.Count > 0)
|
|
{
|
|
// first deal with the smallticks preceding the first big tick
|
|
// positioning before the first tick
|
|
double pos1=(double)largeTickPositions[0]/10.0f;
|
|
for (int i=0; i<m.Length; i++)
|
|
{
|
|
double pos=pos1*m[i];
|
|
if (pos>this.WorldMin)
|
|
{
|
|
smallTickPositions.Add(pos);
|
|
}
|
|
}
|
|
// now go on for all other Major ticks
|
|
for (int i=0; i<largeTickPositions.Count; ++i )
|
|
{
|
|
pos1=(double)largeTickPositions[i];
|
|
for (int j=0; j<m.Length; ++j )
|
|
{
|
|
double pos=pos1*m[j];
|
|
// check to see if we are still in the range
|
|
if (pos < WorldMax)
|
|
{
|
|
smallTickPositions.Add( pos );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// probably a minor tick would anyway fall in the range
|
|
// find the decade preceding the minimum
|
|
double dec=Math.Floor(Math.Log10(WorldMin));
|
|
double pos1=Math.Pow(10.0,dec);
|
|
for (int i=0; i<m.Length; i++)
|
|
{
|
|
double pos=pos1*m[i];
|
|
if (pos>this.WorldMin && pos< this.WorldMax )
|
|
{
|
|
smallTickPositions.Add(pos);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private static double m_d5Log = -Math.Log10(0.5); // .30103
|
|
private static double m_d5RegionPos = Math.Abs(m_d5Log + ((1 - m_d5Log) / 2)); // ' .6505
|
|
private static double m_d5RegionNeg = Math.Abs(m_d5Log / 2); // '.1505
|
|
|
|
private void CalcGrids( double dLenAxis, int nNumDivisions, ref double dDivisionInterval)
|
|
{
|
|
double dMyInterval = dLenAxis / nNumDivisions;
|
|
double dPower = Math.Log10(dMyInterval);
|
|
dDivisionInterval = 10 ^ (int)dPower;
|
|
double dFixPower = dPower - (int)dPower;
|
|
double d5Region = Math.Abs(dPower - dFixPower);
|
|
double dMyMult;
|
|
if (dPower < 0)
|
|
{
|
|
d5Region = -(dPower - dFixPower);
|
|
dMyMult = 0.5;
|
|
}
|
|
else
|
|
{
|
|
d5Region = 1 - (dPower - dFixPower);
|
|
dMyMult = 5;
|
|
}
|
|
if ((d5Region >= m_d5RegionNeg) && (d5Region <= m_d5RegionPos))
|
|
{
|
|
dDivisionInterval = dDivisionInterval * dMyMult;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines the positions, in world coordinates, of the log spaced large ticks.
|
|
/// </summary>
|
|
/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
|
|
/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
|
|
/// <param name="largeTickPositions">ArrayList containing the positions of the large ticks.</param>
|
|
/// <param name="smallTickPositions">null</param>
|
|
internal override void WorldTickPositions_FirstPass(
|
|
Point physicalMin,
|
|
Point physicalMax,
|
|
out ArrayList largeTickPositions,
|
|
out ArrayList smallTickPositions
|
|
)
|
|
{
|
|
|
|
smallTickPositions = null;
|
|
largeTickPositions = new ArrayList();
|
|
|
|
if ( double.IsNaN(WorldMin) || double.IsNaN(WorldMax) )
|
|
{
|
|
throw new NPlotException( "world extent of axis not set." );
|
|
}
|
|
|
|
double roundTickDist = this.DetermineTickSpacing( );
|
|
|
|
// now determine first tick position.
|
|
double first = 0.0f;
|
|
|
|
// if the user hasn't specified a large tick position.
|
|
if (double.IsNaN(largeTickValue_))
|
|
{
|
|
if( WorldMin > 0.0 )
|
|
{
|
|
|
|
double nToFirst = Math.Floor(Math.Log10(WorldMin) / roundTickDist)+1.0f;
|
|
first = nToFirst * roundTickDist;
|
|
}
|
|
|
|
// could miss one, if first is just below zero.
|
|
if (first-roundTickDist >= Math.Log10(WorldMin))
|
|
{
|
|
first -= roundTickDist;
|
|
}
|
|
}
|
|
|
|
// the user has specified one place they would like a large tick placed.
|
|
else
|
|
{
|
|
first = Math.Log10( this.LargeTickValue );
|
|
|
|
// TODO: check here not too much different.
|
|
// could result in long loop.
|
|
while (first < Math.Log10(WorldMin))
|
|
{
|
|
first += roundTickDist;
|
|
}
|
|
|
|
while (first > Math.Log10(WorldMin)+roundTickDist)
|
|
{
|
|
first -= roundTickDist;
|
|
}
|
|
}
|
|
|
|
double mark = first;
|
|
while (mark <= Math.Log10(WorldMax))
|
|
{
|
|
// up to here only logs are dealt with, but I want to return
|
|
// a real value in the arraylist
|
|
double val;
|
|
val = Math.Pow( 10.0, mark );
|
|
largeTickPositions.Add( val );
|
|
mark += roundTickDist;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Determines the tick spacing.
|
|
/// </summary>
|
|
/// <returns>The tick spacing (in decades!)</returns>
|
|
private double DetermineTickSpacing( )
|
|
{
|
|
if ( double.IsNaN(WorldMin) || double.IsNaN(WorldMax) )
|
|
{
|
|
throw new NPlotException( "world extent of axis is not set." );
|
|
}
|
|
|
|
// if largeTickStep has been set, it is used
|
|
if ( !double.IsNaN( this.largeTickStep_) )
|
|
{
|
|
if ( this.largeTickStep_ <= 0.0f )
|
|
{
|
|
throw new NPlotException( "can't have negative tick step - reverse WorldMin WorldMax instead." );
|
|
}
|
|
|
|
return this.largeTickStep_;
|
|
}
|
|
|
|
double MagRange = (double)(Math.Floor(Math.Log10(WorldMax)) - Math.Floor(Math.Log10(WorldMin))+1.0);
|
|
|
|
if ( MagRange > 0.0 )
|
|
{
|
|
// for now, a simple logic
|
|
// start with a major tick every order of magnitude, and
|
|
// increment if in order not to have more than 10 ticks in
|
|
// the plot.
|
|
double roundTickDist=1.0F;
|
|
int nticks=(int)(MagRange/roundTickDist);
|
|
while (nticks > 10)
|
|
{
|
|
roundTickDist++;
|
|
nticks=(int)(MagRange/roundTickDist);
|
|
}
|
|
return roundTickDist;
|
|
}
|
|
else
|
|
{
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Determines the number of small ticks between two large ticks.
|
|
/// </summary>
|
|
/// <param name="bigTickDist">The distance between two large ticks.</param>
|
|
/// <returns>The number of small ticks.</returns>
|
|
private int DetermineNumberSmallTicks( double bigTickDist )
|
|
{
|
|
// if the big ticks is more than one decade, the
|
|
// small ticks are every decade, I don't let the user set it.
|
|
if (this.numberSmallTicks_ != null && bigTickDist == 1.0f)
|
|
{
|
|
return (int)this.numberSmallTicks_+1;
|
|
}
|
|
|
|
// if we are plotting every decade, we have to
|
|
// put the log ticks. As a start, I put every
|
|
// small tick (.2,.3,.4,.5,.6,.7,.8,.9)
|
|
if (bigTickDist == 1.0f)
|
|
{
|
|
return 8;
|
|
}
|
|
// easy, put a tick every missed decade
|
|
else if (bigTickDist > 1.0f)
|
|
{
|
|
return (int)bigTickDist - 1;
|
|
}
|
|
else
|
|
{
|
|
throw new NPlotException("Wrong Major tick distance setting");
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// The step between large ticks, expressed in decades for the Log scale.
|
|
/// </summary>
|
|
public double LargeTickStep
|
|
{
|
|
set
|
|
{
|
|
largeTickStep_ = value;
|
|
}
|
|
get
|
|
{
|
|
return largeTickStep_;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Position of one of the large ticks [other positions will be calculated relative to this one].
|
|
/// </summary>
|
|
public double LargeTickValue
|
|
{
|
|
set
|
|
{
|
|
largeTickValue_ = value;
|
|
}
|
|
get
|
|
{
|
|
return largeTickValue_;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// The number of small ticks between large ticks.
|
|
/// </summary>
|
|
public int NumberSmallTicks
|
|
{
|
|
set
|
|
{
|
|
numberSmallTicks_ = value;
|
|
}
|
|
}
|
|
|
|
|
|
// Private members
|
|
private object numberSmallTicks_;
|
|
private double largeTickValue_ = double.NaN;
|
|
private double largeTickStep_ = double.NaN;
|
|
|
|
/// <summary>
|
|
/// World to physical coordinate transform.
|
|
/// </summary>
|
|
/// <param name="coord">The coordinate value to transform.</param>
|
|
/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
|
|
/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
|
|
/// <param name="clip">if false, then physical value may extend outside worldMin / worldMax. If true, the physical value returned will be clipped to physicalMin or physicalMax if it lies outside this range.</param>
|
|
/// <returns>The transformed coordinates.</returns>
|
|
/// <remarks>TODO: make Reversed property work for this.</remarks>
|
|
public override PointF WorldToPhysical(
|
|
double coord,
|
|
PointF physicalMin,
|
|
PointF physicalMax,
|
|
bool clip )
|
|
{
|
|
// if want clipped value, return extrema if outside range.
|
|
if (clip)
|
|
{
|
|
if (coord > WorldMax)
|
|
{
|
|
return physicalMax;
|
|
}
|
|
if (coord < WorldMin)
|
|
{
|
|
return physicalMin;
|
|
}
|
|
}
|
|
|
|
if (coord < 0.0f)
|
|
{
|
|
throw new NPlotException( "Cannot have negative values for data using Log Axis" );
|
|
}
|
|
|
|
// inside range or don't want to clip.
|
|
double lrange = (double)(Math.Log10(WorldMax) - Math.Log10(WorldMin));
|
|
double prop = (double)((Math.Log10(coord) - Math.Log10(WorldMin)) / lrange);
|
|
PointF offset = new PointF( (float)(prop * (physicalMax.X - physicalMin.X)),
|
|
(float)(prop * (physicalMax.Y - physicalMin.Y)) );
|
|
|
|
return new PointF( physicalMin.X + offset.X, physicalMin.Y + offset.Y );
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Return the world coordinate of the projection of the point p onto
|
|
/// the axis.
|
|
/// </summary>
|
|
/// <param name="p">The point to project onto the axis</param>
|
|
/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
|
|
/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
|
|
/// <param name="clip">If true, the world value will be clipped to WorldMin or WorldMax as appropriate if it lies outside this range.</param>
|
|
/// <returns>The world value corresponding to the projection of the point p onto the axis.</returns>
|
|
public override double PhysicalToWorld( PointF p, PointF physicalMin, PointF physicalMax, bool clip )
|
|
{
|
|
// use the base method to do the projection on the axis.
|
|
double t = base.PhysicalToWorld( p, physicalMin, physicalMax, clip );
|
|
|
|
// now reconstruct phys dist prop along this assuming linear scale as base method did.
|
|
double v = (t - this.WorldMin) / (this.WorldMax - this.WorldMin);
|
|
|
|
double ret = WorldMin*Math.Pow( WorldMax / WorldMin, v );
|
|
|
|
// if want clipped value, return extrema if outside range.
|
|
if (clip)
|
|
{
|
|
ret = Math.Max( ret, WorldMin );
|
|
ret = Math.Min( ret, WorldMax );
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// The minimum world extent of the axis. Must be greater than zero.
|
|
/// </summary>
|
|
public override double WorldMin
|
|
{
|
|
get
|
|
{
|
|
return (double)base.WorldMin;
|
|
}
|
|
set
|
|
{
|
|
if (value > 0.0f)
|
|
{
|
|
base.WorldMin = value;
|
|
}
|
|
else
|
|
{
|
|
throw new NPlotException("Cannot have negative values in Log Axis");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// The maximum world extent of the axis. Must be greater than zero.
|
|
/// </summary>
|
|
public override double WorldMax
|
|
{
|
|
get
|
|
{
|
|
return (double)base.WorldMax;
|
|
}
|
|
set
|
|
{
|
|
if (value > 0.0F)
|
|
{
|
|
base.WorldMax = value;
|
|
}
|
|
else
|
|
{
|
|
throw new NPlotException("Cannot have negative values in Log Axis");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get whether or not this axis is linear. It is not.
|
|
/// </summary>
|
|
public override bool IsLinear
|
|
{
|
|
get
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|