Includes preprocessed Mantaflow source files for both OpenMP and TBB (if OpenMP is not present, TBB files will be used instead). These files come directly from the Mantaflow repository. Future updates to the core fluid solver will take place by updating the files. Reviewed By: sergey, mont29 Maniphest Tasks: T59995 Differential Revision: https://developer.blender.org/D3850
781 lines
23 KiB
C++
781 lines
23 KiB
C++
|
|
|
|
// DO NOT EDIT !
|
|
// This file is generated using the MantaFlow preprocessor (prep generate).
|
|
|
|
/******************************************************************************
|
|
*
|
|
* MantaFlow fluid solver framework
|
|
* Copyright 2011 Tobias Pfaff, Nils Thuerey
|
|
*
|
|
* This program is free software, distributed under the terms of the
|
|
* Apache License, Version 2.0
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Smoothing etc. for meshes
|
|
*
|
|
******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
// Copyright note:
|
|
//
|
|
// These functions (C) Chris Wojtan
|
|
// Long-term goal is to unify with his split&merge codebase
|
|
//
|
|
/******************************************************************************/
|
|
|
|
#include <queue>
|
|
#include <algorithm>
|
|
#include "mesh.h"
|
|
#include "kernel.h"
|
|
#include "edgecollapse.h"
|
|
#include <mesh.h>
|
|
#include <stack>
|
|
|
|
using namespace std;
|
|
|
|
namespace Manta {
|
|
|
|
//! Mesh smoothing
|
|
/*! see Desbrun 99 "Implicit fairing of of irregular meshes using diffusion and curvature flow"*/
|
|
void smoothMesh(Mesh &mesh, Real strength, int steps = 1, Real minLength = 1e-5)
|
|
{
|
|
const Real dt = mesh.getParent()->getDt();
|
|
const Real str = min(dt * strength, (Real)1);
|
|
mesh.rebuildQuickCheck();
|
|
|
|
// calculate original mesh volume
|
|
Vec3 origCM;
|
|
Real origVolume = mesh.computeCenterOfMass(origCM);
|
|
|
|
// temp vertices
|
|
const int numCorners = mesh.numTris() * 3;
|
|
const int numNodes = mesh.numNodes();
|
|
vector<Vec3> temp(numNodes);
|
|
vector<bool> visited(numNodes);
|
|
|
|
for (int s = 0; s < steps; s++) {
|
|
// reset markers
|
|
for (size_t i = 0; i < visited.size(); i++)
|
|
visited[i] = false;
|
|
|
|
for (int c = 0; c < numCorners; c++) {
|
|
const int node = mesh.corners(c).node;
|
|
if (visited[node])
|
|
continue;
|
|
|
|
const Vec3 pos = mesh.nodes(node).pos;
|
|
Vec3 dx(0.0);
|
|
Real totalLen = 0;
|
|
|
|
// rotate around vertex
|
|
set<int> &ring = mesh.get1Ring(node).nodes;
|
|
for (set<int>::iterator it = ring.begin(); it != ring.end(); it++) {
|
|
Vec3 edge = mesh.nodes(*it).pos - pos;
|
|
Real len = norm(edge);
|
|
|
|
if (len > minLength) {
|
|
dx += edge * (1.0 / len);
|
|
totalLen += len;
|
|
}
|
|
else {
|
|
totalLen = 0.0;
|
|
break;
|
|
}
|
|
}
|
|
visited[node] = true;
|
|
temp[node] = pos;
|
|
if (totalLen != 0)
|
|
temp[node] += dx * (str / totalLen);
|
|
}
|
|
|
|
// copy back
|
|
for (int n = 0; n < numNodes; n++)
|
|
if (!mesh.isNodeFixed(n))
|
|
mesh.nodes(n).pos = temp[n];
|
|
}
|
|
|
|
// calculate new mesh volume
|
|
Vec3 newCM;
|
|
Real newVolume = mesh.computeCenterOfMass(newCM);
|
|
|
|
// preserve volume : scale relative to CM
|
|
Real beta;
|
|
#if defined(WIN32) || defined(_WIN32)
|
|
beta = pow((Real)std::abs(origVolume / newVolume), (Real)(1. / 3.));
|
|
#else
|
|
beta = cbrt(origVolume / newVolume);
|
|
#endif
|
|
|
|
for (int n = 0; n < numNodes; n++)
|
|
if (!mesh.isNodeFixed(n))
|
|
mesh.nodes(n).pos = origCM + (mesh.nodes(n).pos - newCM) * beta;
|
|
}
|
|
static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "smoothMesh", !noTiming);
|
|
PyObject *_retval = 0;
|
|
{
|
|
ArgLocker _lock;
|
|
Mesh &mesh = *_args.getPtr<Mesh>("mesh", 0, &_lock);
|
|
Real strength = _args.get<Real>("strength", 1, &_lock);
|
|
int steps = _args.getOpt<int>("steps", 2, 1, &_lock);
|
|
Real minLength = _args.getOpt<Real>("minLength", 3, 1e-5, &_lock);
|
|
_retval = getPyNone();
|
|
smoothMesh(mesh, strength, steps, minLength);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "smoothMesh", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("smoothMesh", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_smoothMesh("", "smoothMesh", _W_0);
|
|
extern "C" {
|
|
void PbRegister_smoothMesh()
|
|
{
|
|
KEEP_UNUSED(_RP_smoothMesh);
|
|
}
|
|
}
|
|
|
|
//! Subdivide and edgecollapse to guarantee mesh with edgelengths between
|
|
//! min/maxLength and an angle below minAngle
|
|
void subdivideMesh(
|
|
Mesh &mesh, Real minAngle, Real minLength, Real maxLength, bool cutTubes = false)
|
|
{
|
|
// gather some statistics
|
|
int edgeSubdivs = 0, edgeCollsAngle = 0, edgeCollsLen = 0, edgeKill = 0;
|
|
mesh.rebuildQuickCheck();
|
|
|
|
vector<int> deletedNodes;
|
|
map<int, bool> taintedTris;
|
|
priority_queue<pair<Real, int>> pq;
|
|
|
|
//////////////////////////////////////////
|
|
// EDGE COLLAPSE //
|
|
// - particles marked for deletation //
|
|
//////////////////////////////////////////
|
|
|
|
for (int t = 0; t < mesh.numTris(); t++) {
|
|
if (taintedTris.find(t) != taintedTris.end())
|
|
continue;
|
|
|
|
// check if at least 2 nodes are marked for delete
|
|
bool k[3];
|
|
int numKill = 0;
|
|
for (int i = 0; i < 3; i++) {
|
|
k[i] = mesh.nodes(mesh.tris(t).c[i]).flags & Mesh::NfKillme;
|
|
if (k[i])
|
|
numKill++;
|
|
}
|
|
if (numKill < 2)
|
|
continue;
|
|
|
|
if (k[0] && k[1])
|
|
CollapseEdge(mesh,
|
|
t,
|
|
2,
|
|
mesh.getEdge(t, 0),
|
|
mesh.getNode(t, 0),
|
|
deletedNodes,
|
|
taintedTris,
|
|
edgeKill,
|
|
cutTubes);
|
|
else if (k[1] && k[2])
|
|
CollapseEdge(mesh,
|
|
t,
|
|
0,
|
|
mesh.getEdge(t, 1),
|
|
mesh.getNode(t, 1),
|
|
deletedNodes,
|
|
taintedTris,
|
|
edgeKill,
|
|
cutTubes);
|
|
else if (k[2] && k[0])
|
|
CollapseEdge(mesh,
|
|
t,
|
|
1,
|
|
mesh.getEdge(t, 2),
|
|
mesh.getNode(t, 2),
|
|
deletedNodes,
|
|
taintedTris,
|
|
edgeKill,
|
|
cutTubes);
|
|
}
|
|
|
|
//////////////////////////////////////////
|
|
// EDGE COLLAPSING //
|
|
// - based on small triangle angle //
|
|
//////////////////////////////////////////
|
|
|
|
if (minAngle > 0) {
|
|
for (int t = 0; t < mesh.numTris(); t++) {
|
|
// we only want to run through the edge list ONCE.
|
|
// we achieve this in a method very similar to the above subdivision method.
|
|
|
|
// if this triangle has already been deleted, ignore it
|
|
if (taintedTris.find(t) != taintedTris.end())
|
|
continue;
|
|
|
|
// first we find the angles of this triangle
|
|
Vec3 e0 = mesh.getEdge(t, 0), e1 = mesh.getEdge(t, 1), e2 = mesh.getEdge(t, 2);
|
|
Vec3 ne0 = e0;
|
|
Vec3 ne1 = e1;
|
|
Vec3 ne2 = e2;
|
|
normalize(ne0);
|
|
normalize(ne1);
|
|
normalize(ne2);
|
|
|
|
// Real thisArea = sqrMag(cross(-e2,e0));
|
|
// small angle approximation says sin(x) = arcsin(x) = x,
|
|
// arccos(x) = pi/2 - arcsin(x),
|
|
// cos(x) = dot(A,B),
|
|
// so angle is approximately 1 - dot(A,B).
|
|
Real angle[3];
|
|
angle[0] = 1.0 - dot(ne0, -ne2);
|
|
angle[1] = 1.0 - dot(ne1, -ne0);
|
|
angle[2] = 1.0 - dot(ne2, -ne1);
|
|
Real worstAngle = angle[0];
|
|
int which = 0;
|
|
if (angle[1] < worstAngle) {
|
|
worstAngle = angle[1];
|
|
which = 1;
|
|
}
|
|
if (angle[2] < worstAngle) {
|
|
worstAngle = angle[2];
|
|
which = 2;
|
|
}
|
|
|
|
// then we see if the angle is too small
|
|
if (worstAngle < minAngle) {
|
|
Vec3 edgevect;
|
|
Vec3 endpoint;
|
|
switch (which) {
|
|
case 0:
|
|
endpoint = mesh.getNode(t, 1);
|
|
edgevect = e1;
|
|
break;
|
|
case 1:
|
|
endpoint = mesh.getNode(t, 2);
|
|
edgevect = e2;
|
|
break;
|
|
case 2:
|
|
endpoint = mesh.getNode(t, 0);
|
|
edgevect = e0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
CollapseEdge(mesh,
|
|
t,
|
|
which,
|
|
edgevect,
|
|
endpoint,
|
|
deletedNodes,
|
|
taintedTris,
|
|
edgeCollsAngle,
|
|
cutTubes);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////
|
|
// EDGE SUBDIVISION //
|
|
//////////////////////
|
|
|
|
Real maxLength2 = maxLength * maxLength;
|
|
for (int t = 0; t < mesh.numTris(); t++) {
|
|
// first we find the maximum length edge in this triangle
|
|
Vec3 e0 = mesh.getEdge(t, 0), e1 = mesh.getEdge(t, 1), e2 = mesh.getEdge(t, 2);
|
|
Real d0 = normSquare(e0);
|
|
Real d1 = normSquare(e1);
|
|
Real d2 = normSquare(e2);
|
|
|
|
Real longest = max(d0, max(d1, d2));
|
|
if (longest > maxLength2) {
|
|
pq.push(pair<Real, int>(longest, t));
|
|
}
|
|
}
|
|
if (maxLength > 0) {
|
|
|
|
while (!pq.empty() && pq.top().first > maxLength2) {
|
|
// we only want to run through the edge list ONCE
|
|
// and we want to subdivide the original edges before we subdivide any newer, shorter edges,
|
|
// so whenever we subdivide, we add the 2 new triangles on the end of the SurfaceTri vector
|
|
// and mark the original subdivided triangles for deletion.
|
|
// when we are done subdividing, we delete the obsolete triangles
|
|
|
|
int triA = pq.top().second;
|
|
pq.pop();
|
|
|
|
if (taintedTris.find(triA) != taintedTris.end())
|
|
continue;
|
|
|
|
// first we find the maximum length edge in this triangle
|
|
Vec3 e0 = mesh.getEdge(triA, 0), e1 = mesh.getEdge(triA, 1), e2 = mesh.getEdge(triA, 2);
|
|
Real d0 = normSquare(e0);
|
|
Real d1 = normSquare(e1);
|
|
Real d2 = normSquare(e2);
|
|
|
|
Vec3 edgevect;
|
|
Vec3 endpoint;
|
|
int which;
|
|
if (d0 > d1) {
|
|
if (d0 > d2) {
|
|
edgevect = e0;
|
|
endpoint = mesh.getNode(triA, 0);
|
|
;
|
|
which = 2; // 2 opposite of edge 0-1
|
|
}
|
|
else {
|
|
edgevect = e2;
|
|
endpoint = mesh.getNode(triA, 2);
|
|
which = 1; // 1 opposite of edge 2-0
|
|
}
|
|
}
|
|
else {
|
|
if (d1 > d2) {
|
|
edgevect = e1;
|
|
endpoint = mesh.getNode(triA, 1);
|
|
which = 0; // 0 opposite of edge 1-2
|
|
}
|
|
else {
|
|
edgevect = e2;
|
|
endpoint = mesh.getNode(triA, 2);
|
|
which = 1; // 1 opposite of edge 2-0
|
|
}
|
|
}
|
|
// This edge is too long, so we split it in the middle
|
|
|
|
// *
|
|
// / \.
|
|
// /C0 \.
|
|
// / \.
|
|
// / \.
|
|
// / B \.
|
|
// / \.
|
|
// /C1 C2 \.
|
|
// *---------------*
|
|
// \C2 C1 /
|
|
// \ /
|
|
// \ A /
|
|
// \ /
|
|
// \ /
|
|
// \C0 /
|
|
// \ /
|
|
// *
|
|
//
|
|
// BECOMES
|
|
//
|
|
// *
|
|
// /|\.
|
|
// / | \.
|
|
// /C0|C0\.
|
|
// / | \.
|
|
// / B1 | B2 \.
|
|
// / | \.
|
|
// /C1 C2|C1 C2 \.
|
|
// *-------*-------*
|
|
// \C2 C1|C2 C1/
|
|
// \ | /
|
|
// \ A2 | A1 /
|
|
// \ | /
|
|
// \C0|C0/
|
|
// \ | /
|
|
// \|/
|
|
// *
|
|
|
|
int triB = -1;
|
|
bool haveB = false;
|
|
Corner ca_old[3], cb_old[3];
|
|
ca_old[0] = mesh.corners(triA, which);
|
|
ca_old[1] = mesh.corners(ca_old[0].next);
|
|
ca_old[2] = mesh.corners(ca_old[0].prev);
|
|
if (ca_old[0].opposite >= 0) {
|
|
cb_old[0] = mesh.corners(ca_old[0].opposite);
|
|
cb_old[1] = mesh.corners(cb_old[0].next);
|
|
cb_old[2] = mesh.corners(cb_old[0].prev);
|
|
triB = cb_old[0].tri;
|
|
haveB = true;
|
|
}
|
|
// else throw Error("nonmanifold");
|
|
|
|
// subdivide in the middle of the edge and create new triangles
|
|
Node newNode;
|
|
newNode.flags = 0;
|
|
|
|
newNode.pos = endpoint + 0.5 * edgevect; // fallback: linear average
|
|
// default: use butterfly
|
|
if (haveB)
|
|
newNode.pos = ModifiedButterflySubdivision(mesh, ca_old[0], cb_old[0], newNode.pos);
|
|
|
|
// find indices of two points of 'which'-edge
|
|
// merge flags
|
|
int P0 = ca_old[1].node;
|
|
int P1 = ca_old[2].node;
|
|
newNode.flags = mesh.nodes(P0).flags | mesh.nodes(P1).flags;
|
|
|
|
Real len0 = norm(mesh.nodes(P0).pos - newNode.pos);
|
|
Real len1 = norm(mesh.nodes(P1).pos - newNode.pos);
|
|
|
|
// remove P0/P1 1-ring connection
|
|
mesh.get1Ring(P0).nodes.erase(P1);
|
|
mesh.get1Ring(P1).nodes.erase(P0);
|
|
mesh.get1Ring(P0).tris.erase(triA);
|
|
mesh.get1Ring(P1).tris.erase(triA);
|
|
mesh.get1Ring(ca_old[0].node).tris.erase(triA);
|
|
if (haveB) {
|
|
mesh.get1Ring(P0).tris.erase(triB);
|
|
mesh.get1Ring(P1).tris.erase(triB);
|
|
mesh.get1Ring(cb_old[0].node).tris.erase(triB);
|
|
}
|
|
|
|
// init channel properties for new node
|
|
for (int i = 0; i < mesh.numNodeChannels(); i++) {
|
|
mesh.nodeChannel(i)->addInterpol(P0, P1, len0 / (len0 + len1));
|
|
}
|
|
|
|
// write to array
|
|
mesh.addTri(Triangle(ca_old[0].node, ca_old[1].node, mesh.numNodes()));
|
|
mesh.addTri(Triangle(ca_old[0].node, mesh.numNodes(), ca_old[2].node));
|
|
if (haveB) {
|
|
mesh.addTri(Triangle(cb_old[0].node, cb_old[1].node, mesh.numNodes()));
|
|
mesh.addTri(Triangle(cb_old[0].node, mesh.numNodes(), cb_old[2].node));
|
|
}
|
|
mesh.addNode(newNode);
|
|
|
|
const int nt = haveB ? 4 : 2;
|
|
int triA1 = mesh.numTris() - nt;
|
|
int triA2 = mesh.numTris() - nt + 1;
|
|
int triB1 = 0, triB2 = 0;
|
|
if (haveB) {
|
|
triB1 = mesh.numTris() - nt + 2;
|
|
triB2 = mesh.numTris() - nt + 3;
|
|
}
|
|
mesh.tris(triA1).flags = mesh.tris(triA).flags;
|
|
mesh.tris(triA2).flags = mesh.tris(triA).flags;
|
|
mesh.tris(triB1).flags = mesh.tris(triB).flags;
|
|
mesh.tris(triB2).flags = mesh.tris(triB).flags;
|
|
|
|
// connect new triangles to outside triangles,
|
|
// and connect outside triangles to these new ones
|
|
for (int c = 0; c < 3; c++)
|
|
mesh.addCorner(Corner(triA1, mesh.tris(triA1).c[c]));
|
|
for (int c = 0; c < 3; c++)
|
|
mesh.addCorner(Corner(triA2, mesh.tris(triA2).c[c]));
|
|
if (haveB) {
|
|
for (int c = 0; c < 3; c++)
|
|
mesh.addCorner(Corner(triB1, mesh.tris(triB1).c[c]));
|
|
for (int c = 0; c < 3; c++)
|
|
mesh.addCorner(Corner(triB2, mesh.tris(triB2).c[c]));
|
|
}
|
|
|
|
int baseIdx = 3 * (mesh.numTris() - nt);
|
|
Corner *cBase = &mesh.corners(baseIdx);
|
|
|
|
// set next/prev
|
|
for (int t = 0; t < nt; t++)
|
|
for (int c = 0; c < 3; c++) {
|
|
cBase[t * 3 + c].next = baseIdx + t * 3 + ((c + 1) % 3);
|
|
cBase[t * 3 + c].prev = baseIdx + t * 3 + ((c + 2) % 3);
|
|
}
|
|
|
|
// set opposites
|
|
// A1
|
|
cBase[0].opposite = haveB ? (baseIdx + 9) : -1;
|
|
cBase[1].opposite = baseIdx + 5;
|
|
cBase[2].opposite = -1;
|
|
if (ca_old[2].opposite >= 0) {
|
|
cBase[2].opposite = ca_old[2].opposite;
|
|
mesh.corners(cBase[2].opposite).opposite = baseIdx + 2;
|
|
}
|
|
// A2
|
|
cBase[3].opposite = haveB ? (baseIdx + 6) : -1;
|
|
cBase[4].opposite = -1;
|
|
if (ca_old[1].opposite >= 0) {
|
|
cBase[4].opposite = ca_old[1].opposite;
|
|
mesh.corners(cBase[4].opposite).opposite = baseIdx + 4;
|
|
}
|
|
cBase[5].opposite = baseIdx + 1;
|
|
if (haveB) {
|
|
// B1
|
|
cBase[6].opposite = baseIdx + 3;
|
|
cBase[7].opposite = baseIdx + 11;
|
|
cBase[8].opposite = -1;
|
|
if (cb_old[2].opposite >= 0) {
|
|
cBase[8].opposite = cb_old[2].opposite;
|
|
mesh.corners(cBase[8].opposite).opposite = baseIdx + 8;
|
|
}
|
|
// B2
|
|
cBase[9].opposite = baseIdx + 0;
|
|
cBase[10].opposite = -1;
|
|
if (cb_old[1].opposite >= 0) {
|
|
cBase[10].opposite = cb_old[1].opposite;
|
|
mesh.corners(cBase[10].opposite).opposite = baseIdx + 10;
|
|
}
|
|
cBase[11].opposite = baseIdx + 7;
|
|
}
|
|
|
|
////////////////////
|
|
// mark the two original triangles for deletion
|
|
taintedTris[triA] = true;
|
|
mesh.removeTriFromLookup(triA);
|
|
if (haveB) {
|
|
taintedTris[triB] = true;
|
|
mesh.removeTriFromLookup(triB);
|
|
}
|
|
|
|
Real areaA1 = mesh.getFaceArea(triA1), areaA2 = mesh.getFaceArea(triA2);
|
|
Real areaB1 = 0, areaB2 = 0;
|
|
if (haveB) {
|
|
areaB1 = mesh.getFaceArea(triB1);
|
|
areaB2 = mesh.getFaceArea(triB2);
|
|
}
|
|
|
|
// add channel props for new triangles
|
|
for (int i = 0; i < mesh.numTriChannels(); i++) {
|
|
mesh.triChannel(i)->addSplit(triA, areaA1 / (areaA1 + areaA2));
|
|
mesh.triChannel(i)->addSplit(triA, areaA2 / (areaA1 + areaA2));
|
|
if (haveB) {
|
|
mesh.triChannel(i)->addSplit(triB, areaB1 / (areaB1 + areaB2));
|
|
mesh.triChannel(i)->addSplit(triB, areaB2 / (areaB1 + areaB2));
|
|
}
|
|
}
|
|
|
|
// add the four new triangles to the prority queue
|
|
for (int i = mesh.numTris() - nt; i < mesh.numTris(); i++) {
|
|
// find the maximum length edge in this triangle
|
|
Vec3 ne0 = mesh.getEdge(i, 0), ne1 = mesh.getEdge(i, 1), ne2 = mesh.getEdge(i, 2);
|
|
Real nd0 = normSquare(ne0);
|
|
Real nd1 = normSquare(ne1);
|
|
Real nd2 = normSquare(ne2);
|
|
Real longest = max(nd0, max(nd1, nd2));
|
|
// longest = (int)(longest * 1e2) / 1e2; // HACK: truncate
|
|
pq.push(pair<Real, int>(longest, i));
|
|
}
|
|
edgeSubdivs++;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////
|
|
// EDGE COLLAPSING //
|
|
// - based on short edge length //
|
|
//////////////////////////////////////////
|
|
if (minLength > 0) {
|
|
const Real minLength2 = minLength * minLength;
|
|
for (int t = 0; t < mesh.numTris(); t++) {
|
|
// we only want to run through the edge list ONCE.
|
|
// we achieve this in a method very similar to the above subdivision method.
|
|
|
|
// NOTE:
|
|
// priority queue does not work so great in the edge collapse case,
|
|
// because collapsing one triangle affects the edge lengths
|
|
// of many neighbor triangles,
|
|
// and we do not update their maximum edge length in the queue.
|
|
|
|
// if this triangle has already been deleted, ignore it
|
|
// if(taintedTris[t])
|
|
// continue;
|
|
|
|
if (taintedTris.find(t) != taintedTris.end())
|
|
continue;
|
|
|
|
// first we find the minimum length edge in this triangle
|
|
Vec3 e0 = mesh.getEdge(t, 0), e1 = mesh.getEdge(t, 1), e2 = mesh.getEdge(t, 2);
|
|
Real d0 = normSquare(e0);
|
|
Real d1 = normSquare(e1);
|
|
Real d2 = normSquare(e2);
|
|
|
|
Vec3 edgevect;
|
|
Vec3 endpoint;
|
|
Real dist2;
|
|
int which;
|
|
if (d0 < d1) {
|
|
if (d0 < d2) {
|
|
dist2 = d0;
|
|
edgevect = e0;
|
|
endpoint = mesh.getNode(t, 0);
|
|
which = 2; // 2 opposite of edge 0-1
|
|
}
|
|
else {
|
|
dist2 = d2;
|
|
edgevect = e2;
|
|
endpoint = mesh.getNode(t, 2);
|
|
which = 1; // 1 opposite of edge 2-0
|
|
}
|
|
}
|
|
else {
|
|
if (d1 < d2) {
|
|
dist2 = d1;
|
|
edgevect = e1;
|
|
endpoint = mesh.getNode(t, 1);
|
|
which = 0; // 0 opposite of edge 1-2
|
|
}
|
|
else {
|
|
dist2 = d2;
|
|
edgevect = e2;
|
|
endpoint = mesh.getNode(t, 2);
|
|
which = 1; // 1 opposite of edge 2-0
|
|
}
|
|
}
|
|
// then we see if the min length edge is too short
|
|
if (dist2 < minLength2) {
|
|
CollapseEdge(
|
|
mesh, t, which, edgevect, endpoint, deletedNodes, taintedTris, edgeCollsLen, cutTubes);
|
|
}
|
|
}
|
|
}
|
|
// cleanup nodes and triangles marked for deletion
|
|
|
|
// we run backwards through the deleted array,
|
|
// replacing triangles with ones from the back
|
|
// (this avoids the potential problem of overwriting a triangle
|
|
// with a to-be-deleted triangle)
|
|
std::map<int, bool>::reverse_iterator tti = taintedTris.rbegin();
|
|
for (; tti != taintedTris.rend(); tti++)
|
|
mesh.removeTri(tti->first);
|
|
|
|
mesh.removeNodes(deletedNodes);
|
|
cout << "Surface subdivision finished with " << mesh.numNodes() << " surface nodes and "
|
|
<< mesh.numTris();
|
|
cout << " surface triangles, edgeSubdivs:" << edgeSubdivs << ", edgeCollapses: " << edgeCollsLen;
|
|
cout << " + " << edgeCollsAngle << " + " << edgeKill << endl;
|
|
// mesh.sanityCheck();
|
|
}
|
|
static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "subdivideMesh", !noTiming);
|
|
PyObject *_retval = 0;
|
|
{
|
|
ArgLocker _lock;
|
|
Mesh &mesh = *_args.getPtr<Mesh>("mesh", 0, &_lock);
|
|
Real minAngle = _args.get<Real>("minAngle", 1, &_lock);
|
|
Real minLength = _args.get<Real>("minLength", 2, &_lock);
|
|
Real maxLength = _args.get<Real>("maxLength", 3, &_lock);
|
|
bool cutTubes = _args.getOpt<bool>("cutTubes", 4, false, &_lock);
|
|
_retval = getPyNone();
|
|
subdivideMesh(mesh, minAngle, minLength, maxLength, cutTubes);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "subdivideMesh", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("subdivideMesh", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_subdivideMesh("", "subdivideMesh", _W_1);
|
|
extern "C" {
|
|
void PbRegister_subdivideMesh()
|
|
{
|
|
KEEP_UNUSED(_RP_subdivideMesh);
|
|
}
|
|
}
|
|
|
|
void killSmallComponents(Mesh &mesh, int elements = 10)
|
|
{
|
|
const int num = mesh.numTris();
|
|
vector<int> comp(num);
|
|
vector<int> numEl;
|
|
vector<int> deletedNodes;
|
|
vector<bool> isNodeDel(mesh.numNodes());
|
|
map<int, bool> taintedTris;
|
|
// enumerate components
|
|
int cur = 0;
|
|
for (int i = 0; i < num; i++) {
|
|
if (comp[i] == 0) {
|
|
cur++;
|
|
comp[i] = cur;
|
|
|
|
stack<int> stack;
|
|
stack.push(i);
|
|
int cnt = 1;
|
|
while (!stack.empty()) {
|
|
int tri = stack.top();
|
|
stack.pop();
|
|
for (int c = 0; c < 3; c++) {
|
|
int op = mesh.corners(tri, c).opposite;
|
|
if (op < 0)
|
|
continue;
|
|
int ntri = mesh.corners(op).tri;
|
|
if (comp[ntri] == 0) {
|
|
comp[ntri] = cur;
|
|
stack.push(ntri);
|
|
cnt++;
|
|
}
|
|
}
|
|
}
|
|
numEl.push_back(cnt);
|
|
}
|
|
}
|
|
// kill small components
|
|
for (int j = 0; j < num; j++) {
|
|
if (numEl[comp[j] - 1] < elements) {
|
|
taintedTris[j] = true;
|
|
for (int c = 0; c < 3; c++) {
|
|
int n = mesh.tris(j).c[c];
|
|
if (!isNodeDel[n]) {
|
|
isNodeDel[n] = true;
|
|
deletedNodes.push_back(n);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::map<int, bool>::reverse_iterator tti = taintedTris.rbegin();
|
|
for (; tti != taintedTris.rend(); tti++)
|
|
mesh.removeTri(tti->first);
|
|
|
|
mesh.removeNodes(deletedNodes);
|
|
|
|
if (!taintedTris.empty())
|
|
cout << "Killed small components : " << deletedNodes.size() << " nodes, " << taintedTris.size()
|
|
<< " tris deleted." << endl;
|
|
}
|
|
static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
|
{
|
|
try {
|
|
PbArgs _args(_linargs, _kwds);
|
|
FluidSolver *parent = _args.obtainParent();
|
|
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
|
pbPreparePlugin(parent, "killSmallComponents", !noTiming);
|
|
PyObject *_retval = 0;
|
|
{
|
|
ArgLocker _lock;
|
|
Mesh &mesh = *_args.getPtr<Mesh>("mesh", 0, &_lock);
|
|
int elements = _args.getOpt<int>("elements", 1, 10, &_lock);
|
|
_retval = getPyNone();
|
|
killSmallComponents(mesh, elements);
|
|
_args.check();
|
|
}
|
|
pbFinalizePlugin(parent, "killSmallComponents", !noTiming);
|
|
return _retval;
|
|
}
|
|
catch (std::exception &e) {
|
|
pbSetError("killSmallComponents", e.what());
|
|
return 0;
|
|
}
|
|
}
|
|
static const Pb::Register _RP_killSmallComponents("", "killSmallComponents", _W_2);
|
|
extern "C" {
|
|
void PbRegister_killSmallComponents()
|
|
{
|
|
KEEP_UNUSED(_RP_killSmallComponents);
|
|
}
|
|
}
|
|
|
|
} // namespace Manta
|