用金字塔图案绘制圆圈的方法

时间:2016-04-07 20:41:00

标签: javascript canvas html5-canvas

我想在金字塔图案的HTML画布上绘制圆形球。

喜欢这样:

Balls in pyramid

小提琴,你可以告诉我算法:

https://jsfiddle.net/ofxmr17c/3/

var canvas = document.getElementById('canvas');
canvas.width = 400;
canvas.height = 400;
var ctx = canvas.getContext('2d');

var balls = [];
var ballsLength = 15;

var Ball = function() {
	this.x = 0;
    this.y = 0;
    this.radius = 10;
};

Ball.prototype.draw = function(x, y) {
	this.x = x;
    this.y = y;
	ctx.fillStyle = '#333';
	ctx.beginPath();
	ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
	ctx.fill();
	ctx.closePath();
};

init();

function init() {
	for (var i = 0; i < ballsLength; i++) {
    	balls.push(new Ball());
    }
    render();
}

function render() {
	for (var i = 1; i <= ballsLength; i++) {
    	if (i >= 1 && i <= 5) {
        	balls[i].draw(i * 20 + balls[i].radius, 20 + balls[i].radius);
        }
        
        if (i >= 6 && i <= 9) {
        	balls[i].draw(i * 20 + balls[i].radius, 20 + balls[i].radius * 2);
        }
        
        if (i >= 10 && i <= 12) {
        	balls[i].draw(i * 20 + balls[i].radius, 20 + balls[i].radius * 3);
        }
        
        if (i >= 13 && i <= 14) {
        	balls[i].draw(i * 20 + balls[i].radius, 20 + balls[i].radius * 4);
        }
        
        if (i == 15) {
        	balls[i].draw(i * 20 + balls[i].radius, 20 + balls[i].radius * 5);
        }
    }
    
	window.requestAnimationFrame(render);
}
canvas {
    border: 1px solid #333;
}
<canvas id="canvas"></canvas>

我的Ball课程包括xyradius个变种:

var Ball = function() {
    this.x = 0;
    this.y = 0;
    this.radius = 10;
};

然后我有Ball类的方法在画布上绘制球:

Ball.prototype.draw = function(x, y) {
    this.x = x;
    this.y = y;
    ctx.fillStyle = '#333';
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
    ctx.fill();
    ctx.closePath();
};

我想创建将任意数量的球放入金字塔的方法。

2 个答案:

答案 0 :(得分:4)

下面的现场演示展示了如何使用一点三角法将任意数量的球包装到金字塔中。要更改金字塔中的图层数量(以及球的数量),请修改<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"> </script> <form> <input type="date" name="date1" required /> <input type="date" name="date2" required /> <input type="submit" /> </form>变量。

这就是它完成时的样子:

Circles packed in a pyramid

现场演示:

NUM_ROWS
var canvas = document.getElementById('canvas');
canvas.width = 400;
canvas.height = 400;
var ctx = canvas.getContext('2d');

var balls = [];
var ballsLength = 15;

var Ball = function() {
  this.x = 0;
  this.y = 0;
  this.radius = 10;
};

Ball.prototype.draw = function(x, y) {
  this.x = x;
  this.y = y;
  ctx.fillStyle = '#333';
  ctx.beginPath();
  ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
  ctx.fill();
  ctx.closePath();
};

init();

function init() {
  for (var i = 0; i < ballsLength; i++) {
    balls.push(new Ball());
  }
  render();
}

function render() {
  var NUM_ROWS = 5;
  for (var i = 1; i <= NUM_ROWS; i++) {
    for (var j = 0; j < i; j++) {
      balls[i].draw(j * balls[0].radius * 2 + 150 - i * balls[0].radius, -(i * balls[0].radius * 2 * Math.sin(Math.PI / 3)) + 150);
    }
  }

  //window.requestAnimationFrame(render);
}
canvas {
    border: 1px solid #333;
}

JSFiddle版本:https://jsfiddle.net/ofxmr17c/6/

答案 1 :(得分:1)

像这样的台球金字塔总是用一些已知的事实做成:

  • 每行总是包含比前一个
  • 多一个球
  • 这是一个等边的等角度(sp?in english?)三角形,这意味着下一行始终偏移60°

所以我们可以制作一个矢量(台球游戏中的其他所有东西都非常涉及矢量,所以为什么不呢!:))对于下一行起点的方向如此:

var deg60 = -60 / 180 * Math.PI;    // -60°, up-right direction
var v = {
    x: radius * Math.cos(deg60),
    y: radius * Math.sin(deg60)
  }

然后算法将(由总球数驱动):

  • 以第一行的最大限制为1开始
  • 绘制球直到达到行的最大限制
    • 然后,添加一个到最大限制
    • 重置行数
    • 将位置移至最后一行+矢量
    • 的开头
  • 重复直到达到球数

结果:

result

实施例

var ctx = c.getContext("2d"),
    radius = 9,                         // ball radius
    deg = -60 / 180 * Math.PI,          // direction of row start -60°
    balls = 15,                         // number of balls to draw
    drawn = 0,                          // count balls drawn on current row
    rowLen = 1,                         // max length of current row (first=1)
    x = 150,                            // start point
    y = 140,
    cx = 150, cy =140,                  // replicates start point + offsets
    v = {                               // vector
      x: radius * 2 * Math.cos(deg),
      y: radius * 2 * Math.sin(deg)
    },
    i;

for(i = 0; i <  balls; i++) {
  drawBall(cx, cy);                     // draw ball
  cx -= radius * 2;                     // move diameter of ball to left (in this case)
  drawn++;                              // increase balls on row count
  if (drawn === rowLen) {               // reached max balls for row?
    cx = x + v.x * rowLen;              // increase one row
    cy = y + v.y * rowLen;
    drawn = 0;                          // reset ball count for row
    rowLen++;                           // increase row limit
  }
}

ctx.fillStyle = "#D70000";
ctx.fill();

function drawBall(x, y) {
  ctx.moveTo(x + radius, y); ctx.arc(x, y, radius, 0, 6.28);
  ctx.closePath();
}
<canvas id=c height=300></canvas>

如果你想在旋转方面有更多的灵活性,你可以简单地交换这一行:

cx -= radius * 2;

向量垂直(计算未显示)到第一个向量,所以:

cx += pv.x;
cy += pv.y;