<commit_before>/*
  This file is part of the Grantlee template system.

  Copyright (c) 2009,2010 Stephen Kelly <steveire@gmail.com>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either version
  2.1 of the Licence, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "engine.h"
#include "engine_p.h"

#include "exception.h"
#include "grantlee_config_p.h"
#include "grantlee_latin1literal_p.h"
#include "grantlee_version.h"
#include "scriptabletags.h"
#include "template_p.h"
#include "templateloader.h"

#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QPluginLoader>
#include <QtCore/QRegExp>
#include <QtCore/QTextStream>

using namespace Grantlee;

static const char __scriptableLibName[] = "grantlee_scriptabletags";

Engine::Engine( QObject *parent )
    : QObject( parent ), d_ptr( new EnginePrivate( this ) )
{
  d_ptr->m_defaultLibraries << QLatin1String( "grantlee_defaulttags" )
                            << QLatin1String( "grantlee_loadertags" )
                            << QLatin1String( "grantlee_defaultfilters" );

  d_ptr->m_pluginDirs = QCoreApplication::instance()->libraryPaths();
  d_ptr->m_pluginDirs << QString::fromLocal8Bit( GRANTLEE_PLUGIN_PATH );
}

Engine::~Engine()
{
  qDeleteAll( d_ptr->m_scriptableLibraries );
  d_ptr->m_libraries.clear();
  delete d_ptr;
}

QList<AbstractTemplateLoader::Ptr> Engine::templateLoaders()
{
  Q_D( Engine );
  return d->m_loaders;
}

void Engine::addTemplateLoader( AbstractTemplateLoader::Ptr loader )
{
  Q_D( Engine );
  d->m_loaders << loader;
}

QPair<QString, QString> Engine::mediaUri( const QString &fileName ) const
{
  Q_D( const Engine );
  QListIterator<AbstractTemplateLoader::Ptr> it( d->m_loaders );

  QPair<QString, QString> uri;
  while ( it.hasNext() ) {
    const AbstractTemplateLoader::Ptr loader = it.next();
    uri = loader->getMediaUri( fileName );
    if ( !uri.second.isEmpty() )
      break;
  }
  return uri;
}

void Engine::setPluginPaths( const QStringList &dirs )
{
  Q_D( Engine );
  d->m_pluginDirs = dirs;
}

void Engine::addPluginPath( const QString &dir )
{
  Q_D( Engine );
  QStringList temp;
  temp << dir;
  temp << d->m_pluginDirs;
  d->m_pluginDirs = temp;
}

void Engine::removePluginPath( const QString &dir )
{
  Q_D( Engine );
  d->m_pluginDirs.removeAll( dir );
}

QStringList Engine::pluginPaths() const
{
  Q_D( const Engine );
  return d->m_pluginDirs;
}

QStringList Engine::defaultLibraries() const
{
  Q_D( const Engine );
  return d->m_defaultLibraries;
}

void Engine::addDefaultLibrary( const QString &libName )
{
  Q_D( Engine );
  d->m_defaultLibraries << libName;
}

void Engine::removeDefaultLibrary( const QString &libName )
{
  Q_D( Engine );
  d->m_defaultLibraries.removeAll( libName );
}

template<uint v>
bool acceptableVersion(uint minorVersion)
{
  return minorVersion >= v;
}

template<>
bool acceptableVersion<0>(uint)
{
  return true;
}

void Engine::loadDefaultLibraries()
{
  Q_D( Engine );
  // Make sure we can load default scriptable libraries if we're supposed to.
  if ( d->m_defaultLibraries.contains( QLatin1String( __scriptableLibName ) ) && !d->m_scriptableTagLibrary ) {
    d->m_scriptableTagLibrary = new ScriptableTagLibrary( this );

    // It would be better to load this as a plugin, but that is not currently possible with webkit/javascriptcore
    // so we new the library directly.
    // https://bugs.webkit.org/show_bug.cgi?id=38193
#if 0
    d->loadCppLibrary( __scriptableLibName, GRANTLEE_VERSION_MINOR );
    PluginPointer<TagLibraryInterface> library = d->loadCppLibrary( __scriptableLibName, GRANTLEE_VERSION_MINOR );
    if ( !library )
      throw Grantlee::Exception( TagSyntaxError, QLatin1String( "Could not load scriptable tags library" ) );
#endif
  }

  Q_FOREACH( const QString &libName, d->m_defaultLibraries ) {
    if ( libName == QLatin1String( __scriptableLibName ) )
      continue;

    // already loaded by the engine.
    if ( d->m_libraries.contains( libName ) )
      continue;

    // Warning. We load C++ plugins in this method, but not plugins written in QtScript.
    // This should be a better situation in Grantlee 0.2 when the TagLibraryInterface
    // can have shared pointers instead of raw pointers in its API. The whole scriptable
    // thing likely needs to be redesigned too.
    // The reason for explicitly not loading scripted plugins here is that they create new
    // NodeFactory and Filter instances with each call to loadLibrary.
    // NodeFactories are memory-managed by the Parser, as are Filters to an extent, because it
    // creates shared pointers for Filters.
    // Because this method loads the libraries but doesn't actually use them or send them
    // back to the Parser the scriptable NodeFactories and Filters but be deleted immediately.
    // The C++ plugins are different because although they are loaded, their NodeFactories
    // and Filters are accessed only by the Parser, which manages them after that.
    uint minorVersion = GRANTLEE_VERSION_MINOR;
    while ( acceptableVersion<GRANTLEE_MIN_PLUGIN_VERSION>(minorVersion) ) {
      // Although we don't use scripted libaries here, we need to recognize them being first
      // in the search path and not load a c++ plugin of the same name in that case.
      ScriptableLibraryContainer* scriptableLibrary = d->loadScriptableLibrary( libName, minorVersion );
      if ( scriptableLibrary ) {
        scriptableLibrary->clear();
        break;
      }

      PluginPointer<TagLibraryInterface> library = d->loadCppLibrary( libName, minorVersion );
      if ( minorVersion == 0)
        break;
      minorVersion--;
      if ( library )
        break;
    }
  }
}

TagLibraryInterface* Engine::loadLibrary( const QString &name )
{
  Q_D( Engine );

  if ( name == QLatin1String( __scriptableLibName ) )
    return 0;

  // already loaded by the engine.
  if ( d->m_libraries.contains( name ) )
    return d->m_libraries.value( name ).data();

  uint minorVersion = GRANTLEE_VERSION_MINOR;
  while ( acceptableVersion<GRANTLEE_MIN_PLUGIN_VERSION>(minorVersion) ) {
    TagLibraryInterface* library = d->loadLibrary( name, minorVersion );
    if ( library )
      return library;
    if (minorVersion == 0)
      break;
    minorVersion--;
  }
  throw Grantlee::Exception( TagSyntaxError, QString::fromLatin1( "Plugin library '%1' not found." ).arg( name ) );
  return 0;
}

TagLibraryInterface* EnginePrivate::loadLibrary( const QString &name, uint minorVersion )
{
  TagLibraryInterface* scriptableLibrary = loadScriptableLibrary( name, minorVersion );
  if ( scriptableLibrary )
    return scriptableLibrary;

  // else this is not a scriptable library.

  return loadCppLibrary( name, minorVersion ).data();
}

EnginePrivate::EnginePrivate( Engine *engine )
  : q_ptr( engine ), m_scriptableTagLibrary( 0 ), m_smartTrimEnabled( false )
{
}

QString EnginePrivate::getScriptLibraryName( const QString &name, uint minorVersion ) const
{
  int pluginIndex = 0;
  const QString prefix = QLatin1Literal( "/grantlee/" )
                       + QString::number( GRANTLEE_VERSION_MAJOR )
                       + QLatin1Char( '.' )
                       + QString::number( minorVersion )
                       + QLatin1Char( '/' );
  while ( m_pluginDirs.size() > pluginIndex ) {
    const QString nextDir = m_pluginDirs.at( pluginIndex++ );
    const QString libFileName = nextDir
                              + prefix
                              + name
                              + QLatin1Literal( ".qs" );

    const QFile file( libFileName );
    if ( !file.exists() )
      continue;
    return libFileName;
  }
  QList<AbstractTemplateLoader::Ptr>::const_iterator it = m_loaders.constBegin();
  const QList<AbstractTemplateLoader::Ptr>::const_iterator end = m_loaders.constEnd();
  for ( ; it != end; ++it ) {
    const QPair<QString, QString> pair = ( *it )->getMediaUri( prefix
                                                            + name
                                                            + QLatin1Literal( ".qs" ) );

    if ( !pair.first.isEmpty() && !pair.second.isEmpty() ) {
      return pair.first + pair.second;
    }
  }
  return QString();
}

ScriptableLibraryContainer* EnginePrivate::loadScriptableLibrary( const QString &name, uint minorVersion )
{
  if ( !m_scriptableTagLibrary )
    return 0;

#if 0
  if ( !m_libraries.contains( __scriptableLibName ) )
    return 0;
#endif

  const QString libFileName = getScriptLibraryName( name, minorVersion );

  if ( libFileName.isEmpty() )
    return 0;

  if ( m_scriptableLibraries.contains( libFileName ) ) {
    ScriptableLibraryContainer *library = m_scriptableLibraries.value( libFileName );
    library->setNodeFactories( m_scriptableTagLibrary->nodeFactories( libFileName ) );
    library->setFilters( m_scriptableTagLibrary->filters( libFileName ) );
    return library;
  }
#if 0
  PluginPointer<TagLibraryInterface> scriptableTagLibrary = m_libraries.value( __scriptableLibName );
#endif

  const QHash<QString, AbstractNodeFactory*> factories = m_scriptableTagLibrary->nodeFactories( libFileName );
  const QHash<QString, Filter*> filters = m_scriptableTagLibrary->filters( libFileName );

  ScriptableLibraryContainer *library = new ScriptableLibraryContainer( factories, filters );
  m_scriptableLibraries.insert( libFileName, library );
  return library;
}

PluginPointer<TagLibraryInterface> EnginePrivate::loadCppLibrary( const QString &name, uint minorVersion )
{
  int pluginIndex = 0;
  QString libFileName;

  while ( m_pluginDirs.size() > pluginIndex ) {
    const QString nextDir = m_pluginDirs.at( pluginIndex++ );
    const QString pluginDirString = nextDir
                                  + QLatin1Literal( "/grantlee/" )
                                  + QString::number( GRANTLEE_VERSION_MAJOR )
                                  + QLatin1Char( '.' )
                                  + QString::number( minorVersion )
                                  + QLatin1Char( '/' );

    const QDir pluginDir( pluginDirString );

    if ( !pluginDir.exists() )
      continue;

    const QStringList list = pluginDir.entryList( QStringList( name + QLatin1Char( '*' ) ) );

    if ( list.isEmpty() )
      continue;

    QString pluginPath=pluginDir.absoluteFilePath( list.first() );
    PluginPointer<TagLibraryInterface> plugin = PluginPointer<TagLibraryInterface>( pluginPath );

    if ( plugin ) {
#ifdef __COVERAGESCANNER__
      __coveragescanner_register_library(pluginPath.toLatin1().data());
#endif
      m_libraries.insert( name, plugin );
      return plugin;
    }
  }
  return 0;
}

Template Engine::loadByName( const QString &name ) const
{
  Q_D( const Engine );

  QListIterator<AbstractTemplateLoader::Ptr> it( d->m_loaders );
  while ( it.hasNext() ) {
    const AbstractTemplateLoader::Ptr loader = it.next();

    if ( !loader->canLoadTemplate( name ) )
      continue;

    const Template t = loader->loadByName( name, this );

    if ( t ) {
      return t;
    }
  }
  Template t = Template( new TemplateImpl( this ) );
  t->setObjectName( name );
  t->d_ptr->m_error = TagSyntaxError;
  t->d_ptr->m_errorString = QString::fromLatin1( "Template not found, %1" ).arg( name );
  return t;
}

Template Engine::newTemplate( const QString &content, const QString &name ) const
{
  Q_D( const Engine );
  Template t = Template( new TemplateImpl( this, d->m_smartTrimEnabled ) );
  t->setObjectName( name );
  t->setContent( content );
  return t;
}

void Engine::setSmartTrimEnabled( bool enabled )
{
  Q_D( Engine );
  d->m_smartTrimEnabled = enabled;
}

bool Engine::smartTrimEnabled() const
{
  Q_D( const Engine );
  return d->m_smartTrimEnabled;
}
<commit_msg>Remove unused variable.<commit_after>/*
  This file is part of the Grantlee template system.

  Copyright (c) 2009,2010 Stephen Kelly <steveire@gmail.com>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either version
  2.1 of the Licence, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "engine.h"
#include "engine_p.h"

#include "exception.h"
#include "grantlee_config_p.h"
#include "grantlee_latin1literal_p.h"
#include "grantlee_version.h"
#include "scriptabletags.h"
#include "template_p.h"
#include "templateloader.h"

#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QPluginLoader>
#include <QtCore/QRegExp>
#include <QtCore/QTextStream>

using namespace Grantlee;

static const char __scriptableLibName[] = "grantlee_scriptabletags";

Engine::Engine( QObject *parent )
    : QObject( parent ), d_ptr( new EnginePrivate( this ) )
{
  d_ptr->m_defaultLibraries << QLatin1String( "grantlee_defaulttags" )
                            << QLatin1String( "grantlee_loadertags" )
                            << QLatin1String( "grantlee_defaultfilters" );

  d_ptr->m_pluginDirs = QCoreApplication::instance()->libraryPaths();
  d_ptr->m_pluginDirs << QString::fromLocal8Bit( GRANTLEE_PLUGIN_PATH );
}

Engine::~Engine()
{
  qDeleteAll( d_ptr->m_scriptableLibraries );
  d_ptr->m_libraries.clear();
  delete d_ptr;
}

QList<AbstractTemplateLoader::Ptr> Engine::templateLoaders()
{
  Q_D( Engine );
  return d->m_loaders;
}

void Engine::addTemplateLoader( AbstractTemplateLoader::Ptr loader )
{
  Q_D( Engine );
  d->m_loaders << loader;
}

QPair<QString, QString> Engine::mediaUri( const QString &fileName ) const
{
  Q_D( const Engine );
  QListIterator<AbstractTemplateLoader::Ptr> it( d->m_loaders );

  QPair<QString, QString> uri;
  while ( it.hasNext() ) {
    const AbstractTemplateLoader::Ptr loader = it.next();
    uri = loader->getMediaUri( fileName );
    if ( !uri.second.isEmpty() )
      break;
  }
  return uri;
}

void Engine::setPluginPaths( const QStringList &dirs )
{
  Q_D( Engine );
  d->m_pluginDirs = dirs;
}

void Engine::addPluginPath( const QString &dir )
{
  Q_D( Engine );
  QStringList temp;
  temp << dir;
  temp << d->m_pluginDirs;
  d->m_pluginDirs = temp;
}

void Engine::removePluginPath( const QString &dir )
{
  Q_D( Engine );
  d->m_pluginDirs.removeAll( dir );
}

QStringList Engine::pluginPaths() const
{
  Q_D( const Engine );
  return d->m_pluginDirs;
}

QStringList Engine::defaultLibraries() const
{
  Q_D( const Engine );
  return d->m_defaultLibraries;
}

void Engine::addDefaultLibrary( const QString &libName )
{
  Q_D( Engine );
  d->m_defaultLibraries << libName;
}

void Engine::removeDefaultLibrary( const QString &libName )
{
  Q_D( Engine );
  d->m_defaultLibraries.removeAll( libName );
}

template<uint v>
bool acceptableVersion(uint minorVersion)
{
  return minorVersion >= v;
}

template<>
bool acceptableVersion<0>(uint)
{
  return true;
}

void Engine::loadDefaultLibraries()
{
  Q_D( Engine );
  // Make sure we can load default scriptable libraries if we're supposed to.
  if ( d->m_defaultLibraries.contains( QLatin1String( __scriptableLibName ) ) && !d->m_scriptableTagLibrary ) {
    d->m_scriptableTagLibrary = new ScriptableTagLibrary( this );

    // It would be better to load this as a plugin, but that is not currently possible with webkit/javascriptcore
    // so we new the library directly.
    // https://bugs.webkit.org/show_bug.cgi?id=38193
#if 0
    d->loadCppLibrary( __scriptableLibName, GRANTLEE_VERSION_MINOR );
    PluginPointer<TagLibraryInterface> library = d->loadCppLibrary( __scriptableLibName, GRANTLEE_VERSION_MINOR );
    if ( !library )
      throw Grantlee::Exception( TagSyntaxError, QLatin1String( "Could not load scriptable tags library" ) );
#endif
  }

  Q_FOREACH( const QString &libName, d->m_defaultLibraries ) {
    if ( libName == QLatin1String( __scriptableLibName ) )
      continue;

    // already loaded by the engine.
    if ( d->m_libraries.contains( libName ) )
      continue;

    // Warning. We load C++ plugins in this method, but not plugins written in QtScript.
    // This should be a better situation in Grantlee 0.2 when the TagLibraryInterface
    // can have shared pointers instead of raw pointers in its API. The whole scriptable
    // thing likely needs to be redesigned too.
    // The reason for explicitly not loading scripted plugins here is that they create new
    // NodeFactory and Filter instances with each call to loadLibrary.
    // NodeFactories are memory-managed by the Parser, as are Filters to an extent, because it
    // creates shared pointers for Filters.
    // Because this method loads the libraries but doesn't actually use them or send them
    // back to the Parser the scriptable NodeFactories and Filters but be deleted immediately.
    // The C++ plugins are different because although they are loaded, their NodeFactories
    // and Filters are accessed only by the Parser, which manages them after that.
    uint minorVersion = GRANTLEE_VERSION_MINOR;
    while ( acceptableVersion<GRANTLEE_MIN_PLUGIN_VERSION>(minorVersion) ) {
      // Although we don't use scripted libaries here, we need to recognize them being first
      // in the search path and not load a c++ plugin of the same name in that case.
      ScriptableLibraryContainer* scriptableLibrary = d->loadScriptableLibrary( libName, minorVersion );
      if ( scriptableLibrary ) {
        scriptableLibrary->clear();
        break;
      }

      PluginPointer<TagLibraryInterface> library = d->loadCppLibrary( libName, minorVersion );
      if ( minorVersion == 0)
        break;
      minorVersion--;
      if ( library )
        break;
    }
  }
}

TagLibraryInterface* Engine::loadLibrary( const QString &name )
{
  Q_D( Engine );

  if ( name == QLatin1String( __scriptableLibName ) )
    return 0;

  // already loaded by the engine.
  if ( d->m_libraries.contains( name ) )
    return d->m_libraries.value( name ).data();

  uint minorVersion = GRANTLEE_VERSION_MINOR;
  while ( acceptableVersion<GRANTLEE_MIN_PLUGIN_VERSION>(minorVersion) ) {
    TagLibraryInterface* library = d->loadLibrary( name, minorVersion );
    if ( library )
      return library;
    if (minorVersion == 0)
      break;
    minorVersion--;
  }
  throw Grantlee::Exception( TagSyntaxError, QString::fromLatin1( "Plugin library '%1' not found." ).arg( name ) );
  return 0;
}

TagLibraryInterface* EnginePrivate::loadLibrary( const QString &name, uint minorVersion )
{
  TagLibraryInterface* scriptableLibrary = loadScriptableLibrary( name, minorVersion );
  if ( scriptableLibrary )
    return scriptableLibrary;

  // else this is not a scriptable library.

  return loadCppLibrary( name, minorVersion ).data();
}

EnginePrivate::EnginePrivate( Engine *engine )
  : q_ptr( engine ), m_scriptableTagLibrary( 0 ), m_smartTrimEnabled( false )
{
}

QString EnginePrivate::getScriptLibraryName( const QString &name, uint minorVersion ) const
{
  int pluginIndex = 0;
  const QString prefix = QLatin1Literal( "/grantlee/" )
                       + QString::number( GRANTLEE_VERSION_MAJOR )
                       + QLatin1Char( '.' )
                       + QString::number( minorVersion )
                       + QLatin1Char( '/' );
  while ( m_pluginDirs.size() > pluginIndex ) {
    const QString nextDir = m_pluginDirs.at( pluginIndex++ );
    const QString libFileName = nextDir
                              + prefix
                              + name
                              + QLatin1Literal( ".qs" );

    const QFile file( libFileName );
    if ( !file.exists() )
      continue;
    return libFileName;
  }
  QList<AbstractTemplateLoader::Ptr>::const_iterator it = m_loaders.constBegin();
  const QList<AbstractTemplateLoader::Ptr>::const_iterator end = m_loaders.constEnd();
  for ( ; it != end; ++it ) {
    const QPair<QString, QString> pair = ( *it )->getMediaUri( prefix
                                                            + name
                                                            + QLatin1Literal( ".qs" ) );

    if ( !pair.first.isEmpty() && !pair.second.isEmpty() ) {
      return pair.first + pair.second;
    }
  }
  return QString();
}

ScriptableLibraryContainer* EnginePrivate::loadScriptableLibrary( const QString &name, uint minorVersion )
{
  if ( !m_scriptableTagLibrary )
    return 0;

#if 0
  if ( !m_libraries.contains( __scriptableLibName ) )
    return 0;
#endif

  const QString libFileName = getScriptLibraryName( name, minorVersion );

  if ( libFileName.isEmpty() )
    return 0;

  if ( m_scriptableLibraries.contains( libFileName ) ) {
    ScriptableLibraryContainer *library = m_scriptableLibraries.value( libFileName );
    library->setNodeFactories( m_scriptableTagLibrary->nodeFactories( libFileName ) );
    library->setFilters( m_scriptableTagLibrary->filters( libFileName ) );
    return library;
  }
#if 0
  PluginPointer<TagLibraryInterface> scriptableTagLibrary = m_libraries.value( __scriptableLibName );
#endif

  const QHash<QString, AbstractNodeFactory*> factories = m_scriptableTagLibrary->nodeFactories( libFileName );
  const QHash<QString, Filter*> filters = m_scriptableTagLibrary->filters( libFileName );

  ScriptableLibraryContainer *library = new ScriptableLibraryContainer( factories, filters );
  m_scriptableLibraries.insert( libFileName, library );
  return library;
}

PluginPointer<TagLibraryInterface> EnginePrivate::loadCppLibrary( const QString &name, uint minorVersion )
{
  int pluginIndex = 0;

  while ( m_pluginDirs.size() > pluginIndex ) {
    const QString nextDir = m_pluginDirs.at( pluginIndex++ );
    const QString pluginDirString = nextDir
                                  + QLatin1Literal( "/grantlee/" )
                                  + QString::number( GRANTLEE_VERSION_MAJOR )
                                  + QLatin1Char( '.' )
                                  + QString::number( minorVersion )
                                  + QLatin1Char( '/' );

    const QDir pluginDir( pluginDirString );

    if ( !pluginDir.exists() )
      continue;

    const QStringList list = pluginDir.entryList( QStringList( name + QLatin1Char( '*' ) ) );

    if ( list.isEmpty() )
      continue;

    QString pluginPath=pluginDir.absoluteFilePath( list.first() );
    PluginPointer<TagLibraryInterface> plugin = PluginPointer<TagLibraryInterface>( pluginPath );

    if ( plugin ) {
#ifdef __COVERAGESCANNER__
      __coveragescanner_register_library(pluginPath.toLatin1().data());
#endif
      m_libraries.insert( name, plugin );
      return plugin;
    }
  }
  return 0;
}

Template Engine::loadByName( const QString &name ) const
{
  Q_D( const Engine );

  QListIterator<AbstractTemplateLoader::Ptr> it( d->m_loaders );
  while ( it.hasNext() ) {
    const AbstractTemplateLoader::Ptr loader = it.next();

    if ( !loader->canLoadTemplate( name ) )
      continue;

    const Template t = loader->loadByName( name, this );

    if ( t ) {
      return t;
    }
  }
  Template t = Template( new TemplateImpl( this ) );
  t->setObjectName( name );
  t->d_ptr->m_error = TagSyntaxError;
  t->d_ptr->m_errorString = QString::fromLatin1( "Template not found, %1" ).arg( name );
  return t;
}

Template Engine::newTemplate( const QString &content, const QString &name ) const
{
  Q_D( const Engine );
  Template t = Template( new TemplateImpl( this, d->m_smartTrimEnabled ) );
  t->setObjectName( name );
  t->setContent( content );
  return t;
}

void Engine::setSmartTrimEnabled( bool enabled )
{
  Q_D( Engine );
  d->m_smartTrimEnabled = enabled;
}

bool Engine::smartTrimEnabled() const
{
  Q_D( const Engine );
  return d->m_smartTrimEnabled;
}
<|endoftext|>


<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2005 Robert Osfield
 *
 * This application is open source and may be redistributed and/or modified
 * freely and without restriction, both in commericial and non commericial
 * applications, as long as this copyright notice is maintained.
 *
 * This application 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.
*/

