Qt: Scale polygons but not children - c++

I am new to Qt. I am representing events from my application as polygons in my scene, using a custom class that inherits QGraphicsPolygomItem. The polygon dimensions are (event duration, fixed height), using 1s : 1px. Event duration can be as low as 1E-6, so I simply scale my view so that the smaller polygon is scaled up to MIN_POLY_WIDTH (10px):
view->scale(qreal(MIN_POLY_WIDTH/min_event_duration), qreal(1.0));
So far so good. However, I have a QGraphicsTextItem child for each polygon, which get stretched by the scale operation to a point they get way outside the polygon boundaries:
The text item is created as follows:
void EventPolygon::setId(QString id) {
if (!this->id) {
this->id = new QGraphicsTextItem(id, this);
} else {
this->id->setPlainText(id);
}
this->id->setPos(0, this->polygon().boundingRect().height() / 2 - this->id->boundingRect().height() / 2);
}
That function is usually called by EventPolygon constructor. I though that was the issue, since scale is done after all items are added to the scene, so it would affect the text items. So I tried calling setId after the scale operation, by iterating over all items in the scene. That way I though only the polygons would be stretched. That was not the case, and the text remained stretched.
I also tried using the following instead of scale:
QTransform t = QTransform();
t.scale(qreal(MIN_POLY_WIDTH/min_event_duration), qreal(1.0));
view->setTransform(t, false);
I thought "false" would avoid the transformation being applied to the polygons children, however it seems that is not the case. Is there any way I can scale the polygons to 10px width min and have a readable text inside them?

The QGraphicsScene forms a scenegraph hierarchy based upon the parent-child relationships, transformations are inherited down this hierarchy - there is no way round this (see the Transformations section here).
So to fix your issue, you will need to make the child QGraphicsTextItem invert the scaling transformation of the parent multiplied by the scaling of your view.
In fact I really recommend that you never set view transformations that are not for simulating a camera operation (pan, zoom, etc.) for this reason. I would simply allow for the seconds per pixel ratio to vary and allow the child items to be able to query this from the view - in other words have the progress items take care of their own size on screen.
That was not the case, and the text remained stretched.
Changing the transformation stack will cause a redraw, that's why it doesn't matter when you set the scale.
I thought "false" would avoid the transformation being applied to the
polygons children
No, the combine argument when false just overrides the existing transformation matrix with the one you are providing.

Related

How to get bounding rectangle of an ArcGIS TextSymbol?

I'm using the Qt C++ ArcGIS Runtime SDK v100.9 and I have various shapes and labels being drawn on a map.
I want to be able to find out the area (bounding rectangle) of Graphic (which is a text label (TextSymbol) at a given point (SpatialReference::wgs84) on the map) so I can determine if the width of the label is more or less than another Graphic (lets say it has a Polygon for its Geometry which is being used to draw a circle) in order to decide if the label should be set to visible or not.
Within a class derived from Esri::ArcGISRuntime::MapGraphicsView the circle and the text label are created along the lines of:
Point centerWgs84(0.0, 0.0, SpatialReference::wgs84());
Graphic* circleGraphic_p = new Graphic(GeometryEngine::bufferGeodetic(centerWgs84, 1000.0, LinearUnit::meters(), 0.5, GeodeticCurveType::Geodesic));
this->graphicsOverlays()->at(0)->graphics()->append(circleGraphic_p);
circleGraphic_p->setSymbol(new SimpleLineSymbol(SimpleLineSymbolStyle::Solid, QColor(Qt::blue), 1.0));
circleGraphic_p->setVisible(true);
TextSymbol* textMarker_p = new TextSymbol("Some Label", Qt::black, 12.0, HorizontalAlignment::Center, VerticalAlignment::Bottom);
Graphic* labelGraphic_p = new Graphic(centerWgs84, textMarker_p);
this->graphicsOverlays()->at(0)->graphics()->append(labelGraphic_p);
labelGraphic_p->setVisible(true);
Rather than always setting the label visibility to true, I thought I would be able to take the Geometry of each Graphic and use it to construct an Envelope which would allow me to then get the width of each envelope that could then be compared:
Envelope circleEnvelope(circleGraphic_p->geometry());
Envelope labelEnvelope(labelGraphic_p->geometry());
labelGraphic_p->setVisible(circleEnvelope.width() >= labelEnvelope.width());
but when I try and do this, the width of each envelope is always a very small negative value (such as -2.25017... e-98)
Any suggestions on what I am doing wrong or if there is a better way to get the size on the map (or in device independent units) of the text label and a Graphic described by the Geometry of a Polyline or Polygon?
EDIT: I've discovered that the Geometry object has an extent() method from which I can get the width of the circle but the Geometry of the Graphic being used for the text label results in a width of zero from its extent() method. I expect this is because the Geometry is just a Point which has no width or height. So the question still stands of how to get the bounding rectangle of a TextSymbol?
You are correct, the extent is being returned of the underlying geometry, not the TextSymbol. I don't think you will be able to achieve what you are wanting in the way you are going about it, as there isn't a way to get the bounding box or screen coordinates of the symbol itself. Instead, have you considered using LabelDefinitions and setting the various deconfliction options? This sample shows labels on layers, but can be applied to graphics as well. There are many labeling options you can apply, and this would allow the internal labeling engine to deconflict for you.

