I'm having this Processing issue where I'm trying to make my ariplane do a 180 turn-over right before it gets to the end of the drawing... It looks fairly simple but I just can't get it to work. Help would be much appreciated.
As I have other shapes (not showned in code below) I finally managed to use pushMatrix(); & popMatrix(); to only impact the airplane. Although rotating it brings me sideways.
int globalX = 0;
int globalY = 600;
int vitesse = 3;
void setup() {
size(800, 600);
rectMode(CENTER);
}
void draw() {
background(255);
pushMatrix();
bouger();
tourner();
dessinerAvion(globalX, globalY);
popMatrix();
}
void bouger() {
// Change the x location by vitesse
//globalX = globalX + vitesse;
globalY = globalY - vitesse;
}
void tourner() {
if (globalY < 0) {
for (int i = 10; i > 0; i = i-1) {
rotate(PI/3.0);
//rotate(radians(10));
}
for (int i = 10; i > 0; i = i-1) {
rotate(PI/3.0);
//rotate(radians(10));
vitesse = vitesse * -1;
}
}
}
void dessinerAvion(int x, int y) {
rectMode(CENTER);
// corps
fill(156,21,21);
ellipse(x + 250, y + 100, 50, 100);
rect(x + 250, y + 200, 50, 250,60);
// reacteur gauche
rect(x + 125, y + 225, 25, 50, 75);
// reacteur droite
rect(x + 375, y + 225, 25, 50, 75);
fill(210,14,14);
// aile gauche
quad(x + 75, y + 275, x + 75, y + 250, x + 225, y + 175, x + 225, y + 250);
// aile droite
quad(x + 425, y + 275, x + 425, y + 250, x + 275, y + 175, x + 275, y + 250);
fill(112,14,14);
// ailette gauche
triangle(x + 225, y + 290, x + 226, y + 310, x + 175, y + 310);
// ailette droites
triangle(x + 275, y + 290, x + 274, y + 310, x + 325, y + 310);
// aile verticale arrière
triangle(x + 250, y + 285, x + 248, y + 310, x + 252, y + 310);
}
Matrix are weird animals. They let you take a bunch of shapes and translate, rotate or scale them all at once, but only through playing with the coordinate system, which implies that the way you draw the shapes that you'll process will have a huge impact on how they react.
This is your plane on a neutral grid (by which I mean that globalX and globalY are 0):
Every square on this grid has a 20 pixels side. As you can see, your plane is far from the [0, 0] point, which is the start of the coordinate system it's drawn into. Also, when you modify it's coordinates, you're doing it through modifying it's coordinates, as we can see on this line of code:
void dessinerAvion(int x, int y)
The problem here is that applying a geometric transformation to an object with these coordinates can be counterintuitive. To simplify the matter, imagine the whole coordinate system that the plane use as if it was an image, like a png. The top left of the image is the [0, 0] point. When you change the plane's y coordinates for a higher number, your png image gets taller. If you change the x coordinate of the plane for a bigger number, the png image gets wider. You get the general idea.
When you apply a rotation to the plane, it's the entire 'png image', the entire coordinate system, that gets rotated. It's the same for any other transformation, too. Now, experimentally, look at what happens if I do this:
for(float i=0; i<20; i+=1) {
pushMatrix();
rotate(PI/i);
dessinerAvion(0, 0);
popMatrix();
}
As you can see, the plane isn't rotating on itself. It's the whole coordinate system that rotates, pivoting on the [0, 0] point, and it brings the plane with itself. That's the deal with matrix: they keep a lot of geometry waaay more simple, but you have to work accordingly.
Which brings me back to the previous point: you're updating the coordinates where you draw the plane. Then you use a matrix. Your updated plane will rotate in an unexpected way because you apply 2 different logic to your transformations: on one hand you modify coordinates (which is fine), while on the other you apply a transformation on a matrix (which is fine too). The issue is to mix those without acknowledging their differences.
Honestly, unless you have a specific thing in mind, you shouldn't mix those to move a single object.
The obvious solution to the first part of this issue would be to translate the plane instead of moving it's coordinates:
void draw() {
background(255);
bouger(); // updating coordinates, this doesn't need to be in the matrix block
pushMatrix();
//tourner();
translate(globalX, globalY);
dessinerAvion(0, 0); // zero here as the translation is doing that part of the job now
popMatrix();
}
Now you should see... well, you should see no difference at all. That's normal. The real difference is not in how the sketch appears, but how it's processed behind the scenes. Now your plane is a static object on which we apply geometric transformations.
Now to the other part of your problem: animating an object. Well, to be honest the animation part is quite easy, but there's a lot of work to do beforehand, more precisely to fix the plane's original coordinates so they are easier to manage. Let me explain:
If you look at the first image where I show the plane without any geometric transformation, you'll notice that the left wing starts at about ~75 pixels from the 0 point, and that the nose of the plane is ~45 pixels away from the 0 on the y axis. Currently, that's the rotation's anchor point. If you want to rotate the plane in a gracious manner, you'll have to fix this. There are many ways to proceed, follow me.
Take notes, because for some reason very few people will explain in simple words how to rotate on an anchor point using a matrix.
To rotate a shape on a specific anchor point, you have to proceed as follow:
pushMatrix
translate to the shape's destination
rotate
draw the shape using [0, 0] as it's anchor point
popMatrix
So if you want to rotate your plane on itself, thus deciding on an angle then moving it to it's destination before you draw the plane, you act as follow:
void draw() {
background(255);
// grid
stroke(0);
for (int i=0; i<height; i+=20) {
line(0, i, width, i);
}
for (int i=0; i<width; i+=20) {
line(i, 0, i, height);
}
pushMatrix();
translate(200, 200); // for the demonstration only
globalAngle--;
rotate(radians(globalAngle)); // I'm lazy and I don't want to do this in radians so I use degrees and convert them
dessinerAvion(-250, -200); // the center of the plane is the [250, 200] point. That's my anchor point.
popMatrix();
}
Which gives us this result:
Now, using the first translate, you can draw the plane wherever you want, and you can use the rotation to make it face any direction you want it to face.
Using these, it would be real easy to make the plane cross the screen back and forth:
void draw() {
background(255);
drawGrid();
// implementing the "u-turn"
globalY -= vitesse;
if (globalY < -200) {
globalX -= 200;
globalAngle = 180;
vitesse = -vitesse;
}
// drawing the sketch
pushMatrix();
translate(globalX, globalY);
rotate(radians(globalAngle));
dessinerAvion(-250, -200);
popMatrix();
}
Now, if we want to watch the plane turn, we have several options. You can rotate the plane frame by frame while plotting it's course. That's a good option. But I'm lazy, as previously stated, so instead I'll move the anchor point somewhere left of the plane and use it to rotate around it in a gracious manner:
void draw() {
background(255);
drawGrid();
pushMatrix();
translate(globalX, globalY);
tourner();
bouger();
dessinerAvion(0, -200); // this coordinate sets my anchor point
popMatrix();
}
void tourner() {
if (globalY < 300) { // starting the u-turn
vitesse = 0; // The plane won't move while it's rotating... except for the rotation itself
globalAngle--; // rotating counterclockwise 1 degree per frame
if (globalAngle<-180) { // u-turn completed
globalAngle = -180;
vitesse = -3;
}
}
rotate(radians(globalAngle));
}
I think that does it. I hope this helps. I'll lurk around in case you have questions about this answer.
Have fun!
I have a problem with jCanvas which is a framework drawing canvas and jquery.
When I set x = 0 , y = 0 The .drawRect output should be same as the pure javascript result.
I tried the document and google but I did not find anything to solve this.
Screen shoot:
Pure js result
jCanvas result
Please help me.
Many thanks.
// Pure javascript
var canvas = document.getElementsByTagName('canvas')[0];
var ctx = canvas.getContext("2d");
ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, 100, 100);
// jCanvas
$('canvas').drawRect({
fillStyle: '#000',
x: 0, y: 0,
width: 100,
height: 100
});
To run this code, please copy and pass to this sanbox.
https://projects.calebevans.me/jcanvas/sandbox/
From jCanvas documentation:
The [.drawRect's] fromCenter property determines if a rectangle’s x and y properties lie at its center (as opposed to its top-left corner). This property is true by default.
So yeah. Your X and Y with jCanvas, refer to the center of the rectangle, unlike canvas' fillRect X and Y, which refer to the top left corner of the rectangle.
In the below image, I would like to have the score as text within the green ellipse. However, the ellipse is being drawn over the text, regardless of the order of the ellipse() and text() functions in the loop. Can anyone suggest why? My draw loop is shown below.
import processing.core.PApplet;
import processing.core.PFont;
public void drawHUD(PApplet marker, Clock time, int score)
{
PFont font = marker.createFont("Impact", 25, true);
marker.textFont(font);
marker.ellipseMode(CENTER);
fill(25, 100, 25);
marker.ellipse(50, marker.height - 50, 75, 50);
marker.noFill();
marker.text("Score: ", 25, marker.height - 100);
marker.text(score, 50, marker.height - 50);
marker.text("Seconds left: ", marker.width - 175, marker.height - 100);
marker.text(time.toString(), marker.width - 125, marker.height - 50);
}
Try replacing marker.noFill(); with marker.fill(255);. I don't believe noFill() works for text, and I have a feeling the text is being drawn above the ellipse, but is just the same colour as the ellipse so can't be seen.
I deleted the ellipsemode. It got rid of the center ellipse, but it got the text to the front.
Here is Ruby code:
require 'rmagick'
include Magick
img = Image.new(300, 300)
draw = Draw.new
draw.line(0, 150, 300, 150)
draw.line(150, 0, 150, 300)
# for each of known gravity constants...
%w[NorthWestGravity NorthGravity NorthEastGravity WestGravity CenterGravity EastGravity SouthWestGravity SouthGravity SouthEastGravity].
each{|g|
# set gravity to this value...
draw.gravity Magick.const_get(g)
# ...and draw text with this constant name
draw.text 150, 150, g
}
draw.draw(img)
img.write('tmp/gravity.png')
Here is image which it produces:
For SouthEast/NorthWest and similar gravities result is as expected (text is near 150,150, moved in desired direction). But for South, North and others result is really pretty weird.
As far as I can understand from code, RMagick just translates gravity and text commands into corresponding ImageMagick drawing primitives, so, I suppose its something in ImageMagick's gravity concept that I can't get.
What is it?..
I suppose its something in ImageMagick's gravity concept that I can't get.
What is it?..
The key to understanding what's going on is to locate the CenterGravity text.
Shifted left by 150px, and down by 150px.
Now compare compare NorthWestGravity position.
Also translated left & down by 150px respectively. Seeing a trend?
Your issue is with this line...
draw.text 150, 150, g
The Magick::Draw API maps to MVG spec. Use Magick::Draw.push & Magick::Draw.pop to control drawing context.
Edit from comments...
For setting the origin of text to be drawing, you'll need to calculate the position after evaluation the text/type metrics.
Example.
require 'rmagick'
include Magick
img = Image.new(300, 300) {
self.background_color = "palegreen"
}
draw = Draw.new
dotes = Draw.new # Dotes added for point of origin
dotes.fill = "red"
cursor = 1
# for each of known gravity constants...
%w[NorthWestGravity NorthGravity NorthEastGravity WestGravity CenterGravity EastGravity SouthWestGravity SouthGravity SouthEastGravity].
each{|g|
offsetX = 150
offsetY = cursor * 25
dotes.circle offsetX, offsetY, offsetX+2, offsetY+2
# Get metrics of text
metrics = draw.get_type_metrics(img, g)
# Full width
if %w[NorthWestGravity WestGravity SouthWestGravity].include? g then
offsetX -= metrics[:width]
end
# Full height
if %w[SouthWestGravity SouthGravity SouthEastGravity].include? g then
offsetY += metrics[:ascent]
end
# Half width
if %w[NorthGravity SouthGravity CenterGravity].include? g then
offsetX -= metrics[:width] / 2
end
# Half height
if %w[WestGravity CenterGravity EastGravity].include? g then
offsetY += metrics[:ascent] / 2
end
draw.text offsetX, offsetY, g
cursor += 1
}
dotes.draw(img)
draw.draw(img)
img.write('output.png')
I've created an SVG canvas inline in HTML, dynamically using keith-wood jquery plugin.
I've used the viewBox attribute in the SVG element to scale the contents of the SVG canvas.
Inside the SVG canvas, I have a rectangle drawn, with a repeated pattern (tiles), each a 1x1 unit.
An 800x800 board:
var svg = $(containerDiv).svg('get');
svg.configure({viewBox: minX + "" + minY + "" + width + "" + height});
var pat1 = svg.pattern('pat1', 0, 0, 1, 1, {patternUnits: 'userSpaceOnUse'});
var rectTile = svg.rect(pat1, 0, 0, 1, 1);
var rectBoard = svg.rect(0, 0, 800, 800, {fill: 'url(#pat1)'});
When the user hovers over one of the elements inside the SVG, I'd like to convert the mouse event's pageX and pageY pixel coordinates to the coordinates of the tile, given the viewBox scaling. So if the user hovers over the first tile, I'd like {x: 0, y: 0} returned, given e.pageX and e.pageY where e is the mousemove jquery event object.
How do I calculate this?
Thanks