#include <osg/StateAttribute>
#include <osg/StateSet>
#include <osg/Notify>

#include <algorithm>

using namespace osg;

void StateAttribute::addParent(osg::StateSet* object)
{
    _parents.push_back(object);
}

void StateAttribute::removeParent(osg::StateSet* object)
{
    ParentList::iterator pitr = std::find(_parents.begin(),_parents.end(),object);
    if (pitr!=_parents.end()) _parents.erase(pitr);
}

void StateAttribute::setUpdateCallback(Callback* uc)
{
    osg::notify(osg::NOTICE)<<"Uniform::Setting Update callbacks"<<std::endl;

    if (_updateCallback==uc) return;

    int delta = 0;
    if (_updateCallback.valid()) --delta;
    if (uc) ++delta;

    _updateCallback = uc;

    if (delta!=0)
    {
        osg::notify(osg::NOTICE)<<"Going to set StateSet parents"<<std::endl;

        for(ParentList::iterator itr=_parents.begin();
            itr!=_parents.end();
            ++itr)
        {
            (*itr)->setNumChildrenRequiringUpdateTraversal((*itr)->getNumChildrenRequiringUpdateTraversal()+delta);
        }
    }
}

void StateAttribute::setEventCallback(Callback* ec)
{
    osg::notify(osg::NOTICE)<<"Uniform::Setting Event callbacks"<<std::endl;

    if (_updateCallback==ec) return;

    int delta = 0;
    if (_updateCallback.valid()) --delta;
    if (ec) ++delta;

    _updateCallback = ec;

    if (delta!=0)
    {
        for(ParentList::iterator itr=_parents.begin();
            itr!=_parents.end();
            ++itr)
        {
            (*itr)->setNumChildrenRequiringUpdateTraversal((*itr)->getNumChildrenRequiringUpdateTraversal()+delta);
        }
    }
}
<commit_msg>Reduced the verbosness of debugging messages<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2005 Robert Osfield
 *
 * This application is open source and may be redistributed and/or modified
 * freely and without restriction, both in commericial and non commericial
 * applications, as long as this copyright notice is maintained.
 *
 * This application 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.
