Stretch image to fit polygon html5 canvas - javascript

I have a square image like this:
I am trying to stretch this image into a polygon like this:
So far I have been able to create a polygon on the canvas as the above image using the following javascript:
function drawCanvas() {
var c2 = document.getElementById('myCanvas6').getContext('2d');
var img = document.getElementById("scream");
c2.fillStyle = '#000';
c2.beginPath();
c2.moveTo(20, 20);
c2.lineTo(320, 50);
c2.lineTo(320, 170);
c2.lineTo(20, 200);
//c2.drawImage(img, 150, 10, img.width, img.height);
c2.closePath();
c2.fill();
}
I tried using drawImage() method, but it does not stretch the points A, B, C, D to the new positions. Is there anyway this can be achieved?

The 2D canvas is called 2D for a very good reason. You can not transform a square such that any of its side converge (are not parallel) hence 2D
But where there is a need there is always a way..
You can do it by cutting the image into slices and then draw each slice slightly smaller than the last.
We humans don't like to see an image distort when it converges, so you need to add the distortion we expect, perspective. The further away the object the smaller the distance between points appears to the eye.
So the function below draws an image with the top and bottom edges converging..
It is not true 3D but it does make the image appear as distorted as jus converging the top and bottom without decreasing the y step. The animation introduced a bit of an optical illusion. the second render shortens the image to make it appear a little less fake.
See the code on how to use the function.
/** CreateImage.js begin **/
// creates a blank image with 2d context
var createImage=function(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;}
/** CreateImage.js end **/
var can = createImage(512,512);
document.body.appendChild(can);
var ctx = can.ctx;
const textToDisplay = "Perspective"
const textSize = 80;
ctx.font = textSize+"px arial";
var w = ctx.measureText(textToDisplay).width + 8;
var text = createImage(w + 64,textSize + 32);
text.ctx.fillStyle = "#08F";
text.ctx.strokeStyle = "black";
text.ctx.lineWidth = 16;
text.ctx.fillRect(0,0,text.width,text.height);
text.ctx.strokeRect(0,0,text.width,text.height);
text.ctx.font = textSize+"px arial";
text.ctx.fillStyle = "#F80";
text.ctx.strokeStyle = "Black";
text.ctx.lineWidth = 4;
text.ctx.strokeText(textToDisplay,38,textSize + 8);
text.ctx.fillText(textToDisplay,38,textSize + 8);
// Not quite 3D
// ctx is the context to draw to
// image is the image to draw
// x1,x2 left and right edges of the image
// zz1,zz2 top offset for left and right
// image top edge has a slops from zz1 to zz2
// yy if the position to draw top. This is where the top would be if z = 0
function drawPerspective(ctx, image, x1, zz1, x2, zz2, yy){
var x, w, h, h2,slop, topLeft, botLeft, zDistR, zDistL, lines, ty;
w = image.width; // image size
h = image.height;
h2 = h /2; // half height
slop = (zz2 - zz1) / (x2 - x1); // Slope of top edge
z1 = h2 - zz1; // Distance (z) to first line
z2 = (z1 / (h2 - zz2)) * z1 - z1; // distance (z) between first and last line
if(z2 === 0){ // if no differance in z then is square to camera
topLeft = - x1 * slop + zz1; // get scan line top left edge
ctx.drawImage(image,0, 0, w, h,x1, topLeft + yy ,x2-x1, h - topLeft * 2) // render to desination
return;
}
// render each display line getting all pixels that will be on that line
for (x = x1; x < x2; x++) { // for each line horizontal line
topLeft = (x - x1) * slop + zz1; // get scan line top left edge
botLeft = ((x + 1) - x1) * slop + zz1; // get scan line bottom left edge
zDistL = (z1 / (h2 - topLeft)) * z1; // get Z distance to Left of this line
zDistR = (z1 / (h2 - botLeft)) * z1; // get Z distance to right of this line
ty = ((zDistL - z1) / z2) * w; // get y bitmap coord
lines = ((zDistR - z1) / z2) * w - ty;// get number of lines to copy
ctx.drawImage(image,
ty % w, 0, lines, h, // get the source location of pixel
x, topLeft + yy,1 , h - topLeft * 2 // render to desination
);
}
}
var animTick = 0;
var animRate = 0.01;
var pos = 0;
var short = 0;
function update1(){
animTick += animRate;
pos = Math.sin(animTick) * 20 + 20;
short = Math.cos((pos / 40) * Math.PI) * text.width * 0.12 - text.width * 0.12;
ctx.clearRect(0,0,can.width,can.height)
drawPerspective(ctx,text,0,0,text.width,pos,20)
drawPerspective(ctx,text,0,0,text.width+short,pos,textSize + 32 + 30)
requestAnimationFrame(update1);
}
update1();

I think this is a good solution for you: http://jsfiddle.net/fQk4h/
Here is the magic:
for (i = 0; i < w; i++) {
dy = (leftTop * (w - i)) / w;
dh = (leftBot * (w - i) + h * i) / w;
ctx.drawImage(tmpCtx.canvas,
i, 0, 1, h,
i, dy, 1, dh);
}
ctx.restore();

Related

How to draw herringbone pattern on html canvas

