Cone to box collision - math

I'm looking to implement collision detection between a cone (With a round bottom. So it's basically a slice of a sphere) and a box. I'm not too fussed about it being AABB or OBB because transforming should be simple enough. Every solution I find uses a triangular cone but my cone is more of an "arc" that has an angle and distance.
Is there a simple solution to doing this collision detection? Or is it a case of doing several types of tests? ie. something like getting intersection points on a sphere with r being my cone distance then testing if they intersect within an angle or something?

I was curious and planned to do stuff needed for this in GLSL math style anyway. So here a different approach. Let consider this definition of your cone:
create a set of basic geometry primitives
You need to support points,lines,triangles,convex triangulated mesh,spherical sector (cone).
implement inside test between point and triangle,mesh,cone
for triangle the results of cross between any side and point - side origin should point on the same side of triangle (like normal). If not point is outside.
for convex mesh dot product between point-face origin and face normal pointing out should be <=0 for all faces.
for cone the point should be inside sphere radius and angle between cone axis and point-cone origin should be <= ang. again dot product can be used for this.
implement closest line between basic primitives
this is like finding closest points on each primitive that forms a line. Its similar to perpendicular distance.
point-point its easy as they are the closest line.
point-line can be done using projection of point onto the line (dot product). However you need to bound the result so it is inside line and not extrapolated beond it.
point-triangle can be obtained as minimum of all the circumference lines vs point combinations and perpendicular distance to surface (dot product with triangle normal).
All the other combinations of primitives can be build from these basic ones.
closest line between mesh and cone
simply use closest line between cone sphere center and mesh. If the line lies inside cone shorten it by sphere radius R. This will account all cap interactions.
Then test lines on surface of the cone so sample along its circumference starting on cone sphere center and ending on the outermost circle (edge between cone and cap). You van test also triangles instead if you need better precision.
intersection between mesh and cone
this one is easy just compute closest lien between mesh and cone. And then test if its point on mesh side is inside cone or not.
check the
`bool intersect(convex_mesh m0,spherical_sector s0);`
implementation in the code below.
Here small C++/OpenGL example (using GLSL style math):
//---------------------------------------------------------------------------
//--- GL geometry -----------------------------------------------------------
//---------------------------------------------------------------------------
#ifndef _gl_geometry_h
#define _gl_geometry_h
//---------------------------------------------------------------------------
const float deg=M_PI/180.0;
const float rad=180.0/M_PI;
float divide(float a,float b){ if (fabs(b)<1e-10) return 0.0; else return a/b; }
double divide(double a,double b){ if (fabs(b)<1e-10) return 0.0; else return a/b; }
#include "GLSL_math.h"
#include "List.h"
//---------------------------------------------------------------------------
class point
{
public:
// cfg
vec3 p0;
point() {}
point(point& a) { *this=a; }
~point() {}
point* operator = (const point *a) { *this=*a; return this; }
//point* operator = (const point &a) { ...copy... return this; }
point(vec3 _p0)
{
p0=_p0;
compute();
}
void compute(){};
void draw()
{
glBegin(GL_POINTS);
glVertex3fv(p0.dat);
glEnd();
}
};
//---------------------------------------------------------------------------
class axis
{
public:
// cfg
vec3 p0,dp;
axis() {}
axis(axis& a) { *this=a; }
~axis() {}
axis* operator = (const axis *a) { *this=*a; return this; }
//axis* operator = (const axis &a) { ...copy... return this; }
axis(vec3 _p0,vec3 _dp)
{
p0=_p0;
dp=_dp;
compute();
}
void compute()
{
dp=normalize(dp);
}
void draw()
{
vec3 p; p=p0+100.0*dp;
glBegin(GL_LINES);
glVertex3fv(p0.dat);
glVertex3fv(p .dat);
glEnd();
}
};
//---------------------------------------------------------------------------
class line
{
public:
// cfg
vec3 p0,p1;
// computed
float l;
vec3 dp;
line() {}
line(line& a) { *this=a; }
~line() {}
line* operator = (const line *a) { *this=*a; return this; }
//line* operator = (const line &a) { ...copy... return this; }
line(vec3 _p0,vec3 _p1)
{
p0=_p0;
p1=_p1;
compute();
}
void swap()
{
vec3 p=p0; p0=p1; p1=p;
}
void compute()
{
dp=p1-p0;
l=length(dp);
}
void draw()
{
glBegin(GL_LINES);
glVertex3fv(p0.dat);
glVertex3fv(p1.dat);
glEnd();
}
};
//---------------------------------------------------------------------------
class triangle
{
public:
// cfg
vec3 p0,p1,p2;
// computed
vec3 n;
triangle() {}
triangle(triangle& a) { *this=a; }
~triangle() {}
triangle* operator = (const triangle *a) { *this=*a; return this; }
//triangle* operator = (const triangle &a) { ...copy... return this; }
triangle(vec3 _p0,vec3 _p1,vec3 _p2)
{
p0=_p0;
p1=_p1;
p2=_p2;
compute();
}
void swap()
{
vec3 p=p1; p1=p2; p2=p;
n=-n;
}
void compute()
{
n=normalize(cross(p1-p0,p2-p1));
}
void draw()
{
glBegin(GL_TRIANGLES);
glNormal3fv(n.dat);
glVertex3fv(p0.dat);
glVertex3fv(p1.dat);
glVertex3fv(p2.dat);
glEnd();
}
};
//---------------------------------------------------------------------------
class convex_mesh
{
public:
// cfg
List<triangle> tri;
// computed
vec3 p0; // center
convex_mesh() { tri.num=0; }
convex_mesh(convex_mesh& a) { *this=a; }
~convex_mesh() {}
convex_mesh* operator = (const convex_mesh *a) { *this=*a; return this; }
//convex_mesh* operator = (const convex_mesh &a) { ...copy... return this; }
void init_box(vec3 _p0,vec3 _u,vec3 _v,vec3 _w) // center, half sizes
{
const vec3 p[8]=
{
_p0-_u+_v-_w,
_p0+_u+_v-_w,
_p0+_u-_v-_w,
_p0-_u-_v-_w,
_p0-_u-_v+_w,
_p0+_u-_v+_w,
_p0+_u+_v+_w,
_p0-_u+_v+_w,
};
const int ix[36]=
{
0,1,2,0,2,3,
4,5,6,4,6,7,
3,2,5,3,5,4,
2,1,6,2,6,5,
1,0,7,1,7,6,
0,3,4,0,4,7,
};
tri.num=0;
for (int i=0;i<36;i+=3) tri.add(triangle(p[ix[i+0]],p[ix[i+1]],p[ix[i+2]]));
compute();
}
void compute()
{
int i,n;
p0=vec3(0.0,0.0,0.0);
if (!tri.num) return;
for (i=0,n=0;i<tri.num;i++,n+=3)
{
p0+=tri.dat[i].p0;
p0+=tri.dat[i].p1;
p0+=tri.dat[i].p2;
} p0/=float(n);
for (i=0;i<tri.num;i++)
if (dot(tri.dat[i].p0-p0,tri.dat[i].n)<0.0)
tri.dat[i].swap();
}
void draw()
{
int i;
glBegin(GL_TRIANGLES);
for (i=0;i<tri.num;i++) tri.dat[i].draw();
glEnd();
}
};
//---------------------------------------------------------------------------
class spherical_sector
{
public:
// cfg
vec3 p0,p1;
float ang;
// computed
vec3 dp;
float r,R;
spherical_sector() {}
spherical_sector(spherical_sector& a) { *this=a; }
~spherical_sector() {}
spherical_sector* operator = (const spherical_sector *a) { *this=*a; return this; }
//spherical_sector* operator = (const spherical_sector &a) { ...copy... return this; }
spherical_sector(vec3 _p0,vec3 _p1,float _ang)
{
p0=_p0;
p1=_p1;
ang=_ang;
compute();
}
void compute()
{
dp=p1-p0;
R=length(dp);
r=R*tan(ang);
}
void draw()
{
const int N=32;
const int M=16;
vec3 pnt[M][N]; // points
vec3 n0[N]; // normals for cine
vec3 n1[M][N]; // normals for cap
int i,j;
float a,b,da,db,ca,sa,cb,sb;
vec3 q,u,v,w;
// basis vectors
w=normalize(dp); u=vec3(1.0,0.0,0.0);
if (fabs(dot(u,w))>0.75) u=vec3(0.0,1.0,0.0);
v=cross(u,w);
u=cross(v,w);
u=normalize(u);
v=normalize(v);
// compute tables
da=2.0*M_PI/float(N-1);
db=ang/float(M-1);
for (a=0.0,i=0;i<N;i++,a+=da)
{
ca=cos(a);
sa=sin(a);
n0[i]=u*ca+v*sa;
for (b=0.0,j=0;j<M;j++,b+=db)
{
cb=cos(b);
sb=sin(b);
q=vec3(ca*sb,sa*sb,cb);
pnt[j][i]=p0+((q.x*u+q.y*v+q.z*w)*R);
n1[j][i]=normalize(pnt[j][i]);
}
}
// render
glBegin(GL_TRIANGLES);
for (i=1,j=M-1;i<N;i++)
{
glNormal3fv(n0[i].dat); // p0 should have average 0.5*(n0[i]+n0[i-1]) as nomal
glVertex3fv(p0.dat);
glVertex3fv(pnt[j][i+0].dat);
glNormal3fv(n0[i-1].dat);
glVertex3fv(pnt[j][i-1].dat);
glNormal3fv( n1[0][0].dat);
glVertex3fv(pnt[0][0].dat);
glNormal3fv( n1[1][i-1].dat);
glVertex3fv(pnt[1][i-1].dat);
glNormal3fv( n1[1][i+0].dat);
glVertex3fv(pnt[1][i+0].dat);
}
glEnd();
glBegin(GL_QUADS);
for (i=0;i<N;i++)
for (j=2;j<M;j++)
{
glNormal3fv( n1[j-1][i+0].dat);
glVertex3fv(pnt[j-1][i+0].dat);
glNormal3fv( n1[j-1][i-1].dat);
glVertex3fv(pnt[j-1][i-1].dat);
glNormal3fv( n1[j+0][i-1].dat);
glVertex3fv(pnt[j+0][i-1].dat);
glNormal3fv( n1[j+0][i+0].dat);
glVertex3fv(pnt[j+0][i+0].dat);
}
glEnd();
}
};
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
bool inside(point p0,triangle t0);
bool inside(point p0,convex_mesh m0);
bool inside(point p0,spherical_sector s0);
//---------------------------------------------------------------------------
line closest(point p0,axis a0);
line closest(point p0,line l0);
line closest(point p0,triangle t0);
line closest(point p0,convex_mesh m0);
//---------------------------------------------------------------------------
line closest(axis a0,point p0);
line closest(axis a0,axis a1);
line closest(axis a0,line l1);
line closest(axis a0,triangle t0);
line closest(axis a0,convex_mesh m0);
//---------------------------------------------------------------------------
line closest(line l0,point p0);
line closest(line l0,axis a0);
line closest(line l0,line l1);
line closest(line l0,triangle t0);
line closest(line l0,convex_mesh m0);
//---------------------------------------------------------------------------
line closest(triangle t0,point p0);
line closest(triangle t0,axis a0);
line closest(triangle t0,line l0);
line closest(triangle t0,triangle t1);
line closest(triangle t0,convex_mesh m0);
//---------------------------------------------------------------------------
line closest(convex_mesh m0,point p0);
line closest(convex_mesh m0,axis a0);
line closest(convex_mesh m0,line l0);
line closest(convex_mesh m0,triangle t0);
line closest(convex_mesh m0,spherical_sector s0);
//---------------------------------------------------------------------------
bool intersect(convex_mesh m0,spherical_sector s0);
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
bool inside(point p0,triangle t0)
{
if (fabs(dot(p0.p0-t0.p0,t0.n))>1e-6) return false;
float d0,d1,d2;
d0=dot(t0.n,cross(p0.p0-t0.p0,t0.p1-t0.p0));
d1=dot(t0.n,cross(p0.p0-t0.p1,t0.p2-t0.p1));
d2=dot(t0.n,cross(p0.p0-t0.p2,t0.p0-t0.p2));
if (d0*d1<-1e-6) return false;
if (d0*d2<-1e-6) return false;
if (d1*d2<-1e-6) return false;
return true;
}
bool inside(point p0,convex_mesh m0)
{
for (int i=0;i<m0.tri.num;i++)
if (dot(p0.p0-m0.tri.dat[i].p0,m0.tri.dat[i].n)>0.0)
return false;
return true;
}
bool inside(point p0,spherical_sector s0)
{
float t,l;
vec3 u;
u=p0.p0-s0.p0;
l=length(u);
if (l>s0.R) return false;
t=divide(dot(u,s0.dp),(l*s0.R));
if (t<cos(s0.ang)) return false;
return true;
}
//---------------------------------------------------------------------------
line closest(point p0,axis a0){ return line(p0.p0,a0.p0+(a0.dp*dot(p0.p0-a0.p0,a0.dp))); }
line closest(point p0,line l0)
{
float t=dot(p0.p0-l0.p0,l0.dp);
if (t<0.0) t=0.0;
if (t>1.0) t=1.0;
return line(p0.p0,l0.p0+(l0.dp*t));
}
line closest(point p0,triangle t0)
{
float t;
point p;
line cl,ll;
cl.l=1e300;
t=dot(p0.p0-t0.p0,t0.n); p=p0.p0-t*t0.n; if ((fabs(t)>1e-6)&&(inside(p,t0))){ ll=line(p0.p0,p.p0); if (cl.l>ll.l) cl=ll; }
ll=closest(p0,line(t0.p0,t0.p1)); if (cl.l>ll.l) cl=ll;
ll=closest(p0,line(t0.p1,t0.p2)); if (cl.l>ll.l) cl=ll;
ll=closest(p0,line(t0.p2,t0.p0)); if (cl.l>ll.l) cl=ll;
return cl;
}
line closest(point p0,convex_mesh m0)
{
int i;
line cl,ll;
cl=line(vec3(0.0,0.0,0.0),vec3(0.0,0.0,0.0)); cl.l=1e300;
for (i=0;i<m0.tri.num;i++)
{
ll=closest(p0,m0.tri.dat[i]);
if (cl.l>ll.l) cl=ll;
}
return cl;
}
//---------------------------------------------------------------------------
line closest(axis a0,point p0){ line cl; cl=closest(p0,a0); cl.swap(); return cl; }
line closest(axis a0,axis a1)
{
vec3 u=a0.dp;
vec3 v=a1.dp;
vec3 w=a0.p0-a1.p0;
float a=dot(u,u); // always >= 0
float b=dot(u,v);
float c=dot(v,v); // always >= 0
float d=dot(u,w);
float e=dot(v,w);
float D=a*c-b*b; // always >= 0
float t0,t1;
// compute the line parameters of the two closest points
if (D<1e-6) // the lines are almost parallel
{
t0=0.0;
t1=(b>c ? d/b : e/c); // use the largest denominator
}
else{
t0=(b*e-c*d)/D;
t1=(a*e-b*d)/D;
}
return line(a0.p0+(a0.dp*t0),a1.p0+(a1.dp*t1));
}
line closest(axis a0,line l1)
{
vec3 u=a0.dp;
vec3 v=l1.dp;
vec3 w=a0.p0-l1.p0;
float a=dot(u,u); // always >= 0
float b=dot(u,v);
float c=dot(v,v); // always >= 0
float d=dot(u,w);
float e=dot(v,w);
float D=a*c-b*b; // always >= 0
float t0,t1;
// compute the line parameters of the two closest points
if (D<1e-6) // the lines are almost parallel
{
t0=0.0;
t1=(b>c ? d/b : e/c); // use the largest denominator
}
else{
t0=(b*e-c*d)/D;
t1=(a*e-b*d)/D;
}
if (t1<0.0) t1=0.0;
if (t1>1.0) t1=1.0;
return line(a0.p0+(a0.dp*t0),l1.p0+(l1.dp*t1));
}
line closest(axis a0,triangle t0)
{
line cl,ll;
cl=closest(a0,line(t0.p0,t0.p1));
ll=closest(a0,line(t0.p1,t0.p2)); if (cl.l>ll.l) cl=ll;
ll=closest(a0,line(t0.p2,t0.p0)); if (cl.l>ll.l) cl=ll;
return cl;
}
line closest(axis a0,convex_mesh m0)
{
int i;
line cl,ll;
cl=line(vec3(0.0,0.0,0.0),vec3(0.0,0.0,0.0)); cl.l=1e300;
for (i=0;i<m0.tri.num;i++)
{
ll=closest(a0,m0.tri.dat[i]);
if (cl.l>ll.l) cl=ll;
}
return cl;
}
//---------------------------------------------------------------------------
line closest(line l0,point p0){ line cl; cl=closest(p0,l0); cl.swap(); return cl; }
line closest(line l0,axis a0) { line cl; cl=closest(a0,l0); cl.swap(); return cl; }
line closest(line l0,line l1)
{
vec3 u=l0.p1-l0.p0;
vec3 v=l1.p1-l1.p0;
vec3 w=l0.p0-l1.p0;
float a=dot(u,u); // always >= 0
float b=dot(u,v);
float c=dot(v,v); // always >= 0
float d=dot(u,w);
float e=dot(v,w);
float D=a*c-b*b; // always >= 0
float t0,t1;
// compute the line parameters of the two closest points
if (D<1e-6) // the lines are almost parallel
{
t0=0.0;
t1=(b>c ? d/b : e/c); // use the largest denominator
}
else{
t0=(b*e-c*d)/D;
t1=(a*e-b*d)/D;
}
if (t0<0.0) t0=0.0;
if (t0>1.0) t0=1.0;
if (t1<0.0) t1=0.0;
if (t1>1.0) t1=1.0;
return line(l0.p0+(l0.dp*t0),l1.p0+(l1.dp*t1));
}
line closest(line l0,triangle t0)
{
float t;
point p;
line cl,ll;
cl.l=1e300;
t=dot(l0.p0-t0.p0,t0.n); p=l0.p0-t*t0.n; if ((fabs(t)>1e-6)&&(inside(p,t0))){ ll=line(l0.p0,p.p0); if (cl.l>ll.l) cl=ll; }
t=dot(l0.p1-t0.p0,t0.n); p=l0.p1-t*t0.n; if ((fabs(t)>1e-6)&&(inside(p,t0))){ ll=line(l0.p1,p.p0); if (cl.l>ll.l) cl=ll; }
ll=closest(l0,line(t0.p0,t0.p1)); if (cl.l>ll.l) cl=ll;
ll=closest(l0,line(t0.p1,t0.p2)); if (cl.l>ll.l) cl=ll;
ll=closest(l0,line(t0.p2,t0.p0)); if (cl.l>ll.l) cl=ll;
return cl;
}
line closest(line l0,convex_mesh m0)
{
int i;
line cl,ll;
cl=line(vec3(0.0,0.0,0.0),vec3(0.0,0.0,0.0)); cl.l=1e300;
for (i=0;i<m0.tri.num;i++)
{
ll=closest(l0,m0.tri.dat[i]);
if (cl.l>ll.l) cl=ll;
}
return cl;
}
//---------------------------------------------------------------------------
line closest(triangle t0,point p0){ line cl; cl=closest(p0,t0); cl.swap(); return cl; }
line closest(triangle t0,axis a0) { line cl; cl=closest(a0,t0); cl.swap(); return cl; }
line closest(triangle t0,line l0) { line cl; cl=closest(l0,t0); cl.swap(); return cl; }
line closest(triangle t0,triangle t1)
{
float t;
point p;
line l0,l1,l2,l3,l4,l5,cl,ll;
l0=line(t0.p0,t0.p1); l3=line(t1.p0,t1.p1);
l1=line(t0.p1,t0.p2); l4=line(t1.p1,t1.p2);
l2=line(t0.p2,t0.p0); l5=line(t1.p2,t1.p0);
cl.l=1e300;
t=dot(t0.p0-t1.p0,t1.n); p=t0.p0-t*t1.n; if ((fabs(t)>1e-6)&&(inside(p,t1))){ ll=line(t0.p0,p.p0); if (cl.l>ll.l) cl=ll; }
t=dot(t0.p1-t1.p0,t1.n); p=t0.p1-t*t1.n; if ((fabs(t)>1e-6)&&(inside(p,t1))){ ll=line(t0.p1,p.p0); if (cl.l>ll.l) cl=ll; }
t=dot(t0.p2-t1.p0,t1.n); p=t0.p2-t*t1.n; if ((fabs(t)>1e-6)&&(inside(p,t1))){ ll=line(t0.p2,p.p0); if (cl.l>ll.l) cl=ll; }
t=dot(t1.p0-t0.p0,t0.n); p=t1.p0-t*t0.n; if ((fabs(t)>1e-6)&&(inside(p,t0))){ ll=line(p.p0,t1.p0); if (cl.l>ll.l) cl=ll; }
t=dot(t1.p1-t0.p0,t0.n); p=t1.p1-t*t0.n; if ((fabs(t)>1e-6)&&(inside(p,t0))){ ll=line(p.p0,t1.p1); if (cl.l>ll.l) cl=ll; }
t=dot(t1.p2-t0.p0,t0.n); p=t1.p2-t*t0.n; if ((fabs(t)>1e-6)&&(inside(p,t0))){ ll=line(p.p0,t1.p2); if (cl.l>ll.l) cl=ll; }
ll=closest(l0,l3); if (cl.l>ll.l) cl=ll;
ll=closest(l0,l4); if (cl.l>ll.l) cl=ll;
ll=closest(l0,l5); if (cl.l>ll.l) cl=ll;
ll=closest(l1,l3); if (cl.l>ll.l) cl=ll;
ll=closest(l1,l4); if (cl.l>ll.l) cl=ll;
ll=closest(l1,l5); if (cl.l>ll.l) cl=ll;
ll=closest(l2,l3); if (cl.l>ll.l) cl=ll;
ll=closest(l2,l4); if (cl.l>ll.l) cl=ll;
ll=closest(l2,l5); if (cl.l>ll.l) cl=ll;
return cl;
}
line closest(triangle t0,convex_mesh m0)
{
int i;
line cl,ll;
cl=line(vec3(0.0,0.0,0.0),vec3(0.0,0.0,0.0)); cl.l=1e300;
for (i=0;i<m0.tri.num;i++)
{
ll=closest(m0.tri.dat[i],t0);
if (cl.l>ll.l) cl=ll;
}
return cl;
}
//---------------------------------------------------------------------------
line closest(convex_mesh m0,point p0) { line cl; cl=closest(p0,m0); cl.swap(); return cl; }
line closest(convex_mesh m0,axis a0) { line cl; cl=closest(a0,m0); cl.swap(); return cl; }
line closest(convex_mesh m0,line l0) { line cl; cl=closest(l0,m0); cl.swap(); return cl; }
line closest(convex_mesh m0,triangle t0){ line cl; cl=closest(t0,m0); cl.swap(); return cl; }
line closest(convex_mesh m0,convex_mesh m1)
{
int i0,i1;
line cl,ll;
cl=line(vec3(0.0,0.0,0.0),vec3(0.0,0.0,0.0)); cl.l=1e300;
for (i0=0;i0<m0.tri.num;i0++)
for (i1=0;i1<m1.tri.num;i1++)
{
ll=closest(m0.tri.dat[i0],m1.tri.dat[i1]);
if (cl.l>ll.l) cl=ll;
}
return cl;
}
line closest(convex_mesh m0,spherical_sector s0)
{
int i,N=18;
float a,da,ca,sa,cb,sb;
vec3 u,v,w,q;
line cl,ll;
// cap
ll=closest(m0,point(s0.p0)); // sphere
if (dot(ll.dp,s0.dp)/(ll.l*s0.R)>=cos(s0.ang)) // cap
ll=line(ll.p0,ll.p1+(ll.dp*s0.R/ll.l));
cl=ll;
// cone
w=normalize(s0.dp); u=vec3(1.0,0.0,0.0);
if (fabs(dot(u,w))>0.75) u=vec3(0.0,1.0,0.0);
v=cross(u,w);
u=cross(v,w);
u=normalize(u)*s0.r;
v=normalize(v)*s0.r;
da=2.0*M_PI/float(N-1);
cb=cos(s0.ang);
sb=sin(s0.ang);
for (a=0.0,i=0;i<N;i++)
{
ca=cos(a);
sa=sin(a);
q=vec3(ca*sb,sa*sb,cb);
q=s0.p0+((q.x*u+q.y*v+q.z*w)*s0.R);
ll=line(s0.p0,q);
ll=closest(m0,ll);
if (cl.l>ll.l) cl=ll;
}
return cl;
}
//---------------------------------------------------------------------------
bool intersect(convex_mesh m0,spherical_sector s0)
{
line cl;
cl=closest(m0,s0);
if (cl.l<=1e-6) return true;
if (inside(cl.p0,s0)) return true;
return false;
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
The GLSL math can be created by this or use GLM or whatever else instead.
I also used mine dynamic list template (just to store the list of triangles in mesh) so:
List<double> xxx; is the same as double xxx[];
xxx.add(5); adds 5 to end of the list
xxx[7] access array element (safe)
xxx.dat[7] access array element (unsafe but fast direct access)
xxx.num is the actual used size of the array
xxx.reset() clears the array and set xxx.num=0
xxx.allocate(100) preallocate space for 100 items
You can use whatever list you have at disposal.
And here test preview testing correctness of this:
The cone is rotating and changing color according to result of intersect test. The yellow line is the closest line result.
I busted this for fun during this weekend so its not yet extensively tested and there still might be unhandled edge cases.
I wanted the code to be as readable as I could so its not optimized at all. Also I did not comment much (as the low level primitives and basic vector math should be obvious enough if not you should learn first before implementing stuff like this)
[edit1]
Looks like I mess some closest test ignoring some edge cases.. It need major rework (apply the fix for all related functions) which I do not have enough time for right now (will update the code once finished) so for now here a quick fix for line vs. line closest test only:
line3D closest(line3D l0,line3D l1)
{
vec3 u=l0.p1-l0.p0;
vec3 v=l1.p1-l1.p0;
vec3 w=l0.p0-l1.p0;
float a=dot(u,u); // always >= 0
float b=dot(u,v);
float c=dot(v,v); // always >= 0
float d=dot(u,w);
float e=dot(v,w);
float D=a*c-b*b; // always >= 0
float t0,t1;
point3D p;
line3D r,rr;
int f; // check distance perpendicular to: 1: l0, 2: l1
f=0; r.l=-1.0;
// compute the line3D parameters of the two closest points
if (D<acc_dot) f=3; // the lines are almost parallel
else{
t0=(b*e-c*d)/D;
t1=(a*e-b*d)/D;
if (t0<0.0){ f|=1; t0=0.0; }
if (t0>1.0){ f|=1; t0=1.0; }
if (t1<0.0){ f|=2; t1=0.0; }
if (t1>1.0){ f|=2; t1=1.0; }
r=line3D(l0.p0+(l0.dp*t0),l1.p0+(l1.dp*t1));
}
// check perpendicular distance from each endpoint marked in f
if (int(f&1))
{
t0=0.0;
t1=divide(dot(l0.p0-l1.p0,l1.dp),l1.l*l1.l);
if (t1<0.0) t1=0.0;
if (t1>1.0) t1=1.0;
rr=line3D(l0.p0+(l0.dp*t0),l1.p0+(l1.dp*t1));
if ((r.l<0.0)||(r.l>rr.l)) r=rr;
t0=1.0;
t1=divide(dot(l0.p1-l1.p0,l1.dp),l1.l*l1.l);
if (t1<0.0) t1=0.0;
if (t1>1.0) t1=1.0;
rr=line3D(l0.p0+(l0.dp*t0),l1.p0+(l1.dp*t1));
if ((r.l<0.0)||(r.l>rr.l)) r=rr;
}
if (int(f&2))
{
t1=0.0;
t0=divide(dot(l1.p0-l0.p0,l0.dp),l0.l*l0.l);
if (t0<0.0) t0=0.0;
if (t0>1.0) t0=1.0;
rr=line3D(l0.p0+(l0.dp*t0),l1.p0+(l1.dp*t1));
if ((r.l<0.0)||(r.l>rr.l)) r=rr;
t1=1.0;
t0=divide(dot(l1.p1-l0.p0,l0.dp),l0.l*l0.l);
if (t0<0.0) t0=0.0;
if (t0>1.0) t0=1.0;
rr=line3D(l0.p0+(l0.dp*t0),l1.p0+(l1.dp*t1));
if ((r.l<0.0)||(r.l>rr.l)) r=rr;
}
return r;
}

Related

CGAL - 2D Delaunay triangulation - remove is not removing - Part 2

This question is continuing of the question
CGAL - 2D Delaunay triangulation - remove is not removing
Trying to solve the problem I decide to use for deletion the point returned on cdt.locate() so there will be not reason for the point do not be removed:
//---TYPEDEFS
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Triangulation_vertex_base_2<K> Vb;
typedef CGAL::Constrained_triangulation_face_base_2<K> Fb;
typedef CGAL::Triangulation_data_structure_2<Vb,Fb> TDS;
typedef CGAL::Exact_predicates_tag Itag;
typedef CGAL::Constrained_Delaunay_triangulation_2<K,TDS,Itag> CDT;
typedef CDT::Point Point;
typedef CDT::Vertex_handle Vertex_handle;
typedef CDT::Vertex_circulator Vertex_circulator;
typedef CDT::Face_handle Face_handle;
...
int RemovePontosDesabilitados(CDT& cdt, tysetPonto3D& SetPonDesabilitados)
{
for (auto& RPonto : SetPonDesabilitados)
{
Point PonRemover(RPonto.x, RPonto.y);
auto Face(cdt.locate(PonRemover));
if (Face != NULL)
{
int C(0);
bool Achou(false);
while (C < 3 && !Achou)
{
if((fabs(Face->vertex(C)->point().x()) - RPonto.x) < 1e-5 &&
(fabs(Face->vertex(C)->point().y()) - RPonto.y) < 1e-5)
{
Achou = true;
}
else
{
C++;
}
}
if (Achou)
{
Point PonRemover(Face->vertex(C)->point().x(), Face->vertex(C)->point().y());
auto Tam = cdt.number_of_vertices();
auto PonIns = cdt.insert(PonRemover);
Tam = cdt.number_of_vertices();
cdt.remove(PonIns);
Tam = cdt.number_of_vertices();
}
}
}
return 1;
}
But on cdt.insert(PonRemover) the point is inserted (See that this point is exactly the same point found in cdt.locate(PonRemover)) and on cdt.remove(PonIns) an exception is raised and the program finished.
Why is this exception being raised?
Thank you

can coroutine be used to solve stack overflow due to too deep recursive function call?

Has anyone tried to use coroutine to solve stack overflow caused by too deep recursive function call? according to the document on coroutines, the coroutine state will be saved on heap instead of on stack, which could have the potential to avoid the limitation imposed by the limited stack size and thus provide a way to solve the stack overflow issue in a generic way. i have tried with some code but it looks like the stack over flow issue persists. anyone has any tips/advice to share? or point me to some tutorial? thanks in advance.
// file main
#include "RecursiveCall.h"
// coroutine
static ReturnObject DoIntegration(Context& ctx, ReturnObject::promise_type* parent, double x_n)
{
double* dummyA = new double[(int)((x_n + 1) * 2)]; // an effort to prevent heap allocation from "optimized out"
co_await AwaitableBase(ctx, parent, x_n);
ctx._dummyVec.push_back(dummyA); // part of the effort to prevent heap allocation from "optimized out"
}
// caller
static double Invoke(Context& ctx, ReturnObject::promise_type* parent, double x_n)
{
auto ret = DoIntegration(ctx, parent, x_n);
std::coroutine_handle<ReturnObject::promise_type> h = ret._coroH;
auto p = h.promise();
while (!h.done())
{
if (p.AreChildrenReady())
{
h();
break;
}
}
return p._area;
}
bool AwaitableBase::await_suspend(std::coroutine_handle<PromiseType> h)
{
_promise = &h.promise();
if (_parent)
{
_parent->RegisterChild(h);
}
if (_x_n <= _ctx._b)
{
_promise->_x_n = 0.0;
_promise->_area = 0.0;
return false;
}
_promise->_area = GetArea(_x_n, _ctx._incr);
double newX = _x_n - _ctx._incr;
_promise->_x_n = newX;
double area = Invoke(_ctx, &h.promise(), newX);
//post calculation
_promise->_area += area;
return true;
}
double CallRecursive(double x0, double x_n, double incr)
{
Context ctx{ x0, incr };
return Invoke(ctx, nullptr, x_n);
}
int main()
{
double x0 = 0.0;
double x_n = 4.5;
double incr = 0.5; // no stackoveflow
//double incr = 0.0015; // stack oveflow
auto area = CallRecursive(x0, x_n, incr);
std::cout << "integrated result: " << area << "\n";
}
// file RecrusiveCall.h
#include <coroutine>
#include <exception>
#include <map>
#include <iostream>
#include <vector>
/* integration certainly can and should be done in a sequencial way in real world. but here is just use it as a simple example of recursive call, so the integration is implemented as a recursive function call and is done from high limit of x to the lower limit */
static double GetY(double x)
{
using CurvePoint = std::pair<double, double>;
constexpr CurvePoint curve[10] = { {0.0, 1.0}, {0.5, 1.2}, {1.0, 1.0}, {1.5, 1.2}, {2.0, 1.0},
{2.5, 1.2}, {3.0, 1.0}, {3.5, 1.2}, {4.0, 1.0}, {4.5, 1.2} };
if (x < curve[0].first || x > curve[9].first)
return 0.0;
CurvePoint newPoint;
const auto p1 = std::lower_bound(&curve[0], &curve[10], x, [](const auto& a, const auto& b) constexpr { return a.first < b; });
// check for special cases: first
const auto p0 = p1 - 1;
return (p1->second - p0->second) * (x - p0->first) / (p1->first - p0->first) + p0->second;
}
static double GetArea(double end, double incr)
{
return (GetY(end) + GetY(end - incr)) * 0.5 * incr;
}
struct Context
{
double _b; // lower limit of the integration range
double _incr; // increment steplength
std::vector<double*> _dummyVec; // effort to prevent heap allocation from being optimzed out
~Context()
{
for (auto p : _dummyVec)
delete p;
}
};
struct ReturnObject
{
struct promise_type
{
using Handle = std::coroutine_handle<promise_type>;
ReturnObject get_return_object() {
return { std::coroutine_handle<promise_type>::from_promise(*this) };
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() {}
void return_void() {}
void RegisterChild(Handle& childH)
{
_children.push_back(childH);
}
bool AreChildrenReady()
{
for (auto c : _children)
{
if (!c.done())
return false;
}
return true;
}
double GetValue() const { return _area; }
std::vector<Handle> _children;
double _area{ 0 };
double _x_n{ 0 };
};
ReturnObject(promise_type::Handle coro) : _coroH(coro)
{
}
operator std::coroutine_handle<promise_type>() const { return _coroH; }
// A coroutine_handle<promise_type> converts to coroutine_handle<>
operator std::coroutine_handle<>() const { return _coroH; }
std::coroutine_handle<promise_type> _coroH;
};
struct AwaitableBase
{
typedef Context Ctx;
using PromiseType = ReturnObject::promise_type; // todo: remove
bool await_ready()
{
return false;
}
bool await_suspend(std::coroutine_handle<PromiseType> h);
PromiseType* await_resume()
{
return _promise;
}
AwaitableBase(Ctx& ctx, PromiseType* parent, double x_n) : _ctx(ctx), _x_n(x_n), _parent(parent)
{
}
~AwaitableBase()
{
}
Ctx& _ctx;
PromiseType* _parent{ nullptr };
PromiseType* _promise{ nullptr };
double _x_n{ 0.0 };
};
no.
the coroutine's stack frame remains allocated. what's pushed to heap (as custom handle struct) is minimal register state plus struct data, to later resume the stack frame.
that is how you can access all local variables after resuming where you left off.

In Qt, how to efficiently determine that a point is inside of one of rectangles?

There is a large set of rectangles. Given a point, I need to quickly find if this point belongs to at least one of the rectangles, and find one.
A space partitioning index would do the trick. You could, in a pinch, add all the rectangles to the graphics scene — it will index them for you and hit tests will have O(log(N)) cost. You’d want a “low cost” item like:
class RectItem : public QGraphicsItem {
QSizeF size;
public:
RectItem(const QRectF &r = {}) : size(r.size()) {
setFlag(ItemHasNoContents);
setPos(r.topLeft());
}
RectItem(const RectItem &o) :
RectItem(o.boundingRect()) {
if (o.scene()) o.scene()->addItem(this);
}
void setRect(const QRect &r) {
prepareGeometryChange();
setPos(r.topLeft());
size = r.size();
}
QRectF boundingRect() const override {
return {{}, size};
}
void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget *) override {}
};
Then:
class HitCache {
QGraphicsScene scene;
std::vector<RectItem> items;
public:
int addRect(const QRectF &rect) {
items.emplace_back(rect);
return items.size()-1;
}
int itemAt(const QPointF &p) const {
auto *item = scene.itemAt(p, {});
if (item)
return item - &items[0];
return -1;
}
QRectF operator[](int i) const {
return items[i].boundingRect();
}
RectItem &get(int i) {
return items[i];
}
};

Bridges in an Undirected Graph

A bridge can only exist between two articulation points or between an articulation point and a node with degree 1.
This is just an observation because if a vertex is articulation point then simply disconnecting from that node will give us bridges only.
Please tell me if this is true or false.
/*A sample file should contain edges as follow
1,2
2,3
4,5
5,3
Output will give the set of bridge nodes
*/
#include<stdio.h>
#include<stdlib.h>
#define MAX 100
typedef struct graph
{
int node;
struct graph *next;
}graph;
void init(int group[],int index[],graph *g[]);
void create_graph(graph *g[],int node1,int node2,int group[],int index[],int max_nodes);
int max(int a,int b);
int min(int a,int b);
void display(graph *g[],int group[],int max_nodes);
int query(graph *g[],int group[],int max_nodes,int first,int second);
void delete_node(graph *g[],int node1,int node2,int group[],int index[],int max_nodes);
int team=0;
int main()
{
char filename[50],ch;
int nodes[MAX][2],temp=0,node1,node2,group[MAX],index[MAX],max_nodes=0,i;
FILE *fp;
graph *g[MAX],*p;
init(group,index,g);
printf("Enter the filename - ");
scanf("%s",filename);
fp=fopen(filename,"r");
if(fp==NULL)
{
printf("File does not exist");
exit(1);
}
while(1)
{
ch=fgetc(fp);
if(isdigit(ch))
temp=temp*10+(ch-48);
else
{
if(ch!=','&&ch!='\n'&&ch!=EOF)
exit(1);
if(ch==',')
node1=temp;
else
{
node2=temp;
if(node1>max_nodes)
max_nodes=node1;
if(node2>max_nodes)
max_nodes=node2;
if(node1!=node2&&node1>0&&node2>0)
create_graph(g,node1,node2,group,index,max_nodes);
}
temp=0;
}
if(ch==EOF)
{
break;
}
}
display(g,group,max_nodes);
temp=0;
fclose(fp);
fp=fopen(filename,"r");
printf("Set of bridges existing - \n\n");
while(1)
{
ch=fgetc(fp);
if(isdigit(ch))
temp=temp*10+(ch-48);
else
{
if(ch!=','&&ch!='\n'&&ch!=EOF)
exit(1);
if(ch==',')
node1=temp;
else
{
node2=temp;
if(node1>max_nodes)
max_nodes=node1;
if(node2>max_nodes)
max_nodes=node2;
if(node1!=node2&&node1>0&&node2>0)
delete_node(g,node1,node2,group,index,max_nodes);
}
temp=0;
}
if(ch==EOF)
{
break;
}
}
return 0;
}
void init(int group[],int index[],graph *g[])
{
int i;
graph *p=NULL;
for(i=0;i<MAX;i++)
{
group[i]=index[i]=0;
g[i]=(graph *)malloc(sizeof(graph));
g[i]->node=0;
g[i]->next=NULL;
}
}
void create_graph(graph *g[],int node1,int node2,int group[],int index[],int max_nodes)
{
int temp_index,other_index,i,minimum;
int stack[MAX],stack_index=0;
graph *p;
p=g[node1];
if(p->next)
{
p=p->next;
while(p)
{
if(p->node==node2)
return;
p=p->next;
}
}
if(g[node1]->node<g[node2]->node)
{
temp_index=node1;
other_index=node2;
}
else
{
temp_index=node2;
other_index=node1;
}
p=g[temp_index];
p->node=p->node+1;
while(p->next!=NULL)
p=p->next;
p->next=(graph *)malloc(sizeof(graph));
p=p->next;
p->node=other_index;
p->next=NULL;
p=g[other_index];
p->node=p->node+1;
while(p->next)
p=p->next;
p->next=(graph *)malloc(sizeof(graph));
p=p->next;
p->node=temp_index;
p->next=NULL;
if(group[temp_index]==group[other_index]&&group[temp_index]==0)
{
{
team++;
group[temp_index]=group[other_index]=team;
}
}
else
{
if(group[temp_index]==0)
{
group[temp_index]=group[other_index];
}
{
minimum=min(group[temp_index],group[other_index]);
group[temp_index]=max(group[temp_index],group[other_index]);
for(i=1;i<=max_nodes;i++)
{
if(group[i]==minimum)
group[i]=max(group[temp_index],group[other_index]);
}
}
}
}
int max(int a,int b)
{
if(a>b)
return a;
return b;
}
int min(int a,int b)
{
if(a<b)
return a;
return b;
}
void display(graph *g[],int group[],int max_nodes)
{
int i;
graph *p;
printf("Graph\n");
printf("Id\tGrp\tNodes\n");
for(i=1;i<=max_nodes;i++)
{
printf("%d\t%d\t%d",i,group[i],g[i]->node);
p=g[i]->next;
while(p)
{
printf(" %d",p->node);
p=p->next;
}
printf("\n");
}
printf("\n\n");
}
int query(graph *g[],int group[],int max_nodes,int first,int second)
{
if(group[first]==group[second])
return 1;
else
return 0;
}
void delete_node(graph *g[],int node1,int node2,int group[],int index[],int max_nodes)
{
graph *p=NULL;
int i,temp,stack_index=0,stack[MAX];
p=g[node1];
while(p->next->node!=node2&&p->next)
p=p->next;
if(p->next)
{
p->next=p->next->next;
g[node1]->node=g[node1]->node-1;
}
p=g[node2];
while(p->next->node!=node1&&p->next)
p=p->next;
if(p->next)
{
p->next=p->next->next;
g[node2]->node=g[node2]->node-1;
}
team++;
group[node2]=team;
stack_index=0;
temp=node2;
p=g[temp]->next;
while(p)
{
stack[stack_index++]=p->node;
p=p->next;
}
for(i=0;i<stack_index;i++)
{
if(group[stack[i]]!=team)
{
group[stack[i]]=team;
p=g[stack[i]]->next;
while(p)
{
stack[stack_index++]=p->node;
p=p->next;
}
}
}
if(query(g,group,max_nodes,node1,node2)==0)
printf("%d %d\n",node1,node2);
create_graph(g,node1,node2,group,index,max_nodes);
}
In an undirected graph, a bridge can only exist between two articulation points. That goes straight from the definitions:
Bridge is an edge, such that removing it from the graph increases the number of connected components.
If you remove any of its endpoints, you therefore increase the number of connected components as well, and that's exactly the definition of articulation point.
I'm not sure what you mean with the part or between an articulation point and a node with out-degree 0. What would out-degree mean in an undirected graph?
Here are nice slides on Tarjan's algorithm, including its usage for detecting bridges and articulation points in graphs.
"The key issue is determining how the reachability relation impacts whether
vertex v is an articulation vertex. There are three cases:
• Root cut-nodes – If the root of the DFS tree has two or more children, it must
be an articulation vertex. No edges from the subtree of the second child can
possibly connect to the subtree of the first child.
• Bridge cut-nodes – If the earliest reachable vertex from v is v, then deleting
the single edge (parent[v], v) disconnects the graph. Clearly parent[v] must
be an articulation vertex, since it cuts v from the graph. Vertex v is also an
articulation vertex unless it is a leaf of the DFS tree. For any leaf, nothing
falls off when you cut it.
• Parent cut-nodes – If the earliest reachable vertex from v is the parent of v,
then deleting the parent must sever v from the tree unless the parent is the
root." - The algorithm design manual by Steve Skiena

Qt: Extract intensity values clipped by a region of interest polygon

Based on a grayscale image and an ordered closed polygon (may be concave), I want to get all grayscale values that lie inside a region of interest polygon (as likewise described in SciPy Create 2D Polygon Mask). What is the most performant realisation of that in Qt 4.8? Endpoint should be some kind of QList<double>. Thanks for your advices.
In addition, is it possible to compute a floating point mask (e.g. 0 for outside the polygon, 0.3 for 30% of the pixel area is within the polygon, 1 for completely inside the polygon)? However, that's just an extra, endpoint would be QPair<double, double> (percentage, value) then. Thanks.
First you need to scanline convert the polygon into horizontal lines -- this is done by getScanLines(). The scanlines are used to sample the image to get all the values within the endpoints of each line, using scanlineScanner().
Below is a complete, standalone and compileable example I had laying around to show that the scanline algorithm is well behaved. It could be tweaked to calculate the fixed point mask as well. So far a point is included if the scanline covers more than half of it in the horizontal extent (due to round()s in scanlineScanner).
Upon startup, resize the window and click around to define consecutive points in the polygon. The polygon you see is rendered solely using the scanlines. For comparison, you can enable the outline of the polygon.
I'm sure the scanline converter could be further optimized. I'm not doing any image sampling, but the scanlineScanner is there to show that it's a trivial thing to do.
// https://github.com/KubaO/stackoverflown/tree/master/questions/scanline-converter-11037252
#define QT_DISABLE_DEPRECATED_BEFORE 5
#include <QtGui>
#if QT_VERSION > QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
#include <algorithm>
typedef QVector<QPointF> PointVector;
typedef QVector<QLineF> LineVector;
// A list of vertex indices in the polygon, sorted ascending in their y coordinate
static QVector<int> sortedVertices(const QPolygonF & poly)
{
Q_ASSERT(! poly.isEmpty());
QVector<int> vertices;
vertices.reserve(poly.size());
for (int i = 0; i < poly.size(); ++i) { vertices << i; }
std::sort(vertices.begin(), vertices.end(), [&](int i, int j){
return poly[i].y() < poly[j].y();
});
return vertices;
}
// Returns point of intersection of infinite lines ref and target
static inline QPointF intersect(const QLineF & ref, const QLineF & target)
{
QPointF p;
target.intersect(ref, &p);
return p;
}
// Allows accessing polygon vertices using an indirect index into a vector of indices.
class VertexAccessor {
const QPolygonF & p;
const QVector<int> & i;
public:
VertexAccessor(const QPolygonF & poly, const QVector<int> & indices) :
p(poly), i(indices) {}
inline QPointF operator[](int ii) const {
return p[i[ii]];
}
inline QPointF prev(int ii) const {
int index = i[ii] - 1;
if (index < 0) index += p.size();
return p[index];
}
inline QPointF next(int ii) const {
int index = i[ii] + 1;
if (index >= p.size()) index -= p.size();
return p[index];
}
};
// Returns a horizontal line scanline rendering of an unconstrained polygon.
// The lines are generated on an integer grid, but this could be modified for any other grid.
static LineVector getScanlines(const QPolygonF & poly)
{
LineVector lines;
if (poly.isEmpty()) return lines;
const QVector<int> indices = sortedVertices(poly);
VertexAccessor vertex{poly, indices};
const QRectF bound = poly.boundingRect();
const auto l = bound.left();
const auto r = bound.right();
int ii = 0;
int yi = qFloor(vertex[0].y());
QList<int> active;
PointVector points;
forever {
const qreal y = yi;
const QLineF sweeper{l, y, r, y};
// Remove vertex from the active list if both lines extending from it are above sweeper
for (int i = 0; i < active.size(); ) {
const int ii = active.at(i);
// Remove vertex
if (vertex.prev(ii).y() < y && vertex.next(ii).y() < y) {
active.removeAt(i);
} else {
++ i;
}
}
// Add new vertices to the active list
while (ii < poly.count() && vertex[ii].y() < y) {
active << ii++;
}
if (ii >= poly.count() && active.isEmpty()) break;
// Generate sorted intersection points
points.clear();
for (auto ii : active) {
const auto a = vertex[ii];
auto b = vertex.prev(ii);
if (b.y() >= y)
points << intersect(sweeper, QLineF{a, b});
b = vertex.next(ii);
if (b.y() >= y)
points << intersect(sweeper, QLineF{a, b});
}
std::sort(points.begin(), points.end(), [](const QPointF & p1, const QPointF & p2){
return p1.x() < p2.x();
});
// Generate horizontal fill segments
for (int i = 0; i < points.size() - 1; i += 2) {
lines << QLineF{points.at(i).x(), y, points.at(i+1).x(), y};
}
yi++;
};
return lines;
}
QVector<int> scanlineScanner(const QImage & image, const LineVector & tess)
{
QVector<int> values;
for (auto & line : tess) {
for (int x = round(line.x1()); x <= round(line.x2()); ++ x) {
values << qGray(image.pixel(x, line.y1()));
}
}
return values;
}
class Ui : public QWidget
{
Q_OBJECT
QPointF lastPoint;
QPolygonF polygon;
LineVector scanlines;
QGridLayout layout{this};
QPushButton reset{"Reset"};
QCheckBox outline{"Outline"};
public:
Ui() {
setMinimumSize(200, 200);
layout.addItem(new QSpacerItem{0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding}, 0, 0, 1, 3);
layout.addWidget(&reset, 1, 0);
layout.addWidget(&outline, 1, 1);
layout.addItem(new QSpacerItem{0, 0, QSizePolicy::Expanding}, 1, 2);
reset.setObjectName("reset");
outline.setObjectName("outline");
QMetaObject::connectSlotsByName(this);
}
protected:
Q_SLOT void on_reset_clicked() {
polygon.clear();
scanlines.clear();
lastPoint = QPointF{};
update();
}
Q_SLOT void on_outline_stateChanged() {
update();
}
void paintEvent(QPaintEvent *) override {
QPainter p{this};
if (false) p.setRenderHint(QPainter::Antialiasing);
p.setPen("cadetblue");
if (!polygon.isEmpty() && scanlines.isEmpty()) {
scanlines = getScanlines(polygon);
qDebug() << "new scanlines";
}
p.drawLines(scanlines);
if (outline.isChecked()) {
p.setPen("orangered");
p.setBrush(Qt::NoBrush);
p.drawPolygon(polygon);
}
if (!lastPoint.isNull()) {
p.setPen("navy");
p.drawEllipse(lastPoint, 3, 3);
}
}
void mousePressEvent(QMouseEvent * ev) override {
lastPoint = ev->posF();
polygon << ev->posF();
scanlines.clear();
update();
}
};
int main(int argc, char** argv)
{
QApplication app{argc, argv};
Ui ui;
ui.show();
return app.exec();
}
#include "main.moc"

Resources