*/

#include <osg/StateAttribute>
#include <osg/StateSet>
#include <osg/Notify>

#include <algorithm>

using namespace osg;

void StateAttribute::addParent(osg::StateSet* object)
{
    _parents.push_back(object);
}

void StateAttribute::removeParent(osg::StateSet* object)
{
    ParentList::iterator pitr = std::find(_parents.begin(),_parents.end(),object);
    if (pitr!=_parents.end()) _parents.erase(pitr);
}

void StateAttribute::setUpdateCallback(Callback* uc)
{
    osg::notify(osg::INFO)<<"StateAttribute::Setting Update callbacks"<<std::endl;

    if (_updateCallback==uc) return;

    int delta = 0;
    if (_updateCallback.valid()) --delta;
    if (uc) ++delta;

    _updateCallback = uc;

    if (delta!=0)
    {
        osg::notify(osg::INFO)<<"Going to set StateAttribute parents"<<std::endl;

        for(ParentList::iterator itr=_parents.begin();
            itr!=_parents.end();
            ++itr)
        {
            osg::notify(osg::INFO)<<"   Setting StateAttribute parent"<<std::endl;

            (*itr)->setNumChildrenRequiringUpdateTraversal((*itr)->getNumChildrenRequiringUpdateTraversal()+delta);
        }
    }
}

void StateAttribute::setEventCallback(Callback* ec)
{
    osg::notify(osg::INFO)<<"StateAttribute::Setting Event callbacks"<<std::endl;

    if (_updateCallback==ec) return;

    int delta = 0;
    if (_updateCallback.valid()) --delta;
    if (ec) ++delta;

    _updateCallback = ec;

    if (delta!=0)
    {
        for(ParentList::iterator itr=_parents.begin();
            itr!=_parents.end();
            ++itr)
        {
            (*itr)->setNumChildrenRequiringUpdateTraversal((*itr)->getNumChildrenRequiringUpdateTraversal()+delta);
        }
    }
}
<|endoftext|>


<commit_before>#include "clay.hpp"
#include <cstdio>
#include <cstdlib>
#include <cstdarg>

//
// invoke stack - a compilation call stack
//

static vector<InvokeStackEntry> invokeStack;

void pushInvokeStack(ObjectPtr callable, const vector<TypePtr> &argsKey) {
    invokeStack.push_back(make_pair(callable, argsKey));
}

void popInvokeStack() {
    invokeStack.pop_back();
}

vector<InvokeStackEntry> getInvokeStack() {
    return invokeStack;
}

void setInvokeStack(const vector<InvokeStackEntry> &x) {
    invokeStack = x;
}

//
// source location of the current item being processed
//

static vector<LocationPtr> errorLocations;

void pushLocation(LocationPtr location) {
    errorLocations.push_back(location);
}

void popLocation() {
    errorLocations.pop_back();
}

LocationPtr topLocation() {
    vector<LocationPtr>::iterator i, begin;
    i = errorLocations.end();
    begin = errorLocations.begin();
    while (i != begin) {
        --i;
        if (i->ptr()) return *i;
    }
    return NULL;
}

//
// DebugPrinter
//

static vector<ObjectPtr> debugStack;

int DebugPrinter::indent = 0;

DebugPrinter::DebugPrinter(ObjectPtr obj)
    : obj(obj)
{
    for (int i = 0; i < indent; ++i)
        std::cout << ' ';
    std::cout << "BEGIN - " << obj << '\n';
    ++indent;
    debugStack.push_back(obj);
}

DebugPrinter::~DebugPrinter()
{
    debugStack.pop_back();
    --indent;
    for (int i = 0; i < indent; ++i)
        std::cout << ' ';
    std::cout << "DONE - " << obj << '\n';
}

//
// report error
//

static void computeLineCol(LocationPtr location, int &line, int &column) {
    char *p = location->source->data;
    char *end = p + location->offset;
    line = column = 0;
    for (; p != end; ++p) {
        ++column;
        if (*p == '\n') {
            ++line;
            column = 0;
        }
    }
}

static void splitLines(SourcePtr source, vector<string> &lines) {
    lines.push_back(string());
    char *p = source->data;
    char *end = p + source->size;
    for (; p != end; ++p) {
        lines.back().push_back(*p);
        if (*p == '\n')
            lines.push_back(string());
    }
}

static bool endsWithNewline(const string& s) {
    if (s.size() == 0) return false;
    return s[s.size()-1] == '\n';
}

static void displayLocation(LocationPtr location, int &line, int &column) {
    computeLineCol(location, line, column);
    vector<string> lines;
    splitLines(location->source, lines);
    fprintf(stderr, "###############################\n");
    for (int i = line-2; i <= line+2; ++i) {
        if ((i < 0) || (i >= (int)lines.size()))
            continue;
        fprintf(stderr, "%s", lines[i].c_str());
        if (!endsWithNewline(lines[i]))
            fprintf(stderr, "\n");
        if (i == line) {
            for (int j = 0; j < column; ++j)
                fprintf(stderr, "-");
            fprintf(stderr, "^\n");
        }
    }
    fprintf(stderr, "###############################\n");
}

static void displayInvokeStack() {
    if (invokeStack.empty())
        return;
    fprintf(stderr, "\n");
    fprintf(stderr, "compilation context: \n");
    for (unsigned i = invokeStack.size(); i > 0; --i) {
        ObjectPtr callable = invokeStack[i-1].first;
        const vector<TypePtr> &argsKey = invokeStack[i-1].second;

        ostringstream sout;
        printName(sout, callable);
        sout << "(";
        printNameList(sout, argsKey);
        sout << ")";
        fprintf(stderr, "  %s\n", sout.str().c_str());
    }
}

static void displayDebugStack() {
    if (debugStack.empty())
        return;
    fprintf(stderr, "\n");
    fprintf(stderr, "debug stack: \n");
    for (unsigned i = debugStack.size(); i > 0; --i) {
        ostringstream sout;
        sout << debugStack[i-1];
        fprintf(stderr, "  %s\n", sout.str().c_str());
    }
}

static bool abortOnError = false;

void setAbortOnError(bool flag) {
    abortOnError = flag;
}

void error(const string &msg) {
    LocationPtr location = topLocation();
    if (location.ptr()) {
        int line, column;
        displayLocation(location, line, column);
        fprintf(stderr, "%s(%d,%d): error: %s\n",
                location->source->fileName.c_str(),
                line+1, column, msg.c_str());
        displayInvokeStack();
        displayDebugStack();
    }
    else {
        fprintf(stderr, "error: %s\n", msg.c_str());
    }
    if (abortOnError)
        abort();
    else
        exit(-1);
}

void fmtError(const char *fmt, ...) {
    va_list ap;
    char s[256];
    va_start(ap, fmt);
    vsnprintf(s, sizeof(s)-1, fmt, ap);
    va_end(ap);
    error(s);
}

void argumentError(unsigned int index, const string &msg) {
    ostringstream sout;
    sout << "argument " << (index+1) << ": " << msg;
    error(sout.str());
}

static const char *valuesStr(int n) {
    if (n == 1)
        return "value";
    else
        return "values";
}

void arityError(int expected, int received) {
    ostringstream sout;
    sout << "expected " << expected << " " << valuesStr(expected);
    sout << ", but received " << received << " " << valuesStr(received);
    error(sout.str());
}

