|
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Text;
using System.Windows.Forms;
using Meta.Numerics;
using Meta.Numerics.Functions;
namespace ComplexExplorer {
public partial class ComplexExplorerForm : Form {
public ComplexExplorerForm () {
InitializeComponent();
}
// create an image based on the current complex function
public void DrawImage () {
// get the function to evaluate
Function<Complex,Complex> f = functions[functionList.SelectedIndex].Function;
Bitmap image = new Bitmap(imageBox.Width, imageBox.Height);
// iterate over all image pixels
for (int x = 0; x < imageBox.Width; x++) {
double re = re_min + x * (re_max - re_min) / imageBox.Width;
for (int y = 0; y < imageBox.Height; y++) {
double im = im_max - y * (im_max - im_min) / imageBox.Height;
// form a complex number based on the pixel value
Complex z = new Complex(re, im);
// compute the value of the current complex function for that complex number
Complex fz = f(z);
// don't try to plot non-numeric values (e.g. at poles)
if (Double.IsInfinity(fz.Re) || Double.IsNaN(fz.Re) || Double.IsInfinity(fz.Im) || Double.IsNaN(fz.Im)) continue;
// convert the complex function value to a HSV color triplet
ColorTriplet hsv = ColorMap.ComplexToHsv(fz);
// convert the HSV color triplet to an RBG color triplet
ColorTriplet rgb = ColorMap.HsvToRgb(hsv);
int r = (int) Math.Truncate(255.0 * rgb.X);
int g = (int) Math.Truncate(255.0 * rgb.Y);
int b = (int) Math.Truncate(255.0 * rgb.Z);
Color color = Color.FromArgb(r, g, b);
// plot the point
image.SetPixel(x, y, color);
}
}
// put the image in the image box control
imageBox.Image = image;
}
// bounds in the complex plane
private double re_min = -3.0;
private double re_max = +3.0;
private double im_min = -3.0;
private double im_max = +3.0;
// the list of complex functions available
private List<ComplexFunction> functions = new List<ComplexFunction>();
// when a new function is selected, re-draw image
private void functionList_SelectedIndexChanged (object sender, EventArgs e) {
valueText.Text = String.Empty;
DrawImage();
}
// when the image is clicked on, display the function value
private void imageBox_MouseClick (object sender, MouseEventArgs e) {
if (functionList.SelectedIndex < 0) return;
double re = re_min + e.X * (re_max - re_min) / imageBox.Width;
double im = im_max - e.Y * (im_max - im_min) / imageBox.Height;
Complex z = new Complex(re, im);
Complex fz = functions[functionList.SelectedIndex].Function(z);
valueText.Text = String.Format(CultureInfo.CurrentCulture, "f({0}) = {1}", FormatComplex(z), FormatComplex(fz));
}
// formatting a complex number
private static string FormatComplex (Complex z) {
StringBuilder text = new StringBuilder();
double re = z.Re;
double im = z.Im;
if ((re != 0.0) || (im == 0.0)) {
text.AppendFormat(CultureInfo.CurrentCulture, "{0:g4}", re);
}
if (im != 0.0) {
if (im < 0.0) {
text.Append(CultureInfo.CurrentCulture.NumberFormat.NegativeSign);
im = -im;
} else {
if (re != 0.0) text.Append(CultureInfo.CurrentCulture.NumberFormat.PositiveSign);
}
text.AppendFormat(CultureInfo.CurrentCulture, "{0:g4}", im);
text.Append("i");
}
return (text.ToString());
}
// save file
private void saveButton_Click (object sender, EventArgs e) {
SaveFileDialog dialog = new SaveFileDialog();
dialog.Filter = "Portable Network Graphics (*.png)|*.png";
dialog.RestoreDirectory = true;
if (dialog.ShowDialog() == DialogResult.OK) {
using (Stream writer = dialog.OpenFile()) {
if (writer == null) return;
imageBox.Image.Save(writer, System.Drawing.Imaging.ImageFormat.Png);
}
}
}
// setup behavior
private void ComplexExplorerForm_Load (object sender, EventArgs e) {
// load functions
functions.AddRange(new ComplexFunction[] {
new ComplexFunction() {
Label = "z",
Function = delegate(Complex z) { return (z); }
},
new ComplexFunction() {
Label = "z^2 - 1",
Function = delegate(Complex z) { return ((z-1.0) * (z+ 1.0)); }
},
new ComplexFunction() {
Label = "z^3 + 1",
Function = delegate(Complex z) { return (z*z*z + 1.0); }
},
new ComplexFunction() {
Label = "1/z",
Function = delegate(Complex z) { return(1.0/z); }
},
// Claudio Rocchini's example function from http://en.wikipedia.org/wiki/File:Color_complex_plot.jpg
new ComplexFunction() {
Label = "(z^2-1)(z-2-i)^2/(z^2+2+2i)",
Function = delegate(Complex z) {
Complex zs = z - 2.0 - ComplexMath.I;
return((z*z-1.0)*zs*zs/(z*z+2.0+2.0*ComplexMath.I));
}
},
new ComplexFunction() {
Label = "sqrt(z)",
Function = ComplexMath.Sqrt
},
new ComplexFunction() {
Label = "exp(z)",
Function = ComplexMath.Exp
}, new ComplexFunction() {
Label = "ln(z)",
Function = ComplexMath.Log
},
new ComplexFunction() {
Label = "Gamma(z)",
Function = AdvancedComplexMath.Gamma
},
new ComplexFunction() {
Label = "Psi(z)",
Function = AdvancedComplexMath.Psi
},
new ComplexFunction() {
Label = "Faddeeva(z)",
Function = AdvancedComplexMath.Faddeeva
}
});
// bind functions to function list
foreach (ComplexFunction function in functions) {
functionList.Items.Add(function.Label);
}
// select first function
functionList.SelectedIndex = 0;
}
// call up web site when footer is clicked
private void footerLabel_LinkClicked (object sender, LinkLabelLinkClickedEventArgs e) {
Process.Start("http://www.meta-numerics.net");
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
I am a .NET developer who works daily on enterprise-scale applications using C#, SQL, XML, ASP.NET, and myriad other technologies. My academic background is in physics and economics.
I am the original architect of
Sandcastle managed reference documentation engine and of the
Meta.Numerics library for scientific computation.