ASE Home Page Products Download Purchase Support About ASE
ChartDirector Support
Forum HomeForum Home   SearchSearch

Message ListMessage List     Post MessagePost Message

  line plot in 3d
Posted by Raimund on Oct-16-2024 00:01
Attachments:
For a scientific paper I have to represent a thin wire carrying an electric current in three dimensions. I would like to do this graphically with a 3D line (see file wire.png).
In the second step I have to visualize the forces acting on the wire, either with colors and/or with arrows (see file Arrows.png). I created the images with Gnuplot or Matplot++.
Is this also possible with ChartDirector?

Regards,
Raimund
wire.png
Arrow.png

  Re: line plot in 3d
Posted by Peter Kwan on Oct-16-2024 15:05
Hi Raimund,

Yes, ChartDirector can draw the charts you need.

It is possible to draw 3d line segments as shown in the example below:

https://www.chartdir.com/forum/download_thread.php?bn=chartdir_support&thread=1681639966#N1681736261

The spiral can be drawn with many small line segments. (In practice, the curves in computers graphics are often drawn as many small line segments.) The arrows can also be drawn as line segments.

If you can inform me the programming language you want to use, I can provide an example for you.

Best Regards
Peter Kwan

  Re: line plot in 3d
Posted by Peter Kwan on Oct-17-2024 01:57
Attachments:
Hi Raimund,

I have modified the MFC version of the 3D Chart Rotation sample code to produce the attached charts. The code contains two methods "drawChart" and "drawChart2". The first one draws the line chart, and the second one draws the arrow chart.

Basically, the code uses a loop to add the line segments to the chart to draw the spiral and arrows.

Best Regards
Peter Kwan
mfcdemo_20241017012616.png
mfcdemo_20241017012452.png
mfcdemo_20241017012438.png
ThreeDChartRotationDlg.cpp
// ThreeDChartRotationDlg.cpp : implementation file
//

#include "stdafx.h"
#include "resource.h"
#include "ThreeDChartRotationDlg.h"
#include <math.h>
#include <vector>
#include <assert.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

//
// Constructor
//
CThreeDChartRotationDlg::CThreeDChartRotationDlg(CWnd* pParent /*=NULL*/)
    : CDialog(IDD_THREEDCHARTROTATION, pParent)
{
	// 3D view angles
	m_elevationAngle = 30;
	m_rotationAngle = 45;

	// Keep track of mouse drag
	m_isDragging = false;
	m_lastMouseX = -1;
	m_lastMouseY = -1;
}

//
// Destructor
//
CThreeDChartRotationDlg::~CThreeDChartRotationDlg()
{
    delete m_ChartViewer.getChart();
}

void CThreeDChartRotationDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_ChartViewer, m_ChartViewer);
	DDX_Control(pDX, IDC_CHECK1, m_DrawFrameOnRotate);
}

BEGIN_MESSAGE_MAP(CThreeDChartRotationDlg, CDialog)
    ON_CONTROL(CVN_ViewPortChanged, IDC_ChartViewer, OnViewPortChanged)
	ON_CONTROL(CVN_MouseMoveChart, IDC_ChartViewer, OnMouseMoveChart)
	ON_CONTROL(BN_CLICKED, IDC_ChartViewer, OnMouseUpChart)
END_MESSAGE_MAP()


//
// Initialization
//
BOOL CThreeDChartRotationDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

	// To handle mouse drags, it is necessary to enable mouse capture. By default, MFC does not
	// enable mouse capture. In ChartDirector, we can use MouseUsageDefaultCapture to enable it.
	m_ChartViewer.setMouseUsage(Chart::MouseUsageDefaultCapture);
	
	// Update the viewport to display the chart
	m_ChartViewer.updateViewPort(true, false);

	// In this example, the default is to draw wire frame on rotate
	m_DrawFrameOnRotate.SetCheck(1);

	return TRUE;
}

//
// View port changed event
//
void CThreeDChartRotationDlg::OnViewPortChanged()
{
    // Update the chart if necessary
    if (m_ChartViewer.needUpdateChart())
        drawChart(&m_ChartViewer);
}

