269 lines
5.8 KiB
C++
269 lines
5.8 KiB
C++
// This code is in the public domain -- castanyo@yahoo.es
|
|
|
|
#include "nvmesh.h" // pch
|
|
|
|
#include "Face.h"
|
|
#include "Vertex.h"
|
|
|
|
#include "nvmath/Fitting.h"
|
|
#include "nvmath/Plane.h"
|
|
#include "nvmath/Vector.inl"
|
|
|
|
#include "nvcore/Array.h"
|
|
|
|
|
|
using namespace nv;
|
|
using namespace HalfEdge;
|
|
|
|
/// Get face area.
|
|
float Face::area() const
|
|
{
|
|
float area = 0;
|
|
const Vector3 & v0 = edge->from()->pos;
|
|
|
|
for (ConstEdgeIterator it(edges(edge->next)); it.current() != edge->prev; it.advance())
|
|
{
|
|
const Edge * e = it.current();
|
|
|
|
const Vector3 & v1 = e->vertex->pos;
|
|
const Vector3 & v2 = e->next->vertex->pos;
|
|
|
|
area += length(cross(v1-v0, v2-v0));
|
|
}
|
|
|
|
return area * 0.5f;
|
|
}
|
|
|
|
float Face::parametricArea() const
|
|
{
|
|
float area = 0;
|
|
const Vector2 & v0 = edge->from()->tex;
|
|
|
|
for (ConstEdgeIterator it(edges(edge->next)); it.current() != edge->prev; it.advance())
|
|
{
|
|
const Edge * e = it.current();
|
|
|
|
const Vector2 & v1 = e->vertex->tex;
|
|
const Vector2 & v2 = e->next->vertex->tex;
|
|
|
|
area += triangleArea(v0, v1, v2);
|
|
}
|
|
|
|
return area * 0.5f;
|
|
}
|
|
|
|
|
|
/// Get boundary length.
|
|
float Face::boundaryLength() const
|
|
{
|
|
float bl = 0;
|
|
|
|
for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance())
|
|
{
|
|
const Edge * edge = it.current();
|
|
bl += edge->length();
|
|
}
|
|
|
|
return bl;
|
|
}
|
|
|
|
|
|
/// Get face normal.
|
|
Vector3 Face::normal() const
|
|
{
|
|
Vector3 n(0);
|
|
|
|
const Vertex * vertex0 = NULL;
|
|
|
|
for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance())
|
|
{
|
|
const Edge * edge = it.current();
|
|
nvCheck(edge != NULL);
|
|
|
|
if (vertex0 == NULL)
|
|
{
|
|
vertex0 = edge->vertex;
|
|
}
|
|
else if (edge->next->vertex != vertex0)
|
|
{
|
|
const HalfEdge::Vertex * vertex1 = edge->from();
|
|
const HalfEdge::Vertex * vertex2 = edge->to();
|
|
|
|
const Vector3 & p0 = vertex0->pos;
|
|
const Vector3 & p1 = vertex1->pos;
|
|
const Vector3 & p2 = vertex2->pos;
|
|
|
|
Vector3 v10 = p1 - p0;
|
|
Vector3 v20 = p2 - p0;
|
|
|
|
n += cross(v10, v20);
|
|
}
|
|
}
|
|
|
|
return normalizeSafe(n, Vector3(0, 0, 1), 0.0f);
|
|
|
|
|
|
// Get face points eliminating duplicates.
|
|
/*Array<Vector3> points(4);
|
|
|
|
points.append(m_edge->prev()->from()->pos);
|
|
|
|
for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance())
|
|
{
|
|
const Edge * edge = it.current();
|
|
nvDebugCheck(edge != NULL);
|
|
|
|
const Vector3 & p = edge->from()->pos;
|
|
if (points.back() != p)
|
|
{
|
|
points.append(edge->from()->pos);
|
|
}
|
|
}
|
|
|
|
points.popBack();
|
|
|
|
if (points.count() < 3)
|
|
{
|
|
// Invalid normal.
|
|
return Vector3(0.0f);
|
|
}
|
|
else
|
|
{
|
|
// Compute regular normal.
|
|
Vector3 normal = normalizeSafe(cross(points[1] - points[0], points[2] - points[0]), Vector3(0.0f), 0.0f);
|
|
|
|
#pragma NV_MESSAGE("TODO: make sure these three points are not colinear")
|
|
|
|
if (points.count() > 3)
|
|
{
|
|
// Compute best fitting plane to the points.
|
|
Plane plane = Fit::bestPlane(points.count(), points.buffer());
|
|
|
|
// Adjust normal orientation.
|
|
if (dot(normal, plane.vector()) > 0) {
|
|
normal = plane.vector();
|
|
}
|
|
else {
|
|
normal = -plane.vector();
|
|
}
|
|
}
|
|
|
|
nvDebugCheck(isNormalized(normal));
|
|
return normal;
|
|
}*/
|
|
}
|
|
|
|
Vector3 Face::centroid() const
|
|
{
|
|
Vector3 sum(0.0f);
|
|
uint count = 0;
|
|
|
|
for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance())
|
|
{
|
|
const Edge * edge = it.current();
|
|
sum += edge->from()->pos;
|
|
count++;
|
|
}
|
|
|
|
return sum / float(count);
|
|
}
|
|
|
|
|
|
bool Face::isValid() const
|
|
{
|
|
uint count = 0;
|
|
|
|
for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance())
|
|
{
|
|
const Edge * edge = it.current();
|
|
if (edge->face != this) return false;
|
|
if (!edge->isValid()) return false;
|
|
if (!edge->pair->isValid()) return false;
|
|
count++;
|
|
}
|
|
|
|
if (count < 3) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// Determine if this face contains the given edge.
|
|
bool Face::contains(const Edge * e) const
|
|
{
|
|
for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance())
|
|
{
|
|
if(it.current() == e) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Returns index in this face of the given edge.
|
|
uint Face::edgeIndex(const Edge * e) const
|
|
{
|
|
int i = 0;
|
|
for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance(), i++)
|
|
{
|
|
if(it.current() == e) return i;
|
|
}
|
|
return NIL;
|
|
}
|
|
|
|
|
|
Edge * Face::edgeAt(uint idx)
|
|
{
|
|
int i = 0;
|
|
for(EdgeIterator it(edges()); !it.isDone(); it.advance(), i++) {
|
|
if (i == idx) return it.current();
|
|
}
|
|
return NULL;
|
|
}
|
|
const Edge * Face::edgeAt(uint idx) const
|
|
{
|
|
int i = 0;
|
|
for(ConstEdgeIterator it(edges()); !it.isDone(); it.advance(), i++) {
|
|
if (i == idx) return it.current();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// Count the number of edges in this face.
|
|
uint Face::edgeCount() const
|
|
{
|
|
uint count = 0;
|
|
for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { ++count; }
|
|
return count;
|
|
}
|
|
|
|
// Determine if this is a boundary face.
|
|
bool Face::isBoundary() const
|
|
{
|
|
for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance())
|
|
{
|
|
const Edge * edge = it.current();
|
|
nvDebugCheck(edge->pair != NULL);
|
|
|
|
if (edge->pair->face == NULL) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Count the number of boundary edges in the face.
|
|
uint Face::boundaryCount() const
|
|
{
|
|
uint count = 0;
|
|
for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance())
|
|
{
|
|
const Edge * edge = it.current();
|
|
nvDebugCheck(edge->pair != NULL);
|
|
|
|
if (edge->pair->face == NULL) {
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|