I am creating an utility to display graphics in the console. I currently have a method that takes 2 coordinates and draws a line between those two points. Is it possible to create a method based on this that will take 3 coordinates and draw a filled triangle?
I was thinking of drawing 2 lines, and then drawing lines from each point of one line to each point of the other. However, I think that the time complexity of this method will be really bad.
Here is a method that I used to draw a line:
void drawLine(Line line)
{
const bool steep = (fabs(line.end.y - line.begin.y) > fabs(line.end.x - line.begin.x));
if (steep)
{
std::swap(line.begin.x, line.begin.y);
std::swap(line.end.x, line.end.y);
}
if (line.begin.x > line.end.x)
{
std::swap(line.begin, line.end);
}
const double dx = line.end.x - line.begin.x;
const double dy = fabs(line.end.y - line.begin.y);
const double zStepLength = fabs(fabs(line.end.z) - fabs(line.begin.z)) / dx;
double error = dx / 2.0;
const int ystep = (line.begin.y < line.end.y) ? 1 : -1;
const double zstep = (line.begin.z < line.end.z) ? zStepLength : -zStepLength;
for (double x = line.begin.x, z = line.begin.z; x <= line.end.x; x++, z += zstep)
{
if (steep)
{
setPixel({ line.begin.y, x, z }, line.color);
}
else
{
setPixel({ x, line.begin.y, z }, line.color);
}
error -= dy;
if (error < 0)
{
line.begin.y += ystep;
error += dx;
}
}
}
Related
I can use the function CheckCollisionCircleRec(Vector2{ x, y }, radius, paddleRect) to find out simply if my circle has collided with my rectangle, but I want to be able to find out what side of the rectangle my circle has collided with. How would I go about doing this? None of the algorithms I've made are working. Example of my most recent blunder:
if (x - radius <= 0 || x + radius >= screenWidth) {
speedX *= -1;
}
else if (y - radius <= 0 || y + radius >= screenHeight) {
speedY *= -1;
}
else if (CheckCollisionCircleRec(Vector2{ x, y }, radius, paddleRect)) {
float paddleBottom = paddleRect.y + paddleRect.height;
float paddleRight = paddleRect.x + paddleRect.width;
if (range(paddleRect.x, paddleRect.x + speedX / 100, x + radius)) {
x = paddleRect.x - radius;
speedX *= -1;
}
if (range(paddleRight - speedX / 100, paddleRight, x - radius)) {
x = paddleRight + radius;
speedX *= -1;
};
if (range(paddleRect.y, paddleRect.y + speedY / 100, y + radius)) {
y = paddleRect.y - radius;
speedY *= -1;
}
if (range(paddleBottom - speedY / 100, paddleBottom, y - radius)) {
y = paddleBottom + radius;
speedY *= -1;
};
EDIT:
Here's the function I used to get the working end result:
// px and py are the ball's previous locations
// x and y are the ball's current locations
void checkCollision(Rectangle rectangle) {
int left = rectangle.x;
int right = rectangle.x + rectangle.width;
int top = rectangle.y;
int bottom = rectangle.y + rectangle.height;
if (CheckCollisionCircleRec(Vector2{ x, y }, radius, rectangle)) {
if (px < left) {
speedX = negative(speedX);
x = left - radius;
}
else if (px > right) {
speedX = positive(speedX);
x = right + radius;
}
else if (py < top) {
speedY = negative(speedY);
y = top - radius;
}
else if (py > bottom) {
speedY = positive(speedY);
y = bottom + radius;
};
};
};
A simply way is to use the PREVIOUS location of your circle. Not sure if you can in your program, but since you have an x and y handy, I'll assume you can have a prevX and prevY. I'll also assume these values represent the CENTER of the circle.
Now if (prevX < paddleRect.x), then you likely collided with the left side (not guaranteed, but resolving ambiguities with complete accuracy requires recursively simulating your physics at smaller and smaller timesteps, which is likely unnecessary here). You can also constrain this more tightly with something like if (prevX < paddleRect.x && prevY > paddleRect.y && prevY < paddleRect.y + paddRect.height). There are various constraints you can add depending on how cleanly you want the side to be hit before detecting it. You can add corner hits, etc.
The reason for using the previous location is that, if your circle is moving fast enough, then in a single frame it can jump straight into the middle of the rectangle. It's usually necessary to use the previous position to give more specific collision information in the current-location collision
I have programmed a simple dragon curve fractal. It seems to work for the most part, but there is an odd logical error that shifts the rotation of certain lines by one pixel. This wouldn't normally be an issue, but after a few generations, at the right size, the fractal begins to look wonky.
I am using open cv in c++ to generate it, but I'm pretty sure it's a logical error rather than a display error. I have printed the values to the console multiple times and seen for myself that there is a one-digit difference between values that are intended to be the exact same - meaning a line may have a y of 200 at one end and 201 at another.
Here is the full code:
#include<iostream>
#include<cmath>
#include<opencv2/opencv.hpp>
const int width=500;
const int height=500;
const double PI=std::atan(1)*4.0;
struct point{
double x;
double y;
point(double x_,double y_){
x=x_;
y=y_;
}};
cv::Mat img(width,height,CV_8UC3,cv::Scalar(255,255,255));
double deg_to_rad(double degrees){return degrees*PI/180;}
point rotate(int degree, int centx, int centy, int ll) {
double radians = deg_to_rad(degree);
return point(centx + (ll * std::cos(radians)), centy + (ll * std::sin(radians)));
}
void generate(point & r, std::vector < point > & verticies, int rotation = 90) {
int curRotation = 90;
bool start = true;
point center = r;
point rot(0, 0);
std::vector<point> verticiesc(verticies);
for (point i: verticiesc) {
double dx = center.x - i.x;
double dy = center.y - i.y;
//distance from centre
int ll = std::sqrt(dx * dx + dy * dy);
//angle from centre
curRotation = std::atan2(dy, dx) * 180 / PI;
//add 90 degrees of rotation
rot = rotate(curRotation + rotation, center.x, center.y, ll);
verticies.push_back(rot);
//endpoint, where the next centre will be
if (start) {
r = rot;
start = false;
}
}
}
void gen(int gens, int bwidth = 1) {
int ll = 7;
std::vector < point > verticies = {
point(width / 2, height / 2 - ll),
point(width / 2, height / 2)
};
point rot(width / 2, height / 2);
for (int i = 0; i < gens; i++) {
generate(rot, verticies);
}
//draw lines
for (int i = 0; i < verticies.size(); i += 2) {
cv::line(img, cv::Point(verticies[i].x, verticies[i].y), cv::Point(verticies[i + 1].x, verticies[i + 1].y), cv::Scalar(0, 0, 0), 1, 8);
}
}
int main() {
gen(10);
cv::imshow("", img);
cv::waitKey(0);
return 0;
}
First, you use int to store point coordinates - that's a bad idea - you lose all accuracy of point position. Use double or float.
Second, your method for drawing fractals is not too stable numericly. You'd better store original shape and all rotation/translation/scale that indicate where and how to draw scaled copies of the original shape.
Also, I believe this is a bug:
for(point i: verices)
{
...
vertices.push_back(rot);
...
}
Changing size of vertices while inside such a for-loop might cause a crash or UB.
Turns out it was to do with floating-point precision. I changed
x=x_;
y=y_;
to
x=std::round(x_);
y=std::round(y_);
and it works.
My task is to detect an orange ball in video. I detected by thresholding image on HSV colorspace and bounding box. Then I have center and radius of ball, with unit is pixel.
When ball is static, I expect center and radius will be static too, but reality, it has noise. I use Kalman Filter to filter noise and it works well. But it delay in real-time. I try to optimize covariance parameters but not work.
So could anyone help me static center and radius when ball is static and without delay?
Are you sure it is the Kalman Filter witch is causing the delay.
Otherwise you can try this lazy filter witch only is noise rejecting but blazingly fast. My suspicion however it is the HSV conversion.
class noiseFilter
{
private:
cv::Point2f ptLast;
float ptMaxTol;
public:
noiseFilter(float maxTol = 1.5f)
{
ptMaxTol = maxTol * maxTol; // we do the pow(2) here so we don't have to do a square root on every update
ptLast = cv::Point2f(0.0f, 0.0f);
}
cv::Point2f update(cv::Point2f &ptNew) // update filter with new found point
{
float dist = pDistance2(ptLast, ptNew);
if (dist > ptMaxTol) ptLast = ptNew; // update only if distance is more than threshold
return ptLast;
}
cv::Point2f getResult() // get result of filter
{
return ptLast;
}
private:
// calculate distance between 2 point without doing a sqrt
float pDistance2(cv::Point2f &p1, cv::Point2f &p2)
{
float dx = p1.x - p2.x;
float dy = p1.y - p2.y;
return (dx * dx + dy * dy);
}
};
int main()
{
cv::Point2f pt;
noiseFilter filter(2.1f); // initialize filter wit max 2.1 pixels noise rejection.
int x = 100, y = 120;
for (int i = 0; i < 100; i++)
{
// generate some noise with 2 pixels variation
pt.x = ((rand() % 200) - 100) * 0.01f + x;
pt.y = ((rand() % 200) - 100) * 0.01f + y;
cv::Point2f pts = filter.update(pt);
printf("input x=%6.2f y=%6.2f output x=%6.2f y=%6.2f\r\n", pt.x, pt.y, pts.x, pts.y);
// do som random big update on random intervals
if ((rand() % 50) == 1) {
x += 15;
printf("big update on X\r\n");
}
if ((rand() % 50) == 1){
y += 25;
printf("big update on Y\r\n");
}
}
return 0;
}
Below a noise filter with smoothing.
Works on slow and fast moving objects.
class noiseFilterSmooth
{
private:
static const int maxHist = 10;
cv::Point2f ptLast;
float ptMaxTol;
cv::Point2f hist[maxHist];
int histHead,histSize;
public:
noiseFilterSmooth(float maxTol = 1.5f)
{
histHead = histSize = 0;
ptMaxTol = maxTol * maxTol; // we do the pow(2) here so we don't have to do a square root on every update
ptLast = cv::Point2f(0.0f, 0.0f);
}
cv::Point2f& update(cv::Point2f &ptNew) // update filter with new found point
{
float dist = pDistance2(ptLast, ptNew);
if (dist > ptMaxTol) histSize = histHead = 0; // reset smoothing filter if distance is more than threshold
// update smoothing filter with last result
hist[histHead] = ptNew; // update smoothing filter with last
histHead = (histHead + 1) % maxHist;
if (histSize < maxHist) histSize++;
return getResult();
}
cv::Point2f& getResult() // get result of filter
{
float sumx = 0, sumy = 0;
for (int i = 0; i < histSize; i++)
{
sumx += hist[i].x;
sumy += hist[i].y;
}
ptLast.x = sumx / histSize;
ptLast.y = sumy / histSize;
return ptLast;
}
private:
// calculate distance between 2 point without doing a sqrt
float pDistance2(cv::Point2f &p1, cv::Point2f &p2)
{
float dx = p1.x - p2.x;
float dy = p1.y - p2.y;
return (dx * dx + dy * dy);
}
};
Issue
I'm trying to implement the Perlin Noise algorithm in 2D with a single octave with a size of 16x16. I'm using this as heightmap data for a terrain, however it only seems to work in one axis. Whenever the sample point moves to a new Y section in the Perlin Noise grid, the gradient is very different from what I expect (for example, it often flips from 0.98 to -0.97, which is a very sudden change).
This image shows the staggered terrain in the z direction (which is the y axis in the 2D Perlin Noise grid)
Code
I've put the code that calculates which sample point to use at the end since it's quite long and I believe it's not where the issue is, but essentially I scale down the terrain to match the Perlin Noise grid (16x16) and then sample through all the points.
Gradient At Point
So the code that calculates out the gradient at a sample point is the following:
// Find the gradient at a certain sample point
float PerlinNoise::gradientAt(Vector2 point)
{
// Decimal part of float
float relativeX = point.x - (int)point.x;
float relativeY = point.y - (int)point.y;
Vector2 relativePoint = Vector2(relativeX, relativeY);
vector<float> weights(4);
// Find the weights of the 4 surrounding points
weights = surroundingWeights(point);
float fadeX = fadeFunction(relativePoint.x);
float fadeY = fadeFunction(relativePoint.y);
float lerpA = MathUtils::lerp(weights[0], weights[1], fadeX);
float lerpB = MathUtils::lerp(weights[2], weights[3], fadeX);
float lerpC = MathUtils::lerp(lerpA, lerpB, fadeY);
return lerpC;
}
Surrounding Weights of Point
I believe the issue is somewhere here, in the function that calculates the weights for the 4 surrounding points of a sample point, but I can't seem to figure out what is wrong since all the values seem sensible in the function when stepping through it.
// Find the surrounding weight of a point
vector<float> PerlinNoise::surroundingWeights(Vector2 point){
// Produces correct values
vector<Vector2> surroundingPoints = surroundingPointsOf(point);
vector<float> weights;
for (unsigned i = 0; i < surroundingPoints.size(); ++i) {
// The corner to the sample point
Vector2 cornerToPoint = surroundingPoints[i].toVector(point);
// Getting the seeded vector from the grid
float x = surroundingPoints[i].x;
float y = surroundingPoints[i].y;
Vector2 seededVector = baseGrid[x][y];
// Dot product between the seededVector and corner to the sample point vector
float dotProduct = cornerToPoint.dot(seededVector);
weights.push_back(dotProduct);
}
return weights;
}
OpenGL Setup and Sample Point
Setting up the heightmap and getting the sample point. Variables 'wrongA' and 'wrongA' is an example of when the gradient flips and changes suddenly.
void HeightMap::GenerateRandomTerrain() {
int perlinGridSize = 16;
PerlinNoise perlin_noise = PerlinNoise(perlinGridSize, perlinGridSize);
numVertices = RAW_WIDTH * RAW_HEIGHT;
numIndices = (RAW_WIDTH - 1) * (RAW_HEIGHT - 1) * 6;
vertices = new Vector3[numVertices];
textureCoords = new Vector2[numVertices];
indices = new GLuint[numIndices];
float perlinScale = RAW_HEIGHT/ (float) (perlinGridSize -1);
float height = 50;
float wrongA = perlin_noise.gradientAt(Vector2(0, 68.0f / perlinScale));
float wrongB = perlin_noise.gradientAt(Vector2(0, 69.0f / perlinScale));
for (int x = 0; x < RAW_WIDTH; ++x) {
for (int z = 0; z < RAW_HEIGHT; ++z) {
int offset = (x* RAW_WIDTH) + z;
float xVal = (float)x / perlinScale;
float yVal = (float)z / perlinScale;
float noise = perlin_noise.gradientAt(Vector2( xVal , yVal));
vertices[offset] = Vector3(x * HEIGHTMAP_X, noise * height, z * HEIGHTMAP_Z);
textureCoords[offset] = Vector2(x * HEIGHTMAP_TEX_X, z * HEIGHTMAP_TEX_Z);
}
}
numIndices = 0;
for (int x = 0; x < RAW_WIDTH - 1; ++x) {
for (int z = 0; z < RAW_HEIGHT - 1; ++z) {
int a = (x * (RAW_WIDTH)) + z;
int b = ((x + 1)* (RAW_WIDTH)) + z;
int c = ((x + 1)* (RAW_WIDTH)) + (z + 1);
int d = (x * (RAW_WIDTH)) + (z + 1);
indices[numIndices++] = c;
indices[numIndices++] = b;
indices[numIndices++] = a;
indices[numIndices++] = a;
indices[numIndices++] = d;
indices[numIndices++] = c;
}
}
BufferData();
}
Turned out the issue was in the interpolation stage:
float lerpA = MathUtils::lerp(weights[0], weights[1], fadeX);
float lerpB = MathUtils::lerp(weights[2], weights[3], fadeX);
float lerpC = MathUtils::lerp(lerpA, lerpB, fadeY);
I had the interpolation in the y axis the wrong way around, so it should have been:
lerp(lerpB, lerpA, fadeY)
Instead of:
lerp(lerpA, lerpB, fadeY)
I am trying to build a game in Opengl. Before I start making better movement mechanics I want to get collision working. I have cube-cube collision working and I have sphere-sphere collision working, but can't figure out cube-sphere collision. Since I want it in 3d I have the pivot at the center of the objects. Anyone have any suggestions?
EDIT: This is the code I currently have:
bool SphereRectCollision( Sphere& sphere, Rectangle& rect)
{
//Closest point on collision box
float cX, cY;
//Find closest x offset
if( sphere.getCenterX() < rect.GetCenterX())//checks if the center of the circle is to the left of the rectangle
cX = rect.GetCenterX();
else if( sphere.getCenterX() > rect.GetCenterX() + rect.GetWidth()) //checks if the center of the circle is to the right of the rectangle
cX = rect.GetCenterX() + rect.GetWidth();
else //the circle is inside the rectagle
cX = sphere.getCenterX();
//Find closest y offset
if( sphere.getCenterY() > rect.GetCenterY() + rect.GetHeight() )
cY = rect.GetCenterY();
else if( sphere.getCenterY() < rect.GetCenterY() - rect.GetHeight() )
cY = rect.GetCenterY() + rect.GetHeight();
else
cY = sphere.getCenterY();
//If the closest point is inside the circle
if( distanceSquared( sphere.getCenterX(), sphere.getCenterY(), cX, cY ) < sphere.getRadius() * sphere.getRadius() )
{
//This box and the circle have collided
return false;
}
//If the shapes have not collided
return true;
}
float distanceSquared( float x1, float y1, float x2, float y2 )
{
float deltaX = x2 - x1;
float deltaY = y2 - y1;
return deltaX*deltaX + deltaY*deltaY;
}
I found the solution. I had the right idea, but didn't quite know how to execute it:
bool SphereRectCollision( Sphere& sphere, Rectangle& rect)
{
float sphereXDistance = abs(sphere.X - rect.X);
float sphereYDistance = abs(sphere.Y - rect.Y);
float sphereZDistance = abs(sphere.Z - rect.Z);
if (sphereXDistance >= (rect.Width + sphere.Radius)) { return false; }
if (sphereYDistance >= (rect.Height + sphere.Radius)) { return false; }
if (sphereZDistance >= (rect.Depth + sphere.Radius)) { return false; }
if (sphereXDistance < (rect.Width)) { return true; }
if (sphereYDistance < (rect.Height)) { return true; }
if (sphereZDistance < (rect.GetDepth)) { return true; }
float cornerDistance_sq = ((sphereXDistance - rect.Width) * (sphereXDistance - rect.Width)) +
((sphereYDistance - rect.Height) * (sphereYDistance - rect.Height) +
((sphereYDistance - rect.Depth) * (sphereYDistance - rect.Depth)));
return (cornerDistance_sq < (sphere.Radius * sphere.Radius));
}
This algorithm doesn't work when a hit happen on an edge, the 2nd set of if conditions triggers but a collision isn't occuring