MRoots
Here is a demonstration of MRoots:I know, it needs some work... You must have Silverlight 4 installed to see the MRoots Applet below. Silverlight is (in my opinion) a more secure/versatile alternative to Adobe Flash.
I need to upgrade this to SVG.js when I get the chance...
Here's a "pretty" listing of the code. I'll probably add a raw file link later:
(*
"And whatever you do, whether in word or deed,
do it all in the name of the Lord Jesus, giving thanks to
God the Father through him."
Collossians 3:17 NIV
!!! Warning !!! This document is currently in a draft status.
it might change a bit. If you actually do cite it,
be sure and let everyone know that you are citing
a draft document.
Title : M Roots Demo
Filename : MSRootsDemo.fs
Author : Shawn Eary
Date : 01-FEB-2011
Revision : dft-0.01 (This is seriously alpha...
I'm a little cramped for time right now...)
Copyright : 2011
License : Free Christian Document License (FCDL)
http://www.conservativeArt.net/fcdl.aspx
Warranty : None (See FCDL Terms)
Purpose : To amuze the reader (Also see spec)
Notes:
The program is covered under the FCDL (see above) as such it
is treated more as a paper than a program. Do not confuse
the FCDL License with other "Open Source" Licenses as this
license is very different. The FCDL is a "good faith"
licence that basically gives you the
right to:
a) Make as many copies of covered documents that you want as
long as the entire covered documents including spelling
errors, biblical references, citations and author credits
are kept intact.
b) Use portions of this document to create your own
religiously neutral or Christian products provided you
properly cite this document.
(Overall URL of document and Author at tail or beginning
of work with (filename:functionname) citations inline)
c) Charge for the distribution of covered documents or
for products that were created by using portions of covered
documents with no obligation to reimburse the authors of
the covered documents.
Resources
Other Notes:
In order to compile this, you will need Mr. Danson's Silverlight
Application Template [1] (See Below)
You cram all of this file into the MainPage.fs file that his template
creates. You should then be able to compile this applet
(which is still in a serious development stage and may have
significant bugs)
*)
// Sources
// Warning: I haven't double checked these references.
// This is not a serious acedemic adventure yet...
//
// [1] - Danson, Neil
// F# Silverlight Application
// December 22, 2010
// http://visualstudiogallery.msdn.microsoft.com/8b9fe647-cfad-4afa-a6d3-07f2fea712a2/
// Last visited on: January 29, 2011
//
// [2] - McNamara, Brian
// Game programming in F# (with Silverlight and WPF)
// April 24, 2010
// http://lorgonblog.wordpress.com/2010/04/24/game-programming-in-f-with-silverlight-and-wpf/
// Last Visited on: January 29, 2011
//
// [3] - McNamara, Brian
// Source code for F# Depth Colorizer extension
// November 21, 2010
// http://lorgonblog.wordpress.com/2010/11/
// Last Visited on: January 29, 2011
//
// [4] - Smith, J.O.
// Mathematics of the Discrete Fourier Transform (DFT)
// with Audio Applications, Second Edition,
// https://ccrma.stanford.edu/~jos/mdft/Back_Mth_Roots.html
// https://ccrma.stanford.edu/~jos/mdft/Euler_s_Identity_I.html
// https://ccrma.stanford.edu/~jos/mdft/Roots_Unity.html
// 2007, online book, accessed January 30, 2011.
//
// [5] - Butler, Tetyana
// Roots of complex numbers
// 2006
// http://www.suitcaseofdreams.net/Roots_complex.htm
// Last Visited on: February 01, 2011
namespace FSharpSilverlightApp
open System
open System.Windows
open System.Windows.Controls
open System.Windows.Media
open System.Windows.Shapes
open System.Windows.Media.Imaging
type MainPage() as this =
inherit UserControl()
do
(***********************************************************)
(* BEGIN: Configuration Constants *)
(***********************************************************)
let canvasHeight = 400.0
let canvasWidth = 400.0
let circleFractionToCanvasSize = 0.70 (* 70 Percent *)
let minRootLevel = 1
let maxRootLevel = 7
let defaultRootLevel = 3
(* Limited to prevent excessive clutter *)
let rec allowedRoots =
[minRootLevel .. maxRootLevel]
(***********************************************************)
(* END: Configuration Constants *)
(***********************************************************)
let canvasCenterX = canvasHeight / 2.0
let canvasCenterY = canvasWidth / 2.0
let minDimValue = min canvasHeight canvasWidth
let circleRadius = (minDimValue * circleFractionToCanvasSize) / 2.0
let phaseCirclesRadius = circleRadius * 0.05
(* Helper function to load a combo box with values *)
let rec loadCB (cb : ComboBox) list =
match list with
| head :: tail ->
cb.Items.Add(head)
loadCB cb tail
| [] -> ()
(* Helper function to return the Magnatude of a complex number *)
let magnitude r i =
Math.Sqrt(r*r + i*i)
(* Helper function to return the Phase of a complex number *)
let phase r i =
Math.Atan(i/r)
(* I "believe" this computes the Mth roots of identity of
the complex number defined by r (real) and i (imaginary).
This is of course assuming that I understood Dr. Smith. [4]
If I understood Dr. Smith correctly, then the M roots are
simply given by his formula:
r^(1/M) * e ^ (j*(initialPhase+2*pi*k)/M)
since r^(1/M) appears to just be a scaling factor then the
Mth roots of unity should just be the same function above
without the scaling factor which would be
e ^ (j*(initialPhase+2*pi*k)/M)
this is confusing to me since Dr. Smith formula for the Mth
roots of unity appears to be
e ^ (j*2*pi*k/M)
which totally ignores the initial phase of the complex
number. Maybe I'm just going a little dense here. Maybe
we want for Dr. Smith's application the initial phase isn't
important. Anyway, I will by using
e ^ (j*(initialPhase+2*pi*k)/M)
and applying it to Euler's identity as proposed by
Dr. Smith [4] which should give me (assuming I'm not really
lost):
cos ((initialPhase + 2*pi*k) / M) +
j sin ((initialPhase + 2*pi*k) / M)
to get the unit circle roots of the original complex
number defined by r (real) and i (imaginary) *)
let rootPhases r i M =
let rec p_rootsOfUnity r i M (k : int) =
if (k > (M-1)) then
[]
else
let complexPhase = phase r i
let (doubleK : double) = Convert.ToDouble(k)
let (doubleM : double) = Convert.ToDouble(M)
let theta = (complexPhase + 2.0 * Math.PI * doubleK) / doubleM
(Math.Cos(theta), Math.Sin(theta)) :: (p_rootsOfUnity r i M (k+1))
p_rootsOfUnity r i M 0
(* Helper function to output a Phase List in a format that
is a little easier to read in the VS2010 debugger *)
let rec getPhaseDebugString pl =
match pl with
| head :: tail ->
"(" + fst(head).ToString() + "," + snd(head).ToString() + ")"
+ (getPhaseDebugString tail)
| [] -> ""
(* Given the assumption that the origin is in the center of the
canvas, this trivial function converts the origin relative
Cord into the absolute cord that is needed by a Silverlight
Canvas *)
let relToAbs (rx,ry) = (canvasCenterX + rx, canvasCenterY - ry)
(* Trivial function to compute the center of the well behaved
shape. This will only work on Ellipses, Rectangles
and Lines *)
let simpleShapeCenter (s : Shape) =
(s.Width / 2.0, s.Height / 2.0)
(* Takes a simple shape and its current position and returns
the absolute coordinated needed to position it on a canvas.
This makes it easy to plot the center of elipses from the
center of the cavas which is considered the origin
Note: This will only work on Ellipses and Rectangles *)
let simpleShapeRelToAbs (s : Shape) (rx,ry) =
let cTup = simpleShapeCenter s
let absTup = relToAbs (rx,ry)
(fst(absTup) - fst(cTup), snd(absTup) - snd(cTup))
(* Helper function to packs the specified control into the
specified grid *)
let pack ctrl row column colSpan (grid : Grid) =
Grid.SetRow(ctrl, row)
Grid.SetColumn(ctrl, column)
Grid.SetColumnSpan(ctrl, colSpan)
grid.Children.Add(ctrl)
(* Helper function to create/locate simple TextBlock controls *)
let newTB text row column colSpan (grid : Grid) =
let someTB = TextBlock(Text=text)
pack someTB row column colSpan grid
let grid = Grid()
grid.RowDefinitions.Add(
RowDefinition(Height=GridLength(0.0, GridUnitType.Auto))
)
grid.RowDefinitions.Add(
RowDefinition(Height=GridLength(0.0, GridUnitType.Auto))
)
grid.RowDefinitions.Add(
RowDefinition(Height=GridLength(0.0, GridUnitType.Auto))
)
grid.RowDefinitions.Add(
RowDefinition(Height=GridLength(0.0, GridUnitType.Auto))
)
grid.ColumnDefinitions.Add(ColumnDefinition())
grid.ColumnDefinitions.Add(ColumnDefinition())
grid.ColumnDefinitions.Add(ColumnDefinition())
let canvas = Canvas()
canvas.Height <- canvasHeight
canvas.Width <- canvasWidth
canvas.Background <- SolidColorBrush(Color(A=255uy,R=0uy,B=0uy,G=255uy))
this.Content <- grid
let a =
Ellipse(
Width=(circleRadius*2.0),
Height=(circleRadius*2.0),
Fill=SolidColorBrush(Color(A=255uy,R=255uy,B=0uy,G=0uy)),
StrokeThickness=3.0,
Stroke=SolidColorBrush(Color(A=255uy,R=0uy,B=0uy,G=0uy))
)
let newCord = simpleShapeRelToAbs a (0.0, 0.0)
Canvas.SetLeft(a, fst(newCord))
Canvas.SetTop(a, snd(newCord))
canvas.Children.Add(a)
pack canvas 0 0 3 grid
(* Create the labels for the input controls *)
newTB "Real:" 1 0 1 grid
newTB "Imaginary:" 1 1 1 grid
newTB "Num Roots:" 1 2 1 grid
let realInput = TextBox()
pack realInput 2 0 1 grid
let imaginaryInput = TextBox()
pack imaginaryInput 2 1 1 grid
let cbNumRoots = ComboBox()
loadCB cbNumRoots allowedRoots
cbNumRoots.SelectedValue <- defaultRootLevel
pack cbNumRoots 2 2 1 grid
(* Helper function to plot the circle dots associated with the
the phases in pl *)
let plotPhases pl (iCanvas : Canvas) baseCircle =
let rec plotPhasesR pl (iCanvas : Canvas) =
match pl with
| head :: tail ->
let e =
Ellipse(
Width=(phaseCirclesRadius)*2.0,
Height=(phaseCirclesRadius)*2.0,
Fill=SolidColorBrush(Color(A=255uy,R=0uy,B=255uy,G=0uy)),
StrokeThickness=3.0,
Stroke=SolidColorBrush(Color(A=255uy,R=0uy,B=0uy,G=0uy))
)
let fstHead = fst(head)
let sndHead = snd(head)
let relX = fstHead*circleRadius
let relY = sndHead*circleRadius
let newCord = simpleShapeRelToAbs e (relX, relY)
Canvas.SetLeft(e, fst(newCord))
Canvas.SetTop(e, snd(newCord))
iCanvas.Children.Add(e)
plotPhasesR tail iCanvas
| [] -> ()
(* Toast everything in the Canvas - Lazy fix *)
iCanvas.Children.Clear()
(* Add the base circle back after the above toast
operation *)
iCanvas.Children.Add(baseCircle)
plotPhasesR pl iCanvas
(* Mr. McNamara shows how to map a handler in [3] *)
let submitHandler() =
let realString = realInput.Text
let realIsValid = fst(Double.TryParse(realString))
if not realIsValid then
MessageBox.Show("Real Component is not a valid Double") |> ignore
let imaginaryString = imaginaryInput.Text
let imaginaryIsValid = fst(Double.TryParse(imaginaryString))
if not imaginaryIsValid then
MessageBox.Show("Imaginary Component is not a valid Double") |> ignore
if realIsValid && imaginaryIsValid then
let (rootDegree : int) = cbNumRoots.SelectedValue :?> int
let realValue = Double.Parse(realString)
let imaginaryValue = Double.Parse(imaginaryString)
let complexRadius = magnitude realValue imaginaryValue
let thePhases = rootPhases realValue imaginaryValue rootDegree
plotPhases thePhases canvas a
()
let bnSubmit = Button(Content="Submit")
pack bnSubmit 3 0 3 grid
(* Mr. McNamara shows how to map a handler in [3] *)
bnSubmit.Click.Add(fun _ -> submitHandler())