<commit_before>/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2013-2015, Numenta, Inc.  Unless you have an agreement
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero Public License for more details.
 *
 * You should have received a copy of the GNU Affero Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

#include <stdexcept>

#include <capnp/any.h>

#include <nupic/engine/RegionImplFactory.hpp>
#include <nupic/engine/RegionImpl.hpp>
#include <nupic/engine/Region.hpp>
#include <nupic/engine/RegisteredRegionImpl.hpp>
#include <nupic/engine/Spec.hpp>
#include <nupic/os/DynamicLibrary.hpp>
#include <nupic/os/Path.hpp>
#include <nupic/os/OS.hpp>
#include <nupic/os/Env.hpp>
#include <nupic/ntypes/Value.hpp>
#include <nupic/ntypes/BundleIO.hpp>
#include <nupic/engine/YAMLUtils.hpp>
#include <nupic/engine/TestNode.hpp>
#include <nupic/regions/PyRegion.hpp>
#include <nupic/regions/VectorFileEffector.hpp>
#include <nupic/regions/VectorFileSensor.hpp>
#include <nupic/utils/Log.hpp>
#include <nupic/utils/StringUtils.hpp>

// from http://stackoverflow.com/a/9096509/1781435
#define stringify(x)  #x
#define expand_and_stringify(x) stringify(x)

namespace nupic
{
  // Path from site-packages to packages that contain NuPIC Python regions
  static std::map<const std::string, std::set<std::string>> pyRegions;

  // Mappings for C++ regions
  static std::map<const std::string, GenericRegisteredRegionImpl*> cppRegions;

  bool initializedRegions = false;

  // Allows the user to add custom regions
  void RegionImplFactory::registerPyRegion(const std::string module, const std::string className)
  {
    // Verify that no regions exist with the same className
    for (auto pyRegion : pyRegions)
    {
      if (pyRegion.second.find(className) != pyRegion.second.end())
      {
        NTA_THROW << "A pyRegion with name '" << className << "' already exists. "
                  << "Unregister the existing region or register the new region using a "
                  << "different name.";
      }
    }

    // Module hasn't been added yet
    if (pyRegions.find(module) == pyRegions.end())
    {
      pyRegions[module] = std::set<std::string>();
    }

    pyRegions[module].insert(className);
  }

  void RegionImplFactory::registerCPPRegion(const std::string name,
                                            GenericRegisteredRegionImpl * wrapper)
  {
    if (cppRegions.find(name) != cppRegions.end())
    {
      NTA_WARN << "A CPPRegion already exists with the name '"
               << name << "'. Overwriting it...";
    }
    cppRegions[name] = wrapper;
  }

  void RegionImplFactory::unregisterPyRegion(const std::string className)
  {
    for (auto pyRegion : pyRegions)
    {
      if (pyRegion.second.find(className) != pyRegion.second.end())
      {
        pyRegions.erase(pyRegion.first);
        return;
      }
    }
    NTA_WARN << "A pyRegion with name '" << className <<
    "' doesn't exist. Nothing to unregister...";
  }

  void RegionImplFactory::unregisterCPPRegion(const std::string name)
  {
    if (cppRegions.find(name) != cppRegions.end())
    {
      cppRegions.erase(name);
      return;
    }
  }

  class DynamicPythonLibrary
  {
    typedef void (*initPythonFunc)();
    typedef void (*finalizePythonFunc)();
    typedef void * (*createSpecFunc)(const char *, void **, const char *);
    typedef int (*destroySpecFunc)(const char *, const char *);
    typedef void * (*createPyNodeFunc)(const char *, void *, void *, void **, const char *);
    typedef void * (*deserializePyNodeFunc)(const char *, void *, void *, void *, const char *);
    typedef void * (*deserializePyNodeProtoFunc)(const char *, void *, void *, void *, const char *);
  public:
    DynamicPythonLibrary() :
      initPython_(nullptr),
      finalizePython_(nullptr),
      createSpec_(nullptr),
      destroySpec_(nullptr),
      createPyNode_(nullptr)
    {
      initPython_ = (initPythonFunc) PyRegion::NTA_initPython;
      finalizePython_ = (finalizePythonFunc) PyRegion::NTA_finalizePython;
      createPyNode_ = (createPyNodeFunc) PyRegion::NTA_createPyNode;
      deserializePyNode_ = (deserializePyNodeFunc) PyRegion::NTA_deserializePyNode;
      deserializePyNodeProto_ = (deserializePyNodeProtoFunc) PyRegion::NTA_deserializePyNodeProto;
      createSpec_ = (createSpecFunc) PyRegion::NTA_createSpec;
      destroySpec_ = (destroySpecFunc) PyRegion::NTA_destroySpec;

      (*initPython_)();
    }

    ~DynamicPythonLibrary()
    {
      //NTA_DEBUG << "In DynamicPythonLibrary Destructor";
      if (finalizePython_)
        finalizePython_();
    }

    void * createSpec(std::string nodeType, void ** exception, std::string className)
    {
      //NTA_DEBUG << "RegionImplFactory::createSpec(" << nodeType << ")";
      return (*createSpec_)(nodeType.c_str(), exception, className.c_str());
    }

    int destroySpec(std::string nodeType, std::string& className)
    {
      NTA_INFO << "destroySpec(" << nodeType << ")";
      return (*destroySpec_)(nodeType.c_str(), className.c_str());
    }

    void * createPyNode(const std::string& nodeType,
                        ValueMap * nodeParams,
                        Region * region,
                        void ** exception,
                        const std::string& className)
    {
      //NTA_DEBUG << "RegionImplFactory::createPyNode(" << nodeType << ")";
      return (*createPyNode_)(nodeType.c_str(),
                              reinterpret_cast<void *>(nodeParams),
                              reinterpret_cast<void*>(region),
                              exception,
                              className.c_str());

    }

    void * deserializePyNode(const std::string& nodeType,
                             BundleIO* bundle,
                             Region * region,
                             void ** exception,
                             const std::string& className)
    {
      //NTA_DEBUG << "RegionImplFactory::deserializePyNode(" << nodeType << ")";
      return (*deserializePyNode_)(nodeType.c_str(),
                                   reinterpret_cast<void*>(bundle),
                                   reinterpret_cast<void*>(region),
                                   exception,
                                   className.c_str());
    }

    void * deserializePyNodeProto(const std::string& nodeType,
                                  capnp::AnyPointer::Reader* proto,
                                  Region * region,
                                  void ** exception,
                                  const std::string& className)
    {
      //NTA_DEBUG << "RegionImplFactory::deserializePyNode(" << nodeType << ")";
      return (*deserializePyNodeProto_)(nodeType.c_str(),
                                        reinterpret_cast<void*>(proto),
                                        reinterpret_cast<void*>(region),
                                        exception,
                                        className.c_str());
    }

    const std::string& getRootDir() const
    {
      return rootDir_;
    }

  private:
    std::string rootDir_;
    boost::shared_ptr<DynamicLibrary> pynodeLibrary_;
    initPythonFunc initPython_;
    finalizePythonFunc finalizePython_;
    createSpecFunc createSpec_;
    destroySpecFunc destroySpec_;
    createPyNodeFunc createPyNode_;
    deserializePyNodeFunc deserializePyNode_;
    deserializePyNodeProtoFunc deserializePyNodeProto_;
  };

RegionImplFactory & RegionImplFactory::getInstance()
{
  static RegionImplFactory instance;

  // Initialize Regions
  if (!initializedRegions)
  {
    // Create C++ regions
    cppRegions["TestNode"] = new RegisteredRegionImpl<TestNode>();
    cppRegions["VectorFileEffector"] = new RegisteredRegionImpl<VectorFileEffector>();
    cppRegions["VectorFileSensor"] = new RegisteredRegionImpl<VectorFileSensor>();

    initializedRegions = true;
  }

  return instance;
}

// This function creates either a NuPIC 2 or NuPIC 1 Python node
static RegionImpl * createPyNode(DynamicPythonLibrary * pyLib,
                                 const std::string & nodeType,
                                 ValueMap * nodeParams,
                                 Region * region)
{
  std::string className(nodeType.c_str() + 3);
  for (auto pyr=pyRegions.begin(); pyr!=pyRegions.end(); pyr++)
  {
    const std::string module = pyr->first;
    std::set<std::string> classes = pyr->second;

    // This module contains the class
    if (classes.find(className) != classes.end())
    {
      void * exception = nullptr;
      void * node = pyLib->createPyNode(module, nodeParams, region, &exception, className);
      if (node)
      {
        return static_cast<RegionImpl*>(node);
      }
    }
  }

  NTA_THROW << "Unable to create region " << region->getName() << " of type " << className;
  return nullptr;
}

// This function deserializes either a NuPIC 2 or NuPIC 1 Python node
static RegionImpl * deserializePyNode(DynamicPythonLibrary * pyLib,
                                      const std::string & nodeType,
                                      BundleIO & bundle,
                                      Region * region)
{
  std::string className(nodeType.c_str() + 3);
  for (auto pyr=pyRegions.begin(); pyr!=pyRegions.end(); pyr++)
  {
    const std::string module = pyr->first;
    std::set<std::string> classes = pyr->second;

    // This module contains the class
    if (classes.find(className) != classes.end())
    {
      void * exception = nullptr;
      void * node = pyLib->deserializePyNode(module, &bundle, region, &exception, className);
      if (node)
      {
        return static_cast<RegionImpl*>(node);
      }
    }
  }

  NTA_THROW << "Unable to deserialize region " << region->getName() << " of type " << className;
  return nullptr;

}

static RegionImpl * deserializePyNode(DynamicPythonLibrary * pyLib,
                                      const std::string & nodeType,
                                      capnp::AnyPointer::Reader& proto,
                                      Region * region)
{
  std::string className(nodeType.c_str() + 3);
  for (auto pyr=pyRegions.begin(); pyr!=pyRegions.end(); pyr++)
  {
    const std::string module = pyr->first;
    std::set<std::string> classes = pyr->second;

    // This module contains the class
    if (classes.find(className) != classes.end())
    {
      void * exception = nullptr;
      void * node = pyLib->deserializePyNodeProto(module, &proto, region, &exception, className);
      if (node)
      {
        return static_cast<RegionImpl*>(node);
      }
    }
  }

  NTA_THROW << "Unable to deserialize region " << region->getName() << " of type " << className;
  return nullptr;
}

RegionImpl* RegionImplFactory::createRegionImpl(const std::string nodeType,
                                                const std::string nodeParams,
                                                Region* region)
{

  RegionImpl *impl = nullptr;
  Spec *ns = getSpec(nodeType);
  ValueMap vm = YAMLUtils::toValueMap(
    nodeParams.c_str(),
    ns->parameters,
    nodeType,
    region->getName());

  if (cppRegions.find(nodeType) != cppRegions.end())
  {
    impl = cppRegions[nodeType]->createRegionImpl(vm, region);
  }
  else if ((nodeType.find(std::string("py.")) == 0))
  {
    if (!pyLib_)
      pyLib_ = boost::shared_ptr<DynamicPythonLibrary>(new DynamicPythonLibrary());

    impl = createPyNode(pyLib_.get(), nodeType, &vm, region);
  } else
  {
    NTA_THROW << "Unsupported node type '" << nodeType << "'";
  }

  return impl;

}

RegionImpl* RegionImplFactory::deserializeRegionImpl(const std::string nodeType,
                                                     BundleIO& bundle,
                                                     Region* region)
{

  RegionImpl *impl = nullptr;

  if (cppRegions.find(nodeType) != cppRegions.end())
  {
    impl = cppRegions[nodeType]->deserializeRegionImpl(bundle, region);
  }
  else if (StringUtils::startsWith(nodeType, "py."))
  {
    if (!pyLib_)
      pyLib_ = boost::shared_ptr<DynamicPythonLibrary>(new DynamicPythonLibrary());

    impl = deserializePyNode(pyLib_.get(), nodeType, bundle, region);
  } else
  {
    NTA_THROW << "Unsupported node type '" << nodeType << "'";
  }
  return impl;

}

RegionImpl* RegionImplFactory::deserializeRegionImpl(
    const std::string nodeType,
    capnp::AnyPointer::Reader& proto,
    Region* region)
{
  RegionImpl *impl = nullptr;

  if (cppRegions.find(nodeType) != cppRegions.end())
  {
    impl = cppRegions[nodeType]->deserializeRegionImpl(proto, region);
  }
  else if (StringUtils::startsWith(nodeType, "py."))
  {
    if (!pyLib_)
     pyLib_ = boost::shared_ptr<DynamicPythonLibrary>(new DynamicPythonLibrary());

    impl = deserializePyNode(pyLib_.get(), nodeType, proto, region);
  }
  else
  {
    NTA_THROW << "Unsupported node type '" << nodeType << "'";
  }
  return impl;
}

// This function returns the node spec of a NuPIC 2 or NuPIC 1 Python node
static Spec * getPySpec(DynamicPythonLibrary * pyLib,
                                const std::string & nodeType)
{
  std::string className(nodeType.c_str() + 3);
  for (auto pyr=pyRegions.begin(); pyr!=pyRegions.end(); pyr++)
  {
    const std::string module = pyr->first;
    std::set<std::string> classes = pyr->second;

    // This module contains the class
    if (classes.find(className) != classes.end())
    {
      void * exception = nullptr;
      void * ns = pyLib->createSpec(module, &exception, className);
      if (ns)
      {
        return (Spec *)ns;
      }
    }
  }

  NTA_THROW << "Matching Python module for " << className << " not found.";
}

Spec * RegionImplFactory::getSpec(const std::string nodeType)
{
  std::map<std::string, Spec*>::iterator it;
  // return from cache if we already have it
  it = nodespecCache_.find(nodeType);
  if (it != nodespecCache_.end())
    return it->second;

  // grab the nodespec and cache it
  // one entry per supported node type
  Spec * ns = nullptr;
  if (cppRegions.find(nodeType) != cppRegions.end())
  {
    ns = cppRegions[nodeType]->createSpec();
  }
  else if (nodeType.find(std::string("py.")) == 0)
  {
    if (!pyLib_)
      pyLib_ = boost::shared_ptr<DynamicPythonLibrary>(new DynamicPythonLibrary());

    ns = getPySpec(pyLib_.get(), nodeType);
  }
  else
  {
    NTA_THROW << "getSpec() -- Unsupported node type '" << nodeType << "'";
  }

  if (!ns)
    NTA_THROW << "Unable to get node spec for: " << nodeType;

  nodespecCache_[nodeType] = ns;
  return ns;
}

void RegionImplFactory::cleanup()
{
  std::map<std::string, Spec*>::iterator ns;
  // destroy all nodespecs
  for (ns = nodespecCache_.begin(); ns != nodespecCache_.end(); ns++)
  {
    assert(ns->second != nullptr);
    // PyNode node specs are destroyed by the C++ PyNode
    if (ns->first.substr(0, 3) == "py.")
    {
      std::string noClass = "";
      pyLib_->destroySpec(ns->first, noClass);
    }
    else
    {
      delete ns->second;
    }

    ns->second = nullptr;
  }

  nodespecCache_.clear();

  // destroy all RegisteredRegionImpls
  for (auto rri = cppRegions.begin(); rri != cppRegions.end(); rri++)
  {
    NTA_ASSERT(rri->second != nullptr);
    delete rri->second;
    rri->second = nullptr;
  }

  cppRegions.clear();
  initializedRegions = false;

  // Never release the Python dynamic library!
  // This is due to cleanup issues of Python itself
  // See: http://docs.python.org/c-api/init.html#Py_Finalize
  //pyLib_.reset();
}

}
<commit_msg>Remove extra commented-out code<commit_after>/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2013-2015, Numenta, Inc.  Unless you have an agreement
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero Public License for more details.
 *
 * You should have received a copy of the GNU Affero Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

