Basic Collision problems - c++

I have a sprite class which I am working on making a function for collision. I want the function to return true if two sprites collide. When I call the function I pass the moving objects desired X and Y position along with it's height and width. The invoking sprite is the object I that I would like to check. For example if I wanted to check if player 1's move will intersect player 2's sprite I would say Player2.Collide(p1X, p1Y, p1H, p1W).
bool Sprite::Collides(int x, int y, unsigned short w, unsigned short h) const
{
if ((x == this->GetWidth() + this->GetLeft()) &&
(y >= this->GetTop() && y <= this->GetTop() + this->GetHeight()) ||
((x + w) == this->GetLeft()) &&
(y >= this->GetTop() && y <= this->GetTop() + this->GetHeight()))
return true;
else if ( (y == this->GetHeight() + this->GetTop()) &&
((x >= this->GetLeft() && x <= this->GetLeft() + this->GetWidth()) ||
((y + h) == this->GetTop() && (x >= this->GetLeft() && x <= this->GetLeft() + this->GetWidth()))))
return true;
return false;
}
The sprites are always rectangles. It seems I am forgetting to check some conditions for my collision. If the rectanges collide perfectly then everything works, however if I shift one up then try to butt against the other, it will glide right through. It seems each direction I approach has a similar behavior where it will work only for certain sections. Can you guys help me find what I'm forgetting to check?

I think you need to check the logic of your actual conditions again, shouldn't
(x == this->GetWidth() + this->GetLeft())
just that part of your if statement create a collision? right after that you have an &&. So in this case it needs to be touching on the top AND right, not just the right OR the top???
I also think the above line should really be:
(x <= this->GetWidth() + this->GetLeft())
Also, depending on your actual movement conditions (i.e. velocity, etc...) the above condition could really be
(x <= this->GetWidth() + this->GetLeft() - 1)
Lets explain the minus 1:
If you have two objects, one moving directly upward, and another moving directly downward, i.e. vertically and parallel to one another, then GetWidth + GetLeft creates a "went by" each other condition not a collision condition.
Example Sprites each 4 x 4 moving VERTICALLY with respect to each other:
Sprite A # (0, 2) Sprite B # (4, 2)
0 1 2 3 0 1 2 3
------------------------- -------------------------
| | | | | | | | | |
------------------------- -------------------------
| | | | | | | | | |
------------------------- -------------------------
| | | | | | | | | |
------------------------- -------------------------
| | | | | | | | | |
------------------------- -------------------------
Sprite A # (0, 1) Sprite B # (4, 3)
0 1 2 3
-------------------------
| | | | |
-------------------------
| | | | | 0 1 2 3
------------------------- -------------------------
| | | | | | | | | |
------------------------- -------------------------
| | | | | | | | | |
------------------------- -------------------------
| | | | |
-------------------------
| | | | |
-------------------------
Sprite A # (0, 0) Sprite B # (4, 4)
0 1 2 3
-------------------------
| | | | |
-------------------------
| | | | |
-------------------------
| | | | |
-------------------------
| | | | | 0 1 2 3
------------------------- -------------------------
| | | | |
-------------------------
| | | | |
-------------------------
| | | | |
-------------------------
| | | | |
-------------------------
When sprite a is at location (0, 0), then GetWidth + GetLeft = (0 + 4) which is RIGHT of the actual end of the sprite. IF another sprite is moving completely vertically downward with x position 4, then you algorithm would flag that as a collision, when in reality they are simply moving VERY CLOSELY by one another. This is where the - 1 comes in.
Also, you really want to make sure to be careful with your screen geometry. What I mean by this is many screens move from top-left being pixel (0, 0) to bottom right being pixel (positive X, positive y). Your second condition:
(y >= this->GetTop() && y <= this->GetTop() + this->GetHeight())
MIGHT need to be:
( (y + h - 1) >= this->GetTop() || y <= (this->GetTop() + this->GetHeight() - 1) )
The first portion needs to change because originally you were testing for the TOP of your input object colliding with the TOP of your object to be tested against. What you really want is the BOTTOM of your input to be tested with the TOP of your object to be tested against.
One thing I have always found extremely useful when trying to perform this type of coding, is to actually draw out very simple sketches with actual pixel based example numerics so that I can visualize it prior to coding it.
I hope all of this helps :-)

