I'm drawing different shapes with drawPolygon function. The following code snippet draws a hexagon as the picture shows (yes, it's partially transparent):
std::vector<Vec2> polyCoords = {
Vec2(75, 7),
Vec2(25, 7),
Vec2(0, 50),
Vec2(25, 93),
Vec2(75, 93),
Vec2(100, 50)
};
DrawNode* poly = DrawNode::create();
poly->setPosition(Vec2(500, 500));
poly->drawPolygon(polyCoords.data(), polyCoords.size(), Color4F(1,0,0,0.8), 10, Color4F(0,1,0,0.4));
As you can see the border is centered. I'd like to set the border to appear only outside the polygon area. It wouldn't be a problem if I were using solid colors, but in my case it's noticeable. Is there a way to remove the inner part of the border?
Related
I am learning C++ and I thought I'd make the original asteroids game with a fresh coat of paint using the SFML graphics library. However, for my player sprite, while the origin is at the top left corner of the screen, to the right of it is the negative x axis and downwards is negative y axis (opposite of what it's supposed to be in both cases). Also, no matter what object or rotation, invoking the setRotation function always rotates any object about the top left corner of the screen even if, for that object, I have set the origin to the object's center.
#include<SFML\Graphics.hpp>
using namespace sf;
const int W{ 1200 }, H{ 800 };
const float degToRad = 0.017453f;
int main() {
float x{ -600 }, y{ -400 };
float dx{}, dy{}, angle{};
bool thrust;
RenderWindow app(VideoMode(W, H), "Asteroids!");
app.setFramerateLimit(60);
Texture t1, t2;
t1.loadFromFile("images/spaceship.png");
t2.loadFromFile("images/background.jpg");
Sprite sPlayer(t1), sBackground(t2);
sPlayer.setTextureRect(IntRect(40, 0, 40, 40));
sPlayer.setOrigin(-600, -400);
while (app.isOpen())
{
app.clear();
app.draw(sPlayer);
app.display();
}
return 0;
}
The above code draws the player (spaceship.png) to the center of my rendered window (app) but notice how I have had to put in negative coordinates. Also, if I further put in the code for taking keyboard inputs and call the setRotation function, instead of rotating my sPlayer sprite about its center (i.e. (-600,-400)), it rotates the sprite about the top left corner of the screen which is (0,0). I can't find any explanation for this in the SFML online documentation. What should I do?
As I mentioned I have tried reading the documentation. I've watched online tutorials but to no avail.
Origin is the point on sprite where you "hold" it.
Position is the point on screen where you put Origin of the sprite.
In short, you take your sprite by Origin and put it so Origin is on Position.
By default, both Origin and Position are (0, 0), so top left of your sprite is put at top left of the screen. What you did was to say "take this point on sprite, which is way to the upper-left that actual visible part of the sprite is and put it to the top left of the screen". This had an effect of moving your sprite to the bottom right.
You probably want something like:
// This is will make sure that Origin, i.e. point which defines rotation and other transformations center is at center of the ship
sPlayer.setOrigin(sprite_width / 2, sprite_height / 2);
// This will put Origin (center of the ship) at center of the screen
sPlayer.setPosition(screen_width / 2, screen_height / 2);
I have just started using OpenGl in visual studio.
I used the drawsquare() and drawRoundRect () to draw multiple shapes in seperate void functions which I called seperately in an another function named Display (void return type) which I then called in int main using glutDisplayFunc(Display) to display.
In the Display function I used glutPushmatrix() and glutPopMatrix () keeping the function of 1 shape in between like this
glutPushmatrix();
glutTranslatef(x,y,0); // z = 0 because i am making 2D shape
DrawSquare(50, 50, 50, colors[BALCK]);
glutPopMatrix ();
glutPushmatrix();
glutTranslatef(x,y,0); // z = 0 because i am making 2D shape
DrawSquare(300, 300, 50, colors[BALCK]);
glutPopMatrix ();
The issue is that the when I click the mouse anywhere on the screen(even not on the shapes) both of these shapes start moving together.
Q1) How to move the shapes one by one only when the mouse is clicked and dragged on the specific shape ?
Q2) What should I do so that the shape only moves when it is clicked and dragged and nothing happens when I click anywhere else on the screen.
I used glutPushMatrix() , glutPopMatrix() and glTranslatef () functions which worked fine for moving one shape but I don't know how I can move multiple shapes only when they are clicked and dragged.
glutPushmatrix();
glutTranslatef(x,y,0); // z = 0 because i am making 2D shape
DrawSquare(50, 50, 50, colors[BALCK]);
glutPopMatrix ();
glutPushmatrix();
glutTranslatef(x,y,0); // z = 0 because i am making 2D shape
DrawSquare(300, 300, 50, colors[BALCK]);
glutPopMatrix ();
Using this code both of my figures move together not seperately.
Also even if the mouse is not clicked on any of the shapes they both start to move.
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.
I am making a small game in C++11 with Qt. However, I am having some issues with scaling.
The background of my map is an image. Each pixel of that image represents a tile, on which a protagonist can walk and enemies/healthpacks can be.
To set the size of a tile, I calculat the maximum amount like so (where imageRows & imageCols is amount of pixels on x- and y-axis of the background image):
QRect rec = QApplication::desktop()->screenGeometry();
int maxRows = rec.height() / imageRows;
int maxCols = rec.width() / imageCols;
if(maxRows < maxCols){
pixSize = maxRows;
} else{
pixSize = maxCols;
}
Now that I have the size of a tile, I add the background-image to the scene (in GameScene ctor, extends from QGraphicsScene):
auto background = new QGraphicsPixmapItem();
background->setPixmap(QPixmap(":/images/map.png").scaledToWidth(imageCols * pixSize));
this->addItem(background);
Then for adding enemies (they extend from a QGraphicsPixMapItem):
Enemy *enemy = new Enemy();
enemy->setPixmap(QPixmap(":/images/enemy.png").scaledToWidth(pixSize));
scene->addItem(enemy);
This all works fine, except that on large maps images get scaled once (to a height of lets say 2 pixels), and when zooming in on that item it does not get more clear, but stays a big pixel. Here is an example: the left one is on a small map where pixSize is pretty big, the second one has a pixSize of pretty small.
So how should I solve this? In general having a pixSize based on the screen resolution is not really useful, since the QGrapicsScene is resized to fit the QGraphicsView it is in, so in the end the view still determines how big the pixels show on the screen.
MyGraphicsView w;
w.setScene(gameScene);
w.fitInView(gameScene->sceneRect(), Qt::KeepAspectRatio);
I think you might want to look at the chip example from Qt (link to Qt5 but also works for Qt4).
The thing that might help you is in the chip.cpp file:
in the paint method:
const qreal lod = option->levelOfDetailFromTransform(painter->worldTransform());
where painter is simply a QPainter and option is of type QStyleOptionGraphicsItem. This quantity gives you back a measure of the current zoom level of your QGraphicsView and thus as in the example you can adjust what is being drawn at which level, e.g.
if (lod < 0.2) {
if (lod < 0.125) {
painter->fillRect(QRectF(0, 0, 110, 70), fillColor);
return;
}
QBrush b = painter->brush();
painter->setBrush(fillColor);
painter->drawRect(13, 13, 97, 57);
painter->setBrush(b);
return;
}
[...]
if (lod >= 2) {
QFont font("Times", 10);
font.setStyleStrategy(QFont::ForceOutline);
painter->setFont(font);
painter->save();
painter->scale(0.1, 0.1);
painter->drawText(170, 180, QString("Model: VSC-2000 (Very Small Chip) at %1x%2").arg(x).arg(y));
painter->drawText(170, 200, QString("Serial number: DLWR-WEER-123L-ZZ33-SDSJ"));
painter->drawText(170, 220, QString("Manufacturer: Chip Manufacturer"));
painter->restore();
}
Does this help?
I'm working on an application that draws handwritten strokes. Strokes are internally stored as vectors of points and they can be transformed into std::vector<Gdiplus::Point>. Points are so close to each other, that simple drawing of each point should result into an image of continual stroke.
I'm using Graphics.DrawEllipse (GDI+) method to draw these points. Here's the code:
// prepare bitmap:
Bitmap *bitmap = new Gdiplus::Bitmap(w, h, PixelFormat32bppRGB);
Graphics graphics(bitmap);
// draw the white background:
SolidBrush myBrush(Color::White);
graphics.FillRectangle(&myBrush, 0, 0, w, h);
Pen blackPen(Color::Black);
blackPen.SetWidth(1.4f);
// draw stroke:
std::vector<Gdiplus::Point> stroke = getStroke();
for (UINT i = 0; i < stroke.size(); ++i)
{
// draw point:
graphics.DrawEllipse(&blackPen, stroke[i].X, stroke[i].Y, 2, 2);
}
At the end I just save this bitmap as a PNG image and sometimes the following problem occurs:
When I saw this "hole" in my stroke, I decided to draw my points again, but this time, by using ellipse with width and height set to 1 by using redPen with width set to 0.1f. So right after the code above I added the following code:
Pen redPen(Color::Red);
redPen.SetWidth(0.1f);
for (UINT i = 0; i < stroke.size(); ++i)
{
// draw point:
graphics.DrawEllipse(&redPen, stroke[i].X, stroke[i].Y, 1, 1);
}
And the new stoke I've got looked like this:
When I use Graphics.DrawRectangle instead of DrawEllipse while drawing this new red stroke, it never happens that this stroke (drawn by drawing rectangles) would have different width or holes in it:
I can't think of any possible reason, why drawing circles would result into this weird behaviour. How come that stroke is always continual and never deformed in any way when I use Graphics.DrawRectangle?
Could anyone explain, what's going on here? Am I missing something?
By the way I'm using Windows XP (e.g. in case it's a known bug). Any help will be appreciated.
I've made the wrong assumption that if I use Graphics.DrawEllipse to draw a circle with radius equal to 2px with pen of width about 2px, it will result in a filled circle with diameter about 4-5 px being drawn.
But I've found out that I actually can't rely on the width of the pen while drawing a circle this way. This method is meant only for drawing of border of this shape, thus for drawing filled ellipse it's much better to use Graphics.FillEllipse.
Another quite important fact to consider is that both of mentioned functions take as parameters coordinates that specify "upper-left corner of the rectangle that specifies the boundaries of the ellipse", so I should subtract half of the radius from both coordinates to make sure the original coordinates specify the middle of this circle.
Here's the new code:
// draw the white background:
SolidBrush whiteBrush(Color::White);
graphics.FillRectangle(&whiteBrush, 0, 0, w, h);
// draw stroke:
Pen blackBrush(Color::Black);
std::vector<Gdiplus::Point> stroke = getStroke();
for (UINT i = 0; i < stroke.size(); ++i)
graphics.FillEllipse(&blackBrush, stroke[i].X - 2, stroke[i].Y - 2, 4, 4);
// draw original points:
Pen redBrush(Color::Red);
std::vector<Gdiplus::Point> origStroke = getOriginalStroke();
for (UINT i = 0; i < origStroke.size(); ++i)
graphics.FillRectangle(&redBrush, origStroke[i].X, origStroke[i].Y, 1, 1);
which yields following result:
So in case someone will face the same problem as I did, the solution is: