Click here to Skip to main content
15,070,511 members
Articles / Programming Languages / C# 4.0
Article
Posted 4 Feb 2021

Stats

32.6K views
2.3K downloads
83 bookmarked

Graph3D: A Windows.Forms Render Control in C#

Rate me:
Please Sign up or sign in to vote.
4.98/5 (70 votes)
4 Oct 2021CPOL5 min read
An easy to use 3D control which can be integrated into an application in a few minutes
A universal ready-to-use 3D Graph control for System.Windows.Forms applications. It displays 3D functions or X,Y,Z data. The control consists of a single C# file and is optimized for maximum speed.

System.Windows.Forms 3D Graph Control in C#

Features

  • NEW: Copy Screenshot to Image
  • NEW: Set Rho, Theta, Phi programmatically
  • NEW: Drawing of scatterplots
  • NEW: Coordinate system now also with negative values
  • Universal ready-to-use 3D Graph control for System.Windows.Forms applications
  • Derived from UserControl
  • Target: Framework 4 (Visual Studio 2010 or higher)
  • Display of 3 dimensional functions or binary data (X, Y, Z values)
  • Very clean and reusable code written by an experienced programmer
  • All code is in one single C# file with < 1200 lines
  • Optional function compiler allows to enter formulas as strings
  • Optional coordinate system with raster lines and labels
  • Optionally multiple color schemes
  • The user can rotate, elevate and zoom with the mouse or with 3 optional TrackBars
  • Zooming is also possible with the mouse wheel, but only if the 3D Graph has the focus.
  • The entire code is optimized for the maximum speed that is possible.
  • An optional legend displays the current rotation angles to the user in the top left corner.
  • An optional legend displays a user defined text for the axis in the bottom left corner.
  • The black lines between the polygons can be turned off.
  • Automatic normalization of 3D input data with 3 options

Why this Project?

I'am writing an ECU tunig software HUD ECU Hacker for which I need a 3D Viewer which displays the calibration tables.
I searched a ready-to-use 3D Control in internet but could not find what fits my needs.
Huge 3D software projects like Helix Toolkit are completely overbloated (220 MB) for my small project.
Commercial 3D software from $250 USD up to $2900 USD is also not an option.

 

WPF 3D Chart (from Jianzhong Zhang)

I found WPF 3D Chart on Codeproject.

It is very fast because WPF is based on ActiveX which uses hardware acceleration.
The graphics processor can render 3D surfaces which must be composed of triangles.
But it is difficult to render lines. Each line would have to be defined as 2 triangles.
I need lines for the coordinate system.
I also need lines which display discrete values on the 3D surface.
I want each value in a data table to be represented as a polygon on the 3D object.
The screenshot above shows the representation of a data table with 22 rows and 17 columns.
I found it too complicated to implement this in WPF.
Extra work must be done to integrate a WPF control into a Windows.Forms application. See this article.

Plot 3D (from Michal Brylka)

Then I found Plot 3D on Codeproject.
This is more what I'am looking for but the code is not reusable and has many issues.

It is one of these many projects on Codeproject or Github which the author never has finished, which are buggy and lack functionality.
There is no useful way to rotate the 3D object. Instead of specifying a rotation angle you must specify the 3D observer coordinates which is a complete misdesign.
After fixing this I found that rotation results in ugly drawing artifacts at certain angles.
The reason is that the polygons are not painted in the correct order.
The code has a bad performance because of wrong programming. For example in OnPaint() he creates each time 100 brushes and disposes them afterwards.
The code has been designed only for formulas but assigning fix values from a data table is not possible.

Graph3D (from me)

I ended up rewriting Plot 3D from the scratch, bug fixing and adding a lot of missing functionality.
The result is a UserControl which you can copy unchanged into your project and which you get working in a few minutes.
The features of my control are already listed above.
As my code does not use hardware acceleration the number of polygons that you display determines the drawing speed.
Without problem you can rotate and elevate the 3D objects of the demos in real time with the mouse without any delay. However if you want to render far more polygons it will be obviously slower.
For my purpose I need less than 2000 polygons which allows real time rotating with the mouse.
Download the ZIP file and then run the already compiled EXE file and play around with it and you will see the speed.

Demo 1 : Fix Values

The screenshot above shows the data from a data table with 22x17 values displayed as 3D surface with coordinate system.

C#
int[,] s32_Values = new int[,]
{
    { 9059,   9634, 10617, 11141, ....., 15368, 15368, 15368, 15368, 15368 }, // row 1
    { 9684,  10387, 11141, 11796, ....., 15794, 15794, 15794, 15794, 15794 }, // row 2
    .........
    { 34669, 34210, 33653, 33096, ....., 27886, 26492, 25167, 25167, 25167 }, // row 21
    { 34767, 34210, 33718, 33096, ....., 27984, 26492, 25167, 25167, 25167 }  // row 22
};

