我从一组点中发现了许多关于Covex Hull的构造的材料,这基本上是通过定向平面的交点(分别为half-spaces)来界定这些点的任务。
现在我对某种逆任务感兴趣-给定N个半空间,找到定义由半空间的交点创建的凸多面体表面的多边形网格(即顶点和连通性图)。
采用O(N ^ 3)算法的天真的算法:
verts = {}
edges = {}
for i < N:
for j < i: # find intersection lines
edge_ij = intersection(planes[i],planes[j])
for k < i: # trim them by other planes
edge_ij = trim(edge_ij,plane[k])
# store all edges of non-zero lenght
if(edge_ij.length > 0.0):
edges.insert(edge_ij)
# store endpoints, if not known yet
i1 = verts.find( edge_ij.endPoint1 )
if i1:
edge_ij.endPoint1 = verts[i1]
else:
verts.insert(edge_ij.endPoint1)
i2 = verts.find( edge_ij.endPoint2 )
if i2:
edge_ij.endPoint2 = verts[i2]
else:
verts.insert(edge_ij.endPoint2)
编辑:好的,这是我在C ++()中对上述算法的实现
class LineInterval3d{ public:
Vec3d p0,hdir;
double t0,t1;
inline bool fromPlanes( Vec3d& dir1, double c1, Vec3d& dir2, double c2 ){
double s = dir1.dot(dir2);
if( s*s>0.999 ) return false;
double s2=s*s;
double denom = 1/(1-s2);
p0.set_lincomb( (c1-c2*s)*denom, dir1, (c2-c1*s)*denom, dir2 );
hdir.set_cross( dir1, dir2 );
hdir.mul( sqrt(denom) );
infiniteSpan();
return true;
};
inline int trim( Vec3d& dir, double c ){
double s = dir.dot(hdir);
double d = c - dir.dot(p0);
if( s*s<0.0001 ){
//printf( "perpendiculer \n" );
if( d > 0 ){ double t=t0; t0=t1; t1=t; }; // always outside
return 0;
}
double t = d/s;
if( s>0 ){ if(t>t0){t0=t; return -1; }; }
else { if(t<t1){t1=t; return 1; }; }
//printf( "no-trim \n" );
return 0;
//return t1<t0;
};
inline Vec3d infiniteSpan(){ t0 = -1e+300; t1 = 1e+300; }
inline Vec3d endPoint0(){ return p0+hdir*t0; };
inline Vec3d endPoint1(){ return p0+hdir*t1; };
};
// order-agnostic hash for triple of inexes i,j,k
inline uint32_t pack32 ( uint8_t x, uint8_t y, uint8_t z, uint8_t w ){ return x|(((uint32_t)y)<<8)|(((uint32_t)z)<<16)|(((uint32_t)w)<<24);};
inline uint32_t vertIdjk( uint8_t i, uint8_t j, uint8_t k ){
uint8_t t;
if(i>k){t=i; i=k; k=t; }
if(i>j){t=i; i=j; j=t; }
if(j>k){t=j; j=k; k=t; }
return pack32(i,j,k,0);
}
void fromPlanes(int n, Plane3D* planes ){
std::unordered_map<uint32_t,int> verts;
std::vector<int> faceEdges[n];
int nv =0;
for(int i=0; i<n; i++){
// find all edges by itersection of two planes
for(int j=i+1; j<n; j++){
LineInterval3d lij;
if( !lij.fromPlanes( planes[i].normal, planes[i].iso, planes[j].normal, planes[j].iso ) ){
//printf("colinear intersection %i,%i \n", i,j );
continue;
}
MeshEdge eij;
decltype(verts.find(0)) it;
uint32_t iv0,iv1;
eij.verts.a = -1; // DEBUG
eij.verts.b = -1; // DEBUG
// trim the edge by other planes
for( int k=0; k<n; k++ ){
if( (k==i)||(k==j) ){ continue; }
int side = lij.trim( planes[k].normal, planes[k].iso );
if( lij.t0>lij.t1 ){
//printf( " line Vanish (%i,%i,%i) \n", i,j,k );
goto lineVanish;
};
if ( side>0 ){ eij.verts.b = k; }
else if( side<0 ){ eij.verts.a = k; }
}
if( (eij.verts.a<0) or (eij.verts.b<0) ){
//printf( "end not trimmed %i %i \n", eij.verts.a, eij.verts.b );
continue; // DEBUG
}
// try insert endpoint 1
eij.faces.a = i;
eij.faces.b = j;
iv0 = vertIdjk( i, j, eij.verts.a );
it = verts.find(iv0);
if( it == verts.end() ){
verts.insert({iv0,nv});
eij.verts.a = nv;
points.push_back( lij.endPoint0() );
nv++;
}else{
eij.verts.a = it->second;
}
// try insert endpoint 2
iv1 = vertIdjk( i, j, eij.verts.b );
it = verts.find(iv1);
if( it == verts.end() ){
verts.insert({iv1,nv});
eij.verts.b = nv;
points.push_back( lij.endPoint1() );
nv++;
}else{
eij.verts.b = it->second;
}
faceEdges[i].push_back( edges.size() );
faceEdges[j].push_back( edges.size() );
edges.push_back( eij );
lineVanish:
;
}
// order edges to make a polygon
if( faceEdges[i].size()>0 ){
//printf( " face: %i nedges %i \n", i, faceEdges[i].size() );
Polygon * pl = new Polygon();
//int ieo = -1;
int ie = faceEdges[i][0];
int iv = edges[ie].verts.a;
int ivEnd = edges[ie].verts.b;
//printf( "%i(%i,%i)\n", ie, iv, ivEnd );
//for(int iter=0; iter<ne; iter++){
// todo - we can perhaps sort it by faster algorithms but for small number of edges-per-polygon it is not worth it
int ndone=0;
while( iv != ivEnd ){
int ivNew = -1;
int j;
//for( j : faceEdges[i]){
for(int jj=0; jj<faceEdges[i].size(); jj++){
j = faceEdges[i][jj];
if(ie==j) continue;
//if((ie==j)||(ie==ieo)) continue;
if (edges[j].verts.a == iv){ ivNew=edges[j].verts.b; break; }
else if (edges[j].verts.b == iv){ ivNew=edges[j].verts.a; break; };
}
if(ivNew>=0){ // anything was found ?
pl->ipoints.push_back(iv);
pl->iedges .push_back(ie);
//ieo = ie;
ie = j;
//printf( "ie %i %i iv %i->%i \n", ie, j, iv, ivNew );
iv = ivNew;
}else { printf("error: cannot find %i \n", iv ); return; }
if(ndone>=faceEdges[i].size()){ printf("error: cannot close polygon \n", iv ); return; }
ndone++;
}
pl->ipoints.push_back(iv);
pl->iedges .push_back(ie);
polygons.push_back(pl);
}
}
}