#include <stdexcept>

#include <capnp/any.h>

#include <nupic/engine/RegionImplFactory.hpp>
#include <nupic/engine/RegionImpl.hpp>
#include <nupic/engine/Region.hpp>
#include <nupic/engine/RegisteredRegionImpl.hpp>
#include <nupic/engine/Spec.hpp>
#include <nupic/os/DynamicLibrary.hpp>
#include <nupic/os/Path.hpp>
#include <nupic/os/OS.hpp>
#include <nupic/os/Env.hpp>
#include <nupic/ntypes/Value.hpp>
#include <nupic/ntypes/BundleIO.hpp>
#include <nupic/engine/YAMLUtils.hpp>
#include <nupic/engine/TestNode.hpp>
#include <nupic/regions/PyRegion.hpp>
#include <nupic/regions/VectorFileEffector.hpp>
#include <nupic/regions/VectorFileSensor.hpp>
#include <nupic/utils/Log.hpp>
#include <nupic/utils/StringUtils.hpp>

// from http://stackoverflow.com/a/9096509/1781435
#define stringify(x)  #x
#define expand_and_stringify(x) stringify(x)

namespace nupic
{
  // Path from site-packages to packages that contain NuPIC Python regions
  static std::map<const std::string, std::set<std::string>> pyRegions;

  // Mappings for C++ regions
  static std::map<const std::string, GenericRegisteredRegionImpl*> cppRegions;

  bool initializedRegions = false;

  // Allows the user to add custom regions
  void RegionImplFactory::registerPyRegion(const std::string module, const std::string className)
  {
    // Verify that no regions exist with the same className
    for (auto pyRegion : pyRegions)
    {
      if (pyRegion.second.find(className) != pyRegion.second.end())
      {
        NTA_THROW << "A pyRegion with name '" << className << "' already exists. "
                  << "Unregister the existing region or register the new region using a "
                  << "different name.";
      }
    }

    // Module hasn't been added yet
    if (pyRegions.find(module) == pyRegions.end())
    {
      pyRegions[module] = std::set<std::string>();
    }

    pyRegions[module].insert(className);
  }

  void RegionImplFactory::registerCPPRegion(const std::string name,
                                            GenericRegisteredRegionImpl * wrapper)
  {
    if (cppRegions.find(name) != cppRegions.end())
    {
      NTA_WARN << "A CPPRegion already exists with the name '"
               << name << "'. Overwriting it...";
    }
    cppRegions[name] = wrapper;
  }

  void RegionImplFactory::unregisterPyRegion(const std::string className)
  {
    for (auto pyRegion : pyRegions)
    {
      if (pyRegion.second.find(className) != pyRegion.second.end())
      {
        pyRegions.erase(pyRegion.first);
        return;
      }
    }
    NTA_WARN << "A pyRegion with name '" << className <<
    "' doesn't exist. Nothing to unregister...";
  }

  void RegionImplFactory::unregisterCPPRegion(const std::string name)
  {
    if (cppRegions.find(name) != cppRegions.end())
    {
      cppRegions.erase(name);
      return;
    }
  }

  class DynamicPythonLibrary
  {
    typedef void (*initPythonFunc)();
    typedef void (*finalizePythonFunc)();
    typedef void * (*createSpecFunc)(const char *, void **, const char *);
    typedef int (*destroySpecFunc)(const char *, const char *);
    typedef void * (*createPyNodeFunc)(const char *, void *, void *, void **, const char *);
    typedef void * (*deserializePyNodeFunc)(const char *, void *, void *, void *, const char *);
    typedef void * (*deserializePyNodeProtoFunc)(const char *, void *, void *, void *, const char *);
  public:
    DynamicPythonLibrary() :
      initPython_(nullptr),
      finalizePython_(nullptr),
      createSpec_(nullptr),
      destroySpec_(nullptr),
      createPyNode_(nullptr)
    {
      initPython_ = (initPythonFunc) PyRegion::NTA_initPython;
      finalizePython_ = (finalizePythonFunc) PyRegion::NTA_finalizePython;
      createPyNode_ = (createPyNodeFunc) PyRegion::NTA_createPyNode;
      deserializePyNode_ = (deserializePyNodeFunc) PyRegion::NTA_deserializePyNode;
      deserializePyNodeProto_ = (deserializePyNodeProtoFunc) PyRegion::NTA_deserializePyNodeProto;
      createSpec_ = (createSpecFunc) PyRegion::NTA_createSpec;
      destroySpec_ = (destroySpecFunc) PyRegion::NTA_destroySpec;

      (*initPython_)();
    }

    ~DynamicPythonLibrary()
    {
      if (finalizePython_)
        finalizePython_();
    }

    void * createSpec(std::string nodeType, void ** exception, std::string className)
    {
      return (*createSpec_)(nodeType.c_str(), exception, className.c_str());
    }

    int destroySpec(std::string nodeType, std::string& className)
    {
      NTA_INFO << "destroySpec(" << nodeType << ")";
      return (*destroySpec_)(nodeType.c_str(), className.c_str());
    }

    void * createPyNode(const std::string& nodeType,
                        ValueMap * nodeParams,
                        Region * region,
                        void ** exception,
                        const std::string& className)
    {
      return (*createPyNode_)(nodeType.c_str(),
                              reinterpret_cast<void *>(nodeParams),
                              reinterpret_cast<void*>(region),
                              exception,
                              className.c_str());

    }

    void * deserializePyNode(const std::string& nodeType,
                             BundleIO* bundle,
                             Region * region,
                             void ** exception,
                             const std::string& className)
    {
      return (*deserializePyNode_)(nodeType.c_str(),
                                   reinterpret_cast<void*>(bundle),
                                   reinterpret_cast<void*>(region),
                                   exception,
                                   className.c_str());
    }

    void * deserializePyNodeProto(const std::string& nodeType,
                                  capnp::AnyPointer::Reader* proto,
                                  Region * region,
                                  void ** exception,
                                  const std::string& className)
    {
      return (*deserializePyNodeProto_)(nodeType.c_str(),
                                        reinterpret_cast<void*>(proto),
                                        reinterpret_cast<void*>(region),
                                        exception,
                                        className.c_str());
    }

    const std::string& getRootDir() const
    {
      return rootDir_;
    }