High-DPI scaling of QQuickItem-derived class

I use QtQuickControls 2 together with QQuickItem-derived class in my app. After I set AA_EnableHighDpiScaling attribute and all QQuickControls 2 components look correctly on my smartphone but object of my custom class is scaled incorrectly. Here is the app without HighDpi scaling with minimum zoom(the way it is meant to work):
And here is the one with scaling with minimum zoom:
It seems that on the second screen the object is scaled too much and I can see square pixels of all textures that I draw with QPixmap or QImage. However, the images that I load from external memory and nodes like QSGGeometryNode look correct. Can I switch off scaling for just one particular QQuickItem? If no, what should I set to render it correctly?
Also, when I try to set opacity on QQuickItem with a lot of QSGOpacityNodes in scene graph node tree I get segmentation fault. What can cause this?
So I solved this problem by dividing the size of QSGTexture by QQuickWindow::effectiveDevicePixelRatio() and also multiplying the size of the image from which texture is created by this ratio.
If you are drawing the text using on QImage you should also multiply your font's size by this ratio. The same thing should be done with geometrical shapes and QPixmap::scaled().

How to rotate an image around its centre in QT QWidgets C++?

I am trying to rotate am image around its origin(center) in QT using QWidgts in C++. I experimented a lot of things here, but no matter what I do, the image keeps rotating around some arbitrary position I have no clue of. Kindly, help me out here. I am new to QT.
void gaugeWithRedZoneImage::rotate()
{
QPixmap pixmap(*gaugeMainScreen->pixmap());
QMatrix rm;
rm.translate(0, 0);
rm.rotate(-360);
pixmap = pixmap.transformed(rm);
gaugeMainScreen->setPixmap(pixmap);
/*QTransform rotate_disc;
rotate_disc.translate(pixmap.width()/2.0 , pixmap.height()/2.0);
rotate_disc.rotate(-60);
rotate_disc.translate(-(pixmap.width()/2.0) , -(pixmap.height()/2.0));
pixmap = pixmap.transformed(rotate_disc);
gaugeMainScreen->setPixmap(pixmap);*/
}
Form the documentation of QPixmap::transformed():
The transformation transform is internally adjusted to compensate for unwanted translation; i.e. the pixmap produced is the smallest pixmap that contains all the transformed points of the original pixmap.
This means that the method ensures no clipping takes place by appending the canvas. No matter what your rotation center was, the automatic extension of canvas will almost always result in a perceived shift.
Image examples might help to further diagnose the problem.
As ypnos said, your problem isn't the rotation center. When you rotate your image, its width and height will most likely change and no longer fit your container (gaugeMainScreen) dimensions.
You have some possibilities to overcome this problem. One of them is to set your container to scale its contents (you can use the method setScaledContents()). In this case, you have to keep the original image around and use it whenever you apply a rotation, otherwise your image will appear increasingly smaller.

