// genfans.cxx -- Combine individual triangles into more optimal fans. // // Written by Curtis Olson, started March 1999. // // Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 2 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // // $Id$ #include "genfans.hxx" // make sure the list is expanded at least to hold "n" and then push // "i" onto the back of the "n" list. void FGGenFans::add_and_expand( reverse_list& by_node, int n, int i ) { int_list empty; int size = (int)by_node.size(); if ( size > n ) { // ok } else { // cout << "capacity = " << by_node.capacity() << endl; // cout << "size = " << size << " n = " << n // << " need to push = " << n - size + 1 << endl; for ( int i = 0; i < n - size + 1; ++i ) { by_node.push_back(empty); } } by_node[n].push_back(i); } // given an input triangle, shuffle nodes so that "center" is the // first node, but maintain winding order. static FGTriEle canonify( const FGTriEle& t, int center ) { if ( t.get_n1() == center ) { // already ok return t; } else if ( t.get_n2() == center ) { return FGTriEle( t.get_n2(), t.get_n3(), t.get_n1(), 0.0 ); } else if ( t.get_n3() == center ) { return FGTriEle( t.get_n3(), t.get_n1(), t.get_n2(), 0.0 ); } else { cout << "ERROR, index doesn't refer to this triangle!!!" << endl; exit(-1); } } // returns a list of triangle indices static int_list make_best_fan( const triele_list& master_tris, const int center, const int_list& local_tris ) { int_list best_result; // try starting with each of local_tris to find the best fan // arrangement for ( int start = 0; start < (int)local_tris.size(); ++start ) { // cout << "trying with first triangle = " << local_tris[start] << endl; int_list tmp_result; tmp_result.clear(); FGTriEle current_tri; FGTriEle test; current_tri = canonify( master_tris[local_tris[start]], center ); tmp_result.push_back( local_tris[start] ); // follow the ring int next = -1; bool matches = true; while ( (next != start) && matches ) { // find next triangle in ring matches = false; for ( int i = 0; i < (int)local_tris.size(); ++i ) { test = canonify( master_tris[local_tris[i]], center ); if ( current_tri.get_n3() == test.get_n2() ) { if ( i != start ) { // cout << " next triangle = " << local_tris[i] << endl; current_tri = test; tmp_result.push_back( local_tris[i] ); matches = true; next = i; break; } } } } if ( tmp_result.size() == local_tris.size() ) { // we found a complete usage, no need to go on // cout << "we found a complete usage, no need to go on" << endl; best_result = tmp_result; break; } else if ( tmp_result.size() > best_result.size() ) { // we found a better way to fan // cout << "we found a better fan arrangement" << endl; best_result = tmp_result; } } return best_result; } static bool in_fan(int index, const int_list& fan ) { const_int_list_iterator current = fan.begin(); const_int_list_iterator last = fan.end(); for ( ; current != last; ++current ) { if ( index == *current ) { return true; } } return false; } // recursive build fans from triangle list fan_list FGGenFans::greedy_build( triele_list tris ) { cout << "starting greedy build of fans" << endl; fans.clear(); while ( ! tris.empty() ) { // cout << "building reverse_list" << endl; reverse_list by_node; by_node.clear(); // traverse the triangle list and for each node, build a list of // triangles that attach to it. for ( int i = 0; i < (int)tris.size(); ++i ) { int n1 = tris[i].get_n1(); int n2 = tris[i].get_n2(); int n3 = tris[i].get_n3(); add_and_expand( by_node, n1, i ); add_and_expand( by_node, n2, i ); add_and_expand( by_node, n3, i ); } // find the node in the tris list that attaches to the most // triangles // cout << "find most connected node" << endl; int_list biggest_group; reverse_list_iterator r_current = by_node.begin(); reverse_list_iterator r_last = by_node.end(); int index = 0; int counter = 0; for ( ; r_current != r_last; ++r_current ) { if ( r_current->size() > biggest_group.size() ) { biggest_group = *r_current; index = counter; } ++counter; } // cout << "triangle pool = " << tris.size() << endl; // cout << "biggest_group = " << biggest_group.size() << endl; // cout << "center node = " << index << endl; // make the best fan we can out of this group // cout << "before make_best_fan()" << endl; int_list best_fan = make_best_fan( tris, index, biggest_group ); // cout << "after make_best_fan()" << endl; // generate point form of best_fan int_list node_list; node_list.clear(); int_list_iterator i_start = best_fan.begin(); int_list_iterator i_current = i_start; int_list_iterator i_last = best_fan.end(); for ( ; i_current != i_last; ++i_current ) { FGTriEle t = canonify( tris[*i_current], index ); if ( i_start == i_current ) { node_list.push_back( t.get_n1() ); node_list.push_back( t.get_n2() ); } node_list.push_back( t.get_n3() ); } // cout << "best list size = " << node_list.size() << endl; // add this fan to the fan list fans.push_back( node_list ); // delete the triangles in best_fan out of tris and repeat triele_list_iterator t_current = tris.begin(); triele_list_iterator t_last = tris.end(); counter = 0; while ( t_current != t_last ) { if ( in_fan(counter, best_fan) ) { // cout << "erasing " // << t_current->get_n1() << "," // << t_current->get_n2() << "," // << t_current->get_n3() // << " from master tri pool" // << endl; tris.erase( t_current ); } else { ++t_current; } ++counter; } } cout << "end of greedy build of fans" << endl; cout << "average fan size = " << ave_size() << endl; return fans; } // report average fan size double FGGenFans::ave_size() { double sum = 0.0; fan_list_iterator current = fans.begin(); fan_list_iterator last = fans.end(); for ( ; current != last; ++current ) { sum += current->size(); } return sum / (double)fans.size(); }