diff --git a/panda/src/pgraph/nodePath.I b/panda/src/pgraph/nodePath.I index 38af5c5115..809be27b35 100644 --- a/panda/src/pgraph/nodePath.I +++ b/panda/src/pgraph/nodePath.I @@ -1832,7 +1832,128 @@ get_net_tag(const string &key) const { //////////////////////////////////////////////////////////////////// INLINE bool NodePath:: has_net_tag(const string &key) const { - return find_net_tag(key).has_tag(key); + return !find_net_tag(key).is_empty(); +} + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_python_tag +// Access: Published +// Description: Associates an arbitrary Python object with a +// user-defined key which is stored on the node. This +// object has no meaning to Panda; but it is stored +// indefinitely on the node until it is requested again. +// +// Each unique key stores a different Python object. +// There is no effective limit on the number of +// different keys that may be stored or on the nature of +// any one key's object. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +set_python_tag(const string &key, PyObject *value) { + nassertv_always(!is_empty()); + node()->set_python_tag(key, value); +} +#endif // HAVE_PYTHON + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_python_tag +// Access: Published +// Description: Retrieves the Python object that was previously +// set on this node for the particular key, if any. If +// no object has been previously set, returns None. +// See also get_net_python_tag(). +//////////////////////////////////////////////////////////////////// +INLINE PyObject *NodePath:: +get_python_tag(const string &key) const { + // An empty NodePath quietly returns no tags. This makes + // get_net_python_tag() easier to implement. + if (is_empty()) { + return Py_None; + } + return node()->get_python_tag(key); +} +#endif // HAVE_PYTHON + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: NodePath::has_python_tag +// Access: Published +// Description: Returns true if a Python object has been defined on +// this node for the particular key (even if that value +// is the empty string), or false if no value has been +// set. See also has_net_python_tag(). +//////////////////////////////////////////////////////////////////// +INLINE bool NodePath:: +has_python_tag(const string &key) const { + // An empty NodePath quietly has no tags. This makes has_net_python_tag() + // easier to implement. + if (is_empty()) { + return false; + } + return node()->has_python_tag(key); +} +#endif // HAVE_PYTHON + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: NodePath::clear_python_tag +// Access: Published +// Description: Removes the Python object defined for this key on this +// particular node. After a call to clear_python_tag(), +// has_python_tag() will return false for the indicated +// key. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +clear_python_tag(const string &key) { + nassertv_always(!is_empty()); + node()->clear_python_tag(key); +} +#endif // HAVE_PYTHON + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_net_python_tag +// Access: Published +// Description: Returns the Python object that has been defined on +// this node, or the nearest ancestor node, for the +// indicated key. If no value has been defined for the +// indicated key on any ancestor node, returns None. +// See also get_python_tag(). +//////////////////////////////////////////////////////////////////// +INLINE PyObject *NodePath:: +get_net_python_tag(const string &key) const { + return find_net_python_tag(key).get_python_tag(key); +} +#endif // HAVE_PYTHON + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: NodePath::has_net_python_tag +// Access: Published +// Description: Returns true if the indicated Python object has been +// defined on this node or on any ancestor node, or +// false otherwise. See also has_python_tag(). +//////////////////////////////////////////////////////////////////// +INLINE bool NodePath:: +has_net_python_tag(const string &key) const { + return !find_net_python_tag(key).is_empty(); +} +#endif // HAVE_PYTHON + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::list_tags +// Access: Published +// Description: Lists the tags to the nout stream, one per line. See +// PandaNode::list_tags() for a variant that allows you +// to specify the output stream. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +list_tags() const { + nassertv_always(!is_empty()); + node()->list_tags(nout); + nout << "\n"; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index 5e01851850..f10777ab33 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/panda/src/pgraph/nodePath.cxx @@ -4998,6 +4998,27 @@ find_net_tag(const string &key) const { return get_parent().find_net_tag(key); } +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: NodePath::find_net_python_tag +// Access: Published +// Description: Returns the lowest ancestor of this node that +// contains a tag definition with the indicated key, if +// any, or an empty NodePath if no ancestor of this node +// contains this tag definition. See set_python_tag(). +//////////////////////////////////////////////////////////////////// +NodePath NodePath:: +find_net_python_tag(const string &key) const { + if (is_empty()) { + return NodePath::not_found(); + } + if (has_python_tag(key)) { + return *this; + } + return get_parent().find_net_python_tag(key); +} +#endif // HAVE_PYTHON + //////////////////////////////////////////////////////////////////// // Function: NodePath::write_bam_file // Access: Published diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index 8be880cf3d..ed17ac907c 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -727,6 +727,18 @@ PUBLISHED: INLINE bool has_net_tag(const string &key) const; NodePath find_net_tag(const string &key) const; +#ifdef HAVE_PYTHON + INLINE void set_python_tag(const string &key, PyObject *value); + INLINE PyObject *get_python_tag(const string &key) const; + INLINE bool has_python_tag(const string &key) const; + INLINE void clear_python_tag(const string &key); + INLINE PyObject *get_net_python_tag(const string &key) const; + INLINE bool has_net_python_tag(const string &key) const; + NodePath find_net_python_tag(const string &key) const; +#endif // HAVE_PYTHON + + INLINE void list_tags() const; + INLINE void set_name(const string &name); INLINE string get_name() const; diff --git a/panda/src/pgraph/pandaNode.I b/panda/src/pgraph/pandaNode.I index 769f0d0edd..6db614faa3 100644 --- a/panda/src/pgraph/pandaNode.I +++ b/panda/src/pgraph/pandaNode.I @@ -772,6 +772,121 @@ clear_tag(const string &key) { cdata->_tag_data.erase(key); } +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::set_python_tag +// Access: Published +// Description: Associates an arbitrary Python object with a +// user-defined key which is stored on the node. This +// is similar to set_tag(), except it can store any +// Python object instead of just a string. However, the +// Python object is not recorded to a bam file. +// +// Each unique key stores a different string value. +// There is no effective limit on the number of +// different keys that may be stored or on the length of +// any one key's value. +//////////////////////////////////////////////////////////////////// +INLINE void PandaNode:: +set_python_tag(const string &key, PyObject *value) { + CDWriter cdata(_cycler); + Py_XINCREF(value); + + pair result; + result = cdata->_python_tag_data.insert(PythonTagData::value_type(key, value)); + + if (!result.second) { + // The insert was unsuccessful; that means the key was already + // present in the map. In this case, we should decrement the + // original value's reference count and replace it with the new + // object. + PythonTagData::iterator ti = result.first; + PyObject *old_value = (*ti).second; + Py_XDECREF(old_value); + (*ti).second = value; + } +} +#endif // HAVE_PYTHON + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::get_python_tag +// Access: Published +// Description: Retrieves the Python object that was previously +// set on this node for the particular key, if any. If +// no value has been previously set, returns None. +//////////////////////////////////////////////////////////////////// +INLINE PyObject *PandaNode:: +get_python_tag(const string &key) const { + CDReader cdata(_cycler); + PythonTagData::const_iterator ti; + ti = cdata->_python_tag_data.find(key); + if (ti != cdata->_python_tag_data.end()) { + return (*ti).second; + } + return Py_None; +} +#endif // HAVE_PYTHON + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::has_python_tag +// Access: Published +// Description: Returns true if a Python object has been defined on +// this node for the particular key (even if that object +// is None), or false if no object has been set. +//////////////////////////////////////////////////////////////////// +INLINE bool PandaNode:: +has_python_tag(const string &key) const { + CDReader cdata(_cycler); + PythonTagData::const_iterator ti; + ti = cdata->_python_tag_data.find(key); + return (ti != cdata->_python_tag_data.end()); +} +#endif // HAVE_PYTHON + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::clear_python_tag +// Access: Published +// Description: Removes the Python object defined for this key on +// this particular node. After a call to +// clear_python_tag(), has_python_tag() will return +// false for the indicated key. +//////////////////////////////////////////////////////////////////// +INLINE void PandaNode:: +clear_python_tag(const string &key) { + CDWriter cdata(_cycler); + PythonTagData::iterator ti; + ti = cdata->_python_tag_data.find(key); + if (ti != cdata->_python_tag_data.end()) { + PyObject *value = (*ti).second; + Py_XDECREF(value); + cdata->_python_tag_data.erase(ti); + } +} +#endif // HAVE_PYTHON + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::has_tags +// Access: Published +// Description: Returns true if the node has any tags (or any Python +// tags) at all, false if it has none. +//////////////////////////////////////////////////////////////////// +INLINE bool PandaNode:: +has_tags() const { + CDReader cdata(_cycler); + if (!cdata->_tag_data.empty()) { + return true; + } +#ifdef HAVE_PYTHON + if (!cdata->_python_tag_data.empty()) { + return true; + } +#endif // HAVE_PYTHON + return false; +} + //////////////////////////////////////////////////////////////////// // Function: PandaNode::ls // Access: Published diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index feca4eeb7c..664930c676 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/panda/src/pgraph/pandaNode.cxx @@ -357,6 +357,20 @@ PandaNode:: #endif // NDEBUG remove_all_children(); + +#ifdef HAVE_PYTHON + { + // Free all of the Python objects held by this node. + CDReader cdata(_cycler); + PythonTagData::const_iterator ti; + for (ti = cdata->_python_tag_data.begin(); + ti != cdata->_python_tag_data.end(); + ++ti) { + PyObject *value = (*ti).second; + Py_XDECREF(value); + } + } +#endif // HAVE_PYTHON } //////////////////////////////////////////////////////////////////// @@ -385,6 +399,20 @@ PandaNode(const PandaNode ©) : cdata->_draw_mask = copy_cdata->_draw_mask; cdata->_into_collide_mask = copy_cdata->_into_collide_mask; cdata->_fixed_internal_bound = copy_cdata->_fixed_internal_bound; + +#ifdef HAVE_PYTHON + // Copy and increment all of the Python objects held by the other + // node. + cdata->_python_tag_data = copy_cdata->_python_tag_data; + PythonTagData::const_iterator ti; + for (ti = cdata->_python_tag_data.begin(); + ti != cdata->_python_tag_data.end(); + ++ti) { + PyObject *value = (*ti).second; + Py_XINCREF(value); + } +#endif // HAVE_PYTHON + } //////////////////////////////////////////////////////////////////// @@ -1248,6 +1276,31 @@ copy_tags(PandaNode *other) { ++ti) { cdataw->_tag_data[(*ti).first] = (*ti).second; } + +#ifdef HAVE_PYTHON + PythonTagData::const_iterator pti; + for (pti = cdatar->_python_tag_data.begin(); + pti != cdatar->_python_tag_data.end(); + ++pti) { + const string &key = (*pti).first; + PyObject *value = (*pti).second; + Py_XINCREF(value); + + pair result; + result = cdataw->_python_tag_data.insert(PythonTagData::value_type(key, value)); + + if (!result.second) { + // The insert was unsuccessful; that means the key was already + // present in the map. In this case, we should decrement the + // original value's reference count and replace it with the new + // object. + PythonTagData::iterator wpti = result.first; + PyObject *old_value = (*wpti).second; + Py_XDECREF(old_value); + (*wpti).second = value; + } + } +#endif // HAVE_PYTHON } //////////////////////////////////////////////////////////////////// @@ -1274,6 +1327,21 @@ list_tags(ostream &out, const string &separator) const { ++ti; } } + +#ifdef HAVE_PYTHON + if (!cdata->_python_tag_data.empty()) { + if (!cdata->_tag_data.empty()) { + out << separator; + } + PythonTagData::const_iterator ti = cdata->_python_tag_data.begin(); + out << (*ti).first; + ++ti; + while (ti != cdata->_python_tag_data.end()) { + out << separator << (*ti).first; + ++ti; + } + } +#endif // HAVE_PYTHON } //////////////////////////////////////////////////////////////////// @@ -1311,7 +1379,7 @@ void PandaNode:: write(ostream &out, int indent_level) const { indent(out, indent_level) << *this; CDReader cdata(_cycler); - if (!cdata->_tag_data.empty()) { + if (has_tags()) { out << " ["; list_tags(out, " "); out << "]"; @@ -2025,7 +2093,7 @@ void PandaNode:: r_list_descendants(ostream &out, int indent_level) const { CDReader cdata(_cycler); indent(out, indent_level) << *this; - if (!cdata->_tag_data.empty()) { + if (has_tags()) { out << " ["; list_tags(out, " "); out << "]"; diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index 787d4d25fb..e7173235b8 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/panda/src/pgraph/pandaNode.h @@ -41,6 +41,14 @@ #include "pointerToArray.h" #include "notify.h" +#ifdef HAVE_PYTHON + +#undef HAVE_LONG_LONG // NSPR and Python both define this. +#undef _POSIX_C_SOURCE +#include + +#endif // HAVE_PYTHON + class NodePathComponent; class CullTraverser; class CullTraverserData; @@ -159,6 +167,15 @@ PUBLISHED: INLINE string get_tag(const string &key) const; INLINE bool has_tag(const string &key) const; INLINE void clear_tag(const string &key); + +#ifdef HAVE_PYTHON + INLINE void set_python_tag(const string &key, PyObject *value); + INLINE PyObject *get_python_tag(const string &key) const; + INLINE bool has_python_tag(const string &key) const; + INLINE void clear_python_tag(const string &key); +#endif // HAVE_PYTHON + + INLINE bool has_tags() const; void copy_tags(PandaNode *other); void list_tags(ostream &out, const string &separator = "\n") const; @@ -286,6 +303,9 @@ private: // This is used to maintain a table of keyed data on each node, for // the user's purposes. typedef phash_map TagData; +#ifdef HAVE_PYTHON + typedef phash_map PythonTagData; +#endif // HAVE_PYTHON // This is the data that must be cycled between pipeline stages. @@ -322,6 +342,9 @@ private: NCPT(TransformState) _prev_transform; TagData _tag_data; +#ifdef HAVE_PYTHON + PythonTagData _python_tag_data; +#endif // HAVE_PYTHON // This is the draw_mask of this particular node. DrawMask _draw_mask;