From 9f198514ce03bef7d0f4a6cf9630ed77cf32e9ef Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 15 Feb 2007 20:59:08 +0000 Subject: [PATCH] add SparseArray --- panda/src/putil/Sources.pp | 3 + panda/src/putil/bitArray.cxx | 159 +++++++- panda/src/putil/bitArray.h | 7 +- panda/src/putil/bitMask.I | 38 ++ panda/src/putil/bitMask.h | 3 + panda/src/putil/config_util.cxx | 2 + panda/src/putil/putil_composite2.cxx | 1 + panda/src/putil/sparseArray.I | 578 +++++++++++++++++++++++++++ panda/src/putil/sparseArray.cxx | 517 ++++++++++++++++++++++++ panda/src/putil/sparseArray.h | 168 ++++++++ 10 files changed, 1474 insertions(+), 2 deletions(-) create mode 100644 panda/src/putil/sparseArray.I create mode 100644 panda/src/putil/sparseArray.cxx create mode 100644 panda/src/putil/sparseArray.h diff --git a/panda/src/putil/Sources.pp b/panda/src/putil/Sources.pp index da67424f17..6fea6e08a1 100644 --- a/panda/src/putil/Sources.pp +++ b/panda/src/putil/Sources.pp @@ -53,6 +53,7 @@ nonDeletor.h \ pta_double.h \ pta_float.h pta_int.h \ + sparseArray.I sparseArray.h \ string_utils.I string_utils.N string_utils.h \ timedCycle.I timedCycle.h typedWritable.I \ typedWritable.h typedWritableReferenceCount.I \ @@ -94,6 +95,7 @@ nonDeletor.cxx \ pta_double.cxx pta_float.cxx \ pta_int.cxx pta_ushort.cxx \ + sparseArray.cxx \ string_utils.cxx timedCycle.cxx typedWritable.cxx \ typedWritableReferenceCount.cxx updateSeq.cxx \ uniqueIdAllocator.cxx \ @@ -145,6 +147,7 @@ nonDeletor.h \ pta_double.h \ pta_float.h pta_int.h pta_ushort.h \ + sparseArray.I sparseArray.h \ string_utils.I \ string_utils.h timedCycle.I timedCycle.h typedWritable.I \ typedWritable.h typedWritableReferenceCount.I \ diff --git a/panda/src/putil/bitArray.cxx b/panda/src/putil/bitArray.cxx index 0e2253669c..48bbbc3a38 100755 --- a/panda/src/putil/bitArray.cxx +++ b/panda/src/putil/bitArray.cxx @@ -17,9 +17,31 @@ //////////////////////////////////////////////////////////////////// #include "bitArray.h" +#include "sparseArray.h" TypeHandle BitArray::_type_handle; +//////////////////////////////////////////////////////////////////// +// Function: BitArray::Constructor (from SparseArray) +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +BitArray:: +BitArray(const SparseArray &from) { + _highest_bits = 0; + + int num_subranges = from.get_num_subranges(); + for (int i = 0; i < num_subranges; ++i) { + int begin = from.get_subrange_begin(i); + int end = from.get_subrange_end(i); + set_range(begin, end - begin); + } + + if (from.is_inverse()) { + invert_in_place(); + } +} + //////////////////////////////////////////////////////////////////// // Function: BitArray::is_zero // Access: Published @@ -37,13 +59,148 @@ is_zero() const { // Start from the high end, since that's more likely to be nonzero. Array::reverse_iterator ai; for (ai = _array.rbegin(); ai != _array.rend(); ++ai) { - if ((*ai) != 0) { + if (!(*ai).is_zero()) { return false; } } return true; } +//////////////////////////////////////////////////////////////////// +// Function: BitArray::is_all_on +// Access: Published +// Description: Returns true if the entire bitmask is one, false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool BitArray:: +is_all_on() const { + if (!_highest_bits) { + // If all the infinite highest bits are not set, certainly the + // bitmask is not all on. + return false; + } + + Array::reverse_iterator ai; + for (ai = _array.rbegin(); ai != _array.rend(); ++ai) { + if (!(*ai).is_all_on()) { + return false; + } + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitArray::has_any_of +// Access: Published +// Description: Returns true if any bit in the indicated range is +// set, false otherwise. +//////////////////////////////////////////////////////////////////// +bool BitArray:: +has_any_of(int low_bit, int size) const { + if ((low_bit + size - 1) / num_bits_per_word >= get_num_words()) { + // This range touches the highest bits. + if (_highest_bits) { + return true; + } + } + + int w = low_bit / num_bits_per_word; + int b = low_bit % num_bits_per_word; + + if (w >= get_num_words()) { + // This range is entirely among the highest bits. + return _highest_bits; + } + if (b + size <= num_bits_per_word) { + // The whole thing fits within one word of the array. + return get_word(w).has_any_of(b, size); + } + + int num_high_bits = num_bits_per_word - b; + if (_array[w].has_any_of(b, num_high_bits)) { + return true; + } + size -= num_high_bits; + ++w; + + while (size > 0) { + if (size <= num_bits_per_word) { + // The remainder fits within one word of the array. + return _array[w].has_any_of(0, size); + } + + // Keep going. + if (!_array[w].is_zero()) { + return true; + } + size -= num_bits_per_word; + ++w; + + if (w >= get_num_words()) { + // Now we're up to the highest bits. + return _highest_bits; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitArray::has_all_of +// Access: Published +// Description: Returns true if all bits in the indicated range are +// set, false otherwise. +//////////////////////////////////////////////////////////////////// +bool BitArray:: +has_all_of(int low_bit, int size) const { + if ((low_bit + size - 1) / num_bits_per_word >= get_num_words()) { + // This range touches the highest bits. + if (!_highest_bits) { + return false; + } + } + + int w = low_bit / num_bits_per_word; + int b = low_bit % num_bits_per_word; + + if (w >= get_num_words()) { + // This range is entirely among the highest bits. + return _highest_bits; + } + if (b + size <= num_bits_per_word) { + // The whole thing fits within one word of the array. + return get_word(w).has_all_of(b, size); + } + + int num_high_bits = num_bits_per_word - b; + if (!_array[w].has_all_of(b, num_high_bits)) { + return false; + } + size -= num_high_bits; + ++w; + + while (size > 0) { + if (size <= num_bits_per_word) { + // The remainder fits within one word of the array. + return _array[w].has_all_of(0, size); + } + + // Keep going. + if (!_array[w].is_all_on()) { + return false; + } + size -= num_bits_per_word; + ++w; + + if (w >= get_num_words()) { + // Now we're up to the highest bits. + return _highest_bits; + } + } + + return true; +} + //////////////////////////////////////////////////////////////////// // Function: BitArray::set_range // Access: Published diff --git a/panda/src/putil/bitArray.h b/panda/src/putil/bitArray.h index 0b689c8c50..4499fa7f99 100755 --- a/panda/src/putil/bitArray.h +++ b/panda/src/putil/bitArray.h @@ -29,6 +29,8 @@ #include "checksumHashGenerator.h" +class SparseArray; + //////////////////////////////////////////////////////////////////// // Class : BitArray // Description : A dynamic array with an unlimited number of bits. @@ -44,11 +46,11 @@ public: enum { num_bits_per_word = MaskType::num_bits }; PUBLISHED: - INLINE BitArray(); INLINE BitArray(WordType init_value); INLINE BitArray(const BitArray ©); INLINE BitArray &operator = (const BitArray ©); + BitArray(const SparseArray &from); INLINE static BitArray all_on(); INLINE static BitArray all_off(); @@ -69,9 +71,12 @@ PUBLISHED: INLINE void set_bit_to(int index, bool value); INLINE bool get_highest_bits() const; bool is_zero() const; + bool is_all_on() const; INLINE WordType extract(int low_bit, int size) const; INLINE void store(WordType value, int low_bit, int size); + bool has_any_of(int low_bit, int size) const; + bool has_all_of(int low_bit, int size) const; void set_range(int low_bit, int size); void clear_range(int low_bit, int size); INLINE void set_range_to(bool value, int low_bit, int size); diff --git a/panda/src/putil/bitMask.I b/panda/src/putil/bitMask.I index 419d7419f4..0d07e431e2 100644 --- a/panda/src/putil/bitMask.I +++ b/panda/src/putil/bitMask.I @@ -273,6 +273,18 @@ is_zero() const { return (_word == 0); } +//////////////////////////////////////////////////////////////////// +// Function: BitMask::is_all_on +// Access: Published +// Description: Returns true if the entire bitmask is one, false +// otherwise. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BitMask:: +is_all_on() const { + return (~_word == 0); +} + //////////////////////////////////////////////////////////////////// // Function: BitMask::extract // Access: Published @@ -300,6 +312,32 @@ store(WordType value, int low_bit, int size) { _word = (_word & ~mask) | ((value << low_bit) & mask); } +//////////////////////////////////////////////////////////////////// +// Function: BitMask::has_any_of +// Access: Published +// Description: Returns true if any bit in the indicated range is +// set, false otherwise. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BitMask:: +has_any_of(int low_bit, int size) const { + WordType mask = BitMask::range(low_bit, size)._word; + return (_word & mask) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: BitMask::has_all_of +// Access: Published +// Description: Returns true if all bits in the indicated range are +// set, false otherwise. +//////////////////////////////////////////////////////////////////// +template +INLINE bool BitMask:: +has_all_of(int low_bit, int size) const { + WordType mask = BitMask::range(low_bit, size)._word; + return (_word & mask) == mask; +} + //////////////////////////////////////////////////////////////////// // Function: BitMask::set_range // Access: Published diff --git a/panda/src/putil/bitMask.h b/panda/src/putil/bitMask.h index 7e9223032f..c79f3c73d2 100644 --- a/panda/src/putil/bitMask.h +++ b/panda/src/putil/bitMask.h @@ -64,9 +64,12 @@ PUBLISHED: INLINE void clear_bit(int index); INLINE void set_bit_to(int index, bool value); INLINE bool is_zero() const; + INLINE bool is_all_on() const; INLINE WordType extract(int low_bit, int size) const; INLINE void store(WordType value, int low_bit, int size); + INLINE bool has_any_of(int low_bit, int size) const; + INLINE bool has_all_of(int low_bit, int size) const; INLINE void set_range(int low_bit, int size); INLINE void clear_range(int low_bit, int size); INLINE void set_range_to(bool value, int low_bit, int size); diff --git a/panda/src/putil/config_util.cxx b/panda/src/putil/config_util.cxx index 0f949e43f3..605c682426 100644 --- a/panda/src/putil/config_util.cxx +++ b/panda/src/putil/config_util.cxx @@ -32,6 +32,7 @@ #include "namable.h" #include "nodeCachedReferenceCount.h" #include "referenceCount.h" +#include "sparseArray.h" #include "typedObject.h" #include "typedReferenceCount.h" #include "typedWritable.h" @@ -94,6 +95,7 @@ ConfigureFn(config_util) { Namable::init_type(); NodeCachedReferenceCount::init_type(); ReferenceCount::init_type(); + SparseArray::init_type(); TypedObject::init_type(); TypedReferenceCount::init_type(); TypedWritable::init_type(); diff --git a/panda/src/putil/putil_composite2.cxx b/panda/src/putil/putil_composite2.cxx index 7b23f69f00..2a24484089 100644 --- a/panda/src/putil/putil_composite2.cxx +++ b/panda/src/putil/putil_composite2.cxx @@ -13,6 +13,7 @@ #include "pta_float.cxx" #include "pta_int.cxx" #include "pta_ushort.cxx" +#include "sparseArray.cxx" #include "string_utils.cxx" #include "timedCycle.cxx" #include "typedWritable.cxx" diff --git a/panda/src/putil/sparseArray.I b/panda/src/putil/sparseArray.I new file mode 100644 index 0000000000..a70498e57a --- /dev/null +++ b/panda/src/putil/sparseArray.I @@ -0,0 +1,578 @@ +// Filename: sparseArray.I +// Created by: drose (14Feb07) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SparseArray:: +SparseArray() : _inverse(false) { +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::Copy Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SparseArray:: +SparseArray(const SparseArray ©) : + _subranges(copy._subranges), + _inverse(copy._inverse) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::Copy Assignment Operator +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SparseArray &SparseArray:: +operator = (const SparseArray ©) { + _subranges = copy._subranges; + _inverse = copy._inverse; + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::Named all_on constructor +// Access: Published, Static +// Description: Returns a SparseArray with an infinite array of bits, +// all on. +//////////////////////////////////////////////////////////////////// +INLINE SparseArray SparseArray:: +all_on() { + SparseArray result; + result._inverse = true; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::Named all_on constructor +// Access: Published, Static +// Description: Returns a SparseArray whose bits are all off. +//////////////////////////////////////////////////////////////////// +INLINE SparseArray SparseArray:: +all_off() { + return SparseArray(); +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::Named lower_on constructor +// Access: Published, Static +// Description: Returns a SparseArray whose lower on_bits bits are on. +//////////////////////////////////////////////////////////////////// +INLINE SparseArray SparseArray:: +lower_on(int on_bits) { + SparseArray result; + result.set_range(0, on_bits); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::Named bit constructor +// Access: Published, Static +// Description: Returns a SparseArray with only the indicated bit on. +//////////////////////////////////////////////////////////////////// +INLINE SparseArray SparseArray:: +bit(int index) { + SparseArray result; + result.set_bit(index); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::Named range constructor +// Access: Published, Static +// Description: Returns a SparseArray whose size bits, beginning at +// low_bit, are on. +//////////////////////////////////////////////////////////////////// +INLINE SparseArray SparseArray:: +range(int low_bit, int size) { + SparseArray result; + result.set_range(low_bit, size); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::Destructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SparseArray:: +~SparseArray() { +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::has_max_num_bits +// Access: Published, Static +// Description: Returns true if there is a maximum number of bits +// that may be stored in this structure, false +// otherwise. If this returns true, the number may be +// queried in get_max_num_bits(). +// +// This method always returns false. The SparseArray has +// no maximum number of bits. This method is defined so +// generic programming algorithms can use BitMask or +// SparseArray interchangeably. +//////////////////////////////////////////////////////////////////// +INLINE bool SparseArray:: +has_max_num_bits() { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::get_max_num_bits +// Access: Published, Static +// Description: If get_max_num_bits() returned true, this method may +// be called to return the maximum number of bits that +// may be stored in this structure. It is an error to +// call this if get_max_num_bits() return false. +// +// It is always an error to call this method. The +// SparseArray has no maximum number of bits. This method +// is defined so generic programming algorithms can use +// BitMask or SparseArray interchangeably. +//////////////////////////////////////////////////////////////////// +INLINE int SparseArray:: +get_max_num_bits() { + nassertr(false, 0); + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::get_num_bits +// Access: Published +// Description: Returns the current number of possibly different bits +// in this array. There are actually an infinite number +// of bits, but every bit higher than this bit will have +// the same value, either 0 or 1 (see +// get_highest_bits()). +// +// This number may grow and/or shrink automatically as +// needed. +//////////////////////////////////////////////////////////////////// +INLINE int SparseArray:: +get_num_bits() const { + if (_subranges.empty()) { + return 0; + } else { + Subranges::const_iterator si = _subranges.begin() + _subranges.size() - 1; + return (*si)._end; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::get_bit +// Access: Published +// Description: Returns true if the nth bit is set, false if it is +// cleared. It is valid for n to increase beyond +// get_num_bits(), but the return value get_num_bits() +// will always be the same. +//////////////////////////////////////////////////////////////////// +INLINE bool SparseArray:: +get_bit(int index) const { + return has_any_of(index, 1); +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::set_bit +// Access: Published +// Description: Sets the nth bit on. If n >= get_num_bits(), this +// automatically extends the array. +//////////////////////////////////////////////////////////////////// +INLINE void SparseArray:: +set_bit(int index) { + set_range(index, 1); +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::clear_bit +// Access: Published +// Description: Sets the nth bit off. If n >= get_num_bits(), this +// automatically extends the array. +//////////////////////////////////////////////////////////////////// +INLINE void SparseArray:: +clear_bit(int index) { + clear_range(index, 1); +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::set_bit_to +// Access: Published +// Description: Sets the nth bit either on or off, according to the +// indicated bool value. +//////////////////////////////////////////////////////////////////// +INLINE void SparseArray:: +set_bit_to(int index, bool value) { + if (value) { + set_bit(index); + } else { + clear_bit(index); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::get_highest_bits +// Access: Published +// Description: Returns true if the infinite set of bits beyond +// get_num_bits() are all on, or false of they are all +// off. +//////////////////////////////////////////////////////////////////// +INLINE bool SparseArray:: +get_highest_bits() const { + return _inverse; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::is_zero +// Access: Published +// Description: Returns true if the entire bitmask is zero, false +// otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool SparseArray:: +is_zero() const { + if (_inverse) { + return false; + } else { + return _subranges.empty(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::is_all_on +// Access: Published +// Description: Returns true if the entire bitmask is one, false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool SparseArray:: +is_all_on() const { + if (_inverse) { + return _subranges.empty(); + } else { + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::has_any_of +// Access: Published +// Description: Returns true if any bit in the indicated range is +// set, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool SparseArray:: +has_any_of(int low_bit, int size) const { + if (_inverse) { + return !do_has_all(low_bit, low_bit + size); + } else { + return do_has_any(low_bit, low_bit + size); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::has_all_of +// Access: Published +// Description: Returns true if all bits in the indicated range are +// set, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool SparseArray:: +has_all_of(int low_bit, int size) const { + if (_inverse) { + return !do_has_any(low_bit, low_bit + size); + } else { + return do_has_all(low_bit, low_bit + size); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::set_range +// Access: Published +// Description: Sets the indicated range of bits on. +//////////////////////////////////////////////////////////////////// +INLINE void SparseArray:: +set_range(int low_bit, int size) { + if (_inverse) { + return do_remove_range(low_bit, low_bit + size); + } else { + return do_add_range(low_bit, low_bit + size); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::clear_range +// Access: Published +// Description: Sets the indicated range of bits off. +//////////////////////////////////////////////////////////////////// +INLINE void SparseArray:: +clear_range(int low_bit, int size) { + if (_inverse) { + return do_add_range(low_bit, low_bit + size); + } else { + return do_remove_range(low_bit, low_bit + size); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::set_range_to +// Access: Published +// Description: Sets the indicated range of bits to either on or off. +//////////////////////////////////////////////////////////////////// +INLINE void SparseArray:: +set_range_to(bool value, int low_bit, int size) { + if (value) { + set_range(low_bit, size); + } else { + clear_range(low_bit, size); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::invert_in_place +// Access: Published +// Description: Inverts all the bits in the SparseArray. This is +// equivalent to array = ~array. +//////////////////////////////////////////////////////////////////// +void SparseArray:: +invert_in_place() { + _inverse = !_inverse; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::clear +// Access: Published +// Description: Sets all the bits in the SparseArray off. +//////////////////////////////////////////////////////////////////// +void SparseArray:: +clear() { + _subranges.clear(); + _inverse = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::operator == +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool SparseArray:: +operator == (const SparseArray &other) const { + return compare_to(other) == 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::operator != +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool SparseArray:: +operator != (const SparseArray &other) const { + return compare_to(other) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::operator < +// Access: Published +// Description: Returns true if the unsigned integer which is +// represented by this SparseArray is less than that of the +// other one, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool SparseArray:: +operator < (const SparseArray &other) const { + return compare_to(other) < 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::operator & +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SparseArray SparseArray:: +operator & (const SparseArray &other) const { + SparseArray result(*this); + result &= other; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::operator | +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SparseArray SparseArray:: +operator | (const SparseArray &other) const { + SparseArray result(*this); + result |= other; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::operator ^ +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SparseArray SparseArray:: +operator ^ (const SparseArray &other) const { + SparseArray result(*this); + result ^= other; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::operator ~ +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SparseArray SparseArray:: +operator ~ () const { + SparseArray result(*this); + result.invert_in_place(); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::operator << +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SparseArray SparseArray:: +operator << (int shift) const { + SparseArray result(*this); + result <<= shift; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::operator >> +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SparseArray SparseArray:: +operator >> (int shift) const { + SparseArray result(*this); + result >>= shift; + return result; +} + + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::operator <<= +// Access: Published +// Description: Logical left shift. Since negative bit positions +// have meaning in a SparseArray, real bit values are +// rotated in on the left (not necessarily zero). +//////////////////////////////////////////////////////////////////// +void SparseArray:: +operator <<= (int shift) { + do_shift(shift); +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::operator >>= +// Access: Published +// Description: Logical right shift. The rightmost bits become +// negative, but are not lost; they will reappear into +// the zero position if the array is later left-shifted. +//////////////////////////////////////////////////////////////////// +void SparseArray:: +operator >>= (int shift) { + do_shift(-shift); +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::is_inverse +// Access: Published +// Description: If this is true, the SparseArray is actually defined +// as a list of subranges of integers that are *not* in +// the set. If this is false (the default), then the +// subranges define the integers that *are* in the set. +// This affects the interpretation of the values +// returned by iterating through get_num_subranges(). +//////////////////////////////////////////////////////////////////// +INLINE bool SparseArray:: +is_inverse() const { + return _inverse; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::get_num_subranges +// Access: Published +// Description: Returns the number of separate subranges stored in +// the SparseArray. You can use this limit to iterate +// through the subranges, calling get_subrange_begin() +// and get_subrange_end() for each one. +// +// Also see is_inverse(). +//////////////////////////////////////////////////////////////////// +INLINE int SparseArray:: +get_num_subranges() const { + return _subranges.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::get_subrange_begin +// Access: Published +// Description: Returns the first numeric element in the nth +// subrange. +// +// Also see is_inverse(). +//////////////////////////////////////////////////////////////////// +INLINE int SparseArray:: +get_subrange_begin(int n) const { + nassertr(n >= 0 && n < (int)_subranges.size(), 0); + return _subranges[n]._begin; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::get_subrange_end +// Access: Published +// Description: Returns the last numeric element, plus one, in the +// nth subrange. +// +// Also see is_inverse(). +//////////////////////////////////////////////////////////////////// +INLINE int SparseArray:: +get_subrange_end(int n) const { + nassertr(n >= 0 && n < (int)_subranges.size(), 0); + return _subranges[n]._end; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::Subrange::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SparseArray::Subrange:: +Subrange(int begin, int end) : + _begin(begin), + _end(end) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::Subrange::operator < +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool SparseArray::Subrange:: +operator < (const SparseArray::Subrange &other) const { + // We compare the end values, rather than the begin values, to make + // lower_bound() sensibly return a possible intersection with the + // indicated Subrange. + return _end < other._end; +} + diff --git a/panda/src/putil/sparseArray.cxx b/panda/src/putil/sparseArray.cxx new file mode 100644 index 0000000000..f9b37e87c2 --- /dev/null +++ b/panda/src/putil/sparseArray.cxx @@ -0,0 +1,517 @@ +// Filename: sparseArray.cxx +// Created by: drose (14Feb07) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "sparseArray.h" +#include "bitArray.h" + +TypeHandle SparseArray::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::Constructor (from BitArray) +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +SparseArray:: +SparseArray(const BitArray &from) { + bool empty_bit = from.get_highest_bits(); + _inverse = empty_bit; + + int begin = 0; + bool current_state = from.get_bit(0); + int i = 0; + + // By including get_num_bits()--one more than the last bit--in this + // traversal, we guarantee that we will end on the empty_bit state + // (because the last bit we visit will be one of the highest_bits). + while (i <= from.get_num_bits()) { + if (from.get_bit(i) != current_state) { + // End of a run. + if (current_state != empty_bit) { + Subrange range(begin, i); + _subranges.push_back(range); + } + begin = i; + current_state = !current_state; + } + ++i; + } + + nassertv(current_state == empty_bit); +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::has_bits_in_common +// Access: Published +// Description: Returns true if this SparseArray has any "one" bits in +// common with the other one, false otherwise. +// +// This is equivalent to (array & other) != 0, but may +// be faster. +//////////////////////////////////////////////////////////////////// +bool SparseArray:: +has_bits_in_common(const SparseArray &other) const { + if (_inverse && other._inverse) { + // Yup, in fact we have an infinite number of bits in common. + return true; + } + + if (_inverse != other._inverse) { + // We'll handle this tricky case the lazy way. + return !(*this & other).is_zero(); + } + + // Actually, we'll handle this easy case the lazy way too. Maybe + // later we'll do this smarter. + return !(*this & other).is_zero(); +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::output +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void SparseArray:: +output(ostream &out) const { + out << "[ "; + if (_inverse) { + out << "all except: "; + } + Subranges::const_iterator si; + for (si = _subranges.begin(); si != _subranges.end(); ++si) { + if ((*si)._end == (*si)._begin + 1) { + // A single element. + out << (*si)._begin << ", "; + } else { + // A range of elements. + out << (*si)._begin << "-" << ((*si)._end - 1) << ", "; + } + } + out << "]"; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::compare_to +// Access: Published +// Description: Returns a number less than zero if this SparseArray +// sorts before the indicated other SparseArray, greater +// than zero if it sorts after, or 0 if they are +// equivalent. This is based on the same ordering +// defined by operator <. +//////////////////////////////////////////////////////////////////// +int SparseArray:: +compare_to(const SparseArray &other) const { + if (_inverse != other._inverse) { + return _inverse ? 1 : -1; + } + + Subranges::const_reverse_iterator ai = _subranges.rbegin(); + Subranges::const_reverse_iterator bi = other._subranges.rbegin(); + + while (ai != _subranges.rend() && bi != other._subranges.rend()) { + if ((*ai)._end < (*bi)._end) { + // B is higher. + return -1; + } else if ((*bi)._end < (*ai)._end) { + // A is higher. + return 1; + } else if ((*ai)._begin < (*bi)._begin) { + // A is higher. + return 1; + } else if ((*bi)._begin < (*ai)._begin) { + // B is higher. + return -1; + } + + --ai; + --bi; + } + + if (ai != _subranges.rend()) { + // A is higher. + return 1; + } + if (bi != other._subranges.rend()) { + // B is higher. + return -1; + } + + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::operator &= +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void SparseArray:: +operator &= (const SparseArray &other) { + // We do this the slow and stupid way. This could be done much + // better with a little effort, but I'm not at all sure it's worth + // the effort. If you need fast boolean operations, you should + // probably be using a BitArray. + + if (_inverse && other._inverse) { + do_union(other); + + } else if (!_inverse && !other._inverse) { + do_intersection(other); + + } else if (_inverse && !other._inverse) { + // a & b == b & a + (*this) = other & (*this); + + } else if (!_inverse && other._inverse) { + do_intersection_neg(other); + + } else { + // TODO. + nassertv(false); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::operator |= +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void SparseArray:: +operator |= (const SparseArray &other) { + // We do this the slow and stupid way. This could be done much + // better with a little effort, but I'm not at all sure it's worth + // the effort. If you need fast boolean operations, you should + // probably be using a BitArray. + + if (_inverse && other._inverse) { + do_intersection(other); + + } else if (!_inverse && !other._inverse) { + do_union(other); + + } else if (_inverse && !other._inverse) { + do_intersection_neg(other); + + } else if (!_inverse && other._inverse) { + // a | b == b | a + (*this) = other | (*this); + + } else { + nassertv(false); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::operator ^= +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void SparseArray:: +operator ^= (const SparseArray &other) { + // We do this the slow and stupid way. This could be done much + // better with a little effort, but I'm not at all sure it's worth + // the effort. If you need fast boolean operations, you should + // probably be using a BitArray. + + (*this) = ((*this) | other) & ~((*this) & other); +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::do_add_range +// Access: Private +// Description: Adds the consecutive range of integers beginning at +// begin, but not including end, to the array. If this +// range overlaps with another range already in the +// array, the result is the union. +//////////////////////////////////////////////////////////////////// +void SparseArray:: +do_add_range(int begin, int end) { + if (begin >= end) { + // Empty range. + return; + } + + Subrange range(begin, end); + Subranges::iterator si = _subranges.lower_bound(range); + if (si == _subranges.end()) { + if (!_subranges.empty()) { + si = _subranges.begin() + _subranges.size() - 1; + if ((*si)._end >= begin) { + // The new range expands the last element of the array to the right. + (*si)._end = end; + // It might also expand it to the left; fall through. + } else { + // The new range is completely after the last element of the array. + _subranges.push_back(range); + return; + } + + } else { + // The new range is completely after the last element of the array. + _subranges.push_back(range); + return; + } + } + + nassertv((*si)._end >= end); + + if ((*si)._begin > end) { + if (si != _subranges.begin()) { + Subranges::iterator si2 = si; + --si2; + if ((*si2)._end >= begin) { + // The new range expands an element within the array to the + // right (but does not intersect the next element). + (*si2)._end = end; + // It might also expand it to the left; fall through. + si = si2; + } else { + // The new range does not intersect any elements in the array. + _subranges.insert_unverified(si, range); + return; + } + } else { + // The new range does not intersect any elements in the array. + _subranges.insert_unverified(si, range); + return; + } + } + + // Check if the new range overlaps with any elements to the left. + while (si != _subranges.begin()) { + Subranges::iterator si2 = si; + --si2; + if ((*si2)._end >= begin) { + // The new range straddles two elements, so they get combined. + (*si2)._end = (*si)._end; + _subranges.erase(si); + } + si = si2; + } + + if ((*si)._begin > begin) { + // The new range expands an element to the left. + (*si)._begin = begin; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::do_remove_range +// Access: Private +// Description: Removes the consecutive range of integers beginning +// at begin, but not including end, from the array. +//////////////////////////////////////////////////////////////////// +void SparseArray:: +do_remove_range(int begin, int end) { + if (begin >= end) { + // Empty range. + return; + } + + Subrange range(begin, end); + Subranges::iterator si = _subranges.lower_bound(range); + if (si == _subranges.end()) { + if (!_subranges.empty()) { + si = _subranges.begin() + _subranges.size() - 1; + if ((*si)._end >= begin) { + // The new range shortens the last element of the array on the right. + end = min(end, (*si)._begin); + (*si)._end = end; + // It might also shorten it on the left; fall through. + } else { + // The new range is completely after the last element of the array. + return; + } + + } else { + // The new range is completely after the last element of the array. + return; + } + } + + nassertv((*si)._end >= end); + + if ((*si)._begin > end) { + if (si != _subranges.begin()) { + Subranges::iterator si2 = si; + --si2; + if ((*si2)._end >= begin) { + // The new range shortens an element within the array on the + // right (but does not intersect the next element). + end = min(end, (*si2)._begin); + (*si2)._end = end; + // It might also shorten it on the left; fall through. + si = si2; + } else { + // The new range does not intersect any elements in the array. + return; + } + } else { + // The new range does not intersect any elements in the array. + return; + } + } + + + if (end < (*si)._end) { + // We must split an element into two. + Subrange left_range((*si)._begin, begin); + (*si)._begin = end; + si = _subranges.insert_unverified(si, left_range); + } + + // Check if the new range removes any elements to the left. + while (begin <= (*si)._begin) { + if (si == _subranges.begin()) { + _subranges.erase(si); + return; + } + Subranges::iterator si2 = si; + --si2; + _subranges.erase(si); + si = si2; + } + + (*si)._end = min((*si)._end, begin); +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::do_has_any +// Access: Private +// Description: Returns true if any of the consecutive range of +// integers beginning at begin, but not including end, +// appear in the array. Note that this will return +// false for an empty range. +//////////////////////////////////////////////////////////////////// +bool SparseArray:: +do_has_any(int begin, int end) const { + if (begin >= end) { + // Empty range. + return false; + } + + Subrange range(begin, end); + Subranges::const_iterator si = _subranges.lower_bound(range); + if (si != _subranges.end() && end > (*si)._begin) { + return true; + } + if (si != _subranges.begin()) { + --si; + if (begin < (*si)._end) { + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::do_has_all +// Access: Private +// Description: Returns true if all of the consecutive range of +// integers beginning at begin, but not including end, +// appear in the array. Note that this will return +// true for an empty range. +//////////////////////////////////////////////////////////////////// +bool SparseArray:: +do_has_all(int begin, int end) const { + if (begin >= end) { + // Empty range. + return true; + } + + Subrange range(begin, end); + Subranges::const_iterator si = _subranges.lower_bound(range); + if (si != _subranges.end() && begin >= (*si)._begin) { + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::do_intersection +// Access: Private +// Description: Removes from this array all of the elements that do +// not appear in the other one. +//////////////////////////////////////////////////////////////////// +void SparseArray:: +do_intersection(const SparseArray &other) { + if (_subranges.empty()) { + return; + } + if (other._subranges.empty()) { + _subranges.clear(); + return; + } + + int my_begin = (*_subranges.begin())._begin; + int other_begin = (*other._subranges.begin())._begin; + do_remove_range(my_begin, other_begin); + + for (size_t i = 0; i < other._subranges.size() - 1; ++i) { + do_remove_range(other._subranges[i]._end, other._subranges[i + 1]._begin); + } + + int my_end = (*(_subranges.begin() + _subranges.size() - 1))._end; + int other_end = (*(other._subranges.begin() + other._subranges.size() - 1))._end; + do_remove_range(other_end, my_end); +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::do_union +// Access: Private +// Description: Adds to this array all of the elements that also +// appear in the other one. +//////////////////////////////////////////////////////////////////// +void SparseArray:: +do_union(const SparseArray &other) { + Subranges::const_iterator si; + for (si = other._subranges.begin(); si != other._subranges.end(); ++si) { + do_add_range((*si)._begin, (*si)._end); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::do_intersection_neg +// Access: Private +// Description: Removes from this array all of the elements that also +// appear in the other one. +//////////////////////////////////////////////////////////////////// +void SparseArray:: +do_intersection_neg(const SparseArray &other) { + Subranges::const_iterator si; + for (si = other._subranges.begin(); si != other._subranges.end(); ++si) { + do_remove_range((*si)._begin, (*si)._end); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SparseArray::do_shift +// Access: Private +// Description: Shifts all the elements in the array by the indicated +// amount. +//////////////////////////////////////////////////////////////////// +void SparseArray:: +do_shift(int offset) { + if (offset != 0) { + Subranges::iterator si; + for (si = _subranges.begin(); si != _subranges.end(); ++si) { + (*si)._begin += offset; + (*si)._end += offset; + } + } +} diff --git a/panda/src/putil/sparseArray.h b/panda/src/putil/sparseArray.h new file mode 100644 index 0000000000..330f4c3cd5 --- /dev/null +++ b/panda/src/putil/sparseArray.h @@ -0,0 +1,168 @@ +// Filename: sparseArray.h +// Created by: drose (14Feb07) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef SPARSEARRAY_H +#define SPARSEARRAY_H + +#include "pandabase.h" +#include "ordered_vector.h" + +class BitArray; + +//////////////////////////////////////////////////////////////////// +// Class : SparseArray +// Description : This class records a set of integers, where each +// integer is either present or not present in the set. +// +// It is similar in principle and in interface to a +// BitArray (which can be thought of as a set of +// integers, one integer corresponding to each different +// bit position), but the SparseArray is implemented as +// a list of min/max subrange lists, rather than as a +// bitmask. +// +// This makes it particularly efficient for storing sets +// which consist of large sections of consecutively +// included or consecutively excluded elements, with +// arbitrarily large integers, but particularly +// inefficient for doing boolean operations such as & or +// |. +// +// Also, unlike BitArray, the SparseArray can store +// negative integers. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA SparseArray { +PUBLISHED: + INLINE SparseArray(); + INLINE SparseArray(const SparseArray ©); + INLINE SparseArray &operator = (const SparseArray ©); + SparseArray(const BitArray &from); + + INLINE static SparseArray all_on(); + INLINE static SparseArray all_off(); + INLINE static SparseArray lower_on(int on_bits); + INLINE static SparseArray bit(int index); + INLINE static SparseArray range(int low_bit, int size); + + INLINE ~SparseArray(); + + INLINE static bool has_max_num_bits(); + INLINE static int get_max_num_bits(); + + INLINE int get_num_bits() const; + INLINE bool get_bit(int index) const; + INLINE void set_bit(int index); + INLINE void clear_bit(int index); + INLINE void set_bit_to(int index, bool value); + INLINE bool get_highest_bits() const; + INLINE bool is_zero() const; + INLINE bool is_all_on() const; + + INLINE bool has_any_of(int low_bit, int size) const; + INLINE bool has_all_of(int low_bit, int size) const; + INLINE void set_range(int low_bit, int size); + INLINE void clear_range(int low_bit, int size); + INLINE void set_range_to(bool value, int low_bit, int size); + + INLINE void invert_in_place(); + bool has_bits_in_common(const SparseArray &other) const; + INLINE void clear(); + + void output(ostream &out) const; + + INLINE bool operator == (const SparseArray &other) const; + INLINE bool operator != (const SparseArray &other) const; + INLINE bool operator < (const SparseArray &other) const; + int compare_to(const SparseArray &other) const; + + INLINE SparseArray + operator & (const SparseArray &other) const; + + INLINE SparseArray + operator | (const SparseArray &other) const; + + INLINE SparseArray + operator ^ (const SparseArray &other) const; + + INLINE SparseArray + operator ~ () const; + + INLINE SparseArray + operator << (int shift) const; + + INLINE SparseArray + operator >> (int shift) const; + + void operator &= (const SparseArray &other); + void operator |= (const SparseArray &other); + void operator ^= (const SparseArray &other); + INLINE void operator <<= (int shift); + INLINE void operator >>= (int shift); + + INLINE bool is_inverse() const; + INLINE int get_num_subranges() const; + INLINE int get_subrange_begin(int n) const; + INLINE int get_subrange_end(int n) const; + +private: + void do_add_range(int begin, int end); + void do_remove_range(int begin, int end); + bool do_has_any(int begin, int end) const; + bool do_has_all(int begin, int end) const; + + void do_intersection(const SparseArray &other); + void do_union(const SparseArray &other); + void do_intersection_neg(const SparseArray &other); + void do_shift(int offset); + + // The SparseArray is implemented as a set of non-overlapping + // Subranges. + class Subrange { + public: + INLINE Subrange(int begin, int end); + INLINE bool operator < (const Subrange &other) const; + + int _begin, _end; + }; + + typedef ov_set Subranges; + Subranges _subranges; + bool _inverse; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + register_type(_type_handle, "SparseArray"); + } + +private: + static TypeHandle _type_handle; +}; + +#include "sparseArray.I" + +INLINE ostream & +operator << (ostream &out, const SparseArray &array) { + array.output(out); + return out; +} + +#endif +