Formula to translate xy movement with rotation - image

i have a canvas element that i rotate with context.rotate();
when i drag around the image in the canvas if i rotated lets say 90 degrees, and i move to the left, the image moves down,
is there a formula in which i can apply a movement of
for example
x+5 y+2 * degrees
and i get the real movement i need to do to move the rotated canvas in the direction i want? I would like to apply it to this function which works but with the undesired efect of moving left and the image moving down `
vm.canvasMouseMove = function (event) {
vm.delta = Date.now();
if (vm.mouseisdown && vm.delta - vm.now > (1000 / 60) && (event.clientX > 0 && event.clientY > 0)) {
vm.now = vm.delta
vm.snapshot.mouse.x -= event.clientX;
vm.snapshot.offsetSlider.value -= vm.snapshot.mouse.x;
if (vm.snapshot.offsetSlider.value < -160) {
vm.snapshot.offsetSlider.value = -160
}
else if (vm.snapshot.offsetSlider.value > 160) {
vm.snapshot.offsetSlider.value = 160
}
vm.snapshot.mouse.y -= event.clientY;
vm.snapshot.verticalOffsetSlider.value += vm.snapshot.mouse.y;
if (vm.snapshot.verticalOffsetSlider.value < -120) {
vm.snapshot.verticalOffsetSlider.value = -120
}
else if (vm.snapshot.verticalOffsetSlider.value > 120) {
vm.snapshot.verticalOffsetSlider.value = 120
}
vm.snapshot.mouse.x = event.clientX;
vm.snapshot.mouse.y = event.clientY;
}
};`
This draws
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(vm.snapshot.rotationSlider.value * Math.PI / 180);
ctx.drawImage(image, vm.snapshot.offsetSlider.value - (canvas.width / 2), (vm.snapshot.verticalOffsetSlider.value * -1) - (canvas.height / 2), vm.scaledImageW * vm.snapshot.zoomSlider.value / 100, vm.scaledImageH * vm.snapshot.zoomSlider.value / 100);
ctx.rotate(-1 * vm.snapshot.rotationSlider.value * Math.PI / 180);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
vm.donePicture = canvas.toDataURL();

This made the trick, passing the offset to the translate method, then draw the image only taking into account the canvas half translation
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate((canvas.width / 2) + (vm.snapshot.offsetSlider.value), (canvas.height / 2) - vm.snapshot.verticalOffsetSlider.value);
ctx.rotate(vm.snapshot.rotationSlider.value * Math.PI / 180);
ctx.drawImage(vm.canvasImage, 0 - (canvas.width/2), 0 - (canvas.height/2), vm.scaledImageW * vm.snapshot.zoomSlider.value / 100, vm.scaledImageH * vm.snapshot.zoomSlider.value / 100);
ctx.restore();

Related

pgraphics element won't center on mobile devices | drawingContext.drawImage causing mobile offset

