This JavaScript program demonstrates how to generate and graph a selected distribution. Use the selector to select a distribution on the bar width on the histogram.
<!DOCTYPE html> <html> <head> <title>XoaX.net's Javascript</title> <script type="text/javascript" src="GraphingASelectedDistribution.js"></script> </head> <body onload="Initialize()"> <div style="width:908px;height:430px;"> <div style="width:108px;height:430px;float:left;"> <input id="idHighY" type="text" size="9" style="width:100px;height:16px;"/> <div style="width:108px;height:356px;"></div> <input id="idLowY" type="text" size="9" style="width:100px;height:16px;"/> </div> <canvas id="idCanvas" width="800" height ="400" style="background-color: #F0F0F0;float:left;"></canvas> <div style="width:800px;height:22px;float:left;"> <input id="idLowX" type="text" size="9" style="width:100px;height:16px;float:left;"/> <div style="width:584px;height:22px;float:left;"> <div style="width:fit-content;margin-left:auto;margin-right:auto;"> <div style="width:fit-content;float:left;">Distribution: <select style="height: 22px;" id="idDistribution" onchange="fnSelectFunction()"> <option value="kUniform">Uniform</option> <option value="kGaussian" selected>Gaussian</option> <option value="kExponential">Exponential</option> <option value="kTriangular">Triangular</option> <option value="kBreitWigner">Breit-Wigner</option> <option value="kSemicircular">Semicircular</option> </select> </div> <div style="width:fit-content;float:left;margin-left:5px;">Bar Width: <select style="height: 22px;" id="idBarWidth" onchange="fnSelectFunction()"> <option value="2">2</option> <option value="3">3</option> <option value="5">5</option> <option value="8">8</option> <option value="13">13</option> <option value="21" selected>21</option> </select> </div> </div> </div> <input id="idHighX" type="text" size="9" style="width:100px;height:16px;float:left;"/> </div> <div style="width:108px;height:22px;float:right;"></div> </div> </body> </html>
class CDistribution {
mdaX;
#mdGraphWidth;
#mdMean = null;
// The graph width should be calculated automatically from the data or set by the distribution
constructor(dGraphWidth) {
this.#mdGraphWidth = dGraphWidth;
}
Mean() {
let dMean = 0;
for (let i = 0; i < this.mdaX.length; ++i) {
dMean += this.mdaX[i]/this.mdaX.length;
}
return dMean;
}
StandardDeviation() {
return Math.sqrt(this.Variance());
}
Variance() {
let dMean = this.Mean();
let dVariance = 0;
for (let i = 0; i < this.mdaX.length; ++i) {
dVariance += Math.pow(this.mdaX[i] - dMean, 2)/this.mdaX.length;
}
return dVariance;
}
PixelXToGraphX(dPixelX, iCanvasWidth) {
// Half the size should map to the mean
let dHalfWidth = this.#mdGraphWidth/2;
if (null == this.#mdMean) {
this.#mdMean = this.Mean();
}
let dT = ((dPixelX - .5)/iCanvasWidth)*this.#mdGraphWidth - dHalfWidth + this.#mdMean;
return dT;
}
Graph(qContext, dBarWidthInPixels) {
qContext.fillStyle = "#606060";
// Outline properties
qContext.strokeStyle = "#F0F0F0";
qContext.lineWidth = "1";
// Reset the transformation so that the flip transformations do not build up an cancel each other.
qContext.resetTransform();
// This is the basic transformation to flip the y-axis
qContext.transform(1, 0, 0, -1, 0, qContext.canvas.height);
// The canvas width and height in pixels or pixels per canvas
let iW = qContext.canvas.width;
let iH = qContext.canvas.height;
// The canvas graph width
let dGraphW = this.#mdGraphWidth;
// Pixel width in graph units = graph width / pixels per canvas width
let dPixW = dGraphW/iW;
// Bar width in pixels / pixels per canvas width
let dBarsPerWidth = iW/dBarWidthInPixels;
// The width of a bar in values
let dBarWidth = dBarWidthInPixels*dPixW;
// The middle bar is centered on zero
// Find the start of the first bar and continue from there until all of them are drawn
let dStartOfFirstBarPix = iW/2 - dBarWidthInPixels/2 - dBarWidthInPixels*Math.ceil(dBarsPerWidth/2);
let dStartOfCurrBarPix = dStartOfFirstBarPix;
let dStartOfCurrBarValue = this.PixelXToGraphX(dStartOfCurrBarPix, iW);
let iIndexOfNextPoint = 0;
// Get to the first point that is in the range of the first bar.
while (iIndexOfNextPoint < this.mdaX.length && dStartOfCurrBarValue > this.mdaX[iIndexOfNextPoint]) {
++iIndexOfNextPoint;
}
let iaBins = new Array();
while (iIndexOfNextPoint < this.mdaX.length && dStartOfCurrBarPix < iW) {
let dEndOfCurrBarPix = dStartOfCurrBarPix + dBarWidthInPixels;
let dEndOfCurrBarValue = this.PixelXToGraphX(dEndOfCurrBarPix, iW);
let iIndexOfEnd = iIndexOfNextPoint;
while (iIndexOfEnd < this.mdaX.length && dEndOfCurrBarValue > this.mdaX[iIndexOfEnd]) {
++iIndexOfEnd;
}
// The number of points in the bin
iaBins.push(iIndexOfEnd - iIndexOfNextPoint);
// The increment for the bar
dStartOfCurrBarPix = dEndOfCurrBarPix;
// Increment for the points
iIndexOfNextPoint = iIndexOfEnd;
}
// Get the size of the largest bin
let iLargestBinSize = 0;
for (let i = 0; i < iaBins.length; ++i) {
if (iLargestBinSize < iaBins[i]) {
iLargestBinSize = iaBins[i];
}
}
// Make the height of the graph 20% higher than the largest bin size.
let dMaxY = iLargestBinSize*1.1;
let iBinIndex = 0;
dStartOfCurrBarPix = dStartOfFirstBarPix;
// Draw the bars of the graph
while (iBinIndex < iaBins.length && dStartOfCurrBarPix < iW) {
let dEndOfCurrBarPix = dStartOfCurrBarPix + dBarWidthInPixels;
let dRectHeight = iaBins[iBinIndex];
qContext.fillRect(dStartOfCurrBarPix, 0, dBarWidthInPixels, (dRectHeight/dMaxY)*iH);
// Draw the half pixel width outline in the background color to frame the bars
qContext.strokeRect(dStartOfCurrBarPix, 0, dBarWidthInPixels, (dRectHeight/dMaxY)*iH);
// The increment for the bar
dStartOfCurrBarPix = dEndOfCurrBarPix;
++iBinIndex;
}
// Fill in the bounds
let qLowYElement = document.getElementById("idLowY");
qLowYElement.value = 0;
let qHighYElement = document.getElementById("idHighY");
qHighYElement.value = dMaxY.toFixed(1);
let qLowXElement = document.getElementById("idLowX");
qLowXElement.value = this.PixelXToGraphX(-.5, iW).toFixed(3);
let qHighXElement = document.getElementById("idHighX");
qHighXElement.value = this.PixelXToGraphX(iW-.5, iW).toFixed(3);
}
}
class CUniformDistribution extends CDistribution {
constructor(iCount) {
// The base class constructor must be called before using this.
super(1.2);
// This generates a sorted array of the sample values of the distribution
// This is a uniform distribution from 0 to 1
this.mdaX = new Array(iCount);
for (let i = 0; i < iCount; ++i) {
this.mdaX[i] = Math.random();
// Insertion sort each point after it is added
let j = i;
while (j > 0 && this.mdaX[j] < this.mdaX[j - 1]) {
let dSwap = this.mdaX[j];
this.mdaX[j] = this.mdaX[j - 1];
this.mdaX[j - 1] = dSwap;
--j;
}
}
}
}
class CGaussianDistribution extends CDistribution {
constructor(iCount) {
super(6.1);
// This generates a sorted array of the sample values of the distribution
// This is an approximately gaussian distribution from 0 to 6
this.mdaX = new Array(iCount);
for (let i = 0; i < iCount; ++i) {
let dSum = 0.0;
for (let j = 0; j < 6; ++j) {
dSum += Math.random();
}
// Add the point to the end of the array
this.mdaX[i] = dSum;
// Insertion sort each point after it is added
let j = i;
while (j > 0 && this.mdaX[j] < this.mdaX[j - 1]) {
let dSwap = this.mdaX[j];
this.mdaX[j] = this.mdaX[j - 1];
this.mdaX[j - 1] = dSwap;
--j;
}
}
}
}
class CExponentialDistribution extends CDistribution {
constructor(iCount) {
// The base class constructor must be called before using this.
super(32.2);
// This generates a sorted array of the sample values of the distribution
// This is a uniform distribution from 0 to 1
this.mdaX = new Array(iCount);
for (let i = 0; i < iCount; ++i) {
let dMean = 12.0;
this.mdaX[i] = -dMean*Math.log(Math.random());
// Insertion sort each point after it is added
let j = i;
while (j > 0 && this.mdaX[j] < this.mdaX[j - 1]) {
let dSwap = this.mdaX[j];
this.mdaX[j] = this.mdaX[j - 1];
this.mdaX[j - 1] = dSwap;
--j;
}
}
}
}
class CTriangularDistribution extends CDistribution {
constructor(iCount) {
// The base class constructor must be called before using this.
super(1.2);
// This generates a sorted array of the sample values of the distribution
// This is a uniform distribution from 0 to 1
this.mdaX = new Array(iCount);
for (let i = 0; i < iCount; ++i) {
let dA = -0.5;
let dB = 0.5;
let dC = 0.0;
let dX = Math.random();
if (dX < (dC - dA)/(dB - dA)) {
this.mdaX[i] = dA + Math.sqrt((dB - dA)*(dC - dA)*dX);
} else {
this.mdaX[i] = dB - Math.sqrt((dB - dA)*(dB - dC)*(1.0 - dX));
}
// Insertion sort each point after it is added
let j = i;
while (j > 0 && this.mdaX[j] < this.mdaX[j - 1]) {
let dSwap = this.mdaX[j];
this.mdaX[j] = this.mdaX[j - 1];
this.mdaX[j - 1] = dSwap;
--j;
}
}
}
}
class CBreitWignerDistribution extends CDistribution {
constructor(iCount) {
// The base class constructor must be called before using this.
super(10.2);
// This generates a sorted array of the sample values of the distribution
// This is a uniform distribution from 0 to 1
this.mdaX = new Array(iCount);
for (let i = 0; i < iCount; ++i) {
let dGamma = 1.0;
let dA = 0.5;
let dX = Math.random();
this.mdaX[i] = dA + (dGamma/2.0)*Math.tan(Math.PI*(dX - .5));
// Insertion sort each point after it is added
let j = i;
while (j > 0 && this.mdaX[j] < this.mdaX[j - 1]) {
let dSwap = this.mdaX[j];
this.mdaX[j] = this.mdaX[j - 1];
this.mdaX[j - 1] = dSwap;
--j;
}
}
}
}
class CSemicircleDistribution extends CDistribution {
constructor(iCount) {
// The base class constructor must be called before using this.
super(2.2);
// This generates a sorted array of the sample values of the distribution
// This is a uniform distribution from 0 to 1
this.mdaX = new Array(iCount);
for (let i = 0; i < iCount; ++i) {
let dX = 2.0*Math.random() - 1.0;
let dY = Math.random();
while (dX*dX + dY*dY > 1.0) {
dX = 2.0*Math.random() - 1.0;
dY = Math.random();
}
this.mdaX[i] = dX;
// Insertion sort each point after it is added
let j = i;
while (j > 0 && this.mdaX[j] < this.mdaX[j - 1]) {
let dSwap = this.mdaX[j];
this.mdaX[j] = this.mdaX[j - 1];
this.mdaX[j - 1] = dSwap;
--j;
}
}
}
}
function Initialize() {
var qCanvas = document.getElementById("idCanvas");
var qContext = qCanvas.getContext("2d");
var qDist = new CGaussianDistribution(50000);
qDist.Graph(qContext, 21);
}
function fnSelectFunction() {
var qCanvas = document.getElementById("idCanvas");
var qContext = qCanvas.getContext("2d");
qContext.clearRect(0, 0, qCanvas.width, qCanvas.height);
// Get the distribution value
var sDistributionValue = document.getElementById("idDistribution").value;
var qDistribution = null;
switch (sDistributionValue) {
case "kUniform":
qDistribution = new CUniformDistribution(50000);
break;
case "kGaussian":
qDistribution = new CGaussianDistribution(50000);
break;
case "kExponential":
qDistribution = new CExponentialDistribution(50000);
break;
case "kTriangular":
qDistribution = new CTriangularDistribution(50000);
break;
case "kBreitWigner":
qDistribution = new CBreitWignerDistribution(50000);
break;
case "kSemicircular":
qDistribution = new CSemicircleDistribution(50000);
break;
}
// Get the bar width
var sBarWidth = document.getElementById("idBarWidth").value;
let iBarWidth = 2;
switch (sBarWidth) {
case "2":
iBarWidth = 2;
break;
case "3":
iBarWidth = 3;
break;
case "5":
iBarWidth = 5;
break;
case "8":
iBarWidth = 8;
break;
case "13":
iBarWidth = 13;
break;
case "21":
iBarWidth = 21;
break;
}
qDistribution.Graph(qContext, iBarWidth);
}
© 20072025 XoaX.net LLC. All rights reserved.