Grid - Obstacle / How to Calculate "Field of view" - algorithm

My english is not my native language, I have no idea how title it, how to explain it clearly and not sure if it's the right term. I've tried to search on google first but for the reason quoted above, I couldn't find anything related.
Could you guys first please check the imgur album : https://imgur.com/a/4mMuCil
So...
Black square is an "obstacle"
Red Square is a "player"
Grey square are "area where player is not able to see"
Depending on the distance of the player from the obstacle, the player can see more or less "things"
Is there a general formula to determine the area that he can see or can't see ?
Or I have to write a unique formula depending on the player position relative to the obstacle
I'm sorry If what I wrote doesn't make sense
Thanks for your help
EDIT :
point player(5, 0);
point obstacle(4, 2);
.....o...
.........
....#....
.........
.....#...
.....##..
.....###.
.....####
......###

This needs a little work. I'll say the steps to make it work:
(1) Let's define the player position p and the obstacle position o.
We know we want to paint a "triangle" after the obstacle.
(2) Let's define the angle according to proximity.
The nearer, the bigger the angle is, so I set the angle to 45 if the distance is 1 and it decreases as the player gets further from the obstacle. The angle is 5 + (max(0, 50 - distance * 10)). You can tune this angle.
(3) Let's build a big triangle. The first vertex is the obstacle. Then, throw a big line from the player through the obstacle. Rotate this line around the obstacle half angle clockwise (to get the second vertex) and half angle anti-clockwise (to get the third vertex), as shown in the image:
(4) Lastly, iterate to the matrix and for each position, ask if that coordinates are inside the triangle.
#include <bits/stdc++.h>
using namespace std;
struct point{
float x, y;
point(){}
point(float x, float y){this->x = x; this->y = y;}
//point(int x, int y){this->x = x; this->y = y;}
};
point rotate(point pivot, float angle, point p, bool clockwise){
float s = sin(angle);
float c = cos(angle);
p.x -= pivot.x;
p.y -= pivot.y;
if(clockwise){
return point(p.x * c + p.y * s + pivot.x, -p.x * s + p.y * c + pivot.y);
}
else{
return point(p.x * c - p.y * s + pivot.x, p.x * s + p.y * c + pivot.y);
}
}
float triangleArea(point p1, point p2, point p3) { //find area of triangle formed by p1, p2 and p3
return abs((p1.x*(p2.y-p3.y) + p2.x*(p3.y-p1.y)+ p3.x*(p1.y-p2.y))/2.0);
}
bool inside(point p1, point p2, point p3, point p) { //check whether p is inside or outside
float area = triangleArea (p1, p2, p3); //area of triangle ABC
float area1 = triangleArea (p, p2, p3); //area of PBC
float area2 = triangleArea (p1, p, p3); //area of APC
float area3 = triangleArea (p1, p2, p); //area of ABP
return abs(area - area1 + area2 + area3) < 1; //when three triangles are forming the whole triangle
}
char m[9][9];
point player(4, 0);
point obstacle(4, 2);
float angle(){
float dist = sqrt(pow(player.x - obstacle.x, 2) + pow(player.y - obstacle.y, 2));
cout<<"dist: "<<(5.0 + max(0.0, 50.0 - 10.0 * dist))<<endl;
return (5.0 + max(0.0, 50.0 - 10.0 * dist)) * 0.0174533;
}
void print(){
for(int i = 0; i < 9; i++){
for(int j = 0; j < 9; j++){
cout<<m[i][j];
}
cout<<endl;
}
}
int main(){
for(int i = 0; i < 9; i++){
for(int j = 0; j < 9; j++){
m[i][j] = '.';
}
}
m[(int)player.y][(int)player.x] = 'o';
m[(int)obstacle.y][(int)obstacle.x] = '#';
float rad = angle();
point end(20.0 * (obstacle.x - player.x) + obstacle.x, 20.0 * (player.y - obstacle.y) + obstacle.y);
point p2 = rotate(obstacle, rad / 2.0, end, true);
point p3 = rotate(obstacle, rad / 2.0, end, false);
for(int i = 0; i < 9; i++){
for(int j = 0; j < 9; j++){
if(i == (int) player.y && j == (int) player.x) continue;
if(i == (int) obstacle.y && j == (int) obstacle.x) continue;
if(inside(obstacle, p2, p3, point(j, i))) m[i][j] = '#';
}
}
print();
return 0;
}
OUTPUT:
....o....
.........
....#....
....#....
....#....
....#....
...###...
...###...
...###...

Suppose the player is at (0,0), and the obstacle is at (j,k), with j>0 and k>=0.
Then a square at (x, y) will be visible if either (2j-1)y >= (2k+1)x or (2k-1)x >= (2j+1)y.
Applying this rule to the other three quadrants is straightforward.

Related

Drawing ellipse with Bresenham's algorithm