void arityError2(int minExpected, int received) {
    ostringstream sout;
    sout << "expected atleast " << minExpected
         << " " << valuesStr(minExpected);
    sout << ", but received " << received << " " << valuesStr(received);
    error(sout.str());
}

void ensureArity(MultiStaticPtr args, unsigned int size) {
    if (args->size() != size)
        arityError(size, args->size());
}

void ensureArity(MultiEValuePtr args, unsigned int size) {
    if (args->size() != size)
        arityError(size, args->size());
}

void ensureArity(MultiPValuePtr args, unsigned int size) {
    if (args->size() != size)
        arityError(size, args->size());
}

void ensureArity(MultiCValuePtr args, unsigned int size) {
    if (args->size() != size)
        arityError(size, args->size());
}
<commit_msg>in the compiler, handle tabs when reporting error location.<commit_after>#include "clay.hpp"
#include <cstdio>
#include <cstdlib>
#include <cstdarg>

//
// invoke stack - a compilation call stack
//

static vector<InvokeStackEntry> invokeStack;

void pushInvokeStack(ObjectPtr callable, const vector<TypePtr> &argsKey) {
    invokeStack.push_back(make_pair(callable, argsKey));
}

void popInvokeStack() {
    invokeStack.pop_back();
}

vector<InvokeStackEntry> getInvokeStack() {
    return invokeStack;
}

void setInvokeStack(const vector<InvokeStackEntry> &x) {
    invokeStack = x;
}

//
// source location of the current item being processed
//

static vector<LocationPtr> errorLocations;

void pushLocation(LocationPtr location) {
    errorLocations.push_back(location);
}

void popLocation() {
    errorLocations.pop_back();
}

LocationPtr topLocation() {
    vector<LocationPtr>::iterator i, begin;
    i = errorLocations.end();
    begin = errorLocations.begin();
    while (i != begin) {
        --i;
        if (i->ptr()) return *i;
    }
    return NULL;
}

//
// DebugPrinter
//

static vector<ObjectPtr> debugStack;

int DebugPrinter::indent = 0;

DebugPrinter::DebugPrinter(ObjectPtr obj)
    : obj(obj)
{
    for (int i = 0; i < indent; ++i)
        std::cout << ' ';
    std::cout << "BEGIN - " << obj << '\n';
    ++indent;
    debugStack.push_back(obj);
}

DebugPrinter::~DebugPrinter()
{
    debugStack.pop_back();
    --indent;
    for (int i = 0; i < indent; ++i)
        std::cout << ' ';
    std::cout << "DONE - " << obj << '\n';
}

//
// report error
//

static void computeLineCol(LocationPtr location,
                           int &line,
                           int &column,
                           int &tabColumn) {
    char *p = location->source->data;
    char *end = p + location->offset;
    line = column = tabColumn = 0;
    for (; p != end; ++p) {
        ++column;
        ++tabColumn;
        if (*p == '\n') {
            ++line;
            column = 0;
            tabColumn = 0;
        }
        else if (*p == '\t') {
            tabColumn += 7;
        }
    }
}

static void splitLines(SourcePtr source, vector<string> &lines) {
    lines.push_back(string());
    char *p = source->data;
    char *end = p + source->size;
    for (; p != end; ++p) {
        lines.back().push_back(*p);
        if (*p == '\n')
            lines.push_back(string());
    }
}

static bool endsWithNewline(const string& s) {
    if (s.size() == 0) return false;
    return s[s.size()-1] == '\n';
}

static void displayLocation(LocationPtr location, int &line, int &column) {
    int tabColumn;
    computeLineCol(location, line, column, tabColumn);
    vector<string> lines;
    splitLines(location->source, lines);
    fprintf(stderr, "###############################\n");
    for (int i = line-2; i <= line+2; ++i) {
        if ((i < 0) || (i >= (int)lines.size()))
            continue;
        fprintf(stderr, "%s", lines[i].c_str());
        if (!endsWithNewline(lines[i]))
            fprintf(stderr, "\n");
        if (i == line) {
            for (int j = 0; j < tabColumn; ++j)
                fprintf(stderr, "-");
            fprintf(stderr, "^\n");
        }
    }
    fprintf(stderr, "###############################\n");
}

static void displayInvokeStack() {
    if (invokeStack.empty())
        return;
    fprintf(stderr, "\n");
    fprintf(stderr, "compilation context: \n");
    for (unsigned i = invokeStack.size(); i > 0; --i) {
        ObjectPtr callable = invokeStack[i-1].first;
        const vector<TypePtr> &argsKey = invokeStack[i-1].second;

        ostringstream sout;
        printName(sout, callable);
        sout << "(";
        printNameList(sout, argsKey);
        sout << ")";
        fprintf(stderr, "  %s\n", sout.str().c_str());
    }
}

static void displayDebugStack() {
    if (debugStack.empty())
        return;
    fprintf(stderr, "\n");
    fprintf(stderr, "debug stack: \n");
    for (unsigned i = debugStack.size(); i > 0; --i) {
        ostringstream sout;
        sout << debugStack[i-1];
        fprintf(stderr, "  %s\n", sout.str().c_str());
    }
}

static bool abortOnError = false;

void setAbortOnError(bool flag) {
    abortOnError = flag;
}

void error(const string &msg) {
    LocationPtr location = topLocation();
    if (location.ptr()) {
        int line, column;
        displayLocation(location, line, column);
        fprintf(stderr, "%s(%d,%d): error: %s\n",
                location->source->fileName.c_str(),
                line+1, column, msg.c_str());
        displayInvokeStack();
        displayDebugStack();
    }
    else {
        fprintf(stderr, "error: %s\n", msg.c_str());
    }
    if (abortOnError)
        abort();
    else
        exit(-1);
}

void fmtError(const char *fmt, ...) {
    va_list ap;
    char s[256];
    va_start(ap, fmt);
    vsnprintf(s, sizeof(s)-1, fmt, ap);
    va_end(ap);
    error(s);
}

void argumentError(unsigned int index, const string &msg) {
    ostringstream sout;
    sout << "argument " << (index+1) << ": " << msg;
    error(sout.str());
}

static const char *valuesStr(int n) {
    if (n == 1)
        return "value";
    else
        return "values";
}

void arityError(int expected, int received) {
    ostringstream sout;
    sout << "expected " << expected << " " << valuesStr(expected);
    sout << ", but received " << received << " " << valuesStr(received);
    error(sout.str());
}

void arityError2(int minExpected, int received) {
    ostringstream sout;
    sout << "expected atleast " << minExpected
         << " " << valuesStr(minExpected);
    sout << ", but received " << received << " " << valuesStr(received);
    error(sout.str());
}

void ensureArity(MultiStaticPtr args, unsigned int size) {
    if (args->size() != size)
        arityError(size, args->size());
}

void ensureArity(MultiEValuePtr args, unsigned int size) {
    if (args->size() != size)
        arityError(size, args->size());
}

void ensureArity(MultiPValuePtr args, unsigned int size) {
    if (args->size() != size)
        arityError(size, args->size());
}

void ensureArity(MultiCValuePtr args, unsigned int size) {
    if (args->size() != size)
        arityError(size, args->size());
}
<|endoftext|>


<commit_before>/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "pathOrderOptimizer.h"
#include "utils/logoutput.h"
#include "utils/BucketGrid2D.h"
#include "utils/linearAlg2D.h"

#define INLINE static inline

namespace cura {

/**
*
*/
void PathOrderOptimizer::optimize()
{
    bool picked[polygons.size()];
    memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses

    for (PolygonRef poly : polygons) /// find closest point to initial starting point within each polygon +initialize picked
    {
        int best = -1;
        float bestDist = std::numeric_limits<float>::infinity();
        for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point in polygon
        {
            float dist = vSize2f(poly[point_idx] - startPoint);
            if (dist < bestDist)
            {
                best = point_idx;
                bestDist = dist;
            }
        }
        polyStart.push_back(best);
        //picked.push_back(false); /// initialize all picked values as false

        assert(poly.size() != 2);
    }

    Point prev_point = startPoint;
    for (unsigned int poly_order_idx = 0; poly_order_idx < polygons.size(); poly_order_idx++) /// actual path order optimizer
    {
        int best_poly_idx = -1;
        float bestDist = std::numeric_limits<float>::infinity();

        for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
        {
            if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
            {
                continue;
            }

            assert (polygons[poly_idx].size() != 2);

            float dist = vSize2f(polygons[poly_idx][polyStart[poly_idx]] - prev_point);
            if (dist < bestDist)
            {
                best_poly_idx = poly_idx;
                bestDist = dist;
            }

        }

        if (best_poly_idx > -1) /// should always be true; we should have been able to identify the best next polygon
        {
            assert(polygons[best_poly_idx].size() != 2);

            prev_point = polygons[best_poly_idx][polyStart[best_poly_idx]];

            picked[best_poly_idx] = true;
            polyOrder.push_back(best_poly_idx);
        }
        else
        {
            logError("Failed to find next closest polygon.\n");
        }
    }

    prev_point = startPoint;
    for (unsigned int order_idx = 0; order_idx < polyOrder.size(); order_idx++) /// decide final starting points in each polygon
    {
        int poly_idx = polyOrder[order_idx];
        int point_idx = getPolyStart(prev_point, poly_idx);
        polyStart[poly_idx] = point_idx;
        prev_point = polygons[poly_idx][point_idx];

    }
}

int PathOrderOptimizer::getPolyStart(Point prev_point, int poly_idx)
{
    switch (type)
    {
        case EZSeamType::BACK:      return getFarthestPointInPolygon(poly_idx);
        case EZSeamType::RANDOM:    return getRandomPointInPolygon(poly_idx);
        case EZSeamType::SHORTEST:  return getClosestPointInPolygon(prev_point, poly_idx);
        default:                    return getClosestPointInPolygon(prev_point, poly_idx);
    }
}

int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx)
{
    PolygonRef poly = polygons[poly_idx];

    int best_point_idx = -1;
    float best_point_score = std::numeric_limits<float>::infinity();
    Point p0 = poly.back();
    for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
    {
        Point& p1 = poly[point_idx];
        Point& p2 = poly[(point_idx + 1) % poly.size()];
        int64_t dist = vSize2(p1 - prev_point);
        float is_on_inside_corner_score = -LinearAlg2D::getAngleLeft(p0, p1, p2) / M_PI * 5000 * 5000; // prefer inside corners
        // this score is in the order of 5 mm
        if (dist + is_on_inside_corner_score < best_point_score)
        {
            best_point_idx = point_idx;
            best_point_score = dist + is_on_inside_corner_score;
        }
        p0 = p1;
    }
    return best_point_idx;
}

int PathOrderOptimizer::getRandomPointInPolygon(int poly_idx)
{
    return rand() % polygons[poly_idx].size();
}

int PathOrderOptimizer::getFarthestPointInPolygon(int poly_idx)
{
    PolygonRef poly = polygons[poly_idx];
    int best_point_idx = -1;
    float best_y = std::numeric_limits<float>::min();
    for(unsigned int point_idx=0 ; point_idx<poly.size() ; point_idx++)
    {
        if (poly[point_idx].Y > best_y)
        {
            best_point_idx = point_idx;
            best_y = poly[point_idx].Y;
        }
    }
    return best_point_idx;
}

