How to scale down QGraphicsItem in Qt? - c++

I have a QGraphicsItem made up of QGraphicsPathItem (polyline) and QGraphicsEllipseItem (an Arc )
Through co-ordinates of Line and an Arc I have drawn QGraphicsItem ( XNOR gate ) But the problem is, it is quite bigger.
I want that it should get fit in a rectangle. But I do not want to resize it manually.
struct mySymbols
{
vector<LINES> lines;
vector<ARCS> arcs;
};
struct LINES
{
tuple<int,int,int,int> lines_
};
struct ARCS
{
tuple<int,int,int,int,int,int> arc_;
};
std::map<std::string, mySymbols> allSymbols ;
The above figure is showing the current scenario. I want to scale down XNOR gate such that it's input line should match to the a0 and a1 rectangle and it's output should get match to the o rectangle. Currently I have hidden whole rectnagle and showing only input , output (small) rectangle.
I am reading co-ordinates of XNOR gate and storing them in a object. And when I am drawing those on a scene I want to resize them.
How to do that ? Can anyone help me ?

Related

Identify outer border of bordering polygons C++

I'm looking for advice on an algorithm I've been struggling with. I have something represented in code (shown later) that looks like the following diagram (initial state on left, desired output on right):
I have this represented in C++ as the following:
#include <vector>
using namespace std;
class Shape {
public:
Shape(vector<vector<float> > c) {
coordinates = c;
}
vector<vector<float> > coordinates; // in format {{x1,y1},{x2,y2}}
};
class Shape_Group {
public:
Shape_Group(vector<Shape> s) {
shapes = s;
}
vector<Shape> shapes;
Shape generate_border();
};
Shape Shape_Group::generate_border() {
/*
define algorithm here
*/
}
int main() {
// define rectangle shapes
Shape s1({{0,0}, {0,1}, {1,1}, {1,0}});
Shape s2({{0,0}, {0,1}, {1,-1}, {-1,0}});
Shape_Group group({s1, s2});
Shape border = group.generate_border(); // should return a shape with the exterior border
// border should be filed with {{1,0}, {1,1}, {1,-1}, {-1,0}};
}
My initial idea for generating this exterior shape was to do something like the following:
loop through each line segment for each shape in the Shape_Group object
loop through every other segment in the Shape_Group object
if both line segments are colinear and overlap, remove them
whatever's left over should be the exterior border
However, with large inputs this is a very slow algorithm. What else can I do?
Geometry O(n^2) approach:
make list of all edges
remove all interrior edges
use hit test to detect interior/exterior edges.
reconnect all remaining edges into new polygon
simply connect edges sharing the same end point.
If you want something simpler then O(n) graphics approach:
render your shape
fill background with specific color
remove all edges not neighboring background color
this is somewhat related:
Render filled complex polygons with large number of vertices with OpenGL
it also combines vector graphics and pixel access ...
There are also different approaches to this problem for example like this one:
Finding holes in 2d point sets?

Qt drawing a ring / circle with a hole

I need to draw a circle with QPainter. When I used drawEllipse function like :
void UserClass::Draw(QPainter &painter) {
painter.save();
painter.setBrush( GetColor() );
QPoint centerPosition = GetCenterPosition();
painter.drawEllipse( centerPosition, m_CircleOuterRadius, m_CircleOuterRadius);
painter.setBrush(QColor(0, 0, 0, 0));
painter.drawEllipse( centerPosition, m_CircleInnerRadius, m_CircleInnerRadius);
painter.restore();
}
Unfortunately result is not what I desired. I want to have inner circle not be filled. That is why I put alpha value as zero but ofcourse it didn't work. How can I have a circle which is not until a certain radius with qt ?
You should create a QPainterPath then add the two circles to it via addEllipse(), the outer first, then the inner. This will effectively give you a shape that is the outer circle with the inner circle punched as a hole.
Then you fill the painter path with a green brush, which will result in a hollow ring. Afterwards, if you want the white outlines, you can stroke the path with a white pen as well.
Also note that the painter path can be created only once and stored for reuse instead of creating it anew every time you redraw.

