C++/CLI Visual C++ 2010 Express - Drawing multiple ellipses - c++

I want to draw multiple filled ellipses on/in some panel. Drawing single one isnt problem, i am using:
Color aColor = Color::FromArgb( 255, 0, 0 );
SolidBrush^ aBrush = gcnew SolidBrush(aColor);
Rectangle rect = Rectangle(x, y, 10, 10);
e->Graphics->FillEllipse(aBrush, rect);
It draws red ellipse bordered by rectangle, and fills it with red color. (assuming i will give x and y). The problem i met, is when I want to draw multiple ellipses like that, in RANDOM places. So i need to pass random x and y (using rand() % somenumber) but i am not sure, how can i pass these variables into the panel1_paint function and draw them when both numbers are randomized. Also, ofc i dont want the last ellipse to disappear when drawing new one. The only way is using global variables?
Any ideas?
Well, i tried as suggested, to use loop inside panel and i got that:
for(int i=0; i<ile_przeszkod; i++){
int x = rand() % 690; int y = rand() % 690;
Color aColor = Color::FromArgb( 255, 0, 0 );
SolidBrush^ aBrush = gcnew SolidBrush(aColor);
Rectangle rect = Rectangle(x, y, 10, 10);
e->Graphics->FillEllipse(aBrush, rect);
MessageBox::Show("x: "+x+ " y: " +y);
}
ile_przeszkod means how many of them i want to be drawn, and message box showes me what numbers it randomized so i am sure ellipses dont overlap. The problem is, after "invalidating" panel1 i see only 1 ellipse. :/ What should i do to see both of them?

all the x, y coordinates are random , so they don't depend on some other deciding procedure, So that need not to be passed to panel1_paint rather you can run a lpop and generate random number to use them as your x, y coordinates.

Related

Draw using textured QBrush without translating texture

I have made a simple texture of an outlined box and have the following snippet of code which draws a checkerboard pattern:
scene.setSceneRect(0, 0, 1000, 1000);
ui->g_view->setScene(&scene);
QPixmap texture("block.png");
QBrush brush(texture);
int count = 0;
for(int x=0; x<1000; x += 50) {
for(int y=0; y<1000; y += 50) {
if (count % 2 == 0) {
scene.addRect(x, y, 50, 50, Qt::NoPen, brush);
}
count++;
}
// Offset rows by 1
count++;
}
This works fine:
However, when I modify the code so that the boxes are drawn "off grid":
scene.addRect(x + 5, y + 5, 50, 50, Qt::NoPen, brush);
The following output is produced:
What I expected to happen was that each call to addRect would draw the texture starting from the top corner each time.
However, for some reason qt translates the texture using the location that it is being drawn too, almost like the texture is infinitely tiled in the background and addRect is just cutting away a window.
How can I make drawRect behave as I expected, i.e. no matter what the values of x and y are the texture is always drawn from the top left corner.
Edit: block.png
I solved this issue by instead using the addPixmap method.
//scene.addRect(x + 5, y + 5, 50, 50, Qt::NoPen, brush);
QGraphicsPixmapItem *pix_map = scene.addPixmap(texture);
pix_map->setPos(x + 5, y + 5);

Trying to draw rectangles stored in an array, but only one rectangle appears?