  private:
    std::string rootDir_;
    boost::shared_ptr<DynamicLibrary> pynodeLibrary_;
    initPythonFunc initPython_;
    finalizePythonFunc finalizePython_;
    createSpecFunc createSpec_;
    destroySpecFunc destroySpec_;
    createPyNodeFunc createPyNode_;
    deserializePyNodeFunc deserializePyNode_;
    deserializePyNodeProtoFunc deserializePyNodeProto_;
  };

RegionImplFactory & RegionImplFactory::getInstance()
{
  static RegionImplFactory instance;

  // Initialize Regions
  if (!initializedRegions)
  {
    // Create C++ regions
    cppRegions["TestNode"] = new RegisteredRegionImpl<TestNode>();
    cppRegions["VectorFileEffector"] = new RegisteredRegionImpl<VectorFileEffector>();
    cppRegions["VectorFileSensor"] = new RegisteredRegionImpl<VectorFileSensor>();

    initializedRegions = true;
  }

  return instance;
}

// This function creates either a NuPIC 2 or NuPIC 1 Python node
static RegionImpl * createPyNode(DynamicPythonLibrary * pyLib,
                                 const std::string & nodeType,
                                 ValueMap * nodeParams,
                                 Region * region)
{
  std::string className(nodeType.c_str() + 3);
  for (auto pyr=pyRegions.begin(); pyr!=pyRegions.end(); pyr++)
  {
    const std::string module = pyr->first;
    std::set<std::string> classes = pyr->second;

    // This module contains the class
    if (classes.find(className) != classes.end())
    {
      void * exception = nullptr;
      void * node = pyLib->createPyNode(module, nodeParams, region, &exception, className);
      if (node)
      {
        return static_cast<RegionImpl*>(node);
      }
    }
  }

  NTA_THROW << "Unable to create region " << region->getName() << " of type " << className;
  return nullptr;
}

// This function deserializes either a NuPIC 2 or NuPIC 1 Python node
static RegionImpl * deserializePyNode(DynamicPythonLibrary * pyLib,
                                      const std::string & nodeType,
                                      BundleIO & bundle,
                                      Region * region)
{
  std::string className(nodeType.c_str() + 3);
  for (auto pyr=pyRegions.begin(); pyr!=pyRegions.end(); pyr++)
  {
    const std::string module = pyr->first;
    std::set<std::string> classes = pyr->second;

    // This module contains the class
    if (classes.find(className) != classes.end())
    {
      void * exception = nullptr;
      void * node = pyLib->deserializePyNode(module, &bundle, region, &exception, className);
      if (node)
      {
        return static_cast<RegionImpl*>(node);
      }
    }
  }

  NTA_THROW << "Unable to deserialize region " << region->getName() << " of type " << className;
  return nullptr;

}

static RegionImpl * deserializePyNode(DynamicPythonLibrary * pyLib,
                                      const std::string & nodeType,
                                      capnp::AnyPointer::Reader& proto,
                                      Region * region)
{
  std::string className(nodeType.c_str() + 3);
  for (auto pyr=pyRegions.begin(); pyr!=pyRegions.end(); pyr++)
  {
    const std::string module = pyr->first;
    std::set<std::string> classes = pyr->second;

    // This module contains the class
    if (classes.find(className) != classes.end())
    {
      void * exception = nullptr;
      void * node = pyLib->deserializePyNodeProto(module, &proto, region, &exception, className);
      if (node)
      {
        return static_cast<RegionImpl*>(node);
      }
    }
  }

  NTA_THROW << "Unable to deserialize region " << region->getName() << " of type " << className;
  return nullptr;
}

RegionImpl* RegionImplFactory::createRegionImpl(const std::string nodeType,
                                                const std::string nodeParams,
                                                Region* region)
{

  RegionImpl *impl = nullptr;
  Spec *ns = getSpec(nodeType);
  ValueMap vm = YAMLUtils::toValueMap(
    nodeParams.c_str(),
    ns->parameters,
    nodeType,
    region->getName());

  if (cppRegions.find(nodeType) != cppRegions.end())
  {
    impl = cppRegions[nodeType]->createRegionImpl(vm, region);
  }
  else if ((nodeType.find(std::string("py.")) == 0))
  {
    if (!pyLib_)
      pyLib_ = boost::shared_ptr<DynamicPythonLibrary>(new DynamicPythonLibrary());

    impl = createPyNode(pyLib_.get(), nodeType, &vm, region);
  } else
  {
    NTA_THROW << "Unsupported node type '" << nodeType << "'";
  }

  return impl;

}

RegionImpl* RegionImplFactory::deserializeRegionImpl(const std::string nodeType,
                                                     BundleIO& bundle,
                                                     Region* region)
{

  RegionImpl *impl = nullptr;

  if (cppRegions.find(nodeType) != cppRegions.end())
  {
    impl = cppRegions[nodeType]->deserializeRegionImpl(bundle, region);
  }
  else if (StringUtils::startsWith(nodeType, "py."))
  {
    if (!pyLib_)
      pyLib_ = boost::shared_ptr<DynamicPythonLibrary>(new DynamicPythonLibrary());

    impl = deserializePyNode(pyLib_.get(), nodeType, bundle, region);
  } else
  {
    NTA_THROW << "Unsupported node type '" << nodeType << "'";
  }
  return impl;

}

RegionImpl* RegionImplFactory::deserializeRegionImpl(
    const std::string nodeType,
    capnp::AnyPointer::Reader& proto,
    Region* region)
{
  RegionImpl *impl = nullptr;

  if (cppRegions.find(nodeType) != cppRegions.end())
  {
    impl = cppRegions[nodeType]->deserializeRegionImpl(proto, region);
  }
  else if (StringUtils::startsWith(nodeType, "py."))
  {
    if (!pyLib_)
     pyLib_ = boost::shared_ptr<DynamicPythonLibrary>(new DynamicPythonLibrary());

    impl = deserializePyNode(pyLib_.get(), nodeType, proto, region);
  }
  else
  {
    NTA_THROW << "Unsupported node type '" << nodeType << "'";
  }
  return impl;
}

// This function returns the node spec of a NuPIC 2 or NuPIC 1 Python node
static Spec * getPySpec(DynamicPythonLibrary * pyLib,
                                const std::string & nodeType)
{
  std::string className(nodeType.c_str() + 3);
  for (auto pyr=pyRegions.begin(); pyr!=pyRegions.end(); pyr++)
  {
    const std::string module = pyr->first;
    std::set<std::string> classes = pyr->second;

    // This module contains the class
    if (classes.find(className) != classes.end())
    {
      void * exception = nullptr;
      void * ns = pyLib->createSpec(module, &exception, className);
      if (ns)
      {
        return (Spec *)ns;
      }
    }
  }

  NTA_THROW << "Matching Python module for " << className << " not found.";
}

Spec * RegionImplFactory::getSpec(const std::string nodeType)
{
  std::map<std::string, Spec*>::iterator it;
  // return from cache if we already have it
  it = nodespecCache_.find(nodeType);
  if (it != nodespecCache_.end())
    return it->second;

  // grab the nodespec and cache it
  // one entry per supported node type
  Spec * ns = nullptr;
  if (cppRegions.find(nodeType) != cppRegions.end())
  {
    ns = cppRegions[nodeType]->createSpec();
  }
  else if (nodeType.find(std::string("py.")) == 0)
  {
    if (!pyLib_)
      pyLib_ = boost::shared_ptr<DynamicPythonLibrary>(new DynamicPythonLibrary());

    ns = getPySpec(pyLib_.get(), nodeType);
  }
  else
  {
    NTA_THROW << "getSpec() -- Unsupported node type '" << nodeType << "'";
  }

  if (!ns)
    NTA_THROW << "Unable to get node spec for: " << nodeType;

  nodespecCache_[nodeType] = ns;
  return ns;
}

void RegionImplFactory::cleanup()
{
  std::map<std::string, Spec*>::iterator ns;
  // destroy all nodespecs
  for (ns = nodespecCache_.begin(); ns != nodespecCache_.end(); ns++)
  {
    assert(ns->second != nullptr);
    // PyNode node specs are destroyed by the C++ PyNode
    if (ns->first.substr(0, 3) == "py.")
    {
      std::string noClass = "";
      pyLib_->destroySpec(ns->first, noClass);
    }
    else
    {
      delete ns->second;
    }

    ns->second = nullptr;
  }

  nodespecCache_.clear();

  // destroy all RegisteredRegionImpls
  for (auto rri = cppRegions.begin(); rri != cppRegions.end(); rri++)
  {
    NTA_ASSERT(rri->second != nullptr);
    delete rri->second;
    rri->second = nullptr;
  }

  cppRegions.clear();
  initializedRegions = false;

  // Never release the Python dynamic library!
  // This is due to cleanup issues of Python itself
  // See: http://docs.python.org/c-api/init.html#Py_Finalize
  //pyLib_.reset();
}

}
<|endoftext|>


<commit_before>#include <stdio.h>
#include <string.h>

#include <osg/Geode>
#include <osg/Group>
#include <osg/Notify>

#include <osgDB/Registry>
#include <osgDB/FileNameUtils>

#include "ConvertToPerformer.h"
#include "ConvertFromPerformer.h"

#include <Performer/pfdu.h>
#include <Performer/pr/pfTexture.h>

extern "C" {
extern pfNode *pfdLoadFile_pfb( const char *);
extern int pfdStoreFile_pfb( pfNode *, const char *);
}

class ReaderWriterPFB : public osgDB::ReaderWriter
{

    public:

        ReaderWriterPFB();
        ~ReaderWriterPFB();

        void initPerformer();

        virtual const char* className() { return "Performer Reader/Writer"; }
        virtual bool acceptsExtension(const std::string& extension)
	{
	    return
                extension=="3ds"     ? true :
                extension=="arcinfo" ? true :
                extension=="bin"     ? true :
                extension=="bpoly"   ? true :
                extension=="bw"      ? true :
                extension=="byu"     ? true :
                extension=="closest" ? true :
                extension=="csb"     ? true :
                extension=="ct"      ? true :
                extension=="dem"     ? true :
                extension=="doublerot" ? true :
                extension=="doublescale" ? true :
                extension=="doubletrans" ? true :
                extension=="dted"    ? true :
                extension=="dwb"     ? true :
                extension=="dxf"     ? true :
                extension=="evt"     ? true :
                extension=="flt"     ? true :
                extension=="gds"     ? true :
                extension=="gfo"     ? true :
                extension=="im"      ? true :
                extension=="irtp"    ? true :
                extension=="iv20"    ? true :
                extension=="iv"      ? true :
                extension=="lodfix"  ? true :
                extension=="lsa"     ? true :
                extension=="lsb"     ? true :
                extension=="medit"   ? true :
                extension=="m"       ? true :
                extension=="nff"     ? true :
                extension=="obj"     ? true :
                extension=="pegg"    ? true :
                extension=="pfb"     ? true :
                extension=="pfs"     ? true :
                extension=="phd"     ? true :
                extension=="poly"    ? true :
                extension=="post"    ? true :
                extension=="proc"    ? true :
                extension=="projtex" ? true :
                extension=="pts"     ? true :
                extension=="rot"     ? true :
                extension=="scale"   ? true :
                extension=="sgf"     ? true :
                extension=="sgo"     ? true :
                extension=="so"      ? true :
                extension=="spf"     ? true :
                extension=="spherepatch3" ? true :
                extension=="spherepatch" ? true :
                extension=="sphere"  ? true :
                extension=="sponge"  ? true :
                extension=="star"    ? true :
                extension=="stla"    ? true :
                extension=="stlb"    ? true :
                extension=="substclip" ? true :
                extension=="sv"      ? true :
                extension=="trans"   ? true :
                extension=="tri"     ? true :
                extension=="unc"     ? true :
                extension=="vct"     ? true :
		false;
	}

        virtual ReadResult readImage(const std::string& fileName, const osgDB::ReaderWriter::Options*)
        {
            osg::notify(osg::INFO)<<"ReaderWriterPFB::readImage( "<<fileName.c_str()<<" )\n";
            initPerformer();

            pfTexture* tex = new pfTexture;
            tex->ref();
            if (tex->loadFile(fileName.c_str()))
            {
                int s=0;
                int t=0;
                int r=0;
                int comp=0;
                unsigned int* imageData = NULL;

                tex->getImage(&imageData,&comp,&s,&t,&r);

                int internalFormat = comp;

                unsigned int pixelFormat =
                    comp == 1 ? GL_LUMINANCE :
                    comp == 2 ? GL_LUMINANCE_ALPHA :
                    comp == 3 ? GL_RGB :
                    comp == 4 ? GL_RGBA : (GLenum)-1;

                unsigned int dataType = GL_UNSIGNED_BYTE;

                // copy image data
                int size = s * t * r * comp;
                unsigned char* data = (unsigned char*) malloc(size);
                memcpy(data, imageData, size);

                osg::Image* image = new osg::Image;
                image->setFileName(fileName.c_str());
                image->setImage(s,t,r,
                                internalFormat,
                                pixelFormat,
                                dataType,
                                data);

                // free texture & image data
                tex->unrefDelete();

                return image;
            }

            // free texture & image data
            tex->unrefDelete();

            return ReadResult::FILE_NOT_HANDLED;
        }

        virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options)
        {
            std::string ext = osgDB::getLowerCaseFileExtension(fileName);
            if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;

            osg::notify(osg::INFO)<<"ReaderWriterPFB::readNode( "<<fileName.c_str()<<" )\n";
            initPerformer();

            pfNode* root = pfdLoadFile(fileName.c_str());
            if (root)
            {
                ConvertFromPerformer converter;
                if (options) {
                    const string option = options->getOptionString();
                    if (option.find("saveImagesAsRGB") != string::npos)
                        converter.setSaveImagesAsRGB(true);
                    if (option.find("saveAbsoluteImagePath") != string::npos)
                        converter.setSaveAbsoluteImagePath(true);
                }

                root->ref();
                osg::Node* node = converter.convert(root);
                root->unrefDelete();
                return node;
            }
            else
            {
                return ReadResult::FILE_NOT_HANDLED;
            }

        }

        virtual WriteResult writeNode(const osg::Node& node,const std::string& fileName, const osgDB::ReaderWriter::Options*)
        {
            std::string ext = osgDB::getLowerCaseFileExtension(fileName);
            if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;

            osg::notify(osg::INFO)<<"ReaderWriterPFB::writeNode( "<<fileName.c_str()<<" )\n";
            initPerformer();

            ConvertToPerformer converter;
            pfNode* root = converter.convert(&node);
            if (root)
            {
                if (pfdStoreFile(root,fileName.c_str())!=0) return WriteResult::FILE_SAVED;
                else return std::string("Unable to write file from performer.");
            }
            else
            {
                return std::string("Unable to convert scene to performer, cannot write file.");
            }
        }

    protected:

        bool _performerInitialised;

};

ReaderWriterPFB::ReaderWriterPFB()
{
    _performerInitialised = false;
}

ReaderWriterPFB::~ReaderWriterPFB()
{
    if (_performerInitialised)
        pfExit();
}

void ReaderWriterPFB::initPerformer()
{
    if (_performerInitialised) return;

    _performerInitialised = true;

    pfInit();

    pfMultiprocess(0);

    //     FileList::iterator itr;
    //     for(itr=filelist.begin();itr!=filelist.end();++itr)
    //     {
    //         pfdInitConverter((*itr).c_str());
    //     }
    pfdInitConverter(".pfb");

    pfConfig();
}

// now register with sgRegistry to instantiate the above
// reader/writer.
osgDB::RegisterReaderWriterProxy<ReaderWriterPFB> g_readerWriter_PFB_Proxy;
<commit_msg>Compile fix from Duvan Cope.<commit_after>#include <stdio.h>
#include <string.h>

#include <osg/Geode>
#include <osg/Group>
#include <osg/Notify>

#include <osgDB/Registry>
#include <osgDB/FileNameUtils>

#include "ConvertToPerformer.h"
#include "ConvertFromPerformer.h"

#include <Performer/pfdu.h>
#include <Performer/pr/pfTexture.h>

extern "C" {
extern pfNode *pfdLoadFile_pfb( const char *);
extern int pfdStoreFile_pfb( pfNode *, const char *);
}

class ReaderWriterPFB : public osgDB::ReaderWriter
{

    public:

        ReaderWriterPFB();
        ~ReaderWriterPFB();

        void initPerformer();

        virtual const char* className() { return "Performer Reader/Writer"; }
        virtual bool acceptsExtension(const std::string& extension)
	{
	    return
                extension=="3ds"     ? true :
                extension=="arcinfo" ? true :
                extension=="bin"     ? true :
                extension=="bpoly"   ? true :
                extension=="bw"      ? true :
                extension=="byu"     ? true :
                extension=="closest" ? true :
                extension=="csb"     ? true :
                extension=="ct"      ? true :
                extension=="dem"     ? true :
                extension=="doublerot" ? true :
                extension=="doublescale" ? true :
                extension=="doubletrans" ? true :
                extension=="dted"    ? true :
                extension=="dwb"     ? true :
                extension=="dxf"     ? true :
                extension=="evt"     ? true :
                extension=="flt"     ? true :
                extension=="gds"     ? true :
                extension=="gfo"     ? true :
                extension=="im"      ? true :
                extension=="irtp"    ? true :
                extension=="iv20"    ? true :
                extension=="iv"      ? true :
                extension=="lodfix"  ? true :
                extension=="lsa"     ? true :
                extension=="lsb"     ? true :
                extension=="medit"   ? true :
                extension=="m"       ? true :
                extension=="nff"     ? true :
                extension=="obj"     ? true :
                extension=="pegg"    ? true :
                extension=="pfb"     ? true :
                extension=="pfs"     ? true :
                extension=="phd"     ? true :
                extension=="poly"    ? true :
                extension=="post"    ? true :
                extension=="proc"    ? true :
                extension=="projtex" ? true :
                extension=="pts"     ? true :
                extension=="rot"     ? true :
                extension=="scale"   ? true :
                extension=="sgf"     ? true :
                extension=="sgo"     ? true :
                extension=="so"      ? true :
                extension=="spf"     ? true :
                extension=="spherepatch3" ? true :
                extension=="spherepatch" ? true :
                extension=="sphere"  ? true :
                extension=="sponge"  ? true :
                extension=="star"    ? true :
                extension=="stla"    ? true :
                extension=="stlb"    ? true :
                extension=="substclip" ? true :
                extension=="sv"      ? true :
                extension=="trans"   ? true :
                extension=="tri"     ? true :
                extension=="unc"     ? true :
                extension=="vct"     ? true :
		false;
	}

        virtual ReadResult readImage(const std::string& fileName, const osgDB::ReaderWriter::Options*)
        {
            osg::notify(osg::INFO)<<"ReaderWriterPFB::readImage( "<<fileName.c_str()<<" )\n";
            initPerformer();

            pfTexture* tex = new pfTexture;
            tex->ref();
            if (tex->loadFile(fileName.c_str()))
            {
                int s=0;
                int t=0;
                int r=0;
                int comp=0;
                unsigned int* imageData = NULL;

                tex->getImage(&imageData,&comp,&s,&t,&r);

                int internalFormat = comp;

                unsigned int pixelFormat =
                    comp == 1 ? GL_LUMINANCE :
                    comp == 2 ? GL_LUMINANCE_ALPHA :
                    comp == 3 ? GL_RGB :
                    comp == 4 ? GL_RGBA : (GLenum)-1;

                unsigned int dataType = GL_UNSIGNED_BYTE;

                // copy image data
                int size = s * t * r * comp;
                unsigned char* data = (unsigned char*) malloc(size);
                memcpy(data, imageData, size);

                osg::Image* image = new osg::Image;
                image->setFileName(fileName.c_str());
                image->setImage(s,t,r,
                                internalFormat,
                                pixelFormat,
                                dataType,
                                data);

                // free texture & image data
                tex->unrefDelete();

                return image;
            }

            // free texture & image data
            tex->unrefDelete();

            return ReadResult::FILE_NOT_HANDLED;
        }

        virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options)
        {
            std::string ext = osgDB::getLowerCaseFileExtension(fileName);
            if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;

            osg::notify(osg::INFO)<<"ReaderWriterPFB::readNode( "<<fileName.c_str()<<" )\n";
            initPerformer();

            pfNode* root = pfdLoadFile(fileName.c_str());
            if (root)
            {
                ConvertFromPerformer converter;
                if (options) {
                    const std::string option = options->getOptionString();
                    if (option.find("saveImagesAsRGB") != std::string::npos)
                        converter.setSaveImagesAsRGB(true);
                    if (option.find("saveAbsoluteImagePath") != std::string::npos)
                        converter.setSaveAbsoluteImagePath(true);
                }

                root->ref();
                osg::Node* node = converter.convert(root);
                root->unrefDelete();
                return node;
            }
            else
            {
                return ReadResult::FILE_NOT_HANDLED;
            }

        }

        virtual WriteResult writeNode(const osg::Node& node,const std::string& fileName, const osgDB::ReaderWriter::Options*)
        {
            std::string ext = osgDB::getLowerCaseFileExtension(fileName);
            if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;

            osg::notify(osg::INFO)<<"ReaderWriterPFB::writeNode( "<<fileName.c_str()<<" )\n";
            initPerformer();

            ConvertToPerformer converter;
            pfNode* root = converter.convert(&node);
            if (root)
            {
                if (pfdStoreFile(root,fileName.c_str())!=0) return WriteResult::FILE_SAVED;
                else return std::string("Unable to write file from performer.");
            }
            else
            {
                return std::string("Unable to convert scene to performer, cannot write file.");
            }
        }

    protected:

        bool _performerInitialised;

};

ReaderWriterPFB::ReaderWriterPFB()
{
    _performerInitialised = false;
}

ReaderWriterPFB::~ReaderWriterPFB()
{
    if (_performerInitialised)
        pfExit();
}

void ReaderWriterPFB::initPerformer()
{
    if (_performerInitialised) return;

    _performerInitialised = true;

    pfInit();

    pfMultiprocess(0);

    //     FileList::iterator itr;
    //     for(itr=filelist.begin();itr!=filelist.end();++itr)
    //     {
    //         pfdInitConverter((*itr).c_str());
    //     }
    pfdInitConverter(".pfb");

    pfConfig();
}

// now register with sgRegistry to instantiate the above
// reader/writer.
osgDB::RegisterReaderWriterProxy<ReaderWriterPFB> g_readerWriter_PFB_Proxy;
<|endoftext|>


<commit_before>#include <osg/Image>
#include <osg/Notify>
#include <osg/Geode>
#include <osg/GL>
#include <osg/Endian>

#include <osgDB/Registry>
#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>

#include <sstream>

using namespace osg;

extern "C"
{
    #include <zlib.h>
    #include <png.h>
}

/* Transparency parameters */
#define PNG_ALPHA     -2         /* Use alpha channel in PNG file, if there is one */
#define PNG_SOLID     -1         /* No transparency                                */
#define PNG_STENCIL    0         /* Sets alpha to 0 for r=g=b=0, 1 otherwise       */

typedef struct
{
    unsigned int Width;
    unsigned int Height;
    unsigned int Depth;
    unsigned int Alpha;
} pngInfo;

class PNGError
{
public:
    PNGError(const char* message)
    {
        _message = "PNG lib error : ";
        _message += message;
    }
    friend std::ostream& operator<<(std::ostream& stream, const PNGError& err)
    {
        stream << err._message;
        return stream;
    }
private:
    std::string _message;
};

void user_error_fn(png_structp png_ptr, png_const_charp error_msg)
{
#ifdef OSG_CPP_EXCEPTIONS_AVAILABLE
    throw PNGError(error_msg);
#else
    osg::notify(osg::WARN) << "PNG lib warning : " << error_msg << std::endl;
#endif
}

void user_warning_fn(png_structp png_ptr, png_const_charp warning_msg)
{
    osg::notify(osg::WARN) << "PNG lib warning : " << warning_msg << std::endl;
}