Hello,
I'm trying to draw an ellipse, which is parallel to the orthogonal system, using Bresenham's algorithm. I want to draw the top-left (W,SW,S) quarter of the ellipse, and then deduce others.
To do this, i'm using an incremental algorithm with the second-order logic. I did it from another algorithm that draw the top-right quarter first, but what i'm doing isn't working.
The problem appears when the 2nd region is drawing, and I don't know where it comes from.
You can see what I have (black), and what I expect (green):
(center of the ellipse (xc, yc) and the upper right button (x2,y2) that is ~(xc+30,yc+20) in this example)
(a is abs(x2-xc), and b is abs(y2-yc))
The first parameter is the middle of the ellipse (xc, yc), the second is the upper right point established the x and y radius. You can see the ellipse goes too far (2 points on the left and on the right). You can see an other example
(center of the ellipse (xc, yc) and the upper right button (x2,y2) that is ~(xc+15,yc+18) in this example)
The algorithm is deduced from the incremental algorithm with the second-order logic.
Here is my code, (a is abs(x2-xc), and b is abs(y2-yc))
ellipse(int a, int b, int xc, int yc) {
int a2 = a*a, b2 = b*b;
int x = 0, y = b; //Starting point
int incSW = b2*2 + a2*2;
int deltaW = b2*(-2*x + 3); //deduced from incremental algorithm with the second-order logic
int deltaS = a2*(-2*y + 3);
int deltaSW = deltaW + deltaS;
int d1 = b2 - a2*b + a2/4; //dp starting value in the first region
int d2 = b2*(x - 0.5)*(x - 0.5) + a2*(y - 1)*(y - 1) - a2*b2; //dp starting value in the second region
//First region
while(a2*(y-0.5) >= b2*(-x-1)) {
DrawPixel(g,-x+xc, -y+yc); // 1st case
DrawPixel(g,-x+xc, y+yc); // 2nd case
DrawPixel(g,x+xc, y+yc); // 3rd case
DrawPixel(g,x+xc, -y+yc); // 4th case
if(d1>0) {
d1+=deltaSW;
deltaW+=b2*2;
deltaSW+=incSW;
y--;
}
else {
d1+=deltaW;
deltaW+=2*b2;
deltaSW+=2*b2;
}
x--;
}
deltaSW = b2*(2 - 2*x) + a2*(-2*y + 3);
//Second region
while(y>=0) {
DrawPixel(g,-x+xc, -y+yc); // 1st case
DrawPixel(g,-x+xc, y+yc); // 2nd case
DrawPixel(g,x+xc, y+yc); // 3rd case
DrawPixel(g,x+xc, -y+yc); // 4th case
if(d2>0) {
d2+=deltaS;
deltaS+=a2*2;
deltaSW+=a2*2;
}
else {
d2+=deltaSW;
deltaSW+=incSW;
deltaS+=a2*2;
x--;
}
y--;
}
}
I hope you can help me, thanks.
Using the error term e = a x^2 + b y^2 - r^2, it's pretty easy to show that a step from (x,y) to (x,y+1) changes the error by 2by + b, a step to (x+1,y+1) by 2ax + a + 2by + b, and a step to (x+1,y) by 2ax + a.
Starting from a point (-x0, 0), choose the least absolute error step from these three. The first two cases are the norm for the "first region" as you call it.
The first time a step right, (x,y) to (x+1,y), produces least error, you know you're in the second region. At this point the first case is no longer needed. The quarter ellipse can be finished using only the second two cases.
Note this check avoids the floating point operations you've used. The whole point of Bresenham-ish algorithms is to avoid floating point.
The last bit to notice is that you don't want to compute 2ax or 2by each iteration. The multiplications can be avoided by maintaining variables, say dx=2ax and dy=2by, and updating them. A step from x to x+1 increments dx by 2a, a constant. Similarly a step from y to y+1 increments dy by 2b.
Putting all this together, you get the (rough) code below.
Note that you can check the incremental error computation by verifying it against the original error term. If (x0,0) is the initial point, then you know x0^2 = r^2. So the actual error in every iteration is a * x^2 + b * y^2 - x0^2. This ought to equal e in the code below, and it does.
import static java.lang.Math.abs;
import java.util.Arrays;
import java.util.function.BiConsumer;
public class EllipseTracer {
static char [] [] raster = new char[51][101];
static void trace(int x, int y, int a, int b, BiConsumer<Integer, Integer> emitter) {
emitter.accept(x, y);
int e = 0;
int dx = 2 * a * x;
int dy = 2 * b * y;
// First region: stepping north and northeast.
while (x < 0) {
int dxa = dx + a;
int dyb = dy + b;
int eUp = e + dyb;
int eRt = e + dxa;
int eDg = e + dxa + dyb;
if (abs(eUp) < abs(eDg)) {
emitter.accept(x, ++y);
e = eUp;
dy += 2 * b;
} else {
if (abs(eRt) < abs(eDg)) {
// Step east is least error. Found second region.
emitter.accept(++x, y);
e = eRt;
dx += 2 * a;
break;
}
emitter.accept(++x, ++y);
e = eDg;
dy += 2 * b;
dx += 2 * a;
}
}
// Second region: step northeast and east.
while (x < 0) {
int dxa = dx + a;
int dyb = dy + b;
int eRt = e + dxa;
int eDg = e + dxa + dyb;
if (abs(eRt) < abs(eDg)) {
emitter.accept(++x, y);
e = eRt;
dx += 2 * a;
} else {
emitter.accept(++x, ++y);
e = eDg;
dy += 2 * b;
dx += 2 * a;
}
}
}
static void emit(int x, int y) {
raster[y][x + 100] = '*';
}
public static void main(String [] args) {
for (int i = 0; i < raster.length; ++i) {
Arrays.fill(raster[i], ' ');
}
trace(-100, 0, 1, 4, EllipseTracer::emit);
for (int i = 0; i < raster.length; ++i) {
System.out.println(raster[i]);
}
}
}
You can add more tricks to avoid the absolute values, but I'll let you look for those.

Obtaining orientation map of fingerprint image using OpenCV

I'm trying to implement the method of improving fingerprint images by Anil Jain. As a starter, I encountered some difficulties while extracting the orientation image, and am strictly following those steps described in Section 2.4 of that paper.
So, this is the input image:
And this is after normalization using exactly the same method as in that paper:
I'm expecting to see something like this (an example from the internet):
However, this is what I got for displaying obtained orientation matrix:
Obviously this is wrong, and it also gives non-zero values for those zero points in the original input image.
This is the code I wrote:
cv::Mat orientation(cv::Mat inputImage)
{
cv::Mat orientationMat = cv::Mat::zeros(inputImage.size(), CV_8UC1);
// compute gradients at each pixel
cv::Mat grad_x, grad_y;
cv::Sobel(inputImage, grad_x, CV_16SC1, 1, 0, 3, 1, 0, cv::BORDER_DEFAULT);
cv::Sobel(inputImage, grad_y, CV_16SC1, 0, 1, 3, 1, 0, cv::BORDER_DEFAULT);
cv::Mat Vx, Vy, theta, lowPassX, lowPassY;
cv::Mat lowPassX2, lowPassY2;
Vx = cv::Mat::zeros(inputImage.size(), inputImage.type());
Vx.copyTo(Vy);
Vx.copyTo(theta);
Vx.copyTo(lowPassX);
Vx.copyTo(lowPassY);
Vx.copyTo(lowPassX2);
Vx.copyTo(lowPassY2);
// estimate the local orientation of each block
int blockSize = 16;
for(int i = blockSize/2; i < inputImage.rows - blockSize/2; i+=blockSize)
{
for(int j = blockSize / 2; j < inputImage.cols - blockSize/2; j+= blockSize)
{
float sum1 = 0.0;
float sum2 = 0.0;
for ( int u = i - blockSize/2; u < i + blockSize/2; u++)
{
for( int v = j - blockSize/2; v < j+blockSize/2; v++)
{
sum1 += grad_x.at<float>(u,v) * grad_y.at<float>(u,v);
sum2 += (grad_x.at<float>(u,v)*grad_x.at<float>(u,v)) * (grad_y.at<float>(u,v)*grad_y.at<float>(u,v));
}
}
Vx.at<float>(i,j) = sum1;
Vy.at<float>(i,j) = sum2;
double calc = 0.0;
if(sum1 != 0 && sum2 != 0)
{
calc = 0.5 * atan(Vy.at<float>(i,j) / Vx.at<float>(i,j));
}
theta.at<float>(i,j) = calc;
// Perform low-pass filtering
float angle = 2 * calc;
lowPassX.at<float>(i,j) = cos(angle * pi / 180);
lowPassY.at<float>(i,j) = sin(angle * pi / 180);
float sum3 = 0.0;
float sum4 = 0.0;
for(int u = -lowPassSize / 2; u < lowPassSize / 2; u++)
{
for(int v = -lowPassSize / 2; v < lowPassSize / 2; v++)
{
sum3 += inputImage.at<float>(u,v) * lowPassX.at<float>(i - u*lowPassSize, j - v * lowPassSize);
sum4 += inputImage.at<float>(u, v) * lowPassY.at<float>(i - u*lowPassSize, j - v * lowPassSize);
}
}
lowPassX2.at<float>(i,j) = sum3;
lowPassY2.at<float>(i,j) = sum4;
float calc2 = 0.0;
if(sum3 != 0 && sum4 != 0)
{
calc2 = 0.5 * atan(lowPassY2.at<float>(i, j) / lowPassX2.at<float>(i, j)) * 180 / pi;
}
orientationMat.at<float>(i,j) = calc2;
}
}
return orientationMat;
}
I've already searched a lot on the web, but almost all of them are in Matlab. And there exist very few ones using OpenCV, but they didn't help me either. I sincerely hope someone could go through my code and point out any error to help. Thank you in advance.
Update
Here are the steps that I followed according to the paper:
Obtain normalized image G.
Divide G into blocks of size wxw (16x16).
Compute the x and y gradients at each pixel (i,j).
Estimate the local orientation of each block centered at pixel (i,j) using equations:
Perform low-pass filtering to remove noise. For that, convert the orientation image into a continuous vector field defined as:
where W is a two-dimensional low-pass filter, and w(phi) x w(phi) is its size, which equals to 5.
Finally, compute the local ridge orientation at (i,j) using:
Update2
This is the output of orientationMat after changing the mat type to CV_16SC1 in Sobel operation as Micka suggested:
Maybe it's too late for me to answer, but anyway somebody could read this later and solve the same problem.
I've been working for a while in the same algorithm, same method you posted... But there's some writting errors when the papper was redacted (I guess). After fighting a lot with the equations I found this errors by looking other similar works.
Here is what worked for me...
Vy(i, j) = 2*dx(u,v)*dy(u,v)
Vx(i,j) = dx(u,v)^2 - dy(u,v)^2
O(i,j) = 0.5*arctan(Vy(i,j)/Vx(i,j)
(Excuse me I wasn't able to post images, so I wrote the modified ecuations. Remeber "u" and "v" are positions of the summation across the BlockSize by BlockSize window)
The first thing and most important (obviously) are the equations, I saw that in different works this expressions were really different and in every one they talked about the same algorithm of Hong et al.
The Key is finding the Least Mean Square (First 3 equations) of the gradients (Vx and Vy), I provided the corrected formulas above for this ation. Then you can compute angle theta for the non overlapping window (16x16 size recommended in the papper), after that the algorithm says you must calculate the magnitud of the doubled angle in "x" and "y" directions (Phi_x and Phi_y).
Phi_x(i,j) = V(i,j) * cos(2*O(i,j))
Phi_y(i,j) = V(i,j) * sin(2*O(i,j))
Magnitud is just:
V = sqrt(Vx(i,j)^2 + Vy(i,j)^2)
Note that in the related work doesn't mention that you have to use the gradient magnitud, but it make sense (for me) in doing it. After all this corrections you can apply the low pass filter to Phi_x and Phi_y, I used a simple Mask of size 5x5 to average this magnitudes (something like medianblur() of opencv).
Last thing is to calculate new angle, that is the average of the 25ith neighbors in the O(i,j) image, for this you just have to:
O'(i,j) = 0.5*arctan(Phi_y/Phi_x)
We're just there... All this just for calculating the angle of the NORMAL VECTOR TO THE RIDGES DIRECTIONS (O'(i,j)) in the BlockSize by BlockSize non overlapping window, what does it mean? it means that the angle we just calculated is perpendicular to the ridges, in simple words we just calculated the angle of the riges plus 90 degrees... To get the angle we need, we just have to substract to the obtained angle 90°.
To draw the lines we need to have an initial point (X0, Y0) and a final point(X1, Y1). For that imagine a circle centered on (X0, Y0) with a radious of "r":
x0 = i + blocksize/2
y0 = j + blocksize/2
r = blocksize/2
Note we add i and j to the first coordinates becouse the window is moving and we are gonna draw the line starting from the center of the non overlaping window, so we can't use just the center of the non overlaping window.
Then to calculate the end coordinates to draw a line we can just have to use a right triangle so...
X1 = r*cos(O'(i,j)-90°)+X0
Y1 = r*sin(O'(i,j)-90°)+Y0
X2 = X0-r*cos(O'(i,j)-90°)
Y2 = Y0-r*cos(O'(i,j)-90°)
Then just use opencv line function, where initial Point is (X0,Y0) and final Point is (X1, Y1). Additional to it, I drawed the windows of 16x16 and computed the oposite points of X1 and Y1 (X2 and Y2) to draw a line of the entire window.
Hope this help somebody.
My results...
Main function:
Mat mat = imread("nwmPa.png",0);
mat.convertTo(mat, CV_32F, 1.0/255, 0);
Normalize(mat);
int blockSize = 6;
int height = mat.rows;
int width = mat.cols;
Mat orientationMap;
orientation(mat, orientationMap, blockSize);
Normalize:
void Normalize(Mat & image)
{
Scalar mean, dev;
meanStdDev(image, mean, dev);
double M = mean.val[0];
double D = dev.val[0];
for(int i(0) ; i<image.rows ; i++)
{
for(int j(0) ; j<image.cols ; j++)
{
if(image.at<float>(i,j) > M)
image.at<float>(i,j) = 100.0/255 + sqrt( 100.0/255*pow(image.at<float>(i,j)-M,2)/D );
else
image.at<float>(i,j) = 100.0/255 - sqrt( 100.0/255*pow(image.at<float>(i,j)-M,2)/D );
}
}
}
Orientation map:
void orientation(const Mat &inputImage, Mat &orientationMap, int blockSize)
{
Mat fprintWithDirectionsSmoo = inputImage.clone();
Mat tmp(inputImage.size(), inputImage.type());
Mat coherence(inputImage.size(), inputImage.type());
orientationMap = tmp.clone();
//Gradiants x and y
Mat grad_x, grad_y;
// Sobel(inputImage, grad_x, CV_32F, 1, 0, 3, 1, 0, BORDER_DEFAULT);
// Sobel(inputImage, grad_y, CV_32F, 0, 1, 3, 1, 0, BORDER_DEFAULT);
Scharr(inputImage, grad_x, CV_32F, 1, 0, 1, 0);
Scharr(inputImage, grad_y, CV_32F, 0, 1, 1, 0);
//Vector vield
Mat Fx(inputImage.size(), inputImage.type()),
Fy(inputImage.size(), inputImage.type()),
Fx_gauss,
Fy_gauss;
Mat smoothed(inputImage.size(), inputImage.type());
// Local orientation for each block
int width = inputImage.cols;
int height = inputImage.rows;
int blockH;
int blockW;
//select block
for(int i = 0; i < height; i+=blockSize)
{
for(int j = 0; j < width; j+=blockSize)
{
float Gsx = 0.0;
float Gsy = 0.0;
float Gxx = 0.0;
float Gyy = 0.0;
//for check bounds of img
blockH = ((height-i)<blockSize)?(height-i):blockSize;
blockW = ((width-j)<blockSize)?(width-j):blockSize;
//average at block WхW
for ( int u = i ; u < i + blockH; u++)
{
for( int v = j ; v < j + blockW ; v++)
{
Gsx += (grad_x.at<float>(u,v)*grad_x.at<float>(u,v)) - (grad_y.at<float>(u,v)*grad_y.at<float>(u,v));
Gsy += 2*grad_x.at<float>(u,v) * grad_y.at<float>(u,v);
Gxx += grad_x.at<float>(u,v)*grad_x.at<float>(u,v);
Gyy += grad_y.at<float>(u,v)*grad_y.at<float>(u,v);
}
}
float coh = sqrt(pow(Gsx,2) + pow(Gsy,2)) / (Gxx + Gyy);
//smoothed
float fi = 0.5*fastAtan2(Gsy, Gsx)*CV_PI/180;
Fx.at<float>(i,j) = cos(2*fi);
Fy.at<float>(i,j) = sin(2*fi);
//fill blocks
for ( int u = i ; u < i + blockH; u++)
{
for( int v = j ; v < j + blockW ; v++)
{
orientationMap.at<float>(u,v) = fi;
Fx.at<float>(u,v) = Fx.at<float>(i,j);
Fy.at<float>(u,v) = Fy.at<float>(i,j);
coherence.at<float>(u,v) = (coh<0.85)?1:0;
}
}
}
} ///for
GaussConvolveWithStep(Fx, Fx_gauss, 5, blockSize);
GaussConvolveWithStep(Fy, Fy_gauss, 5, blockSize);
for(int m = 0; m < height; m++)
{
for(int n = 0; n < width; n++)
{
smoothed.at<float>(m,n) = 0.5*fastAtan2(Fy_gauss.at<float>(m,n), Fx_gauss.at<float>(m,n))*CV_PI/180;
if((m%blockSize)==0 && (n%blockSize)==0){
int x = n;
int y = m;
int ln = sqrt(2*pow(blockSize,2))/2;
float dx = ln*cos( smoothed.at<float>(m,n) - CV_PI/2);
float dy = ln*sin( smoothed.at<float>(m,n) - CV_PI/2);
arrowedLine(fprintWithDirectionsSmoo, Point(x, y+blockH), Point(x + dx, y + blockW + dy), Scalar::all(255), 1, CV_AA, 0, 0.06*blockSize);
// qDebug () << Fx_gauss.at<float>(m,n) << Fy_gauss.at<float>(m,n) << smoothed.at<float>(m,n);
// imshow("Orientation", fprintWithDirectionsSmoo);
// waitKey(0);
}
}
}///for2
normalize(orientationMap, orientationMap,0,1,NORM_MINMAX);
imshow("Orientation field", orientationMap);
orientationMap = smoothed.clone();
normalize(smoothed, smoothed, 0, 1, NORM_MINMAX);
imshow("Smoothed orientation field", smoothed);
imshow("Coherence", coherence);
imshow("Orientation", fprintWithDirectionsSmoo);
}
seems nothing forgot )
I have read your code thoroughly and found that you have made a mistake while calculating sum3 and sum4:
sum3 += inputImage.at<float>(u,v) * lowPassX.at<float>(i - u*lowPassSize, j - v * lowPassSize);
sum4 += inputImage.at<float>(u, v) * lowPassY.at<float>(i - u*lowPassSize, j - v * lowPassSize);
instead of inputImage you should use a low pass filter.

Polygon splitted by 4 parts

I have an arbitrary convex polygon. And it is splitted by 2 perpendicular lines (vectors of them are (0,1) and (1,0)). Is there any algorithm that can calculate area by smaller figures (S1, S2, S3, S4). All I can do is to calculate points where lines cross the polygon and then calculate areas, but is there something better optimized?
I store all vertexes in array double **v;
And then I calculate all points, where my polygon crosses X and Y axises:
void cross() { //calculates buf (crossing with Y)
act = 0;
for (int i = 0; i < n; ++i) {
buf[act][0]=v[i][0];
buf[act][1]=v[i][1];
act++;
if (v[i][0]*v[(i+1)%n][0] < 0) {
buf[act][0] = 0;
buf[act][1] = v[i][1] + std::abs(v[i][0])*(v[(i+1)%n][1]-v[i][1])/(std::abs(v[i][0])+std::abs(v[(i+1)%n][0]));
act++;
}
}
}
void vert() { /calculates buf2 (crossing with X)
act2 =0;
for (int i = 0; i < act; ++i) {
buf2[act2][0]=buf[i][0];
buf2[act2][1]=buf[i][1];
act2++;
if (buf[i][1]*buf[(i+1)%act][1] < 0) {
buf2[act2][1] = 0;
buf2[act2][0] = buf[i][0] + std::abs(buf[i][1])*(buf[(i+1)%act][0] - buf[i][0])/ (std::abs(buf[i][1])+std::abs(buf[(i+1)%act][1]));
act2++;
}
}
}
After calling cross(); vert(); I get an array buf2 and number of elements there is act2;
After this I'am triangilating polygon and detect in what squad does traingle lay.
double s_trian (double a, double b, double c, double d) {
//area of triangle
double s =0;
s=0.5*std::abs((a)*(d)-(c)*(b));
return s;
}
void triang() { //calculate areas of s1,s2,s3,s4 by
//triangulating
bool rotation;
double temror;
s1=0, s2 =0, s3 =0, s4 =0;
int a,b;
for (int i =0; i < act2; ++i) {
a=i%act2;
b=(i+1)%act2;
temror = s_trian(buf2[a][0], buf2[a][1], buf2[b][0], buf2[b][1]);
if ((buf2[a][0]+buf2[b][0]) > 0) {
if((buf2[a][1]+buf2[b][1] > 0))
s1+=temror;
else
s4+=temror;
} else {
if ((buf2[a][1]+buf2[b][1] > 0))
s2+=temror;
else
s3+=temror;
}
}
}
Can I optimize something here?
Given that your polygon is convex, just select an arbitrary point P inside the polygon and split it up in triangles with one corner in P.
Then calculate the area of each triangle: http://www.mathopenref.com/heronsformula.html and sum them up.
You can do slightly better.
Ignore X to begin with.
Project horizontally every vertex on Y. This way, you define trapezoids. The sum of the algebraic areas of these trapezoids gives the total surface. Add the positive and negative areas in separate accumulators, this will give you the areas on both sides of Y. But some trapezoids will cross Y and be skewed: compute the areas of the two triangles and accumulate where appropriate.
Now to deal with the horizontal axis, similarly you will add the contributions to a positive/negative accumulator, or to both.
In total there will be four accumulators, for all sign combinations, giving you the four requested areas.
This procedure will cost you a little more than one accumulation per side, instead of four. It can be done in a single loop, avoiding the need to compute and store the four subpolygons.
[Following up on my comment yesterday; has much in common with Dan Bystrom's answer.]
Loop over all sides, and compute the area of the triangle made of the side and the origin. Add to the appropriate quad area. Where a side crosses the axis, compute the intercept and split the triangle. Compute both triangle areas parts and add each to the appropriate quad.
Using the origin as a point for a triangle vertex makes the cross product based formula for a triangle area very fast and simple. You don't even need the call to fabs() if you take care to pass the parameters in the right order.
This code has not handled the problem where a vertex lies on an axis or cases where no point lies in a given quadrant.
struct Point
{
double x;
double y;
};
double areaOfTriangle(double ax, double ay, double bx, double by)
{
return fabs(by*ax - bx *ay)/2;
}
unsigned getQuad(double x, double y)
{
int xPos = (x > 0) ? 0 : 1;
int yPos = (y > 0) ? 0 : 1 ;
int quad = xPos + yPos;
if (!xPos && yPos)
quad = 3;
return quad;
}
Point getIntercept(const Point& a, const Point& b)
{
Point intercept;
if ( (a.x * b.x) < 0)
{
// Crosses y axis.
intercept.x = 0;
intercept.y = a.y - (b.y - a.y) / (b.x - a.x)*a.x;
}
else
{
// Crosses x axis.
intercept.y = 0;
intercept.x = a.x - (b.x - a.x) / (b.y - a.y)*a.y;
}
return intercept;
}
void getAreaOfQuads(double* retQuadArea, const Point* points, unsigned numPts)
{
for (unsigned i = 0; i != 4; ++i)
retQuadArea[i] = 0;
const Point* a = &points[numPts - 1];
unsigned quadA = getQuad(a->x, a->y);
for (unsigned i = 0; i != numPts; ++i)
{
const Point* b = &points[i];
unsigned quadB = getQuad(b->x, b->y);
if (quadA == quadB)
{
retQuadArea[quadA] += areaOfTriangle(a->x, a->y, b->x, b->y);
}
else
{
// The side a->b crosses an axis.
// First, find out where.
Point c = getIntercept(*a, *b);
retQuadArea[quadA] += areaOfTriangle(a->x, a->y, c.x, c.y);
retQuadArea[quadB] += areaOfTriangle(c.x, c.y, b->x, b->y);
}
a = b;
quadA = quadB;
}
}
void test(Point* polygon, unsigned n)
{
double areas[4] = {};
getAreaOfQuads(areas, polygon, n);
for (unsigned i = 0; i != 4; ++i)
std::cout << areas[i] << ", ";
std::cout << std::endl;
}
Point polygon[]
{
{0.6, 0.2},
{ 0.2, 0.8 },
{ -0.2, 0.7 },
{ -0.6, 0.6 },
{ -1.0, 0.1 },
{ -0.6, -0.5 },
{ 0.1, -0.5 },
{ 0.9, -0.1 }
};
Point square[]
{
{1, 1},
{ -1, 1 },
{ -1, -1 },
{ 1, -1 }
};
int main()
{
test(square, 4);
test(polygon, 8);
return 0;
}

Optimizing / simplifying a path

Say I have a path with 150 nodes / verticies. How could I simplify if so that for example a straight line with 3 verticies, would remove the middle one since it does nothing to add to the path. Also how could I avoid destroying sharp corners? And how could I remove tiny variations and have smooth curves remaining.
Thanks
For every 3 vertices, pick the middle one and calculate its distance to the line segment between the other two. If the distance is less than the tolerance you're willing to accept, remove it.
If the middle vertex is very close to one of the endpoints, you should tighten the tolerance to avoid removing rounded corners for instance.
The simpler approach. Take 3 verticies a, b and c and calculate the angle/inclination between verticies.
std::vector<VERTEX> path;
//fill path
std::vector<int> removeList;
//Assure that the value is in the same unit as the return of angleOf function.
double maxTinyVariation = SOMETHING;
for (int i = 0; i < quantity-2; ++i) {
double a = path[i], b = path[i + 1] , c = path[i + 2]
double abAngle = angleOf(a, b);
double bcAngle = angleOf(b, c);
if (abs(ab - bc) <= maxTinyVariation) {
//a, b and c are colinear
//remove b later
removeList.push_back(i+1);
}
}
//Remove vertecies from path using removeList.
How could I simplify if so that for example a straight line with 3 verticies, would remove the middle one since it does nothing to add to the path.
For each set of three consecutive vertices, test whether they are all in a straight line. If they are, remove the middle vertex.
Also how could I avoid destroying sharp corners?
If you're only removing vertices that fall on a straight line between two others, then you won't have a problem with this.
Use Douglas-Peucker method to simplify a Path.
epsilon parameter defines level of "simplicity":
private List<Point> douglasPeucker (List<Point> points, float epsilon){
int count = points.size();
if(count < 3) {
return points;
}
//Find the point with the maximum distance
float dmax = 0;
int index = 0;
for(int i = 1; i < count - 1; i++) {
Point point = points.get(i);
Point lineA = points.get(0);
Point lineB = points.get(count-1);
float d = perpendicularDistance(point, lineA, lineB);
if(d > dmax) {
index = i;
dmax = d;
}
}
//If max distance is greater than epsilon, recursively simplify
List<Point> resultList;
if(dmax > epsilon) {
List<Point> recResults1 = douglasPeucker(points.subList(0,index+1), epsilon);
List<Point> recResults2 = douglasPeucker(points.subList(index, count), epsilon);
List<Point> tmpList = new ArrayList<Point>();
tmpList.addAll(recResults1);
tmpList.remove(tmpList.size()-1);
tmpList.addAll(recResults2);
resultList = tmpList;
} else {
resultList = new ArrayList<Point>();
resultList.add(points.get(0));
resultList.add(points.get(count-1));
}
return resultList;
}
private float perpendicularDistance(Point point, Point lineA, Point lineB){
Point v1 = new Point(lineB.x - lineA.x, lineB.y - lineA.y);
Point v2 = new Point(point.x - lineA.x, point.y - lineA.y);
float lenV1 = (float)Math.sqrt(v1.x * v1.x + v1.y * v1.y);
float lenV2 = (float)Math.sqrt(v2.x * v2.x + v2.y * v2.y);
float angle = (float)Math.acos((v1.x * v2.x + v1.y * v2.y) / (lenV1 * lenV2));
return (float)(Math.sin(angle) * lenV2);
}
Let A, B, C be some points.
The easiest way to check they lie on the same line is to count crossproduct of vectors
B-A, C-A.
If it equals zero, they lie on the same line:
// X_ab, Y_ab - coordinates of vector B-A.
float X_ab = B.x - A.x
float Y_ab = B.y - A.y
// X_ac, Y_ac - coordinates of vector C-A.
float X_ac = C.x - A.x
float Y_ac = C.y - A.y
float crossproduct = Y_ab * X_ac - X_ab * Y_ac
if (crossproduct < EPS) // if crossprudct == 0
{
// on the same line.
} else {
// not on the same line.
}
After you know that A, B, C lie on the same line it is easy to know whether B lies between A and C throw innerproduct of vectors B-A and C-A. If B lies between A and C, then (B-A) has the same direction as (C-A), and innerproduct > 0, otherwise < 0:
float innerproduct = X_ab * X_ac + Y_ab * Y_ac;
if (innerproduct > 0) {
// B is between A and C.
} else {
// B is not between A and C.
}

Equidistant points in a line segment

Let assume you have two points (a , b) in a two dimensional plane. Given the two points, what is the best way to find the maximum points on the line segment that are equidistant from each point closest to it with a minimal distant apart.
I use C#, but examples in any language would be helpful.
List<'points> FindAllPointsInLine(Point start, Point end, int minDistantApart)
{
// find all points
}
Interpreting the question as:
Between point start
And point end
What is the maximum number of points inbetween spaced evenly that are at least minDistanceApart
Then, that is fairly simply: the length between start and end divided by minDistanceApart, rounded down minus 1. (without the minus 1 you end up with the number of distances between the end points rather than the number of extra points inbetween)
Implementation:
List<Point> FindAllPoints(Point start, Point end, int minDistance)
{
double dx = end.x - start.x;
double dy = end.y - start.y;
int numPoints =
Math.Floor(Math.Sqrt(dx * dx + dy * dy) / (double) minDistance) - 1;
List<Point> result = new List<Point>;
double stepx = dx / numPoints;
double stepy = dy / numPoints;
double px = start.x + stepx;
double py = start.y + stepy;
for (int ix = 0; ix < numPoints; ix++)
{
result.Add(new Point(px, py));
px += stepx;
py += stepy;
}
return result;
}
If you want all the points, including the start and end point, then you'll have to adjust the for loop, and start 'px' and 'py' at 'start.x' and 'start.y' instead. Note that if accuracy of the end-points is vital you may want to perform a calculation of 'px' and 'py' directly based on the ratio 'ix / numPoints' instead.
I'm not sure if I understand your question, but are you trying to divide a line segment like this?
Before:
A +--------------------+ B
After:
A +--|--|--|--|--|--|--+ B
Where "two dashes" is your minimum distance? If so, then there'll be infinitely many sets of points that satisfy that, unless your minimum distance can exactly divide the length of the segment. However, one such set can be obtained as follows:
Find the vectorial parametric equation of the line
Find the total number of points (floor(length / minDistance) + 1)
Loop i from 0 to n, finding each point along the line (if your parametric equation takes a t from 0 to 1, t = ((float)i)/n)
[EDIT]
After seeing jerryjvl's reply, I think that the code you want is something like this: (doing this in Java-ish)
List<Point> FindAllPointsInLine(Point start, Point end, float distance)
{
float length = Math.hypot(start.x - end.x, start.y - end.y);
int n = (int)Math.floor(length / distance);
List<Point> result = new ArrayList<Point>(n);
for (int i=0; i<=n; i++) { // Note that I use <=, not <
float t = ((float)i)/n;
result.add(interpolate(start, end, t));
}
return result;
}
Point interpolate(Point a, Point b, float t)
{
float u = 1-t;
float x = a.x*u + b.x*t;
float y = a.y*u + b.y*t;
return new Point(x,y);
}
[Warning: code has not been tested]
Find the number of points that will fit on the line. Calculate the steps for X and Y coordinates and generate the points. Like so:
lineLength = sqrt(pow(end.X - start.X,2) + pow(end.Y - start.Y, 2))
numberOfPoints = floor(lineLength/minDistantApart)
stepX = (end.X - start.X)/numberOfPoints
stepY = (end.Y - start.Y)/numberOfPoints
for (i = 1; i < numberOfPoints; i++) {
yield Point(start.X + stepX*i, start.Y + stepY*i)
}

Resources