dust3d/thirdparty/carve-1.4.0/lib/intersect_classify_simple.cpp

773 lines
25 KiB
C++

// Begin License:
// Copyright (C) 2006-2008 Tobias Sargeant (tobias.sargeant@gmail.com).
// All rights reserved.
//
// This file is part of the Carve CSG Library (http://carve-csg.com/)
//
// This file may be used under the terms of the GNU General Public
// License version 2.0 as published by the Free Software Foundation
// and appearing in the file LICENSE.GPL2 included in the packaging of
// this file.
//
// This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
// INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE.
// End:
#if defined(HAVE_CONFIG_H)
# include <carve_config.h>
#endif
#include <carve/csg.hpp>
#include <list>
#include <set>
#include <iostream>
#include <algorithm>
#include "intersect_common.hpp"
#include "intersect_classify_common.hpp"
#if 0
class edge_graph_t :
public std::hash_map<const Vector *,
std::pair<int, std::set<const Vector *> >,
hash_vector_ptr> {
public:
typedef mapped_type data_type;
};
#if defined(CARVE_DEBUG)
static void drawEdgeGraph(edge_graph_t &eg) {
for (edge_graph_t::const_iterator
j = eg.begin(), je = eg.end(); j != je; ++j) {
const Vector *v1 = (*j).first;
for (std::set<const Vector *>::const_iterator
k = (*j).second.second.begin(), ke = (*j).second.second.end();
k != ke;
++k) {
const Vector *v2 = (*k);
HOOK(drawEdge(v1, v2, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 3.0f););
}
}
}
#endif
static bool find_cycles(edge_graph_t &eg, std::list<V2Set> &cycles) {
for (edge_graph_t::const_iterator
i = eg.begin(), e = eg.end(); i != e; ++i) {
if ((*i).second.second.size() != 1) {
#if defined(CARVE_DEBUG)
// HOOK(drawEdgeGraph(eg););
#endif
std::cerr << "WARNING:edge loop graph does not consist of simple cycles"
<< std::endl;
return false;
}
}
while (eg.size()) {
#if defined(CARVE_DEBUG)
std::cerr << "eg.size(): " << eg.size() << std::endl;
#endif
std::list<const Vector *> path;
edge_graph_t::iterator i;
int c = 0;
const Vector *v;
i = eg.begin();
path.push_back(v = (*i).first);
(*i).second.first = c++;
while (1) {
const Vector *v2 = *((*i).second.second.begin());
path.push_back(v2);
i = eg.find(v2);
if (i == eg.end()) {
break;
}
if ((*i).second.first < c) {
break;
}
(*i).second.first = c++;
}
#if defined(CARVE_DEBUG)
std::cerr << (i != eg.end() ? "found" : "didn't find") << " cycle "
<< std::endl
<< "path.size(): " << path.size()
<< std::endl;
#endif
if (i != eg.end()) {
c = (*i).second.first;
cycles.push_back(V2Set());
}
const Vector *src = NULL, *tgt = NULL;
for (std::list<const Vector *>::const_iterator
j = path.begin(), je = path.end(); j != je; ++j) {
tgt = (*j);
if (src) {
edge_graph_t::iterator k = eg.find(src);
if ((*k).second.first >= c) {
cycles.back().insert(std::make_pair(src, tgt));
}
(*k).second.first = INT_MAX;
(*k).second.second.erase(tgt);
if ((*k).second.second.size() == 0) {
eg.erase(k);
}
}
src = tgt;
}
}
return true;
}
static bool eliminate_simple(const LoopEdges &edge_map,
V2Set &edges,
std::set<FaceLoop *> &loops,
V2Set &extra_edges) {
const Vector *v1, *v2;
loops.clear();
#if defined(CARVE_DEBUG)
std::cerr << edges.size() << " edges to eliminate" << std::endl;
#endif
while (edges.size()) {
LoopEdges::const_iterator i = edge_map.find(*(edges.begin()));
if (i == edge_map.end()) {
std::pair<const Vector *, const Vector *> t = *(edges.begin());
return false;
}
if ((*i).second.size() != 1) {
throw intersect_exception(
"eliminate_simple failed: topology too complex");
}
FaceLoop *to_remove = *((*i).second.begin());
#if defined(CARVE_DEBUG)
std::cerr << " removing face loop " << to_remove << std::endl;
#endif
loops.insert(to_remove);
v1 = to_remove->vertices[to_remove->vertices.size() - 1];
for (unsigned j = 0; j < to_remove->vertices.size(); ++j) {
v2 = to_remove->vertices[j];
V2Set::iterator v = edges.find(std::make_pair(v1, v2));
if (v != edges.end()) {
#if defined(CARVE_DEBUG)
std::cerr << " removing edge " << v1 << "-" << v2 << std::endl;
#endif
edges.erase(v);
} else {
#if defined(CARVE_DEBUG)
std::cerr << " adding edge " << v2 << "-" << v1 << std::endl;
#endif
edges.insert(std::make_pair(v2, v1));
extra_edges.insert(std::make_pair(std::min(v1, v2),
std::max(v1, v2)));
}
v1 = v2;
}
#if defined(CARVE_DEBUG)
std::cerr << " " << edges.size() << " edges remain." << std::endl;
#endif
}
#if defined(CARVE_DEBUG)
std::cerr << "eliminate succeeded" << std::endl;
#endif
return true;
}
static void classifyEdges(const V2Set &edges,
const Polyhedron *poly,
int &n_in, int &n_on, int &n_out) {
n_in = n_on = n_out = 0;
for (V2Set::const_iterator
i = edges.begin(), e = edges.end(); i != e; ++i) {
Vector c((*(*i).first + *(*i).second) / 2.0);
switch (poly->containsVertex(c, NULL)) {
case POINT_IN: n_in++; break;
case POINT_OUT: n_out++; break;
case POINT_ON: n_on++; break;
default: break;
}
}
}
static FaceClass faceClassificationBasedOnEdges(const V2Set &edges,
const Polyhedron *poly,
FaceClass on_hint) {
int n_in, n_on, n_out;
classifyEdges(edges, poly, n_in, n_on, n_out);
std::cerr << ">>> classification: n_in: " << n_in << " n_on: " << n_on << " n_out: " << n_out << std::endl;
CARVE_ASSERT(n_in == (int)edges.size() || n_out == (int)edges.size() || n_on == (int)edges.size());
if (n_in) return FACE_IN;
if (n_out) return FACE_OUT;
return on_hint;
}
static void classifySimpleOnFaces(FaceLoopList &a_face_loops,
FaceLoopList &b_face_loops,
const Polyhedron *poly_a,
const Polyhedron *poly_b,
CSG::Collector &collector) {
LoopEdges edge_map;
for (FaceLoop *i = b_face_loops.head; i; i = i->next) {
edge_map.addFaceLoop(i);
}
for (FaceLoop *i = a_face_loops.head; i;) {
LoopEdges::const_iterator t;
t = edge_map.find(std::make_pair(i->vertices[0], i->vertices[1]));
if (t != edge_map.end()) {
for (std::list<FaceLoop *>::const_iterator
u = (*t).second.begin(), ue = (*t).second.end(); u != ue; ++u) {
FaceLoop *j(*u);
int k = is_same(i->vertices, j->vertices);
CARVE_ASSERT(k != -1);
if (k == +1) {
collector.collect(i->orig_face,
i->vertices,
i->orig_face->normal,
true,
FACE_ON_ORIENT_OUT);
collector.collect(j->orig_face,
j->vertices,
j->orig_face->normal,
false,
FACE_ON_ORIENT_OUT);
edge_map.removeFaceLoop(j);
i = a_face_loops.remove(i);
b_face_loops.remove(j);
goto next_loop;
}
}
}
t = edge_map.find(std::make_pair(i->vertices[1], i->vertices[0]));
if (t != edge_map.end()) {
for (std::list<FaceLoop *>::const_iterator
u = (*t).second.begin(), ue = (*t).second.end(); u != ue; ++u) {
FaceLoop *j(*u);
int k = is_same(i->vertices, j->vertices);
CARVE_ASSERT(k != +1);
if (k == -1) {
collector.collect(i->orig_face,
i->vertices,
i->orig_face->normal,
true,
FACE_ON_ORIENT_IN);
collector.collect(j->orig_face,
j->vertices,
j->orig_face->normal,
false,
FACE_ON_ORIENT_IN);
edge_map.removeFaceLoop(j);
i = a_face_loops.remove(i);
b_face_loops.remove(j);
goto next_loop;
}
}
}
i = i->next;
next_loop:;
}
}
static void classifyOnFaces_2(FaceLoopList &a_face_loops,
FaceLoopList &b_face_loops,
const VertexClassification &vclass,
const Polyhedron *poly_a,
const Polyhedron *poly_b,
CSG::Collector &collector) {
LoopEdges edge_map_a;
LoopEdges edge_map_b;
std::list<V2Set> cycles;
for (FaceLoop *i = a_face_loops.head; i; i = i->next) {
edge_map_a.addFaceLoop(i);
}
for (FaceLoop *i = b_face_loops.head; i; i = i->next) {
edge_map_b.addFaceLoop(i);
}
edge_graph_t edge_graph;
edge_graph.clear();
for (LoopEdges::const_iterator
i = edge_map_a.begin(), e = edge_map_a.end();
i != e;
++i) {
const Vector *v1((*i).first.first), *v2((*i).first.second);
LoopEdges::const_iterator j = edge_map_b.find(std::make_pair(v2, v1));
if (j != edge_map_b.end()) {
edge_graph_t::data_type &data(edge_graph[v1]);
data.first = INT_MAX;
data.second.insert(v2);
#if defined(CARVE_DEBUG)
std::cerr << "--- " << v1 << "-" << v2 << std::endl;
#endif
}
}
cycles.clear();
if (!find_cycles(edge_graph, cycles)) return;
#if defined(CARVE_DEBUG)
std::cerr << cycles.size() << " reverse cycles" << std::endl;
#endif
for (std::list<V2Set>::iterator
i = cycles.begin(), e = cycles.end(); i != e; ++i) {
std::set<FaceLoop *> a_loopset, b_loopset;
V2Set b_cycle;
V2Set a_extra_edges, b_extra_edges;
for (V2Set::const_iterator
j = (*i).begin(), je = (*i).end(); j != je; ++j) {
b_cycle.insert(std::make_pair((*j).second, (*j).first));
}
if (eliminate_simple(edge_map_a, (*i), a_loopset, a_extra_edges) &&
eliminate_simple(edge_map_b, b_cycle, b_loopset, b_extra_edges)) {
#if defined(CARVE_DEBUG)
std::cerr << "paired "
<< a_loopset.size() << " poly_a faces with "
<< b_loopset.size() << " poly_b faces in reverse loop ("
<< a_extra_edges.size() << " extra edges in set a, "
<< b_extra_edges.size() << " extra edges in set b)"
<< std::endl;
#endif
FaceClass a_class, b_class;
a_class = faceClassificationBasedOnEdges(a_extra_edges,
poly_b,
FACE_ON_ORIENT_IN);
b_class = faceClassificationBasedOnEdges(b_extra_edges,
poly_a,
FACE_ON_ORIENT_IN);
#if defined(CARVE_DEBUG)
std::cerr << "a_class = " << ENUM(a_class) << std::endl
<< "b_class = " << ENUM(b_class) << std::endl;
#endif
if (a_class == FACE_ON_ORIENT_IN && b_class != FACE_ON_ORIENT_IN)
a_class = b_class;
else if (a_class != FACE_ON_ORIENT_IN && b_class == FACE_ON_ORIENT_IN)
b_class = a_class;
#if defined(CARVE_DEBUG)
std::cerr << "modified a_class = " << ENUM(a_class) << std::endl
<< " b_class = " << ENUM(b_class) << std::endl;
#endif
for (std::set<FaceLoop *>::const_iterator
j = a_loopset.begin(), je = a_loopset.end(); j != je; ++j) {
collector.collect((*j)->orig_face,
(*j)->vertices,
(*j)->orig_face->normal,
true,
a_class);
edge_map_a.removeFaceLoop((*j));
a_face_loops.remove((*j));
}
for (std::set<FaceLoop *>::const_iterator
j = b_loopset.begin(), je = b_loopset.end(); j != je; ++j) {
collector.collect((*j)->orig_face,
(*j)->vertices,
(*j)->orig_face->normal,
false,
a_class);
edge_map_b.removeFaceLoop((*j));
b_face_loops.remove((*j));
}
} else {
#if defined(CARVE_DEBUG)
std::cerr << "pairing failed" << std::endl;
#endif
}
}
edge_graph.clear();
for (LoopEdges::const_iterator
i = edge_map_a.begin(), e = edge_map_a.end();
i != e;
++i) {
const Vector *v1((*i).first.first), *v2((*i).first.second);
LoopEdges::const_iterator j = edge_map_b.find(std::make_pair(v1, v2));
if (j != edge_map_b.end()) {
edge_graph_t::data_type &data(edge_graph[v1]);
data.first = INT_MAX;
data.second.insert(v2);
#if defined(CARVE_DEBUG)
std::cerr << "+++ " << v1 << "-" << v2 << std::endl;
#endif
}
}
cycles.clear();
if (!find_cycles(edge_graph, cycles)) return;
#if defined(CARVE_DEBUG)
std::cerr << cycles.size() << " forward cycles" << std::endl;
#endif
for (std::list<V2Set>::iterator
i = cycles.begin(), e = cycles.end(); i != e; ++i) {
std::set<FaceLoop *> a_loopset, b_loopset;
V2Set b_cycle((*i));
V2Set a_extra_edges, b_extra_edges;
if (eliminate_simple(edge_map_a, (*i), a_loopset, a_extra_edges) &&
eliminate_simple(edge_map_b, b_cycle, b_loopset, b_extra_edges)) {
#if defined(CARVE_DEBUG)
std::cerr << "paired "
<< a_loopset.size() << " poly_a faces with "
<< b_loopset.size() << " poly_b faces in forward loop"
<< a_extra_edges.size() << " extra edges in set a, "
<< b_extra_edges.size() << " extra edges in set b)"
<< std::endl;
#endif
FaceClass a_class, b_class;
a_class = faceClassificationBasedOnEdges(a_extra_edges,
poly_b,
FACE_ON_ORIENT_OUT);
b_class = faceClassificationBasedOnEdges(b_extra_edges,
poly_a,
FACE_ON_ORIENT_OUT);
#if defined(CARVE_DEBUG)
std::cerr << "a_class = " << ENUM(a_class) << std::endl
<< "b_class = " << ENUM(b_class) << std::endl;
#endif
if (a_class == FACE_ON_ORIENT_OUT && b_class != FACE_ON_ORIENT_OUT)
a_class = (FaceClass)-b_class;
else if (a_class != FACE_ON_ORIENT_OUT && b_class == FACE_ON_ORIENT_OUT)
b_class = (FaceClass)-a_class;
#if defined(CARVE_DEBUG)
std::cerr << "modified a_class = " << ENUM(a_class) << std::endl
<< " b_class = " << ENUM(b_class) << std::endl;
#endif
for (std::set<FaceLoop *>::const_iterator
j = a_loopset.begin(), je = a_loopset.end(); j != je; ++j) {
collector.collect((*j)->orig_face,
(*j)->vertices,
(*j)->orig_face->normal,
true,
a_class);
edge_map_a.removeFaceLoop((*j));
a_face_loops.remove((*j));
}
for (std::set<FaceLoop *>::const_iterator
j = b_loopset.begin(), je = b_loopset.end(); j != je; ++j) {
collector.collect((*j)->orig_face,
(*j)->vertices,
(*j)->orig_face->normal,
false,
b_class);
edge_map_b.removeFaceLoop((*j));
b_face_loops.remove((*j));
}
} else {
#if defined(CARVE_DEBUG)
std::cerr << "pairing failed" << std::endl;
#endif
}
}
}
void classifyEasyFaces(FaceLoopList &face_loops,
VertexClassification &vclass,
const Polyhedron *other_poly,
int other_poly_num,
Intersections &intersections,
CSG::Collector &collector) {
for (FaceLoop *i = face_loops.head; i;) {
unsigned j;
const std::vector<const Vector *> &f_loop(i->vertices);
PointClass pc = POINT_ON;
unsigned test = 0;
for (j = 0; j < f_loop.size(); j++) {
PointClass temp = vclass[f_loop[j]].cls[other_poly_num];
if (temp == POINT_ON) continue;
pc = temp;
test = j;
if (pc != POINT_UNK) break;
}
if (pc != POINT_ON) {
// EASY CASE
// IF a face uses a POINT_OUT vertex, it is OUT, and no face
// vertices may be POINT_IN
// IF a face uses a POINT_IN vertex, it is IN, and no face
// vertices may be POINT_OUT
if (pc == POINT_UNK) {
pc = other_poly->containsVertex(*f_loop[test]);
#if defined(CARVE_DEBUG)
std::cerr << "testing " << f_loop[test] << " pc = " << pc << std::endl;
#endif
#if defined(CARVE_DEBUG)
if (pc != POINT_IN && pc != POINT_OUT) {
HOOK(drawFaceLoop(f_loop, i->orig_face->normal, 1.0, 0.0, 0.0, 0.5, true););
HOOK(drawFaceLoopWireframe(f_loop, i->orig_face->normal, 1.0, 1.0, 0.0, 1.0););
HOOK(drawPoint(f_loop[test], 1, 1, 1, 1, 8.0););
}
#endif
CARVE_ASSERT(pc == POINT_OUT || pc == POINT_IN);
}
for (j = 0; j < f_loop.size(); j++) {
PC2 &pc2(vclass[f_loop[j]]);
if (pc2.cls[other_poly_num] == POINT_UNK) {
pc2.cls[other_poly_num] = pc;
} else {
CARVE_ASSERT(pc2.cls[other_poly_num] == POINT_ON || pc2.cls[other_poly_num] == pc);
}
}
// this face loop is trivially either IN or OUT.
{
std::vector<const Face *> ifaces;
intersections.commonFaces(f_loop, ifaces);
if (ifaces.size()) {
std::cerr << "ERROR: JUST CLASSIFIED A FACE AS IN OR OUT WHEN "
"ifaces.size() == " << ifaces.size() << " [ ";
for (std::vector<const Face *>::const_iterator
x = ifaces.begin(), xe = ifaces.end(); x != xe; ++x) {
std::cerr << (*x) << " ";
}
std::cerr << "]" << std::endl;
}
}
if (pc == POINT_IN) {
collector.collect(i->orig_face,
f_loop,
i->orig_face->normal,
other_poly_num == 1,
FACE_IN);
} else {
collector.collect(i->orig_face,
f_loop,
i->orig_face->normal,
other_poly_num == 1,
FACE_OUT);
}
i = face_loops.remove(i);
} else {
i = i ->next;
}
}
}
#endif
void carve::csg::CSG::classifyFaceGroupsSimple(const V2Set &shared_edges,
VertexClassification &vclass,
const carve::poly::Polyhedron *poly_a,
FLGroupList &a_loops_grouped,
const LoopEdges &a_edge_map,
const carve::poly::Polyhedron *poly_b,
FLGroupList &b_loops_grouped,
const LoopEdges &b_edge_map,
Collector &collector) {
#if 0
// Old proto
void carve::csg::CSG::classifyFaces(carve::csg::FaceLoopList &a_face_loops,
size_t a_edge_count,
carve::csg::FaceLoopList &b_face_loops,
size_t b_edge_count,
VertexClassification &vclass,
const carve::poly::Polyhedron *poly_a,
const carve::poly::Polyhedron *poly_b,
Collector &collector);
std::cerr << "classify: "
<< a_face_loops.size() << " cases in polyhedron a"
<< std::endl;
std::cerr << "classify: "
<< b_face_loops.size() << " cases in polyhedron b"
<< std::endl;
classifyEasyFaces(a_face_loops, vclass, poly_b, 1, intersections, collector);
classifyEasyFaces(b_face_loops, vclass, poly_a, 0, intersections, collector);
#if 0 && defined(CARVE_DEBUG)
HOOK(drawFaceLoopList(a_face_loops, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f););
HOOK(drawFaceLoopList(b_face_loops, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f););
#endif
std::cerr << "classify: "
<< a_face_loops.size() << " hard cases in polyhedron a"
<< std::endl;
std::cerr << "classify: "
<< b_face_loops.size() << " hard cases in polyhedron b"
<< std::endl;
classifySimpleOnFaces(a_face_loops, b_face_loops, poly_a, poly_b, collector);
std::cerr << "classify: "
<< a_face_loops.size()
<< " after elimination of easy pairs: polyhedron a" << std::endl;
std::cerr << "classify: "
<< b_face_loops.size()
<< " after elimination of easy pairs: polyhedron b" << std::endl;
classifyOnFaces_2(a_face_loops,
b_face_loops,
vclass, poly_a,
poly_b,
collector);
std::cerr << "classify: " << a_face_loops.size() << " after elimination of hard pairs: polyhedron a" << std::endl;
std::cerr << "classify: " << b_face_loops.size() << " after elimination of hard pairs: polyhedron b" << std::endl;
FaceLoop *fla, *flb;
for (fla = a_face_loops.head; fla; fla = fla->next) {
const Face *f(fla->orig_face);
std::vector<const Vector *> &loop(fla->vertices);
std::vector<P2> proj;
proj.reserve(loop.size());
for (unsigned i = 0; i < loop.size(); ++i) {
proj.push_back((f->*(f->project))(*loop[i]));
}
P2 pv;
if (!pickContainedPoint(proj, pv)) {
CARVE_FAIL("pickContainedPoint failed");
}
Vector v = (f->*(f->unproject))(pv);
const Face *hit_face;
PointClass pc = poly_b->containsVertex(v, &hit_face);
FaceClass fc = FACE_ON;
if (pc != POINT_IN && pc != POINT_OUT) {
std::cerr << "WARNING: last resort classifier found an ON face"
<< std::endl;
}
switch (pc) {
case POINT_IN: fc = FACE_IN; break;
case POINT_OUT: fc = FACE_OUT; break;
case POINT_ON: {
double d = dot(hit_face->normal, f->normal);
fc = d < 0.0 ? FACE_ON_ORIENT_IN : FACE_ON_ORIENT_OUT;
break;
}
default:
CARVE_FAIL("should not happen");
}
// CARVE_ASSERT(pc == POINT_IN || pc == POINT_OUT);
#if defined(CARVE_DEBUG)
{
float r,g,b,a;
switch(fc) {
case FACE_IN: r=1; g=0; b=0; a=1; break;
case FACE_OUT: r=0; g=0; b=1; a=1; break;
case FACE_ON_ORIENT_OUT: r=1; g=1; b=0; a=1; break;
case FACE_ON_ORIENT_IN: r=0; g=1; b=1; a=1; break;
}
if (fc != FACE_IN && fc != FACE_OUT) {
HOOK(drawFaceLoopWireframe(loop, f->normal, r, g, b, a););
HOOK(drawPoint(&v, 1, 1, 1, 1, 30.0););
}
}
#endif
collector.collect(f, loop, f->normal, true, fc);
}
for (flb = b_face_loops.head; flb; flb = flb->next) {
const Face *f(flb->orig_face);
std::vector<const Vector *> &loop(flb->vertices);
std::vector<P2> proj;
proj.reserve(loop.size());
for (unsigned i = 0; i < loop.size(); ++i) {
proj.push_back((f->*(f->project))(*loop[i]));
}
P2 pv;
if (!pickContainedPoint(proj, pv)) {
CARVE_FAIL("pickContainedPoint failed");
}
Vector v = (f->*(f->unproject))(pv);
const Face *hit_face;
PointClass pc = poly_a->containsVertex(v, &hit_face);
FaceClass fc = FACE_ON;
if (pc != POINT_IN && pc != POINT_OUT) {
std::cerr << "WARNING: last resort classifier found an ON face"
<< std::endl;
}
switch (pc) {
case POINT_IN: fc = FACE_IN; break;
case POINT_OUT: fc = FACE_OUT; break;
case POINT_ON: {
double d = dot(hit_face->normal, f->normal);
fc = d < 0.0 ? FACE_ON_ORIENT_IN : FACE_ON_ORIENT_OUT;
break;
}
default:
CARVE_FAIL("should not happen");
}
// CARVE_ASSERT(pc == POINT_IN || pc == POINT_OUT);
#if defined(CARVE_DEBUG)
{
float r,g,b,a;
switch(fc) {
case FACE_IN: r=1; g=0; b=0; a=1; break;
case FACE_OUT: r=0; g=0; b=1; a=1; break;
case FACE_ON_ORIENT_OUT: r=1; g=1; b=0; a=1; break;
case FACE_ON_ORIENT_IN: r=0; g=1; b=1; a=1; break;
}
// HOOK(drawFaceLoop(loop, f->normal, r, g, b, a););
if (fc != FACE_IN && fc != FACE_OUT) {
HOOK(drawFaceLoopWireframe(loop, f->normal, r, g, b, a););
HOOK(drawPoint(&v, 1, 1, 1, 1, 30.0););
}
}
#endif
collector.collect(f, loop, f->normal, false, fc);
}
#endif
}