How to appropriately get position of QGraphicsRectItem after drag-release?

I wanted to have an online monitoring system that could tell where the shape is currently, but am getting very weird coordinates of the item, also the dimensions of it get higher by 1 each time I create new one and drag it.
Initial position (map size is 751 by 751, checked by outputting to qDebug(), scene bound to yellow space) :
Dragging it to the left top corner.
As you can see in the beginning it was on (200;200), but after dragging it is on (-201;-196). After deleting it and creating new shape on the same position with the same properties, new shape can't be seen because it is outside of the map, which suggests that edits don't show correct data.
Here is the code of updating the edits:
void CallableGraphicsRectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
QGraphicsRectItem::mouseReleaseEvent(event);
ptr->updateEdits(this);
}
Here is what I managed to cut down into updateEdits():
void MainWindow::updateEdits(QAbstractGraphicsShapeItem* item)
{
//stuff not related to scene
auto posReal = item->scenePos();
auto pos = posReal.toPoint();
//create QString from coordinates
QString coordinate;
coordinate.setNum(pos.x());
ui->leftXEdit->setText(coordinate);
coordinate.setNum(pos.y());
ui->upperYEdit->setText(coordinate);
//get width and height for rect, radius for circle
auto boundingRectReal = item->sceneBoundingRect();
auto boundingRect = boundingRectReal.toRect();
ui->widthEdit->setText(QString::number(boundingRect.width()));
//disables height edit for circles, not really relevant
if (!items[currentShapeIndex].isRect)
{
ui->heightEdit->setDisabled(true);
}
else
{
ui->heightEdit->setDisabled(false);
ui->heightEdit->setText(QString::number(boundingRect.height()));
}
}
Here is how I anchor the QGraphicsScene to the left top corner of the yellow area:
scene->setSceneRect(0, 0, mapSize.width() - 20, mapSize.height() - 20);
ui->graphicsView->setScene(scene);
How can I report the right data to the edits?
You're better off overriding the itemChange method and using the ItemPositionHasChanged notification. You have to set the ItemSendsGeometryChanges flag on the item so that it receives these notifications.
I'm not sure that your item's final position has been set when you're still in the mouseReleaseEvent method. Tracking it in itemChange will ensure that the data is valid, and this kind of thing is what it's for.
Also, note that "pos" is in the item's parent coordinates, and "boundingRect" is in the item's coordinate space. You should use "scenePos" and "sceneBoundingRect" if you want to be sure you're using scene coordinates. If the item doesn't have a parent, then "pos" and "scenePos" will return the same values, but "boundingRect" and "sceneBoundingRect" will generally differ.

How to map item coordinates?

I know that every item has own coordinates relative to scene. I am adding an ellipse in the scene. Each of them returns the following from boundingRect(): QRect(0, 0, 50, 50). I don't know how to map coordinates to another QGraphicsItem which is a line. The line supposed to connect this two ellipses. I have correct coordinates of ellipses and I am passing them to a custom QGraphicsLineItem constructor. However the line is in a wrong place. How should I use mapFromItem() or other method to get the result?
I get each ellipse's coordinates as follows:
selfCoords = ellipse->mapFromScene(QPointF(0.0,0.0));
You should map the coordinates from each ellipse to some common coordinate system that you can then map to the line's parent. The common coordinate system can be the scene's coordinate system.
For example, to connect the centers of the ellipses:
QGraphicsScene scene;
QGraphicsEllipseItem e1, e2;
scene.addItem(&e1);
scene.addItem(&e2);
... // set the ellipse rects/sizes
auto start = e1.mapToScene(e1.boundingRect().center());
auto end = e2.mapToScene(e2.boundingRect().center());
QGraphicsLineItem l(QLineF(start, end));
scene.addItem(&l);
You can do this because the line's parent is the scene. Now assume that we had some other parent for the line - you'd need to map the coordinates to that parent instead.
...
QGraphicsItem p;
p(20, 20);
scene.addItem(&p);
auto start = e1.mapToItem(&p, e1.boundingRect().center());
auto end = e2.mapToItem(&p, e2.boundingRect().center());
QGraphicsLineItem l(QLineF(start, end), &p);
If I want to add new ellipse on mouse position, how to map coords to ellipse item to get right position on scene ? For example from contextMenuEvent I get
QPointF coords = event->scenePos(); and there I want to create ellipse. I have custom QGraphicsScene MyScene where I have pointer to QGraphicsView* view.
I use event form void MyScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
QPointF coords = event->scenePos();
QPointF ellpiseCoords = view->mapToScene(coords .x(), coords .y())
I always get wrong transform.

