PDA

View Full Version : Rotating a QGraphicsLineItem in a QGraphicsScene



lxman
22nd February 2011, 22:14
I have been banging my head against a wall for about a week. I would much appreciate any help. :confused:

What I am trying to accomplish is the following:

I have a QGraphicsScene to which I have added a MyQGraphicsLineItem (a subclass of QGraphicsLineItem) using setLine(). I wish to be able to click on either end of the line and rotate it while the opposite end automatically becomes the anchor point for rotation.

The relevant code:

if(rotating) {
qDebug() << this->pos();
QPointF mouse_offset = p - anchor;
qreal angle = qAtan2(mouse_offset.x(), mouse_offset.y());
qreal new_angle = angle * 180 / M_PI;
QPointF tpoint;
if(mouse_region.rotate_p2) {
new_angle += 180;
tpoint = this->mapToScene(this->line().p1());
} else tpoint = this->mapToScene(this->line().p2());
this->prepareGeometryChange();
this->setTransform(QTransform().translate(tpoint.x(), tpoint.y()).
rotate(-90 - new_angle).
translate(-tpoint.x(), -tpoint.y()));
my_angle = new_angle;
return;
}


This code is contained in the mouseMoveEvent of MyQGraphicsLineItem. p is the current mouse coordinates in scene coordinates. anchor is the scene coordinates of my anchor point (i.e. if I press the mouse while I am over p1, the anchor is set to the scene coordinates of p2).

This code works as expected if my line's p1 is at (0, 0) in scene coordinates. If p1 is not (0, 0) things break.

Any advice would be appreciated.

Thank you in advance.

totem
23rd February 2011, 09:19
This code works as expected if my line's p1 is at (0, 0) in scene coordinates. If p1 is not (0, 0) things break

You mean it does not rotate as expected ? My guess would be : the origin of the rotation is not correctly computed.

You might find easier to use QGraphicsRotation (and maybe QGraphicsScale) to achieve what you want to do. Qt 4.6 needed

stampede
23rd February 2011, 09:51
Funny thing is that to accomplish this you dont need to rotate anything - note that anchor point of rotation A and mouse position during the move event M designates the new direction for your line L. So, you just need to find a point P, that meets the requirements that there exists a real number t in [0,1] : P = A*t +M*(1-t), which implies |P-A| == |L|
So the algorithm would be something like (in the mouse move event):
1. define a line L1 = Line( A, M )
2. resize L1 to have length of your line L
3. move the anchor A by vector described by L1 - here you have the "rotated" second point of your initial line L

-----------------------
alright, maybe my explantion was not clear, so here you have some sample code:

void Line::mousePressEvent( QGraphicsSceneMouseEvent * event ){
const QPointF pos = event->pos();
const qreal l1 = QLineF( pos, this->line().p1() ).length();
const qreal l2 = QLineF( pos, this->line().p2() ).length();
const qreal threshold = 3.5;
if( l1 < l2 and l1 < threshold ){
_dragIndex = 1;
} else if ( l2 < l1 and l2 < threshold ){
_dragIndex = 0;
} else{
_dragIndex = -1;
}
event->setAccepted( _dragIndex != -1 );
}

void Line::mouseMoveEvent( QGraphicsSceneMouseEvent * event ){
if( _dragIndex != -1 ){
const QPointF anchor = _dragIndex == 0 ? this->line().p1() : this->line().p2();
QLineF ma = QLineF(anchor,event->pos());
ma.setLength( line().length() );
const QPointF rotated = anchor + QPointF( ma.dx(), ma.dy() );
this->setLine( _dragIndex == 0 ? QLineF(anchor,rotated) : QLineF(rotated,anchor) );
}
}

void Line::mouseReleaseEvent( QGraphicsSceneMouseEvent * event ){
_dragIndex = -1;
QGraphicsLineItem::mouseReleaseEvent(event);
}

lxman
23rd February 2011, 13:43
Thank you very much for the replies. It would appear that I have some conceptual barriers to overcome. I will experiment with both sets of solutions. I have come up with a "hack" solution which does work for now. Since QGraphicsLineItem prefers to be rotated in reference to its origin (0, 0), the "top left" corner, what I currently do is, if I click on the end of the line and begin to rotate, the program actually flips the line 180 degrees - then rotation works as expected - from either end of the line.

Your solutions, however, do appear more elegant. I will experiment with them.

Thank you