I Have to draw Herringbone pattern on canvas and fill with image
some one please help me I am new to canvas 2d drawing.
I need to draw mixed tiles with cross pattern (Herringbone)
var canvas = this.__canvas = new fabric.Canvas('canvas');
var canvas_objects = canvas._objects;
// create a rectangle with a fill and a different color stroke
var left = 150;
var top = 150;
var x=20;
var y=40;
var rect = new fabric.Rect({
left: left,
top: top,
width: x,
height: y,
angle:45,
fill: 'rgba(255,127,39,1)',
stroke: 'rgba(34,177,76,1)',
strokeWidth:0,
originX:'right',
originY:'top',
centeredRotation: false
});
canvas.add(rect);
for(var i=0;i<15;i++){
var rectangle = fabric.util.object.clone(getLastobject());
if(i%2==0){
rectangle.left = rectangle.oCoords.tr.x;
rectangle.top = rectangle.oCoords.tr.y;
rectangle.originX='right';
rectangle.originY='top';
rectangle.angle =-45;
}else{
fabric.log('rectangle: ', rectangle.toJSON());
rectangle.left = rectangle.oCoords.tl.x;
rectangle.top = rectangle.oCoords.tl.y;
fabric.log('rectangle: ', rectangle.toJSON());
rectangle.originX='left';
rectangle.originY='top';
rectangle.angle =45;
}
//rectangle.angle -90;
canvas.add(rectangle);
}
fabric.log('rectangle: ', canvas.toJSON());
canvas.renderAll();
function getLastobject(){
var last = null;
if(canvas_objects.length !== 0){
last = canvas_objects[canvas_objects.length -1]; //Get last object
}
return last;
}
How to draw this pattern in canvas using svg or 2d,3d method. If any third party library that also Ok for me.
I don't know where to start and how to draw this complex pattern.
some one please help me to draw this pattern with rectangle fill with dynamic color on canvas.
Here is a sample of the output I need: (herringbone pattern)
I tried something similar using fabric.js library here is my JSFiddle
Trippy disco flooring
To get the pattern you need to draw rectangles one horizontal tiled one space left or right for each row down and the same for the vertical rectangle.
The rectangle has an aspect of width 2 time height.
Drawing the pattern is simple.
Rotating is easy as well the harder part is finding where to draw the tiles for the rotation.
To do that I create a inverse matrix of the rotation (it reverses a rotation). I then apply that rotation to the 4 corners of the canvas 0,0, width,0 width,height and 0,height this gives me 4 points in the rotated space that are at the edges of the canvas.
As I draw the tiles from left to right top to bottom I find the min corners for the top left, and the max corners for the bottom right, expand it out a little so I dont miss any pixels and draw the tiles with a transformation set the the rotation.
As I could not workout what angle you wanted it at the function will draw it at any angle. On is animated, the other is at 60deg clockwise.
Warning demo contains flashing content.
Update The flashing was way to out there, so have made a few changes, now colours are a more pleasing blend and have fixed absolute positions, and have tied the tile origin to the mouse position, clicking the mouse button will cycle through some sizes as well.
const ctx = canvas.getContext("2d");
const colours = []
for(let i = 0; i < 1; i += 1/80){
colours.push(`hsl(${Math.floor(i * 360)},${Math.floor((Math.sin(i * Math.PI *4)+1) * 50)}%,${Math.floor(Math.sin(i * Math.PI *8)* 25 + 50)}%)`)
}
const sizes = [0.04,0.08,0.1,0.2];
var currentSize = 0;
const origin = {x : canvas.width / 2, y : canvas.height / 2};
var size = Math.min(canvas.width * 0.2, canvas.height * 0.2);
function drawPattern(size,origin,ang){
const xAx = Math.cos(ang); // define the direction of xAxis
const xAy = Math.sin(ang);
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.setTransform(xAx,xAy,-xAy,xAx,origin.x,origin.y);
function getExtent(xAx,xAy,origin){
const im = [1,0,0,1]; // inverse matrix
const dot = xAx * xAx + xAy * xAy;
im[0] = xAx / dot;
im[1] = -xAy / dot;
im[2] = xAy / dot;
im[3] = xAx / dot;
const toWorld = (x,y) => {
var point = {};
var xx = x - origin.x;
var yy = y - origin.y;
point.x = xx * im[0] + yy * im[2];
point.y = xx * im[1] + yy * im[3];
return point;
}
return [
toWorld(0,0),
toWorld(canvas.width,0),
toWorld(canvas.width,canvas.height),
toWorld(0,canvas.height),
]
}
const corners = getExtent(xAx,xAy,origin);
var startX = Math.min(corners[0].x,corners[1].x,corners[2].x,corners[3].x);
var endX = Math.max(corners[0].x,corners[1].x,corners[2].x,corners[3].x);
var startY = Math.min(corners[0].y,corners[1].y,corners[2].y,corners[3].y);
var endY = Math.max(corners[0].y,corners[1].y,corners[2].y,corners[3].y);
startX = Math.floor(startX / size) - 2;
endX = Math.floor(endX / size) + 2;
startY = Math.floor(startY / size) - 2;
endY = Math.floor(endY / size) + 2;
// draw the pattern
ctx.lineWidth = size * 0.1;
ctx.lineJoin = "round";
ctx.strokeStyle = "black";
var colourIndex = 0;
for(var y = startY; y <endY; y+=1){
for(var x = startX; x <endX; x+=1){
if((x + y) % 4 === 0){
colourIndex = Math.floor(Math.abs(Math.sin(x)*size + Math.sin(y) * 20));
ctx.fillStyle = colours[(colourIndex++)% colours.length];
ctx.fillRect(x * size,y * size,size * 2,size);
ctx.strokeRect(x * size,y * size,size * 2,size);
x += 2;
ctx.fillStyle = colours[(colourIndex++)% colours.length];
ctx.fillRect(x * size,y * size, size, size * 2);
ctx.strokeRect(x * size,y * size, size, size * 2);
x += 1;
}
}
}
}
// Animate it all
var update = true; // flag to indecate something needs updating
function mainLoop(time){
// if window size has changed update canvas to new size
if(canvas.width !== innerWidth || canvas.height !== innerHeight || update){
canvas.width = innerWidth;
canvas.height = innerHeight
origin.x = canvas.width / 2;
origin.y = canvas.height / 2;
size = Math.min(canvas.width, canvas.height) * sizes[currentSize % sizes.length];
update = false;
}
if(mouse.buttonRaw !== 0){
mouse.buttonRaw = 0;
currentSize += 1;
update = true;
}
// draw the patter
drawPattern(size,mouse,time/2000);
requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
mouse = (function () {
function preventDefault(e) { e.preventDefault() }
var m; // alias for mouse
var mouse = {
x : 0, y : 0, // mouse position
buttonRaw : 0,
over : false, // true if mouse over the element
buttonOnMasks : [0b1, 0b10, 0b100], // mouse button on masks
buttonOffMasks : [0b110, 0b101, 0b011], // mouse button off masks
bounds : null,
eventNames : "mousemove,mousedown,mouseup,mouseout,mouseover".split(","),
event(e) {
var t = e.type;
m.bounds = m.element.getBoundingClientRect();
m.x = e.pageX - m.bounds.left - scrollX;
m.y = e.pageY - m.bounds.top - scrollY;
if (t === "mousedown") { m.buttonRaw |= m.buttonOnMasks[e.which - 1] }
else if (t === "mouseup") { m.buttonRaw &= m.buttonOffMasks[e.which - 1] }
else if (t === "mouseout") { m.over = false }
else if (t === "mouseover") { m.over = true }
e.preventDefault();
},
start(element) {
if (m.element !== undefined) { m.remove() }
m.element = element === undefined ? document : element;
m.eventNames.forEach(name => document.addEventListener(name, mouse.event) );
document.addEventListener("contextmenu", preventDefault, false);
},
}
m = mouse;
return mouse;
})();
mouse.start(canvas);
canvas {
position : absolute;
top : 0px;
left : 0px;
}
<canvas id=canvas></canvas>
Un-animated version at 60Deg
const ctx = canvas.getContext("2d");
const colours = ["red","green","yellow","orange","blue","cyan","magenta"]
const origin = {x : canvas.width / 2, y : canvas.height / 2};
var size = Math.min(canvas.width * 0.2, canvas.height * 0.2);
function drawPattern(size,origin,ang){
const xAx = Math.cos(ang); // define the direction of xAxis
const xAy = Math.sin(ang);
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.setTransform(xAx,xAy,-xAy,xAx,origin.x,origin.y);
function getExtent(xAx,xAy,origin){
const im = [1,0,0,1]; // inverse matrix
const dot = xAx * xAx + xAy * xAy;
im[0] = xAx / dot;
im[1] = -xAy / dot;
im[2] = xAy / dot;
im[3] = xAx / dot;
const toWorld = (x,y) => {
var point = {};
var xx = x - origin.x;
var yy = y - origin.y;
point.x = xx * im[0] + yy * im[2];
point.y = xx * im[1] + yy * im[3];
return point;
}
return [
toWorld(0,0),
toWorld(canvas.width,0),
toWorld(canvas.width,canvas.height),
toWorld(0,canvas.height),
]
}
const corners = getExtent(xAx,xAy,origin);
var startX = Math.min(corners[0].x,corners[1].x,corners[2].x,corners[3].x);
var endX = Math.max(corners[0].x,corners[1].x,corners[2].x,corners[3].x);
var startY = Math.min(corners[0].y,corners[1].y,corners[2].y,corners[3].y);
var endY = Math.max(corners[0].y,corners[1].y,corners[2].y,corners[3].y);
startX = Math.floor(startX / size) - 4;
endX = Math.floor(endX / size) + 4;
startY = Math.floor(startY / size) - 4;
endY = Math.floor(endY / size) + 4;
// draw the pattern
ctx.lineWidth = 5;
ctx.lineJoin = "round";
ctx.strokeStyle = "black";
for(var y = startY; y <endY; y+=1){
for(var x = startX; x <endX; x+=1){
ctx.fillStyle = colours[Math.floor(Math.random() * colours.length)];
if((x + y) % 4 === 0){
ctx.fillRect(x * size,y * size,size * 2,size);
ctx.strokeRect(x * size,y * size,size * 2,size);
x += 2;
ctx.fillStyle = colours[Math.floor(Math.random() * colours.length)];
ctx.fillRect(x * size,y * size, size, size * 2);
ctx.strokeRect(x * size,y * size, size, size * 2);
x += 1;
}
}
}
}
canvas.width = innerWidth;
canvas.height = innerHeight
origin.x = canvas.width / 2;
origin.y = canvas.height / 2;
size = Math.min(canvas.width * 0.2, canvas.height * 0.2);
drawPattern(size,origin,Math.PI / 3);
canvas {
position : absolute;
top : 0px;
left : 0px;
}
<canvas id=canvas></canvas>
The best way to approach this is to examine the pattern and analyse its symmetry and how it repeats.
You can look at this several ways. For example, you could rotate the patter 45 degrees so that the tiles are plain orthogonal rectangles. But let's just look at it how it is. I am going to assume you are happy with it with 45deg tiles.
Like the tiles themselves, it turns out the pattern has a 2:1 ratio. If we repeat this pattern horizontally and vertically, we can fill the canvas with the completed pattern.
We can see there are five tiles that overlap with our pattern block. However we don't need to draw them all when we draw each pattern block. We can take advantage of the fact that blocks are repeated, and we can leave the drawing of some tiles to later rows and columns.
Let's assume we are drawing the pattern blocks from left to right and top to bottom. Which tiles do we need to draw, at a minimum, to ensure this pattern block gets completely drawn (taking into account adjacent pattern blocks)?
Since we will be starting at the top left (and moving right and downwards), we'll need to draw tile 2. That's because that tile won't get drawn by either the block below us, or the block to the right of us. The same applies to tile 3.
It turns out those two are all we'll need to draw for each pattern block. Tile 1 and 4 will be drawn when the pattern block below us draws their tile 2 and 3 respectively. Tile 5 will be drawn when the pattern block to the south-east of us draws their tile 1.
We just need to remember that we may need to draw an extra column on the right-hand side, and at the bottom, to ensure those end-of-row and end-of-column pattern blocks get completely drawn.
The last thing to work out is how big our pattern blocks are.
Let's call the short side of the tile a and the long side b. We know that b = 2 * a. And we can work out, using Pythagoras Theorem, that the height of the pattern block will be:
h = sqrt(a^2 + a^2)
= sqrt(2 * a^2)
= sqrt(2) * a
The width of the pattern block we can see will be w = 2 * h.
Now that we've worked out how to draw the pattern, let's implement our algorithm.
const a = 60;
const b = 120;
const h = 50 * Math.sqrt(2);
const w = h * 2;
const h2 = h / 2; // How far tile 1 sticks out to the left of the pattern block
// Set of colours for the tiles
const colours = ["red","cornsilk","black","limegreen","deepskyblue",
"mediumorchid", "lightgrey", "grey"]
const canvas = document.getElementById("herringbone");
const ctx = canvas.getContext("2d");
// Set a universal stroke colour and width
ctx.strokeStyle = "black";
ctx.lineWidth = 4;
// Loop through the pattern block rows
for (var y=0; y < (canvas.height + h); y+=h)
{
// Loop through the pattern block columns
for (var x=0; x < (canvas.width + w); x+=w)
{
// Draw tile "2"
// I'm just going to draw a path for simplicity, rather than
// worrying about drawing a rectangle with rotation and translates
ctx.beginPath();
ctx.moveTo(x - h2, y - h2);
ctx.lineTo(x, y - h);
ctx.lineTo(x + h, y);
ctx.lineTo(x + h2, y + h2);
ctx.closePath();
ctx.fillStyle = colours[Math.floor(Math.random() * colours.length)];
ctx.fill();
ctx.stroke();
// Draw tile "3"
ctx.beginPath();
ctx.moveTo(x + h2, y + h2);
ctx.lineTo(x + w - h2, y - h2);
ctx.lineTo(x + w, y);
ctx.lineTo(x + h, y + h);
ctx.closePath();
ctx.fillStyle = colours[Math.floor(Math.random() * colours.length)];
ctx.fill();
ctx.stroke();
}
}
<canvas id="herringbone" width="500" height="400"></canvas>

JavaScript Point Collision with Regular Hexagon

I'm making an HTML5 canvas hexagon grid based system and I need to be able to detect what hexagonal tile in a grid has been clicked when the canvas is clicked.
Several hours of searching and trying my own methods led to nothing, and porting implementations from other languages has simply confused me to a point where my brain is sluggish.
The grid consists of flat topped regular hexagons like in this diagram:
Essentially, given a point and the variables specified in this image as the sizing for every hexagon in the grid (R, W, S, H):
I need to be able to determine whether a point is inside a hexagon given.
An example function call would be pointInHexagon(hexX, hexY, R, W, S, H, pointX, pointY) where hexX and hexY are the coordinates for the top left corner of the bounding box of a hexagonal tile (like the top left corner in the image above).
Is there anyone who has any idea how to do this? Speed isn't much of a concern for the moment.
Simple & fast diagonal rectangle slice.
Looking at the other answers I see that they have all a little over complicated the problem. The following is an order of magnitude quicker than the accepted answer and does not require any complicated data structures, iterators, or generate dead memory and unneeded GC hits. It returns the hex cell row and column for any related set of R, H, S or W. The example uses R = 50.
Part of the problem is finding which side of a rectangle a point is if the rectangle is split diagonally. This is a very simple calculation and is done by normalising the position of the point to test.
Slice any rectangle diagonally
Example a rectangle of width w, and height h split from top left to bottom right. To find if a point is left or right. Assume top left of rectangle is at rx,ry
var x = ?;
var y = ?;
x = ((x - rx) % w) / w;
y = ((y - ry) % h) / h;
if (x > y) {
// point is in the upper right triangle
} else if (x < y) {
// point is in lower left triangle
} else {
// point is on the diagonal
}
If you want to change the direction of the diagonal then just invert one of the normals
x = 1 - x; // invert x or y to change the direction the rectangle is split
if (x > y) {
// point is in the upper left triangle
} else if (x < y) {
// point is in lower right triangle
} else {
// point is on the diagonal
}
Split into sub cells and use %
The rest of the problem is just a matter of splitting the grid into (R / 2) by (H / 2) cells width each hex covering 4 columns and 2 rows. Every 1st column out of 3 will have diagonals. with every second of these column having the diagonal flipped. For every 4th, 5th, and 6th column out of 6 have the row shifted down one cell. By using % you can very quickly determine which hex cell you are on. Using the diagonal split method above make the math easy and quick.
And one extra bit. The return argument retPos is optional. if you call the function as follows
var retPos;
mainLoop(){
retPos = getHex(mouse.x, mouse.y, retPos);
}
the code will not incur a GC hit, further improving the speed.
Pixel to Hex coordinates
From Question diagram returns hex cell x,y pos. Please note that this function only works in the range 0 <= x, 0 <= y if you need negative coordinates subtract the min negative pixel x,y coordinate from the input
// the values as set out in the question image
var r = 50;
var w = r * 2;
var h = Math.sqrt(3) * r;
// returns the hex grid x,y position in the object retPos.
// retPos is created if not supplied;
// argument x,y is pixel coordinate (for mouse or what ever you are looking to find)
function getHex (x, y, retPos){
if(retPos === undefined){
retPos = {};
}
var xa, ya, xpos, xx, yy, r2, h2;
r2 = r / 2;
h2 = h / 2;
xx = Math.floor(x / r2);
yy = Math.floor(y / h2);
xpos = Math.floor(xx / 3);
xx %= 6;
if (xx % 3 === 0) { // column with diagonals
xa = (x % r2) / r2; // to find the diagonals
ya = (y % h2) / h2;
if (yy % 2===0) {
ya = 1 - ya;
}
if (xx === 3) {
xa = 1 - xa;
}
if (xa > ya) {
retPos.x = xpos + (xx === 3 ? -1 : 0);
retPos.y = Math.floor(yy / 2);
return retPos;
}
retPos.x = xpos + (xx === 0 ? -1 : 0);
retPos.y = Math.floor((yy + 1) / 2);
return retPos;
}
if (xx < 3) {
retPos.x = xpos + (xx === 3 ? -1 : 0);
retPos.y = Math.floor(yy / 2);
return retPos;
}
retPos.x = xpos + (xx === 0 ? -1 : 0);
retPos.y = Math.floor((yy + 1) / 2);
return retPos;
}
Hex to pixel
And a helper function that draws a cell given the cell coordinates.
// Helper function draws a cell at hex coordinates cellx,celly
// fStyle is fill style
// sStyle is strock style;
// fStyle and sStyle are optional. Fill or stroke will only be made if style given
function drawCell1(cellPos, fStyle, sStyle){
var cell = [1,0, 3,0, 4,1, 3,2, 1,2, 0,1];
var r2 = r / 2;
var h2 = h / 2;
function drawCell(x, y){
var i = 0;
ctx.beginPath();
ctx.moveTo((x + cell[i++]) * r2, (y + cell[i++]) * h2)
while (i < cell.length) {
ctx.lineTo((x + cell[i++]) * r2, (y + cell[i++]) * h2)
}
ctx.closePath();
}
ctx.lineWidth = 2;
var cx = Math.floor(cellPos.x * 3);
var cy = Math.floor(cellPos.y * 2);
if(cellPos.x % 2 === 1){
cy -= 1;
}
drawCell(cx, cy);
if (fStyle !== undefined && fStyle !== null){ // fill hex is fStyle given
ctx.fillStyle = fStyle
ctx.fill();
}
if (sStyle !== undefined ){ // stroke hex is fStyle given
ctx.strokeStyle = sStyle
ctx.stroke();
}
}
I think you need something like this~
EDITED
I did some maths and here you have it. This is not a perfect version but probably will help you...
Ah, you only need a R parameter because based on it you can calculate H, W and S. That is what I understand from your description.
// setup canvas for demo
var canvas = document.getElementById('canvas');
canvas.width = 300;
canvas.height = 275;
var context = canvas.getContext('2d');
var hexPath;
var hex = {
x: 50,
y: 50,
R: 100
}
// Place holders for mouse x,y position
var mouseX = 0;
var mouseY = 0;
// Test for collision between an object and a point
function pointInHexagon(target, pointX, pointY) {
var side = Math.sqrt(target.R*target.R*3/4);
var startX = target.x
var baseX = startX + target.R / 2;
var endX = target.x + 2 * target.R;
var startY = target.y;
var baseY = startY + side;
var endY = startY + 2 * side;
var square = {
x: startX,
y: startY,
side: 2*side
}
hexPath = new Path2D();
hexPath.lineTo(baseX, startY);
hexPath.lineTo(baseX + target.R, startY);
hexPath.lineTo(endX, baseY);
hexPath.lineTo(baseX + target.R, endY);
hexPath.lineTo(baseX, endY);
hexPath.lineTo(startX, baseY);
if (pointX >= square.x && pointX <= (square.x + square.side) && pointY >= square.y && pointY <= (square.y + square.side)) {
var auxX = (pointX < target.R / 2) ? pointX : (pointX > target.R * 3 / 2) ? pointX - target.R * 3 / 2 : target.R / 2;
var auxY = (pointY <= square.side / 2) ? pointY : pointY - square.side / 2;
var dPointX = auxX * auxX;
var dPointY = auxY * auxY;
var hypo = Math.sqrt(dPointX + dPointY);
var cos = pointX / hypo;
if (pointX < (target.x + target.R / 2)) {
if (pointY <= (target.y + square.side / 2)) {
if (pointX < (target.x + (target.R / 2 * cos))) return false;
}
if (pointY > (target.y + square.side / 2)) {
if (pointX < (target.x + (target.R / 2 * cos))) return false;
}
}
if (pointX > (target.x + target.R * 3 / 2)) {
if (pointY <= (target.y + square.side / 2)) {
if (pointX < (target.x + square.side - (target.R / 2 * cos))) return false;
}
if (pointY > (target.y + square.side / 2)) {
if (pointX < (target.x + square.side - (target.R / 2 * cos))) return false;
}
}
return true;
}
return false;
}
// Loop
setInterval(onTimerTick, 33);
// Render Loop
function onTimerTick() {
// Clear the canvas
canvas.width = canvas.width;
// see if a collision happened
var collision = pointInHexagon(hex, mouseX, mouseY);
// render out text
context.fillStyle = "Blue";
context.font = "18px sans-serif";
context.fillText("Collision: " + collision + " | Mouse (" + mouseX + ", " + mouseY + ")", 10, 20);
// render out square
context.fillStyle = collision ? "red" : "green";
context.fill(hexPath);
}
// Update mouse position
canvas.onmousemove = function(e) {
mouseX = e.offsetX;
mouseY = e.offsetY;
}
#canvas {
border: 1px solid black;
}
<canvas id="canvas"></canvas>
Just replace your pointInHexagon(hexX, hexY, R, W, S, H, pointX, pointY) by the var hover = ctx.isPointInPath(hexPath, x, y).
This is for Creating and copying paths
This is about the Collision Detection
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var hexPath = new Path2D();
hexPath.lineTo(25, 0);
hexPath.lineTo(75, 0);
hexPath.lineTo(100, 43);
hexPath.lineTo(75, 86);
hexPath.lineTo(25, 86);
hexPath.lineTo(0, 43);
function draw(hover) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = hover ? 'blue' : 'red';
ctx.fill(hexPath);
}
canvas.onmousemove = function(e) {
var x = e.clientX - canvas.offsetLeft, y = e.clientY - canvas.offsetTop;
var hover = ctx.isPointInPath(hexPath, x, y)
draw(hover)
};
draw();
<canvas id="canvas"></canvas>
I've made a solution for you that demonstrates the point in triangle approach to this problem.
http://codepen.io/spinvector/pen/gLROEp
maths below:
isPointInside(point)
{
// Point in triangle algorithm from http://totologic.blogspot.com.au/2014/01/accurate-point-in-triangle-test.html
function pointInTriangle(x1, y1, x2, y2, x3, y3, x, y)
{
var denominator = ((y2 - y3)*(x1 - x3) + (x3 - x2)*(y1 - y3));
var a = ((y2 - y3)*(x - x3) + (x3 - x2)*(y - y3)) / denominator;
var b = ((y3 - y1)*(x - x3) + (x1 - x3)*(y - y3)) / denominator;
var c = 1 - a - b;
return 0 <= a && a <= 1 && 0 <= b && b <= 1 && 0 <= c && c <= 1;
}
// A Hex is composite of 6 trianges, lets do a point in triangle test for each one.
// Step through our triangles
for (var i = 0; i < 6; i++) {
// check for point inside, if so, return true for this function;
if(pointInTriangle( this.origin.x, this.origin.y,
this.points[i].x, this.points[i].y,
this.points[(i+1)%6].x, this.points[(i+1)%6].y,
point.x, point.y))
return true;
}
// Point must be outside.
return false;
}
Here is a fully mathematical and functional representation of your problem. You will notice that there are no ifs and thens in this code other than the ternary to change the color of the text depending on the mouse position. This whole job is in fact nothing more than pure simple math of just one line;
(r+m)/2 + Math.cos(a*s)*(r-m)/2;
and this code is reusable for all polygons from triangle to circle. So if interested please read on. It's very simple.
In order to display the functionality I had to develop a mimicking model of the problem. I draw a polygon on a canvas by utilizing a simple utility function. So that the overall solution should work for any polygon. The following snippet will take the canvas context c, radius r, number of sides s, and the local center coordinates in the canvas cx and cy as arguments and draw a polygon on the given canvas context at the right position.
function drawPolgon(c, r, s, cx, cy){ //context, radius, sides, center x, center y
c.beginPath();
c.moveTo(cx + r,cy);
for(var p = 1; p < s; p++) c.lineTo(cx + r*Math.cos(p*2*Math.PI/s), cy + r*Math.sin(p*2*Math.PI/s));
c.closePath();
c.stroke();
}
We have some other utility functions which one can easily understand what exactly they are doing. However the most important part is to check whether the mouse is floating over our polygon or not. It's done by the utility function isMouseIn. It's basically calculating the distance and the angle of the mouse position to the center of the polygon. Then, comparing it with the boundaries of the polygon. The boundaries of the polygon can be expressed by simple trigonometry, just like we have calculated the vertices in the drawPolygon function.
We can think of our polygon as a circle with an oscillating radius at the frequency of number of sides. The oscillation's peak is at the given radius value r (which happens to be at the vertices at angle 2π/s where s is the number of sides) and the minimum m is r*Math.cos(Math.PI/s) (each shows at at angle 2π/s + 2π/2s = 3π/s). I am pretty sure the ideal way to express a polygon could be done by the Fourier transformation but we don't need that here. All we need is a constant radius component which is the average of minimum and maximum, (r+m)/2 and the oscillating component with the frequency of number of sides, s and the amplitude value maximum - minimum)/2 on top of it, Math.cos(a*s)*(r-m)/2. Well of course as per Fourier states we might carry on with smaller oscillating components but with a hexagon you don't really need further iteration while with a triangle you possibly would. So here is our polygon representation in math.
(r+m)/2 + Math.cos(a*s)*(r-m)/2;
Now all we need is to calculate the angle and distance of our mouse position relative to the center of the polygon and compare it with the above mathematical expression which represents our polygon. So all together our magic function is orchestrated as follows;
function isMouseIn(r,s,cx,cy,mx,my){
var m = r*Math.cos(Math.PI/s), // the min dist from an edge to the center
d = Math.hypot(mx-cx,my-cy), // the mouse's distance to the center of the polygon
a = Math.atan2(cy-my,mx-cx); // angle of the mouse pointer
return d <= (r+m)/2 + Math.cos(a*s)*(r-m)/2;
}
So the following code demonstrates how you might approach to solve your problem.
// Generic function to draw a polygon on the canvas
function drawPolgon(c, r, s, cx, cy){ //context, radius, sides, center x, center y
c.beginPath();
c.moveTo(cx + r,cy);
for(var p = 1; p < s; p++) c.lineTo(cx + r*Math.cos(p*2*Math.PI/s), cy + r*Math.sin(p*2*Math.PI/s));
c.closePath();
c.stroke();
}
// To write the mouse position in canvas local coordinates
function writeText(c,x,y,msg,col){
c.clearRect(0, 0, 300, 30);
c.font = "10pt Monospace";
c.fillStyle = col;
c.fillText(msg, x, y);
}
// Getting the mouse position and coverting into canvas local coordinates
function getMousePos(c, e) {
var rect = c.getBoundingClientRect();
return { x: e.clientX - rect.left,
y: e.clientY - rect.top
};
}
// To check if mouse is inside the polygone
function isMouseIn(r,s,cx,cy,mx,my){
var m = r*Math.cos(Math.PI/s),
d = Math.hypot(mx-cx,my-cy),
a = Math.atan2(cy-my,mx-cx);
return d <= (r+m)/2 + Math.cos(a*s)*(r-m)/2;
}
// the event listener callback
function mouseMoveCB(e){
var mp = getMousePos(cnv, e),
msg = 'Mouse at: ' + mp.x + ',' + mp.y,
col = "black",
inside = isMouseIn(radius,sides,center[0],center[1],mp.x,mp.y);
writeText(ctx, 10, 25, msg, inside ? "turquoise" : "red");
}
// body of the JS code
var cnv = document.getElementById("myCanvas"),
ctx = cnv.getContext("2d"),
sides = 6,
radius = 100,
center = [150,150];
cnv.addEventListener('mousemove', mouseMoveCB, false);
drawPolgon(ctx, radius, sides, center[0], center[1]);
#myCanvas { background: #eee;
width: 300px;
height: 300px;
border: 1px #ccc solid
}
<canvas id="myCanvas" width="300" height="300"></canvas>
At the redblog there is a full explanation with math and working examples.
The main idea is that hexagons are horizontally spaced by $3/4$ of hexagons size, vertically it is simply $H$ but the column needs to be taken to take vertical offset into account. The case colored red is determined by comparing x to y at 1/4 W slice.

