I am new to MFC trying to learn from the scratch.
I am trying to create a small dialog based application in VS2012 - MFC.
On the dialog window i have drawn a rectangle (in OnPaint event) like this.
CPaintDC dc(this);
CRect background1(10,10,208,92);
dc.Rectangle(10,10,208,92);
Then i filled the bacground with some color. Like this.
CBrush brush1;
brush1.CreateSolidBrush(RGB(2,3,4));
dc.FillRect(background1,&brush1);
Now i wanted to draw waves in form of pixels continuously inside the rectangle.
As of now what i have done is,
bool top = false;
for(i=10,j=92;i<200 && j>10;)
{
pDC->SetPixel(i,j,NewColor);
Sleep(10);
if(!top)
j-=1;
else
j+=1;
if(j%4==0)
{
i+=1;
}
if(j==12)
top=true;
if(j==90)
top = false;
}
I am just drawing the pixels straightaway on the window, but within the dimensions where the rectangle lies. And the waves stop as it reaches the right dimension of the rect. But i feel thats not the right way to do it.
I want to draw the waves inside the rectangle and also continuosly, like when it reacahes the right end it should move left and it should be continuous.
Is there any proper way in MFC to draw something inside a rectangle (technically inside another object)?
Please help me out.
The ScrollWindow function can be used to scroll the existing graph to the left. Then you draw the new data to the right of the scrolled area. The drawing will be much faster if you use the Polyline function instead of DrawPixel: Polyline draws an array of pixels.
I tried your code and just added some condition, so that if it reaches right side it should start moving toward left.
void CMyDlg::OnBnClickedOk()
{
CWnd *cWnd = FromHandle(m_hWnd);
CDC *pDC = cWnd->GetDC();
bool top = false;
bool right = false;
int i,j;
for(i=10,j=92;i<=200 && j>10;)
{
pDC->SetPixel(i,j,RGB(255,255,255));
Sleep(10);
if(!top)
j-=1;
else
j+=1;
if(j%4==0)
{
if(!right)
i++; // if reaches right side i--;
else{
i--;
}
}
if(j==12)
top=true;
else if(j==90)
top = false;
if(i == 200)// make right = true if reaches right side
right = true;
else if(i == 10)// make false it reaches left side
right = false;
}
}
I am getting output something like this
NOTE: this will cause infinite loop, you need to check condition where you have to stop printing pixels.
You are NOT looping with Sleep(10) in your WM_PAINT handler, are you?
MFC or not, you should separate your data processing from presentation.
It appears you are trying to do an animation; the simplest way to do it is to have an off-screen (memory) DC and drawing your pixels on it. At certain times (frame rate?), you would call parent's InvalidateRect()/UpdateWindow() pair. Then in ON_PAINT, you would BitBlt() your prepared DC onto your preferred location in the dialog.
Related
I'm adding Direct Draw rendering option to my 2D Graphics Engine.
When setting the Direct Draw Clipper on a non fullscreen application clipper clips the client area with an offset if the Window's client area top left position is not on the 0,0 of the screen.
Here is my clipper setup code :
LPDIRECTDRAWCLIPPER directDrawClipper = nullptr;
if (!FAILED(mDirectDraw->CreateClipper(0, &directDrawClipper, NULL))) {
if (!FAILED(directDrawClipper->SetHWnd(0, App->getWindow()->getHandle()))) {
if (!FAILED(mDirectDrawFrontBuffer->SetClipper(directDrawClipper))) {
if (!FAILED(mDirectDrawBackBuffer->SetClipper(directDrawClipper))) {
return true; //all good
} else {
throw Error::Exception(L"DirectDraw arka görüntü bellek tamponu kesicisi kurulamadı",
L"Renderer Kurulum Hatası");
}
} else {
throw Error::Exception(L"DirectDraw ana görüntü bellek tamponu kesicisi kurulamadı",
L"Renderer Kurulum Hatası");
}
} else {
throw Error::Exception(L"DirectDraw kesicisi pencereye kurulamadı",
L"Renderer Kurulum Hatası");
}
} else {
throw Error::Exception(L"DirectDraw kesicisi kurulamadı",
L"Renderer Kurulum Hatası");
}
And here is the screenshot :
the size of the white areas are the distance of client area to the screen upper left corner.
Moving the window to upper left corner of the screen before setting the clipper and moving it back to original position doesn't help.
Thanks in advance.
For the curious I solved the problem with a hack.
Before setting the clipper move the window to a position which the client area of the window will be on 0,0 of the screen. You can use ClientToScreen function to determine the window position which client area will be on the 0,0 of the screen.
After setting up the DirectDraw move the window back to it's original position but there is a problem, if you move the window immediately after setting up the DirectDraw issue persists. (I believe clipper setting functions works async). To solve that you can set a windows timer , right after setting the DirectDraw set a timer (1 second got the job done in my case) and move window back to it's original position when the timer updates.
To summarize the process :
Move window to a new position which client area of the window will be on the 0,0 of the screen.
Set up the clipper
Set a timer (1 second worked in my case)
When timer updates move window back the it's original position.
But still if someone knows the proper solution it would be nice.
I'm trying to make some animation with SDL2.
SDL2 has some bad performance, and my simple chess game was running at 10fps.
I don't want to redraw the whole screen every frames, it takes way to long. So in order to optimize my game I decided to code an Animation class ( a simple fade in/out effect) which only redraw some part of the screen everyframe (basicly redraw the selected piece)
void myGame::sdlLoop() {
SDL_Event events;
bool quit = false;
while (!quit) {
bool redraw = false; //We assume we don't want to redraw everything (yet)
while (SDL_PollEvent(&events)) {
switch (events.key.keysym.scancode) {
....
}
if(redraw) draw(); // Only redraw whole screen IF NEEDED
else drawAnimations(); // Better use that for better performance
}
}
}
void myGame::drawAnimations(){
int i = 0;
while(i < arr.size()){
....
drawThingsAtCertainsPixels(now_time, animation_start_time, animation_duration); //Basicly a simple fade effect, something like
//pixelColor = color1 * advancement + color2 * (1 - advancement)
}
// Show the window
SDL_RenderPresent( m_renderer );
}
So far so good, but I noticed a weird behavior.
The animation is "jerky", most of the frames are skipped
I ended up avec all of my fadeout unfinished because the last frame where skipped.
But, when I constantly move the mouse, everything goes right and no frame are dropped
I think it is linked to SDL wanting to optimize performance, and only run at 100% speed when someting important is going on (user inputting things or windows interacted).
Do you know why is this happening, and how to fix that ?
I mean how to have SDL2 computing every frame even if I don't move the mouse.
I feel a bit guilty for finding the solution right after posting this question (but I swear I tore my hair out a lot)
The solution is quite simple
while (!quit) {
bool redraw = false; //We assume we don't want to redraw everything (yet)
while (SDL_PollEvent(&events)) {
switch (events.key.keysym.scancode) {
....
}
if(redraw) draw(); // Only redraw whole screen IF NEEDED
else drawAnimation(); // Better use that for better performance
}
}
Just move the drawAnimations() function a bit lower, out of the while (SDL_PollEvent(&events)) {
while (!quit) {
bool redraw = false; //We assume we don't want to redraw everything (yet)
while (SDL_PollEvent(&events)) {
switch (events.key.keysym.scancode) {
....
}
}
if(redraw) draw(); // Only redraw whole screen IF NEEDED
else drawAnimation(); // Better use that for better performance
}
I'm building a 2D drawing canvas that allows users to draw shapes on the screen by first selecting which shape you want to draw, and then clicking into the drawing area to draw your shape. Here's what the UI looks like for a better visualization:
So far, I have some code that allows the user to click on the derired shape, draw the shape, then click on another shape to draw, and so on...
First I check if a button has been clicked on in my processMouse() function:
if (x >= 0 && x <= 95 && y >= 302 && y <= 380) { // area of SQUARE shape
// squareShape = false by default
squareShape = true;
}
else squareShape = false;
If it has, we highlight the shape in my display() function:
if (squareShape != false) {
drawHighlight(100, 50, 350, true); // highlighted SQUARE
}
and in the same function, we enable some points to be drawn:
if (areaClicked2 != false) {
for (int i = 0; i < points.size();i++)
{
//areaClicked2 = true;
glLineWidth(1);
glBegin(GL_LINES);
glVertex2i(points[i].x + lineOffset.x, points[i].y + lineOffset.y);
}
}
I have applied this exact same method for every other button, and they all seem to be working fine...
My issue?
If I click on a button to draw squares, it draws a few squares, but when I click on an another button, my squares disappear because I don't have the square button selected. So for every button when I click to draw, the shapes are only visible until I click on an another button. How can I fix this?
EDIT:
Everytime I want to make a new points vector for a new shape, I make a new vector like this:
std::vector <point> lines; // old vector
point OnePoint;
std::vector <point> dots; // new vector
point OneDot;
Should I not be doing this? If not, how should I be? I tried to keep lines and just make new point variables but then when I draw, they're exactly the same point.
Just a guess, I think you are clearing(or creating new) points[] collection on each shape selection. You should preserve old point collection and append new shape points so that it will be visible even on different shape selection.
I move sprites stored in a formationvector by holding the mousebutton. Problem is: Whenever i move the sprite onto another one, i move both sprites at the same time.
What i want: Moving the sprite1 over the other sprites2 without changing the position of sprite 2 or with other words:
-Testing in a loop if a sprite of a vector is clicked on
-If sprite is clicked on:
Move this sprite around while button is pressed but somehow stop this move while this movement in order to avoid moving more than this one sprite.
Here is my try so far:
while (App.pollEvent(Event))
{
// Window closed
if (Event.type == sf::Event::Closed)
{
return (-1);
}
if (sf::Mouse::isButtonPressed(sf::Mouse::Left))
{
for (size_t k = 0; k < formation.size(); k++)
{
if (isMouseOver(formation[k], App) == true)
{
Mouseposition = sf::Vector2f(sf::Mouse::getPosition(App));
Mouseposition.x = Mouseposition.x - formation[k].getLocalBounds().width / 2;
Mouseposition.y = Mouseposition.y - formation[k].getLocalBounds().height / 2;
formation[k].setPosition(sf::Vector2f(Mouseposition));
Formation_playernames.clear();
Formation_playerinformation.clear();
Formation_Playernames(Font, Formation_playernames, formation, playerlist);
Formation_Playerinformation(Font, Formation_playerinformation, formation, playerlist);
}
}
}
}
The Formation-functions store the correct new positions of the sprites and their colors based on the positions into the formation vector
The problem is the for-loop, i could do it without but that would lead to a much longer code. How could i do it ?
Instead of simply checking whether or not the mouse button is pressed, you should structure your code so you can check exactly when the button is pressed, and when it is released. You can then structure your code such that different presses of the button are distinguished from each other. semi-pseudocode follows
bool button_is_pressed = false;
Sprite* currently_selected_sprite = nullptr;
// main application loop
while (...)
{
...
// other application logic
...
if (!button_is_pressed)
{
if (CheckIfButtonIsPressed())
{
button_is_pressed = true;
// Button was just pressed.
// Select the appropriate sprite by checking
// the mouse coordinates against the positions
// of the sprites.
}
else
{
// Button not being pressed.
// Likely no logic needed here.
}
}
else // button_is_pressed == true
{
if (CheckIfButtonIsPressed())
{
// Button is being held down.
// Implement dragging logic using the
// pointer to the selected sprite.
}
else
{
button_is_pressed = false;
// Button was just released.
// Deselect the sprite.
currently_selected_sprite = nullptr;
}
}
}
Alternatively, you could handle mouse events, which will take care of much of this logic for you. http://sfml-dev.org/documentation/2.0/classsf_1_1Event.php
In a more English like pseudo code, this is what your function is doing:
if the mouse button is currently pressed
move all the sprites which are under the mouse to be centered on the mouse cursor
else
do nothing
This is what I'm suggesting
at the moment the mouse button is pressed down
select the sprite which is under the mouse cursor
at the moment the mouse button is released
deselect the selected sprite
if the mouse button is currently pressed, and a sprite is selected
move the selected sprite to the position of the mouse cursor
What I'm trying to do is:
I have a sprite. And this sprite has 3 textures. First texture, let's call it "not-visited", is the initial one. Now, when I click on the sprite it should change it texture to "clicked" meaning that the sprite was clicked. And this texture should remain until I click somewhere else on the screen. When I do this, the texture should change to the third one, "visited". And if I click on the sprite again it should change its texture to "clicked"...
So, I should have here a bool function called "ClickDetected" and its code should do this:
if (event.type == Event::MouseButtonReleased && event.mouseButton.button == Left)
if (mouse_over_sprite)
return true;
Right? OK, then, I have a class Node. This class has a method "Clicked":
bool Clicked {
if (ClickDetected) { return true; }
}
I know it may be unnecessary, but let it stay for now. Next, in Update() method of this class:
if (Clicked) { change_texture_to_"clicked"; if (!visited) visited=true; }
else {
if (!visited) change_texture_to_"not-visited";
if (visited) change_texture_to_"visited";
}
But this doesn't work how it should. When I click on sprite texture changes to "clicked". When I release the mouse button the "clicked" texture stays... But when I move the mouse texture changes to "visited". And it should remain "clicked", until I click somewhere else. I tried to use a while loop here, but it didn't work. What can I do?
I've put this code to Graph class (method Update()) to manage many Nodes. I've change it a little bit and now it works fine.