jeevan_ravula
9th March 2014, 20:04
@stampede : I was going through your algorithm on QGraphicsLineRotation and I really need it badly :-). the solution looks brilliant. However I have few doubts in wrt your solution. How did you compute the value of threshold to 3.5? whats is the logic behind the value?

My next question is, in the event handler mousePressEvent, wouldn't the length of line l1 be 0, since the event pos and this->line().p1() are the same point. Because the user presses on the line point P1 to initiate the line roatation and it is the same as event point.

stampede
9th March 2014, 20:46
How did you compute the value of threshold to 3.5? whats is the logic behind the value?
No logic at all :) Feel free to experiment which value gives you the best "use experience" :) I think I just set it to 3.5 pixels and it felt ok, so I left it like this.

My next question is, in the event handler mousePressEvent, wouldn't the length of line l1 be 0, since the event pos and this->line().p1() are the same point. Because the user presses on the line point P1 to initiate the line roatation and it is the same as event point.
"pos" could be somewhere around P1 - maybe exactly equal to P1 - but whats more important, it is in the radius defined by 'threshold'. In the "mousePressEvent" I just wanted to choose the point closest to clicked pos, but not farther away than "threshold" pixels.

jeevan_ravula
10th March 2014, 19:49
@stampede - thank you very much for your response. I really appreciate your help. I'm new to graphics programming and hence I have some more doubts.

I am aware that QGraphicsItem have their own local corordinates system and QGraphicsScene has it's own coordinate system. Incase of rotation algorithm above, I believe all the points are in QGraphicsLineItem coordinate system. should'nt we finally map the rotated point coordinates to scene coordinates, since we finally display the line on the scene? for example using mapToScene() on rotated point or calling scenePos() on rotated point?

For a Line GraphicsItem where will its origin (0,0) start? what is the origin point for a line graphics item?


Could you also please help me with an algorithm to resize the line by dragging any of its end point. the user should be able to resize the line by clicking and dragging on any of its end points. But the resizing should not happen beyond the scene boundaries.

stampede
10th March 2014, 20:54
Yes, in above code, everything happens in local coordinates. But this is also true when item is painted:

void QGraphicsItem::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0)

This function, which is usually called by QGraphicsView, paints the contents of an item in local coordinates.
So you don't need to do any transformations.


Could you also please help me with an algorithm to resize the line by dragging any of its end point.
Sure, have you tried anything yet ?

jeevan_ravula
11th March 2014, 20:34
@stampede - I haven't implemented resizing the line item yet. I'll work on it today and see how it goes.

Btw I implemented the rotation algorithm first hand today and it works brilliant!!!. Now I want to handle the corner cases too, for instance if the line item is dropped very close to the bounding rect of the graphicssceen. Now if we intend to rotate the line item, how should it behave?


Should the line stop rotating if it touches the screen boundary? or should the rotating anchor shift such that the rotation is possible? I guess the second thought is too complicated to implement.

I have a vague idea how to proceed in this regard. my understanding is that, in the QGrpahicsLineItem::mouseMoveEvent() after we calculated the rotated point,
we need to check if the rotated point lies with in the boundary rect of the graphics screen(convert rotated to screen coordinates by using mapToScreen() ), if yes then proceed to call setLine() on the line item other wise do not proceed to set the line.


void Line::mouseMoveEvent( QGraphicsSceneMouseEvent * event ){
if( _dragIndex != -1 ){
const QPointF anchor = _dragIndex == 0 ? this->line().p1() : this->line().p2();
QLineF ma = QLineF(anchor,event->pos());
ma.setLength( line().length() );
const QPointF rotated = anchor + QPointF( ma.dx(), ma.dy() );
//Check if the QPointF rotated falls with the bounding rect of the screen, if yes proceed else do not setLine with new coordinates.
this->setLine( _dragIndex == 0 ? QLineF(anchor,rotated) : QLineF(rotated,anchor) );
}
}

Am I thinking on correct lines? I'm not sure if there is a way to check if the QPointF rotated falls with in the bound rect of screen.

jeevan_ravula
12th March 2014, 15:58
@stampede - I just added the below code in mouseMoveEvent() just after calculating the rotated point. My scene is 640x480. I guess its working, although I believe its not a good way to do it. I am able to stop the rotation beyond the scene boundaries.