void png_read_istream(png_structp png_ptr, png_bytep data, png_size_t length)
{
    std::istream *stream = (std::istream*)png_get_io_ptr(png_ptr); //Get pointer to istream
    stream->read((char*)data,length); //Read requested amount of data
}

void png_write_ostream(png_structp png_ptr, png_bytep data, png_size_t length)
{
    std::ostream *stream = (std::ostream*)png_get_io_ptr(png_ptr); //Get pointer to ostream
    stream->write((char*)data,length); //Write requested amount of data
}

void png_flush_ostream(png_structp png_ptr)
{
    std::ostream *stream = (std::ostream*)png_get_io_ptr(png_ptr); //Get pointer to ostream
    stream->flush();
}

class ReaderWriterPNG : public osgDB::ReaderWriter
{
    public:
        ReaderWriterPNG()
        {
            supportsExtension("png","PNG Image format");
        }

        virtual const char* className() const { return "PNG Image Reader/Writer"; }

        WriteResult::WriteStatus writePngStream(std::ostream& fout, const osg::Image& img, int compression_level) const
        {
            png_structp png = NULL;
            png_infop   info = NULL;
            int color;
            png_bytep *rows = NULL;

            //Create write structure
            png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
            if(!png) return WriteResult::ERROR_IN_WRITING_FILE;

            //Create infr structure
            info = png_create_info_struct(png);
            if(!info) return WriteResult::ERROR_IN_WRITING_FILE;

            //Set custom write function so it will write to ostream
            png_set_write_fn(png,&fout,png_write_ostream,png_flush_ostream);

            //Set compression level
            png_set_compression_level(png, compression_level);

            switch(img.getPixelFormat()) {
                case(GL_LUMINANCE): color = PNG_COLOR_TYPE_GRAY; break;
                case(GL_ALPHA): color = PNG_COLOR_TYPE_GRAY; break; //Couldn't find a color type for pure alpha, using gray instead
                case(GL_LUMINANCE_ALPHA): color = PNG_COLOR_TYPE_GRAY_ALPHA ; break;
                case(GL_RGB): color = PNG_COLOR_TYPE_RGB; break;
                case(GL_RGBA): color = PNG_COLOR_TYPE_RGB_ALPHA; break;
                default: return WriteResult::ERROR_IN_WRITING_FILE; break;
            }

            //Create row data
            rows = new png_bytep[img.t()];
            for(int i = 0; i < img.t(); ++i) {
                rows[i] = (png_bytep)img.data(0,img.t() - i - 1);
            }

            //Write header info
            png_set_IHDR(png, info, img.s(), img.t(),
                        8, color, PNG_INTERLACE_NONE,
                        PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

            png_write_info(png, info);

            //Write data
            png_write_image(png, rows);

            //End write
            png_write_end(png, NULL);

            //Cleanup
            png_destroy_write_struct(&png,&info);
            delete [] rows;

            return WriteResult::FILE_SAVED;
        }

        ReadResult readPNGStream(std::istream& fin) const
        {
            int trans = PNG_ALPHA;
            pngInfo pInfo;
            pngInfo *pinfo = &pInfo;

            unsigned char header[8];
            png_structp png;
            png_infop   info;
            png_infop   endinfo;
            png_bytep   data;    //, data2;
            png_bytep  *row_p;
            double  fileGamma;

            png_uint_32 width, height;
            int depth, color;

            png_uint_32 i;
            png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

            // Set custom error handlers
            png_set_error_fn(png, png_get_error_ptr(png), user_error_fn, user_warning_fn);

            #ifdef OSG_CPP_EXCEPTIONS_AVAILABLE
            try
            #endif
            {
                info = png_create_info_struct(png);
                endinfo = png_create_info_struct(png);

                fin.read((char*)header,8);
                if (fin.gcount() == 8 && png_sig_cmp(header, 0, 8) == 0)
                    png_set_read_fn(png,&fin,png_read_istream); //Use custom read function that will get data from istream
                else
                {
                    png_destroy_read_struct(&png, &info, &endinfo);
                    return ReadResult::FILE_NOT_HANDLED;
                }
                png_set_sig_bytes(png, 8);

                png_read_info(png, info);
                png_get_IHDR(png, info, &width, &height, &depth, &color, NULL, NULL, NULL);

                if (pinfo != NULL)
                {
                    pinfo->Width  = width;
                    pinfo->Height = height;
                    pinfo->Depth  = depth;
                }

                osg::notify(osg::INFO)<<"width="<<width<<" height="<<height<<" depth="<<depth<<std::endl;
                if ( color == PNG_COLOR_TYPE_RGB) osg::notify(osg::INFO) << "color == PNG_COLOR_TYPE_RGB "<<std::endl;
                if ( color == PNG_COLOR_TYPE_GRAY) osg::notify(osg::INFO) << "color == PNG_COLOR_TYPE_GRAY "<<std::endl;
                if ( color == PNG_COLOR_TYPE_GRAY_ALPHA) osg::notify(osg::INFO) << "color ==  PNG_COLOR_TYPE_GRAY_ALPHA"<<std::endl;

                // png default to big endian, so we'll need to swap bytes if on a little endian machine.
                if (depth>8 && getCpuByteOrder()==osg::LittleEndian)
                    png_set_swap(png);

                if (color == PNG_COLOR_TYPE_GRAY || color == PNG_COLOR_TYPE_GRAY_ALPHA)
                {
                    //png_set_gray_to_rgb(png);
                }

                if (color&PNG_COLOR_MASK_ALPHA && trans != PNG_ALPHA)
                {
                    png_set_strip_alpha(png);
                    color &= ~PNG_COLOR_MASK_ALPHA;
                }

                //    if (!(PalettedTextures && mipmap >= 0 && trans == PNG_SOLID))
                //if (color == PNG_COLOR_TYPE_PALETTE)
                //    png_set_expand(png);

                // In addition to expanding the palette, we also need to check
                // to expand greyscale and alpha images.  See libpng man page.
                if (color == PNG_COLOR_TYPE_PALETTE)
                    png_set_palette_to_rgb(png);
                if (color == PNG_COLOR_TYPE_GRAY && depth < 8)
                {
                #if PNG_LIBPNG_VER >= 10209
                    png_set_expand_gray_1_2_4_to_8(png);
                #else
                    // use older now deprecated but identical call
                    png_set_gray_1_2_4_to_8(png);
                #endif
                }
                if (png_get_valid(png, info, PNG_INFO_tRNS))
                    png_set_tRNS_to_alpha(png);

                // Make sure that files of small depth are packed properly.
                if (depth < 8)
                    png_set_packing(png);

                /*--GAMMA--*/
                //    checkForGammaEnv();
                double screenGamma = 2.2 / 1.0;
                if (png_get_gAMA(png, info, &fileGamma))
                    png_set_gamma(png, screenGamma, fileGamma);
                else
                    png_set_gamma(png, screenGamma, 1.0/2.2);

                png_read_update_info(png, info);

                data = (png_bytep) new unsigned char [png_get_rowbytes(png, info)*height];
                row_p = new png_bytep [height];

                bool StandardOrientation = true;
                for (i = 0; i < height; i++)
                {
                    if (StandardOrientation)
                        row_p[height - 1 - i] = &data[png_get_rowbytes(png, info)*i];
                    else
                        row_p[i] = &data[png_get_rowbytes(png, info)*i];
                }

                png_read_image(png, row_p);
                delete [] row_p;
                png_read_end(png, endinfo);

                GLenum pixelFormat = 0;
                GLenum dataType = depth<=8?GL_UNSIGNED_BYTE:GL_UNSIGNED_SHORT;
                switch(color)
                {
                  case(PNG_SOLID): pixelFormat = GL_LUMINANCE; break;
                  case(PNG_ALPHA): pixelFormat = GL_ALPHA; break;
                  case(PNG_COLOR_TYPE_GRAY): pixelFormat =GL_LUMINANCE ; break;
                  case(PNG_COLOR_TYPE_GRAY_ALPHA): pixelFormat = GL_LUMINANCE_ALPHA; break;
                  case(PNG_COLOR_TYPE_RGB): pixelFormat = GL_RGB; break;
                  case(PNG_COLOR_TYPE_PALETTE): pixelFormat = GL_RGB; break;
                  case(PNG_COLOR_TYPE_RGB_ALPHA): pixelFormat = GL_RGBA; break;
                  default: break;
                }

                // Some paletted images contain alpha information.  To be
                // able to give that back to the calling program, we need to
                // check the number of channels in the image.  However, the
                // call might not return correct information unless
                // png_read_end is called first.  See libpng man page.
                if (pixelFormat == GL_RGB && png_get_channels(png, info) == 4)
                    pixelFormat = GL_RGBA;

                int internalFormat = pixelFormat;

                png_destroy_read_struct(&png, &info, &endinfo);

                //    delete [] data;

                if (pixelFormat==0)
                    return ReadResult::FILE_NOT_HANDLED;

                osg::Image* pOsgImage = new osg::Image();

                pOsgImage->setImage(width, height, 1,
                    internalFormat,
                    pixelFormat,
                    dataType,
                    data,
                    osg::Image::USE_NEW_DELETE);

                return pOsgImage;

            }
            #ifdef OSG_CPP_EXCEPTIONS_AVAILABLE
            catch (PNGError& err)
            {
                osg::notify(osg::WARN) << err << std::endl;
                png_destroy_read_struct(&png, &info, &endinfo);
                return ReadResult::ERROR_IN_READING_FILE;
            }
            #endif
        }

        int getCompressionLevel(const osgDB::ReaderWriter::Options *options) const
        {
            if(options) {
                std::istringstream iss(options->getOptionString());
                std::string opt;
                while (iss >> opt) {
                    if(opt=="PNG_COMPRESSION") {
                        int level;
                        iss >> level;
                        return level;
                    }
                }
            }

            return Z_DEFAULT_COMPRESSION;
        }

        virtual ReadResult readObject(std::istream& fin,const osgDB::ReaderWriter::Options* options =NULL) const
        {
            return readImage(fin, options);
        }

        virtual ReadResult readObject(const std::string& file, const osgDB::ReaderWriter::Options* options =NULL) const
        {
            return readImage(file, options);
        }

        virtual ReadResult readImage(std::istream& fin,const Options* =NULL) const
        {
            return readPNGStream(fin);
        }

        virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options* options) const
        {
            std::string ext = osgDB::getLowerCaseFileExtension(file);
            if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;

            std::string fileName = osgDB::findDataFile( file, options );
            if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;

            osgDB::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary);
            if(!istream) return ReadResult::FILE_NOT_HANDLED;
            ReadResult rr = readPNGStream(istream);
            if(rr.validImage()) rr.getImage()->setFileName(file);
            return rr;
        }

        virtual WriteResult writeImage(const osg::Image& img,std::ostream& fout,const osgDB::ReaderWriter::Options *options) const
        {
            WriteResult::WriteStatus ws = writePngStream(fout,img,getCompressionLevel(options));
            return ws;
        }

        virtual WriteResult writeImage(const osg::Image &img,const std::string& fileName, const osgDB::ReaderWriter::Options *options) const
        {
            std::string ext = osgDB::getFileExtension(fileName);
            if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;

            osgDB::ofstream fout(fileName.c_str(), std::ios::out | std::ios::binary);
            if(!fout) return WriteResult::ERROR_IN_WRITING_FILE;

            return writeImage(img,fout,options);
        }
};

// now register with Registry to instantiate the above
// reader/writer.
REGISTER_OSGPLUGIN(png, ReaderWriterPNG)
<commit_msg>Converted osg::notify to OSG_INFO etc.<commit_after>#include <osg/Image>
#include <osg/Notify>
#include <osg/Geode>
#include <osg/GL>
#include <osg/Endian>