//
// Draw the chart and display it in the given viewer
//
void CThreeDChartRotationDlg::drawChart(CChartViewer *viewer)
{
	std::vector<double> dataX(100);
	std::vector<double> dataY(100);
	std::vector<double> dataZ(100);

	for (int i = 0; i < 100; ++i)
	{
		double angle = i * 10 * 3.1416 / 180;
		dataX[i] = 8 * sin(angle);
		dataY[i] = 8 * cos(angle);
		dataZ[i] = 0.08 * i;
	}

	// Create a SurfaceChart object of size 720 x 600 pixels
	ThreeDScatterChart*c = new ThreeDScatterChart(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);
	
	c->addScatterGroup(DoubleArray(dataX.data(), dataX.size()), 
		DoubleArray(dataY.data(), dataY.size()), DoubleArray(dataZ.data(), dataZ.size()),
		"", Chart::SquareShape, 1, Chart::Transparent);

	// Set the view angles
	c->setViewAngle(m_elevationAngle, m_rotationAngle);

	// 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);

	c->makeChart();
	
	double x0 = c->getXCoor(dataX[0], dataY[0], dataZ[0]);
	double y0 = c->getYCoor(dataX[0], dataY[0], dataZ[0]);
	for (int i = 1; i < dataX.size(); ++i)
	{
		double x1 = c->getXCoor(dataX[i], dataY[i], dataZ[i]);
		double y1 = c->getYCoor(dataX[i], dataY[i], dataZ[i]);
		c->addLine(x0, y0, x1, y1, 0x66aaee);
		x0 = x1;
		y0 = y1;
	}

	// Output the chart
	delete viewer->getChart();
    viewer->setChart(c);
}


void CThreeDChartRotationDlg::drawChart2(CChartViewer* viewer)
{
	std::vector<double> dataX(100);
	std::vector<double> dataY(100);
	std::vector<double> dataZ(100);

	std::vector<double> arrowX(100);
	std::vector<double> arrowY(100);
	std::vector<double> arrowZ(100);


	for (int i = 0; i < 100; ++i)
	{
		double angle = i * 10 * 3.1416 / 180;
		dataX[i] = 8 * sin(angle);
		dataY[i] = 8 * cos(angle);
		dataZ[i] = 0.08 * i;

		arrowX[i] = 2 * cos(angle);
		arrowY[i] = 2 * sin(angle);
		arrowZ[i] = 0; // 0.6 * sin(angle + i * 0.2);
	}

	// Create a SurfaceChart object of size 720 x 600 pixels
	ThreeDScatterChart* c = new ThreeDScatterChart(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);

	c->addScatterGroup(DoubleArray(dataX.data(), dataX.size()),
		DoubleArray(dataY.data(), dataY.size()), DoubleArray(dataZ.data(), dataZ.size()),
		"", Chart::SquareShape, 1, Chart::Transparent);

	// Set the view angles
	c->setViewAngle(m_elevationAngle, m_rotationAngle);

	// 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);

	c->makeChart();

	for (int i = 1; i < dataX.size(); ++i)
	{
		double x0 = c->getXCoor(dataX[i], dataY[i], dataZ[i]);
		double y0 = c->getYCoor(dataX[i], dataY[i], dataZ[i]);
		double x1 = c->getXCoor(dataX[i] + arrowX[i], dataY[i] + arrowY[i], dataZ[i] + arrowZ[i]);
		double y1 = c->getYCoor(dataX[i] + arrowX[i], dataY[i] + arrowY[i], dataZ[i] + arrowZ[i]);
				
		c->addLine(x0, y0, x1, y1, 0x66aaee);

		double dx = x1 - x0;
		double dy = y1 - y0;
		double len = (std::max)(1.0, sqrt(dx * dx + dy * dy));
		dx *= 10 / len;
		dy *= 10 / len;

		c->addLine(x1, y1, x1 - dx + dy / 4, y1 - dy - dx / 4, 0x66aaee);
		c->addLine(x1, y1, x1 - dx - dy / 4, y1 - dy + dx / 4, 0x66aaee);
	}

	// Output the chart
	delete viewer->getChart();
	viewer->setChart(c);
}



//
// Rotate surface chart when mouse is moving over plotarea
//
void CThreeDChartRotationDlg::OnMouseMoveChart()
{
    int mouseX = m_ChartViewer.getChartMouseX();
	int mouseY = m_ChartViewer.getChartMouseY();

    // Drag occurs if mouse button is down and the mouse is captured by the m_ChartViewer
	if (((GetKeyState(VK_LBUTTON) & 0x100) != 0) && (GetCapture() == &m_ChartViewer))
	{
		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;
			m_ChartViewer.updateViewPort(true, false);
		}

		// Keep track of the last mouse position
		m_lastMouseX = mouseX;
		m_lastMouseY = mouseY;
		m_isDragging = true;
	}
}

//
// Stops dragging on mouse up
//
void CThreeDChartRotationDlg::OnMouseUpChart()
{
	m_isDragging = false;
	m_ChartViewer.updateViewPort(true, false);
}