///
/// This file is part of ILNumerics Community Edition.
///
/// ILNumerics Community Edition - high performance computing for applications.
/// Copyright (C) 2006 - 2012 Haymo Kutschbach, http://ilnumerics.net
///
/// ILNumerics Community Edition is free software: you can redistribute it and/or modify
/// it under the terms of the GNU General Public License version 3 as published by
/// the Free Software Foundation.
///
/// ILNumerics Community Edition is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with ILNumerics Community Edition. See the file License.txt in the root
/// of your distribution package. If not, see .
///
/// In addition this software uses the following components and/or licenses:
///
/// =================================================================================
/// The Open Toolkit Library License
///
/// Copyright (c) 2006 - 2009 the Open Toolkit library.
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to deal
/// in the Software without restriction, including without limitation the rights to
/// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
/// the Software, and to permit persons to whom the Software is furnished to do
/// so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in all
/// copies or substantial portions of the Software.
///
/// =================================================================================
///
#pragma warning disable 1591
using System;
using System.Collections.Generic;
using System.Text;
using ILNumerics.Exceptions;
using ILNumerics.Drawing.Misc;
using ILNumerics.Misc;
namespace ILNumerics.Drawing.Graphs {
///
/// Class representing abstract implementation of filled graphs
///
public abstract class ILFilledGraph : ILGraph {
#region attributes / properties
protected float[] m_xCoords;
protected float[] m_yCoords;
protected ILArray m_colors = ILMath.returnType();
protected ILArray m_sourceArray = ILMath.returnType();
protected int m_Vertcount;
protected int m_rows, m_cols;
protected bool m_vertexReady, m_indexReady;
protected uint[] m_indices;
protected uint[] m_gridIndices;
protected int m_gridIndicesCount;
protected int m_indicesCount;
protected int m_oldSubQuadrant;
protected int m_stripesCount;
protected int m_stripesLen;
protected int m_gridStripsLen;
protected int m_gridStripsLenOnce;
protected int m_gridStripsCount;
protected bool m_shadeVertexDirectionLowHigh = false;
protected static readonly float MAXHUEVALUE = Misc.ILColorProvider.MAXHUEVALUE;
protected static readonly float CAMERA_ANG_OFFSET = - (float)(Math.PI / 4);
// visible properties
protected float m_opacity;
protected bool m_filled;
protected ILLineProperties m_wireLines;
///
/// Wireframe line properties
///
public ILLineProperties Wireframe {
get {
return m_wireLines;
}
}
///
/// Get / set the transparency for the graph (percent)
///
/// 1.0f (opaque) ... 0.0f (transparent)
public float Opacity {
get {
return m_opacity;
}
set {
m_opacity = value;
Invalidate();
OnChanged("Opacity");
}
}
///
/// Determine, if the tiles (rectangle areas between data points)
/// constructing the surface will be filled or invisible
///
public bool Filled {
get {
return m_filled;
}
set {
m_filled = value;
OnChanged("Filled");
}
}
///
/// get a reference to the internal data array
///
/// modifications to the array returned will
/// not alter the data the graph is based on.
public ILRetArray Data {
get {
return m_sourceArray.C;
}
}
#endregion
#region constructor
///
/// construct new filled graph
///
/// panel hosting the graph
/// X coords, if null, range 0..[cols of Z] will be created
/// Y coords, if null, range 0..[rows of Z] will be created
/// Z coords (heights)
/// Colors for Z
/// gloabal limits of panel
public ILFilledGraph(ILPanel panel, ILArray X, ILArray Y,
ILArray Z, ILArray C, ILClippingData clippingContainer)
: base(panel, clippingContainer) {
using (ILScope.Enter(X, Y, Z, C)) {
#region argument checking
m_localClipping.EventingSuspend();
if (Z == null || !Z.IsMatrix)
throw new ILArgumentException("ILFilledGraph: Z must be matrix!");
if (!Z.IsNumeric)
throw new ILArgumentException("ILFilledGraph: Z must be numeric!");
m_sourceArray.a = ILMath.tosingle(Z);
m_rows = m_sourceArray.Size[0];
m_cols = m_sourceArray.Size[1];
ILArray tmp;
if (!object.Equals(X, null) && !X.IsEmpty) {
if (!X.IsMatrix || !X.IsNumeric) {
throw new ILArgumentException("ILFilledGraph: X must be numeric matrix!");
}
if (X.Size.IsSameSize(Z.Size)) {
tmp = ILMath.tosingle(X);
tmp.ExportValues(ref m_xCoords);
m_localClipping.XMax = tmp.MaxValue;
m_localClipping.XMin = tmp.MinValue;
} else {
throw new ILArgumentException("ILFilledGraph: X must be of same size than Z!");
}
} else {
ILMath.tosingle(ILMath.repmat(ILMath.counter(0.0, 1.0, 1, m_cols), m_rows, 1)).ExportValues(ref m_xCoords);
m_localClipping.XMin = 0;
m_localClipping.XMax = m_cols - 1;
}
if (!object.Equals(Y, null) && !Y.IsEmpty) {
if (!Y.IsMatrix || !Y.IsNumeric) {
throw new ILArgumentException("ILFilledGraph: Y must be numeric matrix!");
}
if (Y.Size.IsSameSize(Z.Size)) {
tmp = ILMath.tosingle(Y);
tmp.ExportValues(ref m_yCoords);
m_localClipping.YMax = tmp.MaxValue;
m_localClipping.YMin = tmp.MinValue;
} else {
throw new ILArgumentException("ILFilledGraph: Y must be same size than Z!");
}
} else {
ILMath.tosingle(ILMath.repmat(ILMath.counter(0.0, 1.0, m_rows, 1), 1, m_cols)).ExportValues(ref m_yCoords);
m_localClipping.YMax = m_rows - 1;
m_localClipping.YMin = 0;
}
if (object.Equals(C, null) || C.IsEmpty) {
m_colors.a = ILMath.empty();
} else {
m_colors.a = ILMath.tosingle(C);
}
m_localClipping.ZMax = m_sourceArray.MaxValue;
m_localClipping.ZMin = m_sourceArray.MinValue;
#endregion
m_Vertcount = m_rows * m_cols;
m_vertexReady = false;
m_indexReady = false;
// default view properties
m_opacity = 1.0f;
m_wireLines = new ILLineProperties();
m_wireLines.Changed += new EventHandler(m_wireLines_Changed);
m_filled = true;
m_localClipping.EventingResume();
}
}
#endregion
#region protected/private helper
protected virtual void CreateVertices() {}
///
/// Create indices for filled graphs
///
/// Indices will be ordered to assemble individual triangles.
protected virtual void CreateIndices() {
#region interpolate shading faster, less exact (obsolete)
//if (m_panel.Camera.CosPhiShift > 0) {
// if (m_panel.Camera.SinPhiShift > 0) {
// System.Diagnostics.Debug.WriteLine("CosPhi > 0 & SinPhi > 0 (front)");
// // looking from front
// checkVertexIndicesLength(m_cols * 2, m_rows-1);
// if (m_panel.Camera.LooksFromLeft) {
// pos1 = m_Vertcount-1; pos2 = pos1 - m_cols;
// for (int r = m_rows-1; r --> 0;) {
// for (int c = m_cols; c--> 0;) {
// m_indices[posI++] = (uint)pos1--;
// m_indices[posI++] = (uint)pos2--;
// }
// }
// } else {
// // looks from right
// pos1 = m_Vertcount-m_cols; pos2 = pos1 - m_cols;
// for (int r = m_rows-1; r --> 0;) {
// for (int c = m_cols; c--> 0;) {
// m_indices[posI++] = (uint)pos2++;
// m_indices[posI++] = (uint)pos1++;
// }
// pos1 -= (m_cols * 2);
// pos2 -= (m_cols * 2);
// }
// }
// } else {
// System.Diagnostics.Debug.WriteLine("CosPhi > 0 & SinPhi < 0 (left)");
// // looking from left
// checkVertexIndicesLength(m_rows * 2, m_cols-1);
// pos1 = m_Vertcount-1; pos2 = m_Vertcount-2;
// for (int c = m_cols-1; c --> 0;) {
// for (int r = m_rows; r --> 0;) {
// m_indices[posI++] = (uint)pos1;
// m_indices[posI++] = (uint)pos2;
// pos1 -= m_cols;
// pos2 -= m_cols;
// }
// pos1 += (m_Vertcount-1);
// pos2 = pos1-1;
// }
// }
//} else {
// if (m_panel.Camera.SinPhiShift > 0) {
// // looking from right
// System.Diagnostics.Debug.WriteLine("CosPhi < 0 & SinPhi > 0 (right)");
// checkVertexIndicesLength(m_rows * 2, m_cols-1);
// pos1 = m_Vertcount-1; pos2 = m_Vertcount-2;
// for (int c = m_cols-1; c --> 0;) {
// for (int r = m_rows-1; r --> 0;) {
// m_indices[posI++] = (uint)pos1;
// m_indices[posI++] = (uint)pos2;
// pos1 -= m_cols;
// pos2 -= m_cols;
// }
// pos1 += m_Vertcount;
// pos2 += m_Vertcount;
// }
// } else {
// // looking from back
// System.Diagnostics.Debug.WriteLine("CosPhi < 0 & SinPhi < 0 (back)");
// checkVertexIndicesLength(m_cols * 2, m_rows-1);
// pos1 = m_cols-1; pos2 = pos1+m_cols;
// for (int r = m_rows-1; r-->0;) {
// for (int c = m_cols; c-->0;) {
// m_indices[posI++] = (uint)pos2--;
// m_indices[posI++] = (uint)pos1--;
// }
// pos1 += m_cols*2;
// pos2 += m_cols*2;
// }
// }
//}
//
#endregion
#region both shading modes WITH transparency, draws individual triangles
// DrawMode "trianglestrips" will not work here! ->
// We must specify each triangle seperately!
// Also we must divide the whole camera angle range in 8 (!) areas
// and handle each individually by different index arrays.
// This all is more exact but less performant ...
int pos1,pos2,posI = 0;
if (m_panel.Camera.CosPhiShift > 0) {
if (m_panel.Camera.SinPhiShift > 0) {
#region looking from front
//System.Diagnostics.Debug.WriteLine("CosPhi > 0 & SinPhi > 0 (front)");
checkVertexIndicesLength((m_cols - 1) * 6,m_rows-1);
if (m_panel.Camera.LooksFromLeft) {
m_oldSubQuadrant = 7;
pos1 = m_Vertcount-1; pos2 = pos1-m_cols;
for (int r = m_rows-1; r --> 0; ) {
for (int c = m_cols-1; c-->0;) {
m_indices[posI++] = (uint)pos1-1;
m_indices[posI++] = (uint)pos2-1;
m_indices[posI++] = (uint)pos1;
m_indices[posI++] = (uint)pos2--;
m_indices[posI++] = (uint)pos2;
m_indices[posI++] = (uint)pos1--;
}
pos1--;
pos2--;
}
} else {
m_oldSubQuadrant = 0;
pos1 = m_Vertcount-2*m_cols; pos2 = m_Vertcount-m_cols;
for (int r = m_rows-1; r --> 0; ) {
for (int c = m_cols-1; c-->0;) {
m_indices[posI++] = (uint)pos2++;
m_indices[posI++] = (uint)pos1;
m_indices[posI++] = (uint)pos2;
m_indices[posI++] = (uint)pos1+1;
m_indices[posI++] = (uint)pos1++;
m_indices[posI++] = (uint)pos2;
}
pos1 -= (m_cols * 2 - 1);
pos2 -= (m_cols * 2 - 1);
}
}
#endregion
} else {
#region looking from left
//System.Diagnostics.Debug.WriteLine("CosPhi > 0 & SinPhi < 0 (left)");
checkVertexIndicesLength((m_rows - 1) * 6,m_cols-1);
if (m_panel.Camera.LooksFromFront) {
m_oldSubQuadrant = 6;
pos1 = m_Vertcount-2; pos2 = pos1 + 1;
for (int r = m_cols-1; r --> 0; ) {
for (int c = m_rows-1; c-->0;) {
m_indices[posI++] = (uint)(pos2-m_cols);
m_indices[posI++] = (uint)(pos1-m_cols);
m_indices[posI++] = (uint)pos2;
m_indices[posI++] = (uint)pos1; pos1 -= m_cols;
m_indices[posI++] = (uint)pos1;
m_indices[posI++] = (uint)pos2; pos2 -= m_cols;
}
pos1 += (m_Vertcount - m_cols - 1);
pos2 += (m_Vertcount - m_cols - 1);
}
} else {
m_oldSubQuadrant = 5;
pos1 = m_cols-2; pos2 = pos1 + 1;
for (int r = m_cols-1; r --> 0; ) {
for (int c = m_rows-1; c-->0;) {
m_indices[posI++] = (uint)pos1;
m_indices[posI++] = (uint)pos2; pos2 += m_cols;
m_indices[posI++] = (uint)pos2;
m_indices[posI++] = (uint)pos1; pos1 += m_cols;
m_indices[posI++] = (uint)pos1;
m_indices[posI++] = (uint)pos2;
}
pos1 -= (m_Vertcount - m_cols + 1);
pos2 -= (m_Vertcount - m_cols + 1);
}
}
#endregion
}
} else {
if (m_panel.Camera.SinPhiShift > 0) {
#region looking from right
checkVertexIndicesLength((m_rows - 1) * 6,m_cols-1);
//System.Diagnostics.Debug.WriteLine("CosPhi < 0 & SinPhi > 0 (right)");
if (m_panel.Camera.LooksFromFront) {
m_oldSubQuadrant = 1;
pos1 = m_Vertcount-m_cols; pos2 = pos1+1;
for (int r = m_cols-1; r --> 0; ) {
for (int c = m_rows-1; c-->0;) {
m_indices[posI++] = (uint)pos1; pos1 -= m_cols;
m_indices[posI++] = (uint)pos1;
m_indices[posI++] = (uint)pos2;
m_indices[posI++] = (uint)pos1;
m_indices[posI++] = (uint)(pos2-m_cols);
m_indices[posI++] = (uint)pos2; pos2 -= m_cols;
}
pos1 += (m_Vertcount - m_cols + 1);
pos2 += (m_Vertcount - m_cols + 1);
}
} else {
m_oldSubQuadrant = 2;
pos1 = 0; pos2 = 1;
for (int r = m_cols-1; r --> 0; ) {
for (int c = m_rows-1; c-->0;) {
m_indices[posI++] = (uint)pos1;
m_indices[posI++] = (uint)(pos1+m_cols);
m_indices[posI++] = (uint)(pos2+m_cols);
m_indices[posI++] = (uint)pos1; pos1 += m_cols;
m_indices[posI++] = (uint)pos2; pos2 += m_cols;
m_indices[posI++] = (uint)pos2;
}
pos1 -= (m_Vertcount - m_cols - 1);
pos2 -= (m_Vertcount - m_cols - 1);
}
}
#endregion
} else {
#region looking from back
//System.Diagnostics.Debug.WriteLine("CosPhi < 0 & SinPhi < 0 (back)");
checkVertexIndicesLength((m_cols - 1) * 6,m_rows-1);
if (m_panel.Camera.LooksFromLeft) {
m_oldSubQuadrant = 4;
pos1 = m_cols-1; pos2 = pos1+m_cols;
for (int r = m_rows-1; r --> 0; ) {
for (int c = m_cols-1; c-->0;) {
m_indices[posI++] = (uint)pos1--;
m_indices[posI++] = (uint)pos1;
m_indices[posI++] = (uint)pos2;
m_indices[posI++] = (uint)pos2-1;
m_indices[posI++] = (uint)pos1;
m_indices[posI++] = (uint)pos2--;
}
pos1 += (2*m_cols-1);
pos2 += (2*m_cols-1);
}
} else {
m_oldSubQuadrant = 3;
pos1 = 0; pos2 = pos1+m_cols;
for (int r = m_rows-1; r --> 0; ) {
for (int c = m_cols-1; c-->0;) {
m_indices[posI++] = (uint)pos1;
m_indices[posI++] = (uint)pos1+1;
m_indices[posI++] = (uint)pos2+1;
m_indices[posI++] = (uint)pos1++;
m_indices[posI++] = (uint)pos2++;
m_indices[posI++] = (uint)pos2;
}
pos1 ++;
pos2 ++;
}
}
#endregion
}
}
#endregion
if (m_wireLines.Visible) {
#region create grid line indices
posI = 0;
if (m_panel.Camera.CosPhiShift > 0) {
if (m_panel.Camera.SinPhiShift > 0) {
#region looking from front
//System.Diagnostics.Debug.WriteLine("CosPhi > 0 & SinPhi > 0 (front)");
checkGridIndicesLength((m_cols-1) * 4 + 2,m_rows-1);
pos1 = m_Vertcount-m_cols;
pos2 = m_Vertcount-m_cols;
// first row is special:
for (int c = m_cols-1; c-->0;) {
m_gridIndices[posI++] = (uint)pos1++;
m_gridIndices[posI++] = (uint)pos1;
}
pos1 -= 2*m_cols-1;
for (int r = m_rows-1; r-->0;) {
// first column is special:
m_gridIndices[posI++] = (uint)pos1;
m_gridIndices[posI++] = (uint)pos2;
for (int c = m_cols-1; c-->0;) {
m_gridIndices[posI++] = (uint)pos1++;
m_gridIndices[posI++] = (uint)pos1;
m_gridIndices[posI++] = (uint)pos1;
m_gridIndices[posI++] = (uint)++pos2;
}
pos1 -= (m_cols * 2 - 1);
pos2 -= (m_cols * 2 - 1);
}
#endregion
} else {
#region looking from left
//System.Diagnostics.Debug.WriteLine("CosPhi > 0 & SinPhi < 0 (left)");
checkGridIndicesLength((m_rows-1) * 4 + 2,m_cols-1);
pos1 = m_Vertcount-1;
pos2 = m_Vertcount-1-m_cols;
// first col is special:
for (int c = m_rows-1; c-->0;) {
m_gridIndices[posI++] = (uint)pos2; pos2 -= m_cols;
m_gridIndices[posI++] = (uint)pos1; pos1 -= m_cols;
}
pos2 = m_Vertcount - 1 - m_cols;
pos1 = m_Vertcount - 2;
for (int r = m_cols-1; r-->0;) {
// first row is special:
m_gridIndices[posI++] = (uint)pos1;
m_gridIndices[posI++] = (uint)pos1+1;
for (int c = m_rows-1; c-->0;) {
m_gridIndices[posI++] = (uint)(pos1-m_cols);
m_gridIndices[posI++] = (uint)pos1; pos1 -= m_cols;
m_gridIndices[posI++] = (uint)pos1;
m_gridIndices[posI++] = (uint)pos2; pos2 -= m_cols;
}
pos1 += (m_Vertcount - m_cols -1);
pos2 += (m_Vertcount - m_cols -1);
}
#endregion
}
} else {
if (m_panel.Camera.SinPhiShift > 0) {
#region looking from right
//System.Diagnostics.Debug.WriteLine("CosPhi < 0 & SinPhi > 0 (right)");
checkGridIndicesLength((m_rows-1) * 4 + 2,m_cols-1);
pos1 = 0;
pos2 = 1;
// first col is special:
for (int c = m_rows-1; c-->0;) {
m_gridIndices[posI++] = (uint)pos1; pos1 += m_cols;
m_gridIndices[posI++] = (uint)pos1;
}
pos1 = m_cols;
for (int r = m_cols-1; r-->0;) {
// first row is special:
m_gridIndices[posI++] = (uint)pos2-1;
m_gridIndices[posI++] = (uint)pos2;
for (int c = m_rows-1; c-->0;) {
m_gridIndices[posI++] = (uint)pos1;
m_gridIndices[posI++] = (uint)pos1+1; pos1 += m_cols;
m_gridIndices[posI++] = (uint)pos2; pos2 += m_cols;
m_gridIndices[posI++] = (uint)pos2;
}
pos1 -= (m_Vertcount - m_cols -1);
pos2 -= (m_Vertcount - m_cols -1);
}
#endregion
} else {
#region looking from back
//System.Diagnostics.Debug.WriteLine("CosPhi < 0 & SinPhi < 0 (back)");
checkGridIndicesLength((m_cols-1) * 4 + 2,m_rows-1);
pos1 = m_cols-1; pos2 = pos1-1;
for (int c = m_cols-1; c-->0;) {
m_gridIndices[posI++] = (uint)pos2--;
m_gridIndices[posI++] = (uint)pos1--;
}
pos2 = m_cols*2-1;
pos1 = m_cols-1;
for (int r = m_rows-1; r-->0;) {
// first column is special:
m_gridIndices[posI++] = (uint)pos1;
m_gridIndices[posI++] = (uint)pos2;
for (int c = m_cols-1; c-->0;) {
m_gridIndices[posI++] = (uint)(pos2-1);
m_gridIndices[posI++] = (uint)pos2--;
m_gridIndices[posI++] = (uint)--pos1;
m_gridIndices[posI++] = (uint)pos2;
}
pos1 += (2*m_cols-1);
pos2 += (2*m_cols-1);
}
#endregion
}
}
#endregion
}
m_indexReady = true;
}
///
/// checks the length of index vector and allocate more memory if needed
///
/// Number of stripes to be drawn
/// Number of indices for each stripe
protected void checkVertexIndicesLength(int vertStrLen, int vertStrCount) {
m_indicesCount = vertStrCount * vertStrLen;
if (m_indices != null && m_indices.Length < m_indicesCount) {
ILMemoryPool.Pool.Free(m_indices);
}
if (m_indices == null || m_indices.Length < m_indicesCount) {
m_indices = ILMemoryPool.Pool.New(m_indicesCount);
}
m_stripesCount = vertStrCount;
m_stripesLen = vertStrLen;
}
///
/// checks the length of grid index vector and allocate more if needed
///
/// Number of stripes to be drawn
/// Number of indices for each stripe
protected void checkGridIndicesLength(int gridStrLen, int gridStrCount) {
m_gridStripsLenOnce = (gridStrLen -2) / 2 + 2;
m_gridIndicesCount = gridStrLen * gridStrCount + m_gridStripsLenOnce;
if (m_gridIndices != null && m_gridIndices.Length < m_gridIndicesCount) {
ILMemoryPool.Pool.Free(m_gridIndices);
}
if (m_gridIndices == null || m_gridIndices.Length < m_gridIndicesCount) {
m_gridIndices = ILMemoryPool.Pool.New(m_gridIndicesCount);
}
m_gridStripsCount = gridStrCount;
m_gridStripsLen = gridStrLen;
}
///
/// Dispose off this filled graph (grid-)indices
///
public override void Dispose() {
if (m_indices != null) {
ILMemoryPool.Pool.Free(m_indices);
}
if (m_gridIndices != null) {
ILMemoryPool.Pool.Free(m_gridIndices);
}
base.Dispose();
}
///
/// checks & if neccessary triggers recreation of all vertices and indices
///
/// This function is called by the enclosing panel, e.g. when rotation
/// occours, which makes a reconfiguration neccessary. It internally calls CreateVertices()
/// and CreateIndices().
internal override void Configure() {
m_isReady = false;
if (!m_vertexReady) CreateVertices();
if (!m_indexReady) CreateIndices();
m_isReady = true;
}
protected override void OnChanged(string source) {
base.OnChanged(source);
m_vertexReady = false;
}
protected void m_wireLines_Changed(object sender, EventArgs args) {
OnWireLinesChanged();
}
protected virtual void OnWireLinesChanged () {
m_indexReady = false;
OnChanged("Wirelines");
}
#endregion
}
}