Calculating particle size and distance between them in Javascript

I'm trying to display an image with particles. It works, but the amount of particles is dependant on a variable (numberOfParticles) that can range between 0 and 3000. On any value, the image should be rendered in the best way possible with the given amount of particles. There is a nested for loop that goes through the image data (height and width) and creates particles like this.
for (var y = 0; y < data.height; y+=averageDistance) {
for (var x = 0; x < data.width; x+=averageDistance) {
if (particles.length < numberOfParticles){
var particle = {
x0: x,
y0: y,
color: "rgb("+data.data[(y * 4 * data.width)+ (x * 4)]+","+data.data[(y * 4 * data.width)+ (x * 4) +1]+","+data.data[(y * 4 * data.width)+ (x * 4) +2]+")"
};
particles.push(particle);
}
}
}
Later in the code, the particles get rendered with a given size.
My question is, how do I calculate the size the particles should have and the distance that should be between them?
I've tried calculating the 'average distance', counting the amount of pixels that are not covered by particles and dividing that through the amount of particles, but I can't get it to work correctly. There's always leftover space (so the bottom part doesn't get filled) or leftover particles (so there are only 40 particles shown, instead of 50) on some value of the variable numberOfParticles.
A solution to the mathematical part can be found in this answer.
To find number of points for x (nx) we can use that formula:
Then number of points for y (ny):
ny = n / nx
In JavaScript code:
nx = Math.sqrt((w / h) * n + Math.pow(w - h, 2) / (4 * Math.pow(h, 2))) - (w - h) / (2 * h);
ny = n / nx;
Using the numbers nx and ny we can then calculate the deltas for x and y:
dx = w / nx;
dy = h / ny;
Example
var ctx = c.getContext("2d"), n, w, h, nx, ny, dx, dy, x, y;
// define values
n = 1600;
w = c.width - 1; // make inclusive
h = c.height - 1;
// plug values into formula
nx = Math.sqrt((w / h) * n + Math.pow(w - h, 2) / (4 * Math.pow(h, 2))) - (w - h) / (2 * h);
ny = n / nx;
// calculate deltas
dx = w / nx;
dy = h / ny;
// render proof-of-concept
for(y = 0; y < h; y += dy) {
for(x = 0; x < w; x += dx) {
ctx.fillStyle = "hsl(" + (360*Math.random()) + ",50%,50%";
ctx.fillRect(x, y, dx-1, dy-1);
}
}
o.innerHTML = "Points to place: " + n + "<br>" +
"<strong>n<sub>x</sub></strong>: " + nx.toFixed(2) + "<br>" +
"<strong>n<sub>y</sub></strong>: " + ny.toFixed(2) + "<br>" +
"ΔX: " + dy.toFixed(2) + "<br>" +
"ΔY: " + dy.toFixed(2) + "<br>" +
"Total (nx × ny): " + (nx * ny).toFixed(0);
<canvas id=c width=600 height=400></canvas>
<br><output id=o></output>
Best fit maintaining aspect.
These types of packing problems come up a lot in CG.
To fill a square box with smaller square boxes you just need to find the square root of the count you want.
var c = 100; // number of boxes
var w = 10; // box width
var h = 10; // box height
var sW = 1000; // screen width
var sH = 1000; // screen height
The fit is sqrt(c) = sqrt(100) = 10. That's 10 across, divide the screen width by the count sW/10 = 100 to get the width of the box so that 100 will fit the screen.
This works for counts that are squares of integers, if no square root we can factorise to find a better solution. But even then there is not always a solution that fits.
Prime numbers will never fit
Any count that is a prime number will not have a solution, it is impossible to fit a prime number into x rows of y columns. This is because the resulting count is x * y and that means its not a prime.
Compromise
In the end you will need to compromise. Either you control the count to only allow counts with a solution (not a prime) or you accept that there will be some error and allow the solution to go outside the bounds.
This solution will fit but will allow the count to change and the area outside the screen to be at a minimum while still maintaining the box aspect ratio.
The width count is the sqrt((c / sA) * bA) where sA is the screen aspect and bA is the box aspect.
var c = 100; // number of boxes
var w = 10; // box width
var h = 10; // box height
var sW = 1000; // screen width
var sH = 1000; // screen height
var sA = sH / sW; // screen aspect
var bA = h / w; // box aspect
var wCount = Math.sqrt((c / sA) * bA); // get nearest fit for width
wCount = Math.round(wCount); // round to an integers. This forces the width to fit
Now you have the wCount it is just a matter of dividing the screen width by that value to get the box render width and multiply the box render width by the box aspect to get the box render height.
var rW = w / wCount; // the size of the box to render
var rH = rW * bA; // the height is the width time aspect
Sometimes you will get a perfect solution but most times you will not. The actual count will be above of below the requested count due to the rounding. But the fit will be the best for both the required box and screen aspects.
Demo
Updates every 3 seconds with a random box size, random screen size (overlaid with green box to show top and bottom excess) Text shows the column and row counts, the requested count and the actual count.
var canvas,ctx;
function createCanvas(){
canvas = document.createElement("canvas");
canvas.style.position = "absolute";
canvas.style.left = "0px";
canvas.style.top = "0px";
canvas.style.zIndex = 1000;
document.body.appendChild(canvas);
}
function resize(){
if(canvas === undefined){
createCanvas();
}
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx = canvas.getContext("2d");
}
window.addEventListener("resize",resize);
resize();
var w = 10;
var h = 20;
var c = 100;
var sW = canvas.width*0.7;
var sH = canvas.height*0.5;
var sA = sH /sW;
var bA = h / w;
function getParticleWidth(particleImageX,particleImageY,w,h,particleCount){
var a = particleImageY / particleImageX; // get particle aspect
var c = particleCount; // same with this
var b = h/w
return Math.sqrt((c/b)*a)
}
function drawTheParticles(){
var x,y;
pCount = Math.round(Math.sqrt((c/sA)*bA));
var pWidth = sW / pCount;
var pHeight = pWidth * bA;
ctx.lineWidth = 1;
ctx.strokeStyle = "black";
ctx.fillStyle = "white";
ctx.clearRect(0,0,canvas.width,canvas.height); // clear last result
var cc = 0;
var sx = (canvas.width-sW)/2;
var sy = (canvas.height-sH)/2;
var hc = ((Math.ceil(sH / pHeight)*pHeight)-sH)/2;
var wc =0;
for(y = 0; y < sH; y += pHeight){
for(x = 0; x < sW-(pWidth/2); x += pWidth){
ctx.fillRect(x + 1+sx-wc, y + 1+sy-hc, pWidth - 2, pHeight - 2);
ctx.strokeRect(x + 1+sx-wc, y + 1+sy-hc, pWidth - 2, pHeight - 2);
cc ++;
}
}
ctx.strokeStyle = "black";
ctx.fillStyle = "rgba(50,200,70,0.25)";
ctx.fillRect(sx, sy, sW, sH);
ctx.strokeRect(sx, sy, sW, sH);
// show the details
ctx.font = "20px arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
var str = ""+pCount+" by "+Math.ceil(sH / pHeight)+" need " + c + " got "+cc;
var width = ctx.measureText(str).width;
ctx.lineWidth = 2;
// clear an area for text
// with a shadow and not the stupid built in shadow
ctx.fillStyle = "rgba(0,0,0,0.4)";
ctx.fillRect((canvas.width / 2) - (width + 8) / 2+6, (canvas.height / 2) - 14+6, width + 8, 28 );
ctx.fillStyle = "#CCC";
ctx.fillRect((canvas.width / 2) - (width + 8) / 2, (canvas.height / 2) - 14, width + 8, 28 );
ctx.fillRect((canvas.width / 2) - (width + 8) / 2, (canvas.height / 2) - 14, width + 8, 28 );
// now draw the text with a bit of an outline
ctx.fillStyle = "blue"
ctx.strokeStyle = "white";
ctx.lineJoin = "round";
ctx.strokeText(str, canvas.width/2, canvas.height / 2);
ctx.fillText(str, canvas.width/2, canvas.height / 2);
// And set up to do it all again in 3 seconds
// get random particle image size
w = Math.floor(Math.random() * 100 + 10);
h = Math.floor(Math.random() * 100 + 10);
// get random particle count
c = Math.floor(Math.random() * 500 + 10);
// get random screen width height
sW = canvas.height*(Math.random()*0.4 + 0.6);
sH = canvas.height*(Math.random()*0.6 + 0.4);
// recaculate aspects
sA = sH /sW;
bA = h / w;
// redo it in 3 seconds
setTimeout(drawTheParticles,3000)
}
drawTheParticles()