#include <osgDB/Registry>
#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>

#include <sstream>

using namespace osg;

extern "C"
{
    #include <zlib.h>
    #include <png.h>
}

/* Transparency parameters */
#define PNG_ALPHA     -2         /* Use alpha channel in PNG file, if there is one */
#define PNG_SOLID     -1         /* No transparency                                */
#define PNG_STENCIL    0         /* Sets alpha to 0 for r=g=b=0, 1 otherwise       */

typedef struct
{
    unsigned int Width;
    unsigned int Height;
    unsigned int Depth;
    unsigned int Alpha;
} pngInfo;

class PNGError
{
public:
    PNGError(const char* message)
    {
        _message = "PNG lib error : ";
        _message += message;
    }
    friend std::ostream& operator<<(std::ostream& stream, const PNGError& err)
    {
        stream << err._message;
        return stream;
    }
private:
    std::string _message;
};

void user_error_fn(png_structp png_ptr, png_const_charp error_msg)
{
#ifdef OSG_CPP_EXCEPTIONS_AVAILABLE
    throw PNGError(error_msg);
#else
    OSG_WARN << "PNG lib warning : " << error_msg << std::endl;
#endif
}

void user_warning_fn(png_structp png_ptr, png_const_charp warning_msg)
{
    OSG_WARN << "PNG lib warning : " << warning_msg << std::endl;
}

void png_read_istream(png_structp png_ptr, png_bytep data, png_size_t length)
{
    std::istream *stream = (std::istream*)png_get_io_ptr(png_ptr); //Get pointer to istream
    stream->read((char*)data,length); //Read requested amount of data
}

void png_write_ostream(png_structp png_ptr, png_bytep data, png_size_t length)
{
    std::ostream *stream = (std::ostream*)png_get_io_ptr(png_ptr); //Get pointer to ostream
    stream->write((char*)data,length); //Write requested amount of data
}

void png_flush_ostream(png_structp png_ptr)
{
    std::ostream *stream = (std::ostream*)png_get_io_ptr(png_ptr); //Get pointer to ostream
    stream->flush();
}

class ReaderWriterPNG : public osgDB::ReaderWriter
{
    public:
        ReaderWriterPNG()
        {
            supportsExtension("png","PNG Image format");
        }

        virtual const char* className() const { return "PNG Image Reader/Writer"; }

        WriteResult::WriteStatus writePngStream(std::ostream& fout, const osg::Image& img, int compression_level) const
        {
            png_structp png = NULL;
            png_infop   info = NULL;
            int color;
            png_bytep *rows = NULL;

            //Create write structure
            png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
            if(!png) return WriteResult::ERROR_IN_WRITING_FILE;

            //Create infr structure
            info = png_create_info_struct(png);
            if(!info) return WriteResult::ERROR_IN_WRITING_FILE;

            //Set custom write function so it will write to ostream
            png_set_write_fn(png,&fout,png_write_ostream,png_flush_ostream);

            //Set compression level
            png_set_compression_level(png, compression_level);

            switch(img.getPixelFormat()) {
                case(GL_LUMINANCE): color = PNG_COLOR_TYPE_GRAY; break;
                case(GL_ALPHA): color = PNG_COLOR_TYPE_GRAY; break; //Couldn't find a color type for pure alpha, using gray instead
                case(GL_LUMINANCE_ALPHA): color = PNG_COLOR_TYPE_GRAY_ALPHA ; break;
                case(GL_RGB): color = PNG_COLOR_TYPE_RGB; break;
                case(GL_RGBA): color = PNG_COLOR_TYPE_RGB_ALPHA; break;
                default: return WriteResult::ERROR_IN_WRITING_FILE; break;
            }

            //Create row data
            rows = new png_bytep[img.t()];
            for(int i = 0; i < img.t(); ++i) {
                rows[i] = (png_bytep)img.data(0,img.t() - i - 1);
            }

            //Write header info
            png_set_IHDR(png, info, img.s(), img.t(),
                        8, color, PNG_INTERLACE_NONE,
                        PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

            png_write_info(png, info);

            //Write data
            png_write_image(png, rows);

            //End write
            png_write_end(png, NULL);

            //Cleanup
            png_destroy_write_struct(&png,&info);
            delete [] rows;

            return WriteResult::FILE_SAVED;
        }

        ReadResult readPNGStream(std::istream& fin) const
        {
            int trans = PNG_ALPHA;
            pngInfo pInfo;
            pngInfo *pinfo = &pInfo;

            unsigned char header[8];
            png_structp png;
            png_infop   info;
            png_infop   endinfo;
            png_bytep   data;    //, data2;
            png_bytep  *row_p;
            double  fileGamma;

            png_uint_32 width, height;
            int depth, color;

            png_uint_32 i;
            png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

            // Set custom error handlers
            png_set_error_fn(png, png_get_error_ptr(png), user_error_fn, user_warning_fn);

            #ifdef OSG_CPP_EXCEPTIONS_AVAILABLE
            try
            #endif
            {
                info = png_create_info_struct(png);
                endinfo = png_create_info_struct(png);

                fin.read((char*)header,8);
                if (fin.gcount() == 8 && png_sig_cmp(header, 0, 8) == 0)
                    png_set_read_fn(png,&fin,png_read_istream); //Use custom read function that will get data from istream
                else
                {
                    png_destroy_read_struct(&png, &info, &endinfo);
                    return ReadResult::FILE_NOT_HANDLED;
                }
                png_set_sig_bytes(png, 8);

                png_read_info(png, info);
                png_get_IHDR(png, info, &width, &height, &depth, &color, NULL, NULL, NULL);

                if (pinfo != NULL)
                {
                    pinfo->Width  = width;
                    pinfo->Height = height;
                    pinfo->Depth  = depth;
                }

                OSG_INFO<<"width="<<width<<" height="<<height<<" depth="<<depth<<std::endl;
                if ( color == PNG_COLOR_TYPE_RGB) { OSG_INFO << "color == PNG_COLOR_TYPE_RGB "<<std::endl; }
                if ( color == PNG_COLOR_TYPE_GRAY) { OSG_INFO << "color == PNG_COLOR_TYPE_GRAY "<<std::endl; }
                if ( color == PNG_COLOR_TYPE_GRAY_ALPHA) { OSG_INFO << "color ==  PNG_COLOR_TYPE_GRAY_ALPHA"<<std::endl; }

                // png default to big endian, so we'll need to swap bytes if on a little endian machine.
                if (depth>8 && getCpuByteOrder()==osg::LittleEndian)
                    png_set_swap(png);

                if (color == PNG_COLOR_TYPE_GRAY || color == PNG_COLOR_TYPE_GRAY_ALPHA)
                {
                    //png_set_gray_to_rgb(png);
                }

                if (color&PNG_COLOR_MASK_ALPHA && trans != PNG_ALPHA)
                {
                    png_set_strip_alpha(png);
                    color &= ~PNG_COLOR_MASK_ALPHA;
                }

                //    if (!(PalettedTextures && mipmap >= 0 && trans == PNG_SOLID))
                //if (color == PNG_COLOR_TYPE_PALETTE)
                //    png_set_expand(png);

                // In addition to expanding the palette, we also need to check
                // to expand greyscale and alpha images.  See libpng man page.
                if (color == PNG_COLOR_TYPE_PALETTE)
                    png_set_palette_to_rgb(png);
                if (color == PNG_COLOR_TYPE_GRAY && depth < 8)
                {
                #if PNG_LIBPNG_VER >= 10209
                    png_set_expand_gray_1_2_4_to_8(png);
                #else
                    // use older now deprecated but identical call
                    png_set_gray_1_2_4_to_8(png);
                #endif
                }
                if (png_get_valid(png, info, PNG_INFO_tRNS))
                    png_set_tRNS_to_alpha(png);

                // Make sure that files of small depth are packed properly.
                if (depth < 8)
                    png_set_packing(png);

                /*--GAMMA--*/
                //    checkForGammaEnv();
                double screenGamma = 2.2 / 1.0;
                if (png_get_gAMA(png, info, &fileGamma))
                    png_set_gamma(png, screenGamma, fileGamma);
                else
                    png_set_gamma(png, screenGamma, 1.0/2.2);

                png_read_update_info(png, info);

                data = (png_bytep) new unsigned char [png_get_rowbytes(png, info)*height];
                row_p = new png_bytep [height];

                bool StandardOrientation = true;
                for (i = 0; i < height; i++)
                {
                    if (StandardOrientation)
                        row_p[height - 1 - i] = &data[png_get_rowbytes(png, info)*i];
                    else
                        row_p[i] = &data[png_get_rowbytes(png, info)*i];
                }

                png_read_image(png, row_p);
                delete [] row_p;
                png_read_end(png, endinfo);

                GLenum pixelFormat = 0;
                GLenum dataType = depth<=8?GL_UNSIGNED_BYTE:GL_UNSIGNED_SHORT;
                switch(color)
                {
                  case(PNG_SOLID): pixelFormat = GL_LUMINANCE; break;
                  case(PNG_ALPHA): pixelFormat = GL_ALPHA; break;
                  case(PNG_COLOR_TYPE_GRAY): pixelFormat =GL_LUMINANCE ; break;
                  case(PNG_COLOR_TYPE_GRAY_ALPHA): pixelFormat = GL_LUMINANCE_ALPHA; break;
                  case(PNG_COLOR_TYPE_RGB): pixelFormat = GL_RGB; break;
                  case(PNG_COLOR_TYPE_PALETTE): pixelFormat = GL_RGB; break;
                  case(PNG_COLOR_TYPE_RGB_ALPHA): pixelFormat = GL_RGBA; break;
                  default: break;
                }

                // Some paletted images contain alpha information.  To be
                // able to give that back to the calling program, we need to
                // check the number of channels in the image.  However, the
                // call might not return correct information unless
                // png_read_end is called first.  See libpng man page.
                if (pixelFormat == GL_RGB && png_get_channels(png, info) == 4)
                    pixelFormat = GL_RGBA;

                int internalFormat = pixelFormat;

                png_destroy_read_struct(&png, &info, &endinfo);

                //    delete [] data;

                if (pixelFormat==0)
                    return ReadResult::FILE_NOT_HANDLED;

                osg::Image* pOsgImage = new osg::Image();

                pOsgImage->setImage(width, height, 1,
                    internalFormat,
                    pixelFormat,
                    dataType,
                    data,
                    osg::Image::USE_NEW_DELETE);

                return pOsgImage;

            }
            #ifdef OSG_CPP_EXCEPTIONS_AVAILABLE
            catch (PNGError& err)
            {
                OSG_WARN << err << std::endl;
                png_destroy_read_struct(&png, &info, &endinfo);
                return ReadResult::ERROR_IN_READING_FILE;
            }
            #endif
        }

        int getCompressionLevel(const osgDB::ReaderWriter::Options *options) const
        {
            if(options) {
                std::istringstream iss(options->getOptionString());
                std::string opt;
                while (iss >> opt) {
                    if(opt=="PNG_COMPRESSION") {
                        int level;
                        iss >> level;
                        return level;
                    }
                }
            }

            return Z_DEFAULT_COMPRESSION;
        }

        virtual ReadResult readObject(std::istream& fin,const osgDB::ReaderWriter::Options* options =NULL) const
        {
            return readImage(fin, options);
        }

        virtual ReadResult readObject(const std::string& file, const osgDB::ReaderWriter::Options* options =NULL) const
        {
            return readImage(file, options);
        }

        virtual ReadResult readImage(std::istream& fin,const Options* =NULL) const
        {
            return readPNGStream(fin);
        }

        virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options* options) const
        {
            std::string ext = osgDB::getLowerCaseFileExtension(file);
            if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;

            std::string fileName = osgDB::findDataFile( file, options );
            if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;

            osgDB::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary);
            if(!istream) return ReadResult::FILE_NOT_HANDLED;
            ReadResult rr = readPNGStream(istream);
            if(rr.validImage()) rr.getImage()->setFileName(file);
            return rr;
        }

        virtual WriteResult writeImage(const osg::Image& img,std::ostream& fout,const osgDB::ReaderWriter::Options *options) const
        {
            WriteResult::WriteStatus ws = writePngStream(fout,img,getCompressionLevel(options));
            return ws;
        }

        virtual WriteResult writeImage(const osg::Image &img,const std::string& fileName, const osgDB::ReaderWriter::Options *options) const
        {
            std::string ext = osgDB::getFileExtension(fileName);
            if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;

            osgDB::ofstream fout(fileName.c_str(), std::ios::out | std::ios::binary);
            if(!fout) return WriteResult::ERROR_IN_WRITING_FILE;

            return writeImage(img,fout,options);
        }
};