How to set the coordinate system?

I have a class MaskGraphicsWidget.cpp inheriting from QGraphicsView which contains several functions, the constructor is:
MaskGraphicsWidget::MaskGraphicsWidget(QTreeWidget * tree, QWidget* parent) : QGraphicsView(parent), m_tree_widget(tree){
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_mask_scene = new QGraphicsScene;
m_mask_scene->setSceneRect(0, 0, 250, 250);
setScene(m_mask_scene);
primitive = 0;
minX = 0;
minY = 0;
}
where I initialize my scene m_mask_scene and I set the coordinate system.
I also have a function MousePressEvent :
http://pastebin.com/wjwDTQzw
And my MaskGraphicsWidget.h is like that :
QGraphicsScene* m_mask_scene;
QList<QPointF> m_polygon_points;
QList<QGraphicsLineItem*> m_polygon_lines;
int minX;
int minY;
My problem is that, I want to set the Pos of the QGraphicsPolygonItem poly but if I set it to the value min and max (that I calculate somewhere else), the item moves from the current Pos to the Pos of min and max. Basically it sets the Pos of the item in his own coordinate system. How can I write that I want to set the Pos in the coordinate system of the m_mask_scene ?
Sorry for my English, if you did not understand, feel free to add a comment !
Edit 1 :
m_polygon_points and m_polygon_lines are filled somewhere else and it's working. (see pastebin)
Edit 2 : Added the plan, see below for better (I hope) understanding !
Explanation of the plan : the black polygon is what I got (I do not set any Pos), but if I do set the Pos at min and max, I get the red polygon. I want to set the Pos at min and max AND still have the black polygon ! Sorry for my poor paint skills
Edit 3 : Of course, if I print the Pos of the QGraphicsPolygonItem poly, it shows (0,0).
Edit 4 : Added a pastebin to not overload the post, I've put everything in it, please ignore the PrimitiveItem things, and the case 0 and 1 which are not for the QGraphicsPolygonItem.
Edit 5 : To clarify, I catch the event on MousePressEvent:
if it's a left click, I use it to create a new point (creating new lines for my polygon)
if it's a right click, I don't use the event but instead I just close my polygon, delete all the lines added to the scene and adding the polygon in the scene.
QPointF point(event->x(), event->y());
With event being a QMouseEvent, a call to x() and y() returns coordinates relative to the widget that receives the event. In this case, MaskGraphicsWidget, which is a QGraphicsView.
If you add an item to a scene and want to set its position, you're setting its position relative to its parent. In the case of an item with no parent, the position is relative to the scene (in scene coordinates).
So your current code is trying to set view coordinates, not scene coordinates. You need to convert the position of the mouse coordinates to the scene: -
QPointF scenePoint = mapToScene(event->pos());
Then use this to set the position of the newly created item.
Note that a QGraphicsView is like a window looking into a world (the QGraphicsScene), so it may not match the scene coordinates.