Canvas JavaScript - Check if circle shape collides with pixeldata

So, I want to check if a circle shape collides with any pixeldata that is not transparent in HTML5 Canvas.
To gather pixeldata, you can just do:
ctx.getImageData(x, y, w, h)
And to check if point is in circle you just use pythagoras:
(x1-x0) * (x1-x0) + (y1-y0) * (y1-y0) < r * r
Where 0 is the center of the circle and 1 is the point's coords, and r is the circles radius.
Do you kinda have to loop trough all pixels or something?
Let's say:
Circles center is at: 300, 420
Circles radius is: 20
Canvas size is: 750 * 500 pixels
For a start, you'll only need to examine the image data within the circle's bounding box:
var imageData = ctx.getImageData(c.x - c.radius, c.y - c.radius, c.radius * 2, c.radius * 2);
var pixels = imageData.data;
Then loop through that data, getting the color values and position:
var w = imageData.width;
var h = imageData.height;
var l = w * h;
for (var i = 0; i < l; i++) {
var r = pixels[i*4]; // Red
var g = pixels[i*4+1]; // Green
var b = pixels[i*4+2]; // Blue
var a = pixels[i*4+3]; // Alpha
// skip to next iteration if the alpha of this pixel is 0
if(a === 0) {
continue;
}
// get the position of this pixel
var y = i / w;
var x = i - y * w;
// check for collision and exit is there is one
if(isInCircle(x, y)) {
collision = true;
break;
}
}
Hope this helps you on your way!

