1223 lines
38 KiB
C#
1223 lines
38 KiB
C#
|
//-------------------------------------------------------------
|
|||
|
// <copyright company=<3D>Microsoft Corporation<6F>>
|
|||
|
// Copyright <20> Microsoft Corporation. All Rights Reserved.
|
|||
|
// </copyright>
|
|||
|
//-------------------------------------------------------------
|
|||
|
// @owner=alexgor, deliant
|
|||
|
//=================================================================
|
|||
|
// File: Matrix3D.cs
|
|||
|
//
|
|||
|
// Namespace: DataVisualization.Charting
|
|||
|
//
|
|||
|
// Classes: Matrix3D
|
|||
|
//
|
|||
|
// Purpose: Matrix3D class is used during the 3D drawings to
|
|||
|
// transform plotting area 3D coordinates into the 2D
|
|||
|
// projection coordinates based on rotation and
|
|||
|
// perspective settings.
|
|||
|
//
|
|||
|
// Reviewed: AG - Dec 4, 2002
|
|||
|
// AG - Microsoft 14, 2007
|
|||
|
//
|
|||
|
//===================================================================
|
|||
|
|
|||
|
#region Used namespaces
|
|||
|
|
|||
|
using System;
|
|||
|
using System.Drawing;
|
|||
|
using System.Drawing.Drawing2D;
|
|||
|
using System.Drawing.Text;
|
|||
|
using System.Drawing.Imaging;
|
|||
|
using System.ComponentModel;
|
|||
|
using System.Collections;
|
|||
|
|
|||
|
#if Microsoft_CONTROL
|
|||
|
using System.Windows.Forms.DataVisualization.Charting;
|
|||
|
using System.Windows.Forms.DataVisualization.Charting.Data;
|
|||
|
using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
|
|||
|
using System.Windows.Forms.DataVisualization.Charting.Utilities;
|
|||
|
using System.Windows.Forms.DataVisualization.Charting.Borders3D;
|
|||
|
|
|||
|
#else
|
|||
|
//using System.Web.UI.DataVisualization.Charting.Utilities;
|
|||
|
//using System.Web.UI.DataVisualization.Charting.Borders3D;
|
|||
|
#endif
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#if Microsoft_CONTROL
|
|||
|
namespace System.Windows.Forms.DataVisualization.Charting
|
|||
|
#else
|
|||
|
namespace System.Web.UI.DataVisualization.Charting
|
|||
|
|
|||
|
#endif
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// This class is responsible for all 3D coordinates transformations: Translation,
|
|||
|
/// Rotation, Scale, Perspective and RightAngle Projection. Translation
|
|||
|
/// and rotation are stored in composite matrix (mainMatrix), and scaling,
|
|||
|
/// projection and non-composite translation are stored in private fields.
|
|||
|
/// Matrix is initialized with Chart Area 3D cube, which is invisible boundary
|
|||
|
/// cube of 3D Chart area. The matrix has to be initialized every time
|
|||
|
/// when angles, position or perspective parameters are changed. Method
|
|||
|
/// TransformPoints will apply 3D Transformation on points using
|
|||
|
/// Initialization values: Main matrix and other initialization values.
|
|||
|
/// </summary>
|
|||
|
internal class Matrix3D
|
|||
|
{
|
|||
|
#region Enumerations
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 3D Axis used for rotation
|
|||
|
/// </summary>
|
|||
|
private enum RotationAxis
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Rotation around X axis.
|
|||
|
/// </summary>
|
|||
|
X,
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Rotation around Y axis.
|
|||
|
/// </summary>
|
|||
|
Y,
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Rotation around Z axis.
|
|||
|
/// </summary>
|
|||
|
Z
|
|||
|
}
|
|||
|
|
|||
|
#endregion // Enumerations
|
|||
|
|
|||
|
#region Fields
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Composite matrix.
|
|||
|
/// </summary>
|
|||
|
private float [][] _mainMatrix;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Default translation for chart area cube ( without composition ).
|
|||
|
/// </summary>
|
|||
|
private float _translateX;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Default translation for chart area cube ( without composition ).
|
|||
|
/// </summary>
|
|||
|
private float _translateY;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Default translation for chart area cube ( without composition ).
|
|||
|
/// </summary>
|
|||
|
private float _translateZ;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The value, which is used to rescale chart area.
|
|||
|
/// </summary>
|
|||
|
private float _scale;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The value used for Isometric Shift.
|
|||
|
/// </summary>
|
|||
|
private float _shiftX;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The value used for Isometric Shift.
|
|||
|
/// </summary>
|
|||
|
private float _shiftY;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Perspective value.
|
|||
|
/// </summary>
|
|||
|
internal float _perspective;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Isometric projection.
|
|||
|
/// </summary>
|
|||
|
private bool _rightAngleAxis;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The value, which is used for perspective.
|
|||
|
/// </summary>
|
|||
|
private float _perspectiveFactor = float.NaN;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The value, which is used to set projection plane.
|
|||
|
/// </summary>
|
|||
|
private float _perspectiveZ;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// X Angle.
|
|||
|
/// </summary>
|
|||
|
private float _angleX;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Y Angle.
|
|||
|
/// </summary>
|
|||
|
private float _angleY;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Private fields used for lighting
|
|||
|
/// </summary>
|
|||
|
Point3D [] _lightVectors = new Point3D[7];
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// LightStyle Style
|
|||
|
/// </summary>
|
|||
|
LightStyle _lightStyle;
|
|||
|
|
|||
|
#endregion // Fields
|
|||
|
|
|||
|
#region Properties
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets the X Angle.
|
|||
|
/// </summary>
|
|||
|
internal float AngleX
|
|||
|
{
|
|||
|
get { return _angleX; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets the Y Angle.
|
|||
|
/// </summary>
|
|||
|
internal float AngleY
|
|||
|
{
|
|||
|
get { return _angleY; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Get perspective value.
|
|||
|
/// </summary>
|
|||
|
internal float Perspective
|
|||
|
{
|
|||
|
get { return _perspective; }
|
|||
|
}
|
|||
|
|
|||
|
#endregion // Properties
|
|||
|
|
|||
|
#region Internal and Public Methods
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Constructor for Matrix 3D
|
|||
|
/// </summary>
|
|||
|
public Matrix3D()
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Checks if 3D matrix was initialized.
|
|||
|
/// </summary>
|
|||
|
/// <returns>True if matrix was initialized.</returns>
|
|||
|
public bool IsInitialized()
|
|||
|
{
|
|||
|
return (this._mainMatrix != null);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Initialize Matrix 3D. This method calculates how much a chart area
|
|||
|
/// cube has to be resized to fit Inner Plotting Area rectangle. Order
|
|||
|
/// of operation is following: Translation for X and Y axes, Rotation
|
|||
|
/// by X-axis, Rotation by Y-axis and same scaling for all axes. All
|
|||
|
/// other elements, which belongs to this chart area cube (Data points,
|
|||
|
/// grid lines etc.) has to follow same order. Translation and rotation
|
|||
|
/// form composite matrix mainMatrix. Scale has to be allied separately.
|
|||
|
/// </summary>
|
|||
|
/// <param name="innerPlotRectangle">Inner Plotting Area position. Chart area cube has to be inside this rectangle</param>
|
|||
|
/// <param name="depth">Depth of chart area cube</param>
|
|||
|
/// <param name="angleX">Angle of rotation by X axis.</param>
|
|||
|
/// <param name="angleY">Angle of rotation by Y axis.</param>
|
|||
|
/// <param name="perspective">Perspective in percentages</param>
|
|||
|
/// <param name="rightAngleAxis">Right angle flag.</param>
|
|||
|
internal void Initialize(
|
|||
|
RectangleF innerPlotRectangle,
|
|||
|
float depth,
|
|||
|
float angleX,
|
|||
|
float angleY,
|
|||
|
float perspective,
|
|||
|
bool rightAngleAxis )
|
|||
|
{
|
|||
|
// Initialization for mainMatrix
|
|||
|
Reset();
|
|||
|
|
|||
|
// Remember non-composite translation
|
|||
|
_translateX = innerPlotRectangle.X+innerPlotRectangle.Width/2;
|
|||
|
_translateY = innerPlotRectangle.Y+innerPlotRectangle.Height/2;
|
|||
|
_translateZ = depth / 2F;
|
|||
|
float width = innerPlotRectangle.Width;
|
|||
|
float height = innerPlotRectangle.Height;
|
|||
|
this._perspective = perspective;
|
|||
|
this._rightAngleAxis = rightAngleAxis;
|
|||
|
|
|||
|
// Remember Angles
|
|||
|
this._angleX = angleX;
|
|||
|
this._angleY = angleY;
|
|||
|
|
|||
|
// Change Degrees to radians.
|
|||
|
angleX = angleX / 180F * (float)Math.PI;
|
|||
|
angleY = angleY / 180F * (float)Math.PI;
|
|||
|
|
|||
|
// Set points for 3D Bar which represents 3D Chart Area Cube.
|
|||
|
Point3D [] points = Set3DBarPoints( width, height, depth );
|
|||
|
|
|||
|
// Translate Chart Area Cube WITH CENTER OF ROTATION - COMPOSITE TRANSLATION.
|
|||
|
Translate( _translateX, _translateY, 0 );
|
|||
|
|
|||
|
// Non Isometric projection
|
|||
|
if( !rightAngleAxis )
|
|||
|
{
|
|||
|
// Rotate Chart Area Cube by X axis.
|
|||
|
Rotate( angleX, RotationAxis.X );
|
|||
|
|
|||
|
// Rotate Chart Area Cube by Y axis.
|
|||
|
Rotate( angleY, RotationAxis.Y );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if( this._angleY >= 45 )
|
|||
|
{
|
|||
|
// Rotate Chart Area Cube by Y axis.
|
|||
|
Rotate( Math.PI / 2, RotationAxis.Y );
|
|||
|
}
|
|||
|
else if( this._angleY <= -45 )
|
|||
|
{
|
|||
|
// Rotate Chart Area Cube by Y axis.
|
|||
|
Rotate( -Math.PI / 2, RotationAxis.Y );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Apply composed transformation ( Translation and rotation ).
|
|||
|
GetValues( points );
|
|||
|
|
|||
|
float maxZ = float.MinValue;
|
|||
|
|
|||
|
if( perspective != 0F || rightAngleAxis )
|
|||
|
{
|
|||
|
// Find projection plane
|
|||
|
foreach( Point3D point in points )
|
|||
|
{
|
|||
|
if( point.Z > maxZ )
|
|||
|
maxZ = point.Z;
|
|||
|
}
|
|||
|
|
|||
|
// Set Projection plane
|
|||
|
_perspectiveZ = maxZ;
|
|||
|
}
|
|||
|
|
|||
|
if( perspective != 0F )
|
|||
|
{
|
|||
|
_perspectiveFactor = perspective / 2000F;
|
|||
|
|
|||
|
// Apply perspective
|
|||
|
ApplyPerspective( points );
|
|||
|
}
|
|||
|
|
|||
|
// Isometric projection is active
|
|||
|
if( rightAngleAxis )
|
|||
|
{
|
|||
|
RightAngleProjection( points );
|
|||
|
|
|||
|
float minX = 0F;
|
|||
|
float minY = 0F;
|
|||
|
float maxX = 0F;
|
|||
|
float maxY = 0F;
|
|||
|
|
|||
|
// Point loop
|
|||
|
foreach( Point3D point in points )
|
|||
|
{
|
|||
|
if( point.X - _translateX < 0F && Math.Abs( point.X - _translateX ) > minX )
|
|||
|
minX = Math.Abs( point.X - _translateX );
|
|||
|
|
|||
|
if( point.X - _translateX >=0F && Math.Abs( point.X - _translateX ) > maxX )
|
|||
|
maxX = Math.Abs( point.X - _translateX );
|
|||
|
|
|||
|
if( point.Y - _translateY < 0F && Math.Abs( point.Y - _translateY ) > minY )
|
|||
|
minY = Math.Abs( point.Y - _translateY );
|
|||
|
|
|||
|
if( point.Y - _translateY >=0F && Math.Abs( point.Y - _translateY ) > maxY )
|
|||
|
maxY = Math.Abs( point.Y - _translateY );
|
|||
|
}
|
|||
|
|
|||
|
_shiftX = (maxX - minX)/2F;
|
|||
|
_shiftY = (maxY - minY)/2F;
|
|||
|
RightAngleShift( points );
|
|||
|
}
|
|||
|
|
|||
|
// This code searches for value, which will be used for scaling.
|
|||
|
float maxXScale = float.MinValue;
|
|||
|
float maxYScale = float.MinValue;
|
|||
|
|
|||
|
foreach( Point3D point in points )
|
|||
|
{
|
|||
|
// Find maximum relative distance for X axis.
|
|||
|
// Relative distance is (distance from the center of plotting area
|
|||
|
// position) / (distance from the edge of rectangle to
|
|||
|
// the center of the rectangle).
|
|||
|
if( maxXScale < Math.Abs(point.X - _translateX) / width * 2 )
|
|||
|
maxXScale = Math.Abs(point.X - _translateX) / width * 2;
|
|||
|
|
|||
|
// Find maximum relative distance for Y axis.
|
|||
|
if( maxYScale < Math.Abs(point.Y - _translateY) / height * 2 )
|
|||
|
maxYScale = Math.Abs(point.Y - _translateY) / height * 2;
|
|||
|
}
|
|||
|
|
|||
|
// Remember scale factor
|
|||
|
_scale = (maxYScale > maxXScale ) ? maxYScale : maxXScale;
|
|||
|
|
|||
|
// Apply scaling
|
|||
|
Scale( points );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Apply transformations on array od 3D Points. Order of operation is
|
|||
|
/// following: Translation ( Set coordinate system for 0:100 to -50:50
|
|||
|
/// Center of rotation is always 0), Composite Translation for X and Y
|
|||
|
/// axes ( Moving center of rotation ), Rotation by X-axis, Rotation
|
|||
|
/// by Y-axis, perspective and same scaling for all axes.
|
|||
|
/// </summary>
|
|||
|
/// <param name="points">3D Points array.</param>
|
|||
|
public void TransformPoints( Point3D[] points )
|
|||
|
{
|
|||
|
TransformPoints( points, true );
|
|||
|
}
|
|||
|
#if RS_DEADCODE
|
|||
|
/// <summary>
|
|||
|
/// This Method returns scale factor
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
internal float GetScale()
|
|||
|
{
|
|||
|
return scale;
|
|||
|
}
|
|||
|
#endif //RS_DEADCODE
|
|||
|
#endregion // Internal and Public Methods
|
|||
|
|
|||
|
#region Private Methods
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Apply transformations on array od 3D Points. Order of operation is
|
|||
|
/// following: Translation ( Set coordinate system for 0:100 to -50:50
|
|||
|
/// Center of rotation is always 0), Composite Translation for X and Y
|
|||
|
/// axes ( Moving center of rotation ), Rotation by X-axis, Rotation
|
|||
|
/// by Y-axis, perspective and same scaling for all axes.
|
|||
|
/// </summary>
|
|||
|
/// <param name="points">3D Points array.</param>
|
|||
|
/// <param name="withPerspective">Applay Perspective</param>
|
|||
|
private void TransformPoints( Point3D[] points, bool withPerspective )
|
|||
|
{
|
|||
|
// Matrix is not initialized.
|
|||
|
if( _mainMatrix == null )
|
|||
|
{
|
|||
|
throw new InvalidOperationException(SR.ExceptionMatrix3DNotinitialized);
|
|||
|
}
|
|||
|
|
|||
|
// Translate point. CENTER OF ROTATION is 0 and that center is in
|
|||
|
// the middle of chart area 3D CUBE. Translate method cannot
|
|||
|
// be used because composite translation WILL MOVE
|
|||
|
// CENTER OF ROTATION.
|
|||
|
foreach( Point3D point in points )
|
|||
|
{
|
|||
|
point.X -= _translateX;
|
|||
|
point.Y -= _translateY;
|
|||
|
point.Z -= _translateZ;
|
|||
|
}
|
|||
|
|
|||
|
// Transform points using composite mainMatrix. (Translation of points together with
|
|||
|
// Center of rotation and rotations by X and Y axes).
|
|||
|
GetValues( points );
|
|||
|
|
|||
|
// Apply perspective
|
|||
|
if( _perspective != 0F && withPerspective )
|
|||
|
{
|
|||
|
ApplyPerspective( points );
|
|||
|
}
|
|||
|
|
|||
|
// RightAngle Projection
|
|||
|
if( _rightAngleAxis )
|
|||
|
{
|
|||
|
RightAngleProjection( points );
|
|||
|
RightAngleShift( points );
|
|||
|
}
|
|||
|
|
|||
|
// Scales data points. Scaling has to be performed SEPARATELY from
|
|||
|
// composite matrix. If scale is used with composite matrix after
|
|||
|
// rotation, scaling will deform object.
|
|||
|
Scale( points );
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// This method adjusts a position of 3D Chart Area cube. This
|
|||
|
/// method will translate chart for better use of the inner
|
|||
|
/// plotting area. Center of rotation is shifted for
|
|||
|
/// right Angle projection.
|
|||
|
/// </summary>
|
|||
|
/// <param name="points">3D Points array.</param>
|
|||
|
private void RightAngleShift( Point3D [] points )
|
|||
|
{
|
|||
|
foreach( Point3D point in points )
|
|||
|
{
|
|||
|
point.X = point.X - _shiftX;
|
|||
|
point.Y = point.Y - _shiftY;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Method used to calculate right Angle projection.
|
|||
|
/// </summary>
|
|||
|
/// <param name="points">3D points array.</param>
|
|||
|
private void RightAngleProjection( Point3D [] points )
|
|||
|
{
|
|||
|
float coorectionAngle = 45F;
|
|||
|
|
|||
|
float xFactor = this._angleX / 45;
|
|||
|
|
|||
|
float yFactor;
|
|||
|
|
|||
|
if( this._angleY >= 45 )
|
|||
|
{
|
|||
|
yFactor = (this._angleY - 90) / coorectionAngle;
|
|||
|
}
|
|||
|
else if ( this._angleY <= -45 )
|
|||
|
{
|
|||
|
yFactor = ( this._angleY + 90 ) / coorectionAngle;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
yFactor = this._angleY / coorectionAngle;
|
|||
|
}
|
|||
|
|
|||
|
// Projection formula
|
|||
|
// perspectiveZ - Position of perspective plain.
|
|||
|
// Perspective Factor - Intensity of projection.
|
|||
|
foreach( Point3D point in points )
|
|||
|
{
|
|||
|
point.X = point.X + ( _perspectiveZ - point.Z ) * yFactor;
|
|||
|
point.Y = point.Y - ( _perspectiveZ - point.Z ) * xFactor;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Method is used for Planar Geometric projection.
|
|||
|
/// </summary>
|
|||
|
/// <param name="points">3D Points array.</param>
|
|||
|
private void ApplyPerspective( Point3D [] points )
|
|||
|
{
|
|||
|
// Projection formula
|
|||
|
// perspectiveZ - Position of perspective plain.
|
|||
|
// perspectiveFactor - Intensity of projection.
|
|||
|
foreach( Point3D point in points )
|
|||
|
{
|
|||
|
point.X = _translateX + (point.X - _translateX) / ( 1 + (_perspectiveZ - point.Z) * _perspectiveFactor);
|
|||
|
point.Y = _translateY + (point.Y - _translateY) / ( 1 + (_perspectiveZ - point.Z) * _perspectiveFactor);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Scales data points. Scaling has to be performed SEPARATELY from
|
|||
|
/// composite matrix. If scale is used with composite matrix after
|
|||
|
/// rotation, scaling will deform object.
|
|||
|
/// </summary>
|
|||
|
/// <param name="points">3D Points array.</param>
|
|||
|
private void Scale( Point3D [] points )
|
|||
|
{
|
|||
|
foreach( Point3D point in points )
|
|||
|
{
|
|||
|
point.X = _translateX + (point.X - _translateX) / _scale;
|
|||
|
point.Y = _translateY + (point.Y - _translateY) / _scale;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Prepend to this Matrix object a translation. This method is used
|
|||
|
/// only if CENTER OF ROTATION HAS TO BE MOVED.
|
|||
|
/// </summary>
|
|||
|
/// <param name="dx">Translate in x axis direction.</param>
|
|||
|
/// <param name="dy">Translate in y axis direction.</param>
|
|||
|
/// <param name="dz">Translate in z axis direction.</param>
|
|||
|
private void Translate( float dx, float dy, float dz )
|
|||
|
{
|
|||
|
float [][] translationMatrix = new float[4][];
|
|||
|
translationMatrix[0] = new float[4];
|
|||
|
translationMatrix[1] = new float[4];
|
|||
|
translationMatrix[2] = new float[4];
|
|||
|
translationMatrix[3] = new float[4];
|
|||
|
|
|||
|
// Matrix initialization
|
|||
|
// Row loop
|
|||
|
for( int row = 0; row < 4; row ++ )
|
|||
|
{
|
|||
|
// Column loop
|
|||
|
for( int column = 0; column < 4; column ++ )
|
|||
|
{
|
|||
|
// For initialization: Diagonal matrix elements are equal to one
|
|||
|
// and all other elements are equal to zero.
|
|||
|
if( row == column )
|
|||
|
{
|
|||
|
translationMatrix[row][column] = 1F;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
translationMatrix[row][column] = 0F;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Set translation values to the matrix
|
|||
|
translationMatrix[0][3] = dx;
|
|||
|
translationMatrix[1][3] = dy;
|
|||
|
translationMatrix[2][3] = dz;
|
|||
|
|
|||
|
// Translate main Matrix
|
|||
|
Multiply( translationMatrix, MatrixOrder.Prepend, true );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// This method initialize and set default values for mainMatrix ( there is no rotation and translation )
|
|||
|
/// </summary>
|
|||
|
private void Reset()
|
|||
|
{
|
|||
|
// First element is row and second element is column !!!
|
|||
|
_mainMatrix = new float[4][];
|
|||
|
_mainMatrix[0] = new float[4];
|
|||
|
_mainMatrix[1] = new float[4];
|
|||
|
_mainMatrix[2] = new float[4];
|
|||
|
_mainMatrix[3] = new float[4];
|
|||
|
|
|||
|
// Matrix initialization
|
|||
|
// Row loop
|
|||
|
for( int row = 0; row < 4; row ++ )
|
|||
|
{
|
|||
|
// Column loop
|
|||
|
for( int column = 0; column < 4; column ++ )
|
|||
|
{
|
|||
|
// For initialization: Diagonal matrix elements are equal to one
|
|||
|
// and all other elements are equal to zero.
|
|||
|
if( row == column )
|
|||
|
{
|
|||
|
_mainMatrix[row][column] = 1F;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
_mainMatrix[row][column] = 0F;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Multiplies this Matrix object by the matrix specified in the
|
|||
|
/// matrix parameter, and in the order specified in the order parameter.
|
|||
|
/// </summary>
|
|||
|
/// <param name="mulMatrix">The Matrix object by which this Matrix object is to be multiplied.</param>
|
|||
|
/// <param name="order">The MatrixOrder enumeration that represents the order of the multiplication. If the specified order is MatrixOrder.Prepend, this Matrix object is multiplied by the specified matrix in a prepended order. If the specified order is MatrixOrder.Append, this Matrix object is multiplied by the specified matrix in an appended order.</param>
|
|||
|
/// <param name="setMainMatrix">Set main matrix to be result of multiplication</param>
|
|||
|
/// <returns>Matrix multiplication result.</returns>
|
|||
|
private float[][] Multiply( float [][] mulMatrix, MatrixOrder order, bool setMainMatrix )
|
|||
|
{
|
|||
|
// A matrix which is result of matrix multiplication
|
|||
|
// of mulMatrix and mainMatrix
|
|||
|
float [][] resultMatrix = new float[4][];
|
|||
|
resultMatrix[0] = new float[4];
|
|||
|
resultMatrix[1] = new float[4];
|
|||
|
resultMatrix[2] = new float[4];
|
|||
|
resultMatrix[3] = new float[4];
|
|||
|
|
|||
|
// Row loop
|
|||
|
for( int row = 0; row < 4; row ++ )
|
|||
|
{
|
|||
|
// Column loop
|
|||
|
for( int column = 0; column < 4; column ++ )
|
|||
|
{
|
|||
|
// Initialize element
|
|||
|
resultMatrix[row][column ] = 0F;
|
|||
|
for( int sumIndx = 0; sumIndx < 4; sumIndx ++ )
|
|||
|
{
|
|||
|
// Find matrix element
|
|||
|
if( order == MatrixOrder.Prepend )
|
|||
|
{
|
|||
|
// Order of matrix multiplication
|
|||
|
resultMatrix[row][column ] += _mainMatrix[row][sumIndx ] * mulMatrix[sumIndx][column];
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Order of matrix multiplication
|
|||
|
resultMatrix[row][column] += mulMatrix[row][sumIndx] * _mainMatrix[sumIndx][column];
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Set result matrix to be main matrix
|
|||
|
if( setMainMatrix )
|
|||
|
{
|
|||
|
_mainMatrix = resultMatrix;
|
|||
|
}
|
|||
|
|
|||
|
return resultMatrix;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Multiplies this Matrix object by the Vector specified in the
|
|||
|
/// vector parameter.
|
|||
|
/// </summary>
|
|||
|
/// <param name="mulVector">The vector object by which this Matrix object is to be multiplied.</param>
|
|||
|
/// <param name="resultVector">Vector which is result of matrix and vector multiplication.</param>
|
|||
|
private void MultiplyVector( float [] mulVector, ref float [] resultVector )
|
|||
|
{
|
|||
|
// Row loop
|
|||
|
for( int row = 0; row < 3; row ++ )
|
|||
|
{
|
|||
|
// Initialize element
|
|||
|
resultVector[ row ] = 0F;
|
|||
|
|
|||
|
// Column loop
|
|||
|
for( int column = 0; column < 4; column ++ )
|
|||
|
{
|
|||
|
// Find matrix element
|
|||
|
resultVector[ row ] += _mainMatrix[row][column] * mulVector[ column ];
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Prepend to this Matrix object a clockwise rotation, around the axis and by the specified angle.
|
|||
|
/// </summary>
|
|||
|
/// <param name="angle">Angle to rotate</param>
|
|||
|
/// <param name="axis">Axis used for rotation</param>
|
|||
|
private void Rotate( double angle, RotationAxis axis )
|
|||
|
{
|
|||
|
float [][] rotationMatrix = new float[4][];
|
|||
|
rotationMatrix[0] = new float[4];
|
|||
|
rotationMatrix[1] = new float[4];
|
|||
|
rotationMatrix[2] = new float[4];
|
|||
|
rotationMatrix[3] = new float[4];
|
|||
|
|
|||
|
// Change angle direction
|
|||
|
angle = -1F * angle;
|
|||
|
|
|||
|
// Matrix initialization
|
|||
|
// Row loop
|
|||
|
for( int row = 0; row < 4; row ++ )
|
|||
|
{
|
|||
|
// Column loop
|
|||
|
for( int column = 0; column < 4; column ++ )
|
|||
|
{
|
|||
|
// For initialization: Diagonal matrix elements are equal to one
|
|||
|
// and all other elements are equal to zero.
|
|||
|
if( row == column )
|
|||
|
{
|
|||
|
rotationMatrix[row][column] = 1F;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
rotationMatrix[row][column] = 0F;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Rotation about axis
|
|||
|
switch( axis )
|
|||
|
{
|
|||
|
// Rotation about X axis
|
|||
|
case RotationAxis.X:
|
|||
|
rotationMatrix[1][1] = (float)Math.Cos( angle );
|
|||
|
rotationMatrix[1][2] = (float)-Math.Sin( angle );
|
|||
|
rotationMatrix[2][1] = (float)Math.Sin( angle );
|
|||
|
rotationMatrix[2][2] = (float)Math.Cos( angle );
|
|||
|
break;
|
|||
|
|
|||
|
// Rotation about Y axis
|
|||
|
case RotationAxis.Y:
|
|||
|
rotationMatrix[0][0] = (float)Math.Cos( angle );
|
|||
|
rotationMatrix[0][2] = (float)Math.Sin( angle );
|
|||
|
rotationMatrix[2][0] = (float)-Math.Sin( angle );
|
|||
|
rotationMatrix[2][2] = (float)Math.Cos( angle );
|
|||
|
break;
|
|||
|
|
|||
|
// Rotation about Z axis
|
|||
|
case RotationAxis.Z:
|
|||
|
rotationMatrix[0][0] = (float)Math.Cos( angle );
|
|||
|
rotationMatrix[0][1] = (float)-Math.Sin( angle );
|
|||
|
rotationMatrix[1][0] = (float)Math.Sin( angle );
|
|||
|
rotationMatrix[1][1] = (float)Math.Cos( angle );
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
// Rotate Main matrix
|
|||
|
Multiply( rotationMatrix, MatrixOrder.Prepend, true );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Returns transformed x and y values from x, y and z values
|
|||
|
/// and composed main matrix values (All rotations,
|
|||
|
/// translations and scaling).
|
|||
|
/// </summary>
|
|||
|
/// <param name="points">Array of 3D points.</param>
|
|||
|
private void GetValues( Point3D [] points )
|
|||
|
{
|
|||
|
// Create one dimensional matrix (vector)
|
|||
|
float [] inputVector = new float[4];
|
|||
|
|
|||
|
// A vector which is result of matrix and vector multiplication
|
|||
|
float [] resultVector = new float[4];
|
|||
|
|
|||
|
foreach( Point3D point in points )
|
|||
|
{
|
|||
|
// Fill input vector with x, y and z coordinates
|
|||
|
inputVector[0] = point.X;
|
|||
|
inputVector[1] = point.Y;
|
|||
|
inputVector[2] = point.Z;
|
|||
|
inputVector[3] = 1;
|
|||
|
|
|||
|
// Apply 3D transformations.
|
|||
|
MultiplyVector( inputVector, ref resultVector );
|
|||
|
|
|||
|
// Return x and y coordinates.
|
|||
|
point.X = resultVector[0];
|
|||
|
point.Y = resultVector[1];
|
|||
|
point.Z = resultVector[2];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Set points for 3D Bar which represents 3D Chart Area.
|
|||
|
/// </summary>
|
|||
|
/// <param name="dx">Width of the bar 3D.</param>
|
|||
|
/// <param name="dy">Height of the bar 3D.</param>
|
|||
|
/// <param name="dz">Depth of the bar 3D.</param>
|
|||
|
/// <returns>Collection of Points 3D.</returns>
|
|||
|
private Point3D [] Set3DBarPoints( float dx, float dy, float dz )
|
|||
|
{
|
|||
|
Point3D [] points = new Point3D[8];
|
|||
|
|
|||
|
// ********************************************
|
|||
|
// 3D Bar side: Front
|
|||
|
// ********************************************
|
|||
|
points[0] = new Point3D(-dx/2, -dy/2, dz/2);
|
|||
|
points[1] = new Point3D(dx/2, -dy/2, dz/2);
|
|||
|
points[2] = new Point3D(dx/2, dy/2, dz/2);
|
|||
|
points[3] = new Point3D(-dx/2, dy/2, dz/2);
|
|||
|
|
|||
|
// ********************************************
|
|||
|
// 3D Bar side: Back
|
|||
|
// ********************************************
|
|||
|
points[4] = new Point3D(-dx/2, -dy/2, -dz/2);
|
|||
|
points[5] = new Point3D(dx/2, -dy/2, -dz/2);
|
|||
|
points[6] = new Point3D(dx/2, dy/2, -dz/2);
|
|||
|
points[7] = new Point3D(-dx/2, dy/2, -dz/2);
|
|||
|
|
|||
|
return points;
|
|||
|
}
|
|||
|
|
|||
|
#endregion // Private Methods
|
|||
|
|
|||
|
#region Lighting Methods
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Initial Lighting. Use matrix transformation only once
|
|||
|
/// for Normal vectors.
|
|||
|
/// </summary>
|
|||
|
/// <param name="lightStyle">LightStyle Style</param>
|
|||
|
internal void InitLight( LightStyle lightStyle )
|
|||
|
{
|
|||
|
// Set LightStyle Style
|
|||
|
this._lightStyle = lightStyle;
|
|||
|
|
|||
|
// Center of rotation
|
|||
|
_lightVectors[0] = new Point3D( 0F, 0F, 0F );
|
|||
|
|
|||
|
// Front side normal Vector.
|
|||
|
_lightVectors[1] = new Point3D( 0F, 0F, 1F );
|
|||
|
|
|||
|
// Back side normal Vector.
|
|||
|
_lightVectors[2] = new Point3D( 0F, 0F, -1F );
|
|||
|
|
|||
|
// Left side normal Vector.
|
|||
|
_lightVectors[3] = new Point3D( -1F, 0F, 0F );
|
|||
|
|
|||
|
// Right side normal Vector.
|
|||
|
_lightVectors[4] = new Point3D( 1F, 0F, 0F );
|
|||
|
|
|||
|
// Top side normal Vector.
|
|||
|
_lightVectors[5] = new Point3D( 0F, -1F, 0F );
|
|||
|
|
|||
|
// Bottom side normal Vector.
|
|||
|
_lightVectors[6] = new Point3D( 0F, 1F, 0F );
|
|||
|
|
|||
|
// Apply matrix transformations
|
|||
|
TransformPoints( _lightVectors, false );
|
|||
|
|
|||
|
// ********************************************************
|
|||
|
// LightStyle Vector and normal vectors have to have same center.
|
|||
|
// Shift Normal vectors.
|
|||
|
// ********************************************************
|
|||
|
|
|||
|
// Front Side shift
|
|||
|
_lightVectors[1].X -= _lightVectors[0].X;
|
|||
|
_lightVectors[1].Y -= _lightVectors[0].Y;
|
|||
|
_lightVectors[1].Z -= _lightVectors[0].Z;
|
|||
|
|
|||
|
// Back Side shift
|
|||
|
_lightVectors[2].X -= _lightVectors[0].X;
|
|||
|
_lightVectors[2].Y -= _lightVectors[0].Y;
|
|||
|
_lightVectors[2].Z -= _lightVectors[0].Z;
|
|||
|
|
|||
|
// Left Side shift
|
|||
|
_lightVectors[3].X -= _lightVectors[0].X;
|
|||
|
_lightVectors[3].Y -= _lightVectors[0].Y;
|
|||
|
_lightVectors[3].Z -= _lightVectors[0].Z;
|
|||
|
|
|||
|
// Right Side shift
|
|||
|
_lightVectors[4].X -= _lightVectors[0].X;
|
|||
|
_lightVectors[4].Y -= _lightVectors[0].Y;
|
|||
|
_lightVectors[4].Z -= _lightVectors[0].Z;
|
|||
|
|
|||
|
// Top Side shift
|
|||
|
_lightVectors[5].X -= _lightVectors[0].X;
|
|||
|
_lightVectors[5].Y -= _lightVectors[0].Y;
|
|||
|
_lightVectors[5].Z -= _lightVectors[0].Z;
|
|||
|
|
|||
|
// Bottom Side shift
|
|||
|
_lightVectors[6].X -= _lightVectors[0].X;
|
|||
|
_lightVectors[6].Y -= _lightVectors[0].Y;
|
|||
|
_lightVectors[6].Z -= _lightVectors[0].Z;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Return intensity of lightStyle for 3D Cube. There are tree types of lights: None,
|
|||
|
/// Simplistic and Realistic. None Style have same lightStyle intensity on
|
|||
|
/// all polygons. Normal vector doesn<73>t have influence on this type
|
|||
|
/// of lighting. Simplistic style have lightStyle source, which is
|
|||
|
/// rotated together with scene. Realistic lighting have fixed lightStyle
|
|||
|
/// source and intensity of lightStyle is change when scene is rotated.
|
|||
|
/// </summary>
|
|||
|
/// <param name="surfaceColor">Color used for polygons without lighting</param>
|
|||
|
/// <param name="front">Color corrected with intensity of lightStyle for Front side of the 3D Rectangle</param>
|
|||
|
/// <param name="back">Color corrected with intensity of lightStyle for Back side of the 3D Rectangle</param>
|
|||
|
/// <param name="left">Color corrected with intensity of lightStyle for Left side of the 3D Rectangle</param>
|
|||
|
/// <param name="right">Color corrected with intensity of lightStyle for Right side of the 3D Rectangle</param>
|
|||
|
/// <param name="top">Color corrected with intensity of lightStyle for Top side of the 3D Rectangle</param>
|
|||
|
/// <param name="bottom">Color corrected with intensity of lightStyle for Bottom side of the 3D Rectangle</param>
|
|||
|
internal void GetLight( Color surfaceColor, out Color front, out Color back, out Color left, out Color right, out Color top, out Color bottom )
|
|||
|
{
|
|||
|
switch( _lightStyle )
|
|||
|
{
|
|||
|
// LightStyle style is None
|
|||
|
case LightStyle.None:
|
|||
|
{
|
|||
|
front = surfaceColor;
|
|||
|
left = surfaceColor;
|
|||
|
top = surfaceColor;
|
|||
|
back = surfaceColor;
|
|||
|
right = surfaceColor;
|
|||
|
bottom = surfaceColor;
|
|||
|
break;
|
|||
|
}
|
|||
|
// LightStyle style is Simplistic
|
|||
|
case LightStyle.Simplistic:
|
|||
|
{
|
|||
|
front = surfaceColor;
|
|||
|
left = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
|
|||
|
top = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
|
|||
|
back = surfaceColor;
|
|||
|
right = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
|
|||
|
bottom = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
|
|||
|
break;
|
|||
|
}
|
|||
|
// LightStyle style is Realistic
|
|||
|
default:
|
|||
|
{
|
|||
|
|
|||
|
// For Right Axis angle Realistic lightStyle should be different
|
|||
|
if( _rightAngleAxis )
|
|||
|
{
|
|||
|
// LightStyle source Vector
|
|||
|
Point3D lightSource = new Point3D( 0F, 0F, -1F );
|
|||
|
Point3D [] rightPRpoints = new Point3D[1];
|
|||
|
rightPRpoints[0] = lightSource;
|
|||
|
RightAngleProjection(rightPRpoints);
|
|||
|
|
|||
|
// ******************************************************************
|
|||
|
// Color correction. Angle between Normal vector of polygon and
|
|||
|
// vector of lightStyle source is used.
|
|||
|
// ******************************************************************
|
|||
|
if( this._angleY >= 45 || this._angleY <= -45 )
|
|||
|
{
|
|||
|
front = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[1])/Math.PI );
|
|||
|
|
|||
|
back = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[2])/Math.PI );
|
|||
|
|
|||
|
left = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0 );
|
|||
|
|
|||
|
right = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0 );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
front = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0 );
|
|||
|
|
|||
|
back = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 1 );
|
|||
|
|
|||
|
left = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[3])/Math.PI );
|
|||
|
|
|||
|
right = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[4])/Math.PI );
|
|||
|
}
|
|||
|
|
|||
|
top = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[5])/Math.PI );
|
|||
|
|
|||
|
bottom = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[6])/Math.PI );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// LightStyle source Vector
|
|||
|
Point3D lightSource = new Point3D( 0F, 0F, 1F );
|
|||
|
|
|||
|
// ******************************************************************
|
|||
|
// Color correction. Angle between Normal vector of polygon and
|
|||
|
// vector of lightStyle source is used.
|
|||
|
// ******************************************************************
|
|||
|
front = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[1])/Math.PI );
|
|||
|
|
|||
|
back = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[2])/Math.PI );
|
|||
|
|
|||
|
left = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[3])/Math.PI );
|
|||
|
|
|||
|
right = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[4])/Math.PI );
|
|||
|
|
|||
|
top = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[5])/Math.PI );
|
|||
|
|
|||
|
bottom = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[6])/Math.PI );
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Return intensity of lightStyle for Polygons. There are tree types of lights: None,
|
|||
|
/// Simplistic and Realistic. None Style have same lightStyle intensity on
|
|||
|
/// all polygons. Normal vector doesn<73>t have influence on this type
|
|||
|
/// of lighting. Simplistic style have lightStyle source, which is
|
|||
|
/// rotated together with scene. Realistic lighting have fixed lightStyle
|
|||
|
/// source and intensity of lightStyle is change when scene is rotated.
|
|||
|
/// </summary>
|
|||
|
/// <param name="points">Points of the polygon</param>
|
|||
|
/// <param name="surfaceColor">Color used for polygons without lighting</param>
|
|||
|
/// <param name="visiblePolygon">This flag gets information if polygon is visible or not.</param>
|
|||
|
/// <param name="rotation">Y angle ( from -90 to 90 ) Should be used width switchSeriesOrder to get from -180 to 180</param>
|
|||
|
/// <param name="surfaceName">Used for lighting of front - back and left - right sides</param>
|
|||
|
/// <param name="switchSeriesOrder">Used to calculate real y angle</param>
|
|||
|
/// <returns>Color corrected with intensity of lightStyle</returns>
|
|||
|
internal Color GetPolygonLight(Point3D[] points, Color surfaceColor, bool visiblePolygon, float rotation, SurfaceNames surfaceName, bool switchSeriesOrder)
|
|||
|
{
|
|||
|
// Corrected color
|
|||
|
Color color = surfaceColor;
|
|||
|
|
|||
|
// Direction of lightStyle source
|
|||
|
Point3D lightSource;
|
|||
|
lightSource = new Point3D( 0F, 0F, 1F );
|
|||
|
|
|||
|
// There are tree different lightStyle styles: None, Simplistic and realistic.
|
|||
|
switch( _lightStyle )
|
|||
|
{
|
|||
|
// LightStyle style is None
|
|||
|
case LightStyle.None:
|
|||
|
{
|
|||
|
// Use same color
|
|||
|
break;
|
|||
|
}
|
|||
|
// LightStyle style is Simplistic
|
|||
|
case LightStyle.Simplistic:
|
|||
|
{
|
|||
|
// Find two vectors of polygon
|
|||
|
Point3D firstVector = new Point3D();
|
|||
|
firstVector.X = points[0].X - points[1].X;
|
|||
|
firstVector.Y = points[0].Y - points[1].Y;
|
|||
|
firstVector.Z = points[0].Z - points[1].Z;
|
|||
|
|
|||
|
Point3D secondVector = new Point3D();
|
|||
|
secondVector.X = points[2].X - points[1].X;
|
|||
|
secondVector.Y = points[2].Y - points[1].Y;
|
|||
|
secondVector.Z = points[2].Z - points[1].Z;
|
|||
|
|
|||
|
// Find Normal vector for Polygon
|
|||
|
Point3D normalVector = new Point3D();
|
|||
|
normalVector.X = firstVector.Y * secondVector.Z - firstVector.Z * secondVector.Y;
|
|||
|
normalVector.Y = firstVector.Z * secondVector.X - firstVector.X * secondVector.Z;
|
|||
|
normalVector.Z = firstVector.X * secondVector.Y - firstVector.Y * secondVector.X;
|
|||
|
|
|||
|
// Polygon is left side ( like side of area chart )
|
|||
|
if( surfaceName == SurfaceNames.Left )
|
|||
|
{
|
|||
|
color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
|
|||
|
}
|
|||
|
// Polygon is right side ( like side of area chart )
|
|||
|
else if( surfaceName == SurfaceNames.Right )
|
|||
|
{
|
|||
|
color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
|
|||
|
}
|
|||
|
// Polygon is front side ( like side of area chart )
|
|||
|
else if( surfaceName == SurfaceNames.Front )
|
|||
|
{
|
|||
|
color = surfaceColor;
|
|||
|
}
|
|||
|
// Polygon is back side ( like side of area chart )
|
|||
|
else if( surfaceName == SurfaceNames.Back )
|
|||
|
{
|
|||
|
color = surfaceColor;
|
|||
|
}
|
|||
|
// Polygon has angle with bottom side ( Line chart or top of area chart )
|
|||
|
else
|
|||
|
{
|
|||
|
float angleLeft;
|
|||
|
float angleRight;
|
|||
|
|
|||
|
// Find angles between lightStyle and polygon for different y-axis angles.
|
|||
|
if( switchSeriesOrder )
|
|||
|
{
|
|||
|
if (rotation > 0 && rotation <= 90)
|
|||
|
{
|
|||
|
angleLeft = GetAngle( normalVector, _lightVectors[3] );
|
|||
|
angleRight = GetAngle( normalVector, _lightVectors[4] );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
angleLeft = GetAngle( normalVector, _lightVectors[4] );
|
|||
|
angleRight = GetAngle( normalVector, _lightVectors[3] );
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (rotation > 0 && rotation <= 90)
|
|||
|
{
|
|||
|
angleLeft = GetAngle( normalVector, _lightVectors[4] );
|
|||
|
angleRight = GetAngle( normalVector, _lightVectors[3] );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
angleLeft = GetAngle( normalVector, _lightVectors[3] );
|
|||
|
angleRight = GetAngle( normalVector, _lightVectors[4] );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if( Math.Abs( angleLeft - angleRight ) < 0.01 )
|
|||
|
{
|
|||
|
color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
|
|||
|
}
|
|||
|
else if( angleLeft < angleRight )
|
|||
|
{
|
|||
|
color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
// LightStyle style is Realistic
|
|||
|
default:
|
|||
|
{
|
|||
|
|
|||
|
// Find two vectors of polygon
|
|||
|
Point3D firstVector = new Point3D();
|
|||
|
firstVector.X = points[0].X - points[1].X;
|
|||
|
firstVector.Y = points[0].Y - points[1].Y;
|
|||
|
firstVector.Z = points[0].Z - points[1].Z;
|
|||
|
|
|||
|
Point3D secondVector = new Point3D();
|
|||
|
secondVector.X = points[2].X - points[1].X;
|
|||
|
secondVector.Y = points[2].Y - points[1].Y;
|
|||
|
secondVector.Z = points[2].Z - points[1].Z;
|
|||
|
|
|||
|
// Find Normal vector for Polygon
|
|||
|
Point3D normalVector = new Point3D();
|
|||
|
normalVector.X = firstVector.Y * secondVector.Z - firstVector.Z * secondVector.Y;
|
|||
|
normalVector.Y = firstVector.Z * secondVector.X - firstVector.X * secondVector.Z;
|
|||
|
normalVector.Z = firstVector.X * secondVector.Y - firstVector.Y * secondVector.X;
|
|||
|
|
|||
|
// ******************************************************************
|
|||
|
// Color correction. Angle between Normal vector of polygon and
|
|||
|
// vector of lightStyle source is used.
|
|||
|
// ******************************************************************
|
|||
|
if( surfaceName == SurfaceNames.Front )
|
|||
|
{
|
|||
|
lightSource.Z *= -1;
|
|||
|
color = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[2])/Math.PI );
|
|||
|
}
|
|||
|
else if( surfaceName == SurfaceNames.Back )
|
|||
|
{
|
|||
|
lightSource.Z *= -1;
|
|||
|
color = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[1])/Math.PI );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if( visiblePolygon )
|
|||
|
{
|
|||
|
lightSource.Z *= -1;
|
|||
|
}
|
|||
|
|
|||
|
color = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,normalVector)/Math.PI );
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
return color;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// This method creates gradien color with brightnes.
|
|||
|
/// </summary>
|
|||
|
/// <param name="beginColor">Start color for gradient.</param>
|
|||
|
/// <param name="position">Position used between Start and end color.</param>
|
|||
|
/// <returns>Calculated Gradient color from gradient position</returns>
|
|||
|
private Color GetBrightGradientColor( Color beginColor, double position )
|
|||
|
{
|
|||
|
position = position * 2;
|
|||
|
double brightness = 0.5;
|
|||
|
if( position < brightness )
|
|||
|
{
|
|||
|
return ChartGraphics.GetGradientColor( Color.FromArgb(beginColor.A,255,255,255), beginColor, 1 - brightness + position );
|
|||
|
}
|
|||
|
else if( -brightness + position < 1 )
|
|||
|
{
|
|||
|
return ChartGraphics.GetGradientColor( beginColor, Color.Black, -brightness + position );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return Color.FromArgb( beginColor.A, 0, 0, 0 );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Returns the angle between two 3D vectors (a and b);
|
|||
|
/// </summary>
|
|||
|
/// <param name="a">First vector</param>
|
|||
|
/// <param name="b">Second Vector</param>
|
|||
|
/// <returns>Angle between vectors</returns>
|
|||
|
private float GetAngle(Point3D a,Point3D b)
|
|||
|
{
|
|||
|
double angle;
|
|||
|
|
|||
|
angle = Math.Acos( ( a.X * b.X + a.Y * b.Y + a.Z * b.Z ) / ( Math.Sqrt( a.X * a.X + a.Y * a.Y + a.Z * a.Z ) * Math.Sqrt( b.X * b.X + b.Y * b.Y + b.Z * b.Z ) ) );
|
|||
|
|
|||
|
return (float)angle;
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
}
|
|||
|
}
|