/**
*
*/
void LineOrderOptimizer::optimize()
{
    int gridSize = 5000; // the size of the cells in the hash grid.
    BucketGrid2D<unsigned int> line_bucket_grid(gridSize);
    bool picked[polygons.size()];
    memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses

    for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) /// find closest point to initial starting point within each polygon +initialize picked
    {
        int best_point_idx = -1;
        float best_point_dist = std::numeric_limits<float>::infinity();
        PolygonRef poly = polygons[poly_idx];
        for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point from polygon
        {
            float dist = vSize2f(poly[point_idx] - startPoint);
            if (dist < best_point_dist)
            {
                best_point_idx = point_idx;
                best_point_dist = dist;
            }
        }
        polyStart.push_back(best_point_idx);

        assert(poly.size() == 2);

        line_bucket_grid.insert(poly[0], poly_idx);
        line_bucket_grid.insert(poly[1], poly_idx);

    }

    Point incommingPerpundicularNormal(0, 0);
    Point prev_point = startPoint;
    for (unsigned int order_idx = 0; order_idx < polygons.size(); order_idx++) /// actual path order optimizer
    {
        int best_line_idx = -1;
        float bestDist = std::numeric_limits<float>::infinity();

        for(unsigned int close_line_poly_idx :  line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
        {
            if (picked[close_line_poly_idx] || polygons[close_line_poly_idx].size() < 1)
            {
                continue;
            }

            checkIfLineIsBest(close_line_poly_idx, best_line_idx, bestDist, prev_point, incommingPerpundicularNormal);
        }

        if (best_line_idx == -1) /// if single-line-polygon hasn't been found yet
        {
            for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
            {
                if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
                {
                    continue;
                }
                assert(polygons[poly_idx].size() == 2);

                checkIfLineIsBest(poly_idx, best_line_idx, bestDist, prev_point, incommingPerpundicularNormal);

            }
        }

        if (best_line_idx > -1) /// should always be true; we should have been able to identify the best next polygon
        {
            assert(polygons[best_line_idx].size() == 2);

            int endIdx = polyStart[best_line_idx] * -1 + 1; /// 1 -> 0 , 0 -> 1
            prev_point = polygons[best_line_idx][endIdx];
            incommingPerpundicularNormal = turn90CCW(normal(polygons[best_line_idx][endIdx] - polygons[best_line_idx][polyStart[best_line_idx]], 1000));

            picked[best_line_idx] = true;
            polyOrder.push_back(best_line_idx);
        }
        else
        {
            logError("Failed to find next closest line.\n");
        }
    }

    prev_point = startPoint;
    for (int poly_idx : polyOrder)
    {
        PolygonRef poly = polygons[poly_idx];
        int best = -1;
        float bestDist = std::numeric_limits<float>::infinity();
        bool orientation = poly.orientation();
        for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
        {
            float dist = vSize2f(polygons[poly_idx][point_idx] - prev_point);
            Point n0 = normal(poly[(point_idx+poly.size()-1)%poly.size()] - poly[point_idx], 2000);
            Point n1 = normal(poly[point_idx] - poly[(point_idx + 1) % poly.size()], 2000);
            float dot_score = dot(n0, n1) - dot(turn90CCW(n0), n1);
            if (orientation)
                dot_score = -dot_score;
            if (dist + dot_score < bestDist)
            {
                best = point_idx;
                bestDist = dist + dot_score;
            }
        }

        polyStart[poly_idx] = best;
        assert(poly.size() == 2);
        prev_point = poly[best *-1 + 1]; /// 1 -> 0 , 0 -> 1

    }
}

inline void LineOrderOptimizer::checkIfLineIsBest(unsigned int poly_idx, int& best, float& best_dist, Point& prev_point, Point& incomming_perpundicular_normal)
{
    { /// check distance to first point on line (0)
        float dist = vSize2f(polygons[poly_idx][0] - prev_point);
        dist += std::abs(dot(incomming_perpundicular_normal, normal(polygons[poly_idx][1] - polygons[poly_idx][0], 1000))) * 0.0001f; /// penalize sharp corners
        if (dist < best_dist)
        {
            best = poly_idx;
            best_dist = dist;
            polyStart[poly_idx] = 0;
        }
    }
    { /// check distance to second point on line (1)
        float dist = vSize2f(polygons[poly_idx][1] - prev_point);
        dist += std::abs(dot(incomming_perpundicular_normal, normal(polygons[poly_idx][0] - polygons[poly_idx][1], 1000))) * 0.0001f; /// penalize sharp corners
        if (dist < best_dist)
        {
            best = poly_idx;
            best_dist = dist;
            polyStart[poly_idx] = 1;
        }
    }
}

}//namespace cura
<commit_msg>Codestyle: Spaces around binary operators<commit_after>/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "pathOrderOptimizer.h"
#include "utils/logoutput.h"
#include "utils/BucketGrid2D.h"
#include "utils/linearAlg2D.h"

#define INLINE static inline

namespace cura {

/**
*
*/
void PathOrderOptimizer::optimize()
{
    bool picked[polygons.size()];
    memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses

    for (PolygonRef poly : polygons) /// find closest point to initial starting point within each polygon +initialize picked
    {
        int best = -1;
        float bestDist = std::numeric_limits<float>::infinity();
        for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point in polygon
        {
            float dist = vSize2f(poly[point_idx] - startPoint);
            if (dist < bestDist)
            {
                best = point_idx;
                bestDist = dist;
            }
        }
        polyStart.push_back(best);
        //picked.push_back(false); /// initialize all picked values as false

        assert(poly.size() != 2);
    }

    Point prev_point = startPoint;
    for (unsigned int poly_order_idx = 0; poly_order_idx < polygons.size(); poly_order_idx++) /// actual path order optimizer
    {
        int best_poly_idx = -1;
        float bestDist = std::numeric_limits<float>::infinity();

        for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
        {
            if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
            {
                continue;
            }

            assert (polygons[poly_idx].size() != 2);

            float dist = vSize2f(polygons[poly_idx][polyStart[poly_idx]] - prev_point);
            if (dist < bestDist)
            {
                best_poly_idx = poly_idx;
                bestDist = dist;
            }

        }

        if (best_poly_idx > -1) /// should always be true; we should have been able to identify the best next polygon
        {
            assert(polygons[best_poly_idx].size() != 2);

            prev_point = polygons[best_poly_idx][polyStart[best_poly_idx]];

            picked[best_poly_idx] = true;
            polyOrder.push_back(best_poly_idx);
        }
        else
        {
            logError("Failed to find next closest polygon.\n");
        }
    }

    prev_point = startPoint;
    for (unsigned int order_idx = 0; order_idx < polyOrder.size(); order_idx++) /// decide final starting points in each polygon
    {
        int poly_idx = polyOrder[order_idx];
        int point_idx = getPolyStart(prev_point, poly_idx);
        polyStart[poly_idx] = point_idx;
        prev_point = polygons[poly_idx][point_idx];

    }
}

int PathOrderOptimizer::getPolyStart(Point prev_point, int poly_idx)
{
    switch (type)
    {
        case EZSeamType::BACK:      return getFarthestPointInPolygon(poly_idx);
        case EZSeamType::RANDOM:    return getRandomPointInPolygon(poly_idx);
        case EZSeamType::SHORTEST:  return getClosestPointInPolygon(prev_point, poly_idx);
        default:                    return getClosestPointInPolygon(prev_point, poly_idx);
    }
}

int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx)
{
    PolygonRef poly = polygons[poly_idx];

    int best_point_idx = -1;
    float best_point_score = std::numeric_limits<float>::infinity();
    Point p0 = poly.back();
    for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
    {
        Point& p1 = poly[point_idx];
        Point& p2 = poly[(point_idx + 1) % poly.size()];
        int64_t dist = vSize2(p1 - prev_point);
        float is_on_inside_corner_score = -LinearAlg2D::getAngleLeft(p0, p1, p2) / M_PI * 5000 * 5000; // prefer inside corners
        // this score is in the order of 5 mm
        if (dist + is_on_inside_corner_score < best_point_score)
        {
            best_point_idx = point_idx;
            best_point_score = dist + is_on_inside_corner_score;
        }
        p0 = p1;
    }
    return best_point_idx;
}

int PathOrderOptimizer::getRandomPointInPolygon(int poly_idx)
{
    return rand() % polygons[poly_idx].size();
}

int PathOrderOptimizer::getFarthestPointInPolygon(int poly_idx)
{
    PolygonRef poly = polygons[poly_idx];
    int best_point_idx = -1;
    float best_y = std::numeric_limits<float>::min();
    for(unsigned int point_idx=0 ; point_idx<poly.size() ; point_idx++)
    {
        if (poly[point_idx].Y > best_y)
        {
            best_point_idx = point_idx;
            best_y = poly[point_idx].Y;
        }
    }
    return best_point_idx;
}

/**
*
*/
void LineOrderOptimizer::optimize()
{
    int gridSize = 5000; // the size of the cells in the hash grid.
    BucketGrid2D<unsigned int> line_bucket_grid(gridSize);
    bool picked[polygons.size()];
    memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses

    for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) /// find closest point to initial starting point within each polygon +initialize picked
    {
        int best_point_idx = -1;
        float best_point_dist = std::numeric_limits<float>::infinity();
        PolygonRef poly = polygons[poly_idx];
        for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point from polygon
        {
            float dist = vSize2f(poly[point_idx] - startPoint);
            if (dist < best_point_dist)
            {
                best_point_idx = point_idx;
                best_point_dist = dist;
            }
        }
        polyStart.push_back(best_point_idx);

        assert(poly.size() == 2);

        line_bucket_grid.insert(poly[0], poly_idx);
        line_bucket_grid.insert(poly[1], poly_idx);

    }

    Point incommingPerpundicularNormal(0, 0);
    Point prev_point = startPoint;
    for (unsigned int order_idx = 0; order_idx < polygons.size(); order_idx++) /// actual path order optimizer
    {
        int best_line_idx = -1;
        float bestDist = std::numeric_limits<float>::infinity();

        for(unsigned int close_line_poly_idx :  line_bucket_grid.findNearbyObjects(prev_point)) /// check if single-line-polygon is close to last point
        {
            if (picked[close_line_poly_idx] || polygons[close_line_poly_idx].size() < 1)
            {
                continue;
            }

            checkIfLineIsBest(close_line_poly_idx, best_line_idx, bestDist, prev_point, incommingPerpundicularNormal);
        }

        if (best_line_idx == -1) /// if single-line-polygon hasn't been found yet
        {
            for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
            {
                if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
                {
                    continue;
                }
                assert(polygons[poly_idx].size() == 2);

                checkIfLineIsBest(poly_idx, best_line_idx, bestDist, prev_point, incommingPerpundicularNormal);

            }
        }

        if (best_line_idx > -1) /// should always be true; we should have been able to identify the best next polygon
        {
            assert(polygons[best_line_idx].size() == 2);

            int endIdx = polyStart[best_line_idx] * -1 + 1; /// 1 -> 0 , 0 -> 1
            prev_point = polygons[best_line_idx][endIdx];
            incommingPerpundicularNormal = turn90CCW(normal(polygons[best_line_idx][endIdx] - polygons[best_line_idx][polyStart[best_line_idx]], 1000));

            picked[best_line_idx] = true;
            polyOrder.push_back(best_line_idx);
        }
        else
        {
            logError("Failed to find next closest line.\n");
        }
    }

    prev_point = startPoint;
    for (int poly_idx : polyOrder)
    {
        PolygonRef poly = polygons[poly_idx];
        int best = -1;
        float bestDist = std::numeric_limits<float>::infinity();
        bool orientation = poly.orientation();
        for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
        {
            float dist = vSize2f(polygons[poly_idx][point_idx] - prev_point);
            Point n0 = normal(poly[(point_idx + poly.size() - 1) % poly.size()] - poly[point_idx], 2000);
            Point n1 = normal(poly[point_idx] - poly[(point_idx + 1) % poly.size()], 2000);
            float dot_score = dot(n0, n1) - dot(turn90CCW(n0), n1);
            if (orientation)
                dot_score = -dot_score;
            if (dist + dot_score < bestDist)
            {
                best = point_idx;
                bestDist = dist + dot_score;
            }
        }

        polyStart[poly_idx] = best;
        assert(poly.size() == 2);
        prev_point = poly[best *-1 + 1]; /// 1 -> 0 , 0 -> 1

    }
}