QPointF sceen_point = this->mapToScene(rotated);
if(sceen_point.x() > 640 || sceen_point.x() < 0 || sceen_point.y() > 480 || sceen_point.y() < 0 )
{
return;
}
this->setLine( _dragIndex == 0 ? QLineF(anchor,rotated) : QLineF(rotated,anchor) );

stampede
12th March 2014, 23:48
Great, but instead of hardcoded values you can use QGraphicsItem::scene (http://qt-project.org/doc/qt-4.8/qgraphicsitem.html#scene) method to access scene, and query it's actual size.

jeevan_ravula
13th March 2014, 19:43
@stampede - I tried the below code to implement the resizing of line item and it actually took care of line rotation too. I tried to find the possible loop holes during the testing but I am able to rotate the line using both end points and also resize it. I commented the actual line rotation algo and used only the below code to test resize and rotation. I believe both functionalities are working fine unless i failed to test any scenario.

CustomGraphicsLineItem::mouseMoveEvent(QGraphicsSc eneMouseEvent *event)
{

if((event->scenePos().x() > this->scene()->width()) || (event->scenePos().y() > this->scene()->height()) ||
(event->scenePos().x() < 0) || (event->scenePos().y() < 0) )
{
return;
}

const QPointF anchor = dragIndex == 0 ? this->line().p1() : this->line().p2();
this->setLine(dragIndex == 0 ? QLineF(anchor, event->pos()) : QLineF(event->pos(),anchor));

}

The other interesting thing which I observed during the testing of line rotation, I tweaked the original rotation algo provided by you such that for both the anchor points, I called this->setLine(anchor, rotated). I did not use this->setLine(rotated, anchor). While the rotation was successful when P1 was the anchor and P2 received event. The rotation fails miserably if P2 is made anchor, the line basically never gets anchored on P2 and the line keeps shifting if tried rotating.

Question: what is the significance of interchaging QlineF points based on who is the anchor? Why isn't line getting anchored on P2 if we do not draw line with interchaged anchor & rotated points.

Sorry for my lengthy post and constant nagging :-)

stampede
13th March 2014, 22:44
what is the significance of interchaging QlineF points based on who is the anchor? Why isn't line getting anchored on P2 if we do not draw line with interchaged anchor & rotated points.
The "anchor" of rotation is simply one of the original points, it is always equal to P1 or P2. It is the one that "stands still" during the rotation, if user clicked on first point, then we should "fix" the second point in place and let him move only P1, and vice versa.
Maybe in the code it looks a bit like swapping these points, but in fact it is fairly straightforward if you rewrite the algorithm:


const bool moveSecond = (_dragIndex == 0);
QPointF anchor;
if (moveSecond)
anchor = line().p1();
else
anchor = line().p2();
// ...
// ...original rotation code
// ...
if (moveSecond)
this->setP2(rotated);
else
this->setP1(rotated);

If user moves around one point, you have to save the other one. In fact there is no "interchanging", in both cases only one of the points is changed.

jeevan_ravula
14th March 2014, 21:13
@stampede - Thanks a lot.I got little confused but I understand it well now. I kindly request you to guide me in my assignment which I am currently doing. I just need your thoughts if i am proceeding in the correct direction. I need guidance wrt to display of Arrow items( single/bi-directional arrows on the scene).

The summary of my assignment is that - We display a snapshot of a surveillance camera on a graphics scene. A QGraphicsLineItem is dropped onto the scene, the line item can be rotated and resized within the scene boundaries. The motivation behind dropping the line item is that, the line item acts as a division between two areas. We are basically divinding certain area under surveillance into two parts.
Once the line item is dropped onto the scene, a Arrow item has to appear perpendicular to the dropped line item indicating the direction of restriction of people movement. The Arrow direction indicates the direction of movement under surveillance. initially arrow item appears in one direction, we can click on the arrow item to change the arrow direction to other way round. Basically I should be able to display the arrow item on either side of the line item. I should also be able to display bi-directional arrows.

Inorder to display arrow item, I am referring to the code in Qt projects example called diagram scene
http://qt-project.org/doc/qt-4.8/graphicsview-diagramscene.html.
The arrow item is subclass of graphicslineitem itself. I intend to use the code from paint() method to display the arrow and use QLineF's normalVector() method to display a line perpendicular to the dropped line item on scene. The normal vector line will have arrow as its head. I will then move the normal vector line by certain distance since normal vector shares the same starting point with the actual lie item.

