Pixel size: Maxmum Iterations:

Lower Left

X: Y:

Upper Right:

X: Y:

The HTML file:

<!DOCTYPE html>
<html>
<head>
<!-- 
   include all of my source code 

   The order is important.  function1 relies on complex
                            newton relies on both
			    doFractal relies on all three, plus graphics

-->

<script type="text/javascript" src="graphics.js"> </script>
<script type="text/javascript" src="complex.js"> </script>
<script type="text/javascript" src="function1.js"> </script>
<script type="text/javascript" src="newton.js"> </script>
<script type="text/javascript" src="doFractal.js"> </script>

</head>
<body>

<script>
   // create a canvas and add it to the document
  
   const REAL_XDIM = 500;
   const REAL_YDIM = 500;

   let ctx = MakeCanvas(REAL_XDIM, REAL_YDIM);
</script>

<!-- build some buttons to control the picture -->
<p>
<b>Pixel size:</b>
<input
      type="text" 
      name="Pixel Size"
      maxLength = "3"
      size = "3"
      id="PixelSize">
<b>Maxmum Iterations:</b>
<input
      type="text" 
      name="Max Iterations"
      maxLength = "5"
      size = "5"
      id="MaxIterations">
<p>
<b>Lower Left </b>
<p>
<b>X:</b>
<input
      type="text" 
      name="LLX"
      maxLength = "5"
      size = "5"
      id="LLX">
<b>Y:</b>
<input
      type="text" 
      name="LLY"
      maxLength = "5"
      size = "5"
      id="LLY">

<p>
<b>Upper Right:</b>
<p>
<b>X:</b>
<input
      type="text" 
      name="URX"
      maxLength = "5"
      size = "5"
      id="URX">
<b>Y:</b>
<input
      type="text" 
      name="URY"
      maxLength = "5"
      size = "5"
      id="URY">

<p>
<button type="button" id="Redraw" onclick="FullRun()">Redraw</button>

<script>
/*
 This script is mostly

 1. Set up variables used by the program
 2. Interact with the UI.

 This script needs to be placed after the input fields are created so it can 
 acces these fields.
*/
var START_POS = new Complex(-1, -1);
var END_POS = new Complex(1,1);

var PixelSize = 1;
var CLOSE_TO_ROOT_EPSILON = 0.0000001;
var MaxIterations = 150;

var XDIM, YDIM;

// set the input boxes to hold the correct values
document.getElementById("PixelSize").value = PixelSize
document.getElementById("MaxIterations").value =MaxIterations 
document.getElementById("LLX").value = START_POS.real 
document.getElementById("LLY").value = START_POS.imag 
document.getElementById("URX").value = END_POS.real 
document.getElementById("URY").value = END_POS.imag 

// This function will redisplay the entire picture based on the values
// in the input boxes

function FullRun() {

    // get the values in the input boxes
    PixelSize =  parseInt(document.getElementById("PixelSize").value);
    MaxIterations =  parseInt(document.getElementById("MaxIterations").value);
    START_POS.real =  parseFloat(document.getElementById("LLX").value);
    START_POS.imag =  parseFloat(document.getElementById("LLY").value);
    END_POS.real =  parseFloat(document.getElementById("URX").value);
    END_POS.imag =  parseFloat(document.getElementById("URY").value);

    // adjust the number of pixels to generate based on the size
    XDIM = REAL_XDIM/PixelSize;
    YDIM = REAL_YDIM/PixelSize;

    // recompute the fractal
    NewtonMain(ctx)
}

FullRun();
</script>

</body>
</html>

complex.js

'use strict'

class Complex {
   constructor(real=0, imag=0) {
       this._real = real;
       this._imag = imag;
   }

   // javascript has a different syntax for getters and setters
   get real() {
      return this._real;
   }
   
   set real(value) {
      if (typeof value === "number") {
          this._real = value;
      }
   }

   get imag() {
      return this._imag;
   }
   
   set imag(value) {
      if (typeof value === "number") {
          this._imag = value;
      }
   }

