Why are points being generated outside of a my triangle in C++? - c++

So I've read this formula generates a uniform random point inside of a triangle, from this article... http://www.cs.princeton.edu/~funk/tog02.pdf
P = (1 - sqrt(R1)) * A + (sqrt(R1) * (1 - R2)) * B + (sqrt(R1) * R2) * C
Where...
R1 & R2 are random floats in between 0 and 1.
A, B, & C are the points that create our triangle.
I've implemented this formula in my code with MANY test conditions, expecting each condition to have a point generated in the triangle... However, there always seems to be a few cases of it outside the triangle.
Here is my code (in C++)...
#include "stdafx.h"
#include <random>
#include <time.h>
class Location
{
public:
Location(
int x,
int y)
{
m_x = x;
m_y = y;
}
int m_x;
int m_y;
private:
};
double RandomValueBetweenZeroAndOne()
{
return ((double)rand() / (RAND_MAX));
}
float GetArea(
float x1, float y1,
float x2, float y2,
float x3, float y3)
{
return abs((x1*(y2 - y3) + x2*(y3 - y1) + x3*(y1 - y2)) / 2.0f);
}
bool InsideTriangle(
float Ax, float Ay,
float Bx, float By,
float Cx, float Cy,
float Px, float Py)
{
/* Calculate area of triangle ABC */
float A = GetArea(Ax, Ay, Bx, By, Cx, Cy);
/* Calculate area of triangle PBC */
float A1 = GetArea(Px, Py, Bx, By, Cx, Cy);
/* Calculate area of triangle PAC */
float A2 = GetArea(Ax, Ay, Px, Py, Cx, Cy);
/* Calculate area of triangle PAB */
float A3 = GetArea(Ax, Ay, Bx, By, Px, Py);
/* Check if sum of A1, A2 and A3 is same as A */
if ((A == (A1 + A2 + A3)))
{
return true;
}
return false;
};
int main()
{
srand(time(0));
Location* A = new Location(-54900, 933200);
Location* B = new Location(-62800, 934300);
Location* C = new Location(-70000, 932100);
bool in_triangle = true;
int i = 0;
do
{
float R1 = static_cast<float>(RandomValueBetweenZeroAndOne());
float R2 = static_cast<float>(RandomValueBetweenZeroAndOne());
float random_x = (1.0f - sqrt(R1)) * static_cast<float>(A->m_x) + (sqrt(R1) * (1.0f - R2)) * static_cast<float>(B->m_x) + (sqrt(R1) * R2) * static_cast<float>(C->m_x);
float random_y = (1.0f - sqrt(R1)) * static_cast<float>(A->m_y) + (sqrt(R1) * (1.0f - R2)) * static_cast<float>(B->m_y) + (sqrt(R1) * R2) * static_cast<float>(C->m_y);
in_triangle = InsideTriangle(
static_cast<float>(A->m_x), static_cast<float>(A->m_y),
static_cast<float>(B->m_x), static_cast<float>(B->m_y),
static_cast<float>(C->m_x), static_cast<float>(C->m_y),
random_x, random_y);
if (!in_triangle)
{
printf("Point located outside of Triangle on %i iteration", i);
}
i++;
} while (in_triangle);
system("pause");
}
In one example... The equation assigned the random coordinates as follows:
random_x = -66886;
random_y = 932326;
I even made a C# program to verify if the point was truley outside of the triangle (visually). Here's the results, everything above the line that is visible is INSIDE of the triangle... Everything Below the line that is visible is OUTSIDE of the triangle...
https://puu.sh/rJtSJ/7a7a88c346.png
For reference, I know that I can just wrap the number generating in a do while loop until a value inside the triangle is generated... I just wanted to know why it's generating outside when it's not supposed to be, and where the bug is...