My concern is how to display the arrow item on either side of line item? Can i use QLineF method transform() to display arrow on either side of line item?
also how do i proceed in case of displaying bi-directional arrows? Please let me know if my approach is fine with arrow item display.

stampede
15th March 2014, 00:20
Please let me know if my approach is fine with arrow item display.
If it works and you are happy with the performance, then I guess it is fine :)
I don't really know what advice I could give you, maybe take a piece of paper and draw / calculate everything by hand (thats what I usually do). If it works there, there is high chance that it will work on the computer screen as well :)

jeevan_ravula
17th March 2014, 20:00
@stampede - Following my discussion with you abt the implementation of Arrow item, I proceeded to implement it. I am able to display the arrow item perpendicular to the dropped QGraphicsLineItem on the scene. The Arrow item used itself is a custom Arrow which is derived from QGraphicsLineItem. The issue I am facing now is that, the Arrow item should not only be perpendicular to the dropped line item but should also intersect the line item at some point. this gives the impression of direction flow from one side to other. Please find the attached images, which shows the way I intend to display the arrows.1013410135

since I used normal vector as reference to display the starting point of arrow item (point P1), I am unable to position it in such a way that it intersects the line item. I tried doing it using some random transformation after first moving the Point P1 to middle of parent line item. But the whole thing blows away once I start rotating the line item. The intent is to see that Arrow item remains in the same position and at same angle(90 degrees) when the parent line item starts rotating. Currently the Arrow item remains at 90 degrees but the position gets displaced as we rotate the line item. how do we fix this issue?

Please find my current code below:

QPainterPath CustomGraphicsArrow::shape() const
{

QPainterPath path = QGraphicsLineItem::shape();
path.addPolygon(arrowHead);
return path;
}

void CustomGraphicsArrow::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{

QPen myPen = pen();
myPen.setColor(myColor);
qreal arrowSize = 10;
painter->setPen(myPen);
painter->setBrush(myColor);

QGraphicsLineItem *parent_line_item = dynamic_cast<QGraphicsLineItem *>(parent_graphics_item);
QLineF normal_vector_line;

QLineF parent_line;
if(parent_line_item)
{

parent_line = parent_line_item->line();
normal_vector_line = parent_line.normalVector();
}

QPointF parent_mid_point = parent_line.pointAt(0.5);
QPointF arrow_line_start_point = this->mapFromParent(parent_mid_point);
normal_vector_line.setLength(50.0);

setLine(normal_vector_line);
QLineF arrow_line = line();

QPointF translate_point = QPointF(-5.0, arrow_line_start_point.y());

arrow_line.translate(translate_point);

double angle = ::acos(line().dx() / line().length());
if (line().dy() >= 0)
{
angle = (Pi * 2) - angle;
}

QPointF arrowP1 = arrow_line.p2() - QPointF(sin(angle + Pi / 3) * arrowSize,
cos(angle + Pi / 3) * arrowSize);
QPointF arrowP2 = arrow_line.p2() - QPointF(sin(angle + Pi - Pi / 3) * arrowSize,
cos(angle + Pi - Pi / 3) * arrowSize);

arrowHead.clear();
arrowHead << arrowP1 << arrowP2 << arrow_line.p2();
painter->drawLine(arrow_line);
painter->drawPolygon(arrowHead);

}


Basically, how do i position an arrow item such that it intersects the dropped line item and when the line item is rotated, the Arrow item maintains its position and angle too?

jeevan_ravula
18th March 2014, 07:13
@Stampede - May be I wasn't very clear in my above mail. What I require is that I should initially be able to set the position of Arrow item such that it intersects the parent line item at 90 degrees. Now when I rotate the parent item, the child Arrow item should maintain its relative position wrt to the parent item. How do I achieve that?

stampede
18th March 2014, 08:01
Now when I rotate the parent item, the child Arrow item should maintain its relative position wrt to the parent item. How do I achieve that?
Just use normal vector of the "parent" line to calculate "arrow head" points. I can see some trigonometric functions in your code, again I think they are not needed here. You have used normal vector to calculate child line coords, now do the same for the "arrow head" - take normal vector N of the "child" line, scale it, and translate a point near the one end of the line two times.
Suppose N is the normal vector of the child item, s - scale factor, depends on the arrow size, B - one of the child line end points, the one with the arrow head, and X - a point near B (something like line.pointAt(0.9), again depends on the arrow size). Now you can use points [B, X+s*N, X-s*N] to describe the arrow head polygon. No explicit rotation needed.

