Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
// The purpose of this program is to "pre-render" pixel
// perfect circles, so that they can be drawn from an image
// instead of using something like an arc function. The
// output image is a tilesheet that can be used in 2D pixel
// art style games.
// Originally I ran into an issue with common algorthithms
// (such as the midpoint circle algorithm) only supporting
// an increase in diameter of 2 pixels at a time. I wanted
// to capture single pixel size increases (thus doubling
// the number of frames and improving animation smoothness).
// This program does support that goal.
// This program was made from a mixture of these sources:
// https://en.wikipedia.org/wiki/Midpoint_circle_algorithm
// https://github.com/donatj/Circle-Generator
"use strict";
var maxDiameter = 64;
var firstFrameEmpty = true;
var frameCount = maxDiameter + (firstFrameEmpty ? 1 : 0);
var canvas = document.createElement('canvas');
canvas.width = maxDiameter * frameCount;
canvas.height = maxDiameter;
var context = canvas.getContext('2d');
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
var pixelData = imageData.data;
var makePixelIndexer = function(width){
return function(i,j){
var index = j*(width*4) + i*4;
//index points to the R chanel of pixel
//at column i and row j calculated from top left
return index;
};
}
var pixelIndexer = makePixelIndexer(canvas.width);
var drawPixel = function(x,y){
var idx = pixelIndexer(x,y);
pixelData[idx] = 255; //red
pixelData[idx+1] = 0; //green
pixelData[idx+2] = 255;//blue
pixelData[idx+3] = 255;//alpha
};
var distance = function( x, y ) {
return Math.sqrt((Math.pow(y, 2)) + Math.pow(x, 2));
};
var filled = function( x, y, radius ) {
return distance(x, y) <= radius;
};
for(var i=0; i<frameCount; i++) {
var diameter = i + (!firstFrameEmpty ? 1 : 0);;
var radius = parseFloat(diameter) / 2;
var maxblocks_x, maxblocks_y;
if( (radius * 2) % 2 == 0 ) {
maxblocks_x = Math.ceil(radius - 0.5) * 2 + 1;
maxblocks_y = Math.ceil(radius - 0.5) * 2 + 1;
} else {
maxblocks_x = Math.ceil(radius) * 2;
maxblocks_y = Math.ceil(radius) * 2;
}
for( var y = -maxblocks_y / 2 + 1; y <= maxblocks_y / 2 - 1; y++ ) {
for( var x = -maxblocks_x / 2 + 1; x <= maxblocks_x / 2 - 1; x++ ) {
var xfilled = filled(x, y, radius);
if(xfilled) {
var nudge = (radius * 2) % 2 == 0 ? 0.5 : 0;
drawPixel(
x + maxDiameter/2 - nudge + (i * maxDiameter),
y + maxDiameter/2 - nudge
);
}
}
}
}
context.putImageData(imageData,0,0);
console.log(canvas.toDataURL());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.