Related
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.
I am a beginner in c++ and have coded a for loop to show a hollow circle when I run the code, however, I was wondering how I could achieve a filled-in circle using the distance formula (d = sqrt((ax-bx)^2 + (ay-by)^2). Here's what I have so far! Any help would be appreciated!
int MAX = 728;
for (float t = 0; t < 2 * 3.14; t += 0.01)
SetPixel(MAX / 4 + MAX / 6 * sin(t), MAX / 4 + MAX / 6 * cos(t), 255, 255, 0);
#include <windows.h>
#include <iostream>
using namespace std;
int main()
{
HWND consoleWindow = GetConsoleWindow(); // Get a console handle
HDC consoleDC = GetDC(consoleWindow); // Get a handle to device context
int max = 628;
float i = 0;
float t;
float doublePi = 6.29;
for (i = 0.0; i < max; i += 2.0) {
for (t = 0.0; t < doublePi; t += 0.01) {
SetPixel(consoleDC, max / 4 + (max - i) / 6 * sin(t), max / 4 + (max - i) / 6 * cos(t), RGB(255, 255, 0));
}
}
ReleaseDC(consoleWindow, consoleDC);
cin.ignore();
return 0;
}
Working almost well. Draw and fill in! A little slow...
Pffff... do not use sin and cos! instead use the sqrt(1-x^2) approach. You can view the formula rendering a circle in google for example: https://www.google.com/search?q=sqrt(1-x^2)
I edit this answer because it seems that is not clear:
float radius = 50.0f;
for (int x = -radius; x <= radius; ++x) {
int d = round(sqrt(1.0f - (x * x / radius / radius)) * radius);
for (int y = -d; y <= d; ++y) {
SetPixel(x, y, 255, 255, 0);
}
}
Note: each graphic library is different, so I assumed that you used rightfully the "SetPixel" function.
Now, for most people say the sqrt(1-x^2) approach should be enough, but it seem that some downvoters does not think the same XD.
Inefficient as can be, and probably the last way you really want to draw a circle ... but ...
Over the entire square encompassing your circle, calculate each pixel's distance from the center and set if under or equal the radius.
// Draw a circle centered at (Xcenter,Ycenter) with given radius using distance formula
void drawCircle(HDC dc, int XCenter, int YCenter, int radius, COLORREF c) {
double fRad = radius * 1.0; // Just a shortcut to avoid thrashing data types
for (int x = XCenter - radius; x<XCenter + radius; x++) {
for (int y = YCenter - radius; y<YCenter + radius; y++) {
double d = sqrt(((x - XCenter) * (x - XCenter)) + ((y - YCenter) * (y - YCenter)) );
if (d <= fRad) SetPixel(dc, x, y, c);
}
}
}
Caveat: No more caveat, used a C++ environment and tested it this time. :-)
Call thusly:
int main()
{
HWND consoleWindow = GetConsoleWindow();
HDC consoleDC = GetDC(consoleWindow);
drawCircle(consoleDC, 50, 50, 20, RGB(255, 0, 255));
ReleaseDC(consoleWindow, consoleDC);
return 0;
}
Currently, I'm trying to draw a symmetric binary tree through IFS (Iterated Function Systems):
but the result is always only the branch tips:
.
I can't figure out what I'm doing wrong or what I'm missing.
This is the IFS:
This is my code:
RenderWindow window(VideoMode(480, 640), "fractals everywhere");
CircleShape point(1);
int chance;
float x, y, w, h, nx, ny, px, py;
void SymmetricBinaryTrees()
{
float r = 0.57f;
float o = 0.785f;
chance = rand() % 3;
switch (chance)
{
case 0:
nx = r * cos(o) * x + (-1 * r * sin(o) * y);
ny = r * sin(o) * x + r * cos(o) * y + 1;
break;
case 1:
nx = r * cos(o) * x + r * sin(o) * y;
ny = -1 * r * sin(o) * x + r * cos(o) * y + 1;
break;
case 2:
nx = x;
ny = y;
break;
}
}
void nextPoint()
{
SymmetricBinaryTrees();
x = nx; y = ny;
}
void drawPoint()
{
px = _map(x, -1.078, 1.078f, 0, w); py = _map(y, 0.f, 2.078f, h, 0); // maps the position accordingly
point.setPosition(px, py);
window.draw(point);
}
int main()
{
srand(time(NULL));
w = window.getSize().x * 1.f;
h = window.getSize().y * 1.f;
x = 0.f; y = 0.f;
window.setFramerateLimit(60);
while (window.isOpen())
{
Event e;
while (window.pollEvent(e))
if (e.type == Event::Closed) window.close();
for (int i = 1; i <= 500; i++)
{
drawPoint();
nextPoint();
}
window.display();
}
return 0;
}
This is the website that I'm using for my code.
If anyone could help me or has any idea I'd be very grateful, thank you.
I share #beyond opinion, I think you're complicating things too much. It will be easier with a different approach. Let's make things easier.
With a recursive function, we can easily understand what should be done each step.
Consider we start from a initial point, then trace a line on an angle of a given lenght, so we need a function like:
void createTreeRecursive(sf::VertexArray &tree, sf::Vector2f point, float angle, float lenght)
Where tree will be our line set, which compose the tree itself.
First thing we can do, is to set the first point, which is already known:
// Add first point
tree.append(sf::Vertex(point, treeColor));
Now we need to calculate our next point, to form a line. With simple trigonometric functions we can determine that point:
float newX = point.x + (cos((2.f * PI / 360.f) * angle) * lenght);
float newY = point.y - (sin((2.f * PI / 360.f) * angle) * lenght); // Caution here! Minus(-) sign because we're drawing upwards
So we add this second point, and then split the tree into 2 new branches, each one rotated some certain degrees:
// Add second point
tree.append(sf::Vertex(nextPoint, treeColor));
// Create sub-tree from 2nd point, rotating +45 degrees (i.e. counterclockwise), reducing lenght of the new branch by 0.6 factor
createTreeRecursive(tree, nextPoint, angle + O, lenght * R);
// Same with the other sub-tree, but rotating -45 (i.e. clockwise)
createTreeRecursive(tree, nextPoint, angle - O, lenght * R);
We need a base case for our recursive function, in this case, I choose 3 as minimum lenght:
if (lenght < 3)
// End condition, can be modified
return;
this must be out first check.
So we're done, we only need the initial call:
sf::VertexArray createTree(){
// Our tree will be made out of lines
sf::VertexArray ret(sf::PrimitiveType::Lines);
// Initial point at botton-center(250, 450), with a 90 degrees rotation, first branch lenght 200
createTreeRecursive(ret, sf::Vector2f(250, 450), 90, 200);
return ret;
}
And the result is:
Full code
#include <SFML/Graphics.hpp>
const double PI = 3.141592;
const double R = 0.57; // Reduction factor
const double O = 45; // Degree rotation each time
sf::Color treeColor = sf::Color::Blue;
void createTreeRecursive(sf::VertexArray &tree, sf::Vector2f point, float angle, float lenght){
if (lenght < 3)
// End condition, can be modified
return;
// Add first point
tree.append(sf::Vertex(point, treeColor));
float newX = point.x + (cos((2.f * PI / 360.f) * angle) * lenght);
float newY = point.y - (sin((2.f * PI / 360.f) * angle) * lenght); // Caution here! Minus(-) sign because we're drawing upwards
sf::Vector2f nextPoint(newX, newY);
// Add second point
tree.append(sf::Vertex(nextPoint, treeColor));
// Create sub-tree from 2nd point, rotating +45 degrees (i.e. counterclockwise), reducing lenght of the new branch by 0.6 factor
createTreeRecursive(tree, nextPoint, angle + O, lenght * R);
// Same with the other sub-tree, but rotating -45 (i.e. clockwise)
createTreeRecursive(tree, nextPoint, angle - O, lenght * R);
}
sf::VertexArray createTree(){
// Our tree will be made out of lines
sf::VertexArray ret(sf::PrimitiveType::Lines);
// Initial point at bottom-center(250, 450), with a 90 degrees rotation, first branch lenght 200
createTreeRecursive(ret, sf::Vector2f(250, 450), 90, 200);
return ret;
}
int main()
{
RenderWindow window({ 500, 500 }, "SFML Tree", Style::Close);
auto tree = createTree();
while (window.isOpen())
{
for (Event event; window.pollEvent(event);){
if (event.type == Event::Closed)
window.close();
}
window.clear();
window.draw(tree);
window.display();
}
return EXIT_SUCCESS;
}
I would advice you to use recursion with a function that 1) draws the current branch (as a line), and then 2) creates two new branches from the current branch. Using global variables doesn't help you either. Looks like you should rethink your approach.
For Linux is:
#include <SFML/Graphics.hpp>
#include <cmath>
const double PI = 3.141592;
const double R = 0.57;
const double O = 45;
sf::Color treeColor = sf::Color::Blue;
void createTreeRecursive(sf::VertexArray &tree, sf::Vector2f point, float angle, float lenght){
if (lenght < 3)
return;
tree.append(sf::Vertex(point, treeColor));
float newX = point.x + (cos((2.f * PI / 360.f) * angle) * lenght);
float newY = point.y - (sin((2.f * PI / 360.f) * angle) * lenght);
sf::Vector2f nextPoint(newX, newY);
tree.append(sf::Vertex(nextPoint, treeColor));
createTreeRecursive(tree, nextPoint, angle + O, lenght * R);
createTreeRecursive(tree, nextPoint, angle - O, lenght * R);
}
sf::VertexArray createTree(){
sf::VertexArray ret(sf::PrimitiveType::Lines);
createTreeRecursive(ret, sf::Vector2f(250, 450), 90, 200);
return ret;
}
int main()
{
sf::RenderWindow window({ 500, 500 }, "SFML Tree", sf::Style::Close);
auto tree = createTree();
while (window.isOpen())
{
for (sf::Event event; window.pollEvent(event);){
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
window.draw(tree);
window.display();
}
return EXIT_SUCCESS;
}
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);
}
};
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I'm trying to implement a collision detection system, and it is working for the most part, no overlapping (or at most very little overlapping) of characters, and wall collisions. The problem is that i have a bunch of characters following a player and just run into it, and when there are about 15-20 of those characters all pushing at the player, it can lead to the player or other objects being pushed through walls.
My code works as follows, first I update all of the characters, and they check collisions against each other, then I check for any character collisions with the walls. I feel like the problem is that the eventual push of all the characters leads to pushing one or more of the characters large distances, but i'm not sure how to fix the problem. Code below if necessary, a thorough explanation of how to fix this is also sufficient.
Character update/collisions:
void CharacterManager::updateAll(float elapsedTime)
{
for(std::vector<std::shared_ptr<Character>>::iterator i = _characters.begin(); i != _characters.end(); i++) {
(*i)->update(elapsedTime);
}
collisions();
}
void CharacterManager::collisions()
{
for(std::vector<std::shared_ptr<Character>>::iterator i = _characters.begin(); i != _characters.end(); i++) {
for(std::vector<std::shared_ptr<Character>>::iterator j = _characters.begin(); j != _characters.end(); j++) {
if(i == j) continue;
float xi = (*i)->position().x;
float yi = (*i)->position().y;
float xj = (*j)->position().x;
float yj = (*j)->position().y;
float dx = xi - xj;
float dy = yi - yj;
float distSquared = dx * dx + dy * dy;
float ri = (*i)->xRect().width/2;
float rj = (*j)->xRect().width/2;
if(distSquared < (ri + rj) * (ri + rj)) {
// fix collisions
float angle = atan2f(dy,dx);
float overlap = (ri + rj) - sqrt(distSquared);
if(xi < xj) {
if(yi < yj) {
(*i)->position(xi - cosf(angle) * overlap/2, yi - sinf(angle) * overlap/2);
(*j)->position(xj + cosf(angle) * overlap/2, yj + sinf(angle) * overlap/2);
} else {
(*i)->position(xi - cosf(angle) * overlap/2, yi + sinf(angle) * overlap/2);
(*j)->position(xj + cosf(angle) * overlap/2, yj - sinf(angle) * overlap/2);
}
} else {
if(yi < yj) {
(*i)->position(xi + cosf(angle) * overlap/2, yi - sinf(angle) * overlap/2);
(*j)->position(xj - cosf(angle) * overlap/2, yj + sinf(angle) * overlap/2);
} else {
(*i)->position(xi + cosf(angle) * overlap/2, yi + sinf(angle) * overlap/2);
(*j)->position(xj - cosf(angle) * overlap/2, yj - sinf(angle) * overlap/2);
}
}
// calc new velocities
float vxi = (*i)->velocity().x;
float vyi = (*i)->velocity().y;
float vxj = (*j)->velocity().x;
float vyj = (*j)->velocity().y;
float vx = vxj - vxi;
float vy = vyj - vyi;
float dotProduct = dx * vx + dy * vy;
if(dotProduct >= 0) {
float collisionScale = dotProduct / distSquared;
float xCollision = dx * collisionScale;
float yCollision = dy * collisionScale;
float combinedMass = (*i)->weight() + (*j)->weight();
float collisionWeightA = 2 * (*j)->weight() / combinedMass;
float collisionWeightB = 2 * (*i)->weight() / combinedMass;
(*i)->velocity(vxi + collisionWeightA * xCollision, vyi + collisionWeightA * yCollision);
(*j)->velocity(vxj - collisionWeightB * xCollision, vyj - collisionWeightB * yCollision);
}
}
}
}
}
Wall collisions:
void Stage::characterCrossCollisions(std::shared_ptr<Character> character)
{
for(std::vector<std::shared_ptr<Tile>>::iterator tile = tiles.begin(); tile != tiles.end(); tile++) {
if(!(*tile)->walkable) {
sf::Rect<float> cxr = character->xRect();
sf::Rect<float> cyr = character->yRect();
sf::Rect<float> tr = (*tile)->getRect();
if(!(cxr.left > tr.left + tr.width ||
cxr.left + cxr.width < tr.left ||
cxr.top > tr.top + tr.height ||
cxr.top + cxr.height < tr.top)) {
float ox = 0;
if(character->position().x > (*tile)->position().x) {
ox = cxr.left - (tr.left + tr.width);
}
else {
ox = cxr.left + cxr.width - tr.left;
}
character->position(character->position().x - ox, character->position().y);
}
if(!(cyr.left > tr.left + tr.width ||
cyr.left + cyr.width < tr.left ||
cyr.top > tr.top + tr.height ||
cyr.top + cyr.height < tr.top)) {
float oy = 0;
if(character->position().y > (*tile)->position().y) {
oy = cyr.top - (tr.top + tr.height);
}
else {
oy = cyr.top + cyr.height - tr.top;
}
character->position(character->position().x, character->position().y - oy);
}
}
}
}
Generally you run the collision code for two objects when the two objects intersect each other. Two objects intersect each other if they share at least one point in space. But the problem with this is that if objects are intersecting that means that there was a collision in the past and not that there is a collision right now.
Ideal collision code should calculate the energy transfer and modify the velocity of the objects at the exact moment when the objects touch each other. Good collision code would roll back time and try to find out the moment when the collision happened, calculate the new velocities based on that moment and roll the time forward. However these are rather hard to do and might be overkill for a simple computer game.
The easy but robust solution that I can recommend to you is:
move the objects forward
check for collision, if no collision repeat from beginning
move the objects away from each other until they don't collide proportional to their mass. Since walls don't move you can consider that they have infinite mass and only move the characters
recalculate the velocity of the colliding objects after the objects don't intersect anymore
repeat
You can also use a constraint like 'objects can never intersect the wall' and you apply this constraint by checking if a new position is valid when moving the characters. And you only move the character if the new position is valid.
This small example should exemplify validation. Make the position only updatable with the MoveTo() method and inside the MoveTo() method you can validate the new position and return whether the move was successful. If the move wasn't successful, the caller will probably want to take a different action. (move the object less until exactly the contact position and this would be the perfect opportunity to process the collision)
class Character{
bool MoveTo(float x, float y)
{
if (this.isValidPosition(x,y))
{
this.x = x;
this.y = y;
return true;
}
return false;
}
void Update(float deltaTime)
{
float new_x = x + velocity_x*deltaTime;
float new_y = y + velocity_y*deltaTime;
if (!this.MoveTo(new_x, new_y))
{
Console.Write("cannot move " + this + " to the new position, something is already there\n");
}
}
}