Scaled and transform Qgraphicspixmapitem

I have a problem while scaling and transforming QGraphicsPixmapItem. I have created graphics editor in which I am using QGraphicsItem of initial size 100,100 and after that I am resizing and rotating it by setTransform() method.
After that I am storing this transform and using these for my another application called player in which I am using those transform and showing images and videos on it, I am using QgraphicsPixmapItem to show images now if I set scaled and setTransform methods to image its clarity gets spoiled.
Is it possible to keep clarity as it is even if I scaled and transform it.The image I am using is of 2560x1440 pixels.
Any help will be appreciated.
The code is :
QGraphicsPixmapItem *pixitem = new QGraphicsPixmapItem;
pixitem->setFlags(QGraphicsItem::ItemIsMovable);
pixitem->setPos(50,50);
QTransform trans;
trans.setMatrix(4.20399,2.9286,0,-3.86601,0.761397,0,33.1015,-134.5,1);
pixitem->setPixmap(QPixmap("abc.jpg").scaled(100,100));
pixitem->setTransform(trans);
pixitem->setShapeMode(QGraphicsPixmapItem::MaskShape);
scene->addItem(pixitem);
You can use setTransformationMode function to set the pixmap item's transformation mode. The default value is Qt::FastTransformation. You should set the transform mode to Qt::SmoothTransformation to enable bilinear filtering which results in a better quality when scaled :
pixitem->setTransformationMode(Qt::SmoothTransformation);

Sets in raphaeljs not real groups? Transform order

I'm having an issue with sets and how transforms are applied. I'm coming from a graphics background, so I'm familiar with scene graphs as well as the normal SVG group syntax, but Raphael is confusing me. Say I have a circle and a set, on which I want to apply a transform.
circle = paper.circle(0,0.5)
set = paper.set()
If I add the circle first, and then transform, it works.
set.push circle
set.transform("s100,100")
To make a 50 radius circle. If I reverse the order, however,
set.transform("s100,100")
set.push circle
The transform is not applied.
This seems as though it will break many, many rendering and animation type algorithms, where your groups/transforms hold your articulation state, and you add or remove objects to them instead of recreating the entire transform every time. Is there an option somewhere in the documentation that I am not seeing that addresses this, or was this functionality discarded in favor of simplicity? It seems very odd to be missing, given that it is supported directly and easily in the group hierarchy of SVG itself... do I need to manually apply the transform from the set to any children added after the set is transformed?
Sets in Raphael are just simple Arrays.
When you perform some actions on set, Raphael goes through all members via for(...){} loop.
Raphael doesn't support SVG groups <g></g>
UPDATE Raphael's code:
// Set
var Set = function (items) {
this.items = [];
this.length = 0;
this.type = "set";
if (items) {
for (var i = 0, ii = items.length; i < ii; i++) {
if (items[i] && (items[i].constructor == elproto.constructor || items[i].constructor == Set)) {
this[this.items.length] = this.items[this.items.length] = items[i];
this.length++;
}
}
}
},
As you can see, all items are stored in this.items which is array.
Raphaël's sets are merely intended to provide a convenient way of managing groups of shapes as unified sets of objects, by aggregating element related actions and delegating them (by proxying the corresponding methods in the set level) to each shape sequentially.
It seems very odd to be missing, given that it is supported directly
and easily in the group hierarchy of SVG itself...
Well, Raphaël is not an attempt to elevate the SVG specs to a JavaScript based API, but rather to offer an abstraction for vector graphics manipulation regardless of the underlying implementation (be it SVG in modern browsers, or VML in IE<9). Thus, sets are by no means representations of SVG groups.
do I need to manually apply the transform from the set to any
children added after the set is transformed?
Absolutely not, you only need to make sure to add any shapes to the set before applying transformations.