Skip to content Skip to sidebar Skip to footer

Method For Drawing Circles In A Pyramid Pattern

I want to draw circular balls on an HTML canvas in a pyramid pattern. Like this: Fiddle where you can show me the algorithm: https://jsfiddle.net/ofxmr17c/3/ I have Ball class wi

Solution 1:

The live demo below shows how to pack an arbitrary number of balls into a pyramid using a bit of trigonometry. To change the amount of layers in the pyramid (and thus the number of balls), edit the NUM_ROWS variable.

This is how it looks when it's done:

Circles packed in a pyramid

Live Demo:

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;
}
<canvas id="canvas"></canvas>

JSFiddle Version: https://jsfiddle.net/ofxmr17c/6/


Solution 2:

A billiard pyramid like this is always made with some known facts:

  • Each row always contains one more ball than the previous
  • It's an equilateral equal angled (sp? in english?) triangle which means next row always starts offset 60°

So we can make a vector (everything else in a billiard game would very much involve vectors so why not! :) ) for the direction of the next row's start point like so:

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

Then the algorithm would be (driven by total number of balls):

  • Start with a max limit of 1 for first row
  • Plot balls until max limit for row is reached
    • Then, add one to the max limit
    • Reset row count
    • Move position to beginning of last row + vector
  • Repeat until number of balls is reached

Result:

result

Example

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>

If you want more flexibility in terms of rotation you can simply swap this line:

cx -= radius * 2;

with a vector perpendicular (calculation not shown) to the first vector so:

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

Post a Comment for "Method For Drawing Circles In A Pyramid Pattern"