inline void LineOrderOptimizer::checkIfLineIsBest(unsigned int poly_idx, int& best, float& best_dist, Point& prev_point, Point& incomming_perpundicular_normal)
{
    { /// check distance to first point on line (0)
        float dist = vSize2f(polygons[poly_idx][0] - prev_point);
        dist += std::abs(dot(incomming_perpundicular_normal, normal(polygons[poly_idx][1] - polygons[poly_idx][0], 1000))) * 0.0001f; /// penalize sharp corners
        if (dist < best_dist)
        {
            best = poly_idx;
            best_dist = dist;
            polyStart[poly_idx] = 0;
        }
    }
    { /// check distance to second point on line (1)
        float dist = vSize2f(polygons[poly_idx][1] - prev_point);
        dist += std::abs(dot(incomming_perpundicular_normal, normal(polygons[poly_idx][0] - polygons[poly_idx][1], 1000))) * 0.0001f; /// penalize sharp corners
        if (dist < best_dist)
        {
            best = poly_idx;
            best_dist = dist;
            polyStart[poly_idx] = 1;
        }
    }
}

}//namespace cura
<|endoftext|>


<commit_before>/** Copyright (C) 2013 Ultimaker - Released under terms of the AGPLv3 License */
#include "pathOrderOptimizer.h"
#include "utils/logoutput.h"
#include "utils/SparsePointGridInclusive.h"
#include "utils/linearAlg2D.h"

#define INLINE static inline

namespace cura {

/**
*
*/
void PathOrderOptimizer::optimize()
{
    bool picked[polygons.size()];
    memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses

    for (unsigned poly_idx = 0; poly_idx < polygons.size(); ++poly_idx) /// find closest point to initial starting point within each polygon +initialize picked
    {
        const ConstPolygonRef poly = *polygons[poly_idx];
        switch (config.type)
        {
            case EZSeamType::USER_SPECIFIED:
                polyStart.push_back(getClosestPointInPolygon(config.pos, poly_idx));
                break;
            case EZSeamType::RANDOM:
                polyStart.push_back(getRandomPointInPolygon(poly_idx));
                break;
            case EZSeamType::SHARPEST_CORNER:
            case EZSeamType::SHORTEST:
            default:
                polyStart.push_back(getClosestPointInPolygon(startPoint, poly_idx));
                break;
        }
        assert(poly.size() != 2);
    }

    Point prev_point;
    switch (config.type)
    {
        case EZSeamType::USER_SPECIFIED:
            prev_point = config.pos;
        case EZSeamType::RANDOM: //TODO: Starting position of the first polygon isn't random.
        case EZSeamType::SHARPEST_CORNER:
        case EZSeamType::SHORTEST:
        default:
            prev_point = startPoint;
    }
    for (unsigned int poly_order_idx = 0; poly_order_idx < polygons.size(); poly_order_idx++) /// actual path order optimizer
    {
        int best_poly_idx = -1;
        float bestDist = std::numeric_limits<float>::infinity();

        for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
        {
            if (picked[poly_idx] || polygons[poly_idx]->size() < 1) /// skip single-point-polygons
            {
                continue;
            }

            assert (polygons[poly_idx]->size() != 2);

            float dist = vSize2f((*polygons[poly_idx])[polyStart[poly_idx]] - prev_point);
            if (dist < bestDist)
            {
                best_poly_idx = poly_idx;
                bestDist = dist;
            }

        }

        if (best_poly_idx > -1) /// should always be true; we should have been able to identify the best next polygon
        {
            assert(polygons[best_poly_idx]->size() != 2);

            prev_point = (*polygons[best_poly_idx])[polyStart[best_poly_idx]];

            picked[best_poly_idx] = true;
            polyOrder.push_back(best_poly_idx);
        }
        else
        {
            logError("Failed to find next closest polygon.\n");
        }
    }
}

int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx)
{
    ConstPolygonRef poly = *polygons[poly_idx];

    int best_point_idx = -1;
    float best_point_score = std::numeric_limits<float>::infinity();
    Point p0 = poly.back();
    for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
    {
        const Point& p1 = poly[point_idx];
        const Point& p2 = poly[(point_idx + 1) % poly.size()];
        // when type is SHARPEST_CORNER, actual distance is ignored, we use a fixed distance and decision is based on curvature only
        int64_t dist_score = (config.type == EZSeamType::SHARPEST_CORNER && config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE)? 100 : vSize2(p1 - prev_point);
        const float corner_angle = LinearAlg2D::getAngleLeft(p0, p1, p2) / M_PI; // 0 -> 2
        int64_t corner_shift;
        if (config.type == EZSeamType::SHORTEST)
        {
            // the more a corner satisfies our criteria, the closer it appears to be
            // shift 10mm for a very acute corner
            corner_shift = 10000 * 10000;
        }
        else
        {
            // the larger the distance from prev_point to p1, the more a corner will "attract" the seam
            // so the user has some control over where the seam will lie.

            // the divisor here may need adjusting to obtain the best results (TBD)
            corner_shift = dist_score / 10;
        }
        switch (config.corner_pref)
        {
            case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_INNER:
                if (corner_angle > 1)
                {
                    // p1 lies on a concave curve so reduce the distance to favour it
                    // the more concave the curve, the more we reduce the distance
                    dist_score -= (corner_angle - 1) * corner_shift;
                }
                break;
            case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_OUTER:
                if (corner_angle < 1)
                {
                    // p1 lies on a convex curve so reduce the distance to favour it
                    // the more convex the curve, the more we reduce the distance
                    dist_score -= (1 - corner_angle) * corner_shift;
                }
                break;
            case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_ANY:
                // the more curved the region, the more we reduce the distance
                dist_score -= fabs(corner_angle - 1) * corner_shift;
                break;
            case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE:
            default:
                // do nothing
                break;
        }
        if (dist_score < best_point_score)
        {
            best_point_idx = point_idx;
            best_point_score = dist_score;
        }
        p0 = p1;
    }
    return best_point_idx;
}

int PathOrderOptimizer::getRandomPointInPolygon(int poly_idx)
{
    return rand() % polygons[poly_idx]->size();
}

/**
*
*/
void LineOrderOptimizer::optimize()
{
    int gridSize = 5000; // the size of the cells in the hash grid. TODO
    SparsePointGridInclusive<unsigned int> line_bucket_grid(gridSize);
    bool picked[polygons.size()];
    memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses

    for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) /// find closest point to initial starting point within each polygon +initialize picked
    {
        int best_point_idx = -1;
        float best_point_dist = std::numeric_limits<float>::infinity();
        ConstPolygonRef poly = *polygons[poly_idx];
        for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point from polygon
        {
            float dist = vSize2f(poly[point_idx] - startPoint);
            if (dist < best_point_dist)
            {
                best_point_idx = point_idx;
                best_point_dist = dist;
            }
        }
        polyStart.push_back(best_point_idx);

        assert(poly.size() == 2);

        line_bucket_grid.insert(poly[0], poly_idx);
        line_bucket_grid.insert(poly[1], poly_idx);

    }

    Point incoming_perpundicular_normal(0, 0);
    Point prev_point = startPoint;
    bool have_chains = true; // start by assuming that line segments are chained together (i.e. zigzags) and set to false later if no chains exist
    for (unsigned int order_idx = 0; order_idx < polygons.size(); order_idx++) /// actual path order optimizer
    {
        int best_line_idx = -1;
        float best_score = std::numeric_limits<float>::infinity(); // distance score for the best next line

        // for the first line we would prefer a line that is at the end of a sequence of connected lines (think zigzag) and
        // so we only consider the closest line when looking for the second line onwards
        if (order_idx > 0)
        {
            /// check if single-line-polygon is close to last point
            for(unsigned int close_line_idx : line_bucket_grid.getNearbyVals(prev_point, gridSize))
            {
                if (picked[close_line_idx] || polygons[close_line_idx]->size() < 1)
                {
                    continue;
                }
                updateBestLine(close_line_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
            }
        }

        // if no line ends close to last_point have been found, see if we can find a point on a line that could be the start of a chain of lines
        if (best_line_idx == -1 && have_chains)
        {
            have_chains = false; // now assume that we don't have any chains and change back to true below if we find any joined line segments
            for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
            {
                if (picked[poly_idx] || polygons[poly_idx]->size() < 1) /// skip single-point-polygons
                {
                    continue;
                }
                assert(polygons[poly_idx]->size() == 2);

                // does this line either end in thin air (doesn't join another line) or exactly join another line that has already been picked?
                // check both of its ends and see if it's a possible candidate to be used to start the next sequence
                for (unsigned point_idx = 0; point_idx < 2; ++point_idx)
                {
                    int num_joined_lines = 0;
                    const Point& p = (*polygons[poly_idx])[point_idx];
                    // look at each of the lines that finish close to this line to see if either of its vertices exactly match this vertex
                    for (unsigned int close_line_idx : line_bucket_grid.getNearbyVals(p, gridSize))
                    {
                        if (close_line_idx != poly_idx && (p == (*polygons[close_line_idx])[0] || p == (*polygons[close_line_idx])[1]))
                        {
                            have_chains = true; // we have found a joint between line segments so we have chains

                            ++num_joined_lines;

                            if (picked[close_line_idx])
                            {
                                // candidate line exactly meets a line that has already been picked so consider this vertex as a start point
                                updateBestLine(poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal, point_idx);
                            }
                        }
                    }
                    if (num_joined_lines == 0)
                    {
                        // candidate line ends in thin air so this vertex could possibly be located at the end of a sequence of lines
                        updateBestLine(poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal, point_idx);
                    }
                }
            }
        }

        // fallback to using the nearest unpicked line
        if (best_line_idx == -1) /// if single-line-polygon hasn't been found yet
        {
            for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
            {
                if (picked[poly_idx] || polygons[poly_idx]->size() < 1) /// skip single-point-polygons
                {
                    continue;
                }
                assert(polygons[poly_idx]->size() == 2);

                updateBestLine(poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);

            }
        }

        if (best_line_idx > -1) /// should always be true; we should have been able to identify the best next polygon
        {
            ConstPolygonRef best_line = *polygons[best_line_idx];
            assert(best_line.size() == 2);

            int line_start_point_idx = polyStart[best_line_idx];
            int line_end_point_idx = line_start_point_idx * -1 + 1; /// 1 -> 0 , 0 -> 1
            const Point& line_start = best_line[line_start_point_idx];
            const Point& line_end = best_line[line_end_point_idx];
            prev_point = line_end;
            incoming_perpundicular_normal = turn90CCW(normal(line_end - line_start, 1000));

            picked[best_line_idx] = true;
            polyOrder.push_back(best_line_idx);
        }
        else
        {
            logError("Failed to find next closest line.\n");
        }
    }
}