Floating point arithmetic isn't perfect.That means when you do an artihmetic operation with floating points it may not give exact expected result.In that cases generally you should correct your code with wisely using a small number.
In that case part that should be corrected should be
float random_x = (1.0f - sqrt(R1)) * static_cast<float>(A->m_x) + (sqrt(R1) * (1.0f - R2)) * static_cast<float>(B->m_x) + (sqrt(R1) * R2) * static_cast<float>(C->m_x);
float random_y = (1.0f - sqrt(R1)) * static_cast<float>(A->m_y) + (sqrt(R1) * (1.0f - R2)) * static_cast<float>(B->m_y) + (sqrt(R1) * R2) * static_cast<float>(C->m_y);
Since I didn't understand the formula I can't fix it.But if you were use that formula:
if(R1+R2>1)
{
R1=1-R1;
R2=1-R2;
}
float random_x = A->m_x+(B->m_x-A->m_x)*R1+(C->m_x-A->m_x)*R2;
float random_y = A->m_y+(B->m_y-A->m_y)*R1+(C->m_y-A->m_y)*R2;
You could correct it as:
if(R1+R2>1)
{
R1=1-R1;
R2=1-R2;
}
float error=0.01;
float D=1-error;
float random_x = D*A->m_x+D*(B->m_x-A->m_x)*R1+D*(C->m_x-A->m_x)*R2+error*(A->m_x+B->m_x+C->m_x)/3;
float random_y = D*A->m_y+D*(B->m_y-A->m_y)*R1+D*(C->m_y-A->m_y)*R2+error*(A->m_x+B->m_x+C->m_x)/3;
That will ensure points will be inside the triangle.

You have a rounding problem, this needs to round down to smaller precision. Also using double instead of float will give better result.
Instead of comparing two double (or float) values to see if they are the same, take their difference and see if it is within a margin of error. The function below has arbitrary margin of 0.00001
bool InsideTriangle(double Ax, double Ay, double Bx, double By,
double Cx, double Cy, double Px, double Py)
{
double A = GetArea(Ax, Ay, Bx, By, Cx, Cy);
double A1 = GetArea(Px, Py, Bx, By, Cx, Cy);
double A2 = GetArea(Ax, Ay, Px, Py, Cx, Cy);
double A3 = GetArea(Ax, Ay, Bx, By, Px, Py);
double diff = A1 + A2 + A3 - A;
if (abs(diff) < 0.00001) return true;
std::cout << diff << "\n";
return false;
};
To compare the values directly, use the example below:
double getround(double f)
{
return floor(f * 1000.0f + 0.5f) / 1000.0f;
}
bool InsideTriangle(double Ax, double Ay, double Bx, double By,
double Cx, double Cy, double Px, double Py)
{
double A = GetArea(Ax, Ay, Bx, By, Cx, Cy);
double A1 = GetArea(Px, Py, Bx, By, Cx, Cy);
double A2 = GetArea(Ax, Ay, Px, Py, Cx, Cy);
double A3 = GetArea(Ax, Ay, Bx, By, Px, Py);
if ((A == (A1 + A2 + A3)))
{
return true;
}
double t1 = getround(A1 + A2 + A3);
double t2 = getround(A);
if (t1 == t2)
return true;
std::cout << t1 << ", " << t2 << "\n";
return false;
};
Note that you should not use cast unless the compiler complains. For example the following cast is not needed
float foo(){return 0.1f;}
...
float R1 = static_cast<float>(foo());
Because foo is already float. If you automatically put cast everywhere then it can hide potential problems.
Avoid using memory allocation when it's not need. For example, use Location A(-54900, 933200); instead of new operator. If you use new then be sure to free the memory with delete when it is no longer needed.

Related

Problem with ellipse (rotated): incorrect position of any point is detected

