wrap image around a cylindrical cup using html 5 canvas and javascript - javascript

My goal is to wrap a image over a coffee mug virtually using HTML5 and javascript.
I have created a prototype:
http://jsfiddle.net/sandeepkum88/uamov2m7/
Currently I am cutting the image in strips of width 1px and then placing those strips on the bezier curve.
var img = new Image();
img.onload = start;
img.src = "http://in-sandeep.printvenue.org/test-images/mug-strap.png";
var pointer = 0;
function start() {
var iw = 198.97826;
var ih = img.height;
var x1 = 50;
var y1 = 40;
var x2 = 97;
var y2 = 60;
var x3 = 152;
var y3 = 40;
// calc unit value for t to calculate bezier curve
var unitT = 1 / iw;
// put image slices on the curve
for (var X = 0, t = 0; X < iw; X++, t+=unitT) {
var xTop = (1-t) * (1-t) * x1 + 2 * (1 - t) * t * x2 + t * t * x3;
var yTop = (1-t) * (1-t) * y1 + 2 * (1 - t) * t * y2 + t * t * y3;
ctx.drawImage(img, X + pointer, 0, 1, ih, xTop, yTop, 0.85, ih-188);
}
}
But I am not satisfied with the result.
Am I doing it wrong somewhere? Is there any better alternative.
What if top curve and bottom curve are different.
Is there a way to achieve this using transformation matrix?

Related

How to get fisheye algorithm better?

I want to make simple fisheye algorithm to make it look like screen lens. Here is simple javascript code for canvas.
var frame = context.getImageData(0, 0, canvas.width, canvas.height);
var source = new Uint8ClampedArray(frame.data);
context2.clearRect(0, 0, canvas.width, canvas.height);
const SIZE = 80;
for (var i = 0; i < frame.data.length; i += 4) {
var x = (i / 4) % frame.width;
var y = Math.floor(i / 4 / frame.width);
let mouseX = frame.width / 2;
let mouseY = frame.height / 2;
var dx = mouseX - x;
var dy = mouseY - y;
var dist = Math.sqrt(dx * dx + dy * dy);
var i2 = i;
if (dist <= SIZE) {
var x2 = Math.round(
mouseX - dx * Math.sin(((dist / SIZE) * Math.PI) / 2)
);
var y2 = Math.round(
mouseY - dy * Math.sin(((dist / SIZE) * Math.PI) / 2)
);
var i2 = (y2 * frame.width + x2) * 4;
}
frame.data[i] = source[i2];
frame.data[i + 1] = source[i2 + 1];
frame.data[i + 2] = source[i2 + 2];
frame.data[i + 3] = source[i2 + 3];
}
context2.putImageData(frame, 0, 0);
Here is the result video: https://youtu.be/9a4q_enf4p8
It works okay, but the center of image is very "aggressive". When some content appears in the middle, the distortion is very big. How can I make it smoother in the middle?
What you see are the source pixels magnified by the lens effect. They have hard edges, which are preserved because you copy the pixel values as such. What you are doing is known as "nearest-neighbor interpolation".
You will obtain a smoother result by "bilinear interpolation".
compute x2, y2 as floats, do not round them;
use the integer part and fetch the four pixels surrounding x2, y2;
use the fractional parts to compute a weighted average of the four pixels.
The formula is
(1-v) ((1-u) V00 + u V10) + v ((1-u) V01 + u V11)
where u, v are the fractional parts and the V are the pixel values (repeat for R, G and B).

Project an image onto an elliptic cylinder