jeevan_ravula
18th March 2014, 08:48
@stampede - I believe you haven't got the problem I am actually facing. Sorry, i couldn't explain my problem clearly. The parent item i am talking about is the QGraphicsLineItem which I dropped on the QGraphicsScene, the parent line item is able to rotate and resize on the graphics scene. This is the one i created last week and we did discuss rotation/resize on this line.

Now the Arrow item I am referring to is actually the combination of the line item + the arrow head( i used trignometry to create arrow head). I used parent line's normal vector to create a arrow line perpendicular to the parent line. Initially the arrow line's P1 has the same origin(0,0) as the parent item. But then I shifted the arrow line to some random point which is QPointF(-5.0, arrow_line_start_point.y()). The arrow_line_start_point is nothing but parent_line.pointAt(0.5). My requirement is that the arrow item(combination of arrow line + arrow head ) should actually be positioned in such a way that, it should intercept the parent line item. The arrow item once positioned at some point, should maintain its relative position when I rotate the parent graphics line item on scene.
When ever I rotate the parent line item the arrow item should also rotate along with the parent line item and also should remain perpendicular to the parent item. I have attached the drawing of relationship b/w the parent line and arrow item.

Your were offering a possible exaplantion on creating arrow head to a arrow line which is a normal vector of parent line. My focus is currently more on transformations, when I am rotating the parent item the child arrow item is unable to maintain its relative position. How should i solve this problem?

jeevan_ravula
18th March 2014, 12:04
@stampede - As we keep rotating the parent item, the coordinate system keeps changing. Initially if we fix postion of the child items at one particular position wrt parent item, how do we ensure that the position remains same, during the rotation of parent item?


In the below code, I am initially fixing the position of child item wrt parent item. Now as we rotate the parent item, since the coordinate system is changing the child item is not always in the same postion that it was initially wrt parent item. How do I fix this issue?



Aren't the following two lines of code sufficient to realize our goal:
QPointF translate_point = this->mapToParent(QPointF(arrow_line_start_point.x(), arrow_line_start_point.y() + 20));
arrow_line.translate(translate_point);



void CustomGraphicsArrow::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
QPen myPen = pen();
myPen.setColor(myColor);
qreal arrowSize = 10;
painter->setPen(myPen);
painter->setBrush(myColor);

QGraphicsLineItem *parent_line_item = dynamic_cast<QGraphicsLineItem *>(parent_graphics_item);
QLineF normal_vector_line;
QLineF parent_line;
if(parent_line_item)
{
parent_line = parent_line_item->line();
normal_vector_line = parent_line.normalVector();
}

QPointF parent_mid_point = parent_line.pointAt(0.5);

QPointF arrow_line_start_point = this->mapToParent(parent_mid_point);


normal_vector_line.setLength(40.0);
setLine(normal_vector_line);

QLineF arrow_line = normal_vector_line;

QPointF translate_point = this->mapToParent(QPointF(arrow_line_start_point.x(), arrow_line_start_point.y() + 20));

arrow_line.translate(translate_point);

double angle = ::acos(line().dx() / line().length());
if (line().dy() >= 0)
{
angle = (Pi * 2) - angle;
}

QPointF arrowP1 = arrow_line.p2() - QPointF(sin(angle + Pi / 3) * arrowSize,
cos(angle + Pi / 3) * arrowSize);

QPointF arrowP2 = arrow_line.p2() - QPointF(sin(angle + Pi - Pi / 3) * arrowSize,
cos(angle + Pi - Pi / 3) * arrowSize);

arrowHead.clear();
arrowHead << arrowP1 << arrowP2 << arrow_line.p2();

painter->drawLine(arrow_line);
painter->drawPolygon(arrowHead);

}

jeevan_ravula
19th March 2014, 16:04
@stampede - How can we translate a parent line's point P1 which currently is mouse move event's pos() back to its local coordinate system?
In the below code, if anchor is P2, then P1 takes mouse event's pos() as its new coordinates. Originally P1 has (0,0) as its coordinates, since we are in item coordinate system. Once the resize or rotate is complete, the P1 coordinates are set to event->pos() coordinates.
Once the rotation/resize of a line is finished, how do I position the item in local coord system again?

I tried using this->setTransformOriginPoint(event->pos()) but it does not change the item P1 coordinates to origin(0,0).

This has caused issue while I place the Arrow item on the parent's line mid point and start resizing/rotating the parent using P2 as an anchor.