When the ellipse is not rotated with this formula 1. If value = 1 - point on the ellipse, if value > 1 - outside, if value < 1 - inside. The program works correctly.
Code:
int checkPointNoAngle(int x0, int y0, int x, int y, int a, int b)
{
int value = (pow((x - x0), 2) / pow(a, 2)) + (pow((y - y0), 2) / pow(b, 2));
return value;
}
I need to work with a rotated ellipse, so I used formula 2.
Now the program incorrectly determines the position of the point.
int checkPoint(int x0, int y0, int x, int y, int a, int b)
{
int angle = 90;
int value = (pow(cos(angle * M_PI / 180)*((x - x0)+sin(angle * M_PI / 180)*(y-y0)), 2) / pow(a, 2)) + (pow(sin(angle * M_PI / 180) * ((x - x0) - cos(angle * M_PI / 180) * (y - y0)), 2) / pow(b, 2));
return value;
}
I drawing an ellipse using this code:
for (int t = 0; t < 360; t++)
{
int x = a * cos(t);
int y = b * sin(t);
int x1 = x * cos(angle * M_PI / 180) + y * sin(angle * M_PI / 180);
int y1 = -x * sin(angle * M_PI / 180) + y * cos(angle * M_PI / 180);
SDL_RenderDrawPoint(ren, x1 + centerX, y1 + centerY);
}
The program draws the ellipse correctly, but determines the position of the point incorrectly.
Examples of work:
3,4,5,6.
Example 4 and 5 works correctly with the checkPointNoAngle method.
I need to rotate the ellipse, so I created checkPoint method.
Example 6 indicates a bug.
The code was confusing a little bit. I tried to simplify the code corresponding to rotation and inverse rotation. The following code seems to work.
#include <iostream>
#include <vector>
#include <cmath>
struct Pt {int x, y;};
double checkPoint(int x0, int y0, int x, int y, int a, int b, int angle) {
double ang = (angle * M_PI)/180;
x = x - x0;
y = y - y0;
double xp = cos(ang)*x + sin(ang)*y;
double yp = -sin(ang)*x + cos(ang)*y;
double value = (xp*xp) / (a*a) + (yp*yp) / (b*b);
return value;
}
std::vector<Pt> gene_ellipse (int centerX, int centerY, int angle, int a, int b) {
std::vector<Pt> v;
double c = cos (angle * M_PI/180);
double s = sin (angle * M_PI/180);
for (int t = 0; t < 360; t++) {
double tt = M_PI * t / 180.0;
double x = a * cos(tt);
double y = b * sin(tt);
int x1 = x * c - y * s;
int y1 = x * s + y * c;
v.push_back (Pt{x1 + centerX, y1 + centerY});
}
return v;
}
int main () {
int centerX = 320;
int centerY = 240;
int angle = 120; // in degrees
int a = 200;
int b = 100;
int index = 25;
auto v = gene_ellipse (centerX, centerY, angle, a, b);
double check = checkPoint (centerX, centerY, v[index].x, v[index].y, a, b, angle);
std::cout << "check = " << check << "\n";
}

Polygon Clipping Error?