   // yucko, get the format right.
   ToString() {
      if (this._real === 0 && this._imag === 0) {
         return "0"
      } else if (this._real === 0) {
         return this._imag.toString() + "i";
      } else if (this._imag === 0) {
         return this._real.toString();
      } else {
         if (this._imag > 0) {
             return this._real.toString() + " + "  + this._imag + "i";
	 } else {
             return this._real.toString() + " - "  + -this._imag + "i";
	 }
      }
   }

   // normal operations on complex.
   Add(i) {
      if (typeof i === "number") {
         this._real += i;
      } else if (this instanceof Complex) {
         this._real += i._real;
	 this._imag += i._imag;
      } else {
          console.log("You must add a complex or number")
	  console.log(typeof i)
      }
   }

   Subtract(i) {
      if (typeof i === "number") {
         this._real -= i;
      } else if (this instanceof Complex) {
         this._real -= i._real;
	 this._imag -= i._imag;
      } else {
          console.log("You must subtract a complex or number")
      }
   }

   Multiply(i) {
      if (typeof i === "number") {
         this._real *= i;
         this._imag *= i;
      } else if (this instanceof Complex) {
         let real = this._real* i._real - this._imag * i._imag;
	 let imag = this._real * i._imag + this._imag * i._real;
	 this._real = real;
	 this._imag = imag;
      } else {
          console.log("You must subtract a complex or number")
      }
   }

   Divide(i) {
      if (typeof i === "number") {
         this._real /= i;
         this._imag /= i;
      } else if (this instanceof Complex) {
         let denom = i._real * i._real + i._imag * i._imag;
         let real  = (this._real* i._real + this._imag * i._imag)/denom;
	 let imag= (this._imag * i._real - this._real * i._imag)/denom;
	 this._real = real;
	 this._imag = imag;
      } else {
          console.log("You must subtract a complex or number")
      }
   }

   Mag() {
      return Math.sqrt(this._real * this._real + this._imag * this._imag);
   }

   Copy() {
      var x = new Complex();
      x._real = this._real;
      x._imag = this._imag;
      return x
   }
}

doFractal.js

'use strict'

// there will be the same number of roots as the degree of the polynomial.
// keep track of them.  
//
// We will use the "root number " to determine the color of the point to be
// plotted

function RootNumber(value, roots) {
   let found = false;
   let i = 0;

   // search for it
   while (!found && i < roots.length) {
      let temp = roots[i].Copy()
      temp.Subtract(value);
      if (temp.Mag() < CLOSE_TO_ROOT_EPSILON) {
         // got it.
         found = true;
      } else {
         i++;
      }
   }

   // not there, so add it.
   if (!found) {
      roots.push(value);
      i++;
   }
   return i;
}

// This is a newton iteration related function, so it is here.
function GetColor(root, iterations) {
    if (iterations >= MaxIterations) {
       return ("black");
    }
    let hue = root * 360/DegreeOfF();
    let saturation = (MaxIterations - iterations) / MaxIterations;
    let lightness = 0.5;

    return ("hsl("+hue+", "+saturation*100 + "%, " + lightness*100 + "%)");
}

function NewtonMain(ctx) {
    let i,j;
    let x,y;
    let roots = []
    x = 0;
    let longestIteration = 0;

    let dx = (END_POS.real - START_POS.real)/(XDIM-1)
    let dy = (END_POS.imag - START_POS.imag)/(YDIM-1)

    for (i= START_POS.real; i<= END_POS.real; i+= dx) {
        y = 0;
        for (j= START_POS.imag; j<= END_POS.imag; j+= dy) {
           let initPos = new Complex(i,j)
           let ans = Newton(initPos, MaxIterations,CLOSE_TO_ROOT_EPSILON);
	   let root = RootNumber(ans[0],roots);
	   let color = GetColor(root, ans[1]);
	   if (ans[1] > longestIteration) {
	      longestIteration =ans[1];
	   }
	   Plot(ctx,x,y,color);
	   y++;
        }
	x++;
    }
}

function.js

'use strict'

function DegreeOfF() {
   return 3
}

function FString() {
   return ("z^3 - 1");
}

function F(x) {
   if (typeof x === "number") {
      // 3x^2+5x+2
      return 3*x*x + 5*x + 2;
   } else if (x instanceof Complex) {
      // z^3 - 1
      let t1 = x.Copy();
      let t2 = x.Copy();
      t1.Multiply(x);
      t1.Multiply(x);
      t1.Add(-1);
      return t1;
   } else {
      console.log("Error, F needs either a number of a Complex");
      return 0;
   }
}

