使用 WebMatrix 制作的 ASP.NET Web Pages 网站
输入一个函数字符串和一个范围,该网站会将其转换为 f(x,y) 图。
我曾在 20 世纪 80 年代学习计算机科学,但从未在这个领域找到职业。但在大学时,我知道我喜欢计算机图形学和编译器构造。
引言
这个 ASP.NET Web Pages 网站允许您绘制 3D 中自定义的 x 和 y 函数。这个项目面向即将发现或已经发现了 WebMatrix 的新手编程爱好者。MVC 并不容易,它适用于专业程序员,但 Web Pages 却对用户友好,并且使用起来很有趣。当您了解 Razor 的基础知识以及如何执行某些操作(例如如何让 cshtml 文件生成图像或如何在文件之间传递数据)时,WebMatrix 的可能性是无限的。
背景
2006 年,我开始使用新的 Visual Studio C# Express Edition 编写 C# 代码,并编写了一个程序来绘制 3D 图形。几年后,我编写了一个 Silverlight 应用程序,绘制了 x 的一个函数。当 WebMatrix 首次发布时,我想到我可以创建一个使用 Razor 语法的 Web Pages 网站,该语法使用了我之前编写的项目的代码。所以我开始创建一个网站,访问者可以在其中输入包含 x 和 y 函数公式的文本字符串以及一个范围,然后我的网站将生成包含 3D 函数的图像。当我第一次开始使用 WebMatrix Web Pages 时,我感觉就像一个在糖果店里的孩子,而且现在仍然如此!
CoCo\R 编译器编译器
为了生成扫描器和解析器,我使用了 CoCo\R。这是一个程序,您向其提供 ATG 语言定义文件,它将其转换为 C# 源代码。这比我预期的要容易,从文本字符串创建基于堆栈的计算器的过程实际上非常简单。
以下是我 ATG 文件中的几行代码
COMPILER FunctionCalc
CHARACTERS
letter = 'A'..'Z' + 'a'..'z'.
digit = '0'..'9'.
TOKENS
VARIABLEX = 'x' | 'X'.
VARIABLEY = 'y' | 'Y'.
NUMBER = digit { digit } [ "." digit { digit } ].
SIN = "sin" | "Sin" | "SIN".
COS = "cos" | "Cos" | "COS".
和
PRODUCTIONS FunctionCalc = Expression (. assemble.AddMnemonic(Mnemonic.Actions.end, 0.0); .) . Expression = Term { '+' Term (. assemble.AddMnemonic(Mnemonic.Actions.add, 0.0); .) | '-' Term (. assemble.AddMnemonic(Mnemonic.Actions.subtract, 0.0); .) } .
我将把 ATG 文件添加到源代码中。
三个组件。
有三个组件,1) 绘制 3D 对象的源,2) 将文本字符串转换为一系列指令以计算输入的 x 和 y 函数的代码。而第 3) 部分是实际的网站。
绘制 3D 图形。
我读了很多关于 C# 的书,但我没有看到任何示例代码,所以我以我认为最好的方式编写了这个模块。
public class Storage
{
public double CenterX = 295.0;
public double CenterY = 295.0;
public int NumberOfVectors = 0;
public int NumberOfVertices = 0;
public int NumberOfFaces = 0;
public int MAXNumberOfVectors = 0;
public int MAXNumberOfVertices = 0;
public int MAXNumberOfFaces = 0;
public Vector[] StoreVectors;
public int[] StoreVerticesFrom;
public int[] StoreVerticesTo;
public Point2D[] StorePoints;
public double[] StoreDistanceToEyePerPoint;
public Face[] StoreFaces;
public double[] StoreFaceDistances;
public double[] StoreSortedFacesValue;
public int[] StoreSortedFacesKey;
我为矩阵和向量的乘法创建了运算符,它们都是我源代码中的类类型。
public class myMatrix
{
public double a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41, a42, a43, a44;
和
public class Vector
{
public double v1, v2, v3, v4;
public Vector(double v1, double v2, double v3, double v4)
{
this.v1 = v1;
this.v2 = v2;
this.v3 = v3;
this.v4 = v4;
}
计算和绘制函数
public class myImage
{
public static myMatrix P;
public static double sizeObject = 100.0;
public static Vector Eye = new Vector(120, 135, 175, 1.0);
public static string fstring = "cos(x)*sin(y)*pi";
public static int raster = 20;
public static double xmin = -3.14;
public static double xmax = 3.14;
public myImage(Graphics g, Vector vantagepoint, String fxystring,
double range, int resolution, double scale, Function.SolidType st)
{
Eye = vantagepoint;
fstring = fxystring;
xmin = -range;
xmax = +range;
sizeObject = scale;
raster = resolution;
string errorstring = "";
g.FillRectangle(Brushes.Black, new Rectangle(0, 0, 590, 590));
Execute ex = new Execute(10000, 5000, fstring);
if (!ex.parsingerror)
{
P = new myMatrix(Eye);
Function F = new Function(ex, raster, xmin, xmax, sizeObject, Eye, out errorstring);
if (errorstring == "") { F.DrawSolid(P, g, Color.Blue, st); }
else g.DrawString("Computing Error:" + '\n' + errorstring, new Font("Times New Roman", 30), Brushes.Red, 20, 20);
}
else g.DrawString("Parsing Error" + '\n' + "check formula", new Font("Times New Roman", 30), Brushes.Red, 20, 20);
}
}
翻译器。
通过稍微更改 CoCo\R 生成的代码,就可以相对容易地创建一个 C# 模块,将包含函数的字符串翻译成一系列指令以计算函数。我编写了几个类来生成程序以处理基于堆栈的计算器的计算。灵感来自于我第一次拥有 Commodore 64 计算机时读到的一本书。
public class Mnemonic
{
public enum Actions
{
sin, cos, tan, add, subtract, multiply, divide, negate, number, variablex,
variabley, e, pi, abs, acos, asin, atan, cosh, exp, log, sinh, sqrt,
tanh, pow, end
}
public Actions action;
public double argument;
public Mnemonic(Actions action, double argument)
{
this.action = action;
this.argument = argument;
}
public void AddMnemonic(Actions action, double argument)
{
this.action = action;
this.argument = argument;
}
}
然后我编写了几个方法来执行翻译器生成的程序。
public double FunctionExecute(double x, double y, out string errorString)
{
int programcounter = 0;
Mnemonic m = assemble.GetMnemonic(programcounter);
errorString = "";
double Loperand, Roperand, Result;
while (m.action != Mnemonic.Actions.end)
{
try
{
switch (m.action)
{
case Mnemonic.Actions.sin: Loperand = stack.Pop(); Result = Math.Sin(Loperand); TestResult(Result, "Sin"); stack.Push(Result); break;
case Mnemonic.Actions.cos: Loperand = stack.Pop(); Result = Math.Cos(Loperand); TestResult(Result, "Cos"); stack.Push(Result); break;
case Mnemonic.Actions.tan: Loperand = stack.Pop(); Result = Math.Tan(Loperand); TestResult(Result, "Tan"); stack.Push(Result); break;
case Mnemonic.Actions.add: Roperand = stack.Pop(); Loperand = stack.Pop(); Result = Loperand + Roperand; ; TestResult(Result, "Add"); stack.Push(Result); break;
case Mnemonic.Actions.subtract: Roperand = stack.Pop(); Loperand = stack.Pop(); Result = Loperand - Roperand; TestResult(Result, "Subtract"); stack.Push(Result); break;
case Mnemonic.Actions.multiply: Roperand = stack.Pop(); Loperand = stack.Pop(); Result = Loperand * Roperand; TestResult(Result, "Multiply"); stack.Push(Result); break;
case Mnemonic.Actions.divide: Roperand = stack.Pop(); Loperand = stack.Pop(); Result = Loperand / Roperand; TestResult(Result, "Divide"); stack.Push(Result); break;
case Mnemonic.Actions.negate: Loperand = stack.Pop(); Result = -Loperand; TestResult(Result, "Negate"); stack.Push(Result); break;
case Mnemonic.Actions.variablex: stack.Push(x); break;
case Mnemonic.Actions.variabley: stack.Push(y); break;
case Mnemonic.Actions.number: stack.Push(m.argument); break;
case Mnemonic.Actions.e: stack.Push(Math.E); break;
case Mnemonic.Actions.pi: stack.Push(Math.PI); break;
case Mnemonic.Actions.abs: Loperand = stack.Pop(); Result = Math.Abs(Loperand); TestResult(Result, "Abs"); stack.Push(Result); break;
case Mnemonic.Actions.acos: Loperand = stack.Pop(); Result = Math.Acos(Loperand); TestResult(Result, "Acos"); stack.Push(Result); break;
case Mnemonic.Actions.asin: Loperand = stack.Pop(); Result = Math.Asin(Loperand); TestResult(Result, "Asin"); stack.Push(Result); break;
case Mnemonic.Actions.atan: Loperand = stack.Pop(); Result = Math.Atan(Loperand); TestResult(Result, "Atan"); stack.Push(Result); break;
case Mnemonic.Actions.cosh: Loperand = stack.Pop(); Result = Math.Cosh(Loperand); TestResult(Result, "Cosh"); stack.Push(Result); break;
case Mnemonic.Actions.exp: Loperand = stack.Pop(); Result = Math.Exp(Loperand); TestResult(Result, "Exp"); stack.Push(Result); break;
case Mnemonic.Actions.log: Loperand = stack.Pop(); Result = Math.Log(Loperand); TestResult(Result, "Log"); stack.Push(Result); break;
case Mnemonic.Actions.sinh: Loperand = stack.Pop(); Result = Math.Sinh(Loperand); TestResult(Result, "Sinh"); stack.Push(Result); break;
case Mnemonic.Actions.sqrt: Loperand = stack.Pop(); Result = Math.Sqrt(Loperand); TestResult(Result, "Sqrt"); stack.Push(Result); break;
case Mnemonic.Actions.tanh: Loperand = stack.Pop(); Result = Math.Tanh(Loperand); TestResult(Result, "Tanh"); stack.Push(Result); break;
case Mnemonic.Actions.pow: Roperand = stack.Pop(); Loperand = stack.Pop(); Result = Math.Pow(Loperand, Roperand); stack.Push(Result); break;
default: throw new Exception("default in Function Execute");
}
}
catch (ArithmeticException aex)
{
errorString = aex.Message;
return (0.0);
}
catch (ArgumentOutOfRangeException aor)
{
errorString = aor.Message;
return (0.0);
}
m = assemble.GetMnemonic(++programcounter);
if (programcounter > _memorySize)
{
throw new Exception("Program Counter out of bounds");
}
}
return (stack.Pop());
}
网站。
我从互联网上免费下载了网站的模板,网址:http://www.freecsstemplates.org/。它很漂亮,并且很容易集成到网站中。此模板就像一个框架,首先您拥有下载的模板网站和示例内容,然后您只需用您自己的内容替换示例内容即可。下一个代码片段是 Image.cshtml 文件,它生成包含 3D 函数的图形的 jpg 文件。
@using System.Drawing.Drawing2D;
@using System.Drawing.Imaging;
@using System.Globalization;
@using _3DFxy1_110909;
@{
Bitmap bitmap = new Bitmap(600, 600);
Graphics g = Graphics.FromImage(bitmap);
Vector vantagepoint = new Vector((double)AppState["EyeX"], (double)AppState["EyeY"], (double)AppState["EyeZ"], 1.0);
String fxystring = AppState["fstring"].ToString();
double range = (double)AppState["range"];
int raster = (int)AppState["raster"];
double scale = (double)AppState["scale"];
Function.SolidType st = Function.SolidType.Intelligent;
string ststr = AppState["solidtype"].ToString();
if (ststr == "White") { st = Function.SolidType.White; }
if (ststr == "Random") { st = Function.SolidType.Random; }
if (ststr == "Intelligent") { st = Function.SolidType.Intelligent; }
myImage img = new myImage(g,vantagepoint,fxystring,range,raster,scale, st);
Response.ContentType = "image/jpeg";
Response.AddHeader("content-disposition", "inline; filename=test.jpg");
bitmap.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
g.Dispose();
bitmap.Dispose();
}
从 _AppStart.cshtml,这些是在应用程序启动时加载到变量中的默认设置
@{
AppState["EyeX"] = 120.0;
AppState["EyeY"] = 135.0;
AppState["EyeZ"] = 175.0;
AppState["vp"] = 1;
AppState["fstring"] = "cos(x)*sin(y) * pi";
AppState["range"] = 3.14;
AppState["raster"] = 20;
AppState["scale"] = 100.0;
AppState["solidtype"] = "Intelligent";
}
在 Default.cshtml 中,您可以通过网站输入变量
@if (IsPost) {
{AppState["fstring"] = Request["finput"].ToString();}
try {AppState["range"] = Math.Abs(double.Parse(Request["domain"]));}
catch {AppState["range"] = 3.14; <h3>You entered an illegal value for Domain, will enter the default value.</h3>}
try{AppState["scale"] = Math.Abs(double.Parse(Request["scale"]));}
catch { AppState["scale"] = 100.0; <h3>You entered an illegal value for Scale, will enter the default value.</h3>}
{AppState["raster"] = int.Parse(Request["resolution"]);}
{AppState["solidtype"] = Request["solidtype"].ToString();}
{AppState["vp"] = int.Parse(Request["vantagepoint"]);}
}
接下来是这行代码,它从上面列出的 Image.cshtml 文件插入一个 jpg 图像
<img src="@Href("~/Image.cshtml")" alt="Fxy" />
这里有两个截图,一个带有输入,另一个带有结果
有三种渲染模式,这种模式(智能)生成一个像温度图一样的图像。
关注点
我非常喜欢使用 WebMatrix 制作这个网站。 使用 Razor 语法的 ASP.NET Web Pages 使得构建具有您想要的功能和外观的网站变得非常容易。 很容易理解。 并且很有趣!
历史
这是我为 Code Project 网站撰写的第一篇文章。