html rotate entire canvas by 90 degrees

I have an image drawn to an html cavas. I would like to be able to rotate the image by 90 degrees but I can't seem to find an example on how to rotate the entire canvas image, only an object on the canvas.
Does anyone have example code to rotate an entire canvas by 90 degrees?
I accepted an anwser below but wanted to provide additional example code : http://jsfiddle.net/VTyNF/1/
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.translate(canvas.width/2,canvas.height/2)
context.rotate(90 * (Math.PI / 180));
context.beginPath();
context.rect(188 - canvas.width/2, 50 - canvas.height/2, 200, 100);
context.fillStyle = 'yellow';
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'black';
context.stroke();
</script>
You would have to apply this rotation before you draw your canvas. You can't rotate anything that is already drawn.
So:
To rotate a canvas, the content.rotate() expects a value in radians. So first, lets make it simple by converting degrees to radians using:
function getRadianAngle(degreeValue) {
return degreeValue * Math.PI / 180;
}
You may want to translate the canvas first before rotating so that it's origin is set correctly.
context.translate(context.width/2,context.height/2);
Once we know what value we want, we simply rotate the canvas before we draw anything!
Please note, in your example, the rectangle you have drawn, is also being offset in the first two parameters of context.rect(X,Y,W,H)`.
I find it's easier to set widths as variables, then do simple math to re position the box automatically, notice now it sits perfectly in the center, and rotates nicely!
DEMO: http://jsfiddle.net/shannonhochkins/VTyNF/6/
Say your canvas element has id "foo". In JavaScript you could do something like this:
var canvas = document.getElementById('foo'),
context = canvas.getContext('2d');
// Rotates the canvas 90 degrees
context.rotate(90 * (Math.PI / 180));
Could you use CSS to rotate the <canvas> element with transform: rotate(90deg);?
You can easily rotate the image ninety degrees by manipulating the pixel data. If your canvas isn't square, you will have to make some choices about what the 'correct' answer will be.
Use the getImageData function to retrieve the pixels, manipulate them in the usual manner, and use putImageData to display the result.
This version doesn't require center point for 90 degree turn:
(Not as easy becase it's a 1d array with 4 values per pixel, initialDimensions means horizontal or 0, 180 rotation state vs 90, 270)
//...
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d', {willReadFrequently: true});
const img = document.createElement('img');
const file = inp.files[0]; // file from user input
img.src = URL.createObjectURL(file);
img.initialDimensions = true;
img.addEventListener('load', function () {
canvas.width = this.width;
canvas.height = this.height;
canvas.crossOrigin = "anonymous";
context.drawImage(img, 0, 0);
rotateClockwiseBy90(canvas, context, img);
}
}
function rotateClockwiseBy90(canvas, context, img) {
if (img.initialDimensions == undefined) {
img.initialDimensions = true;
}
var width, height;
if (img.initialDimensions) {
width = img.naturalWidth;
height = img.naturalHeight;
img.initialDimensions = false;
}
else {
width = img.naturalHeight;
height = img.naturalWidth;
img.initialDimensions = true;
}
const imageData = context.getImageData(0, 0, width, height);
const rotatedImageData = context.createImageData(height, width);
//[redIndex, greenIndex, blueIndex, alphaIndex]
const width4 = width * 4;
const height4 = height * 4;
for (let y = 0; y < height4; y += 4) {
for (let x = 0; x < width4; x += 4) {
rotatedImageData.data[x * height + (height4 - y -1) - 3] = imageData.data[y * width + x];
rotatedImageData.data[x * height + (height4 - y -1) - 2] = imageData.data[y * width + x + 1];
rotatedImageData.data[x * height + (height4 - y -1) - 1] = imageData.data[y * width + x + 2];
rotatedImageData.data[x * height + (height4 - y -1)] = imageData.data[y * width + x + 3];
}
}
const cw = canvas.width;
canvas.width = canvas.height;
canvas.height = cw;
context.putImageData(rotatedImageData, 0, 0);
}
If someone is trying to understand the logic:
rotatedImageData.data[x * height ...
should really be:
rotatedImageData.data[x / 4 * height * 4 ...
because for the rotated array x represents row number and height represents row length, but the result is same.
Version for counterclockwise rotation:
for (let y = 0; y < height4; y += 4) {
for (let x = 0; x < width4; x += 4) {
rotatedImageData.data[(height4 * (width - x/4) - height4 + y)] = imageData.data[y * width + x];
rotatedImageData.data[(height4 * (width - x/4) - height4 + y) + 1] = imageData.data[y * width + x + 1];
rotatedImageData.data[(height4 * (width - x/4) - height4 + y) + 2] = imageData.data[y * width + x + 2];
rotatedImageData.data[(height4 * (width - x/4) - height4 + y) + 3] = imageData.data[y * width + x + 3];
}
}

Categories

Resources