function FPrime(x) {
   // 3x^2 + 5x + 2  => 6x+5
   if (typeof x === "number") {
      return 6*x + 5;
   } else if (x instanceof Complex) {
      // 3z^2 
      let t1 = x.Copy();
      t1.Multiply(t1);
      t1.Multiply(3) ;
      return t1;
   } else {
      console.log("Error, FPrime needs either a number of a Complex");
      return 0;
   }
}

function1.js

'use strict'

function DegreeOfF() {
   return 8
}

function FString() {
   return (" z^8 + 15z^4 - 16");
}

function F(x) {
   if (typeof x === "number") {
      // 3x^2+5x+2
      return 3*x*x + 5*x + 2;
   } else if (x instanceof Complex) {
      // z^8+15z^4 - 16
      let t1 = x.Copy();
      t1.Multiply(x); // z^2
      t1.Multiply(t1); // z^4
      let t2 = t1.Copy();
      t1.Multiply(t1); // z^8
      t2.Multiply(15)
      t1.Add(t2);
      t1.Add(-16);
      return t1;
   } else {
      console.log("Error, F needs either a number of a Complex");
      return 0;
   }
}

function FPrime(x) {
   // 3x^2 + 5x + 2  => 6x+5
   if (typeof x === "number") {
      return 6*x + 5;
   } else if (x instanceof Complex) {
      // 8z^7 + 60^3;
      let t1 = x.Copy();
      t1.Multiply(t1); // z^2
      t1.Multiply(x); // z^3
      let t2 = t1.Copy();
      t1.Multiply(x) ; // z^4
      t1.Multiply(t2); //z^7
      t1.Multiply(8);
      t2.Multiply(60);
      t1.Add(t2);
      return t1;
   } else {
      console.log("Error, FPrime needs either a number of a Complex");
      return 0;
   }
}

graphics.js

'use strict'

// This function plots a single point with a given color.
function Plot(drawArea, x, y, color) {
   drawArea.ctx.fillStyle = color;
   drawArea.ctx.fillRect(x*PixelSize,y*PixelSize,PixelSize,PixelSize);
}

// This function creates a canvas and adds it to the current document
function MakeCanvas(xdim=500, ydim=500) {

    let canvas = document.createElement('canvas')

    canvas.height = xdim;
    canvas.width = ydim;

    document.body.appendChild(canvas);

    let ctx={"width":xdim, "height":ydim, "ctx": canvas.getContext("2d")}

    return ctx;
}

newton.js

function Newton(start, maxIterations = 1000, epsilon = 0.001) {

   if (typeof start === "number") {
       return RegularNewton(start, maxIterations, epsilon) ;
   } else if (start instanceof Complex) {
       return ComplexNewton(start, maxIterations, epsilon);
   } else {
      console.log("Error, Newton needs a number of Complex")
      return (0);
   }
}

function RegularNewton(start, maxIterations, epsilon) {
    let distance, oldX, newX, FofX
    let iterations = 0;

    oldX = start;
    newX = start;
    FofX = F(oldX);
    distance = Math.abs(FofX);

    while (iterations < maxIterations && distance > epsilon) {
       newX = oldX - FofX/FPrime(oldX);
       FofX = F(newX);
       distance = Math.abs(FofX);
       oldX = newX
       iterations ++;
    }
    return [newX, iterations];
}

function ComplexNewton(start, maxIterations, epsilon) {
    let distance, oldX, newX, FofX;
    let iterations = 0;

    oldX = start.Copy();
    newX = start.Copy();
    FofX = F(oldX);
    distance = Math.abs(FofX.Mag());

    while (iterations < maxIterations && distance > epsilon) {
       // newX = oldX - FofX/FPrime(oldX)
       let t1 = FPrime(oldX);
       let t2 = FofX.Copy();
       let t3 = oldX.Copy();
       t2.Divide(t1);
       t3.Subtract(t2);

       newX = t3.Copy();

       FofX = F(newX);
       distance = Math.abs(FofX.Mag());
       oldX = newX.Copy()
       iterations ++;
    }
    return [newX, iterations];
}