I’m working on some of Tim Rodenbrökers code involving a copy() function (https://timrodenbroeker.de/processing-tutorial-kinetic-typography-1/), expanding it and making it ready for web.
This involves replacing the copy() function with drawingContext.drawImage() for performance increase (found here: https://discourse.processing.org/t/p5-js-copy-function-slow-performance-since-version-0-10-0/30007).
Doing this works great for desktop; on mobile, however, the pgraphics element (centered on the canvas, usually), moves position.
Using the regular copy() function centeres it correctly.
The positioning varies according to mobile screen size, I can’t seem to figure out the exact behavior to fix. It's not the font size, I've tried adapting the position to screen.size and document.documentElement.clientWidth, no luck.
let font;
let pg;
function setup() {
font = loadFont("./assets/FGrotesk-Regular.otf");
createCanvas(innerWidth, innerHeight);
pg = createGraphics(innerWidth, innerHeight, P2D);
frameRate(60);
pixelDensity(1);
}
function draw() {
background(0);
pg.background(0);
pg.fill(255);
pg.textFont(font);
pg.textSize(380);
pg.push();
pg.translate(innerWidth / 2, innerHeight / 2);
pg.textAlign(CENTER, CENTER);
pg.text("Enrico", 0, -50);
pg.text("Gisana", 0, 50);
pg.pop();
let tilesX = 400;
let tilesY = 20;
let tileW = int(width / tilesX);
let tileH = int(height / tilesY);
for (y = 0; y < tilesY; y++) {
for (x = 0; x < tilesX; x++) {
// WARP
let wave_x = int(sin(frameCount * 0.02 + (x * y) * 0.07) * 100) - (mouseY / 2);
let wave_y = int(sin(frameCount * 0.02 + (x * y) * 0.07) * 100) - (mouseY / 2);
if (mouseX - (width / 2) >= 0) {
wave_x = int(sin(frameCount * 0.02 + ((x / 0.8) * (y/0.2)) * 0.04) * (-1 * (mouseX - (width / 2)) / 30));
} else {
wave_x = int(sin(frameCount * 0.02 + ((x / 0.8) * (y/0.2)) * 0.04) * (-1 * (mouseX - (width / 2)) / 30));
}
if (mouseY - (height / 2) >= 0) {
wave_y = int(sin(frameCount * 0.02 + ((x / 0.2) * (y/0.8)) * 0.04) * ((mouseY - (height / 2)) / 30));
} else {
wave_y = int(sin(frameCount * 0.02 + ((x / 0.2) * (y/0.8)) * 0.04) * ((mouseY - (height / 2)) / 30));
}
// SOURCE
let sx = x * tileW + wave_x;
// + wave should be added here
let sy = y * tileH - wave_y;
let sw = tileW;
let sh = tileH;
// DESTINATION
let dx = x * tileW;
let dy = y * tileH;
let dw = tileW;
let dh = tileH;
drawingContext.drawImage(pg.elt, sx, sy, sw, sh, dx, dy, dw, dh);
}
}
}

processing collision test return false at the beginning

I am doing the collision test for the programme, and I tried to insert the test into draw() and I'm expecting it to display "GameOver" and no further events will result any change.
float x;
float y;
float w;
float h;
float xPed;
float yPed;
float yPedc;
float objectX;
float objectY;
float objectWidth;
float objectHeight;
float triangleWidth;
float triangleHeight;
float dia;
int speed = 2;
boolean gameOver;
int N_LANES = 1;
void setup() {
size(1200, 400);
background(255);
x = width / 60;
y = height / 40;
w = width / 80;
xPed = width / 2;
triangleWidth = height / 4;
triangleHeight = 2.5 * w;
yPed = height - 3 * w;
yPedc = 2.5 * w / 2;
dia = w / 2;
h = w / 2;
objectX = x + 2 * w;
objectY = y + 5 * h;
objectWidth = 4 * w;
objectHeight = 10 * h;
gameOver = false;
}
void draw() {
//reset background
background(255);
line(0, height/4, width, height/4);
vehicle();
pedestrian();
// collision detect
if (gameOver == true) {
textSize(100);
fill(255, 0, 0);
text("Game Over", width / 3, height / 2);
}
}
void vehicle() {
//moving vehicle
x += speed;
// reset vehicle
noFill();
if (x > width) {
x = 0;
} else if (x < 0) {
x = width;
}
for (int i = 0; i < N_LANES; i++) {
//head of vehicle
fill(0, 194, 0, 50);
rect(x, y, w, h);
rect(x + 3 * w, y, w, h);
rect(x, y + h, 4 * w, 4 *h);
//eyes of vehicle
fill(0);
rect(x + w, 3 * y, w * 0.85, h);
rect(x + 3 * w, 3 * y, w * 0.85, h);
//body of vehicle
fill(0, 240, 0, 80);
rect(x + 1.5 * w, 6.3 * h, 1.5 * w, 3 *h );
//left arm
line(x + 1.5 *w, 6.3 * h, x + 0.5 * w, 7.3 * h);
//right arm
line(x + 3 * w, 6.3 * h, x + 4 * w, 7.3 * h);
//left leg
line(x + 1.5 * w, 9.3 * h, x + w, 11.3 * h);
//right leg
line(x + 3 * w, 9.3 * h, x + 3.5 * w, 11.3 * h);
}
}
// draw pedestrian
void pedestrian() {
fill(255, 140, 0, 70);
//body of pedestrian
triangle(xPed, yPed, xPed - triangleWidth / 2, yPed + 2.5 * w, xPed + triangleWidth / 2, yPed + 2.5 * w);
fill(0);
circle(xPed + triangleWidth / 4, yPed, dia);
circle(xPed - triangleWidth / 4, yPed, dia);
fill(255, 165, 0);
ellipse(xPed, yPed + w, 1.5 * dia, 3 * dia);
}
// arrow key moving
void keyPressed() {
if (gameOver != true) {
if (key == CODED) {
if (keyCode == UP) {
yPed -= height / 4;
if (yPed <= 0) {
yPed = height - 3 * w;
}
}
if (keyCode == DOWN) {
yPed += height / 4;
if (yPed > height) {
yPed = height - 3 * w;
}
}
if (keyCode==LEFT) {
xPed -= height / 4;
if (xPed < 0 + triangleWidth / 2) {
xPed = width / 2;
}
}
if (keyCode==RIGHT) {
xPed += height / 4;
if (xPed > width - triangleWidth / 2) {
xPed = width / 2;
}
}
}
}
}
boolean gameOver() {
// optional visualise collision objects
rect(objectX, objectY, objectWidth, objectHeight);
rect(xPed, yPed, triangleWidth, triangleHeight);
// x axis
float distX = abs( (objectX + objectWidth / 2) - (xPed + triangleWidth / 2) );
// y axis
float distY = abs( (objectY + objectHeight / 2) - (yPedc + triangleHeight / 2) );
// half combined x distance
float combinedHalfWidth = ( (objectWidth / 2) + (triangleWidth / 2) );
// half combined y distance
float combinedHalfHeight = ( (objectHeight / 2) + (triangleHeight / 2) );
// check collision
if (distX < combinedHalfWidth) {
if (distY < combinedHalfHeight) {
return true;
}
}
return false;
}
I tried to insert the gameOver boolean into draw and it returns the same value everytime.
Just wondering what the logical problem or the coding problem is.
As Rabbid76 mentions, you should format the code correctly first.
Let's assume the code been copied from a pdf with code snippets and that's how formatting got messed up.
The code still has a few glaring bugs:
confusingly you're using both a boolean variable gameOver and a boolean function gameOver() and the variable isn't always updated. (In fact gameOver is only set once in setup() and gameOver() which actually does the collision detection is never called). I recommend either update the boolean variable, or simpler yet, just call gameOver() to compute the collision as needed. This is one of the reasons your code won't behave as you expect.
You're checking collisions between two objects: the vehicle and the pedestrian. However, there are 3 sets of coordinates: x, y, xPed, objectX, objectY. When rendering in draw, the vehicle uses x,y, however, when checking for collisions, gameOver() uses objectX, objectY (which don't match and aren't updated). This is the other reason you're collisions don't behave as expected.
aside from the question you've asked, N_LANES is used in a for loop, but never declared. Even if I declare it, the for loop uses the exact same x,y coordinates, making the for loop redundant.
Here's a formatted version of your code with extra bounding boxes highlighting what the collision function is checking against (and commenting a few unused variables):
float x;
float y;
float w;
float h;
float xPed;
float yPed;
//float yPedc;
float objectX;
float objectY;
float objectWidth;
float objectHeight;
float triangleWidth;
float triangleHeight;
float dia;
int speed = 2;
//boolean gameOver;
int N_LANES = 1;
void setup() {
size(1200, 400);
background(255);
x = width / 60;
y = height / 40;
w = width / 80;
xPed = width / 2;
triangleWidth = height / 4;
triangleHeight = 2.5 * w;
yPed = height - 3 * w;
//yPedc = 2.5 * w / 2;
dia = w / 2;
h = w / 2;
objectX = x + 2 * w;
objectY = y + 5 * h;
objectWidth = 4 * w;
objectHeight = 10 * h;
//gameOver = false;
}
void draw() {
//reset background
background(255);
line(0, height/4, width, height/4);
vehicle();
pedestrian();
// collision detect
if (gameOver() == true) {
textSize(100);
fill(255, 0, 0);
text("Game Over", width / 3, height / 2);
}
}
void vehicle() {
//moving vehicle
x += speed;
// reset vehicle
noFill();
if (x > width) {
x = 0;
} else if (x < 0) {
x = width;
}
for (int i = 0; i < N_LANES; i++) {
//head of vehicle
fill(0, 194, 0, 50);
rect(x, y, w, h);
rect(x + 3 * w, y, w, h);
rect(x, y + h, 4 * w, 4 *h);
//eyes of vehicle
fill(0);
rect(x + w, 3 * y, w * 0.85, h);
rect(x + 3 * w, 3 * y, w * 0.85, h);
//body of vehicle
fill(0, 240, 0, 80);
rect(x + 1.5 * w, 6.3 * h, 1.5 * w, 3 *h );
//left arm
line(x + 1.5 *w, 6.3 * h, x + 0.5 * w, 7.3 * h);
//right arm
line(x + 3 * w, 6.3 * h, x + 4 * w, 7.3 * h);
//left leg
line(x + 1.5 * w, 9.3 * h, x + w, 11.3 * h);
//right leg
line(x + 3 * w, 9.3 * h, x + 3.5 * w, 11.3 * h);
}
}
// draw pedestrian
void pedestrian() {
fill(255, 140, 0, 70);
//body of pedestrian
triangle(xPed, yPed, xPed - triangleWidth / 2, yPed + 2.5 * w, xPed + triangleWidth / 2, yPed + 2.5 * w);
fill(0);
circle(xPed + triangleWidth / 4, yPed, dia);
circle(xPed - triangleWidth / 4, yPed, dia);
fill(255, 165, 0);
ellipse(xPed, yPed + w, 1.5 * dia, 3 * dia);
// visualise bounding box
//fill(255, 140, 0, 70);
//rect(xPed - triangleWidth / 2, yPed, triangleWidth, triangleHeight);
}
// arrow key moving
void keyPressed() {
if (gameOver() != true) {
if (key == CODED) {
if (keyCode == UP) {
yPed -= height / 4;
if (yPed <= 0) {
yPed = height - 3 * w;
}
}
if (keyCode == DOWN) {
yPed += height / 4;
if (yPed > height) {
yPed = height - 3 * w;
}
}
if (keyCode==LEFT) {
xPed -= height / 4;
if (xPed < 0 + triangleWidth / 2) {
xPed = width / 2;
}
}
if (keyCode==RIGHT) {
xPed += height / 4;
if (xPed > width - triangleWidth / 2) {
xPed = width / 2;
}
}
}
}
}
boolean gameOver() {
// optional visualise collision objects
rect(objectX, objectY, objectWidth, objectHeight);
rect(xPed, yPed, triangleWidth, triangleHeight);
// x axis
float distX = abs( (objectX + objectWidth / 2) - (xPed + triangleWidth / 2) );
// y axis
float distY = abs( (objectY + objectHeight / 2) - (yPed + triangleHeight / 2) );
// half combined x distance
float combinedHalfWidth = ( (objectWidth / 2) + (triangleWidth / 2) );
// half combined y distance
float combinedHalfHeight = ( (objectHeight / 2) + (triangleHeight / 2) );
// check collision
if (distX < combinedHalfWidth) {
if (distY < combinedHalfHeight) {
return true;
}
}
return false;
}
Here's a version of the code with a few of the 3 notes above applied, somewhat resolving the collision and gameOver state issue:
float x;
float y;
float w;
float h;
float xPed;
float yPed;
float objectWidth;
float objectHeight;
float triangleWidth;
float triangleHeight;
float dia;
int speed = 2;
void setup() {
size(1200, 400);
background(255);
x = width / 60;
y = height / 40;
w = width / 80;
xPed = width / 2;
triangleWidth = height / 4;
triangleHeight = 2.5 * w;
yPed = height - 3 * w;
dia = w / 2;
h = w / 2;
objectWidth = 4 * w;
objectHeight = 10 * h;
}
void draw() {
//reset background
background(255);
line(0, height/4, width, height/4);
vehicle();
pedestrian();
// collision detect
if (gameOver()) {
textSize(100);
fill(255, 0, 0);
text("Game Over", width / 3, height / 2);
}
}
void vehicle() {
//moving vehicle
x += speed;
// reset vehicle
noFill();
if (x > width) {
x = 0;
} else if (x < 0) {
x = width;
}
//head of vehicle
fill(0, 194, 0, 50);
rect(x, y, w, h);
rect(x + 3 * w, y, w, h);
rect(x, y + h, 4 * w, 4 *h);
//eyes of vehicle
fill(0);
rect(x + w, 3 * y, w * 0.85, h);
rect(x + 3 * w, 3 * y, w * 0.85, h);
//body of vehicle
fill(0, 240, 0, 80);
rect(x + 1.5 * w, 6.3 * h, 1.5 * w, 3 *h );
//left arm
line(x + 1.5 *w, 6.3 * h, x + 0.5 * w, 7.3 * h);
//right arm
line(x + 3 * w, 6.3 * h, x + 4 * w, 7.3 * h);
//left leg
line(x + 1.5 * w, 9.3 * h, x + w, 11.3 * h);
//right leg
line(x + 3 * w, 9.3 * h, x + 3.5 * w, 11.3 * h);
}
// draw pedestrian
void pedestrian() {
fill(255, 140, 0, 70);
//body of pedestrian
triangle(xPed, yPed, xPed - triangleWidth / 2, yPed + 2.5 * w, xPed + triangleWidth / 2, yPed + 2.5 * w);
fill(0);
circle(xPed + triangleWidth / 4, yPed, dia);
circle(xPed - triangleWidth / 4, yPed, dia);
fill(255, 165, 0);
ellipse(xPed, yPed + w, 1.5 * dia, 3 * dia);
// visualise bounding box
//fill(255, 140, 0, 70);
//rect(xPed - triangleWidth / 2, yPed, triangleWidth, triangleHeight);
}
// arrow key moving
void keyPressed() {
if (!gameOver()) {
if (key == CODED) {
if (keyCode == UP) {
yPed -= height / 4;
if (yPed <= 0) {
yPed = height - 3 * w;
}
}
if (keyCode == DOWN) {
yPed += height / 4;
if (yPed > height) {
yPed = height - 3 * w;
}
}
if (keyCode==LEFT) {
xPed -= height / 4;
if (xPed < 0 + triangleWidth / 2) {
xPed = width / 2;
}
}
if (keyCode==RIGHT) {
xPed += height / 4;
if (xPed > width - triangleWidth / 2) {
xPed = width / 2;
}
}
}
}
}
boolean gameOver() {
// optional visualise collision objects
rect(x, y, objectWidth, objectHeight);
rect(xPed, yPed, triangleWidth, triangleHeight);
// x axis
float distX = abs( (x + objectWidth / 2) - (xPed + triangleWidth / 2) );
// y axis
float distY = abs( (y + objectHeight / 2) - (yPed + triangleHeight / 2) );
// half combined x distance
float combinedHalfWidth = ( (objectWidth / 2) + (triangleWidth / 2) );
// half combined y distance
float combinedHalfHeight = ( (objectHeight / 2) + (triangleHeight / 2) );
// check collision
if (distX < combinedHalfWidth) {
if (distY < combinedHalfHeight) {
return true;
}
}
return false;
}
Notice that the triangle bounding box needs to be moved to the left by half the triangle width ideally.
I'd also like to point you to java.awt.Rectangle which has an intersects() that could be useful:
import java.awt.Rectangle;
float x;
float y;
float w;
float h;
float xPed;
float yPed;
float objectWidth;
float objectHeight;
float triangleWidth;
float triangleHeight;
float dia;
int speed = 2;
Rectangle vehicleBoundingBox;
Rectangle pedestrianBoundingBox;
void setup() {
size(1200, 400);
background(255);
x = width / 60;
y = height / 40;
w = width / 80;
xPed = width / 2;
triangleWidth = height / 4;
triangleHeight = 2.5 * w;
yPed = height - 3 * w;
dia = w / 2;
h = w / 2;
objectWidth = 4 * w;
objectHeight = 10 * h;
vehicleBoundingBox = new Rectangle((int)x, (int)y, (int)objectWidth, (int)objectHeight);
pedestrianBoundingBox = new Rectangle((int)xPed, (int)yPed, (int)triangleWidth, (int)triangleHeight);
}
void draw() {
//reset background
background(255);
line(0, height/4, width, height/4);
vehicle();
pedestrian();
// collision detect
if (gameOver()) {
textSize(100);
fill(255, 0, 0);
text("Game Over", width / 3, height / 2);
}
}
void vehicle() {
//moving vehicle
x += speed;
// reset vehicle
noFill();
if (x > width) {
x = 0;
} else if (x < 0) {
x = width;
}
//head of vehicle
fill(0, 194, 0, 50);
rect(x, y, w, h);
rect(x + 3 * w, y, w, h);
rect(x, y + h, 4 * w, 4 *h);
//eyes of vehicle
fill(0);
rect(x + w, 3 * y, w * 0.85, h);
rect(x + 3 * w, 3 * y, w * 0.85, h);
//body of vehicle
fill(0, 240, 0, 80);
rect(x + 1.5 * w, 6.3 * h, 1.5 * w, 3 *h );
//left arm
line(x + 1.5 *w, 6.3 * h, x + 0.5 * w, 7.3 * h);
//right arm
line(x + 3 * w, 6.3 * h, x + 4 * w, 7.3 * h);
//left leg
line(x + 1.5 * w, 9.3 * h, x + w, 11.3 * h);
//right leg
line(x + 3 * w, 9.3 * h, x + 3.5 * w, 11.3 * h);
}
// draw pedestrian
void pedestrian() {
fill(255, 140, 0, 70);
//body of pedestrian
triangle(xPed, yPed, xPed - triangleWidth / 2, yPed + 2.5 * w, xPed + triangleWidth / 2, yPed + 2.5 * w);
fill(0);
circle(xPed + triangleWidth / 4, yPed, dia);
circle(xPed - triangleWidth / 4, yPed, dia);
fill(255, 165, 0);
ellipse(xPed, yPed + w, 1.5 * dia, 3 * dia);
}
// arrow key moving
void keyPressed() {
if (!gameOver()) {
if (key == CODED) {
if (keyCode == UP) {
yPed -= height / 4;
if (yPed <= 0) {
yPed = height - 3 * w;
}
}
if (keyCode == DOWN) {
yPed += height / 4;
if (yPed > height) {
yPed = height - 3 * w;
}
}
if (keyCode==LEFT) {
xPed -= height / 4;
if (xPed < 0 + triangleWidth / 2) {
xPed = width / 2;
}
}
if (keyCode==RIGHT) {
xPed += height / 4;
if (xPed > width - triangleWidth / 2) {
xPed = width / 2;
}
}
}
}
}
boolean gameOver(){
// update bounding box positions
vehicleBoundingBox.x = (int)x;
vehicleBoundingBox.y = (int)y;
pedestrianBoundingBox.x = (int)(xPed - triangleWidth / 2);
pedestrianBoundingBox.y = (int)yPed;
//optional: visualise boxes
fill(255, 140, 0, 70);
rect(pedestrianBoundingBox.x, pedestrianBoundingBox.y, pedestrianBoundingBox.width, pedestrianBoundingBox.height);
rect(vehicleBoundingBox.x, vehicleBoundingBox.y, vehicleBoundingBox.width, vehicleBoundingBox.height);
return vehicleBoundingBox.intersects(pedestrianBoundingBox);
}
//boolean gameOver() {
// // optional visualise collision objects
// rect(x, y, objectWidth, objectHeight);
// rect(xPed, yPed, triangleWidth, triangleHeight);
// // x axis
// float distX = abs( (x + objectWidth / 2) - (xPed + triangleWidth / 2) );
// // y axis
// float distY = abs( (y + objectHeight / 2) - (yPed + triangleHeight / 2) );
// // half combined x distance
// float combinedHalfWidth = ( (objectWidth / 2) + (triangleWidth / 2) );
// // half combined y distance
// float combinedHalfHeight = ( (objectHeight / 2) + (triangleHeight / 2) );
// // check collision
// if (distX < combinedHalfWidth) {
// if (distY < combinedHalfHeight) {
// return true;
// }
// }
// return false;
//}
void circle(float x, float y, float dia){
ellipse(x, y, dia, dia);
}
Bare in mind, if this is an assignment/homework, you might not be allowed to use java.awt.Rectangle. Speaking of which, if this is an assignment you should mention that in the question.
Update
Here's an updated version better handling the game over state.
float x;
float y;
float w;
float h;
float xPed;
float yPed;
float objectWidth;
float objectHeight;
float triangleWidth;
float triangleHeight;
float dia;
int speed = 2;
// defaults to false
boolean isGameOver;
void setup() {
size(1200, 400);
background(255);
x = width / 60;
y = height / 40;
w = width / 80;
xPed = width / 2;
triangleWidth = height / 4;
triangleHeight = 2.5 * w;
yPed = height - 3 * w;
dia = w / 2;
h = w / 2;
objectWidth = 4 * w;
objectHeight = 10 * h;
}
void draw() {
//reset background
background(255);
line(0, height/4, width, height/4);
if (isGameOver) {
textSize(100);
fill(255, 0, 0);
text("Game Over", width / 3, height / 2);
}else{
// check colloisions only in game state and update game over state
isGameOver = checkCollisions();
vehicle();
pedestrian();
}
}
void vehicle() {
//moving vehicle
x += speed;
// reset vehicle
noFill();
if (x > width) {
x = 0;
} else if (x < 0) {
x = width;
}
//head of vehicle
fill(0, 194, 0, 50);
rect(x, y, w, h);
rect(x + 3 * w, y, w, h);
rect(x, y + h, 4 * w, 4 *h);
//eyes of vehicle
fill(0);
rect(x + w, 3 * y, w * 0.85, h);
rect(x + 3 * w, 3 * y, w * 0.85, h);
//body of vehicle
fill(0, 240, 0, 80);
rect(x + 1.5 * w, 6.3 * h, 1.5 * w, 3 *h );
//left arm
line(x + 1.5 *w, 6.3 * h, x + 0.5 * w, 7.3 * h);
//right arm
line(x + 3 * w, 6.3 * h, x + 4 * w, 7.3 * h);
//left leg
line(x + 1.5 * w, 9.3 * h, x + w, 11.3 * h);
//right leg
line(x + 3 * w, 9.3 * h, x + 3.5 * w, 11.3 * h);
}
// draw pedestrian
void pedestrian() {
fill(255, 140, 0, 70);
//body of pedestrian
triangle(xPed, yPed, xPed - triangleWidth / 2, yPed + 2.5 * w, xPed + triangleWidth / 2, yPed + 2.5 * w);
fill(0);
circle(xPed + triangleWidth / 4, yPed, dia);
circle(xPed - triangleWidth / 4, yPed, dia);
fill(255, 165, 0);
ellipse(xPed, yPed + w, 1.5 * dia, 3 * dia);
// visualise bounding box
//fill(255, 140, 0, 70);
//rect(xPed - triangleWidth / 2, yPed, triangleWidth, triangleHeight);
}
// arrow key moving
void keyPressed() {
if (isGameOver){
// exit game over
isGameOver = false;
// lazy way to restart the game
// normally you'd write & call a reset() function to reset player/vehicle positions, avodiing instant gameOver
setup();
} else {
if (key == CODED) {
if (keyCode == UP) {
yPed -= height / 4;
if (yPed <= 0) {
yPed = height - 3 * w;
}
}
if (keyCode == DOWN) {
yPed += height / 4;
if (yPed > height) {
yPed = height - 3 * w;
}
}
if (keyCode==LEFT) {
xPed -= height / 4;
if (xPed < 0 + triangleWidth / 2) {
xPed = width / 2;
}
}
if (keyCode==RIGHT) {
xPed += height / 4;
if (xPed > width - triangleWidth / 2) {
xPed = width / 2;
}
}
}
}
}
boolean checkCollisions() {
// optional visualise collision objects
fill(255, 140, 0, 70);
rect(x, y, objectWidth, objectHeight);
rect(xPed - triangleWidth / 2, yPed, triangleWidth, triangleHeight);
// x axis
float distX = abs( (x + objectWidth / 2) - xPed );
// y axis
float distY = abs( (y + objectHeight / 2) - (yPed + triangleHeight / 2) );
// half combined x distance
float combinedHalfWidth = ( (objectWidth / 2) + (triangleWidth / 2) );
// half combined y distance
float combinedHalfHeight = ( (objectHeight / 2) + (triangleHeight / 2) );
// check collision
if (distX < combinedHalfWidth) {
if (distY < combinedHalfHeight) {
return true;
}
}
return false;
}
The main changes are:
reintroduced gameOver boolean as isGameOver which is used to change between the two states
renamed gameOver() to checkCollisions() to avoid confusion.
In this case, with just two states, a boolean will do.
It's important to also reset game variables when changing state (e.g. reset player/vehicle positions, etc.)
In case your game may require more states you can use an integer and constants. This answer has a demo code snippet.
If multiple states are required OOP was introduced, this answer also has demo code.

rotating a single line multiple times in canvas

the code below is almost fine but I want to make it short and simple by using a single makeline() function so as to avoid the repetition of the code lines.
I want the loading image to remain same only I want the code to be short. by using the above-mentioned function. As you can see below the code is too lengthy and when it is run the lines are not properly adjusted.
I want the shape to remain the same the position of the lines should be same only a little bit of adjustment
<!DOCTYPE html>
<html>
<head>
<style>
myCanvas {
border: 1px;
background: rgba( 240, 238, 238, 0.898);
}
</style>
</head>
<body>
<canvas id="canvas" width="1000" height="1000" style="border: ">
</canvas>
<script>
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
ctx.lineWidth = 5;
ctx.beginPath();
ctx.translate(470, 470)
ctx.rotate(15 * Math.PI / 180);
ctx.moveTo(25, 45);
ctx.lineTo(100, 150);
ctx.stroke();
ctx.beginPath();
ctx.rotate(55 * Math.PI / 180);
ctx.moveTo(35, 35);
ctx.lineTo(100, 150);
ctx.stroke();
ctx.beginPath();
ctx.rotate(45 * Math.PI / 180);
ctx.moveTo(35, 35);
ctx.lineTo(100, 150);
ctx.stroke();
ctx.beginPath();
ctx.rotate(45 * Math.PI / 180);
ctx.moveTo(35, 35);
ctx.lineTo(100, 150);
ctx.stroke();
ctx.beginPath();
ctx.rotate(50 * Math.PI / 180);
ctx.moveTo(25, 35);
ctx.lineTo(100, 150);
ctx.stroke();
ctx.beginPath();
ctx.rotate(50 * Math.PI / 180);
ctx.moveTo(35, 35);
ctx.lineTo(100, 150);
ctx.stroke();
ctx.beginPath();
ctx.rotate(40 * Math.PI / 180);
ctx.moveTo(35, 50);
ctx.lineTo(100, 150);
ctx.stroke();
ctx.beginPath();
ctx.rotate(5 * Math.PI / 180);
ctx.moveTo(35, 50);
ctx.lineTo(100, 150);
ctx.stroke();
ctx.beginPath();
ctx.rotate(15 * Math.PI / 180);
ctx.moveTo(35, 45);
ctx.lineTo(100, 150);
ctx.stroke();
ctx.beginPath();
ctx.rotate(15 * Math.PI / 180);
ctx.moveTo(35, 45);
ctx.lineTo(100, 150);
ctx.stroke();
ctx.beginPath();
ctx.rotate(25 * Math.PI / 180);
ctx.moveTo(35, 35);
ctx.lineTo(100, 150);
ctx.stroke();
ctx.beginPath();
ctx.rotate(45 * Math.PI / 180);
ctx.moveTo(35, 35);
ctx.lineTo(100, 150);
ctx.stroke();
</script>
</body>
</html>
Sweep A Line
If you are wanting to move the line along its normal (90deg from its direction) and rotate it while doing so the following will explain how to do this using absolute positioning (no ctx.rotate calls)
Some utility functions to get started.
// A Point also defines a vector.
const Point = (x,y) => ({x,y});
const Line = (p1, p2) => ({p1, p2});
const lengthOfVec = vec => (vec.x ** 2 + vec.y ** 2) ** 0.5;
const normalVec = line => { // normalize the line and return a vector
const vec = Point(line.p2.x - line.p1.x, line.p2.y - line.p1.y);
const length =lengthOfVec(vec);
vec.x /= length;
vec.y /= length;
return vec;
}
const rotateVec90 = vec => ([vec.y, vec.x] = [vec.x, - vec.y], vec);
We can start with a line defined by its end points p1 and p2 and the normal of the line is a vector that is at 90 deg to the left of the line.
We will create a function that moved the line along this normal, the function will also rotate the line as it does, and by the looks of your example the line needs to change length as well so we can add a scale argument as well.
The steps
The function will be sweepLine(line, distance, rotate, scale), whererotate is in radians (I will not use degrees), distance is in pixels, scale > 1 will grow the line and scale < 1 will shrink the line.
function sweepLine(line, dist, rot, scale){
We need the center of the line and the line normalised and normal as a vector
const center = Point((line.p1.x + line.p2.x) / 2, (line.p1.y + line.p2.y) / 2);
const lineNorm = normalVec(line);
const norm = rotateVec90(Point(lineNorm.x, lineNorm.y));
No rotate
We need the new position of the center, if we are rotating the new center will be at the end of an arc, if not rotating the new position will be at the end of a line. Just move the center along the normal
if(rot !== 0){
// need the dist of point from center
const ax = line.p2.x - center.x;
const ay = line.p2.y - center.y;
// move the point
center.x += norm.x * dist;
center.y += norm.y * dist;
Now we can just scale the line
line.p1.x = center.x - ax * scale
line.p1.y = center.y - ay * scale;
line.p2.x = center.x + ax * scale;
line.p2.y = center.y + ay * scale;
}
Rotate moves on an arc
For the rotated line we need to find the point on the arc, and to define an arc we need that arc's center. The length of an arc is the change in angle times the radius, we dont have the radius
else {
const arcRadius = dist / rot;
The arc center is arcRadius distance from the center (Note that rot can be negative an that will move the center to the correct position)
const arcCenter = Point(
center.x + lineNorm.x * arcRadius,
center.y + lineNorm.y * arcRadius
);
now we have the center we need the start angle of the arc, which is the direction of the line.
const startAngle = Math.atan2(lineNorm.y, lineNorm.x);
const endAngle = startAngle + rot;
We add the rotation to the startAngle and then move arcRadius distance from the arcCenter along that new angle to the new center.
center.x = arcCenter.x + Math.cos(endAngle) * arcRadius;
center.y = arcCenter.y + Math.sin(endAngle) * arcRadius;
With the new position of the center we can change the lines size and rotate it at the same time if we get the length of the line.
const len = lengthOfVec(Point(line.p1.x - line.p2.x, line.p1.y - line.p2.y));
line.p1.x = center.x - Math.cos(endAngle) * len * scale * 0.5;
line.p1.y = center.y - Math.sin(endAngle) * len * scale * 0.5;
line.p2.x = center.x + Math.cos(endAngle) * len * scale * 0.5;
line.p2.y = center.y + Math.sin(endAngle) * len * scale * 0.5;
And that is it. The function can return.
}
}
Example
To show usage example the snippet below does the same but has some optimizations along the way.
The example creates a random line and then moves it using the sweepLine function. It is animated to continuously draw lines.
requestAnimationFrame(update);
const ctx = canvas.getContext("2d");
var w = canvas.width;
var h = canvas.height;
function update(timer){
if(w !== innerWidth || h !== innerHeight){
w = canvas.width = innerWidth;
h = canvas.height = innerHeight;
}
jiggle();
sweepLine(line, moveBy,rotateBy, scaleBy);
drawLine(line);
requestAnimationFrame(update);
}
// A Point also defines a vector.
const Point = (x,y) => ({x,y});
const Line = (p1, p2) => ({p1, p2});
const lengthOfVec = vec => (vec.x ** 2 + vec.y ** 2) ** 0.5;
const normalVec = line => { // normalize the line and return a vector
const vec = Point(line.p2.x - line.p1.x, line.p2.y - line.p1.y);
const length = lengthOfVec(vec);
vec.x /= length;
vec.y /= length;
return vec;
}
const rotateVec90 = vec => {
const t = vec.x;
vec.x = - vec.y;
vec.y = t;
return vec;
}
function sweepLine(line, dist, rot, scale){
const center = Point((line.p1.x + line.p2.x) / 2, (line.p1.y + line.p2.y) / 2);
const len = lengthOfVec(Point(line.p1.x - line.p2.x, line.p1.y - line.p2.y));
const lineNorm = normalVec(line);
const norm = rotateVec90(Point(lineNorm.x, lineNorm.y));
if(rot === 0){
const ax = (line.p2.x - center.x) * scale;
const ay = (line.p2.y - center.y) * scale;
center.x += norm.x * dist;
center.y += norm.y * dist;
line.p1.x = center.x - ax
line.p1.y = center.y - ay;
line.p2.x = center.x + ax;
line.p2.y = center.y + ay;
} else {
const arcRadius = dist / rot;
const arcCenter = Point(
center.x - lineNorm.x * arcRadius, center.y - lineNorm.y * arcRadius
);
const endAngle = Math.atan2(lineNorm.y, lineNorm.x) + rot;
var ax = Math.cos(endAngle);
var ay = Math.sin(endAngle);
center.x = arcCenter.x + ax * arcRadius;
center.y = arcCenter.y + ay * arcRadius;
const len = lengthOfVec(Point(line.p1.x - line.p2.x, line.p1.y - line.p2.y));
ax *= len * scale * 0.5;
ay *= len * scale * 0.5;
line.p1.x = center.x - ax;
line.p1.y = center.y - ay;
line.p2.x = center.x + ax;
line.p2.y = center.y + ay;
}
}
function drawLine(line){
ctx.lineWidth = 8;
ctx.lineCap = "round";
ctx.strokeStyle = col;
ctx.beginPath();
ctx.lineTo(line.p1.x, line.p1.y);
ctx.lineTo(line.p2.x, line.p2.y);
ctx.stroke();
}
function createRandomLine(){
const x = Math.random() * w * 0.3 + w * 0.35;
const y = Math.random() * h * 0.3 + h * 0.35;
const len = Math.random() * 40 + 10;
const dir = Math.random() * Math.PI * 2;
return Line(
Point(x - Math.cos(dir) * len * 0.5, y - Math.sin(dir) * len * 0.5),
Point(x + Math.cos(dir) * len * 0.5, y + Math.sin(dir) * len * 0.5)
);
}
// sweep the line randomly needs some settings
var line, rotateBy, moveBy, scaleBy, col, l = 50,s = 70,hue = 0,moveFor = 0; //
function randomize(){
rotateBy = Math.random() * 0.5 - 0.25;
moveBy = Math.random() * 5 + 5;
scaleBy = 1;
moveFor = 200;
line = createRandomLine();
}
function jiggle(){
if(moveFor === 0 ){ randomize() }
rotateBy += (Math.random() - 0.5) * 0.2;
scaleBy = Math.random() < 0.2 ? 1/1.1 : Math.random() < 0.2 ? 1.1 : 1;
moveBy += (Math.random() - 0.5) * 4;
moveFor --;
hue = (hue + 1) % 360;
s = (s + 100 + Math.random() - 0.5) % 100;
l = (l + 100 + Math.random() - 0.5) % 100;
col = "hsl("+hue+","+s+"%,"+l+"%)";
}
canvas { position : absolute; top : 0px; left : 0px; }
<canvas id="canvas"></canvas>
Looks like you want to draw some sort of sun with rays coming out.
Here is one option with much less code than yours, the key is to use a function to do the drawing
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var hw = c.width/2;
var hh = c.height/2
ctx.translate(hw, hh)
ctx.lineWidth = 5;
function drawLine(x, y) {
ctx.beginPath();
ctx.moveTo(x * hw/3, y * hh/3);
ctx.lineTo(x * hw, y * hh);
ctx.stroke();
}
var p200 = Math.PI * 200
for (i = 0; i < p200; i += p200 / 12)
drawLine(Math.sin(i/100), Math.cos(i/100));
<canvas id="canvas" width="170" height="170">
And with that same function and a few math tricks you can draw some more complex shapes like a sea shell looking shape like this:
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
const p200 = Math.PI * 200
function drawLine(x, y, m) {
ctx.beginPath();
ctx.moveTo(x * m/3, y * m/3);
ctx.lineTo(x * m, y * m);
ctx.stroke();
}
function shell(ini, end, mid) {
ctx.lineWidth = 5;
ctx.strokeStyle="black";
for (i = ini; i < end; i += p200 / 124)
drawLine(Math.cos(i/100), Math.sin(i/100), mid - i/10);
ctx.translate(-3, -3)
ctx.strokeStyle="cyan";
for (i = ini; i < end; i += p200 / 96)
drawLine(Math.cos(i/100), Math.sin(i/100), mid - i/10);
ctx.strokeStyle="blue";
for (i = ini; i < end; i += p200 / 48)
drawLine(Math.cos(i/100), Math.sin(i/100), mid - i/10);
ctx.lineWidth = 0.5;
ctx.strokeStyle="green";
for (i = ini; i < end; i += p200 / 48)
drawLine(Math.cos(i/100), Math.sin(i/100), mid - i/10);
}
ctx.translate(70, 60)
shell(0, p200, 95)
ctx.translate(200, 40)
shell(p200/1.8, p200+p200/1.8, 135)
<canvas id="canvas" width="340" height="170">
If you are happy with the result, you may write a function like this:
function drawLine(t,r, p1,p2){
//t: translation object
//r: rotation object
//p1: point object for the moveTo() method
//p2: point object for the lineTo() method
//ctx.save();
ctx.beginPath();
ctx.translate(t.x, t.y)
ctx.rotate(r * Math.PI / 180);
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.stroke();
//ctx.restore();
}
I've commented out the save & restore methods since you don't use them, but those methods are very useful and help you to save a lot of calculations.

How to calculate an orbit around the earth with respect to Mercator projection?

I'm trying to create a small interactive UFO-flying-around-the-earth scene in three.js.
I thought it would be good to control the UFO on a 2D projection of the map (like a minimap), convert the pixel coordinates to lat/lng coordinates and finally transform lat/lng to a Vector that I can use for my 3D scene.
As it turns out, it wasn't.
Works great if the UFO flies around the Equator (+/- some degrees), but for sure I forgot to apply the projection to my 2D controls:
Now I'm a little bit lost. My controls (WASD keys) basically sets the speed and the rotation of the UFO on the minimap, but I have to add some sort of "correction" to the player. That's how the values are set at the moment:
let left = parseFloat(this.minimapPlayer.style.left) + this.speed * Math.cos(Math.PI / 180 * this.rotation)
let top = parseFloat(this.minimapPlayer.style.top) + this.speed * Math.sin(Math.PI / 180 * this.rotation)
Is there a way to create a "real" orbit, so that my UFO doesn't always fly through both poles? Or may there be a better approach to handle the orbit of the UFO (maybe without the minimap)?
Here's the full animation code so far:
animatePlane(firstRender) {
requestAnimationFrame(this.animatePlane)
// set next position
let left = parseFloat(this.minimapPlayer.style.left) + this.speed * Math.cos(Math.PI / 180 * this.rotation)
let top = parseFloat(this.minimapPlayer.style.top) + this.speed * Math.sin(Math.PI / 180 * this.rotation)
// handle border collisions
if (left < 0) {
left = this.minimapBounds.width
}
if (left > this.minimapBounds.width) {
left = 0
}
if (top < 0 || top > this.minimapBounds.height) {
this.rotation = this.rotation * -1
if (this.rotation > 0) {
this.plane.up.set(0, 1, 0)
} else {
this.plane.up.set(0, -1, 0)
}
top = top + this.speed * Math.sin(Math.PI / 180 * this.rotation)
if (left < this.minimapBounds.width / 2) {
left = left + this.speed * Math.cos(Math.PI / 180 * this.rotation) + this.minimapBounds.width / 2
} else {
left = left + this.speed * Math.cos(Math.PI / 180 * this.rotation) - this.minimapBounds.width / 2
}
}
this.minimapPlayer.style.left = `${left}px`
this.minimapPlayer.style.top = `${top}px`
// convert to lat/lng
const lat = (180 / this.minimapBounds.height) * (top - this.minimapBounds.height / 2) * -1
const lng = (360 / this.minimapBounds.width) * (left - this.minimapBounds.width / 2)
// convert to vector
const p = this.latLongToVector3(lat, lng, this.radius, 200)
this.plane.position.set(p.x, p.y, p.z)
// bottom of the plane should always look the the middle of the earth
this.plane.lookAt(new THREE.Vector3(0,0,0))
}

Scaling small objects using THREE.TransformControls

I have a simple editor for 3D models using three.js.
This is my camera:
new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 0.01, 10);
And this is my mesh's geometry:
new THREE.BoxGeometry(0.3, 0.3, 0.3);
I'm using THREE.TransformControls to scale the mesh.
Scaling works perfectly fine when meshes have units ~ 100, but when an object is between 0 and 1, scaling is very slow. With slow, I mean that when you drag the scale slider half the canvas size, the mesh increases or decreases only a few percentages in size. I don't have this problem with other TransformControls modes.
JSFiddle demonstrating slow scaling
I could switch to bigger units, but I'd like to know if this can be solved without having to replace all units throughout my app code.
So my question: In THREE.TransformControls, is there a way to translate dragging to scaling in a more natural way while dealing with small units?
The problem is that TransformControls scales the distance of the mouse movement by a seemingly arbitrary amount (1/50) within the onMouseMove function:
} else if (_mode == "scale") {
point.sub(offset);
point.multiply(parentScale);
if (scope.space == "local") {
if (scope.axis == "XYZ") {
scale = 1 + ((point.y) / 50);
scope.object.scale.x = oldScale.x * scale;
scope.object.scale.y = oldScale.y * scale;
scope.object.scale.z = oldScale.z * scale;
} else {
point.applyMatrix4(tempMatrix.getInverse(worldRotationMatrix));
if (scope.axis == "X") scope.object.scale.x = oldScale.x * (1 + point.x / 50);
if (scope.axis == "Y") scope.object.scale.y = oldScale.y * (1 + point.y / 50);
if (scope.axis == "Z") scope.object.scale.z = oldScale.z * (1 + point.z / 50);
}
}
}
This parameter (50) effectively controls the "speed" at which the object is scaled (compared to how far the mouse is dragged). You're going to need to patch TransformControls in order to change it; I got it to work better by changing 50 to something smaller, like 1.
} else if (_mode == "scale") {
point.sub(offset);
point.multiply(parentScale);
// by default this is 50; set it to smaller values if you're using smaller units
scaleSpeed = 1;
if (scope.space == "local") {
if (scope.axis == "XYZ") {
scale = 1 + ((point.y) / scaleSpeed);
scope.object.scale.x = oldScale.x * scale;
scope.object.scale.y = oldScale.y * scale;
scope.object.scale.z = oldScale.z * scale;
} else {
point.applyMatrix4(tempMatrix.getInverse(worldRotationMatrix));
if (scope.axis == "X") scope.object.scale.x = oldScale.x * (1 + point.x / scaleSpeed);
if (scope.axis == "Y") scope.object.scale.y = oldScale.y * (1 + point.y / scaleSpeed);
if (scope.axis == "Z") scope.object.scale.z = oldScale.z * (1 + point.z / scaleSpeed);
}
}
}
Working fiddle: http://jsfiddle.net/494uvxfg/ .
three.js r68

Resources