void CustomGraphicsLineItem::mouseMoveEvent(QGraphicsSc eneMouseEvent *event)
{
qDebug()<<"Line mouse move event";

if( dragIndex != -1 )
{
const QPointF anchor = dragIndex == 0 ? this->line().p1() : this->line().p2();
this->setLine(dragIndex == 0 ? QLineF(anchor, event->pos()) : QLineF(event->pos(),anchor));
}
if(dragIndex == 1)
{
this->setTransformOriginPoint(event->pos());
}
this->update();
}

stampede
19th March 2014, 18:44
This has caused issue while I place the Arrow item on the parent's line mid poin
So maybe it would be easier just to draw the child line in reimplemented parent's paint(...) method, instead of trying to manage parent-child relation. The arrow is only for display anyway, right ?

jeevan_ravula
19th March 2014, 19:31
the Arrow line item needs to handle mouse press events too. If we click on the existing arrow item then it should toggle and display in opposite direction. I am planning to have two independent arrow line item classes each responsible for displaying arrow line in upward and downward direction respectively. Rendering both these arrow items together will display bi-directional scenario too.
I guess its because of the above reason i cannot draw in the parent's paint() method(or is it still possible?). so is there a way to translate the newly rotated/resized parent line point P1 with event->pos() coordinates to origin(0,0)? how can the translation be achieved?

I am really very stressed out dealing with all this scenarios and trying to figure out how it can be achieved especially when my graphics understanding is
bare minimum :-(

stampede
20th March 2014, 09:05
I guess its because of the above reason i cannot draw in the parent's paint() method(or is it still possible?).
of course it is
it can be done with a simple subclass of QGraphicsItem, using basic math on normal vectors to draw both lines and arrows, type of arrow can be simply an enum
I created an example of such item for you, just to prove it is possible without any explicit "higher math" :) it looks ugly, but it works :)
sorry the code is without any comments, but I'm already late :/
have fun

jeevan_ravula
20th March 2014, 20:29
@stampede - Thank you very much for your initiative!!! It meant a lot to me. However, in the course of our conversations, I realized that there is quite a bit of lost in translation. I got the impression that the terminology I am using to ask questions is different from what your are understanding. Let me explain clearly what I meant by parent item and child item in my case.

Parent Item - Its a custom line item class called CustomGraphicsLineItem, derived from QGraphicsLineItem. this line item class is dropped onto the scene from another scene(this scene just holds certain shapes). This line item once dropped onto the scene can be rotated and resized. It is this line item, that acts as a parent to a Arrow item class.

Child Item - Its again a custom line item class called CustomArrowItem. This class renders the complete arrow, like the sample class which u sent me. The complete arrow here is the line plus triangle shape. The Arrow line item can be rendered in upward or downward direction.

In the code that i am currently working, the Arrow line item acts a child of the parent line item which is already dropped on the scene. The Arrow line item rests on the parent line item at 90 degrees at any point we intend to position.
Please check the attached screen shots inorder to get more clear picture.1015310154

jeevan_ravula
21st March 2014, 14:23
In your discussion with me you were considering parent-child relationship b/w arrow head(triangle) and the straight line on which arrow head is positioned.
While I kept referring to parent as a graphics line on which the arrow line item, which is child is positioned.

When an arrow line item is positioned on the parent line, the axis of rotation of both parent and child is the same, hence when we rotate the line item the Arrow line rotates along with parent line.
In my case arrow line item does not position its starting point the parent line, it cuts/intersects the parent line at 90 degree. In my scenario, it gives the impression of direction flow from one side to other.
In case such as this, rotating parent item does not rotate the child( arrow line item) along with it perfectly. With each rotation, the arrow line item gets displaced from its initial position. Hope you got the picture?

stampede
21st March 2014, 16:30
Hope you got the picture?
I think so :)
Ok, so maybe you could give the parent line a pointer to the arrow and let it control the arrow position manually at each rotation, something like:


// assuming _arrow is a pointer to Arrow class in Line class
void Line::attach(Arrow * arrow){
if (!_arrow && arrow){
_arrow = arrow;
_arrow->setParentItem(this);
updateArrowItem();
}
}

void Line::mouseMoveEvent ( QGraphicsSceneMouseEvent * event ){
if (_dragIndex != -1){
prepareGeometryChange();
if (_dragIndex == 0)
_p1 = event->pos();
else
_p2 = event->pos();
updateArrowItem();
update();
}
}

