// Filename: setTransitionHelpers.T // Created by: drose (25Jan99) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved // // All use of this software is subject to the terms of the Panda 3d // Software license. You should have received a copy of this license // along with this source code; you will also find a current copy of // the license at http://www.panda3d.org/license.txt . // // To contact the maintainers of this program write to // panda3d@yahoogroups.com . // //////////////////////////////////////////////////////////////////// #include "nodeTransitionCacheEntry.h" #include "config_graph.h" //////////////////////////////////////////////////////////////////// // Function: tmap_get_interest // Description: Returns the NodeTransition elements of interest to // us. These are the elements of the first sequence--a // NodeTransition map--whose keys appear in the second // sequence, a sorted list of NodeTransition // TypeHandles. //////////////////////////////////////////////////////////////////// template OutputIterator tmap_get_interest(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result) { while (first1 != last1 && first2 != last2) { if ((*first1).first < *first2) { ++first1; ++result; } else if (*first2 < (*first1).first) { ++first2; ++result; } else { *result = *first1; ++first1; ++first2; ++result; } } return result; } //////////////////////////////////////////////////////////////////// // Function: tmap_override_union // Description: Accepts two NodeTransition maps, and // builds a new map which is the union of the first two. // If an element appears in both maps, the entry from // the second is preferred. //////////////////////////////////////////////////////////////////// template OutputIterator tmap_override_union(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result) { while (first1 != last1 && first2 != last2) { if ((*first1).first < (*first2).first) { *result = *first1; ++first1; ++result; } else if ((*first2).first < (*first1).first) { *result = *first2; ++first2; ++result; } else { *result = *first2; ++first1; ++first2; ++result; } } while (first1 != last1) { *result = *first1; ++first1; ++result; } while (first2 != last2) { *result = *first2; ++first2; ++result; } return result; } //////////////////////////////////////////////////////////////////// // Function: tmap_arc_union // Description: As above, but updates the transitions on the way to // indicate they are being attached to (or removed from) // the indicated arc. //////////////////////////////////////////////////////////////////// template OutputIterator tmap_arc_union(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, NodeRelation *to_arc, OutputIterator result) { while (first1 != last1 && first2 != last2) { if ((*first1).first < (*first2).first) { *result = *first1; ++first1; ++result; } else if ((*first2).first < (*first1).first) { (*first2).second->added_to_arc(to_arc); *result = *first2; ++first2; ++result; } else { (*first1).second->removed_from_arc(to_arc); (*first2).second->added_to_arc(to_arc); *result = *first2; ++first1; ++first2; ++result; } } while (first1 != last1) { *result = *first1; ++first1; ++result; } while (first2 != last2) { (*first2).second->added_to_arc(to_arc); *result = *first2; ++first2; ++result; } return result; } //////////////////////////////////////////////////////////////////// // Function: tmap_arc_compose // Description: //////////////////////////////////////////////////////////////////// template OutputIterator tmap_arc_compose(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, NodeRelation *to_arc, OutputIterator result) { while (first1 != last1 && first2 != last2) { if ((*first1).first < (*first2).first) { *result = *first1; ++first1; ++result; } else if ((*first2).first < (*first1).first) { (*first2).second->added_to_arc(to_arc); *result = *first2; ++first2; ++result; } else { (*first1).second->removed_from_arc(to_arc); PT(NodeTransition) c = (*first1).second->compose((*first2).second); if (c != (NodeTransition *)NULL) { c->added_to_arc(to_arc); *result = pair((*first1).first, c); ++result; } ++first1; ++first2; } } while (first1 != last1) { *result = *first1; ++first1; ++result; } while (first2 != last2) { (*first2).second->added_to_arc(to_arc); *result = *first2; ++first2; ++result; } return result; } //////////////////////////////////////////////////////////////////// // Function: tmap_arc_compose // Description: //////////////////////////////////////////////////////////////////// template OutputIterator tmap_arc_compose(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result) { while (first1 != last1 && first2 != last2) { if ((*first1).first < (*first2).first) { *result = *first1; ++first1; ++result; } else if ((*first2).first < (*first1).first) { *result = *first2; ++first2; ++result; } else { NodeTransition *c = (*first1).second->compose((*first2).second); if (c != (NodeTransition *)NULL) { *result = pair ((*first1).first, c); ++result; } ++first1; ++first2; } } while (first1 != last1) { *result = *first1; ++first1; ++result; } while (first2 != last2) { *result = *first2; ++first2; ++result; } return result; } //////////////////////////////////////////////////////////////////// // Function: tmap_compose // Description: Accepts two NodeTransition maps, and builds a new // list (a NodeTransition map) which represents the // memberwise composition of the two input maps. //////////////////////////////////////////////////////////////////// template OutputIterator tmap_compose(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result) { while (first1 != last1 && first2 != last2) { if ((*first1).first < (*first2).first) { *result = *first1; ++first1; ++result; } else if ((*first2).first < (*first1).first) { *result = *first2; ++first2; ++result; } else { *result = pair ((*first1).first, NodeTransitionCacheEntry::compose((*first1).second, (*first2).second)); ++first1; ++first2; ++result; } } while (first1 != last1) { *result = *first1; ++first1; ++result; } while (first2 != last2) { *result = *first2; ++first2; ++result; } return result; } //////////////////////////////////////////////////////////////////// // Function: tmap_invert_compose // Description: Accepts two NodeTransition maps, and builds a new // list (a NodeTransition map) which represents the // memberwise result of invert_compose of the two input // maps. //////////////////////////////////////////////////////////////////// template OutputIterator tmap_invert_compose(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result) { while (first1 != last1 && first2 != last2) { if ((*first1).first < (*first2).first) { *result = pair ((*first1).first, NodeTransitionCacheEntry::invert((*first1).second)); ++first1; ++result; } else if ((*first2).first < (*first1).first) { *result = *first2; ++first2; ++result; } else { NodeTransitionCacheEntry ic = NodeTransitionCacheEntry::invert_compose((*first1).second, (*first2).second); if (!ic.is_identity()) { // Only bother to store the result if it's not identity. *result = pair((*first1).first, ic); ++result; } ++first1; ++first2; } } while (first1 != last1) { *result = pair ((*first1).first, NodeTransitionCacheEntry::invert((*first1).second)); ++first1; ++result; } while (first2 != last2) { *result = *first2; ++first2; ++result; } return result; } // This macro is used in the various cached_compose functions to // possibly output debug statements about which pieces we're // composing. #ifndef NDEBUG #define OUTPUT_CC_ELEM(desc, type) \ { \ if (wrt_cat.is_spam()) { \ wrt_cat.spam() << "in " << desc << ": " << (type) << "\n"; \ } \ } #else #define OUTPUT_CC_ELEM(desc, type) #endif //////////////////////////////////////////////////////////////////// // Function: tmap_cached_compose_not_1 // Description: One of several support functions for // tmap_cached_compose(), below, this handles the case // of tmap_cached_compose() for a NodeTransition that // is already known not to be present in list 1, but may // be in one or both of lists 2 and 3. //////////////////////////////////////////////////////////////////// template void tmap_cached_compose_not_1(InputIterator2 &first2, InputIterator3 &first3, UpdateSeq now, OutputIterator &result) { if ((*first2).first < (*first3).first) { // Here's an element in list 2 that is not in lists 1 or 3. OUTPUT_CC_ELEM("2", (*first2).first); *result = pair ((*first2).first, NodeTransitionCacheEntry::cached_compose (NodeTransitionCacheEntry(), (*first2).second, NodeTransitionCacheEntry(), now)); ++first2; ++result; } else if ((*first3).first < (*first2).first) { // Here's an element in list 3 that is not lists 2 or 1. OUTPUT_CC_ELEM("3", (*first3).first); *result = pair ((*first3).first, NodeTransitionCacheEntry::cached_compose (NodeTransitionCacheEntry(), NodeTransitionCacheEntry(), (*first3).second, now)); ++first3; ++result; } else { // (*first2).first == (*first3).first // Here's an element in lists 2 and 3 that is not in list 1. OUTPUT_CC_ELEM("2, 3", (*first2).first); *result = pair ((*first2).first, NodeTransitionCacheEntry::cached_compose (NodeTransitionCacheEntry(), (*first2).second, (*first3).second, now)); ++first2; ++first3; ++result; } } //////////////////////////////////////////////////////////////////// // Function: tmap_cached_compose_not_2 // Description: One of several support functions for // tmap_cached_compose(), below, this handles the case // of tmap_cached_compose() for an NodeTransition that // is already known not to be present in list 2, but may // be in one or both of lists 1 and 3. //////////////////////////////////////////////////////////////////// template void tmap_cached_compose_not_2(InputIterator1 &first1, InputIterator3 &first3, UpdateSeq now, OutputIterator &result) { if ((*first1).first < (*first3).first) { // Here's an element in list 1 that is not in lists 2 or 3. OUTPUT_CC_ELEM("1", (*first1).first); *result = pair ((*first1).first, NodeTransitionCacheEntry::cached_compose ((*first1).second, NodeTransitionCacheEntry(), NodeTransitionCacheEntry(), now)); ++first1; ++result; } else if ((*first3).first < (*first1).first) { // Here's an element in list 3 that is not lists 1 or 2. OUTPUT_CC_ELEM("3", (*first3).first); *result = pair ((*first3).first, NodeTransitionCacheEntry::cached_compose (NodeTransitionCacheEntry(), NodeTransitionCacheEntry(), (*first3).second, now)); ++first3; ++result; } else { // (*first1).first == (*first3).first // Here's an element in lists 1 and 3 that is not in list 2. OUTPUT_CC_ELEM("1, 3", (*first1).first); *result = pair ((*first1).first, NodeTransitionCacheEntry::cached_compose ((*first1).second, NodeTransitionCacheEntry(), (*first3).second, now)); ++first1; ++first3; ++result; } } //////////////////////////////////////////////////////////////////// // Function: tmap_cached_compose_not_3 // Description: One of several support functions for // tmap_cached_compose(), below, this handles the case // of tmap_cached_compose() for an NodeTransition that // is already known not to be present in list 3, but may // be in one or both of lists 1 and 2. //////////////////////////////////////////////////////////////////// template void tmap_cached_compose_not_3(InputIterator1 &first1, InputIterator2 &first2, UpdateSeq now, OutputIterator &result) { if ((*first1).first < (*first2).first) { // Here's an element in list 1 that is not in lists 2 or 3. OUTPUT_CC_ELEM("1", (*first1).first); *result = pair ((*first1).first, NodeTransitionCacheEntry::cached_compose ((*first1).second, NodeTransitionCacheEntry(), NodeTransitionCacheEntry(), now)); ++first1; ++result; } else if ((*first2).first < (*first1).first) { // Here's an element in list 2 that is not lists 1 or 3. OUTPUT_CC_ELEM("2", (*first2).first); *result = pair ((*first2).first, NodeTransitionCacheEntry::cached_compose (NodeTransitionCacheEntry(), (*first2).second, NodeTransitionCacheEntry(), now)); ++first2; ++result; } else { // (*first1).first == (*first2).first // Here's an element in lists 1 and 2 that is not in list 3. OUTPUT_CC_ELEM("1, 2", (*first1).first); *result = pair ((*first1).first, NodeTransitionCacheEntry::cached_compose ((*first1).second, (*first2).second, NodeTransitionCacheEntry(), now)); ++first1; ++first2; ++result; } } //////////////////////////////////////////////////////////////////// // Function: tmap_cached_compose_12 // Description: One of several support functions for // tmap_cached_compose(), below, this handles the case // of tmap_cached_compose() for an NodeTransition that // is already known to be in both lists 1 and 2, and may // or may not also be in list 3. //////////////////////////////////////////////////////////////////// template void tmap_cached_compose_12(InputIterator1 &first1, InputIterator2 &first2, InputIterator3 &first3, UpdateSeq now, OutputIterator &result) { if ((*first1).first < (*first3).first) { // Here's an element in lists 1 and 2 that is not in list 3. OUTPUT_CC_ELEM("1, 2", (*first1).first); *result = pair ((*first1).first, NodeTransitionCacheEntry::cached_compose ((*first1).second, (*first2).second, NodeTransitionCacheEntry(), now)); ++first1; ++first2; ++result; } else if ((*first3).first < (*first1).first) { // Here's an element in list 3 that is not lists 1 or 2. OUTPUT_CC_ELEM("3", (*first3).first); *result = pair ((*first3).first, NodeTransitionCacheEntry::cached_compose (NodeTransitionCacheEntry(), NodeTransitionCacheEntry(), (*first3).second, now)); ++first3; ++result; } else { // (*first1).first == (*first3).first // Here's an element in lists 1, 2, and 3. OUTPUT_CC_ELEM("1, 2, 3", (*first1).first); *result = pair ((*first1).first, NodeTransitionCacheEntry::cached_compose ((*first1).second, (*first2).second, (*first3).second, now)); ++first1; ++first2; ++first3; ++result; } } //////////////////////////////////////////////////////////////////// // Function: tmap_cached_compose // Description: Calls dynamic_cached_compose() on each matching // element of [first1 .. last1), [cached_first // .. cached_last), and [value_first .. value_last). // // This function is big and complicated because it has // to do a parallel merge on three separate lists. Any // NodeTransition type may appear in any or all of the // lists; if a particular type appears in any list but // is absent from the others, an implicit identity // transition should be inferred where it is absent. //////////////////////////////////////////////////////////////////// template OutputIterator tmap_cached_compose(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, InputIterator3 first3, InputIterator3 last3, UpdateSeq now, OutputIterator result) { while (first1 != last1 && first2 != last2 && first3 != last3) { if ((*first1).first < (*first2).first) { tmap_cached_compose_not_2(first1, first3, now, result); } else if ((*first2).first < (*first1).first) { tmap_cached_compose_not_1(first2, first3, now, result); } else { // (*first1).first == (*first2).first tmap_cached_compose_12(first1, first2, first3, now, result); } } // Now pick up everything else. if (first1 == last1) { while (first2 != last2 && first3 != last3) { tmap_cached_compose_not_1(first2, first3, now, result); } } else if (first2 == last2) { while (first1 != last1 && first3 != last3) { tmap_cached_compose_not_2(first1, first3, now, result); } } else if (first3 == last3) { while (first1 != last1 && first2 != last2) { tmap_cached_compose_not_3(first1, first2, now, result); } } while (first1 != last1) { // Here's an element in list 1 that is not in lists 2 or 3. OUTPUT_CC_ELEM("1", (*first1).first); *result = pair ((*first1).first, NodeTransitionCacheEntry::cached_compose ((*first1).second, NodeTransitionCacheEntry(), NodeTransitionCacheEntry(), now)); ++first1; ++result; } while (first2 != last2) { // Here's an element in list 2 that is not in lists 1 or 3. OUTPUT_CC_ELEM("2", (*first2).first); *result = pair ((*first2).first, NodeTransitionCacheEntry::cached_compose (NodeTransitionCacheEntry(), (*first2).second, NodeTransitionCacheEntry(), now)); ++first2; ++result; } while (first3 != last3) { // Here's an element in list 3 that is not lists 1 or 2. OUTPUT_CC_ELEM("3", (*first3).first); *result = pair ((*first3).first, NodeTransitionCacheEntry::cached_compose (NodeTransitionCacheEntry(), NodeTransitionCacheEntry(), (*first3).second, now)); ++first3; ++result; } return result; } //////////////////////////////////////////////////////////////////// // Function: tmap_invert // Description: Accepts a NodeTransition map, and builds a new list // which represents the memberwise inversion of the // input. Guarantees that the new list will have // exactly the same length as the input list. //////////////////////////////////////////////////////////////////// template OutputIterator tmap_invert(InputIterator first, InputIterator last, OutputIterator result) { while (first != last) { *result = pair ((*first).first, NodeTransitionCacheEntry::invert((*first).second)); ++first; ++result; } return result; } //////////////////////////////////////////////////////////////////// // Function: tmap_equiv_trans // Description: Accepts a pair of NodeTransition maps, and returns // true if they are equivalent, false otherwise. Two // NodeTransition maps are defined to be equivalent if // all nonidentity members present in one set are // present and equivalent in the other set, //////////////////////////////////////////////////////////////////// template bool tmap_equiv_trans(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2) { while (first1 != last1 && first2 != last2) { if ((*first1).first < (*first2).first) { if (!(*first1).second.is_identity()) { return false; } ++first1; } else if ((*first2).first < (*first1).first) { if (!(*first2).second.is_identity()) { return false; } ++first2; } else { if (!(*first1).second.compare_to((*first2).second) == 0) { return false; } ++first1; ++first2; } } while (first1 != last1) { if (!(*first1).second.is_identity()) { return false; } ++first1; } while (first2 != last2) { if (!(*first2).second.is_identity()) { return false; } ++first2; } return true; } //////////////////////////////////////////////////////////////////// // Function: tmap_compare_cache // Description: Accepts a pair of NodeTransition maps, and returns // -1 if the first one sorts before the second one, // 1 if it sorts after, or 0 if they are equivalent. //////////////////////////////////////////////////////////////////// template int tmap_compare_cache(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2) { while (first1 != last1 && first2 != last2) { if ((*first1).first < (*first2).first) { if (!(*first1).second.is_identity()) { return 1; } ++first1; } else if ((*first2).first < (*first1).first) { if (!(*first2).second.is_identity()) { return -1; } ++first2; } else { int result; result = (*first1).second.compare_to((*first2).second); if (result != 0) { return result; } ++first1; ++first2; } } while (first1 != last1) { if (!(*first1).second.is_identity()) { // list1 is longer. return -1; } ++first1; } while (first2 != last2) { if (!(*first2).second.is_identity()) { // list2 is longer. return 1; } ++first2; } return 0; } //////////////////////////////////////////////////////////////////// // Function: tmap_compare_trans // Description: Accepts a pair of NodeTransition maps, and returns // -1 if the first one sorts before the second one, // 1 if it sorts after, or 0 if they are equivalent. //////////////////////////////////////////////////////////////////// template int tmap_compare_trans(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2) { while (first1 != last1 && first2 != last2) { if ((*first1).first < (*first2).first) { if ((*first1).second != (NodeTransition *)NULL) { return 1; } ++first1; } else if ((*first2).first < (*first1).first) { if ((*first2).second != (NodeTransition *)NULL) { return -1; } ++first2; } else { if ((*first1).second != (*first2).second) { if ((*first1).second == (NodeTransition *)NULL) { return -1; } else if ((*first2).second == (NodeTransition *)NULL) { return 1; } else { int result; result = (*first1).second->compare_to(*(*first2).second); if (result != 0) { return result; } } } ++first1; ++first2; } } while (first1 != last1) { if ((*first1).second != (NodeTransition *)NULL) { // list1 is longer. return -1; } ++first1; } while (first2 != last2) { if ((*first2).second != (NodeTransition *)NULL) { // list2 is longer. return 1; } ++first2; } return 0; } //////////////////////////////////////////////////////////////////// // Function: tmap_is_identity // Description: Accepts a NodeTransition map, and returns true if all // elements in the map correspond to the identity // transition, false otherwise. //////////////////////////////////////////////////////////////////// template bool tmap_is_identity(InputIterator first, InputIterator last) { while (first != last) { if (!(*first).second.is_identity()) { return false; } ++first; } return true; } //////////////////////////////////////////////////////////////////// // Function: tmap_output // Description: Accepts a NodeTransition map and // writes each element to the given output stream with a // single space separating them. //////////////////////////////////////////////////////////////////// template ostream & tmap_output(InputIterator first, InputIterator last, ostream &out) { if (first != last) { out << *((*first).second); ++first; while (first != last) { out << " " << *((*first).second); ++first; } } return out; }