I have been trying to work out this problem with my Sutherland-Hodgman Polygon Clipper algorithm. When filling in the polygon i keep getting this result:
I cant seem to figure out what causing the bug, here is my code:
// #param in the number of vertices in the polygon to be clipped
// #param inV the incoming vertex list
// #param outV the outgoing vertex list
// #param ll the lower-left corner of the clipping rectangle
// #param ur the upper-right corner of the clipping rectangle
//
// #return number of vertices in the polygon resulting after clipping
int Clipper::clipPolygon( int in, const Vertex inV[], Vertex outV[],
Vertex ll, Vertex ur )
{
int out = 0, out2 = 0, out3 = 0, out4 = 0;
Vertex *outV2, *outV3, *outV4;
// We clip entire figure by clipping on the 4 edges of the clipping window
// each step will be stored, in *out pointers.
SHPC(inV, outV, in, out, ll.x, ur.y, ll.x, ll.y); // left
outV2 = new Vertex[out];
SHPC(outV, outV2, out, out2, ll.x, ll.y, ur.x, ll.y); // bottom
outV3 = new Vertex[out2];
SHPC(outV2, outV3, out2, out3, ur.x, ll.y, ur.x, ur.y); // right
outV4 = new Vertex[out3];
SHPC(outV3, outV4, out3, out4, ur.x, ur.y, ll.x, ur.y); // top
// we want outx and outy to store the final clipped polygons, which
// means we need to get content of outx4 and outy4
for (int i = 0; i < out; i++) { outV[i].x = outV4[i].x; outV[i].y = outV4[i].y; }
// same principle for out value
out = out4;
delete[] outV2;
delete[] outV3;
delete[] outV4;
return out;
}
void Clipper::SHPC(const Vertex inV[],
Vertex outV[], int in, int &out,
float x0, float y0, float x1, float y1) {
float px = inV[in - 1].x, py = inV[in - 1].y; // last vertex is the initial “predecessor”
float _x = px, _y = py;
for (int j = 0; j < in; j++) {
if (inside(inV[j].x, inV[j].y, x0, y0, x1, y1)) { // Cases 1 & 4
if (inside(px, py, x0, y0, x1, y1)) { // Case 1
output(inV[j].x, inV[j].y, out, outV);
}
else { // Case 4
intersect(px, py, inV[j].x, inV[j].y, x0, y0, x1, y1, _x, _y);
output(_x, _y, out, outV);
output(inV[j].x, inV[j].y, out, outV);
}
}
else { // Cases 2 & 3
if (inside(px, py, x0, y0, x1, y1)) { // Case 2
intersect(px, py, inV[j].x, inV[j].y, x0, y0, x1, y1, _x, _y);
output(_x, _y, out, outV);
} // Case 3 has no output
}
px = inV[j].x;
py = inV[j].y;
} // for
}
// is point inside boundary?
bool Clipper::inside(float _x, float _y, float x0, float y0, float x1, float y1) {
if (y0 == y1) { // horizontal edge
if (x0 < x1) return _y >= y0;
if (x0 > x1) return _y <= y0;
} else { // vertical edge
if (y1 > y0) return _x <= x0;
if (y0 > y1) return _x >= x0;
}
return false;
}
// put point into vector, update length
void Clipper::output(float _x, float _y, int &out, Vertex outV[]) {
outV[out].x = _x;
outV[out++].y = _y;
}
// compute intersection point, put point into newpoint parameter
void Clipper::intersect(float sx, float sy, float ex, float ey, float x0, float y0, float x1, float y1, float &_x, float &_y) {
if (x0 == x1) { // if it's a vertical edge
_x = x0;
_y = sy + (x0 - sx) * (ey - sy) / (ex - sx);
}
else { // if it's a horizontal edge
_y = y0;
_x = sx + (y0 - sy) * (ex - sx) / (ey - sy);
}
}
If I would have to guess I would say it is an issue with my inside function, but that raises the question of why is it only not working for the star and not for the rest of the shapes? I have tried rewriting the inside function, but it usually results in the same type of error only in a different direction. If anyone can point me in the right direction I would appreciate it.

c++ 2D servos robot arm inverse kinematics

