Bスプライン曲線を復習する


滑らかな曲線を描く発生器が欲しくなったので再び考える
最終的な目的は、始点が(0,0)で終点が(1,1)、中に2点で全部で4つ。
高速に動作する。もちろんある程度短いことにこしたことはない。
で基本から。

<!DOCTYPE html>
<meta charset="utf-8">
<title>B-spline</title>

<body>
<canvas width="500" height="500"></canvas>

<script>
function B_Spline_Generator (point) {
  var m = point.length;
  var n = m - 1;

  return function (t) {
    var cn, i, j, t0, t1, t2;
    var x = 0, y = 0;

    t0 = Math.min (Math.max (0, t), 1) * m - 1;
		for (i = -2; i < m + 2; i += 1) {
      cn = ((t1 = Math.abs (t0 - i)) < 1)
           ? (t2 = 3 * t1 * t1, t1 * t2 - 2 * t2 + 4) / 6
           : (t1 < 2)
             ? (t2 = t1 - 2, t2 * t2 * t2 / -6)
             : 0;

			j = Math.min (Math.max (0, i), n);
			x += point[j][0] * cn;
			y += point[j][1] * cn;
		}
    return {x: x, y: y};
  }
}

//__________________________


(function () {
  var ps =[[30, 90],[231,147],[63,495],[513,129],[459,492]];

  var pointer = B_Spline_Generator (ps);
  var canvas = document.querySelector ('canvas');
  var ctx = canvas.getContext('2d');
  var p, x, y;

  ctx.beginPath ();
  ctx.strokeStyle = 'rgb(255,0,0)';
  for (var i = 0; i <= 1; i += .01) {
    p = pointer (i);
    ctx[i ? 'lineTo': 'moveTo'](p.x, p.y);
  }
  ctx.stroke ();
})();

</script>

気持ち高速にする

<!DOCTYPE html>
<meta charset="utf-8">
<title>B-spline</title>

<body>
<canvas width="500" height="500"></canvas>

<script>
function B_Spline_Generator (x1, y1, x2, y2) {
  var p = [[0,0], [x1,y1], [x2,y2], [1,1]];

  return function (t) {
    var cn, i, t0, t1, t2, p0;
    var x = 0, y = 0;

    t0 = Math.min (Math.max (0, t), 1) * 3;
    for (i = -2; i < 6; i += 1) {
			t1 = Math.abs (t0 - i);

			if (2 <= t1)
			  continue;

			cn = (t1 < 1)
			     ? (t2 = 3 * t1 * t1, t1 * t2 - 2 * t2 + 4) / 6
			     : (t2 = t1 - 2, t2 * t2 * t2 / -6);

			p0 = p[Math.min (Math.max (0, i), 3)];
			x += p0[0] * cn;
			y += p0[1] * cn;

    }

    return {x: x, y: y};
  }
}

//__________________________


(function () {
  var pointer = B_Spline_Generator (1,0,0,1);
  var canvas = document.querySelector ('canvas');
  var ctx = canvas.getContext('2d');
  var p, x, y;

  ctx.beginPath ();
  ctx.strokeStyle = 'rgb(255,0,0)';
  for (var i = 0; i <= 1; i += .01) {
    p = pointer (i);
    ctx[i ? 'lineTo': 'moveTo'](p.x*400, p.y*400);
  }
  ctx.stroke ();
})();

</script>

2017-4-19 書き直す

<!DOCTYPE html>
<meta charset="utf-8">
<title>B-spline</title>

<body>
<canvas width="500" height="500"></canvas>

<script>

(function () {

  var min = Math.min;
  var max = Math.max;
  var abs = Math.abs;


  function Spline (p, m ,n) {
    this.p = p;
    this.m = m;
    this.n = n;
  }


  function calc (t) {
    var cn, i, j, t0, t1, t2;
    var x = 0, y = 0;
    var m = this.m;
    var n = this.n;
    var p = this.p;

    t0 = min (max (0, t), 1) * m - 1;
    for (i = -2; i < m + 2; i += 1) {
      cn = ((t1 = abs (t0 - i)) < 1)
           ? (t2 = 3 * t1 * t1, t1 * t2 - 2 * t2 + 4) / 6
           : (t1 < 2)
             ? (t2 = t1 - 2, t2 * t2 * t2 / -6)
             : 0;

      j = min (max (0, i), n);
      x += p[j].x * cn;
      y += p[j].y * cn;
    }
    return {x: x, y: y};  
  }
  

  function create (aryPoint) {
    var len = aryPoint.length;
    var obj = new Spline (aryPoint, len, len - 1);
    return obj;
  }


  Spline.create = create;
  Spline.prototype.calc = calc;
  
  this.Spline = Spline;

})();

//__________________________


(function () {
  var ps =[
    {x:  30, y:  90},
    {x: 231, y: 147},
    {x:  63, y: 495},
    {x: 513, y: 129},
    {x: 459, y: 492}
  ];

  var pointer = Spline.create (ps);
  var canvas = document.querySelector ('canvas');
  var ctx = canvas.getContext('2d');
  var p, x, y, i;

  ctx.beginPath ();
  ctx.strokeStyle = 'rgb(255,0,0)';
  for (i = 0; i <= 1; i += .01) {
    p = pointer.calc (i);
    ctx[i ? 'lineTo': 'moveTo'](p.x, p.y);
  }
  ctx.stroke ();
})();

</script>

2017-4-19 ES6っぽく

<!DOCTYPE html>
<meta charset="utf-8">
<title>B-spline</title>

<body>
<canvas width="500" height="500"></canvas>

<script>
{
  class Spline {

    constructor (ps = []) {
      this.p = ps;
      this.m = ps.length;
      this.n = this.m - 1;
    }


    calc (t = 0) {
      let
        {min, max, abs} = Math,
        {p, m, n} = this,
        x = 0, y = 0,
        t0 = min (max (0, t), 1) * m - 1;

      for (let i = -2; i < m + 2; i += 1) {
        let
          t1 = abs (t0 - i),
          t2,
          j = min (max (0, i), n),
          cn = t1 < 1
             ? (t2 = 3 * t1 * t1, t1 * t2 - 2 * t2 + 4) / 6
             : (t1 < 2)
               ? (t2 = t1 - 2, t2 * t2 * t2 / -6)
               : 0;

        x += p[j].x * cn;
        y += p[j].y * cn;
      }
      return {x: x, y: y};  
    }
  }
  
  this.Spline = Spline; 
}

//__________________________


{
  let
    ps =[
      {x:  30, y:  90},
      {x: 231, y: 147},
      {x:  63, y: 495},
      {x: 513, y: 129},
      {x: 459, y: 492}
    ],
    pointer = new Spline (ps),
    ctx = document.querySelector ('canvas').getContext('2d');

  ctx.beginPath ();
  ctx.strokeStyle = 'rgb(255,0,0)';
  for (let i = 0; i <= 1; i += .01) {
    let {x, y} = pointer.calc (i);
    ctx[i ? 'lineTo': 'moveTo'](x, y);
  }
  ctx.stroke ();
}

</script>