How to make a GUI that plots user-entered function(x)? - c++

I'm developing a program that takes a function of x from the user, also it takes the min and max values of x, then the program have to plot this function.
for example:
user-entered function(x) is: x^2+2x-1
Max value of x is : 3
Min value of x is : -3
now the GUI have to display (if the entered function is free of errors otherwise the error will be displayed to the user) something similar to this image:
The entered function also maybe a little bit complex E.g.(sin(x), cos(2*x+1), etc..)
I'm trying to make this job with C++ and QT, so any advice how to make the plotting part of the program using QT, or if anyone knows better recommendation instead of QT that works with C++ and can do this job.
Thanks in advance.

You are going to need a library that interprets mathematical expressions (i.e: muparser). In the code i did my own math, but you will be doing that using a library. Considering you managed all those; with QCustomPlot you can draw your graphs.
Here's a sample to give an idea how you can use QCustomPlot:
/** I copy pasted the code from one of my projects, please ignore function & class namings
*/
/** somewhere in your window constructor, create a QCustomPlot
* widget and add graphs to your QCustomPlot widget.
* example: ui->plt_freq_domain->addGraph();
*/
void ControllerMain::plot_frequency_domain()
{
QVector <double> vec_keys(1024), vec_values(1024);
//fill x axis values with incrementing numbers from 0 to 1024 (or any other number you want)
// let's say your function is y = x^2 and calculate all y values and store them in vec_values
for(int i = 0; i < vec_keys.size(); i++)
{
vec_values[i] = std::pow(vec_keys[i], 2);
}
// we fill keys with continuous integers [0,1023] so our graph spans along x-axis
std::iota(vec_keys.begin(), vec_keys.end(), 0);
ui->plt_freq_domain->graph(0)->setData(vec_keys, vec_values, true);
ui->plt_freq_domain->graph(0)->setPen(QPen(QColor(245, 197, 66)));
ui->plt_freq_domain->yAxis->setLabel("A/m"); // change it with your unit or just keep empty
ui->plt_freq_domain->yAxis->setRange(*std::min_element(vec_values.begin(), vec_values.end()),
*std::max_element(vec_values.begin(), vec_values.end()));
ui->plt_freq_domain->xAxis->setLabel("f"); // change it with your unit or just keep empty
ui->plt_freq_domain->xAxis->setRange(vec_keys.constFirst(), vec_keys.constLast());
ui->plt_freq_domain->replot();
}

Related

Loop to check apple position against snake body positions

