You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
753 lines
24 KiB
753 lines
24 KiB
#ifndef VCG_PIVOT_H |
|
#define VCG_PIVOT_H |
|
|
|
#include <vector> |
|
#include <list> |
|
|
|
|
|
#include "vcg/space/index/grid_static_ptr.h" |
|
#include "vcg/complex/algorithms/closest.h" |
|
|
|
namespace vcg { |
|
namespace tri { |
|
|
|
|
|
struct Hinge { |
|
int v0, v1, v2; //v0, v1 represent the Hinge, v2 the other vertex in the face |
|
//this Hinge belongs to |
|
int face; //corresponding face |
|
Point3f center; //center of the sphere touching the face |
|
int count; //test delay touch Hinges. |
|
|
|
//the loops in the front are mantained as a double linked list |
|
std::list<Hinge>::iterator next; |
|
std::list<Hinge>::iterator previous; |
|
|
|
Hinge() {} |
|
Hinge(int _v0, int _v1, int _v2, int _face, Point3f &_center): |
|
v0(_v0), v1(_v1), v2(_v2), |
|
face(_face), center(_center), count(0) { |
|
assert(v0 != v1 && v1 != v2 && v0 != v2); |
|
} |
|
}; |
|
|
|
template <class MESH> |
|
class Pivot { |
|
public: |
|
// typedef CMesh MESH; |
|
typedef GridStaticPtr<typename MESH::VertexType, typename MESH::ScalarType > StaticGrid; |
|
|
|
|
|
|
|
float radius; //default 1 (not meaningful |
|
float mindist; //minimum distance between points in the mesh (% of radius) |
|
float crease; // -0.5 |
|
Box3f box; |
|
|
|
MESH &mesh; |
|
StaticGrid grid; |
|
|
|
/* front Hinges of the mesh: |
|
to expand the front we get the first Hinge |
|
if an Hinge cannot create a new triangle it is marked dead and moved |
|
to the end of the list |
|
the new Hinges are inserted to the back (before dead_begin) */ |
|
|
|
std::list<Hinge> front; |
|
std::list<Hinge> deads; |
|
std::vector<int> nb; //number of fronts a vertex is into, |
|
//this is used for the Visited and Border flags |
|
//but adding topology may not be needed anymode |
|
|
|
|
|
Pivot(MESH &_mesh, float _radius, float _mindist = 0.05, float _crease = -0.5): |
|
mesh(_mesh), radius(_radius), mindist(_mindist), crease(_crease) { |
|
|
|
//Compute bounding box. (this may be passed as a parameter? |
|
for(int i = 0; i < mesh.vert.size(); i++) |
|
box.Add(mesh.vert[i].P()); |
|
|
|
/* we need to enlarge the grid to allow queries from little outside of the box |
|
Someone is a bit lazy... */ |
|
box.Offset(4*radius); |
|
grid.Set(mesh.vert.begin(), mesh.vert.end(), box); |
|
nb.clear(); |
|
nb.resize(mesh.vert.size(), 0); |
|
for(int i = 0; i < mesh.vert.size(); i++) |
|
mesh.vert[i].ClearFlags(); |
|
|
|
} |
|
|
|
|
|
/* select a vertex at random, a small group of nearby vertices and looks |
|
for a sphere that touches 3 and contains none. |
|
Use the center of the box to get a sphere inside (or outside) the model |
|
You may be unlucky... */ |
|
|
|
bool seed(bool outside = true, int start = -1) { |
|
//pick a random point (well...) |
|
if(start == -1) start = rand()%mesh.vert.size(); |
|
|
|
//get a sphere of neighbours |
|
std::vector<int> targets; |
|
std::vector<float> dists; |
|
int n = getInSphere(mesh.vert[start].P(), 2*radius, targets, dists); |
|
if(n < 3) { |
|
//bad luck. we should call seed again (assuming random pick) up to |
|
//some maximum tries. im lazy. |
|
return false; |
|
} |
|
int v0, v1, v2; |
|
bool found = false; |
|
//find a triplet that does not contains any other point |
|
Point3f center; |
|
for(int i = 0; i < n; i++) { |
|
v0 = targets[i]; |
|
CVertex &vv0 = mesh.vert[v0]; |
|
if(vv0.IsD() || vv0.IsB() || vv0.IsV()) continue; |
|
Point3f &p0 = mesh.vert[v0].P(); |
|
Point3f out = (p0 - box.Center()); |
|
if(!outside) out = -out; |
|
|
|
for(int k = i+1; k < n; k++) { |
|
v1 = targets[k]; |
|
CVertex &vv1 = mesh.vert[v1]; |
|
if(vv1.IsD() || vv1.IsB() || vv1.IsV()) continue; |
|
Point3f &p1 = mesh.vert[v1].P(); |
|
if((p1 - p0).Norm() < mindist*radius) continue; |
|
|
|
for(int j = k+1; j < n; j++) { |
|
v2 = targets[j]; |
|
CVertex &vv2 = mesh.vert[v2]; |
|
if(vv2.IsD() || vv2.IsB() || vv2.IsV()) continue; |
|
Point3f &p2 = mesh.vert[v2].P(); |
|
if((p2 - p0).Norm() < mindist*radius) continue; |
|
if((p2 - p1).Norm() < mindist*radius) continue; |
|
Point3f normal = (p1 - p0)^(p2 - p0); |
|
//check normal pointing inside |
|
if(normal * out < 0) continue; |
|
if(!findSphere(p0, p1, p2, center)) continue; |
|
|
|
bool failed = false; |
|
//check no other point inside |
|
for(int t = 0; t < n; t++) { |
|
Point3f &p = mesh.vert[targets[t]].P(); |
|
if((center - p).Norm() <= radius) { |
|
failed = true; |
|
break; |
|
} |
|
} |
|
if(failed) continue; |
|
found = true; |
|
i = k = j = n; |
|
} |
|
} |
|
} |
|
|
|
if(!found) //see bad luck above |
|
return false; |
|
|
|
assert(!front.size()); |
|
//TODO: should i check the Hinges too? |
|
addFace(v0, v1, v2); |
|
|
|
//create the border of the first face |
|
std::list<Hinge>::iterator e = front.end(); |
|
std::list<Hinge>::iterator last; |
|
for(int i = 0; i < 3; i++) { |
|
int v0 = (int)(mesh.face.back().V0(i)); |
|
int v1 = (int)(mesh.face.back().V1(i)); |
|
int v2 = (int)(mesh.face.back().V2(i)); |
|
nb[v0] = 1; |
|
assert(!mesh.vert[v0].IsB()); |
|
mesh.vert[v0].SetB(); |
|
Hinge Hinge(v0, v1, v2, 0, center); |
|
Hinge.previous = e; |
|
e = front.insert(front.begin(), Hinge); |
|
if(i == 0) last = e; |
|
(*Hinge.previous).next = e; |
|
|
|
cluster(v0); |
|
} |
|
|
|
//connect last and first |
|
(*e).next = last; |
|
(*last).previous = e; |
|
return true; |
|
} |
|
|
|
|
|
/* expand the front adding 1 face. Return false on failure (id when |
|
all Hinges are dead returns: |
|
1: added a face |
|
0: added nothing |
|
-1: finished */ |
|
int addFace() { |
|
//We try to seed again |
|
if(!mesh.face.size()) { |
|
for(int i = 0; i < 100; i++) |
|
if(seed()) return 1; |
|
return -1; |
|
} |
|
|
|
if(!front.size()) { |
|
//maybe there are unconnected parts of the mesh: |
|
//find a non D, V, B point and try to seed if failed D it. |
|
for(int i = 0; i < mesh.vert.size();i ++) { |
|
CVertex &v = mesh.vert[i]; |
|
if(v.IsD() || v.IsV() || v.IsB()) continue; |
|
if(seed(true, i)) return 1; |
|
v.SetD(); |
|
} |
|
return -1; |
|
} |
|
|
|
std::list<Hinge>::iterator ei = front.begin(); |
|
Hinge &e = *ei; |
|
Hinge &previous = *e.previous; |
|
Hinge &next = *e.next; |
|
int v0 = e.v0, v1 = e.v1; |
|
|
|
//last triangle missing. or it is the first? |
|
if(0 &&next.next == e.previous) { |
|
|
|
int v[3] = { previous.v0, next.v0, e.v0 }; |
|
int c[3] = { 0, 0, 0 }; |
|
|
|
for(int k = 0; k < 3; k++) { |
|
int vert = v[k]; |
|
nb[vert]--; |
|
if(nb[vert] == 0) { |
|
mesh.vert[vert].SetV(); |
|
mesh.vert[vert].ClearB(); |
|
} |
|
} |
|
assert(previous.previous == e.next); |
|
addFace(previous.v0, next.v0, e.v0); |
|
|
|
front.erase(e.previous); |
|
front.erase(e.next); |
|
front.erase(ei); |
|
|
|
return 1; |
|
} |
|
|
|
int v2; |
|
Point3f center; |
|
|
|
std::vector<int> targets; |
|
bool success = pivot(e, v2, center, targets); |
|
|
|
//if no pivoting move this thing to the end and try again |
|
//or we are trying to connect to the inside of the mesh. BAD. |
|
if(!success || mesh.vert[v2].IsV()) { |
|
killHinge(ei); |
|
return 0; |
|
} |
|
|
|
//does v2 belongs to a front? (and which?) |
|
std::list<Hinge>::iterator touch = touches(v2, ei); |
|
|
|
assert(v2 != v0 && v2 != v1); |
|
|
|
int fn = mesh.face.size(); |
|
if(touch != front.end()) { |
|
|
|
if(!checkHinge(v0, v2) || !checkHinge(v2, v1)) { |
|
killHinge(ei); |
|
return 0; |
|
} |
|
|
|
if(v2 == previous.v0) { |
|
/*touching previous Hinge (we reuse previous) |
|
next |
|
------->v2 -----> v1------> |
|
\ / |
|
\ / |
|
previous \ / e |
|
\ / |
|
v0 */ |
|
|
|
detach(v0); |
|
|
|
previous.v1 = v1; |
|
previous.v2 = v0; |
|
previous.face = fn; |
|
previous.center = center; |
|
|
|
previous.next = e.next; |
|
next.previous = e.previous; |
|
moveBack(e.previous); |
|
|
|
//this checks if we can glue something to e.previous |
|
trovamiunnome(e.previous); |
|
front.erase(ei); |
|
|
|
|
|
} else if(v2 == next.v1) { |
|
/*touching previous Hinge (we reuse next) |
|
previous |
|
------->v0 -----> v2------> |
|
\ / |
|
\ / |
|
\ / next |
|
\ / |
|
v1 */ |
|
|
|
detach(v1); |
|
|
|
next.v0 = v0; |
|
next.v2 = v1; |
|
next.face = fn; |
|
next.center = center; |
|
next.previous = e.previous; |
|
previous.next = e.next; |
|
// moveBack(e.next); |
|
|
|
//this checks if we can glue something to e.previous |
|
trovamiunnome(e.next); |
|
front.erase(ei); |
|
|
|
} else { |
|
|
|
/* this code would delay the joining Hinge to avoid bad situations not used but.. |
|
if(e.count < 2) { |
|
e.count++; |
|
moveBack(ei); |
|
return true; |
|
}*/ |
|
|
|
//touching some loop: split (or merge it is local does not matter. |
|
//like this |
|
/* |
|
left right |
|
<--------v2-<------ |
|
/|\ |
|
/ \ |
|
up / \ down |
|
/ \ |
|
/ V |
|
----v0 - - - > v1--------- |
|
e */ |
|
std::list<Hinge>::iterator left = touch; |
|
std::list<Hinge>::iterator right = (*touch).previous; |
|
std::list<Hinge>::iterator up = ei; |
|
|
|
if(v1 == (*right).v0 || v0 == (*left).v1) { |
|
// cout << "Bad join.\n"; |
|
killHinge(ei); |
|
return 0; |
|
} |
|
|
|
nb[v2]++; |
|
|
|
std::list<Hinge>::iterator down = newHinge(Hinge(v2, v1, v0, fn, center)); |
|
|
|
(*right).next = down; |
|
(*down).previous = right; |
|
|
|
(*down).next = e.next; |
|
next.previous = down; |
|
|
|
(*left).previous = up; |
|
(*up).next = left; |
|
|
|
(*up).v2 = v1; |
|
(*up).v1 = v2; |
|
(*up).face = fn; |
|
(*up).center = center; |
|
moveBack(ei); |
|
} |
|
|
|
|
|
|
|
} else { |
|
assert(!mesh.vert[v2].IsB()); //fatal error! a new point is already a border? |
|
|
|
/* adding a new vertex |
|
|
|
v2 |
|
/|\ |
|
/ \ |
|
up / \ down |
|
/ \ |
|
/ V |
|
----v0 - - - > v1--------- */ |
|
cluster(v2); |
|
nb[v2]++; |
|
mesh.vert[v2].SetB(); |
|
std::list<Hinge>::iterator down = newHinge(Hinge(v2, v1, v0, fn, center)); |
|
(*down).previous = ei; |
|
(*down).next = e.next; |
|
next.previous = down; |
|
|
|
e.v2 = v1; |
|
e.v1 = v2; |
|
e.face = fn; |
|
e.center = center; |
|
e.next = down; |
|
moveBack(ei); |
|
} |
|
addFace(v0, v2, v1); |
|
return 1; |
|
} |
|
|
|
|
|
|
|
/* return new vertex and the center of the new sphere pivoting from Hinge |
|
if the vertex belongs to another Hinge, touch points to it. */ |
|
bool pivot(Hinge &Hinge, int &candidate, Point3f &end_pivot, std::vector<int> &targets) { |
|
Point3f v0 = mesh.vert[Hinge.v0].P(); |
|
Point3f v1 = mesh.vert[Hinge.v1].P(); |
|
Point3f v2 = mesh.vert[Hinge.v2].P(); |
|
/* TODO why using the face normals everything goes wrong? should be |
|
exactly the same................................................ |
|
Check if the e.face is correct. |
|
Point3f &normal = mesh.face[Hinge.face].N(); |
|
*/ |
|
|
|
Point3f normal = ((v1 - v0)^(v2 - v0)).Normalize(); |
|
|
|
Point3f middle = (v0 + v1)/2; |
|
Point3f start_pivot = Hinge.center - middle; |
|
Point3f axis = (v1 - v0); |
|
|
|
float axis_len = axis.SquaredNorm(); |
|
if(axis_len > 4*radius*radius) return false; |
|
axis.Normalize(); |
|
|
|
// r is the radius of the thorus of all possible spheres passing throug v0 and v1 |
|
float r = sqrt(radius*radius - axis_len/4); |
|
|
|
|
|
std::vector<float> dists; |
|
getInSphere(middle, r + radius, targets, dists); |
|
|
|
if(targets.size() == 0) return false; //this really would be strange but one never knows. |
|
|
|
candidate = -1; |
|
float minangle = 0; |
|
Point3f center; //to be computed for each sample |
|
for(int i = 0; i < targets.size(); i++) { |
|
int id = targets[i]; |
|
|
|
if(id == Hinge.v0 || id == Hinge.v1 || id == Hinge.v2) continue; |
|
|
|
if(mesh.vert[id].IsD()) { |
|
continue; |
|
} |
|
|
|
|
|
Point3f p = mesh.vert[id].P(); |
|
|
|
/* Prevent 360 Hinges, also often reject ~ 50% points */ |
|
Point3f n = ((p - v0)^(v1 - v0)).Normalize(); |
|
if(n * normal < -0.5) { |
|
continue; |
|
} |
|
|
|
|
|
/* Find the sphere through v0, p, v1 (store center on end_pivot */ |
|
if(!findSphere(v0, p, v1, center)) { |
|
continue; |
|
} |
|
|
|
/* Angle between old center and new center */ |
|
float alpha = angle(start_pivot, center - middle, axis); |
|
|
|
/* adding a small bias to already chosen vertices. |
|
doesn't solve numerical problems, but helps. */ |
|
if(mesh.vert[id].IsB()) alpha -= 0.001; |
|
|
|
/* Sometimes alpha might be little less then M_PI while it should be 0, |
|
by numerical errors: happens for example pivoting |
|
on the diagonal of a square. */ |
|
|
|
if(alpha > 2*M_PI - 0.8) { |
|
// Angle between old center and new *point* |
|
//TODO is this really overshooting? shouldbe enough to alpha -= 2*M_PI |
|
Point3f proj = p - axis * (axis * p - axis * middle); |
|
float beta = angle(start_pivot, proj - middle, axis); |
|
|
|
if(alpha > beta) alpha -= 2*M_PI; |
|
} |
|
if(candidate == -1 || alpha < minangle) { |
|
candidate = id; |
|
minangle = alpha; |
|
end_pivot = center; |
|
} |
|
} |
|
//found no point suitable. |
|
if(candidate == -1) { |
|
return false; |
|
} |
|
|
|
assert(candidate != Hinge.v0 && candidate != Hinge.v1); |
|
return true; |
|
} |
|
|
|
private: |
|
//front management: |
|
//Add a new Hinge to the back of the queue |
|
std::list<Hinge>::iterator newHinge(Hinge e) { |
|
return front.insert(front.end(), e); |
|
} |
|
//move an Hinge among the dead ones |
|
void killHinge(std::list<Hinge>::iterator e) { |
|
deads.splice(deads.end(), front, e); |
|
} |
|
|
|
//move an Hinge to the back of the queue |
|
void moveBack(std::list<Hinge>::iterator e) { |
|
front.splice(front.end(), front, e); |
|
} |
|
|
|
void moveFront(std::list<Hinge>::iterator e) { |
|
front.splice(front.begin(), front, e); |
|
} |
|
bool checkHinge(int v0, int v1) { |
|
int tot = 0; |
|
//HACK to speed up things until i can use a seach structure |
|
int i = mesh.face.size() - 2*(front.size()); |
|
// i = 0; |
|
if(i < 0) i = 0; |
|
for(; i < mesh.face.size(); i++) { |
|
CFace &f = mesh.face[i]; |
|
for(int k = 0; k < 3; k++) { |
|
if(v1== (int)f.V(k) && v0 == (int)f.V((k+1)%3)) ++tot; |
|
else if(v0 == (int)f.V(k) && v1 == (int)f.V((k+1)%3)) { //orientation non constistent |
|
return false; |
|
} |
|
} |
|
if(tot >= 2) { //non manifold |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
|
|
void Pivot::cluster(int v) { |
|
/* clean up too close points */ |
|
std::vector<int> targets; |
|
std::vector<float> dists; |
|
getInSphere(mesh.vert[v].P(), mindist*radius, targets, dists); |
|
|
|
for(int i = 0; i < targets.size(); i++) { |
|
int id = targets[i]; |
|
if(id == v) continue; |
|
|
|
CVertex &v = mesh.vert[id]; |
|
if(v.IsD() || v.IsV() || v.IsB()) continue; |
|
v.SetD(); |
|
} |
|
|
|
} |
|
|
|
void Pivot::trovamiunnome(std::list<Hinge>::iterator e) { |
|
if(glue((*e).previous, e)) return; |
|
glue(e, (*e).next); |
|
} |
|
|
|
//glue toghether a and b (where a.next = b |
|
bool Pivot::glue(std::list<Hinge>::iterator a, std::list<Hinge>::iterator b) { |
|
if((*a).v0 != (*b).v1) return false; |
|
|
|
std::list<Hinge>::iterator previous = (*a).previous; |
|
std::list<Hinge>::iterator next = (*b).next; |
|
(*previous).next = next; |
|
(*next).previous = previous; |
|
detach((*a).v1); |
|
detach((*a).v0); |
|
front.erase(a); |
|
front.erase(b); |
|
return true; |
|
} |
|
|
|
void Pivot::detach(int v) { |
|
assert(nb[v] > 0); |
|
if(--nb[v] == 0) { |
|
mesh.vert[v].SetV(); |
|
mesh.vert[v].ClearB(); |
|
} |
|
} |
|
|
|
|
|
/* compute angle from p to q, using axis for orientation */ |
|
float angle(Point3f p, Point3f q, Point3f &axis) { |
|
p.Normalize(); |
|
q.Normalize(); |
|
Point3f vec = p^q; |
|
float angle = acos(p*q); |
|
if(vec*axis < 0) angle = -angle; |
|
if(angle < 0) angle += 2*M_PI; |
|
return angle; |
|
} |
|
/* add a new face. compute normals. */ |
|
void addFace(int a, int b, int c) { |
|
CFace face; |
|
face.V(0) = (CVertex *)a; |
|
face.V(1) = (CVertex *)b; |
|
face.V(2) = (CVertex *)c; |
|
Point3f &p0 = mesh.vert[a].P(); |
|
Point3f &p1 = mesh.vert[b].P(); |
|
Point3f &p2 = mesh.vert[c].P(); |
|
face.N() = ((p1 - p0)^(p2 - p0)).Normalize(); |
|
|
|
mesh.face.push_back(face); |
|
mesh.fn++; |
|
} |
|
|
|
|
|
/* intersects segment [v0, v1] with the sphere of radius radius. */ |
|
bool intersect(int v0, int v1, Point3f ¢er) { |
|
Point3f m = mesh.vert[v1].P() - mesh.vert[v0].P(); |
|
float t = m*(center - mesh.vert[v0].P()); |
|
if(t < 0) return false; |
|
if(t > m*m) return false; |
|
return true; |
|
} |
|
|
|
|
|
float distance(int v0, int v1, Point3f ¢er) { |
|
Point3f m = mesh.vert[v1].P() - mesh.vert[v0].P(); |
|
float t = m*(center - mesh.vert[v0].P())/(m*m); |
|
Point3f p = mesh.vert[v0].P() + m*t; |
|
return (p - center).Norm(); |
|
} |
|
|
|
|
|
/* return all point in a given ball, notice as i want the index |
|
of the vertices not the pointers... this may change in future */ |
|
unsigned int getInSphere(vcg::Point3f &p, float distance, |
|
std::vector<int> &results, |
|
std::vector<float> &dists) { |
|
std::vector<CVertex *> ptr; |
|
std::vector<Point3f> points; |
|
int n = vcg::tri::GetInSphereVertex(mesh, grid, p, distance, ptr, dists, points); |
|
for(int i = 0; i < ptr.size(); i++) |
|
results.push_back(ptr[i] - &(mesh.vert[0])); |
|
return n; |
|
} |
|
|
|
|
|
|
|
/* returns the sphere touching p0, p1, p2 of radius r such that |
|
the normal of the face points toward the center of the sphere */ |
|
bool findSphere(Point3f &p0, Point3f &p1, Point3f &p2, Point3f ¢er) { |
|
Point3f q1 = p1 - p0; |
|
Point3f q2 = p2 - p0; |
|
|
|
Point3f up = q1^q2; |
|
float uplen = up.Norm(); |
|
|
|
//the three points are aligned |
|
if(uplen < 0.001*q1.Norm()*q2.Norm()) return false; |
|
up /= uplen; |
|
|
|
|
|
float a11 = q1*q1; |
|
float a12 = q1*q2; |
|
float a22 = q2*q2; |
|
|
|
float m = 4*(a11*a22 - a12*a12); |
|
float l1 = 2*(a11*a22 - a22*a12)/m; |
|
float l2 = 2*(a11*a22 - a12*a11)/m; |
|
|
|
center = q1*l1 + q2*l2; |
|
float circle_r = center.Norm(); |
|
if(circle_r > radius) return false; //need too big a sphere |
|
|
|
float height = sqrt(radius*radius - circle_r*circle_r); |
|
center += p0 + up*height; |
|
|
|
return true; |
|
} |
|
std::list<Hinge>::iterator touches(int v, std::list<Hinge>::iterator e) { |
|
//TODO what happens when it touches more than one front? |
|
//might still work. |
|
|
|
std::list<Hinge>::iterator touch = front.end(); |
|
if(mesh.vert[v].IsB()) { |
|
//test nearby Hinges: it is faster |
|
std::list<Hinge>::iterator p = e; |
|
p = (*e).previous; |
|
if(v == (*p).v0) return p; |
|
e = (*e).next; |
|
if(v == (*e).v0) return e; |
|
|
|
p = (*p).previous; |
|
if(v == (*p).v0) return p; |
|
e = (*e).next; |
|
if(v == (*e).v0) return e; |
|
|
|
//test all. sigh. |
|
|
|
for(std::list<Hinge>::iterator k = front.begin(); k != front.end(); k++) { |
|
if(v == (*k).v0) { |
|
touch = k; |
|
break; |
|
} |
|
} |
|
for(std::list<Hinge>::iterator k = deads.begin(); k != deads.end(); k++) { |
|
if(v == (*k).v0) { |
|
touch = k; |
|
break; |
|
} |
|
} |
|
assert(touch != front.end()); |
|
} |
|
|
|
return touch; |
|
} |
|
|
|
|
|
public: |
|
|
|
}; |
|
|
|
|
|
}//namespace |
|
}//namespace |
|
/* CODE FOR PIVOTING IN A TOUCH SITUATION not used now. |
|
|
|
//if touch we want to check the ball could really pivot around that point |
|
if(touch != front.end() && touch != (*Hinge.next).next && touch != Hinge.previous) { |
|
Point3f &hinge = mesh.vert[min].P(); |
|
Point3f target = (*touch).center - hinge; |
|
float d = (target * start_pivot)/(target.Norm()*start_pivot.Norm()); |
|
if(d < -0.8) { |
|
return false; |
|
} |
|
|
|
|
|
|
|
if(d < 0.5) { //they are far enough so test . |
|
Point3f naxis = (target ^ start_pivot).Normalize(); |
|
Point3f d1 = naxis^start_pivot; |
|
Point3f d2 = target^naxis; |
|
|
|
for(int i = 0; i < targets.size(); i++) { |
|
int id = targets[i]; |
|
if(id == Hinge.v0 || id == Hinge.v1 || id == Hinge.v2 || id == min) continue; |
|
if(mesh.vert[id].IsD()) { |
|
continue; |
|
} |
|
|
|
Point3f intruder = mesh.vert[targets[i]].P() - hinge; |
|
float h = intruder*naxis; |
|
if(fabs(h) > radius) continue; |
|
intruder -= naxis*h; |
|
assert(fabs(intruder *naxis) < 0.01); |
|
float off = radius - intruder.Norm(); //(distance from the center ring of the thorus |
|
if(h*h + off*off > radius*radius) continue; //outside of thorus |
|
if(d1*intruder < 0 || d2*intruder < 0) continue; //ouside of sector |
|
cout << "could not pivot while touching;\n"; |
|
return false; |
|
} |
|
|
|
} |
|
}*/ |
|
|
|
|
|
#endif
|
|
|