Graphical Matrix Representation

Introduction

In the realm of data visualization, representing complex data sets in three dimensions can offer valuable insights and enhance understanding. This article explores the Draw_Matrix class, a component of a C# application Graphical Matrix Representation, designed to visualize numerical data stored in a three-dimensional matrix. Utilizing Microsoft DirectX for rendering, this class converts matrix data into a dynamic 3D representation, allowing users to interactively explore and analyze the data.

Through a combination of custom vertex rendering, interactive controls, and real-time updates, the Draw_Matrix class demonstrates key concepts in 3D graphics programming and offers a practical tool for visualizing and interpreting complex data structures. This introduction provides an overview of the class’s functionality, from initializing the 3D environment to handling user interactions, and serves as a guide for understanding its implementation and applications.

3D environment

Visualizing 3D Data with a Graphical Matrix Representation

The Graphical Matrix Representation program was developed to provide a graphical representation of numerical data stored in a three-dimensional matrix. Using the matrix M( x, y, z ) the program maps data coordinates to a visually intuitive 3D space, where each matrix value corresponds to a color point in a 3D orthogonal grid. This allows users to explore complex data through visual patterns.

Matrix Structure and Data Visualization

The program employs a 3D matrix to store values, where each ( x, y, z ) coordinate holds a specific color value. These color values represent points in 3D space, creating a vibrant and interactive visual display. For instance, the program can render multiple mathematical functions simultaneously. In the example provided, three functions—an orange circle, a red sphere, and a grey 3D function—are displayed within the same XYZ coordinate system. The values of X, Y, and Z are calculated within a selected range, and the corresponding color values are stored in the matrix.

Program Structure and Functionality

The program is built around two key classes.

  • Main Class: Responsible for populating the 3D matrix with numerical values, representing coordinates and color data.
  • DrawMatrix Class: This class handles rendering the 3D graphical display, transforming the matrix into a visual output for users.

Class Overview: Draw_Matrix

The Draw_Matrix class is part of a C# application designed to graphically represent a 3D matrix. It utilizes Microsoft DirectX for rendering and provides the functionality to visualize data points in 3D space based on a matrix stored in a DataGridView. Users can rotate, zoom, and interact with the 3D scene using keyboard and mouse controls.

The Draw_Matrix class is pivotal in rendering the 3D graphical representation of the matrix data. Here's a detailed look at its key components.

Initialization

  1. Device and Camera Setup: Initializes the DirectX device and camera settings, defining how the 3D scene will be projected and viewed.
    • The first important part of the class is how it sets up the 3D rendering environment using DirectX.
    • The Initialize_Device() method creates a DirectX Device which manages the 3D rendering.
      void Initialize_Device()
      {
          presentParams = new PresentParameters
          {
              Windowed = true,
              SwapEffect = SwapEffect.Copy
          };
      
          device = new Device(
              0,
              DeviceType.Hardware,
              Panel,
              CreateFlags.HardwareVertexProcessing,
              presentParams
          );
      
          device.RenderState.Lighting = false;
          device.RenderState.FillMode = FillMode.Solid;
          device.RenderState.CullMode = Cull.None;
      }
    • Device Setup: DirectX Device is created to control hardware-accelerated graphics.
    • RenderState: This defines settings like turning off lighting (since the scene is based on colored points) and disabling face culling to render both sides of triangles.