cPoint3D[,] i_Points3D = new cPoint3D[s32_Values.GetLength(0), s32_Values.GetLength(1)];

for (int X=0; X<s32_Values.GetLength(0); X++)
{
    for (int Y=0; Y<s32_Values.GetLength(1); Y++)
    {
        i_Points3D[X,Y] = new cPoint3D(X * 10, Y * 500, s32_Values[X,Y]);
    }
}

graph3D.AxisX_Legend = "MAP (kPa)";
graph3D.AxisY_Legend = "Engine Speed (rpm)";
graph3D.AxisZ_Legend = "ADS";

graph3D.Raster = eRaster.Labels; // Show axis, raster lines, labels
graph3D.SetSurfacePoints(i_Points3D, eNormalize.Separate);

 

When you use discrete values for X,Y and Z which are not related like in this example make sure that X,Y and Z values
are normalized separately by using the parameter eNormalize.Separate because X < 300 and Z > 30000.

Demo 2 : Callback

Or you can write a C# callback function which calculates the Z values from the given X and Y values.

C#
delRendererFunction f_Callback = delegate(double X, double Y)
{
    double r = 0.15 * Math.Sqrt(X * X + Y * Y);
    if (r < 1e-10) return 120;
    else           return 120 * Math.Sin(r) / r;
};

PointF k_Start = new PointF(-120, -80);
PointF k_End   = new PointF( 120,  80);
graph3D.SetFunction(f_Callback, k_Start, k_End, 5, eNormalize.MaintainXYZ);

This code defines a modulated sinus function which is displayed on the X axis from -120 to +120 and on the Y axis from -80 to +80.
The last parameter (5) is the density which defines the count of polygons: (2* 120) / 5 = 48 polygons on the X axis and (2* 80) / 5 = 32 polygons for Y, which results in totally 1536 poygons.

When you use functions make sure that the relation between X,Y and Z values is not distorted
by using the parameter eNormalize.MaintainXYZ.

System.Windows.Forms 3D Graph Control in C#

 

Demo 3 : Formula

Or you can let the user enter a string formula which will be compiled at run time:

C#
String s_Formula = "12 * sin(x) * cos(y) / (sqrt(sqrt(x * x + y * y)) + 0.2)";

delRendererFunction f_Function = FunctionCompiler.Compile(s_Formula);

PointF k_Start = new PointF(-10, -10);
PointF k_End   = new PointF( 10,  10);
graph3D.SetFunction(f_Function, k_Start, k_End, 0.5, eNormalize.MaintainXYZ);

System.Windows.Forms 3D Graph Control in C#

 

Demo 4 : ScatterPlot

C#
List<cScatter> i_List = new List<cScatter>();

for (double P = -22.0; P < 22.0; P += 0.1)
{
    double X = Math.Sin(P) * P;
    double Y = Math.Cos(P) * P;
    double Z = P;
    if (Z > 0.0) Z/= 3.0;
    i_List.Add(new cScatter(X, Y, Z, null));
}

graph3D.Raster = eRaster.Labels; // Show axis, raster lines, labels

// You can also specify MaintainXY or MaintainXYZ here
graph3D.SetScatterPoints(i_List.ToArray(), eNormalize.Separate);

You can either pass a Brush to cScatter for each point in the graph.
Or you pass null and a color from the color scheme is used.

System.Windows.Forms 3D Graph Control in C#

 

Demo 5 : Valentine

Well, this code has just been written on 14th february 2021.

C#
List<cScatter> i_List = new List<cScatter>();

// Upper (round) part of heart
double X = 0.0;
double Z = 0.0;
for (double P = 0.0; P <= Math.PI * 1.32; P += 0.025)
{
    X = Math.Cos(P) * 1.5 - 1.5;
    Z = Math.Sin(P) * 3.0 + 6.0;
    i_List.Add(new cScatter( X, -X, Z, Brushes.Red));
    i_List.Add(new cScatter(-X,  X, Z, Brushes.Red));
}

// Lower (linear) part of heart
double d_X = X / 70;
double d_Z = Z / 70;
while (Z >= 0.0)
{
    i_List.Add(new cScatter( X, -X, Z, Brushes.Red));
    i_List.Add(new cScatter(-X,  X, Z, Brushes.Red));
    X -= d_X;
    Z -= d_Z;
}

graph3D.SetScatterPoints(i_List.ToArray(), eNormalize.MaintainXYZ);

