ChartDirector 7.1 (.NET Edition)

3D Chart Rotation




This example demonstrates how to interactively rotate a 3D chart.

ThreeDChart objects can be rotated by simply redrawing the chart with an updated rotation and/or elevation angles. For interactive rotation, some user interface is needed to change the angles. In this example, we use the chart itself as the user interface. When the user drags on the chart, the angles will be updated depending on the drag direction, and WinChartViewer.updateViewPort (for Windows Forms) or WPFChartViewer.updateViewPort (for WPF) will be called to trigger a redraw of the chart.

The main source code listing of this sample program is included at the end of this section. The code consists of the following main parts:

Source Code Listing

[Windows Forms - C# version] NetWinCharts\CSharpWinCharts\frmthreedchartrotation.cs
using System; using System.Windows.Forms; using ChartDirector; namespace CSharpChartExplorer { public partial class FrmThreeDChartRotation : Form { // 3D view angles private double m_elevationAngle; private double m_rotationAngle; // Keep track of mouse drag private int m_lastMouseX; private int m_lastMouseY; private bool m_isDragging; public FrmThreeDChartRotation() { InitializeComponent(); } // // Form Load event handler // private void FrmThreeDChartRotation_Load(object sender, EventArgs e) { // 3D view angles m_elevationAngle = 30; m_rotationAngle = 45; // Keep track of mouse drag m_isDragging = false; m_lastMouseX = -1; m_lastMouseY = -1; // Draw the chart winChartViewer1.updateViewPort(true, false); } // // The ViewPortChanged event handler // private void winChartViewer1_ViewPortChanged(object sender, WinViewPortEventArgs e) { // Update the chart if necessary if (e.NeedUpdateChart) drawChart((WinChartViewer)sender); } public void drawChart(WinChartViewer viewer) { // The x and y coordinates of the grid double[] dataX = {-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; double[] dataY = {-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // The values at the grid points. In this example, we will compute the values using the // formula z = x * sin(y) + y * sin(x). double[] dataZ = new double[dataX.Length * dataY.Length]; for (int yIndex = 0; yIndex < dataY.Length; ++yIndex) { double y = dataY[yIndex]; for (int xIndex = 0; xIndex < dataX.Length; ++xIndex) { double x = dataX[xIndex]; dataZ[yIndex * dataX.Length + xIndex] = x * Math.Sin(y) + y * Math.Sin(x); } } // Create a SurfaceChart object of size 720 x 600 pixels SurfaceChart c = new SurfaceChart(720, 600); // Set the center of the plot region at (330, 290), and set width x depth x height to // 360 x 360 x 270 pixels c.setPlotRegion(330, 290, 360, 360, 270); // Set the data to use to plot the chart c.setData(dataX, dataY, dataZ); // Spline interpolate data to a 80 x 80 grid for a smooth surface c.setInterpolation(80, 80); // Set the view angles c.setViewAngle(m_elevationAngle, m_rotationAngle); // Check if draw frame only during rotation if (m_isDragging && DrawFrameOnRotate.Checked) c.setShadingMode(Chart.RectangularFrame); // Add a color axis (the legend) in which the left center is anchored at (660, 270). Set // the length to 200 pixels and the labels on the right side. c.setColorAxis(650, 270, Chart.Left, 200, Chart.Right); // Set the x, y and z axis titles using 10 points Arial Bold font c.xAxis().setTitle("X", "Arial Bold", 15); c.yAxis().setTitle("Y", "Arial Bold", 15); // Set axis label font c.xAxis().setLabelStyle("Arial", 10); c.yAxis().setLabelStyle("Arial", 10); c.zAxis().setLabelStyle("Arial", 10); c.colorAxis().setLabelStyle("Arial", 10); // Output the chart viewer.Chart = c; //include tool tip for the chart viewer.ImageMap = c.getHTMLImageMap("", "", "title='<*cdml*>X: {x|2}<*br*>Y: {y|2}<*br*>Z: {z|2}'"); } private void winChartViewer1_MouseMoveChart(object sender, MouseEventArgs e) { int mouseX = winChartViewer1.ChartMouseX; int mouseY = winChartViewer1.ChartMouseY; // Drag occurs if mouse button is down and the mouse is captured by the m_ChartViewer if (((MouseButtons & MouseButtons.Left) != 0) && winChartViewer1.Capture) { if (m_isDragging) { // The chart is configured to rotate by 90 degrees when the mouse moves from // left to right, which is the plot region width (360 pixels). Similarly, the // elevation changes by 90 degrees when the mouse moves from top to buttom, // which is the plot region height (270 pixels). m_rotationAngle += (m_lastMouseX - mouseX) * 90.0 / 360; m_elevationAngle += (mouseY - m_lastMouseY) * 90.0 / 270; winChartViewer1.updateViewPort(true, false); } // Keep track of the last mouse position m_lastMouseX = mouseX; m_lastMouseY = mouseY; m_isDragging = true; } } private void winChartViewer1_MouseUpChart(object sender, MouseEventArgs e) { m_isDragging = false; winChartViewer1.updateViewPort(true, false); } } }

[Windows Forms - VB Version] NetWinCharts\VBNetWinCharts\frmthreedchartrotation.vb
Imports ChartDirector Public Class FrmThreeDChartRotation ' The view angles Private m_elevationAngle As Double Private m_rotationAngle As Double ' Keep track of mouse drag Private m_lastMouseX As Integer Private m_lastMouseY As Integer Private m_isDragging As Boolean ' ' Form Load event handler ' Private Sub FrmThreeDChartRotation_Load(sender As Object, e As EventArgs) Handles MyBase.Load ' 3D view angles m_elevationAngle = 30 m_rotationAngle = 45 ' Keep track of mouse drag m_isDragging = False m_lastMouseX = -1 m_lastMouseY = -1 'Display the chart winChartViewer1.updateViewPort(True, False) End Sub ' ' The ViewPortChanged event handler ' Private Sub winChartViewer1_ViewPortChanged(sender As Object, e As ChartDirector.WinViewPortEventArgs) Handles winChartViewer1.ViewPortChanged ' Update the chart if necessary If e.NeedUpdateChart Then drawChart(winChartViewer1) End If End Sub Public Sub drawChart(viewer As WinChartViewer) ' The x And y coordinates of the grid Dim dataX As Double() = {-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} Dim dataY As Double() = {-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} ' The values at the grid points. In this example, we will compute the values using the ' formula z = x * sin(y) + y * sin(x). Dim dataZ(dataX.Length * dataY.Length - 1) As Double For yIndex As Integer = 0 To UBound(dataY) Dim y As Double = dataY(yIndex) For xIndex As Integer = 0 To UBound(dataX) Dim x As Double = dataX(xIndex) dataZ(yIndex * dataX.Length + xIndex) = x * Math.Sin(y) + y * Math.Sin(x) Next Next ' Create a SurfaceChart object of size 720 x 600 pixels Dim c As SurfaceChart = New SurfaceChart(720, 600) ' Set the center of the plot region at (330, 290), And set width x depth x height to ' 360 x 360 x 270 pixels c.setPlotRegion(330, 290, 360, 360, 270) ' Set the data to use to plot the chart c.setData(dataX, dataY, dataZ) ' Spline interpolate data to a 80 x 80 grid for a smooth surface c.setInterpolation(80, 80) ' Set the view angles c.setViewAngle(m_elevationAngle, m_rotationAngle) ' Check if draw frame only during rotation If m_isDragging And DrawFrameOnRotate.Checked Then c.setShadingMode(Chart.RectangularFrame) End If ' Add a color axis (the legend) in which the left center Is anchored at (660, 270). Set ' the length to 200 pixels And the labels on the right side. c.setColorAxis(650, 270, Chart.Left, 200, Chart.Right) ' Set the x, y And z axis titles using 10 points Arial Bold font c.xAxis().setTitle("X", "Arial Bold", 15) '; c.yAxis().setTitle("Y", "Arial Bold", 15) '; ' Set axis label font c.xAxis().setLabelStyle("Arial", 10) c.yAxis().setLabelStyle("Arial", 10) c.zAxis().setLabelStyle("Arial", 10) c.colorAxis().setLabelStyle("Arial", 10) ' Output the chart viewer.Chart = c 'include tool tip for the chart viewer.ImageMap = c.getHTMLImageMap("", "", "title='<*cdml*>X: {x|2}<*br*>Y: {y|2}<*br*>Z: {z|2}'") End Sub Private Sub winChartViewer1_MouseMove(sender As Object, e As MouseEventArgs) Handles winChartViewer1.MouseMove Dim mouseX As Integer = winChartViewer1.ChartMouseX Dim mouseY As Integer = winChartViewer1.ChartMouseY ' Drag occurs if mouse button Is down And the mouse Is captured by the m_ChartViewer If (MouseButtons And MouseButtons.Left) <> 0 AndAlso winChartViewer1.Capture Then If m_isDragging Then ' The chart Is configured to rotate by 90 degrees when the mouse moves from ' left to right, which Is the plot region width (360 pixels). Similarly, the ' elevation changes by 90 degrees when the mouse moves from top to buttom, ' which Is the plot region height (270 pixels). m_rotationAngle += (m_lastMouseX - mouseX) * 90.0 / 360 m_elevationAngle += (mouseY - m_lastMouseY) * 90.0 / 270 winChartViewer1.updateViewPort(True, False) End If ' Keep track of the last mouse position m_lastMouseX = mouseX m_lastMouseY = mouseY m_isDragging = True End If End Sub Private Sub winChartViewer1_MouseUp(sender As Object, e As MouseEventArgs) Handles winChartViewer1.MouseUp m_isDragging = False winChartViewer1.updateViewPort(True, False) End Sub End Class

[WPF - XAML] NetWPFCharts\CSharpWPFCharts\ThreeDChartRotationWindow.xaml
<Window x:Class="CSharpWPFCharts.ThreeDChartRotationWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:CSharpWPFCharts" mc:Ignorable="d" xmlns:ChartDirector="clr-namespace:ChartDirector;assembly=netchartdir" UseLayoutRounding="True" Title="3D Interactive Rotation" Loaded="Window_Loaded" SizeToContent="WidthAndHeight" ResizeMode="NoResize" > <StackPanel> <DockPanel Background="#FFDDDDDD" > <CheckBox x:Name="DrawFrameOnRotate" Content="Draw Frame On Rotate" Margin="8" IsChecked="True" /> </DockPanel> <ChartDirector:WPFChartViewer x:Name="WPFChartViewer1" Width="720" Height="600" ViewPortChanged="WPFChartViewer1_ViewPortChanged" MouseMoveChart="WPFChartViewer1_MouseMoveChart" MouseUp="WPFChartViewer1_MouseUpChart" /> </StackPanel> </Window>

[WPF - C#] NetWPFCharts\CSharpWPFCharts\ThreeDChartRotationWindow.xaml.cs
using System; using System.Windows; using System.Windows.Input; using ChartDirector; namespace CSharpWPFCharts { /// <summary> /// Interaction logic for ThreeDChartRotationWindow.xaml /// </summary> public partial class ThreeDChartRotationWindow : Window { // 3D view angles private double m_elevationAngle; private double m_rotationAngle; // Keep track of mouse drag private int m_lastMouseX; private int m_lastMouseY; private bool m_isDragging; public ThreeDChartRotationWindow() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { // 3D view angles m_elevationAngle = 30; m_rotationAngle = 45; // Keep track of mouse drag m_isDragging = false; m_lastMouseX = -1; m_lastMouseY = -1; // Draw the chart WPFChartViewer1.updateViewPort(true, false); } // // The ViewPortChanged event handler // private void WPFChartViewer1_ViewPortChanged(object sender, WPFViewPortEventArgs e) { // Update the chart if necessary if (e.NeedUpdateChart) drawChart((WPFChartViewer)sender); } public void drawChart(WPFChartViewer viewer) { // The x and y coordinates of the grid double[] dataX = {-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; double[] dataY = {-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // The values at the grid points. In this example, we will compute the values using the // formula z = x * sin(y) + y * sin(x). double[] dataZ = new double[dataX.Length * dataY.Length]; for (int yIndex = 0; yIndex < dataY.Length; ++yIndex) { double y = dataY[yIndex]; for (int xIndex = 0; xIndex < dataX.Length; ++xIndex) { double x = dataX[xIndex]; dataZ[yIndex * dataX.Length + xIndex] = x * Math.Sin(y) + y * Math.Sin(x); } } // Create a SurfaceChart object of size 720 x 600 pixels SurfaceChart c = new SurfaceChart(720, 600); // Set the center of the plot region at (330, 290), and set width x depth x height to // 360 x 360 x 270 pixels c.setPlotRegion(330, 290, 360, 360, 270); // Set the data to use to plot the chart c.setData(dataX, dataY, dataZ); // Spline interpolate data to a 80 x 80 grid for a smooth surface c.setInterpolation(80, 80); // Set the view angles c.setViewAngle(m_elevationAngle, m_rotationAngle); // Check if draw frame only during rotation if (m_isDragging && DrawFrameOnRotate.IsChecked.Value) c.setShadingMode(Chart.RectangularFrame); // Add a color axis (the legend) in which the left center is anchored at (660, 270). Set // the length to 200 pixels and the labels on the right side. c.setColorAxis(650, 270, Chart.Left, 200, Chart.Right); // Set the x, y and z axis titles using 10 points Arial Bold font c.xAxis().setTitle("X", "Arial Bold", 15); c.yAxis().setTitle("Y", "Arial Bold", 15); // Set axis label font c.xAxis().setLabelStyle("Arial", 10); c.yAxis().setLabelStyle("Arial", 10); c.zAxis().setLabelStyle("Arial", 10); c.colorAxis().setLabelStyle("Arial", 10); // Output the chart viewer.Chart = c; //include tool tip for the chart viewer.ImageMap = c.getHTMLImageMap("", "", "title='<*cdml*>X: {x|2}<*br*>Y: {y|2}<*br*>Z: {z|2}'"); } // // Draw track cursor when mouse is moving over plotarea // private void WPFChartViewer1_MouseMoveChart(object sender, MouseEventArgs e) { int mouseX = WPFChartViewer1.ChartMouseX; int mouseY = WPFChartViewer1.ChartMouseY; // Drag occurs if mouse button is down and the mouse is captured by the m_ChartViewer if (Mouse.LeftButton == MouseButtonState.Pressed) { if (m_isDragging) { // The chart is configured to rotate by 90 degrees when the mouse moves from // left to right, which is the plot region width (360 pixels). Similarly, the // elevation changes by 90 degrees when the mouse moves from top to buttom, // which is the plot region height (270 pixels). m_rotationAngle += (m_lastMouseX - mouseX) * 90.0 / 360; m_elevationAngle += (mouseY - m_lastMouseY) * 90.0 / 270; WPFChartViewer1.updateViewPort(true, false); } // Keep track of the last mouse position m_lastMouseX = mouseX; m_lastMouseY = mouseY; m_isDragging = true; } } private void WPFChartViewer1_MouseUpChart(object sender, MouseEventArgs e) { m_isDragging = false; WPFChartViewer1.updateViewPort(true, false); } } }