void Line::updateArrowItem(){
if (_arrow) {
// this will attach arrow on the center of line item, but it is easy to change that with different 'center' point
const QPointF center = (_p1+_p2)/2;
QLineF normal = QLineF(_p1,_p2).normalVector();
normal.setLength(20); // or whatever else arrow length you need
const QPointF head = center + (normal.p2()-normal.p1()); // change '+' to '-' in order to point the arrow in other direction
_arrow->_p1 = this->mapToItem(_arrow, center); // assume _p1 = arrow base, _p2 = arrow head
_arrow->_p2 = this->mapToItem(_arrow, head);
}
}


Now you have two separate classes and you can literally attach arrow to the line item :) I think you can figure out the details but I'll be happy to help if not.

jeevan_ravula
23rd March 2014, 16:23
@Stampede - thanks for the code. But my requirement is slightly different from the code above.
In the above code, the arrow line item is positioned on the parent line item at its center. The arrow line P1 is positioned at the center of the parent line. In this case the Arrow line rotates along with the parent line, when the parent line rotates since the Arrow line's P1 is on the axis of rotation of the Parent line.

In my requirement, the Arrow line item's base P1 does not actually lie on the Parent line. The Arrow line's P1 and P2 points lie on either side of the parent line.
In such a scenario, when rotating the parent line, the Arrow item gets displaced from its initial position each time we rotate the parent line.
I do not know how to solve this problem. Please find the attched screen shot showing how the Arrow line is positioned with respect to the parent line.

10162

Each time I roate the parent line and move it to new coordinate system, the Arrow line is getting displaced. My requirement is that Arrow line should remain in the same position wrt to parent line item no matter in which ever direction the parent line is rotated.

stampede
23rd March 2014, 17:05
You can simply change the "center" point in the following code to whatever you need as a "base" point of attachement

// this will attach arrow on the center of line item, but it is easy to change that with different 'center' point
const QPointF center = (_p1+_p2)/2;

jeevan_ravula
24th March 2014, 20:14
@stampede - I tried very hard, I wish it were that simple. I spent considerably amount of time on this, and it doesnt really work.
The Arrow line item rotates along with the parent "only if its base point lies any where on the parent line". The Arrow line starts getting displaced if it's "base" point is not exactly located on the parent line but is located just "below or above" the parent line . For example, if the base point is calculated as below, arrow item gets displaced from the parents axis as the parent is rotated.

_p2 = _p2 + QPointF(0,20); // the calculation is just random, to place arrow base at above or below parent line.
const QPointF center = (_p1+_p2)/2;

Now if the arrow "base" point is located on the above center point, As the parent line is rotated eventually arrow line gets displaced from its initial position.
If you test the whole code together, I am sure you will face this issue. Could you please combine the parent line code and arrow item code and check this behavior? My guess is that it is working if the parent line coordinate system is positive one but once iparent line starts rotating and switches to new coordinate system the center point calculations goes for a toss and doesn't hold good for the arrow position.

Btw did you see the attached images in my previous post?

stampede
24th March 2014, 20:49
_p2 = _p2 + QPointF(0,20); // the calculation is just random, to place arrow base at above or below parent line.
const QPointF center = (_p1+_p2)/2;
You have changed _p2 of "this" object in a place where you shouldn't, this method should only place the child line in correct position. In this method just use the existing _p1 and _p2 of the parent item to calculate new _p1 and _p2 of child, nothing more - look at the method name, "updateArrowItem", and not "updateMyPositionWithRandomData()".
If you keep changing _p2 like that in this method I doubt you can predict the final result.
Don't do "random" calculations, according to your specs it shouldn't be random. If you want to translate the base point for attachement, do that correctly using parent line normal vector, for example:


void Line::updateArrowItem(){
if (_arrow) {
QLineF normal = QLineF(_p1,_p2).normalVector();
// this will attach arrow sligthly below parent line, near the second point
normal.setLength(10);
const QPointF p = QLineF(_p1,_p2).pointAt(0.8);
const QPointF center = p - (normal.p2()-normal.p1()); // move the base point below the parent line
normal.setLength(30); // or whatever else arrow length you need
const QPointF head = center + (normal.p2()-normal.p1()); // change '+' to '-' in order to point the arrow in other direction
_arrow->_p1 = this->mapToItem(_arrow, center); // assume _p1 = arrow base, _p2 = arrow head
_arrow->_p2 = this->mapToItem(_arrow, head);
}
}

