The following is the three vertices with x,y and z co-ordinates of a triangle in 3D:
-0.2035416, 0.1107585, 0.9516008 (vertex A)
-0.0334390, -0.2526040, 0.9751212 (vertex B)
0.2569092, 0.0913718, 0.9817184 (Vertex C)
The projection plane is divided into a grid of (height*width) pixels.
I want to traverse every pixel inside the triangle inside the projection plane manually starting from the bottom to top of the triangle and print each pixel co-ordinates inside the triangle on the screen in c++. Say, I have already found the top and bottom vertex of the triangle. But now, how will I traverse from bottom to top and print each pixel co-ordinate? What's the logic behind this?
I have an idea of making two nested for loops like below but what will I do inside the loops? how will I make the sideway move after each x and y increment?
for (int y = ymin; y <= ymax; ++y) {
for (int x = xmin; x <= xmax; ++x) {
//what to to here?
}
}
Traversing demo:
#include <iostream>
template <size_t kD>
class Vector{
public:
template <class... Args>
Vector(double coord, Args&&... args) {
static_assert( sizeof...(args)+1 == kD, "Unmatched vector dimension" );
InitCoord(0, coord, std::forward<Args>(args)...);
}
Vector(const Vector &) = default;
Vector &operator=(const Vector &) = default;
double &operator[](const size_t i) {
return coord_[i];
}
double operator[](const size_t i) const {
return coord_[i];
}
friend Vector<kD> operator-(const Vector<kD> &A, const Vector<kD> &B) {
Vector v;
for (size_t i=0; i<kD; ++i)
v[i] = A[i]-B[i];
return v;
}
private:
Vector() = default;
template <class... Args>
void InitCoord(const int pos, double coord, Args&&... args) {
coord_[pos] = coord;
InitCoord(pos+1, std::forward<Args>(args)...);
}
void InitCoord(const int pos) {}
double coord_[kD];
};
class Line {
public:
Line(const double x1, const double y1, const double x2, const double y2)
: x1_(x1), y1_(y1), x2_(x2), y2_(y2) {}
Line(const Vector<2> A, const Vector<2> B)
: x1_(A[0]), y1_(A[1]), x2_(B[0]), y2_(B[1]) {}
double operator()(const double x, const double y) const {
return (y-y1_)*(x2_-x1_) - (x-x1_)*(y2_-y1_);
}
int_fast8_t Sign(const double x, const double y) const {
return Signum( (y-y1_)*(x2_-x1_) - (x-x1_)*(y2_-y1_) );
}
private:
int_fast8_t Signum(const double x) const {
return (0.0 < x) - (x < 0.0);
}
const double x1_,y1_;
const double x2_,y2_;
};
void Transpos(Vector<2> &v) {
v[0] = (v[0]+1)/2*720; // col
v[1] = (v[1]+1)/2*480; // row
}
double CalculateZ(const Vector<2> &D, const Vector<2> &AB, const Vector<2> &AC,
const Vector<3> &AB3, const Vector<3> &AC3) {
const double b = (D[1]*AB[0]-D[0]*AB[1]) / (AC[1]*AB[0]-AC[0]*AB[1]);
const double a = AB[0]==0 ? (D[1]-b*AC[1])/AB[1] : (D[0]-b*AC[0])/AB[0];
std::cout << a << " " << b << std::endl;
return a*AB3[2]+b*AC3[2];
}
int main()
{
const auto A3 = Vector<3>(0.0, 0.0, 7.0);
const auto B3 = Vector<3>(0.0, 0.3, 9.0);
const auto C3 = Vector<3>(0.4, 0.0, 1.0);
const auto AB3 = B3-A3;
const auto AC3 = C3-A3;
const auto BC3 = C3-B3;
// Some projection works here, which I am not good at.
// A B C store the projected triangle coordiate in the [-1,1][-1,1] area
auto A = Vector<2>(0.0, 0.0);
auto B = Vector<2>(0.0, 0.3);
auto C = Vector<2>(0.4, 0.0);
Transpos(A);
Transpos(B);
Transpos(C);
const auto AB2 = B-A;
const auto AC2 = C-A;
const auto BC2 = C-B;
const Line AB(A, B);
const Line AC(A, C);
const Line BC(B, C);
const auto signAB = AB.Sign(C[0],C[1]);
const auto signAC = AC.Sign(B[0],B[1]);
const auto signBC = BC.Sign(A[0],A[1]);
// top
// 0------------720 (col x)
// |
// |
// |
// |
// 480 (row y)
// bottom
for (int row=480-1; row>=0; --row) {
for (int col=0; col<720; ++col) {
if (signAB*AB.Sign(col,row)>=0 && signAC*AC.Sign(col,row)>=0 &&
signBC*BC.Sign(col,row)>=0 )
std::cout << row << "," << col << " Z:"
<< CalculateZ(Vector<2>(col, row)-A, AB2, AC2, AB3, AC3) + A3[2]
<< std::endl;
}
}
return 0;
}
Projection:
first space [-1,1][-1,1]
second space [0,720][0,480]
Let's say we have a (x1,y1) in the first space, then (x_,y_) with x_=(x1+1)/2*720, y_=(y1+1)/2*480 will be in the second space.
More generalize:
first space [xmin,xmax][ymin,ymax]
second space [xmin_,xmax_][ymin_,ymax_]
(x1,y1)
->
( (x1-xmin)/(xmax-xmin)*(xmax_-xmin_)+xmin_ ,
(y1-ymin)/(ymax-ymin)*(ymax_-ymin_)+ymin_ )
If you just want to zoom it, not twist it or something...
Edit #1:
Thanks to #Adrian Colomitchi's advices, which is outstanding, I have
improved the demo.
Ax Ay Bx By Cx Cy are now coordinates in the first space, they are
then "transposed" into the second space. As a result, Line AB AC BC
are now "in" the second space. And the two loops are modified
accordingly, they now iterate the points of the second space.
How to find z value from (x,y):
AB represents a vector from A(Ax,Ay) to B(Bx,By), i.e. AB = B-A = (Bx-Ax,By-Ay).
For any given point D(Dx,Dy) in the triangle, represent it into AD = aAB + bAC : (Dx-Ax, Dy-Ay) = a*(Bx-Ax, By-Ay) + b*(Cx-Ax, Cy-Ay) where Dx Dy Ax Ay Bx By Cx Cy is known. Find out a and b, and then Dz = a*(Bz-Az) + b*(Cz-Az). Dx Dy in the 3D space can be calculated the same way.
Edit #2:
Z value calculation added to the demo.
I tried to keep the demo simple, but calculating the Z value really involved lots of variables and calculations. I declare a new class called Vector to manage points and vectors, while the Line class remains unchanged.
You need to change the inner loop slightly; don't go from xmin to xmax. For each value of y from ymin to ymax there will be exactly two different pixels (two different x values) which lie exactly on the two edges of the triangle. Calculate those points and then all points between them will be inside the triangle. And you'll have to handle some edge cases such as when one of the edges is horizontal.
First you must transform your {0,1} ranges (vert & horz) to pixel coordinates. You speak of 720x480. This is not a square, but a rectangle. If you decide to keep one-to-one scale, you'll get a distorted triangle. If not, perhaps you only use 480x480 pixels.
Second, now you have your three vertices in pixel-space, you can iterate every pixel in this pixel-space and tell if it belongs to the triangle or not. The function 'InTriangle' for this job is what #felix posted in the code of his solution:
if (signAB*AB(i,j)>=0 && signAC*AC(i,j)>=0 && signBC*BC(i,j)>=0 )
Related
2nd task:
For a function f : R^n → R the gradient at a point ~x ∈ R^n is to be calculated:
- Implement a function
CMyVector gradient(CMyVector x, double (*function)(CMyVector x)),
which is given in the first parameter the location ~x and in the second parameter the function f as function pointer in the second parameter, and which calculates the gradient ~g = grad f(~x) numerically
by
gi = f(x1, . . . , xi-1, xi + h, xi+1 . . . , xn) - f(x1, . . . , xn)/h
to fixed h = 10^-8.
My currently written program:
Header
#pragma once
#include <vector>
#include <math.h>
class CMyVektor
{
private:
/* data */
int Dimension = 0;
std::vector<double>Vector;
public:
CMyVektor();
~CMyVektor();
//Public Method
void set_Dimension(int Dimension /* Aktuelle Dim*/);
void set_specified_Value(int index, int Value);
double get_specified_Value(int key);
int get_Vector_Dimension();
int get_length_Vektor();
double& operator [](int index);
string umwandlung()
};
CMyVektor::CMyVektor(/* args */)
{
Vector.resize(0, 0);
}
CMyVektor::~CMyVektor()
{
for (size_t i = 0; i < Vector.size(); i++)
{
delete Vector[i];
}
}
void CMyVektor::set_Dimension(int Dimension /* Aktuelle Dim*/)
{
Vector.resize(Dimension);
};
void CMyVektor::set_specified_Value(int index, int Value)
{
if (Vector.empty())
{
Vector.push_back(Value);
}
else {
Vector[index] = Value;
}
};
double CMyVektor::get_specified_Value(int key)
{
// vom intervall anfang - ende des Vectors
for (unsigned i = 0; i < Vector.size(); i++)
{
if (Vector[i] == key) {
return Vector[i];
}
}
};
int CMyVektor::get_Vector_Dimension()
{
return Vector.size();
};
// Berechnet den Betrag "länge" eines Vectors.
int CMyVektor::get_length_Vektor()
{
int length = 0;
for (size_t i = 0; i < Vector.size(); i++)
{
length += Vector[i]^2
}
return sqrt(length);
}
// [] Operator überladen
double& CMyVektor::operator [](int index)
{
return Vector[index];
}
main.cpp
#include <iostream>
#include "ClassVektor.h"
using namespace std;
CMyVektor operator+(CMyVektor a, CMyVektor b);
CMyVektor operator*(double lambda, CMyVektor a);
CMyVektor gradient(CMyVektor x, double (*funktion)(CMyVektor x));
int main() {
CMyVektor V1;
CMyVektor V2;
CMyVektor C;
C.set_Dimension(V1.get_length_Vector());
C= V1 + V2;
std::cout << "Addition : "<< "(";;
for (int i = 0; i < C.get_length_Vector(); i++)
{
std::cout << C[i] << " ";
}
std::cout << ")" << endl;
C = lamda * C;
std::cout << "Skalarprodukt: "<< C[0]<< " ";
}
// Vector Addition
CMyVektor operator+(CMyVektor a, CMyVektor b)
{
int ai = 0, bi = 0;
int counter = 0;
CMyVektor c;
c.set_Dimension(a.get_length_Vector());
// Wenn Dimension Gleich dann addition
if (a.get_length_Vector() == b.get_length_Vector())
{
while (counter < a.get_length_Vector())
{
c[counter] = a[ai] + b[bi];
counter++;
}
return c;
}
}
//Berechnet das Skalarprodukt
CMyVektor operator*(double lambda, CMyVektor a)
{
CMyVektor c;
c.set_Dimension(1);
for (unsigned i = 0; i < a.get_length_Vector(); i++)
{
c[0] += lambda * a[i];
}
return c;
}
/*
* Differenzenquotient : (F(x0+h)+F'(x0)) / h
* Erster Parameter die Stelle X - Zweiter Parameter die Funktion
* Bestimmt numerisch den Gradienten.
*/
CMyVektor gradient(CMyVektor x, double (*funktion)(CMyVektor x))
{
}
My problem now is that I don't quite know how to deal with the
CMyVector gradient(CMyVector x, double (*function)(CMyVector x))
function and how to define a function that corresponds to it.
I hope that it is enough information. Many thanks.
The function parameter is the f in the difference formula. It takes a CMyVector parameter x and returns a double value. You need to supply a function parameter name. I'll assume func for now.
I don't see a parameter for h. Are you going to pass a single small value into the gradient function or assume a constant?
The parameter x is a vector. Will you add a constant h to each element?
This function specification is a mess.
Function returns a double. How do you plan to turn that into a vector?
No wonder you're confused. I am.
Are you trying to do something like this?
You are given a function signature
CMyVector gradient(CMyVector x, double (*function)(CMyVector x))
Without knowing the exact definition I will assume, that at least the basic numerical vector operations are defined. That means, that the following statements compile:
CMyVector x {2.,5.,7.};
CMyVector y {1.,7.,4.};
CMyVector z {0.,0.,0.};
double a = 0.;
// vector addition and assigment
z = x + y;
// vector scalar multiplication and division
z = z * a;
z = x / 0.1;
Also we need to know the dimension of the CMyVector class. I assumed and will continue to do so that it is three dimensional.
The next step is to understand the function signature. You get two parameters. The first one denotes the point, at which you are supposed to calculate the gradient. The second is a pointer to the function f in your formula. You do not know it, but can call it on a vector from within your gradient function definition. That means, inside of the definition you can do something like
double f_at_x = function(x);
and the f_at_x will hold the value f(x) after that operation.
Armed with this, we can try to implement the formula, that you mentioned in the question title:
CMyVector gradient(CMyVector x, double (*function)(CMyVector x)) {
double h = 0.001;
// calculate first element of the gradient
CMyVector e1 {1.0, 0.0, 0.0};
double result1 = ( function(x + e1*h) - function(x) )/h;
// calculate second element of the gradient
CMyVector e2 {0.0, 1.0, 0.0};
double result2 = ( function(x + e2*h) - function(x) )/h;
// calculate third element of the gradient
CMyVector e3 {0.0, 0.0, 1.0};
double result3 = ( function(x + e3*h) - function(x) )/h;
// return the result
return CMyVector {result1, result2, result3};
}
There are several thing worth to mention in this code. First and most important I have chosen h = 0.001. This may like a very arbitrary choice, but the choice of the step size will very much impact the precision of your result. You can find a whole lot of discussion about that topic here. I took the same value that according to that wikipedia page a lot of handheld calculators use internally. That might not be the best choice for the floating point precision of your processor, but should be a fair one to start with.
Secondly the code looks very ugly for an advanced programmer. We are doing almost the same thing for each of the three dimensions. Ususally you would like to do that in a for loop. The exact way of how this is done depends on how the CMyVector type is defined.
Since the CMyVektor is just rewritting the valarray container, I will directly use the valarray:
#include <iostream>
#include <valarray>
using namespace std;
using CMyVektor = valarray<double>;
CMyVektor gradient(CMyVektor x, double (*funktion)(CMyVektor x));
const double h = 0.00000001;
int main()
{
// sum(x_i^2 + x_i)--> gradient: 2*x_i + 1
auto fun = [](CMyVektor x) {return (x*x + x).sum();};
CMyVektor d = gradient(CMyVektor{1,2,3,4,5}, fun);
for (auto i: d) cout << i<<' ';
return 0;
}
CMyVektor gradient(CMyVektor x, double (*funktion)(CMyVektor x)){
CMyVektor grads(x.size());
CMyVektor pos(x.size());
for (int i = 0; i<x.size(); i++){
pos[i] = 1;
grads[i] = (funktion(x + h * pos) - funktion(x))/ h;
pos[i] = 0;
}
return grads;
}
The prints out 3 5 7 9 11 which is what is expected from the given function and the given location
I need my code to find the 4th vertex of a rectangle. Tried to make something but as always failed. It works in some cases, bot not everytime. Could someone help me with it?
Also, it has to be done with 2 classes, one for vertices and other one for a whole rectangle.
Problem is that my code works for a certain vertex setting. I was trying many things but still fail.
Here's the code:
#include <iostream>
#include <cmath>
using namespace std;
class vertex{
public:
double x;
double y;
void insert(){
cin>>x;
cin>>y;
}
};
class rect{
private:
double ax, ay, bx, by, cx, cy, dx, dy;
public:
void insert_data(vertex A, vertex B, vertex C){ //inserting rectangle data
ax=A.x;
ay=A.y;
bx=B.x;
by=B.y;
cx=C.x;
cy=C.y;
}
void calculate(){ //finding 4h vertex coordinates
dx=cx+(abs(ax-bx));
dy=cy+(abs(ay+by));
}
void out(){ //output
cout<<dx<<" "<<dy;
}
};
using namespace std;
int main() {
vertex A, B, C;
A.insert();
B.insert();
C.insert();
rect X;
X.insert_data(A, B, C);
X.calculate();
X.out();
return 0;
}
As I think about it now it may have something to do with inserting the coordinates to the right variables, but can't think of a solution to fix this.
For example:
Input:
1 1
0 3
3 2
Output:
2 4
Order of inputing each vertex is not specified.
Working with vectors is a very interesting topic. Here are some good explainations about vector
To answer your question:
From 3 givens vertexs A, B and C, you have only three cases of the right angle : at A, at B or at C. If you found the right angle, for example, at B (no matter order of A and C), you have the D coordinate calculated by the formulation : D = A + C - B.
To detect if the right angle is at B: the dot product of two vectors BA and BC is 0, no matter order of A and C.
In C++ way, (not C way) you should add operators to manipulate vectors in your vertex class, here is an example :
#define MY_EPSILON 10E-6
class vertex {
public:
double X, Y;
vertex(double x_, double y_ ) :X(x_), Y( y_){}
vertex():X(0), Y(0){}
vertex operator +( vertex v ){ return vertex( X + v.X, Y + v.Y ); }
vertex operator -( vertex v ){ return vertex( X - v.X, Y - v.Y ); }
double dot( vertex v ){ return X * v.X + Y * v.Y; }
double length() { return sqrt(X * X + Y * Y ); }
vertex normalize( bool &bOk ){
double len = length(); bOk = false;
if( len > MY_EPSILON ){ bOk = true; return vertex( X/len, Y/len ); }
return *this;
}
};
std::ostream & operator << ( std::ostream & s, vertex v ){
s << std::setprecision(6) << "(" << v.X << "," << v.Y << ") ";
return s;
}
Dot product of two vectors:
To verify if the right angle is at the point B, we can use the following function, it will compute the dot product of two normalized vectors of AB and BC :
bool isRighAngle( vertex a, vertex b, vertex c){
bool bOkAB, bOkBC;
vertex uAB = ( b - a ).normalize( bOkAB ), uBC = ( c - b ).normalize( bOkBC );
return bOkAB && bOkBC && fabs(uAB.dot( uBC )) < MY_EPSILON;
}
Note that when we compaire a double value with zero, use always an epsilon, there is no zero absolut for a double. This function also return false if one of normalized vectors cannot be computed (two points are too close to each other).
Compute the last coordinate from the right angle :
This following function return true if the last cordinate D is calculated from the right angle B:
bool getLastCoordinateIfRightAngle( vertex a, vertex b, vertex c, vertex & d ){
if( isRighAngle( a, b, c ) ){
d = (a + c) - b;
return true;
}
return false;
}
Look for the right angle:
So to find the last coordinate D from 3 vertexs A, B and C, you should do the test for three cases of right angle, the test stops when a solution was found :
bool getLastCoordinate( vertex a, vertex b, vertex c, vertex &d ){
if( getLastCoordinateIfRightAngle( a, b, c, d ) //if B is at the right angle
|| getLastCoordinateIfRightAngle( a, c, b, d ) //if C is at the right angle
|| getLastCoordinateIfRightAngle( b, a, c, d ) ) //if A is at the right angle
{
return true;
}
//No right angle found.
return false;
}
Quick test :
We can do a quick test if it works:
int main(int argc, char *argv[])
{
vertex A(0.0, 0.0), B(1.0, 0.0), C(0.0, 1.0), D;
if( getLastCoordinate( A, B, C, D ) ){
std::cout << "D coordinate " << D << " found from inputs : " << A << B << C << std::endl;
}else {
std::cout << "D coordinate not found for input: " << A << B << C << std::endl;
}
return 0;
}
EPSILON CHOICE:
It depends on your domain, if you work in a very small object domain that (X, Y) are very small (close to 10E-5 for example), you will have some difficulties in calculations (floating point in GPU is very limited in precision). It's better to transform the working domain to a normal range.
In the example above, EPSILON is set to 10E-6. If the length between two points is smaller to this value, the two points can be considered as an unique point - they stay in the same position).
If you have three vertices, then you have half of the rectangle: a right triangle. First you need to determine which point is at the right angle. You can do this in different ways. A way is to apply Pythagoras theorem: find the two vertices which are the farthest apart. The remaining vertex is at the right angle (Another way could be calculating the dot product between each pair of edges, and the one which closest to zero forms right angle).
Let's call the vertex at the right angle A, and the other two B and C. Now, the vectors of the two shorter edges of the right triangle is B-A and C-A. If you add these edges to A, you'll get the 4th vertex:
D=A+(B-A)-(C-A)=B+C-A
Having the coordinates of three of the four vertices of the rectangle, we can find the 4th vertex coordinates by the given below formula.
x4 = x1 ^ x2 ^ x3;
y4 = y1 ^ y2 ^ y3;
^ = bitwise XOR.
What happens here is that XORing them together causes the pair of coordinates that are the same to cancel.
I hope this helps.
I have a quad defined by 4 (x,y,z) points (like a plane that has edges). I have an OpenVDB grid. I want to fill all the voxels with value 1 that are inside my quad (including edges). Is such thing possible with out setting each voxel of the quad (limited plane) manually?
If the four points build a rectangle, it could be possible using the
void fill(const CoordBBox& bbox, const ValueType& value, bool active = true);
function that exists in the Grid-class. It is not possible to transform the CoordBBox for rotations, instead you would have to do that by changing the transformation of the grid. With pseudo-code it could look like
CoordBBox plane; // created from your points
Transform old = grid.transform();
grid.setTransform(...); // Some transformation that places the grid correctly with respect to the plane
grid.fill(plane, 1);
grid.setTransform(old);
If this is not the case, you would have to set the values yourself.
There is an unoptimized method, for arbitrarily shaped planar quadrilaterals, you only need to input four vertices of the 3D space plane, and the output is filled planar voxels.
Get the vertex with the longest sum of adjacent sides as A, the adjacent vertices of A are B and D, and obtain the voxel coordinates and voxel numbers between A-B and A-D based on ray-cast, and in addition A vertex C-B and C-D progressively sample the same number of voxels.
Make one-to-one correspondence between the voxels adjacent to the two vertices A and D above, and fill the plane area based on ray-cast.
void VDBVolume::fillPlaneVoxel(const PlanarEquation& planar) {
auto accessor = volume_->getUnsafeAccessor();
const auto transform = volume_->transform();
const openvdb::Vec3f vdbnormal(planar.normal.x(), planar.normal.y(), planar.normal.z());
int longside_vtx[2];
calVtxLongSide(planar.vtx, longside_vtx);
// 1. ray cast long side
std::vector<Eigen::Vector3f> longsidepoints;
int neiborvtx[2];
{
const Eigen::Vector3f origin = planar.vtx[longside_vtx[0]];
const openvdb::Vec3R eye(origin.x(), origin.y(), origin.z());
GetRectNeiborVtx(longside_vtx[0], neiborvtx);
for(int i = 0; i < 2; ++i) {
Eigen::Vector3f direction = planar.vtx[neiborvtx[i]] - origin;
openvdb::Vec3R dir(direction.x(), direction.y(), direction.z());
dir.normalize();
const float length = static_cast<float>(direction.norm());
if(length > 50.f) {
std::cout << "GetRectNeiborVtx length to large, something wrong: "<< i << "\n" << origin.transpose() << "\n"
<< planar.vtx[neiborvtx[i]].transpose() << std::endl;
continue;
}
const float t0 = -voxel_size_/2;
const float t1 = length + voxel_size_/2;
const auto ray = openvdb::math::Ray<float>(eye, dir, t0, t1).worldToIndex(*volume_);
openvdb::math::DDA<decltype(ray)> dda(ray);
do {
const auto voxel = dda.voxel();
const auto voxel_center_world = GetVoxelCenter(voxel, transform);
longsidepoints.emplace_back(voxel_center_world);
} while (dda.step());
}
}
// 2. 在小边均匀采样相同数目点
const int longsidepointnum = longsidepoints.size();
std::vector<Eigen::Vector3f> shortsidepoints;
shortsidepoints.resize(longsidepointnum);
{
// 输入:顶点、两邻接点,vtxs, 输出采样带年坐标,根据距离进行采样
GenerateShortSidePoints(longside_vtx[1], neiborvtx, planar.vtx, shortsidepoints);
}
// 3. ray cast from longsidepoints to shortsidepoints
// std::cout << "longsidepointnum: " << longsidepointnum << std::endl;
for(int pid = 0; pid < longsidepointnum; ++pid) {
const Eigen::Vector3f origin = longsidepoints[pid];
const openvdb::Vec3R eye(origin.x(), origin.y(), origin.z());
const Eigen::Vector3f direction = shortsidepoints[pid] - origin;
openvdb::Vec3R dir(direction.x(), direction.y(), direction.z());
dir.normalize();
const float length = direction.norm();
if(length > 50.f) {
std::cout << "length to large, something wrong: "<< pid << "\n" << origin.transpose() << "\n"
<< shortsidepoints[pid].transpose() << std::endl;
continue;
}
const float t0 = -voxel_size_/2;
const float t1 = length + voxel_size_/2;
const auto ray = openvdb::math::Ray<float>(eye, dir, t0, t1).worldToIndex(*volume_);
openvdb::math::DDA<decltype(ray)> dda(ray);
do {
const auto voxel = dda.voxel();
accessor.setValue(voxel, vdbnormal);
} while (dda.step());
}
}
Seems there is no way to compute line line intersection using boost::geometry, but I wonder what is the most common way to do it in C++?
I need intersection algorithms for two infinite lines in 2D, if it will be faster it can be two different functions like:
bool line_intersection(line,line);
point line_intersetion(line,line);
P.S. I really try to avoid a wheel invention, so incline to use some library.
The best algorithms that I've found for finding the intersection of lines are in: Real Time Collision Detection by Christer Ericson, a copy of the book can be found here.
Chapter 5 from page 146 onwards describes how to find the closest point of 3D lines which is also the crossing point of 2D lines... with example code in C.
Note: beware of parallel lines, they can cause divide by zero errors.
Express one of the lines in parametric form and the other in implicit form:
X = X0 + t (X1 - X0), Y= Y0 + t (Y1 - Y0)
S(X, Y) = (X - X2) (Y3 - Y2) - (Y - Y2) (X3 - X2) = 0
By linearity of the relations, you have
S(X, Y) = S(X0, Y0) + t (S(X1, Y1) - S(X0, Y0)) = S0 + t (S1 - S0) = 0
From this you get t, and from t the coordinates of the intersection.
It takes a total of 15 adds, 6 multiplies and a single divide.
Degeneracy is indicated by S1 == S0, meaning that the lines are parallel. In practice, the coordinates may not be exact because of truncation errors or others, so that test for equality to 0 can fail. A workaround is to consider the test
|S0 - S1| <= µ |S0|
for small µ.
You can try my code, I'm using boost::geometry and I put a small test case in the main function.
I define a class Line with two points as attributes.
Cross product is very a simple way to know if two lines intersect. In 2D, you can compute the perp dot product (see perp function) that is a projection of cross product on the normal vector of 2D plane. To compute it, you need to get a direction vector of each line (see getVector method).
In 2D, you can get the intersection point of two lines using perp dot product and parametric equation of line. I found an explanation here.
The intersect function returns a boolean to check if two lines intersect. If they intersect, it computes the intersection point by reference.
#include <cmath>
#include <iostream>
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
namespace bg = boost::geometry;
// Define two types Point and Vector for a better understanding
// (even if they derive from the same class)
typedef bg::model::d2::point_xy<double> Point;
typedef bg::model::d2::point_xy<double> Vector;
// Class to define a line with two points
class Line
{
public:
Line(const Point& point1,const Point& point2): p1(point1), p2(point2) {}
~Line() {}
// Extract a direction vector
Vector getVector() const
{
Vector v(p2);
bg::subtract_point(v,p1);
return v;
}
Point p1;
Point p2;
};
// Compute the perp dot product of vectors v1 and v2
double perp(const Vector& v1, const Vector& v2)
{
return bg::get<0>(v1)*bg::get<1>(v2)-bg::get<1>(v1)*bg::get<0>(v2);
}
// Check if lines l1 and l2 intersect
// Provide intersection point by reference if true
bool intersect(const Line& l1, const Line& l2, Point& inter)
{
Vector v1 = l1.getVector();
Vector v2 = l2.getVector();
if(std::abs(perp(v1,v2))>0.)
{
// Use parametric equation of lines to find intersection point
Line l(l1.p1,l2.p1);
Vector v = l.getVector();
double t = perp(v,v2)/perp(v1,v2);
inter = v1;
bg::multiply_value(inter,t);
bg::add_point(inter,l.p1);
return true;
}
else return false;
}
int main(int argc, char** argv)
{
Point p1(0.,0.);
Point p2(1.,0.);
Point p3(0.,1.);
Point p4(0.,2.);
Line l1(p1,p2);
Line l2(p3,p4);
Point inter;
if( intersect(l1,l2,inter) )
{
std::cout<<"Coordinates of intersection: "<<inter.x()<<" "<<inter.y()<<std::endl;
}
return 0;
}
EDIT: more detail on cross product and perp dot product + delete tol argument (off topic)
This code should work for you. You may be able to optimize it a bit:
template <class Tpoint>
Tpoint line<Tpoint>::intersect(const line& other) const{
Tpoint x = other.first - first;
Tpoint d1 = second - first;
Tpoint d2 = other.second - other.first;
auto cross = d1.x*d2.y - d1.y*d2.x;
auto t1 = (x.x * d2.y - x.y * d2.x) / static_cast<float>(cross);
return first + d1 * t1;
}
Perhaps a common way is to approximate the infinity? From my library using boost::geometry:
// prev and next are segments and RAY_LENGTH is a very large constant
// create 'lines'
auto prev_extended = extendSeg(prev, -RAY_LENGTH, RAY_LENGTH);
auto next_extended = extendSeg(next, -RAY_LENGTH, RAY_LENGTH);
// intersect!
Points_t isection_howmany;
bg::intersection(prev_extended, next_extended, isection_howmany);
then you could test whether the 'lines' intersect like this:
if (isection_howmany.empty())
cout << "parallel";
else if (isection_howmany.size() == 2)
cout << "collinear";
extendSeg() simply extends the segment in both directions by the given amounts.
Also bear in mind - to support an infinite line arithmetic the point type should also support an infinite value. However here the assumption is that you are looking for a numerical solution!
To solve this problem, I pieced together the following function, but unexpectedly found that it cannot calculate the intersection of line segments, but the intersection of lines.
class Solution {
typedef complex<double> point;
#define x real()
#define y imag()
struct LinePara
{
double k;
double b;
};
LinePara getLinePara(float x1, float y1, float x2, float y2)
{
LinePara ret;
double m = x2 - x1;
if (m == 0)
{
ret.k = 1000.0;
ret.b = y1 - ret.k * x1;
}
else
{
ret.k = (y2 - y1) / (x2 - x1);
ret.b = y1 - ret.k * x1;
}
return ret;
}
struct line {
double a, b, c;
};
const double EPS = 1e-6;
double det(double a, double b, double c, double d) {
return a * d - b * c;
}
line convertLineParaToLine(LinePara s)
{
return line{ s.k,-1,s.b };
}
bool intersect(line m, line n, point& res) {
double zn = det(m.a, m.b, n.a, n.b);
if (abs(zn) < EPS)
return false;
res.real(-det(m.c, m.b, n.c, n.b) / zn);
res.imag(-det(m.a, m.c, n.a, n.c) / zn);
return true;
}
bool parallel(line m, line n) {
return abs(det(m.a, m.b, n.a, n.b)) < EPS;
}
bool equivalent(line m, line n) {
return abs(det(m.a, m.b, n.a, n.b)) < EPS
&& abs(det(m.a, m.c, n.a, n.c)) < EPS
&& abs(det(m.b, m.c, n.b, n.c)) < EPS;
}
vector<double> mian(vector<vector<double>> line1, vector<vector<double>> line2)
{
vector<point> points;
points.push_back(point(line1[0][0], line1[0][1]));
points.push_back(point(line1[1][0], line1[1][1]));
points.push_back(point(line2[0][0], line2[0][1]));
points.push_back(point(line2[1][0], line2[1][1]));
line li1 = convertLineParaToLine(getLinePara(line1[0][0], line1[0][1], line1[1][0], line1[1][1]));
line li2 = convertLineParaToLine(getLinePara(line2[0][0], line2[0][1], line2[1][0], line2[1][1]));
point pos;
if (intersect(li1, li2, pos))
{
return{ pos.x ,pos.y };
}
else
{
if (equivalent(li1, li2)) {
if (points[1].x < points[2].x)
{
return vector<double>{ points[1].x, points[1].y };
}
else if (points[1].x > points[2].x)
{
return vector<double>{ points[2].x, points[2].y };
}
else if (points[1].x == points[2].x)
{
if (points[1].y < points[2].y)
{
return vector<double>{ points[1].x, points[1].y };
}
else if (points[1].y > points[2].y)
{
return vector<double>{ points[2].x, points[2].y };
}
}
else
{
return vector<double>{ points[2].x, points[2].y };
}
}
else
{
return {}/* << "平行!"*/;
}
return {};
}
}
public:
vector<double> intersection(vector<int>& start1, vector<int>& end1, vector<int>& start2, vector<int>& end2) {
vector<vector<double>> line1{ {(double)start1[0],(double)start1[1]},{(double)end1[0],(double)end1[1] } };
vector<vector<double>> line2{ {(double)start2[0],(double)start2[1]},{(double)end2[0],(double)end2[1] } };
return mian(line1, line2);
}
};
From there
If I have a simple 2-D matrix with normalized values on x-axis between 0 and 1 and y-axys between 0 and 1, and I have 3 points in this matrix e.g. P1 (x=0.2,y=0.9), P2 (x=0.5,y=0.1) and P3 (x=0.9,y=0.4).
How can I simply calculate a curve thru this points, meaning having a function which is giving me the y for any x.
I now that there are any number of possible curves thru 3 points. But hey, you know what I mean: I want a smooth curve thru it, usable for audio-sample-interpolation, usable for calculation a volume-fade-curve, usable for calculating a monster-walking-path in a game.
Now I have searched the net for this question about 3 days, and I cannot believe that there is no usable solution for this task. All the text dealing about Catmull-rom-Splines, bezier-curves and all that theroretical stuff has all at least one point which doesn't make it for me usable. For example Catmull-Rom-splines need to have a fix distance between the control-points (I would use this code and set the 4. point-y to the 3. point y) :
void CatmullRomSpline(float *x,float *y,float x1,float y1,float x2,float y2,float x3,float y3,float x4,float y4,float u)
{
//x,y are calculated for x1,y1,x2,y2,x3,y3 and x4,y4 if u is the normalized distance (0-1) in relation to the distance between x2 and x3 for my whiched point
float u3,u2,f1,f2,f3,f4;
u3=u*u*u;
u2=u*u;
f1=-0.5f * u3 + u2 -0.5f *u;
f2= 1.5f * u3 -2.5f * u2+1.0f;
f3=-1.5f * u3 +2.0f * u2+0.5f*u;
f4=0.5f*u3-0.5f*u2;
*x=x1*f1+x2*f2+x3*f3+x4*f4;
*y=y1*f1+y2*f2+y3*f3+y4*f4;
}
But I don't see that x1 to x4 have any affect on the calculation of y, so I think x1 to x4 must have the same distance?
...
Or bezier-code doesn't calcuate the curve thru the points. The points (at least the 2. point) seem only to have a force-effect on the line.
typedef struct Point2D
{
double x;
double y;
} Point2D;
class bezier
{
std::vector<Point2D> points;
bezier();
void PushPoint2D( Point2D point );
Point2D GetPoint( double time );
~bezier();
};
void bezier::PushPoint2D(Point2D point)
{
points.push_back(point);
}
Point2D bezier::GetPoint( double x )
{
int i;
Point2D p;
p.x=0;
p.y=0;
if( points.size() == 1 ) return points[0];
if( points.size() == 0 ) return p;
bezier b;
for (i=0;i<(int)points.size()-1;i++)
{
p.x = ( points[i+1].x - points[i].x ) * x + points[i].x;
p.y = ( points[i+1].y - points[i].y ) * x + points[i].y;
if (points.size()<=2) return p;
b.PushPoint2D(p);
}
return b.GetPoint(x);
}
double GetLogicalYAtX(double x)
{
bezier bz;
Point2D p;
p.x=0.2;
p.y=0.9;
bz.PushPoint2D(p);
p.x=0.5;
p.y=0.1;
bz.PushPoint2D(p);
p.x=0.9;
p.y=0.4;
bz.PushPoint2D(p);
p=bz.GetPoint(x);
return p.y;
}
This is better than nothing, but it is 1. very slow (recursive) and 2. as I said doesn't really calculate the line thru the 2. point.
Is there a mathematical brain outside which could help me?
Thank you TOCS (Scott) for providing your code, I will also try it if I have some time. But what I have tested now is the hint by INFACT (answer 3): This "Largrange polynomials" are very very close to what I am searching for:
I have renamed my class bezier to curve, because I have added some code for lagrangeinterpolation. I also have added 3 pictures of graphical presentation what the code is calculation.
In Picture 1 you can see the loose middle point of the old bezier-function.
In Picture 2 you can now see the going thru all-points-result of lagrange interpolation.
In Picture 3 you can see the only problem, or should I say "thing which I also need to be solved" (anyway its the best solution till now): If I move the middle point, the curve to going to fast, to quick to the upper or lower boundaries). I would like it to go more smoothely to the upper and lower. So that it looks more logarithm-function like. So that it doesn't exeed the y-boundaries between 0 and 1 too soon.
Now my code looks like this:
curve::curve(void)
{
}
void curve::PushPoint2D(Point2D point)
{
points.push_back(point);
}
Point2D curve::GetPoint( double x )
{
//GetPoint y for x with bezier-mathematics...
//was the only calculating function in old class "bezier"
//now the class is renamed "curve"
int i;
Point2D p;
p.x=0;
p.y=0;
if( points.size() == 1 ) return points[0];
if( points.size() == 0 ) return p;
curve b;
for (i=0;i<(int)points.size()-1;i++)
{
p.x = ( points[i+1].x - points[i].x ) * x + points[i].x;
p.y = ( points[i+1].y - points[i].y ) * x + points[i].y;
if (points.size()<=2) return p;
b.PushPoint2D(p);
}
return b.GetPoint(x);
}
//THIS IS NEW AND VERY VERY COOL
double curve::LagrangeInterpolation(double x)
{
double y = 0;
for (int i = 0; i <= (int)points.size()-1; i++)
{
double numerator = 1;
double denominator = 1;
for (int c = 0; c <= (int)points.size()-1; c++)
{
if (c != i)
{
numerator *= (x - points[c].x);
denominator *= (points[i].x - points[c].x);
}
}
y += (points[i].y * (numerator / denominator));
}
return y;
}
curve::~curve(void)
{
}
double GetLogicalYAtX(double x)
{
curve cv;
Point2D p;
p.x=0; //always left edge
p.y=y1; //now by var
cv.PushPoint2D(p);
p.x=x2; //now by var
p.y=y2; //now by var
cv.PushPoint2D(p);
p.x=1; //always right edge
p.y=y3; //now by var
cv.PushPoint2D(p);
//p=cv.GetPoint(x);
//return p.y;
return cv.LagrangeInterpolation(x);
}
Do you have any ideas how I could get the new solution a little bit more "soft"? So that I can move the 2. Point in larger areas without the curve going out of boundaries? Thank you
static bezier From3Points(const Point2D &a, const Point2D &b, const Point2D &c)
{
bezier result;
result.PushPoint2D(a);
Point2D middle;
middle.x = 2*b.x - a.x/2 - c.x/2;
middle.y = 2*b.y - a.y/2 - c.y/2;
result.PushPoint2D(middle);
result.PushPoint2D(c);
return result;
}
Untested, but should return a bezier curve where at t=0.5 the curve passes through point 'b'.
Additionally (more untested code ahead), you can calculate your points using bernstein basis polynomials like so.
static int binomialcoefficient (int n, int k)
{
if (k == 0)
return 1;
if (n == 0)
return 0;
int result = 0;
for (int i = 1; i <= k; ++i)
{
result += (n - (k - i))/i;
}
return result;
}
static double bernstein (int v, int n, double t)
{
return binomialcoefficient(v,n) * pow(t,v) * pow(1 - t,n - v);
}
Point2D GetPoint (double t)
{
Point2D result;
result.x = 0;
result.y = 0;
for (int i = 0; i < points.size(); ++i)
{
double coeff = bernstein (i,points.size(),t);
result.x += coeff * points[i].x;
result.y += coeff * points[i].y;
}
return result;
}