inline void LineOrderOptimizer::updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, Point incoming_perpundicular_normal, int just_point)
{
    const Point& p0 = (*polygons[poly_idx])[0];
    const Point& p1 = (*polygons[poly_idx])[1];
    float dot_score = (just_point >= 0) ? 0 : getAngleScore(incoming_perpundicular_normal, p0, p1);
    if (just_point != 1)
    { /// check distance to first point on line (0)
        float score = vSize2f(p0 - prev_point) + dot_score; // prefer 90 degree corners
        if (score < best_score)
        {
            best = poly_idx;
            best_score = score;
            polyStart[poly_idx] = 0;
        }
    }
    if (just_point != 0)
    { /// check distance to second point on line (1)
        float score = vSize2f(p1 - prev_point) + dot_score; // prefer 90 degree corners
        if (score < best_score)
        {
            best = poly_idx;
            best_score = score;
            polyStart[poly_idx] = 1;
        }
    }
}

float LineOrderOptimizer::getAngleScore(Point incoming_perpundicular_normal, Point p0, Point p1)
{
    return dot(incoming_perpundicular_normal, normal(p1 - p0, 1000)) * 0.0001f;
}

}//namespace cura
<commit_msg>Make the test for coincident points less stringent.<commit_after>/** Copyright (C) 2013 Ultimaker - Released under terms of the AGPLv3 License */
#include "pathOrderOptimizer.h"
#include "utils/logoutput.h"
#include "utils/SparsePointGridInclusive.h"
#include "utils/linearAlg2D.h"

#define INLINE static inline

namespace cura {

/**
*
*/
void PathOrderOptimizer::optimize()
{
    bool picked[polygons.size()];
    memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses

    for (unsigned poly_idx = 0; poly_idx < polygons.size(); ++poly_idx) /// find closest point to initial starting point within each polygon +initialize picked
    {
        const ConstPolygonRef poly = *polygons[poly_idx];
        switch (config.type)
        {
            case EZSeamType::USER_SPECIFIED:
                polyStart.push_back(getClosestPointInPolygon(config.pos, poly_idx));
                break;
            case EZSeamType::RANDOM:
                polyStart.push_back(getRandomPointInPolygon(poly_idx));
                break;
            case EZSeamType::SHARPEST_CORNER:
            case EZSeamType::SHORTEST:
            default:
                polyStart.push_back(getClosestPointInPolygon(startPoint, poly_idx));
                break;
        }
        assert(poly.size() != 2);
    }

    Point prev_point;
    switch (config.type)
    {
        case EZSeamType::USER_SPECIFIED:
            prev_point = config.pos;
        case EZSeamType::RANDOM: //TODO: Starting position of the first polygon isn't random.
        case EZSeamType::SHARPEST_CORNER:
        case EZSeamType::SHORTEST:
        default:
            prev_point = startPoint;
    }
    for (unsigned int poly_order_idx = 0; poly_order_idx < polygons.size(); poly_order_idx++) /// actual path order optimizer
    {
        int best_poly_idx = -1;
        float bestDist = std::numeric_limits<float>::infinity();

        for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
        {
            if (picked[poly_idx] || polygons[poly_idx]->size() < 1) /// skip single-point-polygons
            {
                continue;
            }

            assert (polygons[poly_idx]->size() != 2);

            float dist = vSize2f((*polygons[poly_idx])[polyStart[poly_idx]] - prev_point);
            if (dist < bestDist)
            {
                best_poly_idx = poly_idx;
                bestDist = dist;
            }

        }

        if (best_poly_idx > -1) /// should always be true; we should have been able to identify the best next polygon
        {
            assert(polygons[best_poly_idx]->size() != 2);

            prev_point = (*polygons[best_poly_idx])[polyStart[best_poly_idx]];

            picked[best_poly_idx] = true;
            polyOrder.push_back(best_poly_idx);
        }
        else
        {
            logError("Failed to find next closest polygon.\n");
        }
    }
}

int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx)
{
    ConstPolygonRef poly = *polygons[poly_idx];

    int best_point_idx = -1;
    float best_point_score = std::numeric_limits<float>::infinity();
    Point p0 = poly.back();
    for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
    {
        const Point& p1 = poly[point_idx];
        const Point& p2 = poly[(point_idx + 1) % poly.size()];
        // when type is SHARPEST_CORNER, actual distance is ignored, we use a fixed distance and decision is based on curvature only
        int64_t dist_score = (config.type == EZSeamType::SHARPEST_CORNER && config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE)? 100 : vSize2(p1 - prev_point);
        const float corner_angle = LinearAlg2D::getAngleLeft(p0, p1, p2) / M_PI; // 0 -> 2
        int64_t corner_shift;
        if (config.type == EZSeamType::SHORTEST)
        {
            // the more a corner satisfies our criteria, the closer it appears to be
            // shift 10mm for a very acute corner
            corner_shift = 10000 * 10000;
        }
        else
        {
            // the larger the distance from prev_point to p1, the more a corner will "attract" the seam
            // so the user has some control over where the seam will lie.

            // the divisor here may need adjusting to obtain the best results (TBD)
            corner_shift = dist_score / 10;
        }
        switch (config.corner_pref)
        {
            case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_INNER:
                if (corner_angle > 1)
                {
                    // p1 lies on a concave curve so reduce the distance to favour it
                    // the more concave the curve, the more we reduce the distance
                    dist_score -= (corner_angle - 1) * corner_shift;
                }
                break;
            case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_OUTER:
                if (corner_angle < 1)
                {
                    // p1 lies on a convex curve so reduce the distance to favour it
                    // the more convex the curve, the more we reduce the distance
                    dist_score -= (1 - corner_angle) * corner_shift;
                }
                break;
            case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_ANY:
                // the more curved the region, the more we reduce the distance
                dist_score -= fabs(corner_angle - 1) * corner_shift;
                break;
            case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE:
            default:
                // do nothing
                break;
        }
        if (dist_score < best_point_score)
        {
            best_point_idx = point_idx;
            best_point_score = dist_score;
        }
        p0 = p1;
    }
    return best_point_idx;
}

int PathOrderOptimizer::getRandomPointInPolygon(int poly_idx)
{
    return rand() % polygons[poly_idx]->size();
}

static inline bool pointsAreCoincident(const Point& a, const Point& b)
{
    return vSize2(a - b) < 25; // points are closer than 5uM, consider them coincident
}

/**
*
*/
void LineOrderOptimizer::optimize()
{
    int gridSize = 5000; // the size of the cells in the hash grid. TODO
    SparsePointGridInclusive<unsigned int> line_bucket_grid(gridSize);
    bool picked[polygons.size()];
    memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses

    for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) /// find closest point to initial starting point within each polygon +initialize picked
    {
        int best_point_idx = -1;
        float best_point_dist = std::numeric_limits<float>::infinity();
        ConstPolygonRef poly = *polygons[poly_idx];
        for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point from polygon
        {
            float dist = vSize2f(poly[point_idx] - startPoint);
            if (dist < best_point_dist)
            {
                best_point_idx = point_idx;
                best_point_dist = dist;
            }
        }
        polyStart.push_back(best_point_idx);

        assert(poly.size() == 2);

        line_bucket_grid.insert(poly[0], poly_idx);
        line_bucket_grid.insert(poly[1], poly_idx);

    }

    Point incoming_perpundicular_normal(0, 0);
    Point prev_point = startPoint;
    bool have_chains = true; // start by assuming that line segments are chained together (i.e. zigzags) and set to false later if no chains exist
    for (unsigned int order_idx = 0; order_idx < polygons.size(); order_idx++) /// actual path order optimizer
    {
        int best_line_idx = -1;
        float best_score = std::numeric_limits<float>::infinity(); // distance score for the best next line

        // for the first line we would prefer a line that is at the end of a sequence of connected lines (think zigzag) and
        // so we only consider the closest line when looking for the second line onwards
        if (order_idx > 0)
        {
            /// check if single-line-polygon is close to last point
            for(unsigned int close_line_idx : line_bucket_grid.getNearbyVals(prev_point, gridSize))
            {
                if (picked[close_line_idx] || polygons[close_line_idx]->size() < 1)
                {
                    continue;
                }
                updateBestLine(close_line_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
            }
        }

        // if no line ends close to last_point have been found, see if we can find a point on a line that could be the start of a chain of lines
        if (best_line_idx == -1 && have_chains)
        {
            have_chains = false; // now assume that we don't have any chains and change back to true below if we find any joined line segments
            for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
            {
                if (picked[poly_idx] || polygons[poly_idx]->size() < 1) /// skip single-point-polygons
                {
                    continue;
                }
                assert(polygons[poly_idx]->size() == 2);

                // does this line either end in thin air (doesn't join another line) or join another line that has already been picked?
                // check both of its ends and see if it's a possible candidate to be used to start the next sequence
                for (unsigned point_idx = 0; point_idx < 2; ++point_idx)
                {
                    int num_joined_lines = 0;
                    const Point& p = (*polygons[poly_idx])[point_idx];
                    // look at each of the lines that finish close to this line to see if either of its vertices are coincident this vertex
                    for (unsigned int close_line_idx : line_bucket_grid.getNearbyVals(p, gridSize))
                    {
                        if (close_line_idx != poly_idx && (pointsAreCoincident(p, (*polygons[close_line_idx])[0]) || pointsAreCoincident(p, (*polygons[close_line_idx])[1])))
                        {
                            have_chains = true; // we have found a joint between line segments so we have chains

                            ++num_joined_lines;

                            if (picked[close_line_idx])
                            {
                                // candidate line exactly meets a line that has already been picked so consider this vertex as a start point
                                updateBestLine(poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal, point_idx);
                            }
                        }
                    }
                    if (num_joined_lines == 0)
                    {
                        // candidate line ends in thin air so this vertex could possibly be located at the end of a sequence of lines
                        updateBestLine(poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal, point_idx);
                    }
                }
            }
        }

        // fallback to using the nearest unpicked line
        if (best_line_idx == -1) /// if single-line-polygon hasn't been found yet
        {
            for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
            {
                if (picked[poly_idx] || polygons[poly_idx]->size() < 1) /// skip single-point-polygons
                {
                    continue;
                }
                assert(polygons[poly_idx]->size() == 2);

                updateBestLine(poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);

            }
        }

        if (best_line_idx > -1) /// should always be true; we should have been able to identify the best next polygon
        {
            ConstPolygonRef best_line = *polygons[best_line_idx];
            assert(best_line.size() == 2);

            int line_start_point_idx = polyStart[best_line_idx];
            int line_end_point_idx = line_start_point_idx * -1 + 1; /// 1 -> 0 , 0 -> 1
            const Point& line_start = best_line[line_start_point_idx];
            const Point& line_end = best_line[line_end_point_idx];
            prev_point = line_end;
            incoming_perpundicular_normal = turn90CCW(normal(line_end - line_start, 1000));

            picked[best_line_idx] = true;
            polyOrder.push_back(best_line_idx);
        }
        else
        {
            logError("Failed to find next closest line.\n");
        }
    }
}