I have a flat image that I want to transform so it looks like a projection on an elliptic cylinder.
I've found the great script from Blindman67 for a regular cylinder at https://stackoverflow.com/a/40999097/4273683
However I don't understand, how to change the script to get an elliptic result. Any idea?
Thanks a lot for help.
var createImage=function(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;}
var canvas = createImage(400,400);
var ctx = canvas.ctx;
document.body.appendChild(canvas)
ctx.clearRect(0,0,500,500)
var image = createImage(400,200);
image.ctx.font = "60px arial";
image.ctx.textAlign = "center";
image.ctx.fillStyle = "#7F5";
image.ctx.fillRect(0,0,image.width,image.height)
image.ctx.fillStyle = "white";
image.ctx.fillText("Wrap around",200,60)
image.ctx.fillText("Some images",200,140)
function draw(ang,tilt, perspective){
var step = 1/(Math.max(image.width,400));
for(var i = 0; i < 1; i += step){
var a = i * Math.PI;
var a1 = (i+ step*2) * Math.PI ;
var ix = i * image.width*1.2;
var iw = step * image.width*1.2;
a += ang * Math.PI * 2;
a1 += ang * Math.PI * 2;
a = Math.PI -a;
a1 = Math.PI -a1;
var x = canvas.width * 0.5;
var y = canvas.height * 0.1;
var x1 = x + Math.cos(a1) * 110;
var y1 = y + Math.sin(a) * tilt;
x += Math.cos(a) * 110;
y += Math.sin(a) * tilt;
var s = Math.sin(a);
var s1 = Math.sin(a1);
if(s > 0 || s1 > 0){
ctx.drawImage(image,ix,0,iw,image.height,x1,y- s * perspective*0.5,x-x1,200 + s * perspective)
}
}
}
var w = canvas.width;
var h = canvas.height;
// main update function
function update(timer){
ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.globalAlpha = 1; // reset alpha
ctx.fillStyle = "black"
ctx.fillRect(0,0,w,h);
draw(timer / 2000, 40,30)
requestAnimationFrame(update);
}
requestAnimationFrame(update);

3d to 2d Projection Algorithm (Perspective projection) - Javascript

I am looking for the algorithm that takes 3D coordinates and change them to 2D coordinates.
I tried the steps form this Wikipedia Page : https://en.wikipedia.org/wiki/3D_projection#Perspective_projection
and my code so far is this :
var WIDTH = 1280/2;
var HEIGHT = 720/2;
// Distance from center of Canvas (Camera) with a Field of View of 90 digress, to the Canvas
var disToCanvas = Math.tan(45) * WIDTH/2;
var canvas = document.createElement('canvas');
canvas.width = WIDTH;
canvas.height = HEIGHT;
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
var Player = function (){ // Camera
// Camera Coordinates
this.x = 0;
this.y = 0;
this.z = 0;
// Camera Rotation (Angle)
this.rx = 0;
this.ry = 90;
this.rz = 0;
};
var player = new Player();
var Point = function (x, y ,z){
// Point 3D Coordinates
this.x = x;
this.y = y;
this.z = z;
// Point 2D Coordinates
this.X2d = 0;
this.Y2d = 0;
// The function that changes 3D coordinates to 2D
this.update = function (){
var X = (player.x - this.x);
var Y = (player.y - this.y);
var Z = (player.z - this.z);
var Cx = Math.cos(player.rx); // cos(θx)
var Cy = Math.cos(player.ry); // cos(θy)
var Cz = Math.cos(player.rz); // cos(θz)
var Sx = Math.sin(player.rx); // sin(θx)
var Sy = Math.sin(player.ry); // sin(θy)
var Sz = Math.sin(player.rz); // sin(θz)
var Dx = Cy * (Sy*Y + Cz*X) - Sy*Z;
var Dy = Sx * (Cy*Z + Sy * (Sz*Y + Cz*X)) + Cx * (Cy*Y + Sz*X);
var Dz = Cx * (Cy*Z + Sy * (Sz*Y + Cz*X)) - Sx * (Cy*Y + Sz*X);
var Ex = this.x / this.z * disToCanvas; // This isn't 100% correct
var Ey = this.y / this.z * disToCanvas; // This isn't 100% correct
var Ez = disToCanvas; // This isn't 100% correct
this.X2d = Ez/Dz * Dx - Ex + WIDTH/2; // Adding WIDTH/2 to center the camera
this.Y2d = Ez/Dz * Dy - Ez + HEIGHT/2; // Adding HEIGHT/2 to center the camera
}
}
// CREATING, UPDATING AND RENDERING A SQUARE
var point = [];
point[0] = new Point(10, 10, 10);
point[1] = new Point(20, 10, 10);
point[2] = new Point(20, 20, 10);
point[3] = new Point(10, 20, 10);
var run = setInterval(function (){
for (key in point){
point[key].update();
}
ctx.beginPath();
ctx.moveTo(point[0].X2d, point[0].Y2d);
ctx.lineTo(point[1].X2d, point[1].Y2d);
ctx.lineTo(point[2].X2d, point[2].Y2d);
ctx.lineTo(point[3].X2d, point[3].Y2d);
ctx.lineTo(point[0].X2d, point[0].Y2d);
}, 1000/30);
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background: rgba(73,72,62,.99);
}
canvas {
position: absolute;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
outline: 1px solid white;
}
<html>
<head>
</head>
<body>
</body>
</html>
I want a function that can translate 3D to 2D depending on both position and rotation of the camera.
Taking a look at the Wikipedia page you linked it appears that you have errors in your equations for D. It should be:
var Dx = Cy * (Sz*Y + Cz*X) - Sy*Z;
var Dy = Sx * (Cy*Z + Sy * (Sz*Y + Cz*X)) + Cx * (Cz*Y + Sz*X);
var Dz = Cx * (Cy*Z + Sy * (Sz*Y + Cz*X)) - Sx * (Cz*Y + Sz*X);
Also I think you are using the wrong coordinates for E, which is "the viewer's position relative to the display surface" and should not depend on the coordinates of the point.
The y coordinate of your 2D position appears to contain an error too; you use Ez instead of Ey.
Additionally i can recommend this site. It is written for C++ and OpenGL, but it contains a lot of good explanations and diagrams to get a better understanding of what it is you are trying to do.