Camera Setup

  1. The Camera is positioned and oriented in the 3D space using the Initialize_Camera() method.
  2. This defines the perspective from which the user views the 3D scene.
    void Initialize_Camera()
    {
        device.Transform.Projection = 
            Matrix.PerspectiveFovRH(
                (float)Math.PI / zoom,
                Panel.Width / Panel.Height,
                1f,
                150f
            );
    
        device.Transform.View = 
            Matrix.LookAtRH(
                new Vector3(camera_x, camera_y, camera_z),
                new Vector3(camera_look_x, camera_look_y, camera_look_z),
                new Vector3(0.0f, 1.0f, 0.0f)
            );
    }
  3. Projection Matrix: This sets the field of view (zoom), and the depth range (1 to 150 units).
  4. View Matrix: It defines the position of the camera and where it’s looking. This gives the user control over how they navigate and rotate through the 3D space.
  5. Vertex and Text Initialization: Converts matrix data into 3D vertex points and sets up text labels for the coordinate axes.
    • The Initialize_Vertex() method processes the matrix data and converts it into 3D vertex points that can be visualized.
      void Initialize_Vertex(DataGridView Data)
      {
          //
          // Convert matrix data to 3D vertex points
          //
          World = Data.Rows.Count - 1;
          points_x = new CustomVertex.PositionColored[World];
      
          int p = 0;
          int q = 0;
      
          for (p = 0; p < World; p++)
          {
              // Matrix
              string[] Matrix = Data[q, p].Value.ToString().Split(';');
      
              // Matrix coordinates
              float x = float.Parse(Matrix[0]);
              float y = float.Parse(Matrix[1]);
              float z = float.Parse(Matrix[2]);
      
              // Matrix value
              var v = Int32.Parse(Matrix[3]);
      
              if (x_max < x)
              {
                  x_max = x;
              }
              if (x_min > x)
              {
                  x_min = x;
              }
      
              if (y_max < y)
              {
                  y_max = y;
              }
              if (y_min > y)
              {
                  y_min = y;
              }
      
              if (z_max < z)
              {
                  z_max = z;
              }
              if (z_min > z)
              {
                  z_min = z;
              }
      
              points_x[p] = new CustomVertex.PositionColored(x, y, z, v);
          }
      
          //
          // Orthogonal Axes vertex declaration
          //
          axes = new CustomVertex.PositionColored[6];
      
          // X
          axes[0] = new CustomVertex.PositionColored(x_min, 0, 0, Color.Blue.ToArgb());
          axes[1] = new CustomVertex.PositionColored(x_max, 0, 0, Color.Blue.ToArgb());
      
          // Y
          axes[2] = new CustomVertex.PositionColored(0, y_min, 0, Color.Green.ToArgb());
          axes[3] = new CustomVertex.PositionColored(0, y_max, 0, Color.Green.ToArgb());
      
          // Z
          axes[4] = new CustomVertex.PositionColored(0, 0, z_min, Color.Purple.ToArgb());
          axes[5] = new CustomVertex.PositionColored(0, 0, z_max, Color.Purple.ToArgb());
      }
    • Matrix to Vertices: The matrix data is read from a DataGridView and converted into 3D points.

These points are stored in points_x and colored based on the values stored in the matrix.

  1. Axis Setup: Axes for the X, Y, and Z directions are also defined for reference in the 3D scene.
  2. Text initialization: Sets up text labels for the coordinate axes
    void Initialize_Text()
    {
        System.Drawing.Font font = new System.Drawing.Font("Arial", 14.0f, FontStyle.Regular);
    
        mesh_x = Mesh.TextFromFont(device, font, "> x", 0.01f, 0.1f);
        mesh_y = Mesh.TextFromFont(device, font, "> y", 0.01f, 0.1f);
        mesh_z = Mesh.TextFromFont(device, font, "> z", 0.01f, 0.1f);
    }