My code is here:
As stated above, I'm trying to draw a series of bars across the screen with different x positions and I've stored them in arrays. It seems the code only draws 1 rectangle, even though I've checked and each bar has a different x position so I'm sure its an issue with how I'm drawing the objects but it feels right.
I've also made a similar program with vectors using the same loop for drawing but with .at(i) instead which does work but this oddly does not.
I've been trying to figure this out for a while and I'm tired now so please help, point out my errors... etc...
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(640, 640), "Square", sf::Style::Close | sf::Style::Resize);
sf::RectangleShape bar[64] = {sf::RectangleShape(sf::Vector2f( (window.getSize().x)/64.0f ,100.0f))};
// creates 64 bars of equal width
for (int i = 0; i < 64; i++)
{
bar[i].setFillColor(sf::Color(0, 0, 255, 255));
bar[i].setPosition( 10*i , (window.getSize().y)/2);
// sets bars x position to shift over for every bar
}
bar[3].setPosition(600, 300);
// just a test doesn't render even though it should
while (window.isOpen())
{
//////////////////////////////////////////////////
window.clear(sf::Color(130, 130, 150, 255));
for (int i = 0; i < 64; i++)
{
window.draw(bar[i]);
}
window.display();
/////////////////////////////////////////////////
}```
I cut out the rest of the code as the rest works and really has nothing to do with the code for simplicity sake
I want it to render out rectangles across the screen but it only displays one and I can't figure out why?
sf::RectangleShape has default ctor:
sf::RectangleShape::RectangleShape ( const Vector2f & size = Vector2f(0, 0) )
You have defined rectangle's size only for the first one, other 63 have default size (0,0).
You can copy/paste your rect definition in raw array, or use std::vector and call ctor which takes value and number of elements:
std::vector<sf::RectangleShape> bars( 64, // num of elems
sf::RectangleShape( sf::Vector2f(window.getSize().x/64.0f ,100.0f) ) );
Another solution is to call setSize in every iteration of loop (just like with setFillColor, setPosition etc).

C++ DirectX DrawText in multiple colors

I use ID3DXFont interface to draw text and that perfectly suits my needs as long as complete string is in single color. Now I'd wish to draw a string but in multiple colors. For instance "abc", with a in red, b in yellow, etc.
I know that I could draw each letter on its own, giving a different Color parameter to DrawText each time. The only issue with this is that I do not know how many pixels should I offset after each letter because every letter has a different width. Hardcoding widths is not really a good solution.
The ID3DXFont interface doesn't allow you to draw multiple colors within a single invocation of DrawText. However, it can give you the bounding rectangles of any text that you wish to draw using the DT_CALCRECT flag, so you do not need to hardcode widths of particular glyphs within your font. This also means you can switch the font and/or size of the font without needing to modify your drawing code, or hardcoding new width. For example:
ID3DXFont* font = ...;
const char* strings[] = { "A", "i", "C" };
D3DCOLOR colors[] = { D3DCOLOR_ARGB(255, 255, 0, 0), D3DCOLOR_ARGB(255, 0, 255, 0), D3DCOLOR_ARGB(255, 0, 0, 255) };
RECT r = { 10,10,0,0}; // starting point
for (int i = 0; i < _countof(strings); ++i)
{
font->DrawText(NULL, strings[i], -1, &r, DT_CALCRECT, 0);
font->DrawText(NULL, strings[i], -1, &r, DT_NOCLIP, colors[i]);
r.left = r.right; // offset for next character.
}
Note: I have used 'i' instead of 'b' from your example, because it makes it apparent that the rectangles are correct, as 'i' is (generally) a very thin glyph. Also note that this assumes a single line of text. The calculated rectangle also includes height, so if you are doing multiple lines, you could also use the height of the calculated rectangle to offset the position.

Drawing points of handwritten stroke using DrawEllipse (GDI+)

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:

Uneven Circles in Connect 4 Board

I'm in the process of creating a 2P Connect 4 game, but I can't seem to get the circular areas to place tokens spaced evenly.
Here's the code that initializes the positions of each circle:
POINT tilePos;
for (int i = 0; i < Board::Dims::MAXX; ++i)
{
tileXY.push_back (std::vector<POINT> (Board::Dims::MAXY)); //add column
for (int j = 0; j < Board::Dims::MAXY; ++j)
{
tilePos.x = boardPixelDims.left + (i + 1./2) * (boardPixelDims.width / Board::Dims::MAXX);
tilePos.y = boardPixelDims.top + (j + 1./2) * (boardPixelDims.height / Board::Dims::MAXY);
tileXY.at (i).push_back (tilePos); //add circle in column
}
}
I use a 2D vector of POINTs, tileXY, to store the positions. Recall the board is 7 circles wide by 6 circles high.
My logic is such that the first circle starts (for X) at:
left + width / #circles * 0 + width / #circles / 2
and increases by width / #circles each time, which is easy to picture for smaller numbers of circles.
Later, I draw the circles like this:
for (const std::vector<POINT> &col : _tileXY)
{
for (const POINT pos : col)
{
if (g.FillEllipse (&red, (int)(pos.x - CIRCLE_RADIUS), pos.y - CIRCLE_RADIUS, CIRCLE_RADIUS, CIRCLE_RADIUS) != Gdiplus::Status::Ok)
MessageBox (_windows.gameWindow, "FillEllipse failed.", 0, MB_SYSTEMMODAL);
}
}
Those loops iterate through each element of the vector and draws each circle in red (to stand out at the moment). The int conversion is to disambiguate the function call. The first two arguments after the brush are the top-left corner, and CIRCLE_RADIUS is 50.
The problem is that my board looks like this (sorry if it hurts your eyes a bit):
As you can see, the circles are too far up and left. They're also too small, but that's easily fixed. I tried changing some ints to doubles, but ultimately ended up with this being the closest I ever got to the real pattern. The expanded formula (expanding (i + 1./2)) for the positions looks the same as well.
Have I missed a small detail, or is my whole logic behind it off?
Edit:
As requested, types:
tilePos.x: POINT (the windows API one, type used is LONG)
boardPixelDims.*: double
Board::Dims::MAXX/MAXY: enum values (integral, contain 7 and 6 respectively)
Depending on whether CIRCLE_SIZE is intended as radius or diameter, two of your parameters seem to be wrong in the FillEllipse call. If it's a diameter, then you should be setting location to pos.x - CIRCLE_SIZE/2 and pos.y - CIRCLE_SIZE/2. If it's a radius, then the height and width paramters should each be 2*CIRCLE_SIZE rather than CIRCLE_SIZE.
Update - since you changed the variable name to CIRCLE_RADIUS, the latter solution is now obviously the correct one.
The easiest way I remember what arguments the shape related functions take is to always think in rectangles. FillEllipse will just draw an ellipse to fill the rectangle you give it. x, y, width and height.
A simple experiment to practice with is if you change your calls to FillRect, get everything positioned okay, and then change them to FillEllipse.