diff --git a/direct/src/dcparser/dcPacker.cxx b/direct/src/dcparser/dcPacker.cxx index ce67a570bf..78e95be6cf 100644 --- a/direct/src/dcparser/dcPacker.cxx +++ b/direct/src/dcparser/dcPacker.cxx @@ -769,7 +769,7 @@ enquote_string(ostream &out, char quote_mark, const string &str) { if ((*pi) == quote_mark || (*pi) == '\\') { out << '\\' << (*pi); - } else if (!isprint(*pi)) { + } else if (!isprint(*pi) || (*pi) == '\t') { char buffer[10]; sprintf(buffer, "%02x", (unsigned char)(*pi)); out << "\\x" << buffer; diff --git a/direct/src/fsm/State.py b/direct/src/fsm/State.py index 3842b0844b..8c53d205fd 100644 --- a/direct/src/fsm/State.py +++ b/direct/src/fsm/State.py @@ -33,15 +33,13 @@ class State(DirectObject): if enterFunc.__func__ == oldFunction: # print 'found: ', enterFunc, oldFunction state.setEnterFunc(types.MethodType(newFunction, - enterFunc.__self__, - enterFunc.__self__.__class__)) + enterFunc.__self__)) count += 1 if type(exitFunc) == types.MethodType: if exitFunc.__func__ == oldFunction: # print 'found: ', exitFunc, oldFunction state.setExitFunc(types.MethodType(newFunction, - exitFunc.__self__, - exitFunc.__self__.__class__)) + exitFunc.__self__)) count += 1 return count @@ -215,18 +213,3 @@ class State(DirectObject): def __str__(self): return "State: name = %s, enter = %s, exit = %s, trans = %s, children = %s" %\ (self.__name, self.__enterFunc, self.__exitFunc, self.__transitions, self.__FSMList) - - - - - - - - - - - - - - - diff --git a/direct/src/interval/FunctionInterval.py b/direct/src/interval/FunctionInterval.py index db626c404e..04f313083c 100644 --- a/direct/src/interval/FunctionInterval.py +++ b/direct/src/interval/FunctionInterval.py @@ -37,8 +37,7 @@ class FunctionInterval(Interval.Interval): if ival.function.__func__ == oldFunction: # print 'found: ', ival.function, oldFunction ival.function = types.MethodType(newFunction, - ival.function.__self__, - ival.function.__self__.__class__) + ival.function.__self__) count += 1 return count diff --git a/direct/src/showbase/Messenger.py b/direct/src/showbase/Messenger.py index e4ba5ccda8..d99c71b3d1 100644 --- a/direct/src/showbase/Messenger.py +++ b/direct/src/showbase/Messenger.py @@ -464,8 +464,7 @@ class Messenger: # 'oldMethod: ' + repr(oldMethod) + '\n' + # 'newFunction: ' + repr(newFunction) + '\n') if (function == oldMethod): - newMethod = types.MethodType( - newFunction, method.__self__, method.__self__.__class__) + newMethod = types.MethodType(newFunction, method.__self__) params[0] = newMethod # Found it retrun true retFlag += 1 diff --git a/direct/src/showbase/PythonUtil.py b/direct/src/showbase/PythonUtil.py index 3c5142a717..7a5efc9dac 100644 --- a/direct/src/showbase/PythonUtil.py +++ b/direct/src/showbase/PythonUtil.py @@ -1561,7 +1561,7 @@ def appendStr(obj, st): return s oldStr = Functor(stringer, str(obj)) stringer = None - obj.__str__ = types.MethodType(Functor(appendedStr, oldStr, st), obj, obj.__class__) + obj.__str__ = types.MethodType(Functor(appendedStr, oldStr, st), obj) appendedStr = None return obj diff --git a/direct/src/task/Task.py b/direct/src/task/Task.py index f8d9156f4c..8f6bbd4766 100644 --- a/direct/src/task/Task.py +++ b/direct/src/task/Task.py @@ -593,9 +593,7 @@ class TaskManager: else: function = method if (function == oldMethod): - newMethod = types.MethodType(newFunction, - method.__self__, - method.__self__.__class__) + newMethod = types.MethodType(newFunction, method.__self__) task.setFunction(newMethod) # Found a match return 1 diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 522e00884e..9df12dddfa 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -8047,6 +8047,10 @@ output_quoted(ostream &out, int indent_level, const std::string &str, << '"'; continue; + case '\t': + out << "\\t"; + break; + default: if (!isprint(*si)) { out << "\\" << oct << std::setw(3) << std::setfill('0') << (unsigned int)(*si) diff --git a/dtool/src/prckeys/makePrcKey.cxx b/dtool/src/prckeys/makePrcKey.cxx index 23bf0914b0..fea188c87c 100644 --- a/dtool/src/prckeys/makePrcKey.cxx +++ b/dtool/src/prckeys/makePrcKey.cxx @@ -93,10 +93,13 @@ output_c_string(std::ostream &out, const string &string_name, last_nl = false; } - if (isprint(data_ptr[i])) { + if (data_ptr[i] == '\t') { + out << "\\t"; + } + else if (isprint(data_ptr[i])) { out << data_ptr[i]; - - } else { + } + else { out << "\\x" << std::hex << std::setw(2) << std::setfill('0') << (unsigned int)(unsigned char)data_ptr[i] << std::dec; } diff --git a/panda/src/audiotraits/fmodAudioSound.cxx b/panda/src/audiotraits/fmodAudioSound.cxx index d8560658a7..e81695969c 100644 --- a/panda/src/audiotraits/fmodAudioSound.cxx +++ b/panda/src/audiotraits/fmodAudioSound.cxx @@ -123,50 +123,62 @@ FmodAudioSound(AudioManager *manager, VirtualFile *file, bool positional) { << "Reading " << _file_name << " into memory (" << sound_info.length << " bytes)\n"; } - - } else if (file->get_system_info(info)) { - // The file exists on disk (or it's part of a multifile that exists on - // disk), so we can have FMod read the file directly. This is also - // safe, because FMod uses its own IO operations that don't involve - // Panda, so this can safely happen in an FMod thread. - os_filename = info.get_filename().to_os_specific(); - name_or_data = os_filename.c_str(); - sound_info.fileoffset = (unsigned int)info.get_start(); - sound_info.length = (unsigned int)info.get_size(); - flags |= FMOD_CREATESTREAM; - if (fmodAudio_cat.is_debug()) { - fmodAudio_cat.debug() - << "Streaming " << _file_name << " from disk (" << name_or_data - << ", " << sound_info.fileoffset << ", " << sound_info.length << ")\n"; - } - - } else { -#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) - // Otherwise, if the Panda threading system is compiled in, we can - // assign callbacks to read the file through the VFS. - name_or_data = (const char *)file; - sound_info.length = (unsigned int)info.get_size(); - sound_info.useropen = open_callback; - sound_info.userclose = close_callback; - sound_info.userread = read_callback; - sound_info.userseek = seek_callback; - flags |= FMOD_CREATESTREAM; - if (fmodAudio_cat.is_debug()) { - fmodAudio_cat.debug() - << "Streaming " << _file_name << " from disk using callbacks\n"; - } - -#else // HAVE_THREADS && !SIMPLE_THREADS - // Without threads, we can't safely read this file. - name_or_data = ""; - - fmodAudio_cat.warning() - << "Cannot stream " << _file_name << "; file is not literally on disk.\n"; -#endif + result = + _manager->_system->createSound(name_or_data, flags, &sound_info, &_sound); } + else { + result = FMOD_ERR_FILE_BAD; - result = - _manager->_system->createSound(name_or_data, flags, &sound_info, &_sound); + if (file->get_system_info(info)) { + // The file exists on disk (or it's part of a multifile that exists on + // disk), so we can have FMod read the file directly. This is also + // safe, because FMod uses its own IO operations that don't involve + // Panda, so this can safely happen in an FMod thread. + os_filename = info.get_filename().to_os_specific(); + name_or_data = os_filename.c_str(); + sound_info.fileoffset = (unsigned int)info.get_start(); + sound_info.length = (unsigned int)info.get_size(); + flags |= FMOD_CREATESTREAM; + if (fmodAudio_cat.is_debug()) { + fmodAudio_cat.debug() + << "Streaming " << _file_name << " from disk (" << name_or_data + << ", " << sound_info.fileoffset << ", " << sound_info.length << ")\n"; + } + + result = + _manager->_system->createSound(name_or_data, flags, &sound_info, &_sound); + } + + // If FMOD can't directly read the file (eg. if Panda is locking it for + // write, or it's compressed) we have to use the callback interface. + if (result == FMOD_ERR_FILE_BAD) { + #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + // Otherwise, if the Panda threading system is compiled in, we can + // assign callbacks to read the file through the VFS. + name_or_data = (const char *)file; + sound_info.fileoffset = 0; + sound_info.length = (unsigned int)info.get_size(); + sound_info.useropen = open_callback; + sound_info.userclose = close_callback; + sound_info.userread = read_callback; + sound_info.userseek = seek_callback; + flags |= FMOD_CREATESTREAM; + if (fmodAudio_cat.is_debug()) { + fmodAudio_cat.debug() + << "Streaming " << _file_name << " from disk using callbacks\n"; + } + result = + _manager->_system->createSound(name_or_data, flags, &sound_info, &_sound); + + #else // HAVE_THREADS && !SIMPLE_THREADS + // Without threads, we can't safely read this file. + name_or_data = ""; + + fmodAudio_cat.warning() + << "Cannot stream " << _file_name << "; file is not literally on disk.\n"; + #endif + } + } } if (result != FMOD_OK) { diff --git a/panda/src/mathutil/triangulator.cxx b/panda/src/mathutil/triangulator.cxx index f179b24e83..e13d856804 100644 --- a/panda/src/mathutil/triangulator.cxx +++ b/panda/src/mathutil/triangulator.cxx @@ -150,11 +150,7 @@ triangulate() { } */ - int attempts = 0; - while (construct_trapezoids(num_segments) != 0) { - nassertv_always(attempts++ < 100); - // If there's an error, re-shuffle the index and try again. Randomizer randomizer; for (i = 0; i < num_segments; ++i) { @@ -263,7 +259,7 @@ cleanup_polygon_indices(vector_int &polygon) { ++pi; } else { // This index is out-of-bounds; remove it. - polygon.erase(_polygon.begin() + pi); + polygon.erase(polygon.begin() + pi); } } @@ -275,14 +271,46 @@ cleanup_polygon_indices(vector_int &polygon) { ++pi; } else { // This vertex repeats the previous one; remove it. - polygon.erase(_polygon.begin() + pi); + polygon.erase(polygon.begin() + pi); } } - if (polygon.size() > 1 && _vertices[polygon.back()] == _vertices[_polygon.front()]) { + if (polygon.size() > 1 && _vertices[polygon.back()] == _vertices[polygon.front()]) { // The last vertex repeats the first one; remove it. polygon.pop_back(); } + + // Another pass over the polygons, this time removing any "tails". + while (polygon.size() >= 3) { + bool removed_any = false; + + int prevprev = polygon[polygon.size() - 2]; + int prev = polygon[polygon.size() - 1]; + + for (size_t i = 0; i < polygon.size(); ++i) { + int cur = polygon[i]; + if (_vertices[prevprev] == _vertices[cur]) { + // Cut off the tail. + removed_any = true; + polygon.erase(polygon.begin() + i); + if (i == 0) { + polygon.pop_back(); + } else { + polygon.erase(polygon.begin() + i - 1); + } + break; + } + + prevprev = prev; + prev = cur; + } + + // This might have been the tip of a longer tail, so if we removed + // something, go again. + if (!removed_any) { + break; + } + } } diff --git a/panda/src/pgui/pgEntry.cxx b/panda/src/pgui/pgEntry.cxx index c729a23038..97d200f8e1 100644 --- a/panda/src/pgui/pgEntry.cxx +++ b/panda/src/pgui/pgEntry.cxx @@ -203,11 +203,6 @@ press(const MouseWatcherParameter ¶m, bool background) { ButtonHandle button = param.get_button(); - if (button == KeyboardButton::tab()) { - // Tab. Ignore the entry. - return; - } - if (button == MouseButton::one() || button == MouseButton::two() || button == MouseButton::three() || @@ -326,7 +321,7 @@ keystroke(const MouseWatcherParameter ¶m, bool background) { int keycode = param.get_keycode(); - if (!isascii(keycode) || isprint(keycode)) { + if ((!isascii(keycode) || isprint(keycode)) && keycode != '\t') { // A normal visible character. Add a new character to the text entry, // if there's room. if (!_candidate_wtext.empty()) { diff --git a/pandatool/src/maya/maya_funcs.cxx b/pandatool/src/maya/maya_funcs.cxx index 2f21adefb3..b07b8089d1 100644 --- a/pandatool/src/maya/maya_funcs.cxx +++ b/pandatool/src/maya/maya_funcs.cxx @@ -519,8 +519,8 @@ describe_compound_attribute(MObject &node) { for (size_t i = 0; i < comp_attr.numChildren(); i++) { MObject child = comp_attr.child(i, &status); if (child.apiType() == MFn::kAttribute3Float){ - LRGBColor color; /* + LRGBColor color; if (get_vec3_attribute(child, "color", color)) { maya_cat.info() << "color: " << color << endl; } diff --git a/pandatool/src/mayaegg/mayaEggLoader.cxx b/pandatool/src/mayaegg/mayaEggLoader.cxx index a60e5d2372..a6ec611d7a 100644 --- a/pandatool/src/mayaegg/mayaEggLoader.cxx +++ b/pandatool/src/mayaegg/mayaEggLoader.cxx @@ -1572,7 +1572,8 @@ void MayaEggLoader::TraverseEggNode(EggNode *node, EggGroup *context, string del mayaloader_cat.debug() << delim+delstring << "found an EggTable: " << node->get_name() << endl; } } else if (node->is_of_type(EggXfmSAnim::get_class_type())) { - //MayaAnim *anim = GetAnim(DCAST(EggXfmSAnim, node)); + // Create a MayaAnim equivalent of the EggXfmSAnim + GetAnim(DCAST(EggXfmSAnim, node)); //anim->PrintData(); if (mayaloader_cat.is_debug()) { mayaloader_cat.debug() << delim+delstring << "found an EggXfmSAnim: " << node->get_name() << endl; diff --git a/tests/mathutil/test_triangulator.py b/tests/mathutil/test_triangulator.py new file mode 100644 index 0000000000..9e8e41c16e --- /dev/null +++ b/tests/mathutil/test_triangulator.py @@ -0,0 +1,73 @@ +from panda3d.core import Triangulator + + +def triangulate(vertices): + t = Triangulator() + for i, v in enumerate(vertices): + t.add_vertex(v) + t.add_polygon_vertex(i) + + t.triangulate() + + # Make sure that the result is consistent by starting each triangle with + # the lowest index value. That makes it easier to use predetermined values + # in the test cases. + result = set() + + for n in range(t.get_num_triangles()): + # Switch to lowest matching index value in case of duplicates. + v0 = vertices.index(vertices[t.get_triangle_v0(n)]) + v1 = vertices.index(vertices[t.get_triangle_v1(n)]) + v2 = vertices.index(vertices[t.get_triangle_v2(n)]) + if v1 < v0: + v0, v1, v2 = v1, v2, v0 + if v1 < v0: + v0, v1, v2 = v1, v2, v0 + result.add((v0, v1, v2)) + + return result + + +def test_triangulator_degenerate(): + assert not triangulate([]) + assert not triangulate([(0, 0)]) + assert not triangulate([(0, 0), (0, 0)]) + assert not triangulate([(0, 0), (1, 0)]) + assert not triangulate([(0, 0), (0, 0), (0, 0)]) + assert not triangulate([(0, 0), (1, 0), (1, 0)]) + assert not triangulate([(1, 0), (1, 0), (1, 0)]) + assert not triangulate([(1, 0), (0, 0), (1, 0)]) + assert not triangulate([(0, 0), (0, 0), (0, 0), (0, 0)]) + + +def test_triangulator_triangle(): + assert triangulate([(0, 0), (1, 0), (1, 1)]) == {(0, 1, 2)} + + +def test_triangulator_tail(): + # This triangle has a long "tail" where the polygon retraces its vertices. + assert triangulate([ + (0, -1), + (0, 1), + (1, 0), + (2, 0), + (3, 1), + (4, 0), + (5, 0), + (4, 0), + (3, 1), + (2, 0), + (1, 0), + ]) == {(0, 2, 1)} + + +def test_triangulator_hourglass(): + # Two triangles with touching tips, effectively. + assert triangulate([ + (-1, 1), + (-1, -1), + (0, 0), + (1, -1), + (1, 1), + (0, 0), + ]) == {(0, 1, 2), (2, 3, 4)}