Rendering

  • Redraw Method: Clears the screen, applies transformations, and draws the matrix points and coordinate axes using DirectX.
    • The Redraw() method is responsible for rendering the 3D scene.
    • It applies transformations and draws the points and axes in the 3D space.
      void Redraw()
      {
          device.Clear(ClearFlags.Target, Color.Black, 1.0f, 0);
      
          device.BeginScene();
          device.VertexFormat = CustomVertex.PositionColored.Format;
      
          // Camera field of view definition
          device.Transform.Projection = 
              Matrix.PerspectiveFovRH(
                  (float)Math.PI / zoom,
                  Panel.Width / Panel.Height,
                  1f,
                  150f
              );
      
          // Camera reposition
          device.Transform.View = 
              Matrix.LookAtRH(
                  new Vector3(camera_x, camera_y, camera_z),
                  new Vector3(camera_look_x, camera_look_y, camera_look_z),
                  new Vector3(0.0f, 1.0f, 0.0f)
              );
      
          // Scene transformations
          device.Transform.World = 
              Matrix.RotationZ(angleZ_R) *
              Matrix.RotationY(angleY_R) *
              Matrix.RotationX(angleX_R);
      
          // Draw matrix values
          device.DrawUserPrimitives(PrimitiveType.PointList, World, points_x);
      
          // Draw axes
          device.DrawUserPrimitives(PrimitiveType.LineList, 3, axes);
      
          device.Transform.World = 
              Matrix.Translation(10 * x_max, -0.3f, 0.05f) *
              Matrix.Scaling(0.1f, 0.1f, 0.1f) *
              Matrix.RotationZ(angleZ_R) *
              Matrix.RotationY(angleY_R) *
              Matrix.RotationX(angleX_R);
      
          mesh_x.DrawSubset(0);
      
          device.Transform.World = 
              Matrix.RotationZ(90 * rad) *
              Matrix.Translation(0.3f, 10 * y_max, 0.05f) *
              Matrix.Scaling(0.1f, 0.1f, 0.1f) *
              Matrix.RotationZ(angleZ_R) *
              Matrix.RotationY(angleY_R) *
              Matrix.RotationX(angleX_R);
      
          mesh_y.DrawSubset(0);
      
          device.Transform.World = 
              Matrix.RotationZ(90 * rad) *
              Matrix.RotationX(90 * rad) *
              Matrix.Translation(0.3f, -0.05f, 10 * z_max) *
              Matrix.Scaling(0.1f, 0.1f, 0.1f) *
              Matrix.RotationZ(angleZ_R) *
              Matrix.RotationY(angleY_R) *
              Matrix.RotationX(angleX_R);
      
          mesh_z.DrawSubset(0);
      
          //
          //
          //
          device.EndScene();
      
          device.Present();
      }
    • Clearing the Screen: Before drawing, the screen is cleared to a black background.
    • World Transformations: Rotations in the X, Y, and Z axes are applied to the scene to allow user interaction with the 3D graph.
    • Drawing Primitives: The matrix values are drawn as colored points in the 3D space, along with the axes.
  • Recalculate Method: Updates rotation and zoom based on user input, adjusting the 3D view dynamically.
    void Recalculate()
    {
        if (x)
        {
            Reset();
        }
    
        #region - speed -
        if (step_p)
        {
            step++;
        }
        if (step_m)
        {
            step--;
        }
    
        if (step == 0)
        {
            step = 1;
        }
        #endregion
    
        #region - rotation -
        if (left)
        {
            angleY_D -= step;
        }
        if (right)
        {
            angleY_D += step;
        }
    
        if (up)
        {
            angleX_D -= step;
        }
        if (down)
        {
            angleX_D += step;
        }
    
        if (a)
        {
            angleZ_D -= step;
        }
        if (d)
        {
            angleZ_D += step;
        }
    
        // World rotation
        if (angleX_D >= 360)
        {
            angleX_D = 0;
        }
        if (angleX_D <= -360)
        {
            angleX_D = 0;
        }
    
        if (angleY_D >= 360)
        {
            angleY_D = 0;
        }
        if (angleY_D <= -360)
        {
            angleY_D = 0;
        }
    
        if (angleZ_D >= 360)
        {
            angleZ_D = 0;
        }
        if (angleZ_D <= -360)
        {
            angleZ_D = 0;
        }
    
        angleX_R = angleX_D * rad;
        angleY_R = angleY_D * rad;
        angleZ_R = angleZ_D * rad;
        #endregion
    
        #region - move -
        if (s)
        {
            camera_z += 0.1f * step;
    
            if (camera_z > 150.0f)
            {
                camera_z = 150f;
            }
        }
    
        if (w)
        {
            camera_z -= 0.1f * step;
    
            if (camera_z < -150.0f)
            {
                camera_z = -150.0f;
            }
        }
        #endregion
    }

User Interaction

Keyboard and Mouse Handlers

Allows users to rotate, zoom, and move the camera view using keyboard and mouse controls, making the visualization interactive and user-friendly.

  • The keyboard and mouse interaction allows the user to rotate, move, and zoom into the 3D space.
  • The OnKeyDown(), OnKeyUp(), and OnMouseWheel() methods handle these interactions.
    • Arrow Keys and A/D: Control the rotation of the 3D graph.
    • W/S Keys: Control zooming in and out of the 3D space.
    • Escape key resets view
    • ( + ) plus and ( - ) minus keys control the speed of animation
    • Mouse wheel Up/Down controls zoom
      protected override void OnKeyDown(KeyEventArgs e)
      {
          base.OnKeyDown(e);
      
          #region - exit -
          if (e.KeyCode == Keys.Escape)
          {
              x = !x;
          }
          #endregion
      
          #region - speed -
          if (e.KeyCode == Keys.Add || e.KeyCode == Keys.Oemplus)
          {
              step_p = true;
          }
          if (e.KeyCode == Keys.Subtract || e.KeyCode == Keys.OemMinus)
          {
              step_m = true;
          }
      
          if (step_p)
          {
              step++;
          }
          if (step_m)
          {
              step--;
          }
      
          if (step == 0)
          {
              step = 1;
          }
          #endregion
      
          #region - rotation -
          if (e.KeyCode == Keys.Left)
          {
              left = true;
          }
          if (e.KeyCode == Keys.Right)
          {
              right = true;
          }
          if (e.KeyCode == Keys.Up)
          {
              up = true;
          }
          if (e.KeyCode == Keys.Down)
          {
              down = true;
          }
      
          if (e.KeyCode == Keys.A)
          {
              a = true;
          }
          if (e.KeyCode == Keys.D)
          {
              d = true;
          }
          #endregion
      
          #region - move -
          if (e.KeyValue == (char)Keys.W)
          {
              w = true;
          }
          if (e.KeyValue == (char)Keys.S)
          {
              s = true;
          }
          #endregion
      }
      protected override void OnKeyUp(KeyEventArgs e)
      {
          base.OnKeyUp(e);
      
          #region - speed -
          if (e.KeyCode == Keys.Add || e.KeyCode == Keys.Oemplus)
          {
              step_p = false;
          }
          if (e.KeyCode == Keys.Subtract || e.KeyCode == Keys.OemMinus)
          {
              step_m = false;
          }
          #endregion
      
          #region - rotation -
          if (e.KeyCode == Keys.Left)
          {
              left = false;
          }
          if (e.KeyCode == Keys.Right)
          {
              right = false;
          }
          if (e.KeyCode == Keys.Up)
          {
              up = false;
          }
          if (e.KeyCode == Keys.Down)
          {
              down = false;
          }
      
          if (e.KeyCode == Keys.A)
          {
              a = false;
          }
          if (e.KeyCode == Keys.D)
          {
              d = false;
          }
          #endregion
      
          #region - zoom -
          if (e.KeyValue == (char)Keys.W)
          {
              w = false;
          }
          if (e.KeyValue == (char)Keys.S)
          {
              s = false;
          }
          #endregion
      }
      protected override void OnMouseWheel(MouseEventArgs e)
       {
          base.OnMouseWheel(e);
      
          #region - zoom -
          if (e.Delta < 0)
              zoom -= 0.1f * step;
          else
              zoom += 0.1f * step;
      
          if (zoom > 10.0f)
              zoom = 10.0f;
          else if (zoom < 1.0f)
              zoom = 1.0f;
          #endregion
      
       }
      

