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

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);
}
}
}

Related

Can you create numbers around the analog clock?

so, as an assaignment i want to make an analog clock, and i am pretty far. I only need the numbers around the clock, but i cant figure out how to make these. I have made some dots right now, but i want to replace theese with the numbers 1-12.. does any1 know an easy and fast way of doing this? my code is as following:
let cx, cy;
let secondsRadius;
let minutesRadius;
let hoursRadius;
let clockDiameter;
function setup() {
createCanvas(800, 800);
let radius= min(width,height)/2;
secondsRadius=radius*0.71;
minutesRadius = radius*0.6;
hoursRadius = radius*0.5;
clockDiameter = radius*1.7;
cx = width/2;
cy = height/2;
}
function draw() {
background("pink");
noStroke();
fill(244, 122, 158);
ellipse(cx, cy, clockDiameter + 25, clockDiameter + 25);
fill("green");
ellipse(cx,cy,clockDiameter, clockDiameter);
let s = map(second(), 0, 60, 0, TWO_PI) - HALF_PI;
let m = map(minute() + norm(second(), 0, 60), 0, 60, 0, TWO_PI) - HALF_PI;
let h = map(hour() + norm(minute(), 0, 60), 0, 24, 0, TWO_PI *2) - HALF_PI;
stroke(255);
strokeWeight(1);
line(cx, cy, cx + cos(s) * secondsRadius, cy + sin(s) * secondsRadius);
strokeWeight(2);
line(cx, cy, cx + cos(m) * minutesRadius, cy + sin(m) * minutesRadius);
strokeWeight(4);
line(cx, cy, cx + cos(h) * hoursRadius, cy + sin(h) * hoursRadius);
strokeWeight(2);
beginShape(POINTS);
for (let a = 0; a < 360; a += 6) {
let angle = radians(a);
let x = cx + cos(angle) * secondsRadius;
let y = cy + sin(angle) * secondsRadius;
vertex(x, y);
}
endShape();
}```
As a quick and dirty:) answer - just add something like that before the last brace:
textSize(36);
fill("white");
noStroke()
for (i = 0; i < 12; i++) {
v = p5.Vector.fromAngle((i + 1) / 12.0 * TAU - HALF_PI);
v.mult(310);
text(i + 1, 382 + v.x, 416 + v.y);
}
My live working demo (your code + this addition) is on ReplIt

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.

Formula to translate xy movement with rotation

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();

Does MinGW influence compilation -- a GTK program

I compiled a GTK program in MinGW environment. It worked perfect. Recently, I updated MinGW and recompiled the GTK program, then the program always stopped with Segmentation fault. I have tried to use gdb to look for errors but I couldn't. The config of GTK is exactly same in the old and new MinGW. Therefore, I am suspicious of MinGW environment that influences compilation. I found the version of gcc is different in two environment:
old MinGW new MinGW
gcc 3.4.5 4.7.2
Any idea? Does anybody meet the situation?
Another clue is about GTK itself. I found the program stopped here:
g_signal_emit_by_name(G_OBJECT(g_object_get_data(G_OBJECT(window), "plat_GA_canvas")), "expose-event", G_TYPE_NONE);
It is a canvas to draw a image according to different situation. Here it just sends a signal to expose the canvas. The curious thing is that it worked in the old MinGW. So I don't doubt it is the point of the problem. Just maybe a clue. By the way, I compiled the GTK program in a pure Linux environment at home. It also worked perfect.
Any idea? Please help.
#duskast, below is the part of the code. It is a little lengthy.
+++++++++++++++++++++++++++++++++++++++++++++++++++
The problem maybe happened -- g_signal_emit_by_name:
void ACC_platform_unit_num_changed (GtkWidget *box, GtkWidget *window)
{
int kind;
kind = gtk_option_menu_get_history((GtkOptionMenu *)box) + 1;
if(plat.unit_num != kind)
{
plat_GA_canvas_refresh (window);
g_signal_emit_by_name(G_OBJECT(g_object_get_data(G_OBJECT(window), "plat_GA_canvas")), "expose-event", G_TYPE_NONE);
plat.unit_num = kind;
if(plat.unit_num == 1)
{
gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(window), "ACC_platform_if_gap"), FALSE);
gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(window), "ACC_platform_if_big_gap"), FALSE);
gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(window), "ACC_platform_gap_w"), FALSE);
gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(window), "ACC_platform_live_load_gap"), FALSE);
}
else
{
gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(window), "ACC_platform_if_gap"), TRUE);
if(plat.has_gap)
{
gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(window), "ACC_platform_if_big_gap"), TRUE);
gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(window), "ACC_platform_gap_w"), TRUE);
gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(window), "ACC_platform_live_load_gap"), TRUE);
}
else
{
gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(window), "ACC_platform_if_big_gap"), FALSE);
gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(window), "ACC_platform_gap_w"), FALSE);
gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(window), "ACC_platform_live_load_gap"), FALSE);
}
}
gap_span_show (g_object_get_data(G_OBJECT(window), "ACC_platform_gap_span_store"));
}
}
The core drawing function:
void ACC_GA_draw (cairo_t *cr, int width, int height)
{
/* #define X_MARGIN 30
#define Y_MARGIN 30 */
#define COLUMN_RADIUS_ASSUME 1.6
#define COLUMN_SQUARE_LEN_ASSUME 2.0
double x, y, x1, y1, x2, y2, scale, x_base, y_base, X_MARGIN, Y_MARGIN;
double x3, y3, downward_height = 0, downward_width = 0;
double x_one_unit, y_one_unit, x_total, y_total, gap_value;
double alpha = 60.0, arrow_side_length = 20.0, arrow_tail_length = 10.0;
ACC_AXIS *x_axis, *y_axis;
int i, j, unit_num, downward;
char *AX, *AY, *Wx_left, *Wx_right, *Wy_top, *Wy_bottom, *gap, *axis;
cairo_text_extents_t te;
AX = g_strdup_printf("%.0f", plat.fan_section_AX * 1000);
AY = g_strdup_printf("%.0f", plat.fan_section_AY * 1000);
Wx_left = g_strdup_printf("%.0f", plat.walkway_Wx_left * 1000);
Wx_right = g_strdup_printf("%.0f", plat.walkway_Wx_right * 1000);
Wy_bottom = g_strdup_printf("%.0f", plat.walkway_Wy_bottom * 1000);
Wy_top = g_strdup_printf("%.0f", plat.walkway_Wy_top * 1000);
x_one_unit = plat.fan_section_AX * plat.SDD_num;
x_total = x_one_unit * plat.unit_num + plat.gap_total + plat.walkway_Wx_left + plat.walkway_Wx_right;
y_one_unit = plat.fan_section_AY * plat.row_num;
y_total = y_one_unit + plat.walkway_Wy_bottom + plat.walkway_Wy_top;
scale = calcu_scale (width, height, x_total, y_total, Wx_left, Wy_top, cr, &X_MARGIN, &Y_MARGIN);
cairo_set_line_width (cr, NORMAL_LINE_WIDTH * 0.7);
for(unit_num = 1; unit_num <= plat.unit_num; unit_num++)
{
if(unit_num == 1)
x_base = X_MARGIN;
else
{
double total_gap = 0;
for(i = 0; i < unit_num - 1; i++)
total_gap += strtod(g_list_nth(plat.gap_w, i)->data, NULL);
x_base = X_MARGIN + (x_one_unit * (unit_num - 1) + total_gap) * scale;
}
y_base = Y_MARGIN;
for(i = 0, x_axis = plat.X_axis; x_axis; x_axis = x_axis->next, i++)
{
if(i == 0)
x = x_base;
else
x += plat.fan_section_AX * scale;
y = y_base;
cairo_move_to(cr, x, y);
y += y_one_unit * scale;
cairo_line_to(cr, x, y); /* vertical grid */
cairo_stroke(cr);
/* SDD */
if(i != plat.SDD_num)
{
cairo_arrow (cr, x + plat.fan_section_AX / 2 * scale, y + plat.walkway_Wy_bottom * scale, alpha, arrow_side_length, 1, arrow_tail_length);
cairo_save(cr);
cairo_set_line_width (cr, BOLD_LINE_WIDTH);
cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
cairo_move_to(cr, x_base + plat.fan_section_AX / 2 * scale, y + plat.walkway_Wy_bottom * scale + arrow_side_length + arrow_tail_length);
cairo_rel_line_to(cr, (x_one_unit - plat.fan_section_AX) * scale, 0);
cairo_stroke(cr);
cairo_restore(cr);
}
/* dim line for A1, A2 ... */
cairo_move_to(cr, x, y + plat.walkway_Wy_bottom * scale + arrow_side_length + arrow_tail_length + 10);
cairo_rel_line_to(cr, 0, 20);
cairo_get_current_point(cr, &x3, &y3);
if(i != plat.SDD_num)
{
cairo_text_align_horizontal_center (cr, AX, 0, 1, plat.fan_section_AX / 2 * scale, -10);
}
else
{
if(plat.unit_num > 1 && plat.has_gap && unit_num != plat.unit_num)
{
gap_value = strtod(g_list_nth(plat.gap_w, unit_num - 1)->data, NULL);
gap = g_strdup_printf("%.0f", 1000 * gap_value);
cairo_text_extents(cr, gap, &te);
if(gap_value * scale < te.x_bearing + te.width)
{
downward = 1;
downward_height = 11;
downward_width = gap_value * scale;
cairo_rel_move_to(cr, gap_value / 2 * scale - (te.x_bearing + te.width / 2), 10);
}
else
{
downward = 0;
cairo_rel_move_to(cr, gap_value / 2 * scale - (te.x_bearing + te.width / 2), -10);
}
cairo_show_text(cr, gap);
g_free(gap);
}
else
downward = 0;
}
if(unit_num == 1 && i == 0)
{
cairo_move_to(cr, x - plat.walkway_Wx_left * scale, y + plat.walkway_Wy_bottom * scale + arrow_side_length + arrow_tail_length + 10);
cairo_rel_line_to(cr, 0, 20);
cairo_rel_move_to(cr, -25, -10);
cairo_show_text(cr, Wx_left);
}
if(unit_num == plat.unit_num && i == plat.SDD_num)
{
cairo_move_to(cr, x + plat.walkway_Wx_right * scale, y + plat.walkway_Wy_bottom * scale + arrow_side_length + arrow_tail_length + 10);
cairo_rel_line_to(cr, 0, 20);
cairo_rel_move_to(cr, 3, -10);
cairo_show_text(cr, Wx_right);
}
if(plat.axis_style_SDD == 0) /* 递增 */
axis = g_strdup_printf("A%d", plat.start_axis_SDD + (unit_num - 1) * (plat.SDD_num + 1) + i);
else
axis = g_strdup_printf("A%d", plat.start_axis_SDD + (unit_num - 1) * (plat.SDD_num + 1) - i);
cairo_text_extents(cr, axis, &te);
if(!downward || ((i != plat.SDD_num) && (unit_num != 1 && i != 0)))
cairo_move_to(cr, x3 - te.width / 2, y3 + te.height + 2);
else
{
if(te.width < downward_width)
cairo_move_to(cr, x3 - te.width / 2, y3 + te.height + downward_height);
else
{
if(i == plat.SDD_num)
cairo_move_to(cr, x3 - te.width, y3 + te.height + downward_height);
else
cairo_move_to(cr, x3, y3 + te.height + downward_height);
}
}
cairo_show_text(cr, axis);
g_free(axis);
cairo_stroke(cr);
for(j = 0, y_axis = plat.Y_axis; y_axis; y_axis = y_axis->next, j++)
{
x1 = x_base;
if(j == 0)
y1 = y_base + y_one_unit * scale;
else
y1 -= plat.fan_section_AY * scale;
if(i == 0)
{
cairo_move_to(cr, x1, y1);
x1 += x_one_unit * scale;
cairo_line_to(cr, x1, y1); /* horizontal grid */
cairo_stroke(cr);
}
if(i != plat.SDD_num && j != plat.row_num) /* fan ring */
{
cairo_arc(cr, x + plat.fan_section_AX / 2 * scale, y1 - plat.fan_section_AY / 2 * scale, plat.fan_ring_opening_D / 2 * scale, 0, 2 * PI);
cairo_stroke(cr);
}
/* columns */
if(plat.col_type == 0) /* 砼悬臂住 */
{
if(plat.SDD_num % 2 == 0)
{
if((i + 1) % 2 == plat.SDD_col_first && (j + 1) % 2 == plat.row_col_first)
{
cairo_arc(cr, x, y1, COLUMN_RADIUS_ASSUME * scale, 0, 2 * PI);
cairo_fill(cr);
cairo_stroke(cr);
}
}
else
{
if(!plat.if_unit_symmetry || unit_num % 2 == 1)
{
if((i + 1) % 2 == plat.SDD_col_first && (j + 1) % 2 == plat.row_col_first)
{
cairo_arc(cr, x, y1, COLUMN_RADIUS_ASSUME * scale, 0, 2 * PI);
cairo_fill(cr);
cairo_stroke(cr);
}
}
else
{
if((i) % 2 == plat.SDD_col_first && (j + 1) % 2 == plat.row_col_first)
{
cairo_arc(cr, x, y1, COLUMN_RADIUS_ASSUME * scale, 0, 2 * PI);
cairo_fill(cr);
cairo_stroke(cr);
}
}
}
}
else if(plat.col_type == 1) /* 钢柱 */
{
cairo_save(cr);
cairo_set_line_width (cr, 2 * NORMAL_LINE_WIDTH);
cairo_move_to(cr, x, y1 - COLUMN_SQUARE_LEN_ASSUME / 2 * scale);
cairo_rel_line_to(cr, 0, COLUMN_SQUARE_LEN_ASSUME * scale);
cairo_move_to(cr, x - COLUMN_SQUARE_LEN_ASSUME / 2 * scale, y1 - COLUMN_SQUARE_LEN_ASSUME / 2 * scale);
cairo_rel_line_to(cr, COLUMN_SQUARE_LEN_ASSUME * scale, 0);
cairo_move_to(cr, x - COLUMN_SQUARE_LEN_ASSUME / 2 * scale, y1 + COLUMN_SQUARE_LEN_ASSUME / 2 * scale);
cairo_rel_line_to(cr, COLUMN_SQUARE_LEN_ASSUME * scale, 0);
cairo_stroke(cr);
cairo_restore(cr);
}
else if(plat.col_type == 2) /* 砼框架柱 */
{
cairo_rectangle(cr, x - COLUMN_SQUARE_LEN_ASSUME / 2 * scale, y1 - COLUMN_SQUARE_LEN_ASSUME / 2 * scale, COLUMN_SQUARE_LEN_ASSUME * scale, COLUMN_SQUARE_LEN_ASSUME * scale);
cairo_fill(cr);
cairo_stroke(cr);
}
/* dim line of AA, AB ... */
if(unit_num == plat.unit_num && i == 0)
{
cairo_move_to(cr, x1 + plat.walkway_Wx_right * scale + 10, y1);
cairo_rel_line_to(cr, 20, 0);
if(j != plat.row_num)
{
cairo_text_align_horizontal_center (cr, AY, 1, 1, -10, -plat.fan_section_AY / 2 * scale);
}
if(j == 0)
{
x2 = x1 + plat.walkway_Wx_right * scale + 10 + 20 - 5;
y2 = y1 + plat.walkway_Wy_bottom * scale;
cairo_move_to(cr, x1 + plat.walkway_Wx_right * scale + 10, y1);
cairo_rel_move_to(cr, 0, plat.walkway_Wy_bottom * scale);
cairo_rel_line_to(cr, 20, 0);
cairo_rel_move_to(cr, -10, 25);
cairo_save(cr);
cairo_rotate(cr, - PI / 2.0);
cairo_show_text(cr, Wy_bottom);
cairo_restore(cr);
}
if(j == plat.row_num)
{
cairo_move_to(cr, x1 + plat.walkway_Wx_right * scale + 10, y1);
cairo_rel_move_to(cr, 0, -plat.walkway_Wy_top * scale);
cairo_rel_line_to(cr, 20, 0);
cairo_rel_move_to(cr, -10, -1);
cairo_save(cr);
cairo_rotate(cr, - PI / 2.0);
cairo_show_text(cr, Wy_top);
cairo_restore(cr);
}
cairo_stroke(cr);
}
else if(unit_num == 1 && i == 0)
{
if(plat.axis_style_row == 0) /* 递增 */
axis = g_strdup_printf("A%c", plat.start_axis_row + j);
else
axis = g_strdup_printf("A%c", plat.start_axis_row - j);
cairo_text_extents(cr, axis, &te);
cairo_move_to(cr, x_base - plat.walkway_Wx_left * scale - 20, y1 + te.height / 2);
cairo_show_text(cr, axis);
g_free(axis);
cairo_stroke(cr);
}
}
}
}
/* walkway line */
x = X_MARGIN - plat.walkway_Wx_left * scale;
y = Y_MARGIN - plat.walkway_Wy_top * scale;
cairo_move_to(cr, x, y);
cairo_rel_line_to(cr, x_total * scale, 0);
cairo_rel_line_to(cr, 0, y_total * scale);
cairo_rel_line_to(cr, -x_total * scale, 0);
cairo_rel_line_to(cr, 0, -y_total * scale);
cairo_stroke(cr);
/* dim total line for A1, A2 ... */
cairo_move_to(cr, X_MARGIN - plat.walkway_Wx_left * scale, Y_MARGIN + (y_one_unit + plat.walkway_Wy_bottom) * scale + arrow_side_length + arrow_tail_length + 10 + 20 - 5);
cairo_rel_line_to(cr, x_total * scale, 0);
cairo_stroke(cr);
/* dim total line for AA, AB ... */
cairo_move_to(cr, x2, y2);
cairo_rel_line_to(cr, 0, -y_total * scale);
cairo_stroke(cr);
g_free(AX);
g_free(AY);
g_free(Wx_left);
g_free(Wx_right);
g_free(Wy_bottom);
g_free(Wy_top);
}
The expose-event signal:
static gboolean ACC_GA_expose (GtkWidget *canvas, GdkEventExpose *event, gpointer user_data)
{
cairo_t *cr;
cr = gdk_cairo_create (canvas->window);
if(plat.if_GA_draw)
{
ACC_GA_draw (cr, canvas->allocation.width, canvas->allocation.height);
}
else
{
char *local = char_to_utf8 ("Press button \"生成GA\" to update!!");
cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size(cr, 0.35);
cairo_move_to(cr, 200, 200);
cairo_show_text(cr, local);
g_free(local);
}
cairo_destroy (cr);
return FALSE;
}
In main() function:
.
.
.
canvas = gtk_drawing_area_new();
g_object_set_data(G_OBJECT(window), "plat_GA_canvas", canvas);
plat.if_GA_draw = 0;
gtk_widget_show(canvas);
gtk_box_pack_start(GTK_BOX(sub_main_box), canvas, TRUE, TRUE, 0);
g_signal_connect(G_OBJECT(canvas), "expose-event", G_CALLBACK(ACC_GA_expose), window);
.
.
.
+++++++++++++++++++++++++++++++++++++++++++++++++++
GCC 3.4.5 and GCC 4.7.2 are ABI incompatible. You need to rebuild all your code.
I found a bug in my program. The bug is that expose-event for a canvas is exposed more than once and it crashed for the second time. But I don't know why the bug didn't come up in gcc3.4.5 or in a pure Linux environment. After I revised my program, it worked for mingw-gcc4.7.2, mingw-gcc3.4.5 and a pure Linux environment.
On the other hand, gcc4.7.2 works for pre-compiled gtk2.10.11 but fails for pre-compiled gtk2.24.10 in my situation. This is maybe due to ABI issue.
I have posted 2 other posts: gtk window moved then crash and compile GTK+ in MinGW, but failed. They are more or less related with this post.

Resources