How to get bezier curve size in HTML5 canvas with cp2 point?

I want to get the rendered size (width/height) of a bézier curve in HTML5 canvas
context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
with this code, for instance
// control points
var cp1x = 200,
cp1y = 150,
cp2x = 260,
cp2y = 10;
var x = 0,
y = 0;
// calculation
var curveWidth = cp1x > x ? cp1x - x : x - cp1x,
curveHeight = cp1y > y ? cp1y - y : y - cp1y;
However, the cp2 point can increase the curve distance (length, size). I.e., suppose cp2 point is the red point in this image and its x coordinate is bigger than cp1's x, which looks to be the end point of the bézier curve:
So, how can I consider the length of cp2 point in curveWidth and in curveHeight to be exact?
To get extent of a quadratic bezier
The points
var x1 = ? // start
var y1 = ?
var x2 = ? // control
var y2 = ?
var x3 = ? // end
var y3 = ?
The extent
extent = {
minx : null,
miny : null,
maxx : null,
maxy : null,
}
The Math.
These equation apply for the x and y axis (thus two equations)
For quadratic bezier
F(u) = a(1-u)^2+2b(1-u)u+cu^2
which is more familiar in the form of a quadratic equation
Ax^2 + Bx + C = 0
so the bezier rearranged
F(u) = (a-2b+c)u^2+2(-a+b)u+a
We need the derivative so that becomes
2(a-2b+c)u-2a+2b
simplify divide common factor 2 to give
(a-2b+c)u + b - a = 0
separate out u
b-a = (a-2b + c)u
(b-a) / (a - 2b + c) = u
Then algorithm optimised for the fact the (b-a) part of (a-2b-c)
function solveB2(a,b,c){
var ba = b-a;
return ba / (ba - (c-b)); // the position on the curve of the maxima
}
var ux = solveB2(x1,x2,x3);
var uy = solveB2(y1,y2,y3);
These two values are positions along the curve so we now just have to find the coordinates of these two points. We need a function that finds a point on a quadratic bezier
function findPoint(u,x1,y1,x2,y2,x3,y3){ // returns array with x, and y at u
var xx1 = x1 + (x2 - x1) * u;
var yy1 = y1 + (y2 - y1) * u;
var xx2 = x2 + (x3 - x2) * u;
var yy2 = y2 + (y3 - y2) * u;
return [
xx1 + (xx2 - xx1) * u,
yy1 + (yy2 - yy1) * u
]
}
First check that they are on the curve and find the point at ux,uy
if(ux >= 0 && ux <= 1){
var px = findPoint(ux,x1,y1,x2,y2,x3,y3);
}
if(uy >= 0 && uy <= 1){
var py = findPoint(uy,x1,y1,x2,y2,x3,y3);
}
Now test against the extent
extent.minx = Math.min(x1,x3,px[0],py[0]);
extent.miny = Math.min(y1,y3,px[1],py[1]);
extent.maxx = Math.max(x1,x3,px[0],py[0]);
extent.maxy = Math.max(y1,y3,px[1],py[1]);
And you are done
extent has the coordinates of the box around the bezier. Top left (min) and bottom right (max)
You can also get the minimum bounding box if you rotate the bezier so that the start and end points fall on the x axis. Then do the above and the resulting rectangle is the minimum sized rectangle that can be placed around the bezier.
Cubics are much the same but just a lot more typing.
And a demo, just to make sure I got it all correct.
var canvas = document.createElement("canvas");
canvas.width = 800;
canvas.height = 400;
var ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
var x1,y1,x2,y2,x3,y3;
var ux,uy,px,py;
var extent = {
minx : null,
miny : null,
maxx : null,
maxy : null,
}
function solveB2(a,b,c){
var ba = b-a;
return ba / (ba - (c-b)); // the position on the curve of the maxima
}
function findPoint(u,x1,y1,x2,y2,x3,y3){ // returns array with x, and y at u
var xx1 = x1 + (x2 - x1) * u;
var yy1 = y1 + (y2 - y1) * u;
var xx2 = x2 + (x3 - x2) * u;
var yy2 = y2 + (y3 - y2) * u;
return [
xx1 + (xx2 - xx1) * u,
yy1 + (yy2 - yy1) * u
]
}
function update(time){
ctx.clearRect(0,0,800,400);
// create random bezier
x1 = Math.cos(time / 1000) * 300 + 400;
y1 = Math.sin(time / 2100) * 150 + 200;
x2 = Math.cos((time + 3000) / 1200) * 300 + 400;
y2 = Math.sin(time / 2300) * 150 + 200;
x3 = Math.cos(time / 1400) * 300 + 400;
y3 = Math.sin(time / 2500) * 150 + 200;
// solve for bounds
var ux = solveB2(x1,x2,x3);
var uy = solveB2(y1,y2,y3);
if(ux >= 0 && ux <= 1){
px = findPoint(ux,x1,y1,x2,y2,x3,y3);
}else{
px = [x1,y1]; // a bit of a cheat but saves having to put in extra conditions
}
if(uy >= 0 && uy <= 1){
py = findPoint(uy,x1,y1,x2,y2,x3,y3);
}else{
py = [x3,y3]; // a bit of a cheat but saves having to put in extra conditions
}
extent.minx = Math.min(x1,x3,px[0],py[0]);
extent.miny = Math.min(y1,y3,px[1],py[1]);
extent.maxx = Math.max(x1,x3,px[0],py[0]);
extent.maxy = Math.max(y1,y3,px[1],py[1]);
// draw the rectangle
ctx.strokeStyle = "red";
ctx.lineWidth = 2;
ctx.strokeRect(extent.minx,extent.miny,extent.maxx-extent.minx,extent.maxy-extent.miny);
ctx.fillStyle = "rgba(255,200,0,0.2)";
ctx.fillRect(extent.minx,extent.miny,extent.maxx-extent.minx,extent.maxy-extent.miny);
// show points
ctx.fillStyle = "blue";
ctx.fillRect(x1-3,y1-3,6,6);
ctx.fillRect(x3-3,y3-3,6,6);
ctx.fillStyle = "black";
ctx.fillRect(px[0]-4,px[1]-4,8,8);
ctx.fillRect(py[0]-4,py[1]-4,8,8);
ctx.lineWidth = 3;
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.quadraticCurveTo(x2,y2,x3,y3);
ctx.stroke();
// control point
ctx.lineWidth = 1;
ctx.strokeStyle = "#0a0";
ctx.strokeRect(x2-3,y2-3,6,6);
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y2);
ctx.lineTo(x3,y3);
ctx.stroke();
// do it all again
requestAnimationFrame(update);
}
requestAnimationFrame(update);
UPDATE
While musing over the bezier I realised that I could remove a lot of code if I assumed that the bezier was normalised (the end points start at (0,0) and end at (1,1)) because the zeros can be removed and the ones simplified.
While changing the code I also realized that I had needlessly calculated the x and y for both the x and y extent coordinates. Giving 4 values while I only need 2.
The resulting code is much simpler. I remove the function solveB2 and findPoint as the calculations seam too trivial to bother with functions.
To find the x and y maxima from quadratic bezier defined with x1, y1, x2, y2, x3, y3
// solve quadratic for bounds by normalizing equation
var brx = x3 - x1; // get x range
var bx = x2 - x1; // get x control point offset
var x = bx / brx; // normalise control point which is used to check if maxima is in range
// do the same for the y points
var bry = y3 - y1;
var by = y2 - y1
var y = by / bry;
var px = [x1,y1]; // set defaults in case maximas outside range
if(x < 0 || x > 1){ // check if x maxima is on the curve
px[0] = bx * bx / (2 * bx - brx) + x1; // get the x maxima
}
if(y < 0 || y > 1){ // same as x
px[1] = by * by / (2 * by - bry) + y1;
}
// now only need to check the x and y maxima not the coordinates of the maxima
extent.minx = Math.min(x1,x3,px[0]);
extent.miny = Math.min(y1,y3,px[1]);
extent.maxx = Math.max(x1,x3,px[0]);
extent.maxy = Math.max(y1,y3,px[1]);
And the example code which has far better performance but unlike the previous demo this version does not calculate the actual coordinates of the x and y maximas.
var canvas = document.createElement("canvas");
canvas.width = 800;
canvas.height = 400;
var ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
var x1,y1,x2,y2,x3,y3;
var ux,uy,px,py;
var extent = {
minx : null,
miny : null,
maxx : null,
maxy : null,
}
function update(time){
ctx.clearRect(0,0,800,400);
// create random bezier
x1 = Math.cos(time / 1000) * 300 + 400;
y1 = Math.sin(time / 2100) * 150 + 200;
x2 = Math.cos((time + 3000) / 1200) * 300 + 400;
y2 = Math.sin(time / 2300) * 150 + 200;
x3 = Math.cos(time / 1400) * 300 + 400;
y3 = Math.sin(time / 2500) * 150 + 200;
// solve quadratic for bounds by normalizing equation
var brx = x3 - x1; // get x range
var bx = x2 - x1; // get x control point offset
var x = bx / brx; // normalise control point which is used to check if maxima is in range
// do the same for the y points
var bry = y3 - y1;
var by = y2 - y1
var y = by / bry;
var px = [x1,y1]; // set defaults in case maximas outside range
if(x < 0 || x > 1){ // check if x maxima is on the curve
px[0] = bx * bx / (2 * bx - brx) + x1; // get the x maxima
}
if(y < 0 || y > 1){ // same as x
px[1] = by * by / (2 * by - bry) + y1;
}
// now only need to check the x and y maxima not the coordinates of the maxima
extent.minx = Math.min(x1,x3,px[0]);
extent.miny = Math.min(y1,y3,px[1]);
extent.maxx = Math.max(x1,x3,px[0]);
extent.maxy = Math.max(y1,y3,px[1]);
// draw the rectangle
ctx.strokeStyle = "red";
ctx.lineWidth = 2;
ctx.strokeRect(extent.minx,extent.miny,extent.maxx-extent.minx,extent.maxy-extent.miny);
ctx.fillStyle = "rgba(255,200,0,0.2)";
ctx.fillRect(extent.minx,extent.miny,extent.maxx-extent.minx,extent.maxy-extent.miny);
// show points
ctx.fillStyle = "blue";
ctx.fillRect(x1-3,y1-3,6,6);
ctx.fillRect(x3-3,y3-3,6,6);
ctx.fillStyle = "black";
ctx.fillRect(px[0]-4,px[1]-4,8,8);
ctx.lineWidth = 3;
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.quadraticCurveTo(x2,y2,x3,y3);
ctx.stroke();
// control point
ctx.lineWidth = 1;
ctx.strokeStyle = "#0a0";
ctx.strokeRect(x2-3,y2-3,6,6);
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y2);
ctx.lineTo(x3,y3);
ctx.stroke();
// do it all again
requestAnimationFrame(update);
}
requestAnimationFrame(update);