System.Windows.Forms 3D Graph Control in C#

Elmü

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Elmue
Software Developer (Senior) ElmüSoft
Chile Chile
Software Engineer since 27 years.

Comments and Discussions

 
AnswerRe: Naming conventions Pin
Elmue18-Feb-21 4:31
MemberElmue18-Feb-21 4:31 
GeneralRe: Naming conventions Pin
Dennis_E21-Feb-21 22:25
professionalDennis_E21-Feb-21 22:25 
QuestionExcellent article and tool Pin
asiwel9-Feb-21 17:41
professionalasiwel9-Feb-21 17:41 
AnswerRe: Negative coordinates Pin
Elmue10-Feb-21 1:51
MemberElmue10-Feb-21 1:51 
GeneralRe: Negative coordinates - See what you mean Pin
asiwel11-Feb-21 17:40
professionalasiwel11-Feb-21 17:40 
AnswerNew version also allows negative coordinates Pin
Elmue12-Feb-21 7:44
MemberElmue12-Feb-21 7:44 
GeneralRe: New version also allows negative coordinates Pin
asiwel12-Feb-21 15:55
professionalasiwel12-Feb-21 15:55 
AnswerDrawing Labels in 2D plane Pin
Elmue13-Feb-21 3:21
MemberElmue13-Feb-21 3:21 
I made a littel bugfix today (Zero label at Y axis appearing twice)
Download again!

> So you didn't draw them bent or when stacked too much or hidden or upside down, etc.

Yes, this is by purpose.
First: the text is better readable this way than when it would be rotated.
The human eye is used to read horizontal text.

Second: To draw rotated text I would have set a transformation on the Graphics object. I tried that but the quality of rotated text is horrible, especially on Windows 10. I did that in an another project and to get really high quality rotated text I had to:
1.) Create a Bitmap
2.) Get a Graphics object from the Bitmap
3.) Use the old GDI APIs SetTextColor(), SelectObject(), DrawText()
4.) Rotate the Bitmap
5.) Copy the Bitmap on the screen

This is the only way to get high quality rotated text on Windows 10. But for such a small font as used for the labels the rotation also costs quality.

And I think it is not worth the work because in the extreme rotated angles you cannot read the text anymore.
On the other hand my current solution always allows to read the labels well and drawing is additionally much faster.
GeneralRe: Drawing Labels in 2D plane Pin
asiwel13-Feb-21 6:11
professionalasiwel13-Feb-21 6:11 
AnswerDrawing Points Pin
Elmue13-Feb-21 10:21
MemberElmue13-Feb-21 10:21 
GeneralRe: Drawing Points Pin
asiwel13-Feb-21 12:32
professionalasiwel13-Feb-21 12:32 
AnswerRe: Scatterplot Pin
Elmue13-Feb-21 15:16
MemberElmue13-Feb-21 15:16 
AnswerScatterPlot ready Pin
Elmue14-Feb-21 6:45
MemberElmue14-Feb-21 6:45 
GeneralRe: ScatterPlot ready Pin
asiwel14-Feb-21 8:16
professionalasiwel14-Feb-21 8:16 
AnswerNew version Valentine Pin
Elmue14-Feb-21 13:32
MemberElmue14-Feb-21 13:32 
GeneralRe: New version Valentine Pin
asiwel14-Feb-21 13:56
professionalasiwel14-Feb-21 13:56 
SuggestionFun Suggestion Pin
Member 96617299-Feb-21 2:25
MemberMember 96617299-Feb-21 2:25 
AnswerRe: Fun Suggestion Pin
Elmue9-Feb-21 13:40
MemberElmue9-Feb-21 13:40 
GeneralRe: Fun Suggestion Pin
Member 966172912-Feb-21 14:50
MemberMember 966172912-Feb-21 14:50 
AnswerRe: Fun Suggestion Pin
Elmue13-Feb-21 3:28
MemberElmue13-Feb-21 3:28 
GeneralRe: Fun Suggestion Pin
victorbos14-Feb-21 6:43
Membervictorbos14-Feb-21 6:43 
GeneralRe: Fun Suggestion Pin
Member 966172915-Feb-21 1:22
MemberMember 966172915-Feb-21 1:22 
GeneralMy vote of 5 Pin
comlist7-Feb-21 20:58
Membercomlist7-Feb-21 20:58 
QuestionVery nice control (two typo's to correct in text) Pin
Member 115023486-Feb-21 10:09
MemberMember 115023486-Feb-21 10:09 
AnswerRe: Very nice control (two typo's to correct in text) Pin
Elmue8-Feb-21 3:23
MemberElmue8-Feb-21 3:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.