Events Handling

  • Panel Events: Responds to panel size changes and paint events, ensuring that the 3D scene is redrawn correctly as the user interacts with the application.
  • PanelPaint event handler: This method handles the Paint event of the panel where the 3D visualization is rendered. It ensures that every time the panel needs to be repainted, it will update the scene accordingly.
    void PanelPaint(object sender, PaintEventArgs e)
    {
        Recalculate();
        Redraw();
    }
  • Recalculate(): This method updates any necessary calculations or states related to the scene, such as camera position, rotation angles, or zoom levels, based on user inputs or other conditions.
  • Redraw(): This method is responsible for rendering the updated scene to the panel. It draws the 3D matrix visualization and other elements like coordinate axes.
  • This method is crucial for maintaining an up-to-date visual representation of the 3D data. It ensures that any changes (like user interactions or updates to the matrix data) are reflected immediately on the panel. Since it’s tied to the Paint event, it will be triggered whenever the panel needs to redraw its contents, such as when the window is resized or minimized and restored.
  • PanelSizeChanged event handler: This method handles the SizeChanged event of the panel. It is invoked whenever the size of the panel changes, which might occur when the window is resized.
    void PanelSizeChanged(object sender, EventArgs e)
    {
        Initialize_Device();
        Initialize_Camera();
        Initialize_Text();
        Recalculate();
        Redraw();
    
        if (Size == MaximumSize)
        {
            Text = "                                    " +
                   "                                    " +
                   "Graphical Matrix Representation";
        }
        else
        {
            Text = "                              " +
                   "Graphical Matrix Representation";
        }
    
        Refresh();
    }
    • Initialize_Device(): Reinitializes the DirectX device settings to adapt to the new panel size.
    • Initialize_Camera(): Adjust the camera settings to fit the new panel dimensions, ensuring that the 3D scene is projected correctly.
    • Initialize_Text(): Updates the text labels for the coordinate axes or other textual elements, if needed.
    • Recalculate(): Updates the scene’s calculations to reflect any changes due to resizing.
    • Redraw(): Renders the updated scene onto the resized panel.
    • Title Adjustment: The method updates the form’s title based on whether the panel is maximized or not, which helps in providing visual feedback to the user regarding the state of the application.
    • Refresh(): Forces the panel to redraw itself immediately, ensuring that all changes are visually updated.

Development Environment and Requirements

The program is implemented as a Windows Forms application, built in the SharpDevelop IDE / MS Visual Studio, using C# version 4.0 and the .NET Framework version 2.0. It leverages Microsoft's DirectX for 3D rendering, making it essential for users to install the DirectX SDK Legacy if they wish to run, modify, or extend the program.

Ideal for C# Beginners

This program serves as a practical example for C# developers looking to explore 3D graphics. It addresses the common question: "How do I create a simple 3D scene, render points and lines, and control scene zoom and rotation with keyboard input?"


Similar Articles