There are some methods for calculating and displaying R2 in R surface functions level curves, one method is to use Euler's approximation derivatives. Most of the methods are computationally expensive. Here, we use a very light method to get a brief view on R2 in R surface level curves.
Introduction
This article describes very briefly (and on a basic level) how to plot user interactive 3D R2 in R surfaces using C# ASP.NET. The example enables the user to interact with the drawing in 5 aspects as below:
- function expression z = f(x,y)
- domain
- integration step
- rotation about x, y and z axis
- model (wire-frame or fill)
Background
- A point in R3 is a set of 3 float values (x,y,z) which defines a position.
- R2 in R functions are expressions in the form z = f(x,y) where x and y are a subset of the function domain.
- The R2 in R domain is the R2 or better, the XY plane.
The Cartesian coordinates
The objective is to get a set of z coordinates applying a generic function z=f(x,y) to a discrete domain, or better a matrix of defined x and y points as the image grid above.
How to Perform the Calculations
At first, we need to define the domain. Let's choose [-1.7,1.3] for x and [-2.1,2.5] for y what means x will run from -1.7 to 1.3 and y will run from -2.1 to 2.5.
How to Obtain the Points Data
As we are working with R (Real) coordinates, we have infinite points between 2 point coordinates, so for this, we create a very small variable which we call 'step', which means the integration we will use to get the grid points by walking between 2 coordinates by 'steps'.
For example, if we define our step as 0.1, the code would be like this:
double x0 = -1.7, x1 = 1.3, y0 = -2.1, y1 = 2.5;
double step = 0.1;
double x=x0;
while(x<=x1)
{
y=y0;
while(y<=y1)
{
y+=step;
}
x+=step;
}
Based on the above definitions, we can handle the calculations of z coordinates from running the XY discrete subdomain by using a generic function z = f(x,y).
double x0=-1.7, x1=1.3, y0=-2.1, y1=2.5;
double step = 0.1;
double x=x0;
double z;
while(x<=x1)
{
y=y0;
while(y<=y1)
{
y+=step;
z = function(x,y);
}
x+=step;
}
Tricky Part
We could use a MathExpressionParser
with a string expression, but here, I made it simpler with the help of a template where we place a replacement tag.
The process is easy:
- read the template txt file,
- replace the tag,
- and write the ASPX file which shows the image.
- Note: You must have write access to the folder where the file will be written ***
string contents = ReadFile(Server.MapPath("./dynafuncapp.txt"));
contents = contents.Replace("@EXPRESSION", functionStr);
SaveFile(Server.MapPath("./dynacontent.aspx"), contents);
Putting Everything Together
A particular note: The better classes, variables and functions are named, it makes commenting code unnecessary or at least redundant.
Pen thePen =
new Pen(Color.FromArgb(ix1, iy1, iz1));
The Template File
<%@ Page Language="c#" Debug="true" Explicit="True" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Drawing" %>
<%@ Import Namespace="System.Drawing.Imaging" %>
<%@ Import Namespace="System.Globalization" %>
<script language="C#" runat="server">
void RotX(ref double cy, ref double cz, double angle)
{
double y = cy * System.Math.Cos(angle) - cz * System.Math.Sin(angle);
double z = cy * System.Math.Sin(angle) + cz * System.Math.Cos(angle);
cy = y;
cz = z;
}
void RotY(ref double cx, ref double cz, double angle)
{
double x = cx * System.Math.Cos(angle) - cz * System.Math.Sin(angle);
double z = cx * System.Math.Sin(angle) + cz * System.Math.Cos(angle);
cx = x;
cz = z;
}
void RotZ(ref double cy, ref double cx, double angle)
{
double x = cx * System.Math.Cos(angle) - cy * System.Math.Sin(angle);
double y = cx * System.Math.Sin(angle) + cy * System.Math.Cos(angle);
cx = x;
cy = y;
}
protected double stFunction(double x, double y)
{
return (@EXPRESSION);
}
public void SaveFile(string filepath, string outputstr)
{
System.IO.FileStream file = new System.IO.FileStream
(filepath, System.IO.FileMode.Create, System.IO.FileAccess.Write);
System.IO.StreamWriter writer = new System.IO.StreamWriter(file);
writer.Write(outputstr);
writer.Flush();
writer.Close();
file.Close();
}
private void Page_Load(object sender, System.EventArgs e)
{
System.Globalization.CultureInfo customCulture =
(System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.
CurrentCulture.Clone();
customCulture.NumberFormat.NumberDecimalSeparator = ".";
System.Threading.Thread.CurrentThread.CurrentCulture = customCulture;
int screenSize = Session["screenSize"] !=null ?(int) Session["screenSize"] :200;
double xleft = Session["xleft"] !=null ?(double) Session["xleft"] :-2;
double xright = Session["xright"] !=null ?(double) Session["xright"] :2;
double yleft = Session["yleft"] !=null ?(double) Session["yleft"] :-2;
double yright = Session["yright"] !=null ?(double) Session["yright"] :2;
double step = Session["step"] !=null ?(double) Session["step"] :0.05;
if (Math.Abs(step) < 1.0E-5)
{
Response.Write("Integration is 0");
return;
}
int maxcount = 1000000;
int sizeX = (int)Math.Ceiling(((xright - xleft) / step)) + 1;
int sizeY = (int)Math.Ceiling(((yright - yleft) / step)) + 1;
int size = sizeX * sizeY;
if (size > maxcount)
{
Response.Write("Overflow");
return;
}
double[,] surface = new double[sizeX, sizeY];
double x = xleft;
double y;
int indexX = 0;
int indexY = 0;
double zmin=+1.0E10, zmax=-1.0E10;
while (x <= xright)
{
y = yleft;
indexY = 0;
while (y <= yright)
{
surface[indexX, indexY] = stFunction(x, y);
if (zmin > surface[indexX, indexY]) zmin = surface[indexX, indexY];
if (zmax < surface[indexX, indexY]) zmax = surface[indexX, indexY];
indexY++;
y += step;
}
x += step;
indexX++;
}
if (Math.Abs(zmin)<1.0E-5) zmin = 0;
if (Math.Abs(zmax)<1.0E-5) zmax = 0;
Session["surface"] = surface;
Bitmap newBitmap = new Bitmap(screenSize, screenSize, PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(newBitmap);
Pen blackPen = new Pen(Color.Black);
Color clrBackground = Color.Black;
g.FillRectangle(new SolidBrush(clrBackground),
new Rectangle(0, 0, screenSize, screenSize));
Rectangle rect = new Rectangle(0, 0, screenSize, screenSize);
double xmin = Session["xleft"] !=null ?(double)Session["xleft"]:-2;
double ymin = Session["yleft"] !=null ?(double)Session["yleft"]:-2;
double xmax = Session["xright"]!=null ?(double)Session["xright"]:2;
double ymax = Session["yright"]!=null ?(double)Session["yright"]:2;
double eyex = Session["eyex"] !=null ?(double)Session["eyex"]:0;
double eyey = Session["eyey"] !=null ?(double)Session["eyey"]:0;
double eyez = Session["eyez"] !=null ?(double)Session["eyez"]:100;
int idx = 0;
x = xmin;
while ((x < xmax) && (idx < sizeX - 1))
{
y = ymin;
int idy = 0;
while ((y < ymax) && (idy < sizeY - 2) && (idx < sizeX - 1))
{
double z1 = surface[idx, idy];
double z2 = surface[idx + 1, idy];
double z3 = surface[idx + 1, idy + 1];
double z4 = surface[idx, idy + 1];
double xx1 = x, yy1 = y, zz1 = z1;
double xx2 = x + step, yy2 = y, zz2 = z2;
double xx3 = x + step, yy3 = y + step, zz3 = z3;
double xx4 = x, yy4 = y + step, zz4 = z4;
RotX(ref yy1, ref zz1, eyex);
RotY(ref xx1, ref zz1, eyey);
RotZ(ref xx1, ref yy1, eyez);
RotX(ref yy2, ref zz2, eyex);
RotY(ref xx2, ref zz2, eyey);
RotZ(ref xx2, ref yy2, eyez);
RotX(ref yy3, ref zz3, eyex);
RotY(ref xx3, ref zz3, eyey);
RotZ(ref xx3, ref yy3, eyez);
RotX(ref yy4, ref zz4, eyex);
RotY(ref xx4, ref zz4, eyey);
RotZ(ref xx4, ref yy4, eyez);
double p1x = ((xx1 - xmin) / (xmax - xmin)) * (screenSize - 0.0) + 0.0;
double p1y = ((yy1 - ymin) / (ymax - ymin)) * (screenSize - 0.0) + 0.0;
double p2x = ((xx2 - xmin) / (xmax - xmin)) * (screenSize - 0.0) + 0.0;
double p2y = ((yy2 - ymin) / (ymax - ymin)) * (screenSize - 0.0) + 0.0;
double p3x = ((xx3 - xmin) / (xmax - xmin)) * (screenSize - 0.0) + 0.0;
double p3y = ((yy3 - ymin) / (ymax - ymin)) * (screenSize - 0.0) + 0.0;
double p4x = ((xx4 - xmin) / (xmax - xmin)) * (screenSize - 0.0) + 0.0;
double p4y = ((yy4 - ymin) / (ymax - ymin)) * (screenSize - 0.0) + 0.0;
int ip1x = (int)Math.Ceiling(p1x);
int ip1y = (int)Math.Ceiling(p1y);
double colorFactorX = (xx1-xmin)/(xmax-xmin);
int ix1 = (int)Math.Abs((255.0 * colorFactorX));
if (ix1 < 0) ix1 = 0;
if (ix1 > 255) ix1 = 255;
double colorFactorY = (yy1-ymin)/(ymax-ymin);
int iy1 = (int)Math.Abs((255.0 * colorFactorY));
if (iy1 < 0) iy1 = 0;
if (iy1 > 255) iy1 = 255;
double colorFactorZ = (zz1-zmin)/(zmax-zmin);
int iz1 = (int)Math.Abs((255.0 * colorFactorZ));
if (iz1 < 0) iz1 = 0;
if (iz1 > 255) iz1 = 255;
int ip2x = (int)Math.Ceiling(p2x);
int ip2y = (int)Math.Ceiling(p2y);
int ip3x = (int)Math.Ceiling(p3x);
int ip3y = (int)Math.Ceiling(p3y);
int ip4x = (int)Math.Ceiling(p4x);
int ip4y = (int)Math.Ceiling(p4y);
int view = Session["view"]!=null?(int)Session["view"]:1;
if (view == 1)
{
Pen thePen = new Pen(Color.FromArgb(ix1, iy1, iz1));
g.DrawLine(thePen, ip1x, ip1y, ip2x, ip2y);
g.DrawLine(thePen, ip1x, ip1y, ip4x, ip4y);
g.DrawLine(thePen, ip2x, ip2y, ip4x, ip4y);
g.DrawLine(thePen, ip2x, ip2y, ip4x, ip4y);
g.DrawLine(thePen, ip2x, ip2y, ip3x, ip3y);
g.DrawLine(thePen, ip3x, ip3y, ip4x, ip4y);
}
else
if (view == 2)
{
Point[] points = new Point[3];
points[0].X = ip1x;
points[0].Y = ip1y;
points[1].X = ip2x;
points[1].Y = ip2y;
points[2].X = ip4x;
points[2].Y = ip4y;
Brush theBrush = new SolidBrush(Color.FromArgb(ix1, iy1, iz1));
g.FillPolygon(theBrush, points);
points = new Point[3];
points[0].X = ip2x;
points[0].Y = ip2y;
points[1].X = ip4x;
points[1].Y = ip4y;
points[2].X = ip3x;
points[2].Y = ip3y;
g.FillPolygon(theBrush, points);
}
idy++;
y += step;
}
x += step;
idx++;
}
MemoryStream tempStream = new MemoryStream();
newBitmap.Save(tempStream, ImageFormat.Png);
Response.ClearContent();
Response.ContentType = "image/png";
Response.BinaryWrite(tempStream.ToArray());
Response.Flush();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>Dynafunc</title>
</head>
<body style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px;
padding-top: 0px; text-align: center;">
<form id="form1" runat="server">
</form>
</body>
</html>
The Main File
<%@ Page Language="c#" Debug="true" Explicit="True" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Drawing" %>
<%@ Import Namespace="System.Drawing.Imaging" %>
<%@ Import Namespace="System.Globalization" %>
<script language="C#" runat="server">
public string ReadFile(string filepath)
{
System.IO.FileStream file = new System.IO.FileStream
(filepath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
System.IO.StreamReader reader = new System.IO.StreamReader(file);
string contents = reader.ReadToEnd();
reader.Close();
file.Close();
return contents;
}
public void SaveFile(string filepath, string outputstr)
{
System.IO.FileStream file = new System.IO.FileStream
(filepath, System.IO.FileMode.Create, System.IO.FileAccess.Write);
System.IO.StreamWriter writer = new System.IO.StreamWriter(file);
writer.Write(outputstr);
writer.Flush();
writer.Close();
file.Close();
}
private void Page_Load(object sender, System.EventArgs e)
{
System.Globalization.CultureInfo customCulture =
(System.Globalization.CultureInfo)System.Threading.Thread.
CurrentThread.CurrentCulture.Clone();
customCulture.NumberFormat.NumberDecimalSeparator = ".";
System.Threading.Thread.CurrentThread.CurrentCulture = customCulture;
outputTxt.Text = "";
String functionStr = function.Text;
functionStr = functionStr.Replace("sin", "Math.Sin");
functionStr = functionStr.Replace("cos", "Math.Cos");
functionStr = functionStr.Replace("cos", "Math.Cos");
functionStr = functionStr.Replace("sqrt", "Math.Sqrt");
functionStr = functionStr.Replace("sinh", "Math.Sinh");
functionStr = functionStr.Replace("acos", "Math.Acos");
functionStr = functionStr.Replace("cosh", "Math.Cosh");
functionStr = functionStr.Replace("abs", "Math.Abs");
functionStr = functionStr.Replace("asin", "Math.Asin");
functionStr = functionStr.Replace("ceiling", "Math.Ceiling");
functionStr = functionStr.Replace("exp", "Math.Exp");
functionStr = functionStr.Replace("floor", "Math.Floor");
functionStr = functionStr.Replace("log", "Math.Log");
functionStr = functionStr.Replace("log10", "Math.Log10");
functionStr = functionStr.Replace("pi", "Math.PI");
functionStr = functionStr.Replace("pow", "Math.Pow");
functionStr = functionStr.Replace("tan", "Math.Tan");
functionStr = functionStr.Replace("tanh", "Math.Tanh");
functionStr = functionStr.Replace("round", "Math.Round");
string contents = ReadFile(Server.MapPath("./templates/template.txt"));
contents = contents.Replace("@EXPRESSION", functionStr);
SaveFile(Server.MapPath("./images/dynacontent.aspx"), contents);
int iscreenSize = int.Parse(screenSize.Text);
double xleft = double.Parse(xmin.Text);
double xright = double.Parse(xmax.Text);
double yleft = double.Parse(ymin.Text);
double yright = double.Parse(ymax.Text);
double step = double.Parse(integ.Text);
if (Math.Abs(step) < 1.0E-5)
{
Response.Write("Integration is 0");
return;
}
int maxcount = 1000000;
int sizeX = (int)Math.Ceiling(((xright - xleft) / step)) + 1;
int sizeY = (int)Math.Ceiling(((yright - yleft) / step)) + 1;
int size = sizeX * sizeY;
outputTxt.Text += "Grid size = " + size.ToString() + " points";
if (size > maxcount)
{
Response.Write("Overflow");
return;
}
outputTxt.Text += "<br>[" + sizeX.ToString() +
"," + sizeY.ToString() + "]";
Session["screenSize"] = iscreenSize;
Session["xleft"] = xleft;
Session["yleft"] = yleft;
Session["xright"] = xright;
Session["yright"] = yright;
Session["step"] = step;
if (view1.Checked)
Session["view"] = (int)1;
if (view2.Checked)
Session["view"] = (int)2;
Session["sizex"] = sizeX;
Session["sizey"] = sizeY;
Session["eyex"] = double.Parse(eyex.Text);
Session["eyey"] = double.Parse(eyey.Text);
Session["eyez"] = double.Parse(eyez.Text);
}
</script>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Dynafunc</title>
</head>
<body style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px;
margin: 0px; padding-top: 0px; text-align: center;">
<form id="form1" runat="server">
<h1>R2 in R dynamically generated function</h1>
<asp:Panel ID="PlotPanel" runat="server" Font-Names="Verdana"
Font-Size="12px" Height="100%"
Style="margin-left: auto; margin-right: auto"
Width="90%" ForeColor="DarkSlateGray"
ScrollBars="Vertical" BorderColor="Gray"
BorderStyle="Solid" BorderWidth="1px">
<table border="0" cellpadding="0"
cellspacing="0" width="100%" id="TABLE1">
<tr>
<td>
<table>
<tr>
<td style="width: 20%; height: 18px;"> </td>
<td style="width: 1px; height: 18px;"></td>
<td style="width: 20%; height: 18px;"> </td>
<td rowspan="1" style="width: 30%; height: 18px;"
valign="top"> </td>
</tr>
<tr>
<td style="width: 20%; height: 1px;">Function</td>
<td style="width: 1px; height: 26px;"></td>
<td style="width: 100px; height: 26px;" align="left">
<asp:TextBox ID="function" runat="server"
Width="300px" Height="16px" Font-Names="Verdana"
Font-Size="12px" ForeColor="DarkGreen">
sin(pow(x,3)+x*y-pow(y,3))</asp:TextBox>
</td>
</tr>
<tr>
<td style="width: 20%; height: 1px;">Domain</td>
<td style="width: 1px; height: 1px;"></td>
<td style="width: 20%;" align="left">
<asp:TextBox ID="xmin" runat="server"
Width="32px" Font-Names="Verdana" Font-Size="12px"
ForeColor="DarkGreen">-2</asp:TextBox>
<x<<asp:TextBox ID="xmax" runat="server"
Width="32px" Font-Names="Verdana"
Font-Size="12px" ForeColor="DarkGreen">2
</asp:TextBox>
<asp:TextBox ID="ymin" runat="server"
Width="32px" Font-Names="Verdana" Font-Size="12px"
ForeColor="DarkGreen">-2</asp:TextBox>
<y<<asp:TextBox ID="ymax" runat="server"
Width="32px" Font-Names="Verdana"
Font-Size="12px" ForeColor="DarkGreen">2
</asp:TextBox></td>
</tr>
<tr>
<td style="width: 20%; height: 1px;">Integration</td>
<td style="width: 1px; height: 16px;"></td>
<td style="width: 20%;" align="left">
<asp:TextBox ID="integ" runat="server"
Width="40px" Font-Names="Verdana" Font-Size="12px"
ForeColor="DarkGreen">0.05</asp:TextBox></td>
</tr>
<tr>
<td style="width: 20%; height: 1px;">Image Size</td>
<td style="width: 1px; height: 16px;"></td>
<td style="width: 20%;" align="left">
<asp:TextBox ID="screenSize" runat="server"
Width="32px" Font-Names="Verdana" Font-Size="12px"
ForeColor="DarkGreen">200</asp:TextBox>px</td>
</tr>
<tr>
<td style="width: 20%; height: 1px">View</td>
<td style="width: 1px; height: 16px"></td>
<td style="width: 20%; height: 16px" align="left">
<asp:RadioButton ID="view1" runat="server"
Text="Wireframe" Checked="True" GroupName="view" />
<asp:RadioButton ID="view2" runat="server"
Text="Fill" GroupName="view" />
</td>
</tr>
<tr>
<td style="width: 20%; height: 1px">Rotation (x,y,z)</td>
<td style="width: 1px; height: 16px"></td>
<td style="width: 20%; height: 16px" align="left">
<asp:TextBox ID="eyex" runat="server" Width="40px"
Font-Names="Verdana" Font-Size="12px"
ForeColor="DarkGreen">0.0</asp:TextBox>
<asp:TextBox ID="eyey" runat="server"
Width="40px" Font-Names="Verdana" Font-Size="12px"
ForeColor="DarkGreen">0.0</asp:TextBox>
<asp:TextBox ID="eyez" runat="server"
Width="40px" Font-Names="Verdana" Font-Size="12px"
ForeColor="DarkGreen">0.0</asp:TextBox>
rad</td>
</tr>
<tr>
<td style="width: 20%; height: 1px;"></td>
<td style="width: 1px"></td>
<td style="width: 20%" align="left">
<asp:Button ID="PlotBtn" runat="server"
Font-Names="Arial" Text="Plot" BackColor="White" />
</td>
</tr>
<tr>
<td style="width: 20%; height: 1px"></td>
<td style="width: 1px"></td>
<td align="left" style="width: 20%">
<asp:Label ID="outputTxt" runat="server"
Font-Names="Arial" Font-Size="X-Small"
ForeColor="MidnightBlue"
Height="28px" Text="Label" Width="100%">
</asp:Label></td>
</tr>
</table>
</td>
<td>
<table>
<tr>
<td style="height: 1px; text-align: center;"
colspan="3">
<asp:Image ID="imageFunction" runat="server"
ImageUrl="./images/dynacontent.aspx" /></td>
<td rowspan="8" style="width: 10%;
height: 1px;" valign="top"></td>
</tr>
</table>
</td>
</tr>
</table>
</asp:Panel>
</form>
</body>
</html>
Example of generated function
sin(pow(x,3)-7*x*y-pow(y,4))