I'm trying to understand how do I use a QSGSimpleTextureNode but Qt documentation is very vague. I want to render text on the scene graph, so basically what I want is to draw a texture with all the glyphs and then set that texture on a QSGSimpleTextureNode. My idea was to create the texture using standard OpenGL code and set the texture data to the data I have just created. I can't find an example to show me how to achieve this.
I would use QSGGeometryNode instead of QSGSimpleTextureNode. If I am not wrong it is not possible to set the texture coordinates in a QSGSimpleTextureNode. You could write a custom QQuickItem for the SpriteText and override the updatePaintNode:
QSGNode* SpriteText::updatePaintNode(QSGNode *old, UpdatePaintNodeData *data)
{
QSGGeometryNode* node = static_cast<QSGGeometryNode*>(old);
if (!node){
node = new QSGGeometryNode();
}
QSGGeometry *geometry = NULL;
if (!old){
geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D()
,vertexCount);
node->setFlag(QSGNode::OwnsGeometry);
node->setMaterial(material); // <-- Texture with your glyphs
node->setFlag(QSGNode::OwnsMaterial);
geometry->setDrawingMode(GL_TRIANGLES);
node->setGeometry(geometry);
} else {
geometry = node->geometry();
geometry->allocate(vertexCount);
}
if (textChanged){
//For every Glyph in Text:
//Calc x + y position for glyph in texture (between 0-1)
//Create vertexes with calculated texture coordinates and calculated x coordinate
geometry->vertexDataAsTexturedPoint2D()[index].set(...);
...
node->markDirty(QSGNode::DirtyGeometry);
}
//you could start timer here which call update() methode
return node;
}
Related
in Qt creator drawPoint() method does not put point if negative valued parameters are passed
following is code for Bresenham's algorithm.but, it is not working in qt creator.it just plots circle in one quadrant.
Bresenham::Bresenham(QWidget*parent):QWidget(parent)
{}
void Bresenham::paintEvent(QPaintEvent *e)
{
Q_UNUSED(e);
QPainter qp(this);
drawPixel(&qp);
}
void Bresenham::drawPixel(QPainter *qp)
{
QPen pen(Qt::red,2,Qt::SolidLine);
qp->setPen(pen);
int x=0,y,d,r=100;
y=r;
d=3-2*r;
do
{
qp->drawPoint(x,y);
qp->drawPoint(y,x);
qp->drawPoint(y,-x);
qp->drawPoint(x,-y);
qp->drawPoint(-x,-y);
qp->drawPoint(-y,-x);
qp->drawPoint(-x,y);
qp->drawPoint(-y,x);
if(d<0)
{
d=d+4*x+6;
}
else
{
d=d+(4*x-4*y)+10;
y=y-1;
}
x=x+1;
}while(x<y);
}
You need to translate the Qt coordinate system to the classic cartesian one. Choose a new center QPoint orig and replace all
qp->drawPoint(x,y);
with
qp->drawPoint(orig + QPoint(x,y));
The Qt coordinates system origin is at (0,0) and the y-axis is inverted. For instance, a segment from A(2,7) to B(6,1) look like this:
Notice how there is only the positive-x, positive-y quadrant. For simplicity assume that no negative coordinates exist.
Note:
For performance reasons it is better to compute all the points first and then draw them all using
QPainter::drawPoints ( const QPoint * points, int pointCount );
I have a 2D BYTE (unsigned char) array. buf[50][100] which is having some data. I need to draw this buffer to an image in Qt using QGraphicsView. The byte in (x,y) represents the (x,y)th pixel of the array. How to pass this array to the QGraphicsView to draw very fast? Or is there any other method (without using QGraphicsView) to draw the image in 2D array Please help.
You can create a QImage object from a pre-existing memory area and then you can use a drawImage call to draw it on a normal QPainter.
Being your image 8 bit per pixel you will need to also set up a palette for the image.
The palette is simply a mapping from a byte index to a QRgb color value. You can set it up like so:
static void setGrayColorMap(QImage * img)
{
img->setColorCount(256);
for (int i = 0; i < 256; ++i) {
img->setColor(i, qRgb(i,i,i));
}
}
We know that for drawing on an image in qt, qpainter is used. Recently, I used drawLine() function to draw whatever an user is scribbling. This was done by passing the lastPoint and currentPoint from the mouseMoveEvent to a custom function which implements drawLine(). I have passed the arguments for that custom function as given below:
void myPaint::mouseMoveEvent(QMouseEvent *event) {
qDebug() << event->pos();
if ((event->buttons() & Qt::LeftButton) && scribbling) {
pixelList.append(event->pos());
drawLineTo(event->pos());
lastPoint = event->pos();
}
}
Now with the help of qDebug() I noticed that some pixels are missed while drawing but the drawing is precise. I looked up the source of qt-painting where I saw that drawLine() was calling drawLines() which was making use of qpainterPath to have a shape drawn on the image.
My question is that, is there anyway to track these "missed" pixels or any approach to find all the pixels which have been drawn?
Thanks!
void myPaint::drawLineTo(const QPoint &endPoint) {
QPainter painter(image); //image is initialized in the constructor of myPaint
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(QPen(Qt::blue, myPenWidth, Qt::SolidLine, Qt::RoundCap,Qt::RoundJoin));
painter.drawLine(lastPoint, endPoint);
modified = true;
lastPoint = endPoint; //at the mousePressEvent, the event->pos() will be stored as
// lastPoint
update();
}
For a start, don't draw in a mouseEvent(). Actually handling a mouseevent should be done as quick as possible. Also, it is not a good idea to look at the Qt source, it can be confusing. Rather assume that what Qt gives you work, and first try to answer "What I am doing wrong?". As I said drawing in a mouse event is definitely wrong.
Your description is really subjective, maybe an image of your output is better. Are you trying to emulate a pen (like in windows paint)? In this case do the mouse button has to be down ? is that the purpose of your variable scribbling?
There is more. following the documentation, QMouseEvent::buttons() always return a combination of all buttons for mouse move event. Which make sense : the mouse movements are independent of the buttons. It means
if ((event->buttons() & Qt::LeftButton)
will always be true.
Let's assume you want to draw the path of your mouse when the left button is pressed. Then you use something like :
void myPaint::mousePressEvent(QMouseEvent *event){
scribbling = true;
pixelList.clear();
}
void myPaint::mouseReleaseEvent(QMouseEvent *event){
scribbling = false;
}
void myPaint::mouseMoveEvent(QMouseEvent *event) {
if ( scribbling) {
pixelList.append(event->pos());
}
}
void myPaint::paintEvent(){
QPainter painter(this)
//some painting here
if ( scribbling) {
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(QPen(Qt::blue, myPenWidth, Qt::SolidLine, Qt::RoundCap,Qt::RoundJoin));
// here draw your path
// for example if your path can be made of lines,
// or you just put the points if they are close from each other
}
//other painting here
}
If after all of this you don't have a good rendering, try using float precision (slower), ie QMouseEvent::posF() instead of QMouseEvent::pos().
EDIT :
"I want to know whether there is any way to calculate all the sub-pixels between any two pixels that we send as arguments to drawLine"
Yes there is. I don't know why you need to do such thing but is really simple. A line can be characterized with the equation
y = ax + b
Both of the endpoints of the line p0 = (x0, y0) and p1 = (x1, y1) satisfy this equation so you can easily find a and b. Now all you need to do is increment from x0 to x1 by the amount of
pixels you want (say 1), and to compute the corresponding y value, each time saving point(x,y).
So will go over all of the points saved in pixelList and repeat this process for any two consecutive points.
I created this function to move a unit along way points that are saved in list_. Every Unit has its own list_. move() is initially called with the speed (distance/step) every step. Then depending on the distance to the next way point three possible actions are taken.
Can you suggest any improvements?
void Unit::move(qreal maxDistance)
{
// Construct a line that goes from current position to next waypoint
QLineF line = QLineF(pos(), list_.firstElement().toPointF());
// Calculate the part of this line that can be "walked" during this step.
qreal part = maxDistance / line.length();
// This step's distance is exactly the distance to next waypoint.
if (part == 1) {
moveBy(line.dx(), line.dy());
path_.removeFirst();
}
// This step's distance is bigger than the distance to the next waypoint.
// So we can continue from next waypoint in this step.
else if (part > 1)
{
moveBy(line.dx() , line.dy());
path_.removeFirst();
if (!path_.isEmpty())
{
move(maxDistance - line.length());
}
}
// This step's distance is not enough to reach next waypoint.
// Walk the appropriate part of the length.
else /* part < 1 */
{
moveBy(line.dx() * part, line.dy() * part);
}
}
I'll hate myself for suggesting a deprecated way of doing things, but there's no reference to the replacing method :(
QGraphicsItemAnimation
It has addStep and linear interpolation stuff as a convenience.
It seems Qt devs would like you to use QTimeLine itself as a replacement.
I'd use Qt Animation Framework, more precisely QPropertyAnimation:
// I use QPainterPath to calculate the % of whole route at each waypoint.
QVector<qreal> lengths;
QPainterPath path;
path.moveTo(list_.first());
lengths.append(0);
foreach (const QPointF &waypoint, list_.mid(1)) {
path.lineTo(waypoint);
lengths.append(path.length());
}
// KeyValues is typedef for QVector< QPair<qreal, QVariant> >
KeyValues animationKeyValues;
for (int i(0); i != lenghts.count(); ++i) {
animationKeyValues.append(qMakePair(path.percentAtLength(lenghts.at(i)), list_.at(i)));
}
// I assume unit is a pointer to a QObject deriving Unit instance and that
// Unit has QPointF "position" property
QPropertyAnimation unitAnimation(unit, "position");
unitAnimation.setKeyValues(animationKeyValues);
unitAnimation.setDuration(/* enter desired number here */);
unitAnimation.start();
I haven't tested this solution, but you should get the general idea.
Currently, the PolarChart joins all the coordinates with lines creating a polygon. I just want it to plot each point with a dot and NOT join them together. Is this possible?
I have tried using translateValueThetaRadiusToJava2D() and Graphics2D to draw circles but it's very clunky and contrived.
Any suggestions welcome!
So the DefaultPolarItemRenderer takes in all the polar points, converts the polar points to regular Java2D coordinates, makes a Polygon with those points and then draws it. Here's how I got it to draw dots instead of a polygon:
public class MyDefaultPolarItemRenderer extends DefaultPolarItemRenderer {
#Override
public void drawSeries(java.awt.Graphics2D g2, java.awt.geom.Rectangle2D dataArea, PlotRenderingInfo info, PolarPlot plot, XYDataset dataset, int seriesIndex) {
int numPoints = dataset.getItemCount(seriesIndex);
for (int i = 0; i < numPoints; i++) {
double theta = dataset.getXValue(seriesIndex, i);
double radius = dataset.getYValue(seriesIndex, i);
Point p = plot.translateValueThetaRadiusToJava2D(theta, radius,
dataArea);
Ellipse2D el = new Ellipse2D.Double(p.x, p.y, 5, 5);
g2.fill(el);
g2.draw(el);
}
}
}
and then instantiated this class elsewhere:
MyDefaultPolarItemRenderer dpir = new MyDefaultPolarItemRenderer();
dpir.setPlot(plot);
plot.setRenderer(dpir);
This one's a little harder. Given a PolarPlot, you can obtain its AbstractRenderer and set the shape. For example,
PolarPlot plot = (PolarPlot) chart.getPlot();
AbstractRenderer ar = (AbstractRenderer) plot.getRenderer();
ar.setSeriesShape(0, ShapeUtilities.createDiamond(5), true);
The diamond will appear in the legend, but the DefaultPolarItemRenderer neither renders shapes, nor provides line control. You'd have to extend the default renderer and override drawSeries(). XYLineAndShapeRenderer is good example for study; you can see how it's used in TimeSeriesChartDemo1.
If this is terra incognita to you, I'd recommend The JFreeChart Developer Guide†.
†Disclaimer: Not affiliated with Object Refinery Limited; I'm a satisfied customer and very minor contributor.
This is an excellent discussion, in case you want the function to pick up the color assigned by user to the series
add ...
Color c =(Color)this.lookupSeriesPaint(seriesIndex);
g2.setColor(c);
before ...
g.draw(e1);
there are other functions... use code completion to see what else functions are available against series rendereing with name starting from lookupSeries........(int seriesindex)
I found a rather strange way to get the points without any lines connecting them.
I set the Stroke of the renderer to be a thin line, with a dash phase of 0, and length of 1e10:
Stroke dashedStroke = new BasicStroke(
0.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
0.0f, new float[] {0.0f, 1e10f}, 1.0f );
renderer.setSeriesStroke(0, dashedStroke);