inline void LineOrderOptimizer::updateBestLine(unsigned int poly_idx, int& best, float& best_score, Point prev_point, Point incoming_perpundicular_normal, int just_point)
{
    const Point& p0 = (*polygons[poly_idx])[0];
    const Point& p1 = (*polygons[poly_idx])[1];
    float dot_score = (just_point >= 0) ? 0 : getAngleScore(incoming_perpundicular_normal, p0, p1);
    if (just_point != 1)
    { /// check distance to first point on line (0)
        float score = vSize2f(p0 - prev_point) + dot_score; // prefer 90 degree corners
        if (score < best_score)
        {
            best = poly_idx;
            best_score = score;
            polyStart[poly_idx] = 0;
        }
    }
    if (just_point != 0)
    { /// check distance to second point on line (1)
        float score = vSize2f(p1 - prev_point) + dot_score; // prefer 90 degree corners
        if (score < best_score)
        {
            best = poly_idx;
            best_score = score;
            polyStart[poly_idx] = 1;
        }
    }
}

float LineOrderOptimizer::getAngleScore(Point incoming_perpundicular_normal, Point p0, Point p1)
{
    return dot(incoming_perpundicular_normal, normal(p1 - p0, 1000)) * 0.0001f;
}

}//namespace cura
<|endoftext|>


<commit_before>#include <catch.hpp>

#include "aux/Aux.hpp"
#include "Chip8.hpp"
#include "CPU.hpp"

namespace {

using namespace Core8;

SCENARIO("CPUs can assign the value of one register to another", "[assign]") {
  GIVEN("A CPU with some initialized registers") {
    Aux::TestKit testKit;
    CPU& cpu = testKit.cpu;
    cpu.writeRegister(Chip8::Register::V0, 0x01);
    cpu.writeRegister(Chip8::Register::VC, 0xCB);
    cpu.writeRegister(Chip8::Register::VF, 0xFF);

    WHEN("the CPU assigns one register to another") {
      cpu.execute(0x8100);
      cpu.execute(0x8DC0);
      cpu.execute(0x8EF0);

      THEN("the target register holds a copy of the source register") {
        REQUIRE(cpu.readRegister(Chip8::Register::V1) == 0x01);
        REQUIRE(cpu.readRegister(Chip8::Register::VD) == 0xCB);
        REQUIRE(cpu.readRegister(Chip8::Register::VE) == 0xFF);
      }
      AND_THEN("the source register remains unchanged") {
        REQUIRE(cpu.readRegister(Chip8::Register::V0) == 0x01);
        REQUIRE(cpu.readRegister(Chip8::Register::VC) == 0xCB);
        REQUIRE(cpu.readRegister(Chip8::Register::VF) == 0xFF);
      }
    }
  }
}

} // unnamed namespace<commit_msg>Refactor assign tests<commit_after>#include <catch.hpp>

#include "aux/Aux.hpp"
#include "Chip8.hpp"
#include "CPU.hpp"

namespace {

struct CpuFixture {
  Aux::TestKit testKit;
  Core8::CPU& cpu = testKit.cpu;
};

SCENARIO_METHOD(
    CpuFixture,
    "CPU assigns register VA to register V0 using op 8XY0", "[assign]"
) {
  GIVEN("A CPU with an initialized register VA") {
    cpu.writeRegister(Core8::Chip8::Register::VA, 0xCC);

    WHEN("the CPU executes a 80A0 opcode") {
      cpu.execute(0x80A0);

      THEN("the value of register VA is copied to register V0") {
        REQUIRE(cpu.readRegister(Core8::Chip8::Register::V0) == 0xCC);
      }
      AND_THEN("the source register VA remains unchanged") {
        REQUIRE(cpu.readRegister(Core8::Chip8::Register::VA) == 0xCC);
      }
    }
  }
}

SCENARIO_METHOD(
    CpuFixture,
    "CPU assigns register VA to register VF using op 8XY0", "[assign]"
) {
  GIVEN("A CPU with an initialized register VA") {
    cpu.writeRegister(Core8::Chip8::Register::VA, 0xDD);

    WHEN("the CPU executes a 8FA0 opcode") {
      cpu.execute(0x8FA0);

      THEN("the value of register VA is copied to register VF") {
        REQUIRE(cpu.readRegister(Core8::Chip8::Register::VF) == 0xDD);
      }
      AND_THEN("the source register VA remains unchanged") {
        REQUIRE(cpu.readRegister(Core8::Chip8::Register::VA) == 0xDD);
      }
    }
  }
}

SCENARIO_METHOD(
    CpuFixture,
    "CPU assigns a register to itself using op 8XY0", "[assign]"
) {
  GIVEN("A CPU with an initialized register V1") {
    cpu.writeRegister(Core8::Chip8::Register::V1, 0x1D);

    WHEN("the CPU executes a 8110 opcode") {
      cpu.execute(0x8000);

      THEN("the source register V0 remains unchanged") {
        REQUIRE(cpu.readRegister(Core8::Chip8::Register::V1) == 0x1D);
      }
    }
  }
}

} // namespace<|endoftext|>


<commit_before>#include "UnitTest++/UnitTest++.h"

#include "StrongAI/AI/CaseBasedAI/CaseBasedAI.hpp"

SUITE( CaseBasedAITest )
{

    TEST( smokeTest )
    {
        CaseBasedAI ai( 0, 0, 0, 0 );

        auto output = ai.output();
        CHECK_EQUAL( 0, output.size() );

        ai.learn( 1 );
    }

    TEST( seeksGoodOutcome )
    {
        CaseBasedAI ai( 1, 1, 1, 2 );

        const std::vector< float > input( { 0 } );
        const std::vector< float > outputToLearn( { 2 } );

        ai.learn( input, outputToLearn, 1 );
        auto actualOutput = ai.output( input );
        CHECK( 2 == actualOutput.at( 0 ) );
    }

    TEST( avoidsBadOutcome )
    {
        CaseBasedAI ai( 1, 1, 1, 2 );

        const std::vector< float > input( { 0 } );
        const std::vector< float > outputToLearn( { 2 } );

        ai.learn( input, outputToLearn, -1 );
        auto actualOutput = ai.output( input );
        CHECK( 2 != actualOutput.at( 0 ) );
    }
}
<commit_msg>CaseBasedAI tested for save and load methods<commit_after>#include "UnitTest++/UnitTest++.h"

#include "StrongAI/AI/CaseBasedAI/CaseBasedAI.hpp"

#include <string>
#include <vector>

SUITE( CaseBasedAITest )
{

    TEST( smokeTest )
    {
        CaseBasedAI ai( 0, 0, 0, 0 );

        auto output = ai.output();
        CHECK_EQUAL( 0, output.size() );

        ai.learn( 1 );
    }

    TEST( seeksGoodOutcome )
    {
        CaseBasedAI ai( 1, 1, 1, 2 );

        const std::vector< float > input( { 0 } );
        const std::vector< float > outputToLearn( { 2 } );

        ai.learn( input, outputToLearn, 1 );
        auto actualOutput = ai.output( input );
        CHECK( 2 == actualOutput.at( 0 ) );
    }

    TEST( avoidsBadOutcome )
    {
        CaseBasedAI ai( 1, 1, 1, 2 );

        const std::vector< float > input( { 0 } );
        const std::vector< float > outputToLearn( { 2 } );

        ai.learn( input, outputToLearn, -1 );
        auto actualOutput = ai.output( input );
        CHECK( 2 != actualOutput.at( 0 ) );
    }

    TEST( saveAndLoad )
    {
        const std::string saveFileName = "caseBasedAI.save";
        const std::vector< float > input( { 0 } );
        const std::vector< float > outputToLearn( { 2 } );

        CaseBasedAI teacherAI ( 1, 1, 2, 2 );
        teacherAI.learn( input, outputToLearn, 1 );
        GeneralAI::save< CaseBasedAI >( teacherAI, saveFileName );

        CaseBasedAI studentAI ( 1, 1, 2, 2 );
        GeneralAI::load< CaseBasedAI >( studentAI, saveFileName );
        auto studentOutput = studentAI.output( input );

        CHECK_EQUAL( outputToLearn.at( 0 ), studentOutput.at( 0 ) );

        // Delete save file
        CHECK( !remove( saveFileName.data() ) ) ;
    }
}
<|endoftext|>


<commit_before>#include <Bull/Network/Address/SockAddrBuffer.hpp>
#include <Bull/Network/Socket/UdpSocketImpl.hpp>

namespace Bull
{
    namespace prv
    {
        UdpSocketImpl::UdpSocketImpl(const std::unique_ptr<SocketImpl>& socket) :
            m_socket(socket)
        {
            /// Nothing
        }

        bool UdpSocketImpl::bind(NetPort port, const IpAddressWrapper& address)
        {
            if(m_socket->isValid())
            {
                SockAddrBuffer buffer(address, port);

                return ::bind(m_socket->getHandler(), buffer.getSockAddr(), buffer.getLength()) == 0;
            }

            return false;
        }

        bool UdpSocketImpl::sendTo(const IpAddressWrapper& address, NetPort port, const void* data, std::size_t length, std::size_t& sent) const
        {
            if(m_socket->isValid())
            {
                SockAddrBuffer buffer(address, port);

                return ::sendto(m_socket->getHandler(), reinterpret_cast<const char*>(data), length, 0, buffer.getSockAddr(), buffer.getLength()) == 0;
            }

            sent = 0;

            return false;
        }

        bool UdpSocketImpl::receiveFrom(IpAddressWrapper& address, NetPort& port, void* data, std::size_t length, std::size_t& received) const
        {
            if(m_socket->isValid())
            {
                sockaddr addr;
                int length = sizeof(sockaddr);

                received = ::recvfrom(m_socket->getHandler(), reinterpret_cast<char*>(data), length, 0, &addr, &length);

                if(received >= 0)
                {
                    SockAddrBuffer buffer(addr, length);

                    port    = buffer.getPort();
                    address = buffer.getIpAddress();

                    return true;
                }
            }

            received = 0;

            return false;
        }
    }
}<commit_msg>[Network/UdpSocket] Fix variable name conflict<commit_after>#include <Bull/Network/Address/SockAddrBuffer.hpp>
#include <Bull/Network/Socket/UdpSocketImpl.hpp>

namespace Bull
{
    namespace prv
    {
        UdpSocketImpl::UdpSocketImpl(const std::unique_ptr<SocketImpl>& socket) :
            m_socket(socket)
        {
            /// Nothing
        }

        bool UdpSocketImpl::bind(NetPort port, const IpAddressWrapper& address)
        {
            if(m_socket->isValid())
            {
                SockAddrBuffer buffer(address, port);

                return ::bind(m_socket->getHandler(), buffer.getSockAddr(), buffer.getLength()) == 0;
            }

            return false;
        }

        bool UdpSocketImpl::sendTo(const IpAddressWrapper& address, NetPort port, const void* data, std::size_t length, std::size_t& sent) const
        {
            if(m_socket->isValid())
            {
                SockAddrBuffer buffer(address, port);

                return ::sendto(m_socket->getHandler(), reinterpret_cast<const char*>(data), length, 0, buffer.getSockAddr(), buffer.getLength()) == 0;
            }

            sent = 0;

            return false;
        }

        bool UdpSocketImpl::receiveFrom(IpAddressWrapper& address, NetPort& port, void* data, std::size_t length, std::size_t& received) const
        {
            if(m_socket->isValid())
            {
                sockaddr addr;
                int sockLength = sizeof(sockaddr);

                received = ::recvfrom(m_socket->getHandler(), reinterpret_cast<char*>(data), length, 0, &addr, &sockLength);

                if(received >= 0)
                {
                    SockAddrBuffer buffer(addr, sockLength);

                    port    = buffer.getPort();
                    address = buffer.getIpAddress();

                    return true;
                }
            }

            received = 0;

            return false;
        }
    }
}<|endoftext|>