How can I generate a rainbow circle using HTML5 canvas?

I would like to generate a canvas image using gradients in some clever way. I would like the image to looks something like this:
I just can't get my head around it. I need to generate lines in the form and arc - or use gradients with color stops in some clever way. Maybe it would be a lot easier if I converted to HSL and just go through the HUE values?
For example in a rectangle format I could
for (var i = 0; i < h; ++i) {
var ratio = i/h;
var hue = Math.floor(360*ratio);
var sat = 100;
var lum = 50;
line(dc, hslColor(hue,sat,lum), left_margin, top_margin+i, left_margin+w, top_margin+i);
}
Does anybody have any clever tips on how to produce this image using canvas?
This is not perfect (due to drawing steps ...), but it can help you :
http://jsfiddle.net/afkLY/2/
HTML:
<canvas id="colors" width="200" height="200"></canvas>
Javascript:
var canvas = document.getElementById("colors");
var graphics = canvas.getContext("2d");
var CX = canvas.width / 2,
CY = canvas.height/ 2,
sx = CX,
sy = CY;
for(var i = 0; i < 360; i+=0.1){
var rad = i * (2*Math.PI) / 360;
graphics.strokeStyle = "hsla("+i+", 100%, 50%, 1.0)";
graphics.beginPath();
graphics.moveTo(CX, CY);
graphics.lineTo(CX + sx * Math.cos(rad), CY + sy * Math.sin(rad));
graphics.stroke();
}
The idea is to draw the disc line by line with a hue value corresponding to the line direction.
You can change the color base rotation by adding a radius angle to rad variable (adding -pi/2 to rad would make the gradient look like your figure).
EDIT:
I made a new demo that generalizes the concept a bit and renders a rainbow polygon. Here is the CodePen.
To get rid of the small voids beteween the colors, I used quads that overflow to the next color part, except for the last one.
Small adjustment to make it have a white center
var canvas = document.getElementById('colorPicker');
var graphics = canvas.getContext("2d");
var CX = canvas.width / 2,
CY = canvas.height / 2,
sx = CX,
sy = CY;
for (var i = 0; i < 360; i += 0.1) {
var rad = i * (2 * Math.PI) / 360;
var grad = graphics.createLinearGradient(CX, CY, CX + sx * Math.cos(rad), CY + sy * Math.sin(rad));
grad.addColorStop(0, "white");
grad.addColorStop(0.01, "white");
grad.addColorStop(0.99, "hsla(" + i + ", 100%, 50%, 1.0)");
grad.addColorStop(1, "hsla(" + i + ", 100%, 50%, 1.0)");
graphics.strokeStyle = grad;
graphics.beginPath();
graphics.moveTo(CX, CY);
graphics.lineTo(CX + sx * Math.cos(rad), CY + sy * Math.sin(rad));
graphics.stroke();
}
Here is an alternate approach that takes a slightly more functional approach:
var canvas = document.getElementById("radial"),
ctx = canvas.getContext("2d"),
width = canvas.width,
height = canvas.height,
center = { x: width/2, y: height/2 },
diameter = Math.min(width, height);
var distanceBetween = function(x1,y1,x2,y2) {
// Get deltas
var deltaX = x2 - x1,
deltaY = y2 - y1;
// Calculate distance from center
return Math.sqrt(deltaX*deltaX+deltaY*deltaY);
}
var angleBetween = function(x1,y1,x2,y2) {
// Get deltas
var deltaX = x2 - x1,
deltaY = y2 - y1;
// Calculate angle
return Math.atan2(deltaY, deltaX);
}
var radiansToDegrees = _.memoize(function(radians) {
// Put in range of [0,2PI)
if (radians < 0) radians += Math.PI * 2;
// convert to degrees
return radians * 180 / Math.PI;
})
// Partial application of center (x,y)
var distanceFromCenter = _.bind(distanceBetween, undefined, center.x, center.y)
var angleFromCenter = _.bind(angleBetween, undefined, center.x, center.y)
// Color formatters
var hslFormatter = function(h,s,l) { return "hsl("+h+","+s+"%,"+l+"%)"; },
fromHue = function(h) { return hslFormatter(h,100,50); };
// (x,y) => color
var getColor = function(x,y) {
// If distance is greater than radius, return black
return (distanceFromCenter(x,y) > diameter/2)
// Return black
? "#000"
// Determine color
: fromHue(radiansToDegrees(angleFromCenter(x,y)));
};
for(var y=0;y<height;y++) {
for(var x=0;x<width;x++) {
ctx.fillStyle = getColor(x,y);
ctx.fillRect( x, y, 1, 1 );
}
}
It uses a function to calculate the color at each pixel – not the most efficient implementation, but perhaps you'll glean something useful from it.
Note it uses underscore for some helper functions like bind() – for partial applications – and memoize.
Codepen for experimentation.

Categories