I am trying to draw a quadrilateral on my canvas with the p5.js library.
I am do this by creating a custom vertex shape. See the code below.
var tr;
function setup(){
createCanvas(window.innerWidth, window.innerHeight);
tr = new surface_triangle(width/2, height/2, 100, 90, "a", 12334);
}
function draw(){
background(0);
tr.draw_triangle();
}
class surface_triangle{
constructor(x, y, size, rotation, properties, id){
this.size = size;
this.h = size * (Math.sqrt(3)/2);
this.x = x;
this.y = y;
}
draw_triangle(){
beginShape();
fill(255,0,0);
vertex(this.x, this.y - this.h/2);
vertex(this.x - this.size/2, this.y + this.h/2);
vertex(this.x + this.size/2, this.y + this.h/2);
endShape(CLOSE)
fill(255);
ellipse(this.x, this.y, 10, 10);
}
}
As you see i managed to draw the triangle but the center point is not correct. And i have no idea why my center point is off? See image below:
#kevingworkman was totally right in his answer but i am trying to draw the triangle from the center point. So i will have a centerx and a centery and now i need to calculate the 3 points of the triangle relative to this center point. Like this image ->.
How would i go about doing this? Since my previous code clearly draws from a different point.
All tips and suggestions are greatly appreciated!
It's an optical illusion. The square is in the "center" of the top and bottom of the triangle. Zoom into your image and count the pixels above and below the edges of the circle. You'll find that there are 75 pixels above the circle, and 75 pixels below (give or take a couple because of anti-aliasing).
You can also see this by drawing a square with the same top and bottom as the triangle.
This looks weird to our human eyes because we want the center to be in between all three of the points, not just the top and bottom.
You can fix this by taking the average of all three points to find the center of the triangle.
Edit: If you want the provided center to be the real center of all three points, then you have to change how you calculate the three points. You can use basic trig (the cos() and sin() functions) for that.
Related
I am running into a weird problem, while performing a simple rotation of an image.
While rotating an image around its center in a canvas, the image is stretched to double of its original width when rotating to 90° and is resizing to its normal width/height rotating to 180° then its stretching again to 270° and resizing back when rotating to 0° again.
Also it seems like the Image is between those 90° steps a parallelogram and not a rectangle.
What I actually do is:
var TO_RADIANS = Math.PI/180;
function drawRotatedImage(x, y, angle, width, height) {
contextL.save();
contextL.translate(x + width/2, y + height/2);
contextL.rotate(angle * TO_RADIANS);
var imgT = new Image();
imgT.src = myPath;
contextL.drawImage(imgT, -width/2, -height/2, width, height);
contextL.restore();
}
ContextL is created like this:
canvasL = document.getElementById("canvasL");
contextL = canvasL.getContext("2d");
I really do not get, what is my fault here. I have read nearly all threads about rotating in canvas and all are doing exactly the same, but it is working. Only special may be, that I use drawImage(image, x, y, width, height) and not drawImage(image, x, y) but is this causing the trouble?
*With drawImage(image, x, y) same trouble is caused
*using contextL.drawImage(imgT, -width/2, -height/2, width/2, height*2); turns the effect around. Starting stretched and regular size at 90°, so it really it is getting the double of its width and half of its height when rotating to 90°.
*Width and Height are calculated as follows:
this.prepareIMG = function(){
this.widthIMG = this.img.width * this.scale;
this.heightIMG = this.img.height * this.scale;
};
This is the function I call every time right before I call drawRotatedImage(...). this.scaleis a value from 0.1to x increasing/decreasing in steps of 0.1.
Also when I log the width and height during the rotation to the console, the value stays always the same!
I do not manipulate the contextLsomewhere outside the function drawRotatedImage(...)!
This is the code which calls the rotate-function
this.rotatePlus = function(){
refreshCanvas();
this.rotation += 5;
this.prepareIMG();
drawRotatedImage(contextL, this.transX, this.transY, this.rotation, this.widthIMG, this.heightIMG/2);
};
Finally refreshCanvas() is defined as follows
function refreshCanvas(){
contextL = canvasL.getContext("2d");
contextL.clearRect(0, 0, canvasL.width, canvasL.height);}
and my canvas is declared in HTML like this <div id="divCanvasL"><canvas id="canvasL"></canvas></div> width & height are set by css.
I'm trying to rotate an image and move it around the canvas using the arrow keys. The plan is to have the left and right keys control the rotation of the image, and the up-down key control the movement - a bit like a tank!
I can successfully rotate an image around a centre point and place it back where it should be in the canvas, but once I rotate it by, say 45 deg, I would like the up key to move it right, rotate 180 and the up-key moves it down the canvas etc. At the moment, I can rotate the image using left/right keys, but up/down keys are always up/down the canvas.
Do I somehow need to rotate the canvas coordinates by the same amount as the image?
This is what I have so far and is in my draw function…
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.translate(T1.x + base_image.width/2, T1.y + base_image.height/2);
ctx.rotate(rotation * Math.PI/180);
ctx.translate(-(T1.x + base_image.width/2), -(T1.y + base_image.height/2));
ctx.drawImage(base_image, T1.x, T1.y);
ctx.stroke();
ctx.restore()
T1.x and T1.y are the x and y coordinates of the image.
Thanks!
Finally got it! The solution was to separate the rotation and the movement rather than trying to do it all using ctx.translate.
In my draw function called every 100 Hz:
ctx.save();
ctx.translate(T1.x + base_image.width/2, T1.y + base_image.height/2);
ctx.rotate(rotation*Math.PI/180);
ctx.drawImage(base_image, -base_image.width/2, -base_image.height/2);
ctx.restore();
ctx.stroke();
The left key is, for example, like:
rotation = rotation - 5;
Draw();
The up key is, for example, like:
var radians = (rotation*Math.PI/180)
T1.x += 4*Math.cos(radians);
T1.y += 4*Math.sin(radians);
Draw();
Note: For this to work, I had to change the default orientation of the image in paint by 45 deg.
The quickest way to draw a rotated, uniformly-scaled image on Edge, Chrome, and Firefox is
// When the image has loaded add the center offsets
image.onload = function(){
// … your code
this.centerX = -this.width/2;
this.centerY = -this.height/2;
}
// When rendering
// x,y position of image center
// scale the uniform scale
// rotate angle in radians starting at 0 and + clockwise
var xdx = Math.cos(rotate) * scale;
var xdy = Math.sin(rotate) * scale;
ctx.setTransform(xdx, xdy, -xdy, xdx, x, y);
ctx.drawImage(image,image.centerX,image.centerY)
The above method with the extra sin and cos and the two multiplications is significantly quicker on Chrome, slightly quicker on Firefox, and I forget the margin on Edge, but it was quicker than the next quickest method:
ctx.setTransform(scale, 0, 0, scale, x, y);
ctx.rotate(rotate);
ctx.drawImage(image, image.centerX, image.centerY);
Though I am leaving the canvas transform state as it is, you can continue using both methods without having to reset the canvas state. When you are done with all the rendering, to restore the current transform, use
ctx.setTransform(1, 0, 0, 1, 0, 0);
To save a tiny bit of time, each time you convert from degrees to radians, create a const DEG2RAD = Math.PI/180;. Then, you can convert with var radians = degrees*DEG2RAD; or save by typing const D2R = Math.PI/180; or call const oneDeg = Math.PI/180;.
function Background() {
this.speed = 1; // Redefine speed of the background for panning
// Implement abstract function
this.draw = function() {
// Pan background
this.y += this.speed;
this.context.drawImage(imageRepository.background, this.x, this.y);
// Draw another image at the top edge of the first image
this.context.drawImage(imageRepository.background, this.x, this.y - this.canvasHeight);
// If the image scrolled off the screen, reset
if (this.y >= this.canvasHeight)
this.y = 0;
};
}
I was trying to understand the above code which gives the logic of rendering a background image in infinite loop(giving an illusion of continuous panning).
I could not understand the following line:
this.context.drawImage(imageRepository.background, this.x, this.y - this.canvasHeight);
Clearly this.y - this.canvasHeight will never be > 0. How is the negative y co-ordinate interpreted by the canvas? Or put simply, what will the following code do?
ctx.drawImage(img, 0, -10);
It draws starting at -10 for the y position based on the origin.
i.e.: Assuming the default origin of 0,0 (left, top) 10 pixels off the y-axis will not be visible or you could think of it as start y at 10 pixels off screen.
(Converting comment to answer)
I am working on a program in Javascript while I tried to rotate my image I have drawn. I tried to search on google to find my answer but all I got was how to rotate the whole canvas. What I am searching for is a way to rotate just an image (think like this. I want to rotate a warrior dependent on the direction he is walking in). I tried many different codes but all went to the same, rotate the whole canvas.
Here is an example of what I used:
ctx.rotate(20*Math.PI/180);
and also:
var angle = 0; //init angle
images[0].onload = function () {
ctx.drawImage(images[0], 0, 0);
setInterval(function () {
ctx.save();
ctx.clearRect(-ctx.canvas.width / 2, -ctx.canvas.height / 2, ctx.canvas.width, ctx.canvas.height);
ctx.rotate(Math.PI / 180 * (angle += 10)); //rotating at 10 degrees interval..
ctx.drawImage(images[0], 0, 0);
ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
ctx.restore();
}, 16);
}
please help me out
The rotating of the canvas is part of how to create the rotation relative to the image.
To keep the image's visible position the same and not move it, you have to center it first.
Without centering the image you will only acheive the visual effect of rotating the entire canvas.
You have to throw in a bit of math to get it to rotate around the center of the image.
ctx.save();
ctx.translate(x, y); /*X and Y of the image, center point of the image */
ctx.rotate((Math.PI / 180) * rotation); /*rotation is in degrees, so this converts it to rads */
ctx.drawImage(img, -(img.width/2), -(img.height/2)); /* Draw and center the image */
ctx.restore();
I want to rotate the arrow to any given angle. What i need to add/modify so that it can be rotated to any angle. Currently its on 180 degree.
function drawArrowAtAngle(cx,cy,angle,context){
context.save();
context.beginPath();
context.lineWidth = 1;
context.strokeStyle = '#aaa';
context.moveTo(cx+25,cx-2);
context.lineTo(cx-55,cx-2);
context.lineTo(cx-58,cx);
context.lineTo(cx-55,cx+2);
context.lineTo(cx+25,cx+2);
context.lineTo(cx+25,cx-2);
context.stroke();
context.closePath();
context.restore();
}
Assuming that you want the center of rotation to be (cx,cx), insert the following three lines after the context.save(); statement.
context.translate(cx,cx) ;
context.rotate(angle) ;
context.translate(-cx,-cx) ;
This will cause the arrow to be rotated clockwise by angle (in radians).
You use (cx,cx) in your code to anchor the arrow. If you really mean (cx,cy), then adjust the above snippet accordingly.