I am making my homework and I have 2 functions unionRect and intersectRect.I am creating a set of my first class Rectangle from a file. I have to return the union and intersect rectangle of that oColl.I am having a problem with returning the values because the object returns two 0 values.
I have tried to return different things but I couldn't do it.
This is from the first class Rectangle
Rectangle unionRect(const Rectangle& rec) const {
int ux1, ux2, uy1, uy2;
ux1 = min(ix1, rec.ix1);
uy1 = min(iy1, rec.iy1);
ux2 = max(ix2, rec.ix2);
uy2 = max(iy2, rec.iy2);
Rectangle a(ux1, ux2, uy1, uy2);
return a;
}
This is second class RectangleCollection function to read from file
RectangleCollection(const string& strFileName) {
ifstream ifile(strFileName.data());
copy(istream_iterator<Rectangle>(ifile), istream_iterator<Rectangle>(), inserter(oColl,oColl.begin()));
};
this is my RectangleCollection class function for union Rect
Rectangle calcUnionColl() {
set<Rectangle>::iterator it;
Rectangle a;
for (it = oColl.begin(); it != oColl.end(); ++it) {
a = unionRect(*it);
}
return a;
}
and the .txt file is
5 5 10 10
6 6 12 12
but when i call calcUnionColl it returns me
x1:0 x2:6 y1:0 y2:12
I expect the output to be x1:5 x2:6 y1:5 y2:12.
Thank you in advance!
You are not union'ing all of the Rectangles together that are in the collection. You are union'ing each individual Rectangle only with the one Rectangle that calcUnionColl() is called on, and then you return the result of just the last union that was performed.
Try something more like this instead:
class Rectangle {
public:
...
Rectangle unionRect(const Rectangle& rec) const;
...
}
Rectangle Rectangle::unionRect(const Rectangle& rec) const {
int ux1, ux2, uy1, uy2;
ux1 = std::min(ix1, rec.ix1);
uy1 = std::min(iy1, rec.iy1);
ux2 = std::max(ix2, rec.ix2);
uy2 = std::max(iy2, rec.iy2);
return Rectangle(ux1, ux2, uy1, uy2);
}
...
class RectangleCollection {
public:
...
Rectangle calcUnionColl() const;
...
}
Rectangle RectangleCollection::calcUnionColl() const {
Rectangle a;
if (!oColl.empty()) {
std::set<Rectangle>::iterator it = oColl.begin();
a = *it++;
while (it != oColl.end()) {
a = a.unionRect(*it++);
}
}
return a;
}
The answer from Remy Lebeau has a minor issue. If oColl is empty, calcUnionColl will return the the default-constructed value of a. So if the default-constructed value of a is x1:0, x2:0, y1:0, y2:0 then if calcUnionColl returns that value, it is impossible to know if that is the actual union of the values in oColl, or if
oColl was empty.
A common trick when finding the the maximum value of multiple ints is to initialize the running maximum with INT_MIN. And in the same way, when finding a minimum value, we initialize the the running minimum with INT_MAX.
Finding the union of rectagles is nothing more than finding the min/max value of the rectangles corner coordinates, so we can use the above trick.
For instance if a = {x1:INT_MAX, x2:INT_MIN, y1:INT_MAX, y2:INT_MIN}, then when calculating the union of a and any rectangle b, we will have:
b.x1 <= a.x1 // a.x1 == INT_MAX
b.y1 <= a.y1 // a.y1 == INT_MAX
b.x2 >= a.x1 // a.x1 == INT_MIN
b.y2 >= a.y2 // a.y2 == INT_MIN
So the union of a and b in this case will be b
Making use of this in calcUnionColl():
// I assume the data-type for your rectangle coordinates is `int`.
// If you use another datatype, change this accoringly.
Rectangle RectangleCollection::calcUnionColl() const {
int i_min = std::numeric_limit<int>::min(); // c++ way of getting INT_MIN
int i_max = std::numeric_limit<int>::max(); // c++ way of getting INT_MAX
set<Rectangle>::iterator it;
Rectangle a(i_max, i_min, i_max, i_min);
for (it = oColl.begin(); it != oColl.end(); ++it) {
a = a.unionRect(*it);
}
return a;
}
Now, if oColl is empty, calcUnionColl() will return x1:INT_MAX, x2:INT_MIN, y1:INT_MAX, y2:INT_MIN. This should be an invalid value for a rectangle since x1>x2 and y1>y2, and should be easy to test for.
Sometimes you don't even have to test for it, since it often is an invalid value "that makes sense" for further calculations.
Related
I am currently mapping a Graph to a Minesweeper like grid, where every Block represents a node.
Here is my Graph class:
class Graph : public sf::Drawable
{
public:
Graph(uint32_t numNodesWidth, uint32_t numNodesHeight);
[[nodiscard]] std::vector<Node> & operator[](std::size_t i)
{ return data[i]; }
[[nodiscard]] sf::Vector2u dimension() const
{ return {static_cast<uint32_t>(data.size()),
static_cast<uint32_t>(data[0].size())};}
...
...
private:
std::vector<std::vector<Node>> data;
};
here is the implementation of the constructor:
Graph::Graph(uint32_t numNodesWidth, uint32_t numNodesHeight)
{
data.resize(numNodesHeight);
for(auto & row : data)
{
row.resize(numNodesWidth);
}
}
Somewhere in another class I read mouse coordinates and convert them to "Graph Coordinates":
sf::Vector2u translatedCoords = toGraphCoords(sf::Mouse::getPosition(window), nodeSize_);
bool inBounds = checkGraphBounds(translatedCoords, graph.dimension());
Here are the helper functions:
sf::Vector2u toGraphCoords(sf::Vector2i mouseCoord, sf::Vector2f nodeSize)
{
return {static_cast<uint32_t>(mouseCoord.y / nodeSize.y),
static_cast<uint32_t>(mouseCoord.x / nodeSize.x)};
}
bool checkGraphBounds(sf::Vector2u mouseCoord, sf::Vector2u bounds)
{
return mouseCoord.x >= 0 &&
mouseCoord.y >= 0 &&
mouseCoord.x < bounds.x &&
mouseCoord.y < bounds.y ;
}
Somehow I get the vector subscript out of range 1655 error when I try to use these new checked Coordinates which is somehow strange, can someone explain to me what I am doing wrong. This error always shows when I try to hover beyond the "Bounds" of the Interactive area, slightly behind or in front the first or the last Node.
Thanks in advance.
There is no guarantee that bounds <= num_nodes * node_size. This is especially risky since there are integer divisions involved, which means that you are at the mercy of rounding.
You could shuffle code around until such a guarantee is present, but there's a better way.
If the checkGraphBounds() function operated on the same math that the grid does, you could be sure that the result would be consistent with grid, no matter how that relates to the bounds.
The ideal way to do so would be to actually use toGraphCoords() as part of it:
bool checkGraphBounds(sf::Vector2u mouseCoord, const Graph& graph,
sf::Vector2f nodeSize)
{
auto coord = toGraphCoords(mouseCoord, nodeSize);
return coord.x >= 0 &&
coord.y >= 0 &&
coord.x < graph.dimensions().x &&
coord.y < graph.dimensions().y) ;
}
With this, you can formally guarantee that should a mouseCoord pass that test, static_cast<uint32_t>(mouseCoord.x / nodeSize.x)} will for certain return a value no greater than graph.dimensions().x.
Personally, I would combine both functions as a method of Graph like so:
class Graph : public sf::Drawable {
// Make nodeSize a member of the Graph
sf::Vector2f nodeSize_;
// This is one of the cases where caching an inferable value is worth it.
sf::Vector2u dimensions_;
public:
std::optional<sf::Vector2u> toGraphCoords(sf::Vector2i mouseCoord) {
sf::Vector2u coord{
static_cast<uint32_t>(mouseCoord.y / nodeSize_.y),
static_cast<uint32_t>(mouseCoord.x / nodeSize_.x)};
};
// No need to compare against 0, we are dealing with unsigned ints
if(coord.x < dimensions_.x &&
coord.y < dimensions_.y ) {
return coord;
}
return std::nullopt;
}
// ...
};
Usage:
void on_click(sf::Vector2i mouse_loc) {
auto maybe_graph_coord = the_graph.toGraphCoords(mouse_loc);
if(maybe_graph_coord) {
sf::Vector2u graph_coord = *maybe_graph_coord;
// ...
}
}
I am trying to implement a painters sort algorithm for a rendering assignment. The premise of the code is that I need to find the average depth of a polygon, and the list of polygons via the depth assigned to them by the for loop.
this is the polygons declaration, as well as a collection of the vertices of the polygon post transformation which are used for the calculation of the depth of the polygon
std::vector<Polygon3D> _polygons;
std::vector<Vertex> _transvertices;
This is the method called by the model class to sort the _polygons vector using std::sort
void Model::Sort()
{
for (int i = 0; i <= GetPolygonCount(); i++)
{
_polygons[i].SetDepth((_transvertices[_polygons[i].GetIndex(0)].Get(2) + _transvertices[_polygons[i].GetIndex(1)].Get(2) + _transvertices[_polygons[i].GetIndex(2)].Get(2)) / 3);
}
sort(_polygons.begin(), _polygons.end(), sortByDepth);
}
This code then links to this binary predicate
bool sortByDepth(const Polygon3D &lhs, const Polygon3D &rhs)
{
float m = lhs.GetDepth(); //For value testing
float n = rhs.GetDepth(); //For value testing
return lhs.GetDepth() > rhs.GetDepth();
}
The issue is, once the sort algorithm starts, the value of lhs and rhs never change - lhs always has a depth of 0 (and looking further into its assignment, it seems to be creating an entirely new polygon?) and rhs always has a value of 30.53 (the depth of the first polygon in the _polygons vertex
I'm concerned that the issue might be with not having a form of iterator linked to the Polygon3D class, but I wouldn't know where to start with making an iterator for the class.
Any help would be appreciated, I've looked through far too many similar questions, but none of them seem to be quite right for my particular problem.
EDIT:
Post got taken down because I didn't provide enough code apparently. I tried to reproduce the problem in a different project but for some reason it iterates just fine there.
This is the "shortest possible reproduction" I tried, but for some reason this doesn't seem to have the same issue as the original.
#include <vector>
#include <algorithm>
class Polygon3D
{
public:
Polygon3D(); // Example data for testing purposes
float GetDepth() const;
void SetDepth(float depth);
private:
float _depthAverage;
};
class Model
{
public:
Model();
size_t GetPolygonCount() const;
void Sort();
private:
std::vector<Polygon3D> _polygons;
std::vector<int> _vertices;
std::vector<int> _transvertices;
};
Polygon3D::Polygon3D()
{
//_depthAverage = float(rand() % 100);
}
float Polygon3D::GetDepth() const
{
return _depthAverage;
}
void Polygon3D::SetDepth(float depth)
{
_depthAverage = depth;
}
Model::Model()
{
for (int i = 0; i < 10; i++)
{
_polygons.push_back(Polygon3D());
}
this->Sort();
}
size_t Model::GetPolygonCount() const
{
return _polygons.size() - 1;
}
bool sortByDepth(const Polygon3D& lhs, const Polygon3D& rhs)
{
float m = lhs.GetDepth();
float n = rhs.GetDepth();
return lhs.GetDepth() > rhs.GetDepth();
}
void Model::Sort()
{
for (int i = 0; i <= GetPolygonCount(); i++)
{
_polygons[i].SetDepth(float(rand() % 100) / 3);
}
sort(_polygons.begin(), _polygons.end(), sortByDepth);
}
int main()
{
Model m = Model();
}
Edit 2:
I played around with just using an auto type variable to manually iterate over _polygons, and that seems to work. I dont understand why std::sort doesnt
auto begin = _polygons.begin();
while(true)
{
begin++;
}
The answer turned out to be something incredibly stupid on my own part. The issue was the copy constructor used within the Polygon3D class - I had forgotten to copy over the depth value in the copy constructor, which meant lhs did not get a depth value.
So I have this function which suppose to return the area of a triangle using only the 3 points representing it's "corners". In the function I have to check if all the points have the same X or Y values ( Because then it's not a triangle )
This is the function itself:
double Triangle::getArea() const
{
if (_points[0].getX() == _points[1].getX() == _points[2].getX() || _points[0].getY() == _points[1].getY() == _points[2].getY())
{
std::cout << "Error: X values or Y values of all the points are equal";
_exit(0);
}
return 0.5 * (_points[0].getX() * (_points[1].getY() - _points[2].getY()) + _points[1].getX() * (_points[0].getY() - _points[3].getY()) + _points[2].getX() * (_points[0].getY() - _points[1].getY()));
}
The triangle is a class, which inherits a vector of the class "Point" which contains all of the points (std::vector ), The class "Point" contains the X and Y value of the point.
The problem occurs when I try to put this Points to the function:
Point point1(0, 0);
Point point2(5, 0);
Point point3(0, 6);
I also noticed that when I change the 3rd point to be (5, 0) The function don't throws my exception, but then it crashes in the return statement with the error "Vector subscript out of range"
This is the base class of Triangle called "Polygon" which creates the Points vector:
#pragma once
#include "Shape.h"
#include "Point.h"
#include <vector>
class Polygon : public Shape
{
protected:
std::vector<Point> _points;
public:
Polygon(const std::string& type, const std::string& name) : Shape(name, type)
{
}
virtual ~Polygon()
{
std::vector<Point>().swap(_points);
}
};
Sorry for the long question, but this problem stumped me for quite a while. And I'm not very sure why it happens, I've tried and read more about the usage of Vectors, but I don't see something wrong with my function nor with my Vector implementation.
Thanks for everybody who is willing to help! :D
EDIT: The problems have been found, I was being dumb as usual. check the first comments for the solve
I have a class which is called Position:
class Position
{
public:
... //Constructor, Destructor, ...
private:
signed int x_;
signed int y_;
}
Then I have a vector which stores pointers of Positions:
std::vector<Position*> positions
How can I check if a Position is contained in the vector? For example, I have an object of a Position:
Position* p_ = new Position(0, 0);
And I want to check if the vector contains a Position with the same coordinates?
Which operator do I have to overload?
Thanks,
Barbara
auto it = find_if(positions.begin(), positions.end(),
[=](position* p)
{
return p->x() == p_->x() && p->y() == p_->y();
});
if(it != positions.end())
{
//object found
}
However, unless you have a real reason to store pointers in the vector (e.g. you're going to use polymorphism), storing objects directly is much simpler.
vector<position> v;
v.push_back(Position(1, 2));
...
Position p_(1, 4);
auto it = find_if(v.begin(), v.end(),
[=](position p)
{
return p.x() == p_.x() && p.y() == p_.y();
});
if(it != v.end())
{
//position found
}
In the latter case it is possible to further simplify the code by overloading operator == for position.
bool operator == (position p1, position p2)
{
return p1.x == p2.x && p1.y == p2.y; //assuming operator == is declared friend
}
Then you can
auto it = find(v.begin(), v.end(), p_);
And I want to check if the vector contains a Position with the same coordinates? Which operator do I have to overload?
If you had a vector of positions (instead of vector of pointers to positions) the operator you'd have to overload would be:
bool Position::operator==(const Position& p);
With this code, you could write (assuming you are using std::vector<Position> positions;, and not std::vector<Position*> positions;):
using std::find; using std::begin; using std::end;
const Position p{}; // element that is sought after
bool exists = (end(positions) != find(begin(positions), end(positions), p));
[comment:] Yeah I am also quite unsure about this. I asked one of my teammates why he does this [i.e. store by pointers] and he said it would be more efficient and faster and it should not be changed - EVER.
It is probably not more efficient, nor faster than storing by values. If you are not in a position to change the vector though, you will have to add the operator declared above, and also a predicate that compares a Position instance to the values in a Position pointer, and using that with std::find:
const Position p{}; // element that is sought after
auto matches_position = [&p](Position const* const x)
{
return x != nullptr // can positions in the vector be null?
&& p == *x;
};
bool exists = (end(positions) != find(begin(positions), end(positions),
matches_position));
== Coping strategy ==
I would go for the first version (no pointers in the vector), by doing the following:
create a new (minimalistic) project, that fills two separate vectors, with a bunch of randomized positions (fixed number of positions, between 2000 and 10000 instances or so); the vectors should contain positions by pointer and by value respectively, with the same values in each position (a position should be in both vectors, at the same index)
perform the search for the same values in both vectors.
repeat the searches multiple times (to average and minimize timing errors)
take results to your colleague(s).
There are two outcomes from this: either your colleague is right (which seems pretty unlikely, but hey! who knows?) or he is wrong, and his code that "should never be changed" - well ... it should be changed.
Add this to class Position
Public:
bool isSamePosition(position * p){
return p->x == this->x && p->y ==this->y;
}
Then compare with all in the vector
bool unique = true;
for (int i = 0; i < positions.length(); ++i){
if (new_position->isSamePosition(positions[i])
unique = false;
}
if (unique==true)
//do something like push_back vector
;
I'm making a simple 2d platformer and I need to find all the values of all objects created, these objects would represent the in game collidables. So I would need all of the x/y co-ordinates, width and height so that I can check if any of them are colliding with the player.
Class Looks like:
class CollidableObject
{
public:
CollidableObject();
virtual ~CollidableObject();
int Height;
int Width;
DirectX::SimpleMath::Vector2 position;
bool collidable;
};
It's obviously a very simple class and I just need a way to find out if the player position collides with any of the collidable objects values, I don't need collision code just a way to get at all of the collidable objects values at once. Hope I've made it clear.
class CollisionPred(
public:
CollisionPred( Player p) : p_(p) {}
bool operator()(const ColiidableObject& o) {
// process object and return true if there is a collision, i.e:
return o.Height*o.Height + o.Width*o.Width > p_.distance;
}
private:
Player p_;
;
int main() {
//...
std::vector<CollidableObject> v(100);
std::vector<CollidableObject>::iterator it =
std::find_if(v.begin(), v.end(), CollisionPred());
//...
}
The simplest and yet, the most popular way of doing it is to just iterate through a collection of collidables to check if any collides with a player.
vector<CollidableObject> objects;
for(CollidableObject& obj: objects)
{
if (obj.position.x - obj.width < player.position.x + player.radius
&& obj.position.x + obj.width > player.position.x - player.radius
&& obj.position.y - obj.height < player.position.y + player.radius
&& obj.position.y + obj.height > player.position.y - player.radius
)
// collision happened
}