Related

Concatenating string_view objects

I've been adding std::string_views to some old code for representing string like config params, as it provides a read only view, which is faster due to no need for copying.
However, one cannot concatenate two string_view together as the operator+ isn't defined. I see this question has a couple answers stating its an oversight and there is a proposal in for adding that in. However, that is for adding a string and a string_view, presumably if that gets implemented, the resulting concatenation would be a std::string
Would adding two string_view also fall in the same category? And if not, why shouldn't adding two string_view be supported?
Sample
std::string_view s1{"concate"};
std::string_view s2{"nate"};
std::string_view s3{s1 + s2};
And here's the error
error: no match for 'operator+' (operand types are 'std::string_view' {aka 'std::basic_string_view<char>'} and 'std::string_view' {aka 'std::basic_string_view<char>'})
A view is similar to a span in that it does not own the data, as the name implies it is just a view of the data. To concatenate the string views you'd first need to construct a std::string then you can concatenate.
std::string s3 = std::string(s1) + std::string(s2);
Note that s3 will be a std::string not a std::string_view since it would own this data.
A std::string_view is an alias for std::basic_string_view<char>, which is a std::basic_string_view templated on a specific type of character, i.e. char.
But what does it look like?
Beside the fairly large number of useful member functions such as find, substr, and others (maybe it's an ordinary number, if compared to other container/string-like things offered by the STL), std::basic_string_view<_CharT>, with _CharT being the generic char-like type, has just 2 data members,
// directly from my /usr/include/c++/12.2.0/string_view
size_t _M_len;
const _CharT* _M_str;
i.e. a constant pointer to _CharT to indicate where the view starts, and a size_t (an appropriate type of number) to indicate how long the view is starting from _M_str's pointee.
In other words, a string view just knows where it starts and how long it is, so it represents a sequence of char-like entities which are consecutive in memory. With just two such memebrs, you can't represent a string which is made up of non-contiguous substrings.
Yet in other words, if you want to create a std::string_view, you need to be able to tell how many chars it is long and from which position. Can you tell where s1 + s2 would have to start and how many characters it should be long? Think about it: you can't, becase s1 and s2 are not adjacent.
Maybe a diagram can help.
Assume these lines of code
std::string s1{"hello"};
std::string s2{"world"};
s1 and s2 are totally unrelated objects, as far as their memory location is concerned; here is what they looks like:
&s2[0]
|
| &s2[1]
| |
&s1[0] | | &s2[2]
| | | |
| &s1[1] | | | &s2[3]
| | | | | |
| | &s1[2] | | | | &s2[4]
| | | | | | | |
| | | &s1[3] v v v v v
| | | | +---+---+---+---+---+
| | | | &s1[4] | w | o | r | l | d |
| | | | | +---+---+---+---+---+
v v v v v
+---+---+---+---+---+
| h | e | l | l | o |
+---+---+---+---+---+
I've intentionally drawn them misaligned to mean that &s1[0], the memory location where s1 starts, and &s2[0], the memory location where s2 starts, have nothing to do with each other.
Now, imagine you create two string views like this:
std::string_view sv1{s1};
std::string_view sv2(s2.begin() + 1, s2.begin() + 4);
Here's what they will look like, in terms of the two implementation-defined members _M_str and _M_len:
&s2[0]
|
| &s2[1]
| |
&s1[0] | | &s2[2]
| | | |
| &s1[1] | | | &s2[3]
| | | | | |
| | &s1[2] | | | | &s2[4]
| | | | | | | |
| | | &s1[3] v v v v v
| | | | +---+---+---+---+---+
| | | | &s1[4] | w | o | r | l | d |
| | | | | +---+---+---+---+---+
v v v v v · ^ ·
+---+---+---+---+---+ · | ·
| h | e | l | l | o | +---+ ·
+---+---+---+---+---+ | · ·
· ^ · | · s2._M_len ·
· | · | <----------->
+---+ · |
| · · +-- s2._M_str
| · s1._M_len ·
| <------------------->
|
+-------- s1._M_str
Given the above, can you see what's wrong with expecting that
std::string_view s3{s1 + s2};
works?
How can you possible define s3._M_str and s3._M_len (based on s1._M_str, s1._M_len, s2._M_str, and s2._M_len), such that they represent a view on "helloworld"?
You can't because "hello" and "world" are located in two unrelated areas of memory.
std::string_view does not own any data, it is only a view. If you want to join two views to get a joined view, you can use boost::join() from the Boost library. But result type will be not a std::string_view.
#include <iostream>
#include <string_view>
#include <boost/range.hpp>
#include <boost/range/join.hpp>
void test()
{
std::string_view s1{"hello, "}, s2{"world"};
auto joined = boost::join(s1, s2);
// print joined string
std::copy(joined.begin(), joined.end(), std::ostream_iterator(std::cout, ""));
std::cout << std::endl;
// other method to print
for (auto c : joined) std::cout << c;
std::cout << std::endl;
}
C++23 has joined ranges in the standard library with the name of std::ranges::views::join_with_view
#include <iostream>
#include <ranges>
#include <string_view>
void test()
{
std::string_view s1{"hello, "}, s2{"world"};
auto joined = std::ranges::views::join_with_view(s1, s2);
for (auto c : joined) std::cout << c;
std::cout << std::endl;
}

How does this code work? The result doesnt make sense to me and doesnt appear in the debugger [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed last year.
Improve this question
#include <iostream>
using namespace std;
int f(int n, int m){
if (n==1)
return 0;
else
return f(n-1, m) + m;
}
int main()
{
cout << f(3874, 1000);
cout << endl;
return 0;
}
The result is 3873000. Why is n multiplied by m after deducting 1 and how does the function work in detail please?
The else block is executed at all levels of recursion, except the deepest one.
The number of levels in the recursion tree is n. So we have n-1 times that the else block is executed.
This else block first makes the recursive call, and then adds m to the result it gets back, and returns that sum to the caller, who will do the same, ...etc until the walk upwards the recursion tree is complete.
The original caller will thus see a base number (0) to which m was repeatedly added, exactly n-1 times.
So the function calculates m(n-1), provided that n is greater than 0. If not, the recursion will run into a stack overflow error.
Visualisation
To visualise this, let's split the second return statement into two parts, where first the result of the recursive call is stored in a variable, and then the sum is returned. Also, let's take a small value for n, like 3.
So this is then the code:
int f(int n, int m){
if (n==1)
return 0;
else {
int result = f(n-1, m)
return result + m;
}
}
int main()
{
cout << f(3, 10);
}
We can imagine each function execution (starting with main) as a box (a frame), in which local variables live their lives. Each recursive call creates a new box, and when return is executed that box vanishes again.
So we can imagine the above code to execute like this:
+-[main]----------------------------+
| f(3, 10) ... |
| +-[f]-------------------------+ |
| | n = 3, m = 10 | |
| | f(3-1, 10) ... | |
| | +-[f]-------------------+ | |
| | | n = 2, m = 10 | | |
| | | f(2-1, 10) ... | | |
| | | +-[f]-------------+ | | |
| | | | n = 1, m = 10 | | | |
| | | | return 0 | | | |
| | | +-----------------+ | | |
| | | result = 0 | | |
| | | return 0 + 10 | | |
| | +-----------------------+ | |
| | result = 10 | |
| | return 10 + 10 | |
| +-----------------------------+ |
| cout << 20 |
+-----------------------------------+
I hope this clarifies it.
The algorithm solves the recurrence
F(n) = F(n-1) + m
with
F(1) = 0.
(I removed m as an argument, as its value is constant).
We have
F(n) = F(n-1) + m = F(n-2) + 2m = F(n-3) + 3m = ... = F(1) + (n-1)m.
As written elsewhere, the recursion depth is n, which is dangerous.

Select right kernel size for median blur to reduce noise

I am new to image processing. We have a requirement to get circle centers with sub pixel accuracy from an image. I have used median blurring to reduce the noise. A portion of the image is shown below. The steps I followed for getting circle boundaries is given below
Reduced the noise with medianBlur
Applied OTSU thresholding with threshold API
Identified circle boundaries with findContours method.
I get different results when used different kernel size for medianBlur. I selected medianBlur to keep edges. I tried kernel size 3, 5 and 7. Now I am confused to use the right kernel size for medianBlur.
How can I decide the right kernel size?
Is there any scientific approach to decide the right kernel size for medianBlur?
I will give you two suggestions here for how to find the centroids of these disks, you can pick one depending on the level of precision you need.
First of all, using contours is not the best method. Contours depend a lot on which pixels happen to fall within the object on thresholding, noise affects these a lot.
A better method is to find the center of mass (or rather, the first order moments) of the disks. Read Wikipedia to learn more about moments in image analysis. One nice thing about moments is that we can use pixel values as weights, increasing precision.
You can compute the moments of a binary shape from its contours, but you cannot use image intensities in this case. OpenCV has a function cv::moments that computes the moments for the whole image, but I don't know of a function that can do this for each object separately. So instead I'll be using DIPlib for these computations (I'm an author).
Regarding the filtering:
Any well-behaved linear smoothing should not affect the center of mass of the objects, as long as the objects are far enough from the image edge. Being close to the edge will cause the blur to do something different on the side of the object closest to the edge compared to the other sides, introducing a bias.
Any non-linear smoothing filter has the ability to change the center of mass. Please avoid the median filter.
So, I recommend that you use a Gaussian filter, which is the most well-behaved linear smoothing filter.
Method 1: use binary shape's moments:
First I'm going to threshold without any form of blurring.
import diplib as dip
a = dip.ImageRead('/Users/cris/Downloads/Ef8ey.png')
a = a(1) # Use green channel only, simple way to convert to gray scale
_, t = dip.Threshold(a)
b = a<t
m = dip.Label(b)
msr = dip.MeasurementTool.Measure(m, None, ['Center'])
print(msr)
This outputs
| Center |
- | ----------------------- |
| dim0 | dim1 |
| (px) | (px) |
- | ---------- | ---------- |
1 | 18.68 | 9.234 |
2 | 68.00 | 14.26 |
3 | 19.49 | 48.22 |
4 | 59.68 | 52.42 |
We can now apply a smoothing to the input image a and compute again:
a = dip.Gauss(a,2)
_, t = dip.Threshold(a)
b = a<t
m = dip.Label(b)
msr = dip.MeasurementTool.Measure(m, None, ['Center'])
print(msr)
| Center |
- | ----------------------- |
| dim0 | dim1 |
| (px) | (px) |
- | ---------- | ---------- |
1 | 18.82 | 9.177 |
2 | 67.74 | 14.27 |
3 | 19.51 | 47.95 |
4 | 59.89 | 52.39 |
You can see there's some small change in the centroids.
Method 2: use gray scale moments:
Here we use the error function to apply a pseudo-threshold to the image. What this does is set object pixels to 1 and background pixels to 0, but pixels around the edges retain some intermediate value. Some people refer to this as a "fuzzy thresholding". These two images show the normal ("hard") threshold, and the error function clip ("fuzzy threshold"):
By using this fuzzy threshold, we retain more information about the exact (sub-pixel) location of the edges, which we can use when computing the first order moments.
import diplib as dip
a = dip.ImageRead('/Users/cris/Downloads/Ef8ey.png')
a = a(1) # Use green channel only, simple way to convert to gray scale
_, t = dip.Threshold(a)
c = dip.ContrastStretch(-dip.ErfClip(a, t, 30))
m = dip.Label(a<t)
m = dip.GrowRegions(m, None, -2, 2)
msr = dip.MeasurementTool.Measure(m, c, ['Gravity'])
print(msr)
This outputs
| Gravity |
- | ----------------------- |
| dim0 | dim1 |
| (px) | (px) |
- | ---------- | ---------- |
1 | 18.75 | 9.138 |
2 | 67.89 | 14.22 |
3 | 19.50 | 48.02 |
4 | 59.79 | 52.38 |
We can now apply a smoothing to the input image a and compute again:
a = dip.Gauss(a,2)
_, t = dip.Threshold(a)
c = dip.ContrastStretch(-dip.ErfClip(a, t, 30))
m = dip.Label(a<t)
m = dip.GrowRegions(m, None, -2, 2)
msr = dip.MeasurementTool.Measure(m, c, ['Gravity'])
print(msr)
| Gravity |
- | ----------------------- |
| dim0 | dim1 |
| (px) | (px) |
- | ---------- | ---------- |
1 | 18.76 | 9.094 |
2 | 67.87 | 14.19 |
3 | 19.50 | 48.00 |
4 | 59.81 | 52.39 |
You can see the differences are smaller this time, because the measurement is more precise.
In the binary case, the differences in centroids with and without smoothing are:
array([[ 0.14768417, -0.05677508],
[-0.256 , 0.01668085],
[ 0.02071882, -0.27547569],
[ 0.2137167 , -0.03472741]])
In the gray-scale case, the differences are:
array([[ 0.01277204, -0.04444567],
[-0.02842993, -0.0276569 ],
[-0.00023144, -0.01711335],
[ 0.01776011, 0.01123299]])
If the centroid measurement is given in µm rather than px, it is because your image file contains pixel size information. The measurement function will use this to give you real-world measurements (the centroid coordinate is w.r.t. the top-left pixel). If you do not desire this, you can reset the image's pixel size:
a.SetPixelSize(1)
The two methods in C++
This is a translation to C++ of the code above, including a display step to double-check that the thresholding produced the right result:
#include "diplib.h"
#include "dipviewer.h"
#include "diplib/simple_file_io.h"
#include "diplib/linear.h" // for dip::Gauss()
#include "diplib/segmentation.h" // for dip::Threshold()
#include "diplib/regions.h" // for dip::Label()
#include "diplib/measurement.h"
#include "diplib/mapping.h" // for dip::ContrastStretch() and dip::ErfClip()
int main() {
auto a = dip::ImageRead("/Users/cris/Downloads/Ef8ey.png");
a = a[1]; // Use green channel only, simple way to convert to gray scale
dip::Gauss(a, a, {2});
dip::Image b;
double t = dip::Threshold(a, b);
b = a < t; // Or: dip::Invert(b,b);
dip::viewer::Show(a);
dip::viewer::Show(b); // Verify that the segmentation is correct
dip::viewer::Spin();
auto m = dip::Label(b);
dip::MeasurementTool measurementTool;
auto msr = measurementTool.Measure(m, {}, { "Center"});
std::cout << msr << '\n';
auto c = dip::ContrastStretch(-dip::ErfClip(a, t, 30));
dip::GrowRegions(m, {}, m, -2, 2);
msr = measurementTool.Measure(m, c, {"Gravity"});
std::cout << msr << '\n';
// Iterate through the measurement structure:
auto it = msr["Gravity"].FirstObject();
do {
std::cout << "Centroid coordinates = " << it[0] << ", " << it[1] << '\n';
} while(++it);
}

Displays nothing when debugged [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to pass objects to functions in C++?
Main class
#include "List.h"
#include "Car.h"
#include "Worker.h"
#include "Queue.h"
#include <iostream>
#include <string>
using namespace std;
void initWorkerList(List<Worker>);
void initCarList(List<Car>, Queue, Queue);
int main() {
List<Worker> WorkerList;
List<Car> CarList;
Queue q1, q2;
initWorkerList(WorkerList);
initCarList(CarList, q1, q2); // Error here
//..... e.g cout << "Successful!"; but it does not displays it...
}
void initWorkerList(List<Worker> WorkerList) {
Worker w1 = Worker("Ben Ang", "Ben123", "pass123", 'M');
WorkerList.add(w1);
Worker w2 = Worker("Grace Eng", "Gr4ce", "loveGrace", 'W');
WorkerList.add(w2);
Worker w3 = Worker("Rebecca Xuan", "Xuanz", "Rebecca Xuan", 'W');
WorkerList.add(w3);
}
void initCarList(List<Car> CarList, Queue q1, Queue q2) {
Car c1 = Car("SJS1006Z","Toyota", "Saloon car");
Car c2 = Car("SFW6666E", "hyundai", "Taxi (Saloon)");
Car c3 = Car("SCF1006G","Mercedes", "Large Van");
Car c4 = Car("SBQ1006Z", "Puma", "Saloon Car");
q1.enqueue(c1);
q2.enqueue(c1);
q2.enqueue(c3);
q1.enqueue(c4);
q1.enqueue(c1);
q1.enqueue(c1);
q1.enqueue(c1);
q2.enqueue(c2);
q2.enqueue(c2);
}
There is no error at all. But nothing is displayed when being debugged...I have tried and my guess is there is something wrong with initCarList(CarList,q1,q2); cause after that code, other codes can work at all. Is there anything wrong with it? Thanks
You are passing the Queue Variables by value rather than by reference.
initCarList(CarList, q1, q2); // Error here
So any change in initCarList wont get reflected back to caller
Change your function signature to
void initCarList(List<Car> CarList, Queue& q1, Queue& q2) {
and the declaration to
void initCarList(List<Car>, Queue&, Queue&);
If you pass parameter by value, any change within initCarList is local to the function scope and does not get reflected back.
Pass by Value
Caller Callee
|------| |------|
workedList workedList
| ___ | | ___ |
|| | |--------> || | | <------
||___| | ||___| | |
| | | | |
|q1 | |q1 | (Changing any of these variables
| ___ | | ___ | won't be reflected back)
|| | |--------> || | | |
||___| | ||___| | |
| | | | |
|q2 | |q2 | |
| ___ | | ___ | |
|| | |--------> || | | |
||___| | ||___| | <------
|______| |______|
Pass by reference
Caller Callee
-------- --------
|wList | |wList |
| ___ | | ____ |
|| | |--------> || || <------
||___|<|------------||-*__|| |
| _ | | | |
|q1 | |q1 | (Changing any of these variables
| ___ | | ____ | will be reflected back)
|| | |--------> || || |
||___|<|------------||-*__|| |
| | | | |
|q2 | |q2 | |
| ___ | | ____ | |
|| | |--------> || || |
||___|<|------------||-*__|| <------
|______| |______|
You're passing your variables in by value, which means that the function's parameters hold a copy of them, which you modify and discard when the function ends. Pass by reference instead to modify the original variable. For example, initCarList would become:
void initCarList(List<Car> CarList, Queue &q1, Queue &q2)
You also don't use the CarList parameter, so you might as well take it out if this is how it is in your code.
Your functions pass by value which means functions make a copy of passed in variables and manipulate on the copied ones. To modify on original ones you need to pass parameter by reference
Change:
void initWorkerList(List<Worker>);
void initCarList(List<Car>, Queue, Queue);
To:
void initWorkerList(List<Worker> &);
void initCarList(List<Car>&, Queue&, Queue&);

How to flip the texture on a CC3PlaneNode?

I'm creating a CC3PlaneNode (cocos3d) with code that looks something like this:
CC3PlaneNode *bnode = [CC3PlaneNode nodeWithName: name];
CC3Texture *texture = [CC3Texture textureFromFile: texName];
[bnode populateAsCenteredRectangleWithSize: sz
andTessellation: ccg(1, 1)
withTexture: texture
invertTexture: YES];
bnode.material.specularColor = kCCC4FLightGray;
bnode.material.emissionColor = kCCC4FWhite;
bnode.material.isOpaque = NO;
bnode.shouldCullBackFaces = NO;
bnode.isTouchEnabled = YES;
bnode.location = loc;
[bnode retainVertexLocations];
Under certain circumstances, I'd like the plane to display the mirror image of its texture (a la the Flip Horizontal feature on many image programs.) I don't just want to flip the plane, itself, because that would throw out-of-position all of it's child-nodes, which is undesirable behaviour.
That is, if X & Y are parts of my texture, and "c" and "c'" are child-nodes in this diagram:
+--------+
| |
| x y |
| |
+--------+
c c'
after the flip, I want things to look like this:
+--------+
| |
| y x |
| |
+--------+
c c'
Other than not wanting to rotate/spin/flip the plane, itself, I'm otherwise pretty flexible on a solution.
Any hints?
Thanks!