I'm trying to figure out how to write a loop to check the position of a circle against a variable number of rectangles so that the apple is not placed on top of the snake, but I'm having a bit of trouble thinking it through. I tried:
do
apple.setPosition(randX()*20+10, randY()*20+10); // apple is a CircleShape
while (apple.getPosition() == snakeBody[i].getPosition());
Although, in this case, if it detects a collision with one rectangle of the snake's body, it could end up just placing the apple at a previous position of the body. How do I make it check all positions at the same time, so it can't correct itself only to have a chance of repeating the same problem again?
There are three ways (I could think of) of generating a random number meeting a requirement:
The first way, and the simpler, is what you're trying to do: retry if it doesn't.
However, you should change the condition so that it checks all the forbidden cells at once:
bool collides_with_snake(const sf::Vector2f& pos, //not sure if it's 2i or 2f
const /*type of snakeBody*/& snakeBody,
std::size_t partsNumber) {
bool noCollision = true;
for( std::size_t i = 0 ; i < partsNumber && noCollision ; ++i )
noCollision = pos != snakeBody[i].getPosition()
return !noCollision;
}
//...
do
apple.setPosition(randX()*20+10, randY()*20+10);
while (collides_with_snake(apple.getCollision(), snakeBody,
/* snakeBody.size() ? */));
The second way is to try to generate less numbers and find a function which will map these numbers to the set you want. For instance, if your grid has N cells, you could generate a number between 0 and N - [number of parts of your Snake] then map this number X to the smallest number Y such that this integer doesn't refer to a cell occupied by a snake part and X = Y + S where S is the number of cells occupied by a snake part referred by a number smaller than Y.
It's more complicated though.
The third way is to "cheat" and choose a stronger requirement which is easier to enforce. For instance, if you know that the cell body is N cells long, then only spawn the apple on a cell which is N + 1 cells away of the snakes head (you can do that by generating the angle).
The question is very broad, but assuming that snakeBody is a vector of Rectangles (or derived from Rectanges), and that you have a checkoverlap() function:
do {
// assuming that randX() and randY() allways return different random variables
apple.setPosition(randX()*20+10, randY()*20+10); // set the apple
} while (any_of(snakeBody.begin(), snakeBody.end(), [&](Rectangle &r)->bool { return checkoverlap(r,apple); } );
This relies on standard algorithm any_of() to check in one simple expression if any of the snake body elements overlaps the apple. If there's an overlap, we just iterate once more and get a new random position until it's fine.
If snakebody is an array and not a standard container, just use snakeBody, snakeBody+snakesize instead of snakeBody.begin(), snakeBody.end() in the code above.
If the overlap check is as simple as to compare the postition you can replace return checkoverlap(r,apple); in the code above with return r.getPosition()==apple.getPosition();
The "naive" approach would be generating apples and testing their positions against the whole snake until we find a free spot:
bool applePlaced = false;
while(!applePlaced) { //As long as we haven't found a valid place for the apple
apple.setPosition(randX()*20+10, randY()*20+10);
applePlaced = true; //We assume, that we can place the apple
for(int i=0; i<snakeBody.length; i++) { //Check the apple position with all snake body parts
if(apple.getPosition() == snakeBody[i].getPosition()) {
applePlaced=false; //Our prediction was wrong, we could not place the apple
break; //No further testing necessary
}
}
}
The better way would be storing all free positions in an array and then pick a Position out of this array(and delete it from the array), so that no random testing is necessary. It requires also updating the array if the snakes moves.

C++ Function not working as expected

So I know this is a very broad topic, but I'm not sure how to describe it and I'm not sure where the bug is. So I'm making a game in the console window, a roguelike-rpg, (I haven't done the random dungeon yet, but I've done it in other languages.) and I'm having problems dealing with walls.
I have a function called placeMeeting(REAL X, REAL Y) that I use to check for collisions, but it appears to be returning bad values and I couldn't tell you why. I have couple of macros defined: #define AND && and #define REAL double.
Here is the function:
bool GlobalClass::placeMeeting(REAL X, REAL Y)
{
//The return value -- False until proven otherwise
bool collision = false;
//Loop through all walls to check for a collision
for(int i = 0; i < wallCount; i++)
{
//If there was a collision, 'say' so
if (X == wallX[ i ] AND Y == wallY[ i ])
{
//Set 'collision' to true
collision = true;
}
}
return collision;
}
But the strange catch is that it only doesn't work when displaying the screen. The player collides with them all the same even though there not displayed. Even stranger, only the first wall is being displayed.
Here is where the walls are defined:
int wallCount;
//Array of walls
REAL wallX[ 1 ];
REAL wallY[ 1 ];
and
wallCount = 1;
//Basic wall stuff; basically just a placeholder
wallX[ 0 ] = 10;
wallY[ 0 ] = 10;
So I have a function used to render the screen (In the console window of course.) and it looks like this:
for (int y = oGlobal.viewY; y < oGlobal.viewY + oGlobal.viewHeight; y++)
{
//The inner 'x' loop of the view
for(int x = oGlobal.viewX; x < oGlobal.viewX + oGlobal.viewWidth; x++)
{
//Call the function to check this spot and print what it returns
screen += oGlobal.checkSpot(x, y);
}
}
That's not the whole function, just the actual screen refreshing. After 'screen' is printed to the screen, to reduce buffer time. And of course, checkSpot:
STRING GlobalClass::checkSpot(REAL x, REAL y)
{
STRING spriteAtSpot;
//First check for the player
if (x == oPlayer.x AND y == oPlayer.y)
{
spriteAtSpot = oPlayer.sprite;
}
else if (placeMeeting(x, y)) //ITS TEH WALL SUCKAS
{
spriteAtSpot = WALL_SPRITE;
}
else //Nothing here, return a space
{
spriteAtSpot = EMPTY_SPRITE;
}
//Return the sprite
return spriteAtSpot;
}
I know it's a lot of code, but I really don't know where I screwed up.
I really appreciate any help!
P.S. Here is an image to help understand
http://i.imgur.com/8XnaHIt.png
I'm not sure if I'm missing something, but since rogue-like games are tile-based, is it necessary to make the X and Y values doubles? I remember being told that doubles are finicky to compare, since even if you assume they should be equal, they could be very slightly off, causing comparison to return false when you'd think it would return true.
I'm not sure we have enough of your code to debug it, but I have developed a Rogue-like console game, and here is my $.02...
Start over. You seem to be doing this in a very non-OO way (GlobalClass?). Consider objects such as Level (aggregates entire level), DungeonObject (essentially each space on the level; it's a base class that can be inherited from into Wall, Player, etc.). Doing this will make the programming much easier.
Embrace the suck. C++ syntax may suck, but the more you fight against it, the harder it will be to learn. Use && and the built-in datatypes. It won't take long to get used to.
Rouge-like locations are essentially integer-based. Use integer for x, y locations, not doubles (the biggest built-in data-type). Not only is it more efficient, you'll find debugging much easier.
Start in the small. Start with a 5 x 5 dungeon level to get the basics down. Then, if you've designed it correctly, scaling up to a 10x10 or 25x25 will be much easier.
That's how I developed my game; I hope it helps.
Apart from the use of double instead of int, I see something strange in your definition of walls:
int wallCount;
//Array of walls
REAL wallX[ 1 ];
REAL wallY[ 1 ];
and
wallCount = 1;
//Basic wall stuff; basically just a placeholder
wallX[ 0 ] = 10;
wallY[ 0 ] = 10;
You are defining a variable called wallCount, which you later use to go through the elements of your array in your placeMeeting function:
//Loop through all walls to check for a collision
for(int i = 0; i < wallCount; i++)
Then why don't you use wallCount to define the size of your arrays? Of course you can't use that syntax, because the size of a static array must be known at compile time, so you should either use new or std::vector, but still you shouldn't have a variable that defines the length of the array and then use another value when you actually create the array, it is a source of bugs if you fail to keep them aligned. So for example you could do this:
const int wallCount = 1;
int* wallX = new int[wallCount];
int* wallY = new int[wallCount];
But there's a bigger problem: why are you creating arrays of size 1? You are having only one wall! It doesn't really make sense to have arrays of size 1, unless you intend to use another value but you have reduced it to 1 for debugging purposes. But, you wrote this:
Even stranger, only the first wall is being displayed.
That's because you only have 1 wall!
By the way, the way you have designed your data isn't the one I would use. From your checkSpot I understand this: oPlayer.x and oPlayer.y are the coordinates of your player, and x and y are the coordinates of the tile you have to draw (and for which you need to choose the appropriate sprite). If in your map you have 3 walls, you have to put 3 values in wallX and 3 in wallY, and you must make sure that you keep the 2 arrays "aligned" (if the coordinates of your second wall are for example x=10 and y=20, you could get confused, or have buggy code, and instead of saving it as
wallX[1] = 10;
wallY[1] = 20;
you might write
wallX[1] = 10;
wallY[2] = 20; // wrong index!
so it's one more source of bugs), and worse, you must check that they are consistent with other arrays of other objects: you could have, for example, doors, and then following your approach you'd have doorX[] and doorY[], and how can you be sure that you don't have a wall and a door at the same place? Like, if you had
doorX[0] = 10;
doorY[0] = 20;
it would be at the same place as the wall, and the error isn't obvious, because you'd have to cross-check all your arrays to find it. So I would suggest to have a level[height][width] instead, and to have a wall at x=10 and y=20 you could use level[10][20] = 'w';. This would ensure that you only have ONE object per tile. Besides, checking for collisions would be faster: with your approach, if you have 50 walls you need 50 checks; with mine, you always only need one. Ok, performance is certainly not an issue in these games, but still I think you should consider my approach (unless there are other reasons to prefer yours, of course).

Why isn't my pictureBox moving (Visual C ++)?

I am in the early stages of making a basic two-body problem program (for those of you that don't know, a 'two-body problem' is when you have two bodies in space being gravitationally attracted to each other). I have it set up so that on each timer tick the objects (which are pictureBoxes) move in accordance with the direction (in degrees) inputted into a textbox.
Once a couple of IF statements make sure that the values in the textbox are valid, they do this inside of a Button_Press event (the button will start the simulation):
this->SimTick->Enabled=true; //Master timer for simulation
radians1=(int::Parse(DirectionBox1->Text))*(3.14/180); //Converts the degrees entered for the first object into radians for use in trig functions
radians2=(int::Parse(DirectionBox2->Text))*(3.14/180); //Converts the degrees entered for the second object into radians for use in trig functions
Inside the timer_tick event:
this->Object1->Location.X+=(int::Parse(VelocityBox1->Text)*cos(radians1));
this->Object1->Location.Y+=(int::Parse(VelocityBox1->Text)*sin(radians1));
this->StartStop->Text=(radians1.ToString()); //This is just here to check that the math was correct, which it is
I haven't coded C++ in a while, so this might be a really simple mistake, but does anyone have any ideas, or need any more code pasted?
Besides updating the Location property properly to get it to take new values, you probably want to keep a temporary x & y as a float so that you can add fractional values to it.
typedef struct { float x, y ; } floatcoord ;
floatcoord tmpLocation ;
tmpLocation.x += int::Parse(VelocityBox1->Text)*cos(radians1) ;
tmpLocation.y += int::Parse(VelocityBox1->Text)*sin(radians1) ;
this->Object1->Location.set( (int) floorf( tmpLocation.x), (int) floorf( tmpLocation.y) ; // or however you update Location

Scanning through a tilemap and checking properties for each tile

How does one iterate through a tilemap and check each tile?
Is there a correct way to do this, is there a built in function to in cocos2d to check a tile?
Or could it be done e.g. take the tile size set when creating the tile, make a nested for loop and take (x,y) for the middle of the first tile and just iterate by adding tilesize to the x on the inner loop and tilesize to the y on the outer loop?
I am wondering if there is a built in, more performance aware approach.
Thanks
I think you might be able to do it using a for loop and CGPoints.
I'm going to for examples sake get color
and store it in an array I guess
CGPoint myPt;
NSMutableArray *tilesofGray;
for (int x = 0; x < tilemapLength)
{
for (int y = 0; y < tilemapHeight)
{
myPt.x = x;
myPt.y = y;
if([[[tilemap layerNamed:#"background"] tileAt:myPt] getColor] == Grey)
{
[tilesofGray addObject:[[tilemap layerNamed:#"background] tileAt:myPt]];
}
}
}
Is this for a game, for like collision detection or, simply for rendering based on tile type?
Your question here is really ambiguous. Please be specific in what you want. The 3rd sentence in particular would make more sense if you explain what you are needing.
But i'll try to answer based on the title alone....
How big is the tileset? if it's not very big, brute-force may be perfectly fine.
If performance is a concern/issue, or if the tileset is large and not all tiles are ever drawn within the screen at any given time, you need to do scene management of some sort.
scene management:
i think there is a technical term/phrase for this, but basically based on some x,y pt on the tileset (i.e. matrix), you can determine (by a function) which tiles you will need to iterate thru. it should be fun to figure it out as it's presumably a 2d array.

"Invalid Handle Object" when plotting 2 figures Matlab

I'm having a difficult time understanding the paradigm of Matlab classes vs compared to c++. I wrote code the other day, and I thought it should work. It did not... until I added
<handle
after the classdef.
So I have two classes, landmarks and robot, both are called from within the simulation class. This is the main loop of obj.simulation.animate() and it works, until I try to plot two things at once.
DATA.path is a record of all the places a robot has been on the map, and it's updated every time the position is updated.
When I try to plot it, by uncommenting the two marked lines below, I get this error:
??? Error using ==> set
Invalid handle object.
Error in ==> simulation>simulation.animate at 45
set(l.lm,'XData',obj.landmarks.apparentPositions(:,1),'YData',obj.landmarks.apparentPositions(:,2));
%INITIALIZE GLOBALS
global DATA XX
XX = [obj.robot.x ; obj.robot.y];
DATA.i=1;
DATA.path = XX;
%Setup Plots
fig=figure;
xlabel('meters'), ylabel('meters')
set(fig, 'name', 'Phil''s AWESOME 80''s Robot Simulator')
xymax = obj.landmarks.mapSize*3;
xymin = -(obj.landmarks.mapSize*3);
l.lm=scatter([0],[0],'b+');
%"UNCOMMENT ME"l.pth= plot(0,0,'k.','markersize',2,'erasemode','background'); % vehicle path
axis([xymin xymax xymin xymax]);
%Simulation Loop
for n = 1:720,
%Calculate and Set Heading/Location
XX = [obj.robot.x;obj.robot.y];
store_data(XX);
if n == 120,
DATA.path
end
%Update Position
headingChange = navigate(n);
obj.robot.updatePosition(headingChange);
obj.landmarks.updatePerspective(obj.robot.heading, obj.robot.x, obj.robot.y);
%Animate
%"UNCOMMENT ME" set(l.pth, 'xdata', DATA.path(1,1:DATA.i), 'ydata', DATA.path(2,1:DATA.i));
set(l.lm,'XData',obj.landmarks.apparentPositions(:,1),'YData',obj.landmarks.apparentPositions(:,2));
rectangle('Position',[-2,-2,4,4]);
drawnow
This is the classdef for landmarks
classdef landmarks <handle
properties
fixedPositions; %# positions in a fixed coordinate system. [ x, y ]
mapSize; %Map Size. Value is side of square
x;
y;
heading;
headingChange;
end
properties (Dependent)
apparentPositions
end
methods
function obj = landmarks(mapSize, numberOfTrees)
obj.mapSize = mapSize;
obj.fixedPositions = obj.mapSize * rand([numberOfTrees, 2]) .* sign(rand([numberOfTrees, 2]) - 0.5);
end
function apparent = get.apparentPositions(obj)
currentPosition = [obj.x ; obj.y];
apparent = bsxfun(#minus,(obj.fixedPositions)',currentPosition)';
apparent = ([cosd(obj.heading) -sind(obj.heading) ; sind(obj.heading) cosd(obj.heading)] * (apparent)')';
end
function updatePerspective(obj,tempHeading,tempX,tempY)
obj.heading = tempHeading;
obj.x = tempX;
obj.y = tempY;
end
end
end
To me, this is how I understand things. I created a figure l.lm that has about 100 xy points. I can rotate this figure by using
set(l.lm,'XData',obj.landmarks.apparentPositions(:,1),'YData',obj.landmarks.apparentPositions(:,2));
When I do that, things work. When I try to plot a second group of XY points, stored in DATA.path, it craps out and I can't figure out why.
I need to plot the robots path, stored in DATA.path, AND the landmarks positions. Ideas on how to do that?
Jonas:
I'm not saying you're wrong, because I don't know the answer, but I have code from another application that plots this way without calling axes('NextPlot','add');
if dtsum==0 & ~isempty(z) % plots related to observations
set(h.xf, 'xdata', XX(4:2:end), 'ydata', XX(5:2:end))
plines= make_laser_lines (z,XX(1:3));
set(h.obs, 'xdata', plines(1,:), 'ydata', plines(2,:))
pfcov= make_feature_covariance_ellipses(XX,PX);
set(h.fcov, 'xdata', pfcov(1,:), 'ydata', pfcov(2,:))
end
drawnow
The above works on the other code, but not mine. I'll try implementing your suggestion and let you know.
When you call plot multiple times on the same figure, the previous plot is by default erased, and the handle to the previous plot points to nothing. Thus the error.
To fix this, you need to set the NextPlot property of the axes to add. You can do this by calling hold on (that's what you'd do if you were plotting from command line), or you can write
fig=figure;
%# create a set of axes where additional plots will be added on top of each other
%# without erasing
axes('NextPlot','add');
If you want, you can store the axes handle as well, and use plot(ah,x,y,...) to make sure that you plot into the right set of axes and not somewhere strange if you happen to click on a different figure window between the time the figure is opened and the plot command is issued.