/// /// 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. /// /// ================================================================================= /// using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Data; using System.Windows.Forms; using ILNumerics.Exceptions; using ILNumerics.Drawing; using System.Resources; using OpenTK; using OpenTK.Graphics.OpenGL; using OpenTK.Graphics; using OpenTK.Graphics.OpenGL.Enums; using ILNumerics.Drawing.Graphs; using ILNumerics.Drawing.Platform.OpenGL; using ILNumerics.Drawing.Misc; using ILNumerics.Misc; using VERTEXTYPEDEF = ILNumerics.Drawing.Platform.OpenGL.ILOGLSurfaceGraph.VertexC4N3V3; namespace ILNumerics.Drawing.Platform.OpenGL { /// /// OpenGL implementation for ILSurfaceGraph /// public class ILOGLSurfaceGraph : ILSurfaceGraph { #region attributes protected float[] m_vertices; protected ShadingStyles m_oldShading; protected ILColormap m_oldColormap; #endregion #region vertex definition /// /// Vertex definition: 4Color, 3Normal, 3 Vertex - all float /// /// CAUTION! The vertex is not usable with OpenGL via vartex arrays this way! /// We had to recognize sporadic crashs when drawing this way! Todo: ... to be investigated. [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] public struct VertexC4N3V3 { public float CR; public float CG; public float CB; public float CA; public float Nx; public float Ny; public float Nz; public float Vx; public float Vy; public float Vz; } protected override void CreateVertices() { ILColormap colormap = m_panel.Colormap; if (m_vertices == null) { m_vertices = ILMemoryPool.Pool.New(m_Vertcount*10); } float val = 0.0f; float minZ = m_globalClipping.ZMin, minC = (ILMath.isnullorempty(m_colors))? 0.0f : m_colors.MinValue; float maxZ = m_globalClipping.ZMax; bool useColorArray = !(object.Equals(m_colors,null) || m_colors.IsEmpty); float a; if (useColorArray) a = colormap.Length / (m_colors.MaxValue - minC); else { if (maxZ - minZ != 0.0f) a = colormap.Length / (maxZ - minZ); else a = 0.0f; } int curVertPos = 0, curVecPos = 0; if (m_shading == ShadingStyles.Interpolate) { #region shading interpolate for (int r = 0; r < m_rows; r++) { for (int c = 0; c < m_cols; c++) { curVecPos = r + c * m_rows; val = m_sourceArray.GetValue(r,c); // set color values if (useColorArray) colormap.Map((m_colors.GetValue(curVecPos) - minC) * a, m_vertices, ref curVertPos); else { if (a != 0) { colormap.Map((val - minZ) * a, m_vertices, ref curVertPos); } else { // plane: minz == maxz colormap.Map(colormap.Length / 2, m_vertices, ref curVertPos); } } m_vertices[curVertPos++] = m_opacity; curVertPos += 3; m_vertices[curVertPos++] = m_xCoords[curVecPos]; m_vertices[curVertPos++] = m_yCoords[curVecPos]; m_vertices[curVertPos++] = val; } } #endregion } else if (m_shading == ShadingStyles.Flat) { #region shading flat /* Consider a surface area like this: * * 8 - 9 -10 -11 * | / | / | / | * 4 - 5 - 6 - 7 * | / | / | / | * 0 - 1 - 2 - 3 * * The rectangle surrounded by 0-1-4-5 is than colored by the * vertex 5. This rectangle will be assembled by 2 triangles: * 0-1-5 and 0-4-5. Therefore the color for both rectangles * is the same and must be stored in the vertex No.5. * The vertices can be assembled in natural order, beginning * with 0 and approching m_vertexCount-1. The color for flat * shading is averaged over the neighbor corners of each * rectangle. In our Example the color stored in 5 is averaged * over the values of the vertices 0,1,4 and 5. The first row * and the first column are not used for the surface. However * it may _be_ used for the wireframe grid if that is drawn with * interpolated colors (Wireframe.Color.IsEmpty). So we must * prepare the color for it -> here we just take the value itself. */ // first row: no color average for (int c = 0; c < m_cols; c++) { val = m_sourceArray.GetValue(0,c); if (useColorArray) colormap.Map((m_colors.GetValue(0,c) - minC) * a, m_vertices,ref curVertPos); else colormap.Map((val - minZ) * a, m_vertices,ref curVertPos); m_vertices[curVertPos++] = m_opacity; curVertPos += 3; m_vertices[curVertPos++] = m_xCoords[m_rows*c]; m_vertices[curVertPos++] = m_yCoords[m_rows*c]; m_vertices[curVertPos++] = val; } for (int r = 1; r < m_rows; r++) { val = m_sourceArray.GetValue(r,0); if (useColorArray) colormap.Map((m_colors.GetValue(r) - minC) * a, m_vertices,ref curVertPos); else colormap.Map((val - minZ) * a, m_vertices,ref curVertPos); m_vertices[curVertPos++] = m_opacity; curVertPos += 3; m_vertices[curVertPos++] = m_xCoords[r]; m_vertices[curVertPos++] = m_yCoords[r]; m_vertices[curVertPos++] = val; // next columns: average color over precomputed corners for (int c = 1; c < m_cols; c++) { curVecPos = r + c * m_rows; val = m_sourceArray.GetValue(r,c); val += m_sourceArray.GetValue(r-1,c); val += m_sourceArray.GetValue(r,c-1); val += m_sourceArray.GetValue(r-1,c-1); val /= 4; if (useColorArray) colormap.Map((m_colors.GetValue(curVecPos) - minC) * a, m_vertices,ref curVertPos); else colormap.Map((val - minZ) * a, m_vertices,ref curVertPos); m_vertices[curVertPos++] = m_opacity; curVertPos += 3; m_vertices[curVertPos++] = m_xCoords[curVecPos]; m_vertices[curVertPos++] = m_yCoords[curVecPos]; m_vertices[curVertPos++] = m_sourceArray.GetValue(r,c); } } #endregion m_oldColormap = colormap; } #region create normals // todo: depends on lighting enabled or not...! // reset vertices pointer and start all over curVertPos = 0; float nx,ny,nz; for (int r = 0; r < m_rows; r++) { for (int c = 0; c < m_cols; c++) { nx= m_vertices[curVertPos+7]; ny= m_vertices[curVertPos+8]; nz= m_vertices[curVertPos+9]; if (c > 0) { nx += m_vertices[curVertPos-3]; ny += m_vertices[curVertPos-2]; nz += m_vertices[curVertPos-1]; } if (c < m_cols-1) { nx += m_vertices[curVertPos+17]; ny += m_vertices[curVertPos+18]; nz += m_vertices[curVertPos+19]; } if (r > 0 && r < m_rows) { nx += m_vertices[curVertPos-m_cols*10+10]; ny += m_vertices[curVertPos-m_cols*10+11]; nz += m_vertices[curVertPos-m_cols*10+12]; } if (r < m_rows - 1) { nx += m_vertices[curVertPos+m_cols*10+10]; ny += m_vertices[curVertPos+m_cols*10+11]; nz += m_vertices[curVertPos+m_cols*10+12]; } // normalize float len = (float)Math.Sqrt(nx*nx+ny*ny+nz*nz); m_vertices[curVertPos+4] = nx / len; m_vertices[curVertPos+5] = ny / len; m_vertices[curVertPos+6] = nz / len; curVertPos+=10; } } #endregion m_oldShading = m_shading; m_vertexReady = true; } #endregion #region constructor internal ILOGLSurfaceGraph(ILOGLPanel panel, ILArray X, ILArray Y, ILArray Z, ILArray C, ILClippingData clippingContainer) : base(panel,X,Y,Z,C,clippingContainer) { m_indexReady = false; m_vertexReady = false; } #endregion #region abstract interface /// /// Dispose off this graph's vertices /// public override void Dispose() { base.Dispose(); if (m_vertices != null) { ILMemoryPool.Pool.Free(m_vertices); } } /// /// Draw the graph /// public override void Draw(ILRenderProperties p) { GL.BlendFunc (BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); ILLineProperties wireprops = m_wireLines; ILOGLPanel.SetupLineStyle(wireprops); unsafe { fixed (float* pVertices = m_vertices) { // populate vertex array to GL GL.InterleavedArrays(InterleavedArrayFormat.C4fN3fV3f ,0,(IntPtr)pVertices); // general setup shading & transparency if (m_shading == ShadingStyles.Interpolate) { GL.ShadeModel(ShadingModel.Smooth); //GL.Disable(EnableCap.DepthTest); } else { GL.ShadeModel(ShadingModel.Flat); //if (m_opacity < 1.0f) //else // GL.Enable(EnableCap.DepthTest); } GL.Enable(EnableCap.DepthTest); if (m_opacity == 1.0f && m_shading == ShadingStyles.Interpolate) { #region no transpareny GL.Disable(EnableCap.Blend); GL.Enable(EnableCap.DepthTest); fixed (UInt32* pGridIndices = m_gridIndices) fixed (UInt32* pIndices = m_indices) { UInt32* pGridIndWalk = pGridIndices; // first surface strip if (m_filled) { GL.DrawElements(BeginMode.TriangleStrip,m_stripesLen, DrawElementsType.UnsignedInt, (IntPtr)pIndices); } // first grid strip if (m_wireLines.Visible) { if (!wireprops.Color.IsEmpty) { // if a color was specified, use it for wireframes! GL.DisableClientState(EnableCap.ColorArray); //GL.Color3(wireprops.ForeColor); // color for grid lines } GL.DrawElements(BeginMode.Lines, m_gridStripsLen + m_gridStripsLenOnce, // 2*(m_cols-1), DrawElementsType.UnsignedInt, (IntPtr)pGridIndWalk); pGridIndWalk += (m_gridStripsLen + m_gridStripsLenOnce); if (!wireprops.Color.IsEmpty) { GL.EnableClientState(EnableCap.ColorArray); } } for (int i = 1; i < m_stripesCount; i++) { // subsequent surface strips if (m_filled) { GL.DrawElements(BeginMode.TriangleStrip, m_stripesLen, DrawElementsType.UnsignedInt, (IntPtr)(pIndices+i*m_stripesLen)); } // subsequent grid strips if (m_wireLines.Visible) { GL.Disable(EnableCap.Blend); if (!wireprops.Color.IsEmpty) { // if a color was specified, use it for wireframes! GL.DisableClientState(EnableCap.ColorArray); //GL.Color3(wireprops.ForeColor); // color for grid lines } GL.DrawElements(BeginMode.Lines, m_gridStripsLen, DrawElementsType.UnsignedInt, (IntPtr)(pGridIndWalk)); pGridIndWalk += m_gridStripsLen; if (!wireprops.Color.IsEmpty) { GL.EnableClientState(EnableCap.ColorArray); } } } GL.Finish(); } #endregion } else { #region transparency or flat shading GL.Enable(EnableCap.Blend); fixed (UInt32* pGridIndices = m_gridIndices) fixed (UInt32* pIndices = m_indices) { UInt32* pGridIndWalk = pGridIndices; // first surface strip if (m_filled) { GL.DrawElements(BeginMode.Triangles,m_stripesLen, DrawElementsType.UnsignedInt, (IntPtr)pIndices); } // first grid strip if (m_wireLines.Visible) { GL.Disable(EnableCap.Blend); if (!wireprops.Color.IsEmpty) { // if a color was specified, use it for wireframes! GL.DisableClientState(EnableCap.ColorArray); GL.Color3(m_wireLines.Color); } GL.DrawElements(BeginMode.Lines, m_gridStripsLen + m_gridStripsLenOnce, DrawElementsType.UnsignedInt, (IntPtr)pGridIndWalk); pGridIndWalk += m_gridStripsLen + m_gridStripsLenOnce; if (!wireprops.Color.IsEmpty) { // if a color was specified, use it for wireframes! GL.EnableClientState(EnableCap.ColorArray); } GL.Enable(EnableCap.Blend); } for (int i = 1; i < m_stripesCount; i++) { // subsequent surface strips if (m_filled) { GL.DrawElements(BeginMode.Triangles, m_stripesLen, DrawElementsType.UnsignedInt, (IntPtr)(pIndices+i*m_stripesLen)); } // subsequent grid strips if (m_wireLines.Visible) { GL.Disable(EnableCap.Blend); if (!wireprops.Color.IsEmpty) { // if a color was specified, use it for wireframes! GL.DisableClientState(EnableCap.ColorArray); GL.Color3(m_wireLines.Color); } GL.DrawElements(BeginMode.Lines, m_gridStripsLen, DrawElementsType.UnsignedInt, (IntPtr)(pGridIndWalk)); if (!wireprops.Color.IsEmpty) { GL.EnableClientState(EnableCap.ColorArray); } GL.Enable(EnableCap.Blend); pGridIndWalk += m_gridStripsLen; } } GL.Finish(); } #endregion } } } //GL.Disable(EnableCap.Lighting); //GL.PopMatrix(); } /// /// Ensures the recreation of the graph if neccessary /// public override void Invalidate() { if (m_panel == null) return; if (Math.Floor(m_panel.Camera.Phi / (Math.PI / 4.0)) != m_oldSubQuadrant) { //must only recalculate indices, and only if the camera subquadrant has changed m_indexReady = false; m_isReady = false; } if (m_oldShading != m_shading) { m_indexReady = false; m_vertexReady = false; m_isReady = false; } if (m_oldColormap != m_panel.Colormap) { m_vertexReady = false; m_isReady = false; } } public override void DrawToLegend(ILRenderProperties p, Rectangle sampleRect, Rectangle labelRect) { if (m_filled) { // draw inner filled area GL.ShadeModel(ShadingModel.Smooth); GL.Begin(BeginMode.TriangleStrip); GL.Color3(m_panel.Colormap[m_panel.Colormap.Length-1]); GL.Vertex2(sampleRect.X,sampleRect.Y + sampleRect.Height); GL.Color3(m_panel.Colormap[(int)(m_panel.Colormap.Length/2)]); GL.Vertex2(sampleRect.X,sampleRect.Y); GL.Vertex2(sampleRect.X+sampleRect.Width,sampleRect.Y + sampleRect.Height); GL.Color3(m_panel.Colormap[0]); GL.Vertex2(sampleRect.X+sampleRect.Width,sampleRect.Y); GL.End(); } if (m_wireLines.Visible) { ILNumerics.Drawing.Platform.OpenGL.ILOGLPanel.SetupLineStyle(m_wireLines); GL.Begin(BeginMode.LineStrip); GL.Vertex2(sampleRect.X,sampleRect.Y); GL.Vertex2(sampleRect.X+sampleRect.Width,sampleRect.Y); GL.Vertex2(sampleRect.X+sampleRect.Width,sampleRect.Y + sampleRect.Height); GL.Vertex2(sampleRect.X,sampleRect.Y + sampleRect.Height); GL.Vertex2(sampleRect.X,sampleRect.Y); GL.End(); } m_label.m_position.X = labelRect.X + labelRect.Width / 2; m_label.m_position.Y = labelRect.Y + labelRect.Height / 2; m_label.Anchor = new PointF(.5f,0); // = TickLabelAlign.center | TickLabelAlign.vertCenter; m_label.Draw(p); } #endregion #region helper function protected override void m_globalClipping_Changed(object sender, ClippingChangedEventArgs e) { base.m_globalClipping_Changed(sender, e); m_vertexReady = false; m_indexReady = false; Configure(); } #endregion } }