html5 draw gaussian function using bezierCurveTo

2019-07-07 17:15发布

I have been trying to draw gaussin-like function using bezierCurveTo

find the code below

<canvas id="thisCan" width="0px" height="0px" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.
</canvas>

<script>
(function() {
var
// Obtain a reference to the canvas element
// using its id.
htmlCanvas = document.getElementById('thisCan'),
   // Obtain a graphics context on the
   // canvas element for drawing.
   ctx = htmlCanvas.getContext('2d');

var width = 0;
var height = 0;

// Start listening to resize events and
// draw canvas.
initialize();

function initialize() 
{
  // Register an event listener to
  // call the resizeCanvas() function each time
  // the window is resized.
  window.addEventListener('resize', resizeCanvas, false);
  // Draw canvas border for the first time.
  resizeCanvas();
}

// Display custom canvas.
// In this case it's a blue, 5 pixel border that
// resizes along with the browser window.
function redraw()
{
  ctx.beginPath();

  ctx.moveTo(width/2, 0);
  ctx.bezierCurveTo(150, 119, 186, 121, 66, 185);

  ctx.moveTo(width/2 + width * 0.08 , 0);
  ctx.bezierCurveTo(344, 119, 344, 121, 504, 185);

  ctx.stroke();
}

// Runs each time the DOM window resize event fires.
// Resets the canvas dimensions to match window,
// then draws the new borders accordingly.
function resizeCanvas()
{
  var contentElement = document.getElementsByClassName("content-box post")[0]
  width = htmlCanvas.width = (contentElement.clientWidth * 0.75)
  height = htmlCanvas.height = (contentElement.offsetWidth*0.75 * 0.5);

  redraw();
}

})();
</script>

I am planning to draw many curves in between as well. But how do I make it parametric, based on width and height variables?

I need to specify the control points using width and height parameters, so that it becomes window-size invariant.

Is there a way?

2条回答
Luminary・发光体
2楼-- · 2019-07-07 17:48

I managed to resolve it.

To be specific, I was looking out for half-gaussian curve.

I managed to figure out that guassian function has a very special property with respect to bezier curves. It might be elementary, but I could not find it on google. So this might be informative finding.

If each control points of the cubic bezier curve reside on a "line parallel to X axis and passing through start point/end point", the resulting curve will be of a half guassian shape.

e.g.

enter image description here

On top of this finding, I have written the following code:

<canvas id="thisCan" width="0px" height="0px">
Your browser does not support the HTML5 canvas tag.
</canvas>

<script>
(function() {
var
// Obtain a reference to the canvas element
// using its id.
htmlCanvas = document.getElementById('thisCan'),
   // Obtain a graphics context on the
   // canvas element for drawing.
   ctx = htmlCanvas.getContext('2d');

// Start listening to resize events and
// draw canvas.
initialize();

function initialize() 
{
  // Register an event listener to
  // call the resizeCanvas() function each time
  // the window is resized.
  window.addEventListener('resize', resizeCanvas, false);
  // Draw canvas border for the first time.
  resizeCanvas();
}

// Display custom canvas.
// In this case it's a blue, 5 pixel border that
// resizes along with the browser window.
function redraw(width, height)
{
  var start = width/2;
  var margin = width * 0.01;
  var height = (width / 3) - (margin * 4);
  var end_step = width/4

  ctx.beginPath();

  ctx.moveTo(start - margin, 0);
  ctx.bezierCurveTo((start - height/3), 0 , (start - height/3), height , end_step*1, height);

  ctx.moveTo(start + margin, 0);
  ctx.bezierCurveTo((start + height/3), 0 , (start + height/3), height , end_step*3, height);

  ctx.moveTo(start - margin, 0);
  ctx.bezierCurveTo((start - height*0.33), 0 , (start - height*0.16), height , end_step*1.5, height);

  ctx.moveTo(start + margin, 0);
  ctx.bezierCurveTo((start + height*0.33), 0 , (start + height*0.16), height , end_step*2.5, height);

  ctx.moveTo(start, 0);
  ctx.bezierCurveTo((start ), 0 , (start ), height , end_step*2, height);

  ctx.stroke();
}

// Runs each time the DOM window resize event fires.
// Resets the canvas dimensions to match window,
// then draws the new borders accordingly.
function resizeCanvas()
{
  var width = 0;
  var height = 0;

  var contentElement = document.getElementsByClassName("content-box post")[0]
  width = htmlCanvas.width = (contentElement.clientWidth * 0.85)
  height = htmlCanvas.height = (contentElement.offsetWidth*0.85 * 0.33);

  redraw(width, height);
}

})();
</script>

and the output:

enter image description here

查看更多
家丑人穷心不美
3楼-- · 2019-07-07 18:04

Don't use Bezier curves if you want an exponent curve, they're different functions. Instead, just plot the function you actually need, with something like http://jsbin.com/nubutodosu/edit?js,output, where you define a Gaussian object:

// Gaussian distribution generator

var Gaussian = function(mean, std) {
  this.mean = mean;
  this.std = std;
  this.a = 1/Math.sqrt(2*Math.PI);
};

Gaussian.prototype = {
  addStd: function(v) {
    this.std += v;
  },

  get: function(x) {
    var f = this.a / this.std;
    var p = -1/2;
    var c = (x-this.mean)/this.std;
    c *= c;
    p *= c;
    return f * Math.pow(Math.E, p);
  },

  generateValues: function(start, end) {
    var LUT = [];
    var step = (Math.abs(start)+Math.abs(end)) / 100;
    for(var i=start; i<end; i+=step) {
      LUT.push(this.get(i));
    }
    return LUT;
  }
};

And then you can give that a draw routine so that it can plot itself over the interval that you need:

...
  draw: function(ctx) {
    var points = this.generateValues(-10,10);
    var len = points.length;
    ctx.strokeStyle = "black";
    ctx.beginPath();
    var p0 = points[0];
    ctx.moveTo(0, height - (height*p0));
    points.forEach(function(p,i) {
      if(i===0) {
        return;
      }
      ctx.lineTo(width * i/len, height - (height*p));
      p0 = p;
    });
    ctx.stroke();
  }
...

So you build your array of values over the interval, then draw them on the canvas by "connecting the dots".

查看更多
登录 后发表回答