Browse Source

add set_python_tag() etc.

David Rose 20 years ago
parent
commit
7a249dde55

+ 122 - 1
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";
 }
 
 ////////////////////////////////////////////////////////////////////

+ 21 - 0
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

+ 12 - 0
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;
 

+ 115 - 0
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<PythonTagData::iterator, bool> 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

+ 70 - 2
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 &copy) :
   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<PythonTagData::iterator, bool> 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 << "]";

+ 23 - 0
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 <Python.h>
+
+#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<string, string, string_hash> TagData;
+#ifdef HAVE_PYTHON
+  typedef phash_map<string, PyObject *, string_hash> 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;