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.
I am trying to create an effect where the total height of a group of shapes is constant (say 300), whilst each shape within that group has a dynamic, oscillating, height. In one instance, maybe the middle shape is 'taller' whilst the outer shapes are shorter.
This desired effect is similar to if you held a slinky, with each end in one hand fixed at 30cm apart, and then shook it around: the total height remains the same (30cm) but the 'sections' inside the slinky are having their individual heights bounce up and down.
My attempts so far use the sin function to get an oscillating number as an angle value increases. This works for the sections, but I can't figure out how to maintain the constant overall height. See the code snippet below; red (and the tip of the bottom black triangle) should always be touching the bottom of the container.
// Prepare variables for angles, separated by 1
let a1 = 0;
let a2 = 1;
let a3 = 2;
let a4 = 3;
let a5 = 4;
// Prepare shape width
let shapeW = 150;
function setup() {
createCanvas(300, 300);
rect(10, 10, 10, 10);
}
function draw() {
background(240);
noStroke();
// Use the sin ratio to 'oscillate' a height value between 0 and 60
let x1 = map(sin(a1), -1, 1, 0, height / 5);
let x2 = map(sin(a2), -1, 1, 0, height / 5);
let x3 = map(sin(a3), -1, 1, 0, height / 5);
let x4 = map(sin(a4), -1, 1, 0, height / 5);
let x5 = map(sin(a5), -1, 1, 0, height / 5);
// Store these in an array so I can loop through
let listOfValues = [x1, x2, x3, x4, x5];
// Loop through and draw shapes
push();
translate((width / 2) - shapeW / 2, 0)
for (let i = 0; i < listOfValues.length; i++) {
fill(255, 0, 0);
rect(0, 0, shapeW, listOfValues[i]);
fill(0)
triangle(0, 0, shapeW / 2, listOfValues[i], shapeW, 0)
translate(0, listOfValues[i]);
}
pop();
// Increment each angle by the same amount
let incAmount = 0.1;
a1 += incAmount;
a2 += incAmount;
a3 += incAmount;
a4 += incAmount;
a5 += incAmount;
}
html,
body {
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.0/lib/p5.js"></script>
With help, I've found the solution is to use binomial coefficients. That is achieved here via a binomial() function. The only caveat is that the number of 'sections' (represented as n) must be an even number.
let angle = 0;
let N;
let containerW = 300;
let shapeW = 150;
let n = 6;
let speed = 0.0075;
function setup() {
createCanvas(containerW, containerW);
N = n * binomial(n, n / 2);
}
function draw() {
background(240);
noStroke();
let listOfVals = [];
for (let i = 0; i < n; i++) {
listOfVals.push(x(i + 1));
}
push();
translate(width / 2 - shapeW / 2, 0);
for (let i = 0; i < listOfVals.length; i++) {
fill(255, 0, 0);
rect(0, 0, shapeW, listOfVals[i] * height);
fill(0);
triangle(0, 0, shapeW / 2, listOfVals[i] * height, shapeW, 0);
translate(0, listOfVals[i] * height);
}
pop();
// Increment angle
angle += speed;
}
function x(k) {
return (2 ** n * sin(angle + (k * PI) / n) ** n) / N;
}
function binomial(n, k) {
if (typeof n !== "number" || typeof k !== "number") return false;
var coeff = 1;
for (var x = n - k + 1; x <= n; x++) coeff *= x;
for (x = 1; x <= k; x++) coeff /= x;
return coeff;
}
html,
body {
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.0/lib/p5.js"></script>
Nice self answer (+1).
This is more of an idea for a slightly different approach, hopefully with a few simplifications:
// Prepare shape width
let shapeW = 150;
// Prepare shape height
let shapeH;
// total number of shapes
let numShapes = 5;
// Increment each angle offset by the same amount
let incAmount = 0.05;
function setup() {
createCanvas(300, 300);
rect(10, 10, 10, 10);
// assign shape height after sketch height has been set
shapeH = height / 5;
}
function draw() {
background(240);
noStroke();
// Loop through and draw shapes
push();
// horizontally center shapes
translate((width - shapeW) / 2, 0);
// for each shape
for (let i = 0; i < numShapes; i++) {
// map the current height to the increment asdasdakrk
let currentH = map(sin(i + (frameCount * incAmount)), -1, 1, 0, shapeH);
fill(255, 0, 0);
rect(0, 0, shapeW, currentH);
fill(0)
triangle(0, 0, shapeW / 2, currentH, shapeW, 0)
translate(0, currentH);
}
pop();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
The above is using the same logic, mostly removing the need for the a1, a2, a3, a4, a5 values as they coincide with the i counter for each shape.
A visual way I think about it having to connect the tip of one triangle with the base of the next triangle (or the current triangle's base being the same as the the previous triangle tip's y position):
// Prepare shape width
let shapeW = 150;
// Prepare shape height
let shapeH;
// total number of shapes
let numShapes = 5;
// Increment each angle offset by the same amount
let incAmount = 0.05;
// sine driven scales
let minYScale = 0.5;
let maxYScale = 2.0;
function setup() {
createCanvas(300, 300);
rect(10, 10, 10, 10);
// assign shape height after sketch height has been set
shapeH = height / 5;
}
function draw() {
background(240);
noStroke();
// Loop through and draw shapes
push();
// horizontally center shapes
translate((width - shapeW) / 2, 0);
// draw red background
fill(255, 0, 0);
rect(0, 0, shapeW, height);
// remember where the previous array base was
let lastY = 0;
// for each shape
for (let i = 0; i < numShapes; i++) {
// map the current y scale to the increment
let currentYScale = map(sin(i + (frameCount * incAmount)), -1, 1, minYScale, maxYScale);
// compute the current scale based on the sine scalar
let currentH = currentYScale * shapeH;
fill(0);
triangle(0, lastY,
shapeW / 2, lastY + currentH,
shapeW, lastY);
// update absolute y position of the arrow base
lastY += currentH;
// optional: for debugging only, visualise lastY
if(mouseIsPressed) rect(-shapeW, lastY, width + shapeW, 3);
}
pop();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
How to draw curved text using MFC functions? I want to achieve like this below.
DrawText() function draws text in straight line only, I do not know how to draw curved text at particular angle. Please help me.
Thanks.
You could use GDI+, There is a sample in code project, which is written in C#, I translate it into C++:
Graphics graphics(hWnd);
RECT rect = { 0 };
GetWindowRect(hWnd, &rect);
POINT center = { (rect.right - rect.left) / 2,(rect.bottom - rect.top) / 2 };
double radius = min(rect.right - rect.left, (rect.bottom - rect.top)) / 3;
TCHAR text[] = L"ABCDEFGHIJLKMNOPQRSTUVWXYZ";
REAL emSize = 24;
Font* font = new Font(FontFamily::GenericSansSerif(), emSize, FontStyleBold);
for (int i = 0; i < _tcslen(text); ++i)
{
RectF re, in;
Status result = graphics.MeasureString(&text[i], 1, font, in, &re);;
double charRadius = radius + re.Height;
double angle = (((float)i / _tcslen(text)) - 0.25) * 2 * M_PI;
double x = (int)(center.x + cos(angle) * charRadius);
double y = (int)(center.y + sin(angle) * charRadius);
result = graphics.TranslateTransform(x, y);
result = graphics.RotateTransform((float)(90 + 360 * angle / (2 * M_PI)));
PointF start(0, 0);
SolidBrush Red(Color(255, 255, 0, 0));
result = graphics.DrawString(&text[i], 1, font, start, &Red);
result = graphics.ResetTransform();
SolidBrush Green(Color(255, 0, 255, 0));
Pen* pen = new Pen(&Green, 2.0f);
result = graphics.DrawArc(pen, (REAL)(center.x - radius), (REAL)(center.y - radius), radius * 2, radius * 2, 0, 360);
}
Some header files:
#define _USE_MATH_DEFINES
#include <math.h>
#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
Usage:
You must call GdiplusStartup before you create any GDI+ objects, and
you must delete all of your GDI+ objects (or have them go out of
scope) before you call GdiplusShutdown.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
//To Do.
GdiplusShutdown(gdiplusToken);
Result:
UPDATE:
Graphics graphics(hWnd);
RECT rect = { 0 };
GetWindowRect(hWnd, &rect);
POINT center = { (rect.right - rect.left) / 2,(rect.bottom - rect.top) / 2 };
double radius = min(rect.right - rect.left, (rect.bottom - rect.top)) / 3;
TCHAR text[72][4] = { 0 };
for (int i = 0; i < 72; i++)
{
_itot((i/2)*10, text[i],10);
i++;
_tcscpy(text[i],L"");
}
REAL emSize = 8;
Font* font = new Font(FontFamily::GenericSansSerif(), emSize, FontStyleBold);
for (int i = 0; i < 72; ++i)
{
RectF re, in,rel;
Status result = graphics.MeasureString(text[i], _tcslen(text[i]), font, in, &re);
result = graphics.MeasureString(L"|", 1, font, in, &rel);
double charRadius = radius - re.Height;
double angle = (((float)i / 72) - 0.25) * 2 * M_PI;
double x = (center.x + cos(angle) * charRadius);
double y = (center.y + sin(angle) * charRadius);
result = graphics.TranslateTransform(x, y);
result = graphics.RotateTransform((float)(90 + 360 * angle / (2 * M_PI)));
PointF start(0- re.Width/2, 0);
SolidBrush Red(Color(255, 255, 0, 0));
result = graphics.DrawString(text[i], _tcslen(text[i]), font, start, &Red);
result = graphics.ResetTransform();
x = (int)(center.x + cos(angle) * radius);
y = (int)(center.y + sin(angle) * radius);
result = graphics.TranslateTransform(x, y);
result = graphics.RotateTransform((float)(90 + 360 * angle / (2 * M_PI)));
PointF start1(0 - rel.Width / 2, 0);
result = graphics.DrawString(L"|", 1, font, start1, &Red);
result = graphics.ResetTransform();
}
SolidBrush Green(Color(255, 0, 255, 0));
Pen* pen = new Pen(&Green, 2.0f);
Status result = graphics.DrawArc(pen, (REAL)(center.x - radius), (REAL)(center.y - radius), radius * 2, radius * 2, 0, 360);
Result: