536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
1223 lines
38 KiB
C#
1223 lines
38 KiB
C#
//-------------------------------------------------------------
|
||
// <copyright company=’Microsoft Corporation’>
|
||
// Copyright © 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’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’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
|
||
}
|
||
}
|