I have a Qt application for creating some composite graphic pictures purpose. E.g. user selects some image component from the toolbar, places it on the QGraphicsScene, transforms it and gets profit.
The question is: how to perform scale and rotation operations on the selected items.
For now i have inherited my own Scene class and using this code for mouseMoveEvent:
void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *evt)
{
QGraphicsScene::mouseMoveEvent(evt);
//bool isLMB = QApplication::mouseButtons() & Qt::LeftButton;
bool isShift = QApplication::keyboardModifiers() & Qt::ShiftModifier;
bool isCtrl = QApplication::keyboardModifiers() & Qt::ControlModifier;
if (this->newChip)
{
qreal ox = this->newChip->pos().x();
qreal oy = this->newChip->pos().y();
qreal nx = evt->scenePos().x();
qreal ny = evt->scenePos().y();
qreal dx = nx - ox;
qreal dy = ny - oy;
qreal w = this->newChip->transform().m11() * this->newChip->boundingRect().width();
qreal h = this->newChip->transform().m22() * this->newChip->boundingRect().height();
qreal l = sqrt(pow((float) dx, (float) 2.f) + pow((float) dy, (float) 2.f));
if (!isShift && !isCtrl)
{
this->newChip->setPos(evt->scenePos());
}
if (isShift)
{
//this->newChip->scale(fabs(w / dx), fabs(h / dy));
qDebug() << "scale: " << dx / w << dy / h;
}
if (isCtrl)
{
//this->newChip->rotate((qreal) (acos((float) (dx / l)) * 180.f / 3.14f));
qDebug() << "rotate: " << acos((float) (dx / l)) * 180.f / 3.14f;
}
}
}
But i do want a more pretty rotation and scale operations, just like any painting software when operating a selection, for example:
P.S.: yeah, i've started modifying the 40000 chips demo application =)
Related
I would like to get the text labels (percentages) centered within each pie slice. It currently works a bit for two of the quadrants:
What am I doing wrong?
void PieChartWidget::paintEvent(QPaintEvent *) {
QPainter painter(this);
QRectF size;
painter.setPen(QPen(Qt::black, 2));
if (this->height() > this->width()) {
size = QRectF(5, 5, this->width() - 10, this->width() - 10);
} else {
size = QRectF(5, 5, this->height() - 5, this->height() - 10);
}
double sum = 0.0, startAng = 0.0;
double angle, endAng;
double percent;
for (int i = 0; i < qvValues.size(); i++) {
sum += qvValues[i];
}
for (int i = 0; i < qvValues.size(); i++) {
percent = qvValues[i] / sum;
angle = percent * 360.0;
endAng = startAng + angle;
painter.setBrush(qvColors[i]);
painter.drawPie(size, static_cast<int>(startAng * 16),
static_cast<int>(angle * 16));
startAng = endAng;
if (percent != 0) {
double draw_x = width() / 2 +
cos(PI * (endAng / 180.0 - angle / 360.0)) * this->width() / 4.0;
double draw_y = height() / 2 +
sin(PI * (endAng / 180.0 - angle / 360.0)) * this->width() / 4.0;
painter.drawText(draw_x, draw_y, QString::number(percent * 100) + "%");
}
}
}
On this line:
painter.drawText(this->width()/4,this->height(), QString::number(percent*100)+"%");
You seem to draw the percentage in the same place every time. You do successfully draw the percentage for each section, they're just being drawn in the same place every time. Try changing it to this:
painter.drawText(double(i + 1) * this->width()/4,this->height(), QString::number(percent*100)+"%");
And you'll see what I mean. By multiplying the x value by some changing value, the x position of each drawn text will change, and thus you will be able to see the different percentages being drawn.
If you want it to draw in each quadrant, then your code might look something like this:
# define PI 3.14159265358979323846
...
double draw_x = this->width / 2.0 + cos(PI * (end_angle / 180.0 - angle / 360.0)) * this->width / 4.0;
double draw_y = this->height / 2.0 - sin(PI * (end_angle / 180.0 - angle / 360.0)) * this->width / 4.0;
painter.drawText(draw_x, draw_y, QString::number(percent*100)+"%");
Basically, what's happening in the above code is I'm calculating the x and y coords of the middle of each slice. Then, I'm drawing the percentages in those positions.
i m working on a project that consist on creating an uml tool with qt and for now i have a problem with drawingtext on an arrow so this is my code :
void Arrow::paint(QPainter *painter, const QStyleOptionGraphicsItem * ,QWidget *)
{
if (myStartItem->collidesWithItem(myEndItem))
return;
QPen myPen = pen();
myPen.setColor(myColor);
qreal arrowSize = 20;
painter->setPen(myPen);
painter->setBrush(myColor);
QLineF centerLine(myStartItem->pos(), myEndItem->pos());
QPolygonF endPolygon = myEndItem->polygon();
QPointF p1 = endPolygon.first() + myEndItem->pos();
QPointF p2;
QPointF intersectPoint;
QLineF polyLine;
for (int i = 1; i < endPolygon.count(); ++i)
{
p2 = endPolygon.at(i) + myEndItem->pos();
polyLine = QLineF(p1, p2);
QLineF::IntersectType intersectType =
polyLine.intersect(centerLine, &intersectPoint);
if (intersectType == QLineF::BoundedIntersection)
break;
p1 = p2;
}
setLine(QLineF(intersectPoint, myStartItem->pos()));
double angle = ::acos(line().dx() / line().length());
if (line().dy() >= 0)
angle = (Pi * 2) - angle;
QPointF arrowP1 = line().p1() + QPointF(sin(angle + Pi / 3) * arrowSize,
cos(angle + Pi / 3) * arrowSize);
QPointF arrowP2 = line().p1() + QPointF(sin(angle + Pi - Pi / 3) * arrowSize,
cos(angle + Pi - Pi / 3) * arrowSize);
arrowHead.clear();
arrowHead << line().p1() << arrowP1 << arrowP2;
painter->drawLine(line());
//painter->drawPolygon(arrowHead);
if (isSelected())
{
painter->setPen(QPen(myColor, 1, Qt::DashLine));
QLineF myLine = line();
myLine.translate(0, 4.0);
painter->drawLine(myLine);
myLine.translate(0,-8.0);
painter->drawLine(myLine);
QPoint point = QPoint( 10, 20 );
painter->drawText( point, "You can draw text from a point..." );
}
}
and nothing happens i can draw the arrow but the text does not appear on the arrow what should i do ? please i need some help
IMO you did that wrong.
You should compose your graphics item using QGraphicsPathItem, QGraphicsPolygonItem, QGraphicsRectItem, and QGraphicsSimpleTextItem instead drawing everything yourself.
Just provide some root item responsible for managing children (lines text and polygons). It will be easier to do this properly.
Secondly your paint method is faulty. You should restore initial state of the painter!
And finally I'm pretty sure your problem is caused by incorrect implementation of boundingRect. It is quite common mistake when performing such complex drawing in paint method.
I'm working on some code to place isometric CCTMXTiledMap onto a CCLayerPanZoom control and then convert a touch location into ISO tilemap co-ordinates. This all works perfectly well for me, so long as the scale of the CClayerPanZoom is 1 (i.e. if I don't zoom in or zoom out). I can pan the map around and still calculate the correct iso tile co-oridinates. However, as soon as I zoom the tiled map in or out the iso cordinates returned by my code are completely wrong. Please see below for my code to calculate the iso co-ordinates from the touch location.
-(CGPoint) tilePosFromLocation:(CGPoint)location tileMap:(CCTMXTiledMap*)thisTileMap panZoom:(CCLayerPanZoom*)thisPanZoom
{
float midScale = (thisPanZoom.minScale + thisPanZoom.maxScale) / 2.0;
float newScale = (thisPanZoom.scale <= midScale) ? thisPanZoom.maxScale : thisPanZoom.minScale;
if (thisPanZoom.scale < 1)
{
newScale = newScale + thisPanZoom.scale;
}
else
{
newScale = newScale - thisPanZoom.scale;
}
CGFloat deltaX = (location.x - thisPanZoom.anchorPoint.x * (thisPanZoom.contentSize.width / CC_CONTENT_SCALE_FACTOR()) ) * (newScale);
CGFloat deltaY = (location.y - thisPanZoom.anchorPoint.y * (thisPanZoom.contentSize.height / CC_CONTENT_SCALE_FACTOR()) ) * (newScale);
CGPoint position = ccp((thisPanZoom.position.x - deltaX) , (thisPanZoom.position.y - deltaY) );
float halfMapWidth = thisTileMap.mapSize.width * 0.5f;
float mapHeight = thisTileMap.mapSize.height;
float tileWidth = thisTileMap.tileSize.width / CC_CONTENT_SCALE_FACTOR() * newScale;
float tileHeight = thisTileMap.tileSize.height / CC_CONTENT_SCALE_FACTOR() * newScale;
CGPoint tilePosDiv = CGPointMake(position.x / tileWidth, position.y / tileHeight );
float inverseTileY = tilePosDiv.y - (mapHeight * CC_CONTENT_SCALE_FACTOR()) * newScale; //mapHeight + tilePosDiv.y;
float posX = (int)(tilePosDiv.y - tilePosDiv.x + halfMapWidth);
float posY = (int)(inverseTileY + tilePosDiv.x - halfMapWidth + mapHeight);
// make sure coordinates are within isomap bounds
posX = MAX(0, posX);
posX = MIN(thisTileMap.mapSize.width - 1, posX);
posY = MAX(0, posY);
posY = MIN(thisTileMap.mapSize.height - 1, posY);
return CGPointMake(posX, posY);
}
Can anyone offer any insight into where I'm going wrong with this?
Thanks,
Alan
I am learning Qt from TrollTech's Qt Tutorial these days, and I'am confused about the source code of calculating the position of bullet in this page:
QRect CannonField::shotRect() const
{
const double gravity = 4;
double time = timerCount / 20.0;
double velocity = shootForce;
double radians = shootAngle * 3.14159265 / 180;
double velx = velocity * cos(radians);
double vely = velocity * sin(radians);
double x0 = (barrelRect.right() + 5) * cos(radians);
double y0 = (barrelRect.right() + 5) * sin(radians);
double x = x0 + velx * time;
double y = y0 + vely * time - 0.5 * gravity * time * time;
QRect result(0, 0, 6, 6);
result.moveCenter(QPoint(qRound(x), height() - 1 - qRound(y)));
return result;
}
In the third-last line:
result.moveCenter(QPoint(qRound(x), height() - 1 - qRound(y)));
I think that - 1 is nonsense, isn't it?
You have a widget:
If the height of widget is height, then y == 0 line is on the top of the widget and bottom line has y == height - 1 coordinate. So, if you want to show a point on the bottom line of the widget, you should set it y coordinate to height - 1.
Apparently, they use bottom of the widget as a ground level, so the bullet can be only above or on this level.
I'm using opengl and trying to create a first person camera. All examples use GLUT and I need to get the mouse differential in cocoa. But I'm running into issues which appers to be tied with mouseMoved being called as soon as the mouse is moved (which is to be expected). Is there a way to make this mroe accurate? Or a simialer function like GLUTS glutMouseFunc?
Current attempt:
-(void)mouseMoved:(NSEvent *)event{
static bool wrap = false;
if(!wrap){
NSPoint eventLocation = [event locationInWindow];
float centerX = self.frame.size.width/2 + [self window].frame.origin.x;
float centerY = self.frame.size.height/2 + [self window].frame.origin.y;
CGPoint mousePointCenter = CGPointMake(centerX, centerY);
CGWarpMouseCursorPosition(mousePointCenter);
int dx = eventLocation.x - self.frame.size.width/2 ;
int dy = eventLocation.y - self.frame.size.height/2 ;
const float mousespeed = 0.001;
angles.x += dx * mousespeed;
angles.y += dy * mousespeed;
if(angles.x < -M_PI)
angles.x += M_PI * 2;
else if(angles.x > M_PI)
angles.x -= M_PI * 2;
if(angles.y < -M_PI / 2)
angles.y = -M_PI / 2;
if(angles.y > M_PI / 2)
angles.y = M_PI / 2;
lookat.x = sinf(angles.x) * cosf(angles.y);
lookat.y = sinf(angles.y);
lookat.z = cosf(angles.x) * cosf(angles.y);
CGWarpMouseCursorPosition(mousePointCenter);
[self setNeedsDisplay:YES];
}
else{
wrap = true;
}
}
I'm not sure I followed what your code is supposed to be doing, but repeatedly warping the mouse cursor to a center point is rarely the right thing to do.
First, you can use the deltaX and deltaY values of the NSEvent.
Perhaps you want to do CGAssociateMouseAndMouseCursorPosition(false) to disassociate the mouse from the cursor position. When you do that, the on-screen cursor no longer moves with the mouse. You can hide it or reposition it (yes, you'd warp it once in this case). Also, events no longer have changes in their absolute position. But they do still carry delta movement values which reflect the mouse movements.