I am currently trying to write code to instruct a robot arm (2 servos motors, 3 sticks) to write out words and am having trouble getting it right. At the moment I am just trying to get it to move to a vector. I think that the inverse kinematics part is correct but all I'm getting is a twitch from one of the motors (if I'm lucky). A Nucleo- F411RE board is being used and I'm using the mbed developer.
#include "mbed.h"
#include "Servo.h"
#include "math.h"
Servo servo1(PA_8), servo2(PA_9);
float len1 = 9.0;
float len2 = 7.5;
double pi = 3.1415926535897;
int lawOfCosines (float a, float b, float c )
{
return acos((a*a + b*b - c*c) / (2 * a * b));
}
int distance(float x, float y) {
return sqrt(x*x + y*y);
}
int deg(float rad) {
return rad * 180 / pi;
}
int main() {
//fmt.Println("Lets do some tests. First move to (5,5):");
float x = 5.0;
float y = 5.0;
float dist = distance(x, y);
float D1 = atan2(y, x);
float D2 = lawOfCosines(dist, len1, len2);
float A1 = D1 + D2;
float A2 = lawOfCosines(len1, len2, dist);
float m1 = A1 * (100/90);
float m2 = A2 * (100/90);
for(int i=0; i<m1; i++) {
servo2 = i/100.0;
wait(0.01);
}
for(int i=0; i<m2; i++) {
servo1 = i/100.0;
wait(0.01);
}
}
`}
Any help to where I'm going wrong is greatly appreciated

Drawing thick line has holes

I've made simple method to draw line with fixed thickness.
Here's my function:
void ColoringScene::drawThickLine(int x1, int y1, int x2, int y2, int r, int g, int b, int a, float wd, bool began, unsigned char* data, unsigned char* areasData){
if(began){
//if just began draw "dot" with filled circle
drawFilledCircle(x1, y1, 1, wd, r, g, b, a, data, areasData);
return;
}
//otherwise calculate angle between two points
int angle = (int)(atan2(-y2 + y1, x2 - x1) * 180 / M_PI);
int a1 = angle - 90;
int a2 = angle + 90;
//calculate a distance between two points
float diff = sqrtf(powf(x1 - x2, 2) + powf(y1 - y2, 2));
if(diff > wd) diff = wd;
//find all points in arc from angle - 90 to angle + 90
for(float _r = a1; _r < a2; _r+=1.0f){
float _x1 = cos(CC_DEGREES_TO_RADIANS(_r)) * wd + x1;
float _y1 = -sin(CC_DEGREES_TO_RADIANS(_r)) * wd + y1;
float _x2 = cos(CC_DEGREES_TO_RADIANS(_r)) * wd + x2;
float _y2 = -sin(CC_DEGREES_TO_RADIANS(_r)) * wd + y2;
//connect point from 2 arcs with line
drawLine(floor(_x1), floor(_y1), floor(_x2), floor(_y2), r, g, b, a, data, areasData);
}
}
it should draw line from (x1, y1) to (x2, y2) with color (r, g, b, a) and thickness wd. I use this method on drawing while moving finger on screen so I've also added extra parameter "began", which says if it's touch began or touch move. data is an array of pixels. areasData does not matter.
But it's not working as expected, here's an example result:
There could be a bit of compression, but you can see 2 dots, which were drawn fine and a curve build from many "thick" lines with holes.
If "wd" is not big enough the problem is not present. I'm almost sure it's some sort of problem with precision.
I tried:
- changing for loop from 0 to 360 (not angle-90, angle+90).
- using round instead of floor
- using sin instead of -sin (y is inverted anyway in my case)
- using _r lower than 1.0 like: 0.05.
And by given fixed thickness (in this example's 60 pixels) there's no way to set parameters like _r increment or angles to not draw without holes.
I decided to write my own function to do this, because other methods I found on web just didn't work as expected (especially with anti-aliasing, which will be perfect solution for me).
Here's drawLine function taken from website: http://willperone.net/Code/codeline.php
void ColoringScene::drawLine(int x1, int y1, int x2, int y2, int r, int g, int b, int a, unsigned char* data, unsigned char* areasData){
int dy = y2 - y1;
int dx = x2 - x1;
int stepx, stepy;
if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; }
if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; }
dy <<= 1; // dy is now 2*dy
dx <<= 1; // dx is now 2*dx
setPixelWithCheckingArea(x1, y1, r, g, b, a, data, areasData);
if (dx > dy)
{
int fraction = dy - (dx >> 1); // same as 2*dy - dx
while (x1 != x2)
{
if (fraction >= 0)
{
y1 += stepy;
fraction -= dx; // same as fraction -= 2*dx
}
x1 += stepx;
fraction += dy; // same as fraction -= 2*dy
setPixelWithCheckingArea(x1, y1, r, g, b, a, data, areasData);
}
} else {
int fraction = dx - (dy >> 1);
while (y1 != y2) {
if (fraction >= 0) {
x1 += stepx;
fraction -= dy;
}
y1 += stepy;
fraction += dx;
setPixelWithCheckingArea(x1, y1, r, g, b, a, data, areasData);
}
}
}

Distance from a point to a line/segment

I have to compute the distance from a point to a line (check if it is line or a line segment). I am not sure that the bool function IsSegment is working properly. Can i have some suggestions? Thank you.
double Distance_From_Line_to_Point(int *a, int *b, int *c, bool IsSegment) {
double distance;
int dot1;
int dot2;
distance = Cross_Product(a, b, c) / Distance(a, b);
if (IsSegment(a,b,c) == true) {
dot1 = Dot_Product(a, b, c);
if (dot1 > 0) {
return Distance(b, c);
}
dot2 = Dot_Product(b, a, c);
if (dot2 > 0) {
return Distance(a, c);
}
}
return fabs(distance);
}
bool IsSegment(int *a, int *b, int *c) {
double angle1;
double angle2;
angle1 = atan(double(b[1] - a[1]) / (b[0] - a[0]));
angle2 = atan(double(c[1] - b[1]) / (c[0] - b[0]));
if ((angle2 - angle1) * (180 / PI) > 90) {
return false;
}
return true;
}
Can't you just use the formula to get the distance?
So to find the line:
void getLine(double x1, double y1, double x2, double y2, double &a, double &b, double &c)
{
// (x- p1X) / (p2X - p1X) = (y - p1Y) / (p2Y - p1Y)
a = y1 - y2; // Note: this was incorrectly "y2 - y1" in the original answer
b = x2 - x1;
c = x1 * y2 - x2 * y1;
}
http://formule-matematica.tripod.com/distanta-de-dreapta.htm
double dist(double pct1X, double pct1Y, double pct2X, double pct2Y, double pct3X, double pct3Y)
{
double a, b, c;
getLine(pct2X, pct2Y, pct3X, pct3Y, a, b, c);
return abs(a * pct1X + b * pct1Y + c) / sqrt(a * a + b * b);
}
Example on how to use the code:
#include <CMATH>
void getLine(double x1, double y1, double x2, double y2, double &a, double &b, double &c)
{
// (x- p1X) / (p2X - p1X) = (y - p1Y) / (p2Y - p1Y)
a = y1 - y2; // Note: this was incorrectly "y2 - y1" in the original answer
b = x2 - x1;
c = x1 * y2 - x2 * y1;
}
double dist(double pct1X, double pct1Y, double pct2X, double pct2Y, double pct3X, double pct3Y)
{
double a, b, c;
getLine(pct2X, pct2Y, pct3X, pct3Y, a, b, c);
return abs(a * pct1X + b * pct1Y + c) / sqrt(a * a + b * b);
}
int main(int argc, char* argv[])
{
double d = dist(1,2,3,4,5,6);
return 0;
}
Distance from a point to a line
You need 2 formulas:
Line formula: source this answer.
private Vector2 m_point1;
private Vector2 m_point1;
private float m_A;
private float m_B;
private float m_C;
public void CalculateLine()
{
m_A = m_point1.y - m_point2.y;
m_B = m_point2.x - m_point1.x;
m_C = m_point1.x * m_point2.y - m_point2.x * m_point1.y;
if(m_A == 0 && m_B == 0)
{
Debug.LogError("Line error: A & B = 0");
}
}
Distance from point to line: source Wikipedia
public float Distance2DPointToLine(Vector2 point)
{
return Mathf.Abs(m_A * point.x + m_B * point.y + m_C) /
Mathf.Sqrt(m_A * m_A + m_B * m_B);
}
Distance from a point to a line segment
It depends what you define "Distance from a point to a line segment"
Maybe distance from a point to a line segment is the distance from a point to the segment's middle point:
Maybe the distance is available if the point can be projected on the line segment
Maybe you didn't imagine what's result when you ask about segment, so I can't answer the segment part for you.