I know this may be a stupid question but I really can't seem to find an answer anywhere. I created a triangle like this:
QPolygonF triangle;
triangle.append(QPointF(0., -15));
triangle.append(QPointF(30., 0));
triangle.append(QPointF(0., 15));
triangle.append(QPointF(15., 0));
This triangle shall represent a car on my map, and I need to animate it. So i did the following:
QGraphicsItemAnimation *animation;
QGraphicsPolygonItem *clientCar;
QTimeLine *timer;
animation = new QGraphicsItemAnimation;
timer = new QTimeLine(10000);
timer->setFrameRange(0, 100);
clientCar = scene->addPolygon(triangle, myPen, myBrush)
animation->setItem(clientCar);
animation->setTimeLine(10000);
animation->setPosAt(0.f / 200.f, map.street1);
animation->setRotationAt(10.f / 200.f, 90.f);
animation->setPosAt(10.f / 200.f, map.street2);
animation->setRotationAt(20.f / 200.f, 180.f);
animation->setPosAt(20.f / 200.f, map.street3);
scene->addItem(clientCar);
ui->graphicsView->setScene(scene);
timer->start();
The problem is, when it reaches an intersection(road cross) it should rotate so that it will face the road it's going next. As you can see above, I tried using setRotationAt(), but what it does is slowly rotating between intersections until it reaches the next one. It should be turning in an instant, only when it's changing it's way. Any help?
From the doc:
QGraphicsItemAnimation will do a simple linear interpolation between
the nearest adjacent scheduled changes to calculate the matrix. For
instance, if you set the position of an item at values 0.0 and 1.0,
the animation will show the item moving in a straight line between
these positions. The same is true for scaling and rotation.
The linear interpolation part will do the trick.
So why don't you try this:
//animation->setPosAt(0.f / 200.f, map.street1);
//animation->setRotationAt(10.f / 200.f, 90.f);
//animation->setPosAt(10.f / 200.f, map.street2);
//animation->setRotationAt(20.f / 200.f, 180.f);
//animation->setPosAt(20.f / 200.f, map.street3);
static float const eps = 1.f / 200.f;
QVector<float> steps = {0.f, 10.f / 200.f, 20.f / 200.f};
QVector<QPointF> points = {map.street1, map.street2, map.street3};
QVector<float> angles = {0, 90.f, 180.f};
// initial conditions
animation->setPosAt(steps[0], points[0]);
animation->setRotationAt(steps[0], angles[0]);
// for each intersection
for(size_t inters = 1; inters < points.size(); ++inters)
{
animation->setRotationAt(steps[inters] - eps, angles[inters - 1]);
animation->setPosAt(steps[inters], points[inters]);
animation->setRotationAt(steps[inters] + eps, angles[inters]);
}
Related
I'm currently trying to implement a zoom feature for the Mandelbrot Set code I've been working on. The idea is to zoom in/out where I left/right click. So far whenever I click the screen, the fractal is indeed zoomed in. The issue is that the fractal is rendered not at the origin-- in other words, it's not zoomed in on the point I want. I was hoping through here I can get both a code review and conceptual understanding of how to zoom in on a point in general.
Here's how I transformed the pixel coordinate before I used escape algorithm:
MandelBrot.Frag
vec2 normalizedFragPos = (gl_FragCoord.xy/windowSize); //normalize fragment position
dvec2 scaledFragPos = normalizedFragPos*aspectRatio;
scaledFragPos -= aspectRatio/2; //Render the fractal at center of window
scaledFragPos /= scale; //Factor to zoom in or out coordinates.
scaledFragPos -= translation; //Translate coordinate
//Escape Algorithm Below
On my left-click handle, I thought I should convert the cursor position to the same coordinate range as the Mandelbrot Range. So I basically did the same thing I did in the fragment shader:
Window.cpp
float x_coord{ float(GET_X_LPARAM(informaton_long))/size.x }; // normalized mouse x-coordinate
float y_coord{ float(GET_Y_LPARAM(informaton_long))/size.y }; // normalized mouse y-coordinate
x_coord *= aspectRatio[0]; //move point based of relative position to length of window.
y_coord *= aspectRatio[1]; //move point based of relative position to width of window.
x_coord /= scale; //Scale point to match previous zoom factor
y_coord /= scale; //Scale point to match previous zoom factor
translation[0] = x_coord;
translation[1] = y_coord;
//increment scale
scale += .15f;
Lets apply some algebra. Your shader does the following transformation:
mandelbrotCoord = aspectRatio * (gl_FragCoord / windowSize - 0.5) / scale - translation
When we zoom in on mouseCoord, we want to change the scale and adjust the translation such that the madelbrotCoord under the mouse stays the same. To do that we first calculate the mandelbrotCoord under the mouse using the old scale:
mandelbrotCoord = aspectRatio * (mouseCoord / windowSize - 0.5) / scale - translation
Then change the scale (which should be changed exponentially BTW):
scale *= 1.1;
Then solve for the new translation:
translation = aspectRatio * (mouseCoord / windowSize - 0.5) / scale - mandelbrotCoord
Also notice that your system probably reports the mouse coordinate with the y coordinate increasing downwards, whereas OpenGL has its window y coordinate increasing upwards (unless you override it with glClipControl). Therefore you're likely to need to flip the y coordinate of the mouseCoord too.
mouseCoord[1] = windowSize[1] - mouseCoord[1];
For best result I would also adjust the mouse coordinates to be in the middle of the pixel (+0.5, +0.5).
Putting it all together:
float mouseCoord[] = {
GET_X_LPARAM(informaton_long) + 0.5,
GET_Y_LPARAM(informaton_long) + 0.5
};
mouseCoord[1] = size[1] - mouseCoord[1];
float anchor[] = {
aspectRatio[0] * (mouseCoord[0] / size[0] - 0.5) / scale - translation[0],
aspectRatio[1] * (mouseCoord[1] / size[1] - 0.5) / scale - translation[1]
};
scale *= 1.1;
translation[0] = aspectRatio[0] * (mouseCoord[0] / size[0] - 0.5) / scale - anchor[0];
translation[1] = aspectRatio[1] * (mouseCoord[1] / size[1] - 0.5) / scale - anchor[1];
Note: some of the math above might be canceled away. However, if you want to implement a proper pan&zoom functionality (when you can zoom with the mouse wheel while you are panning) then you'll need to store the initial mandelbrotCoord of where the panning started, and then reuse it on subsequent motion and wheel events till the mouse is released. Surprisingly large amount of image viewers get this part wrong!
I've successfully implemented dragging a sprite on the screen, by simply remembering touch position on touchbegan and translating sprite by currentTouchPoint - startingTouchPoint, which gives me nice dragging. But now I want to sprite track my finger instead so it also should rotate. This would give me an advantage of making user able to rotate sprite as well as drag it around with just one finger.
I was getting unexpected results, but I've ran into this code:
http://www.freeactionscript.com/2011/01/mouse-follow-with-easing-smooth-rotation-v2/
There's a demo so you could try it on the browser.
It's pretty much the same I need except sprite should be a lot faster (near the finger all the time) and stop after reaching it's destination instead of orbiting around (while holding touch). I've tried to port it to cocos2d-x, but it doesn't work the same. Well, updatePosition() looks good, sprite is going where it should be pretty much, but updateRotation() is a total mess. It's rotating in strange directions and sometimes even makes a full very fast circle in opposite direction.
onTouchBegan:
//check if any sprite is touched and then assign it to draggedItem
onTouchMove:
_destinationX = touchPoint.x;
_destinationY = touchPoint.y;
onTouchEnd:
//make draggedItem nullptr
void CreatorScene::update(float delta){
if(draggedItem != nullptr){
updateItemPosition();
updateItemRotation();
}
}
void CreatorScene::updateItemRotation()
{
// calculate rotation
_dx = draggedItem->getPositionX() - _destinationX;
_dy = draggedItem->getPositionY() - _destinationY;
// which way to rotate
float rad = atan2(_dy, _dx);
if(_dy < 0) rad += 2 * M_PI;
float rotateTo = CC_RADIANS_TO_DEGREES(rad);
// keep rotation positive, between 0 and 360 degrees
if (rotateTo > draggedItem->getRotation() + 180) rotateTo -= 360;
if (rotateTo < draggedItem->getRotation() - 180) rotateTo += 360;
// ease rotation
_trueRotation = (rotateTo - draggedItem->getRotation()) / ROTATE_SPEED_MAX;
// update rotation
draggedItem->setRotation(draggedItem->getRotation() + _trueRotation);
}
void CreatorScene::updateItemPosition()
{
// check if mouse is down
// if (_isActive)
// {
// update destination
// _destinationX = stage.mouseX;
// _destinationY = stage.mouseY;
// update velocity
_vx += (_destinationX - draggedItem->getPositionX()) / MOVE_SPEED_MAX;
_vy += (_destinationY - draggedItem->getPositionY()) / MOVE_SPEED_MAX;
// }
// else
// {
// // when mouse is not down, update velocity half of normal speed
// _vx += (_destinationX - _player.x) / _moveSpeedMax * .25;
// _vy += (_destinationY - _player.y) / _moveSpeedMax * .25;
// }
// apply decay (drag)
_vx *= DECAY;
_vy *= DECAY;
// if close to target, slow down turn speed
if (sqrtf((_dx*_dx)+(_dy*_dy)) < 50)
{
_trueRotation *= .5;
}
// update position
draggedItem->setPosition(draggedItem->getPosition() + Point(_vx, _vy));
}
What's wrong here? How can I achieve such a smooth movement?
Edit:
Functionality that I want to exactly achieve is here:
https://www.youtube.com/watch?v=RZouMyyNGG8
You can see it on 2:10.
I have problem with the animation of a ball, which flies according to the equations of motion
x = speed*cos(angle) * time;
y = speed*sin(angle) * time - (g*pow(time,2)) / 2;
I create a QGraphicsScene with QGraphicsEllipseItem
QGraphicsScenescene = new QGraphicsScene;
QGraphicsEllipseItemball = new QGraphicsEllipseItem(0,scene);
then I try to animate ball
scene->setSceneRect( 0.0, 0.0, 640.0, 480.0 );
ball->setRect(15,450,2*RADIUS,2*RADIUS);
setScene(scene);
QTimeLine *timer = new QTimeLine(5000);
timer->setFrameRange(0, 100);
QGraphicsItemAnimation *animation = new QGraphicsItemAnimation;
animation->setItem(ball);
animation->setTimeLine(timer);
animation->setPosAt(0.1, QPointF(10, -10));
timer->start();
But I can't understand how setPosAt works and how I can use my calculated x,y in this case.
The official Qt documentation for setPosAt is very short and incomprehensible.
You need to call setPosAt() multiple times with various values of (step) between 0.0 and 1.0. Then when you play the animation, Qt will use linear interpolation to animate smoothly between the points you set, as Qt increases its "current step" value from 0.0 to 1.0.
For example, to make the ball move in a straight line you could do something like:
animation->setPosAt(0.0, QPointF(0,0));
animation->setPosAt(1.0, QPointF(10,0));
... or to make the ball go up, and then down, you could do:
animation->setPosAt(0.0, QPointF(0,0));
animation->setPosAt(0.5, QPointF(0,10));
animation->setPosAt(1.0, QPointF(0,0));
... so to get the arc you want you could do something like:
for (qreal step=0.0; step<1.0; step += 0.1)
{
qreal time = step*10.0; // or whatever the relationship should be between step and time
animation->setPosAt(step, QPointF(speed*cos(angle) * time, speed*sin(angle) * time - (g*pow(time,2)) / 2);
}
As I am newbie to cocoa2d I am struggling alot to rotate the physics or dynamic body along an arc path.
The way I tried is as follows:
#define COS_ANIMATOR(position, timeCount, speed, waveMagnitude) ((cosf(timeCount * speed) * waveMagnitude) + position)
#define SIN_ANIMATOR(position, timeCount, speed, waveMagnitude) ((sinf(timeCount * speed) * waveMagnitude) + position)
CCSpriteBatchNode *pipe_parent = [CCSpriteBatchNode batchNodeWithFile:#"pipe.png" capacity:100];
CCTexture2D *pipeSpriteTexture_ = [pipe_parent texture];
PhysicsSprite *pipeSprite = [PhysicsSprite spriteWithTexture:pipeSpriteTexture_ rect:CGRectMake(0 ,0 ,55,122)];
//pipe = [CCSprite spriteWithFile:#"pipe.png"
// rect:CGRectMake(0, 0, 55, 122)];
[self addChild:pipeSprite];
// pipe.position = ccp(s.width/2 , 420.0);
b2BodyDef myBodyDef;
myBodyDef.type = b2_staticBody; //this will be a dynamic body
myBodyDef.position.Set(((s.width/2) - 90)/PTM_RATIO, 420.0/PTM_RATIO); //set the starting position
myBodyDef.angle = 0; //set the starting angle
b2Body* staticBody = world->CreateBody(&myBodyDef);
b2PolygonShape boxShape;
boxShape.SetAsBox(1,1);
b2FixtureDef boxFixtureDef;
boxFixtureDef.shape = &boxShape;
boxFixtureDef.density = 1;
boxFixtureDef.userData = pipeSprite;
boxFixtureDef.filter.groupIndex = -1;
staticBody->CreateFixture(&boxFixtureDef);
[pipeSprite setPhysicsBody:staticBody];
-(void) draw
{
//
// IMPORTANT:
// This is only for debug purposes
// It is recommend to disable it
//
[super draw];
const CGPoint newSpritePosition = ccp(COS_ANIMATOR(150, mTimeCounter, 0.05,50), SIN_ANIMATOR(400, mTimeCounter, -0.05, 50));
pipeSprite.position = newSpritePosition;
ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position );
kmGLPushMatrix();
world->DrawDebugData();
kmGLPopMatrix();
}
on following this approach my sprite rotating in circular motion instead of rotating in an arc path.
Please give your ideas or suggestions.
Thanks all
I'm not entirely sure what it is you are looking to accomplish when you talk about rotating in an arc path. I only see you setting a position, not a rotation, so are you just wanting to set a position, or a rotation, or both? Your position code looks like you are trying to achieve a circular (or elliptical) path because you are using the sine and cosine in the x,y position.
If you're looking to move a sprite along a sine curve, I did that today and it took a bit of trial and error. I had some variables for the amplitude and period, and from there I traced out a nice sine curve movement in the sprite's update: method.
CGPoint initialPosition; // set this to the sprite's initial position
float amplitude; // in points
float period; // in points
float y, x = initialPosition.x;
-(void) update:(ccTime)dt
{
x += dt * 100; // speed of movement across the screen. Picked by trial and error.
y = initalPosition.y + amplitude * sinf((x - initialPosition.x)/period);
sprite.position = ccp(x,y);
sprite.rotation = cosf((x - initialPosition.x)/period); // optional if you want to rotate along the path as well
}
Don't know if this is anything you are looking for but it might give you some ideas.
I am trying to calculate the vertices of a rotated rectangle (2D).
It's easy enough if the rectangle has not been rotated, I figured that part out.
If the rectangle has been rotated, I thought of two possible ways to calculate the vertices.
Figure out how to transform the vertices from local/object/model space (the ones I figured out below) to world space. I honestly have no clue, and if it is the best way then I feel like I would learn a lot from it if I could figure it out.
Use trig to somehow figure out where the endpoints of the rectangle are relative to the position of the rectangle in world space. This has been the way I have been trying to do up until now, I just haven't figured out how.
Here's the function that calculates the vertices thus far, thanks for any help
void Rect::calculateVertices()
{
if(m_orientation == 0) // if no rotation
{
setVertices(
&Vertex( (m_position.x - (m_width / 2) * m_scaleX), (m_position.y + (m_height / 2) * m_scaleY), m_position.z),
&Vertex( (m_position.x + (m_width / 2) * m_scaleX), (m_position.y + (m_height / 2) * m_scaleY), m_position.z),
&Vertex( (m_position.x + (m_width / 2) * m_scaleX), (m_position.y - (m_height / 2) * m_scaleY), m_position.z),
&Vertex( (m_position.x - (m_width / 2) * m_scaleX), (m_position.y - (m_height / 2) * m_scaleY), m_position.z) );
}
else
{
// if the rectangle has been rotated..
}
//GLfloat theta = RAD_TO_DEG( atan( ((m_width/2) * m_scaleX) / ((m_height / 2) * m_scaleY) ) );
//LOG->writeLn(&theta);
}
I would just transform each point, applying the same rotation matrix to each one. If it's a 2D planar rotation, it would look like this:
x' = x*cos(t) - y*sin(t)
y' = x*sin(t) + y*cos(t)
where (x, y) are the original points, (x', y') are the rotated coordinates, and t is the angle measured in radians from the x-axis. The rotation is counter-clockwise as written.
My recommendation would be to do it out on paper once. Draw a rectangle, calculate the new coordinates, and redraw the rectangle to satisfy yourself that it's correct before you code. Then use this example as a unit test to ensure that you coded it properly.
I think you were on the right track using atan() to return an angle. However you want to pass height divided by width instead of the other way around. That will give you the default (unrotated) angle to the upper-right vertex of the rectangle. You should be able to do the rest like this:
// Get the original/default vertex angles
GLfloat vertex1_theta = RAD_TO_DEG( atan(
(m_height/2 * m_scaleY)
/ (m_width/2 * m_scaleX) ) );
GLfloat vertex2_theta = -vertex1_theta; // lower right vertex
GLfloat vertex3_theta = vertex1_theta - 180; // lower left vertex
GLfloat vertex4_theta = 180 - vertex1_theta; // upper left vertex
// Now get the rotated vertex angles
vertex1_theta += rotation_angle;
vertex2_theta += rotation_angle;
vertex3_theta += rotation_angle;
vertex4_theta += rotation_angle;
//Calculate the distance from the center (same for each vertex)
GLfloat r = sqrt(pow(m_width/2*m_scaleX, 2) + pow(m_height/2*m_scaleY, 2));
/* Calculate each vertex (I'm not familiar with OpenGL, DEG_TO_RAD
* might be a constant instead of a macro)
*/
vertexN_x = m_position.x + cos(DEG_TO_RAD(vertexN_theta)) * r;
vertexN_y = m_position.y + sin(DEG_TO_RAD(vertexN_theta)) * r;
// Now you would draw the rectangle, proceeding from vertex1 to vertex4.
Obviously more longwinded than necessary, for the sake of clarity. Of course, duffymo's solution using a transformation matrix is probably more elegant and efficient :)
EDIT: Now my code should actually work. I changed (width / height) to (height / width) and used a constant radius from the center of the rectangle to calculate the vertices. Working Python (turtle) code at http://pastebin.com/f1c76308c