I have tried it and it works ok, you are very close to the solution.

jeevan_ravula
27th March 2014, 06:03
@Stampede - Thanks a lot for all the help. I really learnt a lot interacting with u and its been pleasure talkin to u :-)

jeevan_ravula
1st April 2014, 20:47
@Stampede : hii, offlate I have been working on resizing of a qgraphicspixmapitem which holds a png image in the shape of a rectangle. I am trying to resize the graphicspixmap item located on the scene. I have implemented the mouseMoveEvent() of my custom pix map item. I am only partly successful. I am able to resize the pixmap only when I resize from the right and bottom side of the rectangle. I am unable to resize when I place my mouse on the left and top side of the rectangle.
Could you please look at my code and let me know what more needs to be done inorder to resize the rect from the top and left side of it?

void PersonSizeGraphicsItem::mouseMoveEvent(QGraphicsSc eneMouseEvent *event)
{
const QPointF event_pos = event->pos();
const QPointF event_scene_pos = event->scenePos();

QPixmap current_pixmap = this->pixmap();
QImage current_image = current_pixmap.toImage();
QRect current_image_rect = current_image.rect();

QPoint current_top_left = current_image_rect.topLeft();
QPoint current_bottom_right = current_image_rect.bottomRight();

if((event->scenePos().x() > this->scene()->width()) || (event->scenePos().y() > this->scene()->height())
|| (event->scenePos().x() < 0) || (event->scenePos().y() < 0) )
{
return;
}

if( this->cursor().shape() == Qt::SizeHorCursor )
{
QRect new_rect( current_top_left, QPoint( event->pos().x(), current_bottom_right.y()) );
setPixmap(QPixmap::fromImage(current_image.scaled( QSize(new_rect.width(),new_rect.height()),Qt::Igno reAspectRatio,Qt::FastTransformation)));
if(rect_left_condition)
{
QRect rect = this->pixmap().rect();
rect.moveTo(event_scene_pos.x(), event_scene_pos.y));
}

}

if( this->cursor().shape() == Qt::SizeVerCursor )
{
QRect new_rect( current_top_left, QPoint(current_bottom_right.x(), event->pos().y()) );
setPixmap(QPixmap::fromImage(current_image.scaled( QSize(new_rect.width(),new_rect.height()),Qt::Igno reAspectRatio,Qt::FastTransformation)));
if(rect_top_condition)
{
QRect rect = this->pixmap().rect();
rect.moveTo(event_scene_pos.x(), event_scene_pos.y));
}
}

this->update();

}


btw how do we attach files here? I couldn't figure out so I am posting only the implementation of mouseMoveEvent().

stampede
2nd April 2014, 08:36
I guess you should start new thread, as this is not related to rotating a graphics line item.
Another thing, don't address me directly in your questions, there are lots of people much more smarter than me, they could help you as well.

btw how do we attach files here? I couldn't figure out so I am posting only the implementation of mouseMoveEvent().
It is better to post code using CODE tags, much more convenient than downloading an attachement just to have a look at your source code.

jeevan_ravula
2nd April 2014, 20:30
Thanks for your reply.I have taken note of your suggestions. I have figured out the problem related to resizing the rectangular pixmap item and fixed it.

However I do have another issue related to qgraphicslineitem. When a parent qgraphicslineitem (containing arrow line graphics item as its child) rotates on either of its end points, how do we ensure that the child item does not go out of bounds on the scene, once the rotation of parent item is stopped?

Currently I am checking the boundary condition for the parent item only by using the mouse move event's scene position. Since my arrow line item is positioned on the parent graphics line item, how can we handle boundary conditions for child item too. Is it possible to create a single bounding rect which covers both parent and children and then check the entire bounding rect position on the scene during mouse move?

The code below shows the current implementation for checking the boundary condition for parent item on a scene.

CustomGraphicsLineItem::mouseMoveEvent(QGraphicsSc eneMouseEvent *event)
{

if((event->scenePos().x() > this->scene()->width()) || (event->scenePos().y() > this->scene()->height()) ||
(event->scenePos().x() < 0) || (event->scenePos().y() < 0) )
{
return;
}

const QPointF anchor = dragIndex == 0 ? this->line().p1() : this->line().p2();
this->setLine(dragIndex == 0 ? QLineF(anchor, event->pos()) : QLineF(event->pos(),anchor));

}