// now register with Registry to instantiate the above
// reader/writer.
REGISTER_OSGPLUGIN(png, ReaderWriterPNG)
<|endoftext|>


<commit_before>
#include "world.hpp"

void collect_collisions_if_object_personal_space_is_at(world& w, unordered_set<object_or_tile_identifier> &results, object_identifier id, shape const& new_shape) {
  unordered_set<object_or_tile_identifier> potential_collisions;
  w.collect_things_exposed_to_collision_intersecting(potential_collisions, new_shape.bounds());
  for (auto const& pc : potential_collisions){
    if (const auto tlocp = pc.get_tile_location()) {
      if (tile_shape(tlocp->coords()).intersects(new_shape)) {
        results.insert(pc);
      }
    }
    if (const auto oidp = pc.get_object_identifier()) {
      if (*oidp != id) {
        if (const auto ps_shape = find_as_pointer(w.get_object_personal_space_shapes(), *oidp)) {
          if (ps_shape->intersects(new_shape)) {
            results.insert(pc);
          }
        }
      }
    }
  }
}

void actually_change_personal_space_shape(object_identifier id, shape const& new_shape, object_shapes_t &personal_space_shapes, world_collision_detector &things_exposed_to_collision) {
  personal_space_shapes[id] = new_shape;
  things_exposed_to_collision.erase(id);
  things_exposed_to_collision.insert(id, new_shape.bounds());
}

void update_moving_objects_(
   world                             &w,
   objects_map<mobile_object>::type  &moving_objects,
   object_shapes_t                   &personal_space_shapes,
   object_shapes_t                   &detail_shapes,
   world_collision_detector          &things_exposed_to_collision) {

  // This entire function is kludgy and horrifically un-optimized.

  // Accelerate everything due to gravity.
  for (pair<const object_identifier, shared_ptr<mobile_object>> &p : moving_objects) {
    p.second->velocity += gravity_acceleration;
  }

  world_collision_detector sweep_box_cd;

  unordered_set<object_identifier> objects_with_some_overlap;
  for (auto const& p : moving_objects) {
    if (shape const* old_personal_space_shape = find_as_pointer(personal_space_shapes, p.first)) {
      shape dst_personal_space_shape(*old_personal_space_shape);
      dst_personal_space_shape.translate(p.second->velocity / velocity_scale_factor);

      bounding_box sweep_bounds = dst_personal_space_shape.bounds();
      sweep_bounds.combine_with(old_personal_space_shape->bounds());

      sweep_box_cd.insert(p.first, sweep_bounds);

      unordered_set<object_or_tile_identifier> this_overlaps;
      sweep_box_cd.get_objects_overlapping(this_overlaps, sweep_bounds);
      w.collect_things_exposed_to_collision_intersecting(this_overlaps, sweep_bounds);

      bool this_is_overlapping = false;
      for (object_or_tile_identifier const& foo : this_overlaps) {
        if (object_identifier const* bar = foo.get_object_identifier()) {
          if (*bar != p.first) {
            objects_with_some_overlap.insert(*bar);
            this_is_overlapping = true;
          }
        }
        else {
          // we must be a tile! nothing to do about that...
          this_is_overlapping = true;
        }
      }
      if (this_is_overlapping) objects_with_some_overlap.insert(p.first);
    }
  }

  // these get changed if an object bounces
  struct object_trajectory_info {
    object_trajectory_info(){}
    object_trajectory_info(vector3<fine_scalar>r):last_step_time(0),remaining_displacement(r),accumulated_displacement(0,0,0){}

    int64_t last_step_time;
    vector3<fine_scalar> remaining_displacement;
    vector3<fine_scalar> accumulated_displacement;
  };

  const int64_t max_time = 1ULL << 32;

  struct stepping_times {
    int64_t current_time;
    std::map<int64_t, object_identifier> queued_steps;
    stepping_times():current_time(0){}

    void queue_next_step(object_identifier id, object_trajectory_info info) {
      if (info.last_step_time == max_time) return;

      fine_scalar dispmag = info.remaining_displacement.magnitude_within_32_bits();

      int64_t step_time;
      if (dispmag == 0) step_time = max_time;
      else {
        // We're content to move in increments of tile_width >> 5 or less
        // ((max_time - info.last_step_time) / dispmag) is simply the inverse speed (in normal fine units per time unit) over the rest of the frame
        // With (max step distance) = (max step time) * (speed)
        // (max step time) = (max step distance) / speed
        // Recall that dispmag is in regular fine units instead of velocity units.
        const int64_t max_normal_step_time = (((tile_width >> 5) * (max_time - info.last_step_time)) / dispmag);
        if (info.last_step_time + max_normal_step_time > current_time) step_time = info.last_step_time + max_normal_step_time;
        else step_time = current_time;
      }

      queued_steps.insert(make_pair(std::min(max_time, step_time), id));
    }

    object_identifier pop_next_step() {
      current_time = queued_steps.begin()->first;
      object_identifier foo = queued_steps.begin()->second;
      queued_steps.erase(queued_steps.begin());
      return foo;
    }
  };

  stepping_times times;

  unordered_map<object_identifier, object_trajectory_info> trajinfo;

  for (object_identifier id : objects_with_some_overlap) {
    if (shared_ptr<mobile_object> const* objp = find_as_pointer(moving_objects, id)) {
      //if (shape const* old_personal_space_shape = find_as_pointer(personal_space_shapes, id)) {
        //fine_scalar velmag = (*objp)->velocity.magnitude_within_32_bits();
        trajinfo[id] = object_trajectory_info((*objp)->velocity / velocity_scale_factor);
        times.queue_next_step(id, trajinfo[id]);
      //}
    }
  }

  while(!times.queued_steps.empty()) {
    object_identifier id = times.pop_next_step();
    if (shared_ptr<mobile_object> const* objp = find_as_pointer(moving_objects, id)) {
      object_trajectory_info &info = trajinfo[id];
      shape new_shape(personal_space_shapes[id]);

      vector3<fine_scalar> wanted_displacement_this_step = info.remaining_displacement * (times.current_time - info.last_step_time) / (max_time - info.last_step_time);
      /*for (int i = 0; i < 3; ++i) {
        fine_scalar amount_replaced = std::max(std::abs(wanted_displacement_this_step[i]), info.displacement_conversion[i].amount);
        if (amount_replaced > 0) {
          wanted_displacement_this_step += info.displacement_conversion[i].target_direction * amount_replaced;
          wanted_displacement_this_step[i] -= amount_replaced * sign(wanted_displacement_this_step);
        }
      }*/
      new_shape.translate(wanted_displacement_this_step);

      unordered_set<object_or_tile_identifier> this_overlaps;
      w.collect_things_exposed_to_collision_intersecting(this_overlaps, new_shape.bounds());

      bool this_is_colliding = false;
      for (object_or_tile_identifier const& foo : this_overlaps) {
        if (object_identifier const* oidp = foo.get_object_identifier()) {
          if (*oidp != id) {
            if (new_shape.intersects(personal_space_shapes[*oidp])) {
              this_is_colliding = true;
            }
          }
        }
        if (tile_location const* locp = foo.get_tile_location()) {
          if (new_shape.intersects(tile_shape(locp->coords()))) {
            this_is_colliding = true;
          }
        }
      }

      if (!this_is_colliding) {
        actually_change_personal_space_shape(id, new_shape, personal_space_shapes, things_exposed_to_collision);
        info.accumulated_displacement += wanted_displacement_this_step;
        info.remaining_displacement -= wanted_displacement_this_step;
        info.last_step_time = times.current_time;
      }
      else {
        info.remaining_displacement -= wanted_displacement_this_step;
        info.last_step_time = times.current_time;
      }

      if (false) { // if our velocity changed...
        info.remaining_displacement = ((*objp)->velocity * (max_time - times.current_time)) / (max_time * velocity_scale_factor);
        unordered_set<object_or_tile_identifier> new_sweep_overlaps;

        shape dst_personal_space_shape(personal_space_shapes[id]);
        dst_personal_space_shape.translate(info.remaining_displacement);
        bounding_box sweep_bounds = dst_personal_space_shape.bounds();
        sweep_bounds.combine_with(personal_space_shapes[id].bounds());
        sweep_box_cd.erase(id);
        sweep_box_cd.insert(id, sweep_bounds);
        sweep_box_cd.get_objects_overlapping(new_sweep_overlaps, sweep_bounds);

        for (object_or_tile_identifier const& foo : this_overlaps) {
          if (object_identifier const* bar = foo.get_object_identifier()) {
            if (*bar != id) {
              if (objects_with_some_overlap.find(*bar) == objects_with_some_overlap.end()) {
                objects_with_some_overlap.insert(*bar);
                trajinfo[*bar] = object_trajectory_info(moving_objects[*bar]->velocity / velocity_scale_factor);
                times.queue_next_step(*bar, trajinfo[*bar]);
              }
            }
          }
        }
      }
    }
  }

  for (auto &p : moving_objects) {
    if (objects_with_some_overlap.find(p.first) == objects_with_some_overlap.end()) {
      personal_space_shapes[p.first].translate(p.second->velocity / velocity_scale_factor);
      detail_shapes[p.first].translate(p.second->velocity / velocity_scale_factor);
    }
    else {
      detail_shapes[p.first].translate(trajinfo[p.first].accumulated_displacement);
    }
  }
}

void world::update_moving_objects() {
  update_moving_objects_(*this, moving_objects, object_personal_space_shapes, object_detail_shapes, things_exposed_to_collision);
}

// If objects overlap with the new position, returns their IDs. If not, changes the shape and returns an empty set.
unordered_set<object_or_tile_identifier> try_to_change_personal_space_shape_(world &w, object_shapes_t &personal_space_shapes, world_collision_detector &things_exposed_to_collision, object_identifier id, shape const& new_shape) {
  unordered_set<object_or_tile_identifier> collisions;
  collect_collisions_if_object_personal_space_is_at(w, collisions, id, new_shape);

  if (collisions.empty()) {
    // Actually move (and create if you weren't there):
    actually_change_personal_space_shape(id, new_shape, personal_space_shapes, things_exposed_to_collision);
  }

  return collisions;
}

unordered_set<object_or_tile_identifier> world::try_to_change_personal_space_shape(object_identifier id, shape const& new_shape) {
  return try_to_change_personal_space_shape_(*this, object_personal_space_shapes, things_exposed_to_collision, id, new_shape);
}

// Objects can't fail to change their detail shape, but it may cause effects (like blocking a laser beam)
void world::change_detail_shape(object_identifier id, shape const& new_shape) {
  // Move
  object_detail_shapes[id] = new_shape;

  // Currently, moving a detail shape changes nothing.

  /*unordered_set<object_identifier> potential_collisions;
  collect_things_exposed_to_collision_intersecting(potential_collisions, new_shape.bounds());
  for (auto const& pc : potential_collisions){
    if (auto oidp = pc.get_object_identifier()) {
      if (auto d_shape = find_as_pointer(mobile_object_detail_shapes, *oidp)) {
        if (d_shape->intersects(new_shape)) {

        }
      }
    }
  }*/
}

<commit_msg>Cleaned up the object moving code a bunch and fixed bugs.<commit_after>
#include "world.hpp"

void collect_collisions_if_object_personal_space_is_at(world& w, unordered_set<object_or_tile_identifier> &results, object_identifier id, shape const& new_shape) {
  unordered_set<object_or_tile_identifier> potential_collisions;
  w.collect_things_exposed_to_collision_intersecting(potential_collisions, new_shape.bounds());
  for (auto const& pc : potential_collisions){
    if (const auto tlocp = pc.get_tile_location()) {
      if (tile_shape(tlocp->coords()).intersects(new_shape)) {
        results.insert(pc);
      }
    }
    if (const auto oidp = pc.get_object_identifier()) {
      if (*oidp != id) {
        if (const auto ps_shape = find_as_pointer(w.get_object_personal_space_shapes(), *oidp)) {
          if (ps_shape->intersects(new_shape)) {
            results.insert(pc);
          }
        }
      }
    }
  }
}

void actually_change_personal_space_shape(
   object_identifier id, shape const& new_shape,
   object_shapes_t           &personal_space_shapes,
   world_collision_detector  &things_exposed_to_collision) {
  personal_space_shapes[id] = new_shape;
  things_exposed_to_collision.erase(id);
  things_exposed_to_collision.insert(id, new_shape.bounds());
}

void update_moving_objects_(
   world                             &w,
   objects_map<mobile_object>::type  &moving_objects,
   object_shapes_t                   &personal_space_shapes,
   object_shapes_t                   &detail_shapes,
   world_collision_detector          &things_exposed_to_collision) {

  // This entire function is kludgy and horrifically un-optimized.

  // Accelerate everything due to gravity.
  for (pair<const object_identifier, shared_ptr<mobile_object>> &p : moving_objects) {
    p.second->velocity += gravity_acceleration;
  }

  world_collision_detector sweep_box_cd;

  unordered_set<object_identifier> objects_with_some_overlap;
  for (auto const& p : moving_objects) {
    if (shape const* old_personal_space_shape = find_as_pointer(personal_space_shapes, p.first)) {
      shape dst_personal_space_shape(*old_personal_space_shape);
      dst_personal_space_shape.translate(p.second->velocity / velocity_scale_factor);

      bounding_box sweep_bounds = dst_personal_space_shape.bounds();
      sweep_bounds.combine_with(old_personal_space_shape->bounds());

      sweep_box_cd.insert(p.first, sweep_bounds);

      unordered_set<object_or_tile_identifier> this_overlaps;
      sweep_box_cd.get_objects_overlapping(this_overlaps, sweep_bounds);
      w.collect_things_exposed_to_collision_intersecting(this_overlaps, sweep_bounds);

      bool this_is_overlapping = false;
      for (object_or_tile_identifier const& foo : this_overlaps) {
        if (object_identifier const* bar = foo.get_object_identifier()) {
          if (*bar != p.first) {
            objects_with_some_overlap.insert(*bar);
            this_is_overlapping = true;
          }
        }
        else {
          // we must be a tile! nothing to do about that...
          this_is_overlapping = true;
        }
      }
      if (this_is_overlapping) objects_with_some_overlap.insert(p.first);
    }
  }

  const int64_t max_time = 1ULL << 32;

  struct object_trajectory_info {
    object_trajectory_info(){}
    object_trajectory_info(vector3<fine_scalar>r):last_step_time(0),remaining_displacement(r),accumulated_displacement(0,0,0){}

    int64_t last_step_time;
    vector3<fine_scalar> remaining_displacement;
    vector3<fine_scalar> accumulated_displacement;
  };

  unordered_map<object_identifier, object_trajectory_info> trajinfo;

  struct stepping_times {
    int64_t current_time;
    std::multimap<int64_t, object_identifier> queued_steps;
    stepping_times():current_time(0){}

    void queue_next_step(object_identifier id, object_trajectory_info info) {
      if (info.last_step_time == max_time) return;

      fine_scalar dispmag = info.remaining_displacement.magnitude_within_32_bits();

      int64_t step_time;
      if (dispmag == 0) step_time = max_time;
      else {
        // We're content to move in increments of tile_width >> 5 or less
        // ((max_time - info.last_step_time) / dispmag) is simply the inverse speed (in normal fine units per time unit) over the rest of the frame
        // With (max step distance) = (max step time) * (speed)
        // (max step time) = (max step distance) / speed
        // Recall that dispmag is in regular fine units instead of velocity units.
        const int64_t max_normal_step_duration = (((tile_width >> 5) * (max_time - info.last_step_time)) / dispmag);
        if (info.last_step_time + max_normal_step_duration > current_time) step_time = info.last_step_time + max_normal_step_duration;
        else step_time = current_time;
      }

      queued_steps.insert(make_pair(std::min(max_time, step_time), id));
    }

    object_identifier pop_next_step() {
      current_time = queued_steps.begin()->first;
      object_identifier foo = queued_steps.begin()->second;
      queued_steps.erase(queued_steps.begin());
      return foo;
    }
  };

  stepping_times times;

  for (object_identifier id : objects_with_some_overlap) {
    shared_ptr<mobile_object> objp = moving_objects[id];
    trajinfo[id] = object_trajectory_info(objp->velocity / velocity_scale_factor);
    times.queue_next_step(id, trajinfo[id]);
  }

  while(!times.queued_steps.empty()) {
    object_identifier id = times.pop_next_step();
    shared_ptr<mobile_object> objp = moving_objects[id];
    object_trajectory_info &info = trajinfo[id];

    shape new_shape(personal_space_shapes[id]);

    vector3<fine_scalar> wanted_displacement_this_step = info.remaining_displacement * (times.current_time - info.last_step_time) / (max_time - info.last_step_time);

    new_shape.translate(wanted_displacement_this_step);

    unordered_set<object_or_tile_identifier> this_overlaps;
    w.collect_things_exposed_to_collision_intersecting(this_overlaps, new_shape.bounds());

    bool this_is_colliding = false;
    for (object_or_tile_identifier const& foo : this_overlaps) {
      if (object_identifier const* oidp = foo.get_object_identifier()) {
        if (*oidp != id) {
          if (new_shape.intersects(personal_space_shapes[*oidp])) {
            this_is_colliding = true;
          }
        }
      }
      if (tile_location const* locp = foo.get_tile_location()) {
        if (new_shape.intersects(tile_shape(locp->coords()))) {
          this_is_colliding = true;
        }
      }
    }

    if (!this_is_colliding) {
      actually_change_personal_space_shape(id, new_shape, personal_space_shapes, things_exposed_to_collision);
      info.accumulated_displacement += wanted_displacement_this_step;
      info.remaining_displacement -= wanted_displacement_this_step;
      info.last_step_time = times.current_time;
    }
    else {
      info.remaining_displacement -= wanted_displacement_this_step;
      info.last_step_time = times.current_time;
    }
    times.queue_next_step(id, info);

    if (false) { // if our velocity changed...
        info.remaining_displacement = (objp->velocity * (max_time - times.current_time)) / (max_time * velocity_scale_factor);
        unordered_set<object_or_tile_identifier> new_sweep_overlaps;

        shape dst_personal_space_shape(personal_space_shapes[id]);
        dst_personal_space_shape.translate(info.remaining_displacement);
        bounding_box sweep_bounds = dst_personal_space_shape.bounds();
        sweep_bounds.combine_with(personal_space_shapes[id].bounds());
        sweep_box_cd.erase(id);
        sweep_box_cd.insert(id, sweep_bounds);
        sweep_box_cd.get_objects_overlapping(new_sweep_overlaps, sweep_bounds);

        for (object_or_tile_identifier const& foo : this_overlaps) {
          if (object_identifier const* bar = foo.get_object_identifier()) {
            if (*bar != id) {
              if (objects_with_some_overlap.find(*bar) == objects_with_some_overlap.end()) {
                objects_with_some_overlap.insert(*bar);
                trajinfo[*bar] = object_trajectory_info(moving_objects[*bar]->velocity / velocity_scale_factor);
                times.queue_next_step(*bar, trajinfo[*bar]);
              }
            }
          }
        }
    }
  }

  for (auto &p : moving_objects) {
    if (objects_with_some_overlap.find(p.first) == objects_with_some_overlap.end()) {
      personal_space_shapes[p.first].translate(p.second->velocity / velocity_scale_factor);
      detail_shapes[p.first].translate(p.second->velocity / velocity_scale_factor);
    }
    else {
      detail_shapes[p.first].translate(trajinfo[p.first].accumulated_displacement);
    }
  }
}

void world::update_moving_objects() {
  update_moving_objects_(*this, moving_objects, object_personal_space_shapes, object_detail_shapes, things_exposed_to_collision);
}

// If objects overlap with the new position, returns their IDs. If not, changes the shape and returns an empty set.
unordered_set<object_or_tile_identifier> try_to_change_personal_space_shape_(world &w, object_shapes_t &personal_space_shapes, world_collision_detector &things_exposed_to_collision, object_identifier id, shape const& new_shape) {
  unordered_set<object_or_tile_identifier> collisions;
  collect_collisions_if_object_personal_space_is_at(w, collisions, id, new_shape);

  if (collisions.empty()) {
    // Actually move (and create if you weren't there):
    actually_change_personal_space_shape(id, new_shape, personal_space_shapes, things_exposed_to_collision);
  }

  return collisions;
}

unordered_set<object_or_tile_identifier> world::try_to_change_personal_space_shape(object_identifier id, shape const& new_shape) {
  return try_to_change_personal_space_shape_(*this, object_personal_space_shapes, things_exposed_to_collision, id, new_shape);
}

// Objects can't fail to change their detail shape, but it may cause effects (like blocking a laser beam)
void world::change_detail_shape(object_identifier id, shape const& new_shape) {
  // Move
  object_detail_shapes[id] = new_shape;

  // Currently, moving a detail shape changes nothing.

  /*unordered_set<object_identifier> potential_collisions;
  collect_things_exposed_to_collision_intersecting(potential_collisions, new_shape.bounds());
  for (auto const& pc : potential_collisions){
    if (auto oidp = pc.get_object_identifier()) {
      if (auto d_shape = find_as_pointer(mobile_object_detail_shapes, *oidp)) {
        if (d_shape->intersects(new_shape)) {

        }
      }
    }
  }*/
}

<|endoftext|>


<commit_before>#include "QxtCKeyEvent.h"

QxtCKeyEvent::QxtCKeyEvent(int qtKey, Qt::KeyboardModifiers mods, int rawKey) : QEvent(QEvent::KeyPress) {
    v = qtKey;
    k = rawKey;
    m = mods;
}

int QxtCKeyEvent::key() {
    return v;
}

int QxtCKeyEvent::nativeScanCode() {
    return k;
}

Qt::KeyboardModifiers QxtCKeyEvent::modifiers() {
    return m;
}
<commit_msg>factored to inline<commit_after><|endoftext|>