<commit_before>
// Qt includes
#include <QHash>
#include <QStringList>
#include <QTextStream>
#include <QDebug>

// CTK includes
#include "ctkCommandLineParser.h"

namespace
{

class CommandLineParserArgumentDescriptionBase
{
public:
  CommandLineParserArgumentDescriptionBase(
    const char* longArg, const char* shortArg, const QString& argHelp, bool ignoreRest)
    {
    this->LongArg = QLatin1String(longArg);
    this->ShortArg = QLatin1String(shortArg);
    this->ArgHelp = argHelp;
    this->IgnoreRest = ignoreRest;
    this->NumberOfParametersToProcess = 0;
    }
  virtual bool addParameter(const QString& value) = 0;
  QString helpText(int fieldWidth, const char charPad);
  QString LongArg;
  QString ShortArg;
  QString ArgHelp;
  bool    IgnoreRest;
  int     NumberOfParametersToProcess;
  QString RegularExpression;
  QString ExactMatchFailedMessage;
  QString ArgumentType;
};

// --------------------------------------------------------------------------
QString CommandLineParserArgumentDescriptionBase::helpText(int fieldWidth, const char charPad)
{
  QString text;
  QTextStream stream(&text);
  stream.setFieldAlignment(QTextStream::AlignLeft);
  stream.setPadChar(charPad);

  if (this->LongArg.isEmpty() && !this->ArgHelp.isEmpty())
    {
    stream.setFieldWidth(fieldWidth);
    }

  if (!this->ShortArg.isEmpty())
    {
    stream  << QString("  %1").arg(this->ShortArg);
    if(!this->LongArg.isEmpty())
      {
      stream << "\n";
      }
    }

  if (!this->LongArg.isEmpty())
    {
    if(!this->ArgHelp.isEmpty())
      {
      stream.setFieldWidth(fieldWidth);
      }
    stream  << QString("  %1").arg(this->LongArg);
    }
  stream.setFieldWidth(0);
  stream << this->ArgHelp << "\n";
  return text;
}

#define CommandLineParserArgumentDescription_class(_NAME, _TYPE,                        \
                                                   _NUMBEROFPARAMTOPROCESS,             \
                                                   _REGEXP, _EXACTMACTHERRORMSG)        \
  class CommandLineParser##_NAME##ArgumentDescription:                                  \
    public CommandLineParserArgumentDescriptionBase                                     \
  {                                                                                \
  public:                                                                               \
    CommandLineParser##_NAME##ArgumentDescription(                                      \
      const char* longArg, const char* shortArg, _TYPE * variable,                      \
      const QString& argHelp, const _TYPE& defaultValue,                                \
      bool ignoreRest):                                                                 \
      CommandLineParserArgumentDescriptionBase(longArg , shortArg, argHelp, ignoreRest) \
      {                                                                                \
      this->Variable = variable;                                                        \
      this->DefaultValue = defaultValue;                                                \
      this->NumberOfParametersToProcess = _NUMBEROFPARAMTOPROCESS;                      \
      this->RegularExpression = _REGEXP;                                                \
      this->ArgumentType = #_TYPE;                                                      \
      }                                                                                \
     virtual bool addParameter(const QString& value);                                   \
    _TYPE* Variable;                                                                    \
    _TYPE DefaultValue;                                                                 \
  };

CommandLineParserArgumentDescription_class(String, QString, 1, ".*", "");
CommandLineParserArgumentDescription_class(Boolean, bool, 0, "", "");
CommandLineParserArgumentDescription_class(StringList, QStringList, -1, ".*", "");
CommandLineParserArgumentDescription_class(Integer, int, 1, "-?[0-9]+",
                                           "A negative or positive integer is expected.");

#undef CommandLineParserArgumentDescription_class

// --------------------------------------------------------------------------
bool CommandLineParserStringArgumentDescription::addParameter(const QString& value)
{
  // Validate value
  QRegExp regexp(this->RegularExpression);
  if (!regexp.exactMatch(value))
    {
    return false;
    }
  (*this->Variable).clear();
  (*this->Variable).append(value);
  return true;
}

// --------------------------------------------------------------------------
bool CommandLineParserBooleanArgumentDescription::addParameter(const QString& value)
{
  *this->Variable = (value == "true");
  return true;
}

// --------------------------------------------------------------------------
bool CommandLineParserStringListArgumentDescription::addParameter(const QString& value)
{
  // Validate value
  QRegExp regexp(this->RegularExpression);
  if (!regexp.exactMatch(value))
    {
    return false;
    }
  *this->Variable << value;
  return true;
}

// --------------------------------------------------------------------------
bool CommandLineParserIntegerArgumentDescription::addParameter(const QString& value)
{
  // Validate value
  QRegExp regexp(this->RegularExpression);
  if (!regexp.exactMatch(value))
    {
    return false;
    }
  *this->Variable = value.toInt();
  return true;
}

}

// --------------------------------------------------------------------------
// ctkCommandLineParser::ctkInternal class

// --------------------------------------------------------------------------
class ctkCommandLineParser::ctkInternal
{
public:
  ctkInternal():Debug(false),FieldWidth(0){}

  CommandLineParserArgumentDescriptionBase* argumentDescription(const QString& argument);

  QList<CommandLineParserArgumentDescriptionBase*>          ArgumentDescriptionList;
  QHash<QString, CommandLineParserArgumentDescriptionBase*> ArgNameToArgumentDescriptionMap;

  #define ctkCommandLineParser_ctkInternal_declare_map(_NAME) \
    QHash<QString, CommandLineParser##_NAME##ArgumentDescription*>       \
      LongArgTo##_NAME##ArgumentDescriptionMap;                          \
    QHash<QString, CommandLineParser##_NAME##ArgumentDescription*>       \
      ShortArgTo##_NAME##ArgumentDescriptionMap;

  ctkCommandLineParser_ctkInternal_declare_map(String);
  ctkCommandLineParser_ctkInternal_declare_map(Boolean);
  ctkCommandLineParser_ctkInternal_declare_map(StringList);
  ctkCommandLineParser_ctkInternal_declare_map(Integer);

  #undef ctkCommandLineParser_ctkInternal_declare_map

  QStringList UnparsedArguments;
  QStringList ProcessedArguments;
  QString     ErrorString;
  bool        Debug;
  int         FieldWidth;
};

// --------------------------------------------------------------------------
// ctkCommandLineParser::ctkInternal methods

// --------------------------------------------------------------------------
CommandLineParserArgumentDescriptionBase*
  ctkCommandLineParser::ctkInternal::argumentDescription(const QString& argument)
{
  if (this->ArgNameToArgumentDescriptionMap.contains(argument))
    {
    return this->ArgNameToArgumentDescriptionMap[argument];
    }
  return 0;
}

// --------------------------------------------------------------------------
// ctkCommandLineParser methods

// --------------------------------------------------------------------------
ctkCommandLineParser::ctkCommandLineParser()
{
  this->Internal = new ctkInternal();
}

// --------------------------------------------------------------------------
ctkCommandLineParser::~ctkCommandLineParser()
{
  delete this->Internal;
}

// --------------------------------------------------------------------------
bool ctkCommandLineParser::parseArguments(const QStringList& arguments)
{
  // Reset
  this->Internal->UnparsedArguments.clear();
  this->Internal->ProcessedArguments.clear();
  this->Internal->ErrorString.clear();

  bool ignoreRest = false;
  CommandLineParserArgumentDescriptionBase * currentArgDesc = 0;
  for(int i = 1; i < arguments.size(); ++i)
    {

    QString argument = arguments.at(i);
    if (this->Internal->Debug) { qDebug() << "Processing" << argument; }

    // should argument be ignored ?
    if (ignoreRest)
      {
      this->Internal->UnparsedArguments << argument;
      continue;
      }

    // Skip if argument has already been parsed ...
    if (this->Internal->ProcessedArguments.contains(argument))
      {
      qDebug() << "Skipping argument" << argument << " - Already processed !";
      continue;
      }

    // Retrieve corresponding argument description
    currentArgDesc = this->Internal->argumentDescription(argument);

    // Is there a corresponding argument description ?
    if (currentArgDesc)
      {
      this->Internal->ProcessedArguments << currentArgDesc->ShortArg << currentArgDesc->LongArg;
      int numberOfParametersToProcess = currentArgDesc->NumberOfParametersToProcess;
      ignoreRest = currentArgDesc->IgnoreRest;

      // Is the number of parameters associated with the argument being processed known ?
      if (numberOfParametersToProcess == 0)
        {
        currentArgDesc->addParameter("true");
        }
      else if (numberOfParametersToProcess > 0)
        {
        QString missingParameterError =
            "Argument %1 has %2 value(s) associated whereas exacly %3 are expected.";
        for(int j=1; j <= numberOfParametersToProcess; ++j)
          {
          if (i + j >= arguments.size())
            {
            this->Internal->ErrorString =
                missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess);
            if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
            return false;
            }
          QString parameter = arguments.at(i + j);
          if (this->Internal->Debug)
            {
            qDebug() << "Processing parameter" << j << ", value:" << parameter;
            }
          if (this->argumentAdded(parameter))
            {
            this->Internal->ErrorString =
                missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess);
            if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
            return false;
            }
          if (!currentArgDesc->addParameter(parameter))
            {
            this->Internal->ErrorString = QString(
                "Value(s) associated with argument %1 are incorrect. %2").
                arg(argument).arg(currentArgDesc->ExactMatchFailedMessage);

            if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
            return false;
            }
          }
        // Update main loop increment
        i = i + numberOfParametersToProcess;
        }
      else if (numberOfParametersToProcess == -1)
        {
        if (this->Internal->Debug)
          {
          qDebug() << "Proccessing StringList ...";
          }
        int j = 1;
        while(j + i < arguments.size())
          {
          if (this->argumentAdded(arguments.at(j + i)))
            {
            if (this->Internal->Debug)
              {
              qDebug() << "No more parameter for" << argument;
              }
            break;
            }
          QString parameter = arguments.at(j + i);
          if (this->Internal->Debug)
            {
            qDebug() << "Processing parameter" << j << ", value:" << parameter;
            }
          if (!currentArgDesc->addParameter(parameter))
            {
            this->Internal->ErrorString = QString(
                "Value(s) associated with argument %1 are incorrect. %2").
                arg(argument).arg(currentArgDesc->ExactMatchFailedMessage);

            if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
            return false;
            }
          j++;
          }
        // Update main loop increment
        i = i + j;
        }
      }
    else
      {
      this->Internal->UnparsedArguments << argument;
      }
    }
  return true;
}

// -------------------------------------------------------------------------
QString ctkCommandLineParser::errorString()
{
  return this->Internal->ErrorString;
}

// -------------------------------------------------------------------------
const QStringList& ctkCommandLineParser::unparsedArguments()
{
  return this->Internal->UnparsedArguments;
}

// -------------------------------------------------------------------------
#define ctkCommandLineParser_addArgument_cxx_core(_NAME, _TYPE)                 \
  /* Make sure it's not already added */                                                   \
  bool added = this->Internal->LongArgTo##_NAME##ArgumentDescriptionMap.contains(longarg); \
  Q_ASSERT(!added);                                                                        \
  if (added) { return; }                                                                   \
                                                                                \
  added = this->Internal->ShortArgTo##_NAME##ArgumentDescriptionMap.contains(shortarg);    \
  Q_ASSERT(!added);                                                                        \
  if (added) { return; }                                                                   \
                                                                                \
  CommandLineParser##_NAME##ArgumentDescription * argDesc =                                \
    new CommandLineParser##_NAME##ArgumentDescription(longarg, shortarg, var,              \
                                                      argHelp, defaultValue, ignoreRest);  \
                                                                                \
  Q_ASSERT(!(longarg == 0 && shortarg == 0));                                              \
  if (longarg == 0 && shortarg == 0) { return; }                                           \
  if (longarg != 0)                                                                        \
    {                                                                                \
    this->Internal->LongArgTo##_NAME##ArgumentDescriptionMap[longarg] = argDesc;           \
    int argWidth = QString(longarg).length() + 7;                                          \
    if (argWidth > this->Internal->FieldWidth)                                             \
      {                                                                                \
      this->Internal->FieldWidth = argWidth;                                               \
      }                                                                                \
    }                                                                                \
  if (shortarg != 0)                                                                       \
    {                                                                                \
    this->Internal->ShortArgTo##_NAME##ArgumentDescriptionMap[shortarg] = argDesc;         \
    int argWidth = QString(shortarg).length() + 7;                                         \
    if (argWidth > this->Internal->FieldWidth)                                             \
      {                                                                                \
      this->Internal->FieldWidth = argWidth;                                               \
      }                                                                                \
    }                                                                                \
  this->Internal->ArgNameToArgumentDescriptionMap[longarg] = argDesc;                      \
  this->Internal->ArgNameToArgumentDescriptionMap[shortarg] = argDesc;                     \
  this->Internal->ArgumentDescriptionList << argDesc;

  // -------------------------------------------------------------------------
  #define ctkCommandLineParser_addArgument_cxx(_NAME, _TYPE)                        \
    void ctkCommandLineParser::add##_NAME##Argument(const char* longarg,            \
      const char* shortarg, _TYPE* var, const QString& argHelp, const _TYPE& defaultValue,     \
      bool ignoreRest)                                                                         \
    {                                                                                \
    ctkCommandLineParser_addArgument_cxx_core(_NAME, _TYPE);                        \
    }

  // -------------------------------------------------------------------------
  #define ctkCommandLineParser_addArgument_cxx_without_ignore_rest(_NAME, _TYPE)    \
    void ctkCommandLineParser::add##_NAME##Argument(const char* longarg,            \
      const char* shortarg, _TYPE* var, const QString& argHelp, const _TYPE& defaultValue)     \
    {                                                                                \
    bool ignoreRest = false;                                                                   \
    ctkCommandLineParser_addArgument_cxx_core(_NAME, _TYPE);                        \
    }

// --------------------------------------------------------------------------
ctkCommandLineParser_addArgument_cxx(String, QString);
ctkCommandLineParser_addArgument_cxx(Boolean, bool);
ctkCommandLineParser_addArgument_cxx_without_ignore_rest(StringList, QStringList);
ctkCommandLineParser_addArgument_cxx(Integer, int);

#undef ctkCommandLineParser_addArgument_cxx

// --------------------------------------------------------------------------
bool ctkCommandLineParser::setExactMatchRegularExpression(
    const QString& argument, const QString& expression, const QString& exactMatchFailedMessage)
{
  CommandLineParserArgumentDescriptionBase * argDesc =
      this->Internal->argumentDescription(argument);
  if (!argDesc)
    {
    return false;
    }

  if (argDesc->ArgumentType == "bool")
    {
    return false;
    }
  argDesc->RegularExpression = expression;
  argDesc->ExactMatchFailedMessage = exactMatchFailedMessage;
  return true;
}

// --------------------------------------------------------------------------
QString ctkCommandLineParser::helpText(const char charPad)
{
  QString text;
  QTextStream stream(&text);

  // Loop over argument descriptions
  foreach(CommandLineParserArgumentDescriptionBase* argDesc,
          this->Internal->ArgumentDescriptionList)
    {
    stream << argDesc->helpText(this->Internal->FieldWidth, charPad);
    }
  return text;
}

// --------------------------------------------------------------------------
bool ctkCommandLineParser::argumentAdded(const QString& argument)
{
  return this->Internal->ArgNameToArgumentDescriptionMap.contains(argument);
}

// --------------------------------------------------------------------------
bool ctkCommandLineParser::argumentParsed(const QString& argument)
{
  return this->Internal->ProcessedArguments.contains(argument);
}

<commit_msg>COMP: Added virtual destructor to CommandLineParserArgumentDescriptionBase class<commit_after>
// Qt includes
#include <QHash>
#include <QStringList>
#include <QTextStream>
#include <QDebug>

// CTK includes
#include "ctkCommandLineParser.h"

namespace
{

class CommandLineParserArgumentDescriptionBase
{
public:
  CommandLineParserArgumentDescriptionBase(
    const char* longArg, const char* shortArg, const QString& argHelp, bool ignoreRest)
    {
    this->LongArg = QLatin1String(longArg);
    this->ShortArg = QLatin1String(shortArg);
    this->ArgHelp = argHelp;
    this->IgnoreRest = ignoreRest;
    this->NumberOfParametersToProcess = 0;
    }
  virtual ~CommandLineParserArgumentDescriptionBase(){}
  virtual bool addParameter(const QString& value) = 0;
  QString helpText(int fieldWidth, const char charPad);
  QString LongArg;
  QString ShortArg;
  QString ArgHelp;
  bool    IgnoreRest;
  int     NumberOfParametersToProcess;
  QString RegularExpression;
  QString ExactMatchFailedMessage;
  QString ArgumentType;
};

// --------------------------------------------------------------------------
QString CommandLineParserArgumentDescriptionBase::helpText(int fieldWidth, const char charPad)
{
  QString text;
  QTextStream stream(&text);
  stream.setFieldAlignment(QTextStream::AlignLeft);
  stream.setPadChar(charPad);

  if (this->LongArg.isEmpty() && !this->ArgHelp.isEmpty())
    {
    stream.setFieldWidth(fieldWidth);
    }

  if (!this->ShortArg.isEmpty())
    {
    stream  << QString("  %1").arg(this->ShortArg);
    if(!this->LongArg.isEmpty())
      {
      stream << "\n";
      }
    }

  if (!this->LongArg.isEmpty())
    {
    if(!this->ArgHelp.isEmpty())
      {
      stream.setFieldWidth(fieldWidth);
      }
    stream  << QString("  %1").arg(this->LongArg);
    }
  stream.setFieldWidth(0);
  stream << this->ArgHelp << "\n";
  return text;
}

#define CommandLineParserArgumentDescription_class(_NAME, _TYPE,                        \
                                                   _NUMBEROFPARAMTOPROCESS,             \
                                                   _REGEXP, _EXACTMACTHERRORMSG)        \
  class CommandLineParser##_NAME##ArgumentDescription:                                  \
    public CommandLineParserArgumentDescriptionBase                                     \
  {                                                                                \
  public:                                                                               \
    CommandLineParser##_NAME##ArgumentDescription(                                      \
      const char* longArg, const char* shortArg, _TYPE * variable,                      \
      const QString& argHelp, const _TYPE& defaultValue,                                \
      bool ignoreRest):                                                                 \
      CommandLineParserArgumentDescriptionBase(longArg , shortArg, argHelp, ignoreRest) \
      {                                                                                \
      this->Variable = variable;                                                        \
      this->DefaultValue = defaultValue;                                                \
      this->NumberOfParametersToProcess = _NUMBEROFPARAMTOPROCESS;                      \
      this->RegularExpression = _REGEXP;                                                \
      this->ArgumentType = #_TYPE;                                                      \
      }                                                                                \
    virtual ~ CommandLineParser##_NAME##ArgumentDescription(){}                         \
     virtual bool addParameter(const QString& value);                                   \
    _TYPE* Variable;                                                                    \
    _TYPE DefaultValue;                                                                 \
  };

CommandLineParserArgumentDescription_class(String, QString, 1, ".*", "");
CommandLineParserArgumentDescription_class(Boolean, bool, 0, "", "");
CommandLineParserArgumentDescription_class(StringList, QStringList, -1, ".*", "");
CommandLineParserArgumentDescription_class(Integer, int, 1, "-?[0-9]+",
                                           "A negative or positive integer is expected.");

#undef CommandLineParserArgumentDescription_class

// --------------------------------------------------------------------------
bool CommandLineParserStringArgumentDescription::addParameter(const QString& value)
{
  // Validate value
  QRegExp regexp(this->RegularExpression);
  if (!regexp.exactMatch(value))
    {
    return false;
    }
  (*this->Variable).clear();
  (*this->Variable).append(value);
  return true;
}

// --------------------------------------------------------------------------
bool CommandLineParserBooleanArgumentDescription::addParameter(const QString& value)
{
  *this->Variable = (value == "true");
  return true;
}

// --------------------------------------------------------------------------
bool CommandLineParserStringListArgumentDescription::addParameter(const QString& value)
{
  // Validate value
  QRegExp regexp(this->RegularExpression);
  if (!regexp.exactMatch(value))
    {
    return false;
    }
  *this->Variable << value;
  return true;
}

// --------------------------------------------------------------------------
bool CommandLineParserIntegerArgumentDescription::addParameter(const QString& value)
{
  // Validate value
  QRegExp regexp(this->RegularExpression);
  if (!regexp.exactMatch(value))
    {
    return false;
    }
  *this->Variable = value.toInt();
  return true;
}

}

// --------------------------------------------------------------------------
// ctkCommandLineParser::ctkInternal class

// --------------------------------------------------------------------------
class ctkCommandLineParser::ctkInternal
{
public:
  ctkInternal():Debug(false),FieldWidth(0){}

  CommandLineParserArgumentDescriptionBase* argumentDescription(const QString& argument);

  QList<CommandLineParserArgumentDescriptionBase*>          ArgumentDescriptionList;
  QHash<QString, CommandLineParserArgumentDescriptionBase*> ArgNameToArgumentDescriptionMap;

  #define ctkCommandLineParser_ctkInternal_declare_map(_NAME) \
    QHash<QString, CommandLineParser##_NAME##ArgumentDescription*>       \
      LongArgTo##_NAME##ArgumentDescriptionMap;                          \
    QHash<QString, CommandLineParser##_NAME##ArgumentDescription*>       \
      ShortArgTo##_NAME##ArgumentDescriptionMap;

  ctkCommandLineParser_ctkInternal_declare_map(String);
  ctkCommandLineParser_ctkInternal_declare_map(Boolean);
  ctkCommandLineParser_ctkInternal_declare_map(StringList);
  ctkCommandLineParser_ctkInternal_declare_map(Integer);

  #undef ctkCommandLineParser_ctkInternal_declare_map

  QStringList UnparsedArguments;
  QStringList ProcessedArguments;
  QString     ErrorString;
  bool        Debug;
  int         FieldWidth;
};

// --------------------------------------------------------------------------
// ctkCommandLineParser::ctkInternal methods

// --------------------------------------------------------------------------
CommandLineParserArgumentDescriptionBase*
  ctkCommandLineParser::ctkInternal::argumentDescription(const QString& argument)
{
  if (this->ArgNameToArgumentDescriptionMap.contains(argument))
    {
    return this->ArgNameToArgumentDescriptionMap[argument];
    }
  return 0;
}

// --------------------------------------------------------------------------
// ctkCommandLineParser methods

// --------------------------------------------------------------------------
ctkCommandLineParser::ctkCommandLineParser()
{
  this->Internal = new ctkInternal();
}

// --------------------------------------------------------------------------
ctkCommandLineParser::~ctkCommandLineParser()
{
  delete this->Internal;
}

// --------------------------------------------------------------------------
bool ctkCommandLineParser::parseArguments(const QStringList& arguments)
{
  // Reset
  this->Internal->UnparsedArguments.clear();
  this->Internal->ProcessedArguments.clear();
  this->Internal->ErrorString.clear();

  bool ignoreRest = false;
  CommandLineParserArgumentDescriptionBase * currentArgDesc = 0;
  for(int i = 1; i < arguments.size(); ++i)
    {

    QString argument = arguments.at(i);
    if (this->Internal->Debug) { qDebug() << "Processing" << argument; }

    // should argument be ignored ?
    if (ignoreRest)
      {
      this->Internal->UnparsedArguments << argument;
      continue;
      }

    // Skip if argument has already been parsed ...
    if (this->Internal->ProcessedArguments.contains(argument))
      {
      qDebug() << "Skipping argument" << argument << " - Already processed !";
      continue;
      }

    // Retrieve corresponding argument description
    currentArgDesc = this->Internal->argumentDescription(argument);

    // Is there a corresponding argument description ?
    if (currentArgDesc)
      {
      this->Internal->ProcessedArguments << currentArgDesc->ShortArg << currentArgDesc->LongArg;
      int numberOfParametersToProcess = currentArgDesc->NumberOfParametersToProcess;
      ignoreRest = currentArgDesc->IgnoreRest;

      // Is the number of parameters associated with the argument being processed known ?
      if (numberOfParametersToProcess == 0)
        {
        currentArgDesc->addParameter("true");
        }
      else if (numberOfParametersToProcess > 0)
        {
        QString missingParameterError =
            "Argument %1 has %2 value(s) associated whereas exacly %3 are expected.";
        for(int j=1; j <= numberOfParametersToProcess; ++j)
          {
          if (i + j >= arguments.size())
            {
            this->Internal->ErrorString =
                missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess);
            if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
            return false;
            }
          QString parameter = arguments.at(i + j);
          if (this->Internal->Debug)
            {
            qDebug() << "Processing parameter" << j << ", value:" << parameter;
            }
          if (this->argumentAdded(parameter))
            {
            this->Internal->ErrorString =
                missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess);
            if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
            return false;
            }
          if (!currentArgDesc->addParameter(parameter))
            {
            this->Internal->ErrorString = QString(
                "Value(s) associated with argument %1 are incorrect. %2").
                arg(argument).arg(currentArgDesc->ExactMatchFailedMessage);

            if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
            return false;
            }
          }
        // Update main loop increment
        i = i + numberOfParametersToProcess;
        }
      else if (numberOfParametersToProcess == -1)
        {
        if (this->Internal->Debug)
          {
          qDebug() << "Proccessing StringList ...";
          }
        int j = 1;
        while(j + i < arguments.size())
          {
          if (this->argumentAdded(arguments.at(j + i)))
            {
            if (this->Internal->Debug)
              {
              qDebug() << "No more parameter for" << argument;
              }
            break;
            }
          QString parameter = arguments.at(j + i);
          if (this->Internal->Debug)
            {
            qDebug() << "Processing parameter" << j << ", value:" << parameter;
            }
          if (!currentArgDesc->addParameter(parameter))
            {
            this->Internal->ErrorString = QString(
                "Value(s) associated with argument %1 are incorrect. %2").
                arg(argument).arg(currentArgDesc->ExactMatchFailedMessage);

            if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
            return false;
            }
          j++;
          }
        // Update main loop increment
        i = i + j;
        }
      }
    else
      {
      this->Internal->UnparsedArguments << argument;
      }
    }
  return true;
}

// -------------------------------------------------------------------------
QString ctkCommandLineParser::errorString()
{
  return this->Internal->ErrorString;
}

// -------------------------------------------------------------------------
const QStringList& ctkCommandLineParser::unparsedArguments()
{
  return this->Internal->UnparsedArguments;
}

// -------------------------------------------------------------------------
#define ctkCommandLineParser_addArgument_cxx_core(_NAME, _TYPE)                 \
  /* Make sure it's not already added */                                                   \
  bool added = this->Internal->LongArgTo##_NAME##ArgumentDescriptionMap.contains(longarg); \
  Q_ASSERT(!added);                                                                        \
  if (added) { return; }                                                                   \
                                                                                \
  added = this->Internal->ShortArgTo##_NAME##ArgumentDescriptionMap.contains(shortarg);    \
  Q_ASSERT(!added);                                                                        \
  if (added) { return; }                                                                   \
                                                                                \
  CommandLineParser##_NAME##ArgumentDescription * argDesc =                                \
    new CommandLineParser##_NAME##ArgumentDescription(longarg, shortarg, var,              \
                                                      argHelp, defaultValue, ignoreRest);  \
                                                                                \
  Q_ASSERT(!(longarg == 0 && shortarg == 0));                                              \
  if (longarg == 0 && shortarg == 0) { return; }                                           \
  if (longarg != 0)                                                                        \
    {                                                                                \
    this->Internal->LongArgTo##_NAME##ArgumentDescriptionMap[longarg] = argDesc;           \
    int argWidth = QString(longarg).length() + 7;                                          \
    if (argWidth > this->Internal->FieldWidth)                                             \
      {                                                                                \
      this->Internal->FieldWidth = argWidth;                                               \
      }                                                                                \
    }                                                                                \
  if (shortarg != 0)                                                                       \
    {                                                                                \
    this->Internal->ShortArgTo##_NAME##ArgumentDescriptionMap[shortarg] = argDesc;         \
    int argWidth = QString(shortarg).length() + 7;                                         \
    if (argWidth > this->Internal->FieldWidth)                                             \
      {                                                                                \
      this->Internal->FieldWidth = argWidth;                                               \
      }                                                                                \
    }                                                                                \
  this->Internal->ArgNameToArgumentDescriptionMap[longarg] = argDesc;                      \
  this->Internal->ArgNameToArgumentDescriptionMap[shortarg] = argDesc;                     \
  this->Internal->ArgumentDescriptionList << argDesc;

  // -------------------------------------------------------------------------
  #define ctkCommandLineParser_addArgument_cxx(_NAME, _TYPE)                        \
    void ctkCommandLineParser::add##_NAME##Argument(const char* longarg,            \
      const char* shortarg, _TYPE* var, const QString& argHelp, const _TYPE& defaultValue,     \
      bool ignoreRest)                                                                         \
    {                                                                                \
    ctkCommandLineParser_addArgument_cxx_core(_NAME, _TYPE);                        \
    }

  // -------------------------------------------------------------------------
  #define ctkCommandLineParser_addArgument_cxx_without_ignore_rest(_NAME, _TYPE)    \
    void ctkCommandLineParser::add##_NAME##Argument(const char* longarg,            \
      const char* shortarg, _TYPE* var, const QString& argHelp, const _TYPE& defaultValue)     \
    {                                                                                \
    bool ignoreRest = false;                                                                   \
    ctkCommandLineParser_addArgument_cxx_core(_NAME, _TYPE);                        \
    }

// --------------------------------------------------------------------------
ctkCommandLineParser_addArgument_cxx(String, QString);
ctkCommandLineParser_addArgument_cxx(Boolean, bool);
ctkCommandLineParser_addArgument_cxx_without_ignore_rest(StringList, QStringList);
ctkCommandLineParser_addArgument_cxx(Integer, int);

#undef ctkCommandLineParser_addArgument_cxx

// --------------------------------------------------------------------------
bool ctkCommandLineParser::setExactMatchRegularExpression(
    const QString& argument, const QString& expression, const QString& exactMatchFailedMessage)
{
  CommandLineParserArgumentDescriptionBase * argDesc =
      this->Internal->argumentDescription(argument);
  if (!argDesc)
    {
    return false;
    }

  if (argDesc->ArgumentType == "bool")
    {
    return false;
    }
  argDesc->RegularExpression = expression;
  argDesc->ExactMatchFailedMessage = exactMatchFailedMessage;
  return true;
}

// --------------------------------------------------------------------------
QString ctkCommandLineParser::helpText(const char charPad)
{
  QString text;
  QTextStream stream(&text);

  // Loop over argument descriptions
  foreach(CommandLineParserArgumentDescriptionBase* argDesc,
          this->Internal->ArgumentDescriptionList)
    {
    stream << argDesc->helpText(this->Internal->FieldWidth, charPad);
    }
  return text;
}

// --------------------------------------------------------------------------
bool ctkCommandLineParser::argumentAdded(const QString& argument)
{
  return this->Internal->ArgNameToArgumentDescriptionMap.contains(argument);
}

// --------------------------------------------------------------------------
bool ctkCommandLineParser::argumentParsed(const QString& argument)
{
  return this->Internal->ProcessedArguments.contains(argument);
}

<|endoftext|>


<commit_before>/*
 * Copyright 2009 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkImageEncoderPriv.h"
#include "SkJpegEncoder.h"
#include "SkPngEncoder.h"
#include "SkWebpEncoder.h"

#ifndef SK_HAS_JPEG_LIBRARY
bool SkJpegEncoder::Encode(SkWStream*, const SkPixmap&, const Options&) { return false; }
std::unique_ptr<SkEncoder> SkJpegEncoder::Make(SkWStream*, const SkPixmap&, const Options&) {
    return nullptr;
}
#endif

#ifndef SK_HAS_PNG_LIBRARY
bool SkPngEncoder::Encode(SkWStream*, const SkPixmap&, const Options&) { return false; }
std::unique_ptr<SkEncoder> SkPngEncoder::Make(SkWStream*, const SkPixmap&, const Options&) {
    return nullptr;
}
#endif

#ifndef SK_HAS_WEBP_LIBRARY
bool SkWebpEncoder::Encode(SkWStream*, const SkPixmap&, const Options&) { return false; }
#endif

bool SkEncodeImage(SkWStream* dst, const SkPixmap& src,
                   SkEncodedImageFormat format, int quality) {
    #ifdef SK_USE_CG_ENCODER
        (void)quality;
        return SkEncodeImageWithCG(dst, src, format);
    #elif SK_USE_WIC_ENCODER
        return SkEncodeImageWithWIC(dst, src, format, quality);
    #else
        switch(format) {
            case SkEncodedImageFormat::kJPEG: {
                SkJpegEncoder::Options opts;
                opts.fQuality = quality;
                return SkJpegEncoder::Encode(dst, src, opts);
            }
            case SkEncodedImageFormat::kPNG: {
                SkPngEncoder::Options opts;
                opts.fUnpremulBehavior = SkTransferFunctionBehavior::kIgnore;
                return SkPngEncoder::Encode(dst, src, opts);
            }
            case SkEncodedImageFormat::kWEBP: {
                SkWebpEncoder::Options opts;
                opts.fCompression = SkWebpEncoder::Compression::kLossy;
                opts.fQuality = quality;
                opts.fUnpremulBehavior = SkTransferFunctionBehavior::kIgnore;
                return SkWebpEncoder::Encode(dst, src, opts);
            }
            default:
                return false;
        }
    #endif
}

bool SkEncoder::encodeRows(int numRows) {
    SkASSERT(numRows > 0 && fCurrRow < fSrc.height());
    if (numRows <= 0 || fCurrRow >= fSrc.height()) {
        return false;
    }

    if (fCurrRow + numRows > fSrc.height()) {
        numRows = fSrc.height() - fCurrRow;
    }

    if (!this->onEncodeRows(numRows)) {
        // If we fail, short circuit any future calls.
        fCurrRow = fSrc.height();
        return false;
    }

    return true;
}
<commit_msg>Use kIgnore blend behavior when encoding JPEG<commit_after>/*
 * Copyright 2009 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkImageEncoderPriv.h"
#include "SkJpegEncoder.h"
#include "SkPngEncoder.h"
#include "SkWebpEncoder.h"

#ifndef SK_HAS_JPEG_LIBRARY
bool SkJpegEncoder::Encode(SkWStream*, const SkPixmap&, const Options&) { return false; }
std::unique_ptr<SkEncoder> SkJpegEncoder::Make(SkWStream*, const SkPixmap&, const Options&) {
    return nullptr;
}
#endif

#ifndef SK_HAS_PNG_LIBRARY
bool SkPngEncoder::Encode(SkWStream*, const SkPixmap&, const Options&) { return false; }
std::unique_ptr<SkEncoder> SkPngEncoder::Make(SkWStream*, const SkPixmap&, const Options&) {
    return nullptr;
}
#endif

#ifndef SK_HAS_WEBP_LIBRARY
bool SkWebpEncoder::Encode(SkWStream*, const SkPixmap&, const Options&) { return false; }
#endif

bool SkEncodeImage(SkWStream* dst, const SkPixmap& src,
                   SkEncodedImageFormat format, int quality) {
    #ifdef SK_USE_CG_ENCODER
        (void)quality;
        return SkEncodeImageWithCG(dst, src, format);
    #elif SK_USE_WIC_ENCODER
        return SkEncodeImageWithWIC(dst, src, format, quality);
    #else
        switch(format) {
            case SkEncodedImageFormat::kJPEG: {
                SkJpegEncoder::Options opts;
                opts.fQuality = quality;
                opts.fBlendBehavior = SkTransferFunctionBehavior::kIgnore;
                return SkJpegEncoder::Encode(dst, src, opts);
            }
            case SkEncodedImageFormat::kPNG: {
                SkPngEncoder::Options opts;
                opts.fUnpremulBehavior = SkTransferFunctionBehavior::kIgnore;
                return SkPngEncoder::Encode(dst, src, opts);
            }
            case SkEncodedImageFormat::kWEBP: {
                SkWebpEncoder::Options opts;
                opts.fCompression = SkWebpEncoder::Compression::kLossy;
                opts.fQuality = quality;
                opts.fUnpremulBehavior = SkTransferFunctionBehavior::kIgnore;
                return SkWebpEncoder::Encode(dst, src, opts);
            }
            default:
                return false;
        }
    #endif
}

bool SkEncoder::encodeRows(int numRows) {
    SkASSERT(numRows > 0 && fCurrRow < fSrc.height());
    if (numRows <= 0 || fCurrRow >= fSrc.height()) {
        return false;
    }

    if (fCurrRow + numRows > fSrc.height()) {
        numRows = fSrc.height() - fCurrRow;
    }

    if (!this->onEncodeRows(numRows)) {
        // If we fail, short circuit any future calls.
        fCurrRow = fSrc.height();
        return false;
    }

    return true;
}
<|endoftext|>


<commit_before>/* indivudal.cpp - CS 472 Project #2: Genetic Programming
 * Copyright 2014 Andrew Schwartzmeyer
 *
 * Source file for individual namespace
 */

#include <algorithm>
#include <cassert>
#include <cmath>
#include <iostream>
#include <memory>
#include <tuple>
#include <vector>

#include "individual.hpp"
#include "../options/options.hpp"
#include "../random_generator/random_generator.hpp"

namespace individual
{
  using std::vector;
  using std::string;
  using namespace random_generator;

  // Default Size struct constructor.
  Size::Size(): internals{0}, leaves{0}, depth{0} {}

  using F = Function;
  // Vectors of same-arity function enums.
  vector<F> nullaries {F::left, F::right, F::forward};
  vector<F> binaries {F::prog2, F::iffoodahead};
  vector<F> trinaries {F::prog3};

  /* Vectors of available function enums.  Should be moved into
     Options struct. */
  vector<F> leaves = nullaries;
  vector<F> internals {F::prog2, F::prog3, F::iffoodahead};

  // Returns a random function from a given set of functions.
  Function
  get_function(const vector<Function>& functions)
  {
    int_dist dist{0, static_cast<int>(functions.size()) - 1}; // closed interval
    return functions.at(dist(rg.engine));
  }

  // Returns bool of whether or not the item is in the set.
  template<typename I, typename S> bool
  contains(const I& item, const S& set)
  {
    return std::find(set.begin(), set.end(), item) != set.end();
  }

  // Returns the appropriate arity for a given function.
  unsigned int
  get_arity(const Function& function)
  {
    if (contains(function, nullaries))
      return 0;
    else if (contains(function, binaries))
      return 2;
    else if (contains(function, trinaries))
      return 3;
    assert(false);
  }

  // Default constructor for "empty" node
  Node::Node(): function{Function::nil}, arity{0} {}

  /* Recursively constructs a parse tree using the given method
     (either GROW or FULL). */
  Node::Node(const Method& method, const unsigned int& max_depth)
  {
    // Create leaf node if at the max depth or randomly (if growing).
    real_dist dist{0, 1};
    float grow_chance =
      static_cast<float>(leaves.size()) / (leaves.size() + internals.size());
    if (max_depth == 0
	or (method == Method::grow and dist(rg.engine) < grow_chance))
      {
	function = get_function(leaves);
	arity = 0; // leaves are always zero
      }
    // Otherwise choose an internal node.
    else
      {
	function = get_function(internals);
	arity = get_arity(function);
	// Recursively create subtrees.
	children.reserve(arity);
	for (unsigned int i = 0; i < arity; ++i)
	  children.emplace_back(Node{method, max_depth - 1});
      }
    assert(function != Function::nil); // do not create null types
    assert(children.size() == arity); // ensure arity
  }

  Node create(const unsigned int& max_depth = 0, const float& chance = 0.5)
  {
    real_dist method_dist{0, 1};
    Method method = (method_dist(rg.engine) < chance)
      ? Method::grow : Method::full;

    int_dist depth_dist{0, static_cast<int>(max_depth)};
    unsigned int depth = depth_dist(rg.engine);

    return Node{method, depth};
  }

  // Returns a string visually representing a particular node.
  string
  Node::represent() const
  {
    switch (function)
      {
      case F::nil:
	assert(false); // Never represent empty node.
      case F::prog2:
	return "prog-2";
      case F::prog3:
	return "prog-3";
      case F::iffoodahead:
	return "if-food-ahead";
      case F::left:
	return "left";
      case F::right:
	return "right";
      case F::forward:
	return "forward";
      }
    assert(false); // Every node should have been matched.
  }

  /* Returns string representation of expression in Polish/prefix
     notation using a pre-order traversal. */
  string
  Node::print() const
  {
    if (children.empty())
      return represent();

    string formula = "(" + represent();

    for (const Node& child : children)
      formula += " " + child.print();

    return formula + ")";
  }

  /* Evaluates an ant over a given map using a depth-first post-order
     recursive continuous evaluation of a decision tree. */
  void
  Node::evaluate(options::Map& map) const
  {
    if (not map.active()) return;

    switch (function)
      {
      case F::left:
	map.left(); // Terminal case
	break;
      case F::right:
	map.right(); // Terminal case
	break;
      case F::forward:
	map.forward(); // Terminal case
	break;
      case F::iffoodahead:
	if (map.look()) // Do left or right depending on if food ahead
	  children.at(0).evaluate(map);
	else
	  children.at(1).evaluate(map);
	break;
      case F::prog2: // Falls through
      case F::prog3:
	for (const Node& child : children)
	  child.evaluate(map);
	break;
      case F::nil:
	assert(false); // Never evaluate empty node
      }
  }

  /* Recursively count children and find maximum depth of tree via
     post-order traversal.  Keep track of internals, leaves, and depth
     via Size struct */
  const Size
  Node::size() const
  {
    Size size;

    if (children.empty())
      ++size.leaves;
    else
      {
	vector<unsigned int> depths;
	depths.reserve(arity);
	for (const Node& child : children)
	  {
	    Size temp = child.size();
	    size.internals += temp.internals;
	    size.leaves += temp.leaves;
	    depths.emplace_back(temp.depth);
	  }
	size.depth = 1 + *std::max_element(depths.begin(), depths.end());
	++size.internals;
      }
    return size;
  }

  // Used to represent "not-found" (similar to a NULL pointer).
  Node empty;

  /* Depth-first search for taget node.  Must be seeking either
     internal or leaf, cannot be both. */
  Node&
  Node::visit(const Size& i, Size& visiting)
  {
    for (Node& child : children)
      {
	// Increase relevant count.
	if (child.children.empty())
	  ++visiting.leaves;
	else
	  ++visiting.internals;

	// Return node reference if found.
	if (visiting.internals == i.internals or visiting.leaves == i.leaves)
	  return child;

	Node& temp = child.visit(i, visiting); // Recursive search.
	if (temp.function != Function::nil)
	  return temp;
      }
    return empty;
  }
    return empty;
  }

  void
  Node::mutate()
  {
    if (arity == 0)
      {
	const Function old = function;
	while (function == old)
	  function = get_function(leaves);
      }
    else if (arity == 2 or arity == 3)
      {
	const Function old = function;
	while (function == old)
	  function = get_function(internals);
	arity = get_arity(function);
      }
    else
      {
	std::cerr << "Arity shouldn't be: " << arity << std::endl;
	assert(false);
      }

    // Fix arity mismatches caused by mutation
    while (children.size() > arity)
      children.pop_back();

    while (children.size() < arity)
      children.emplace_back(create(4));

    if (arity != children.size())
      {
	std::cerr << "Arity was: " << arity
		  << ", with children: " << children.size()
		  << std::endl;
      }
    assert(arity == children.size());
    assert(function != Function::nil);
  }

  // Default constructor for Individual
  Individual::Individual() {}

  /* Create an Individual tree by having a root node (to which the
     actual construction is delegated). The depth is passed by value
     as its creation elsewhere is temporary. */
  Individual::Individual(const unsigned int depth, const float& chance,
			 options::Map map): fitness{0}, adjusted{0}
  {
    root = create(depth, chance);
    /* The evaluate method updates the size and both raw and adjusted
       fitnesses. */
    evaluate(map);
  }

  // Return string representation of a tree's size and fitness.
  string
  Individual::print() const
  {
    using std::to_string;

    string info = "# Size " + to_string(get_total()) + ", with "
      + to_string(get_internals()) + " internals, "
      + to_string(get_leaves()) + " leaves, and depth of "
      + to_string(get_depth()) + ".\n"
      + "# Score: " + to_string(score)
      + ", and adjusted fitness: " + to_string(adjusted) + ".\n";

    return info;
  }

  // Return string represenation of tree's expression (delegated).
  string
  Individual::print_formula() const
  {
    return "# Formula: " + root.print() + "\n";
  }

  /* Evaluate Individual for given values and calculate size.  Update
     Individual's size and fitness accordingly. Return non-empty
     string if printing. */
  string
  Individual::evaluate(options::Map map, const float& penalty,
		       const bool& print)
  {
    // Update size on evaluation because it's incredibly convenient.
    size = root.size();

    while (map.active())
      root.evaluate(map);

    score = map.fitness();
    // Adjusted fitness does not have size penalty.
    adjusted = static_cast<float>(score) / map.max();
    // Apply size penalty if not printing.
    fitness = score - penalty * get_total();

    string evaluation;
    if (print) evaluation = map.print();

    return evaluation;
  }

  using O = Operator;
  // Vectors of same-arity function enums.
  vector<O> operators {O::shrink, O::hoist, O::subtree, O::replacement};

  // Mutate each node with given probability.
  void
  Individual::mutate()
  {
    int_dist op_dist{0, static_cast<int>(operators.size()) - 1}; // closed interval
    const Operator op = operators.at(op_dist(rg.engine));
    /* Accessing the array operator of Individual to get the target
       node (p) is a little wonky syntax-wise, the shortest version is
       (*this)[p]. */
    Size p = get_node(Type::internal);
    if ((*this)[p].children.empty()) return; // p may have been root
    int_dist c_dist{0, static_cast<int>((*this)[p].children.size()) - 1};
    const unsigned int c = c_dist(rg.engine);
    assert(c <= 3);
    switch (op)
      {
      case O::shrink:
	{
	  (*this)[p].children.at(c) = std::move(create());
	  break;
	}
      case O::hoist:
	{
	  root = std::move((*this)[p].children.at(c));
	  break;
	}
      case O::subtree:
	{
	  // (*this)[p].children.pop_back();
	  (*this)[p].children.at(c) = std::move(create(4));
	  break;
	}
      case O::replacement:
	{
	  (*this)[p].children.at(c).mutate();
	  break;
	}
      }
  }

  // Safely return reference to desired node.
  Node&
  Individual::operator[](const Size& i)
  {
    assert(i.internals <= get_internals());
    assert(i.leaves <= get_leaves());

    Size visiting;
    // Return root node if that's what we're seeking.
    if (i.internals == 0 and i.leaves == 0)
      return root;
    else
      return root.visit(i, visiting);
  }

  {

    else
      {
      }

  Size
  Individual::get_node(const Type& type)
  {
    Size target;
    // Guaranteed to have at least 1 leaf, but may have 0 internals.
    if (type == Type::internal and get_internals() != 0)
      {
	// Choose an internal node.
	int_dist dist{0, static_cast<int>(get_internals()) - 1};
	target.internals = dist(rg.engine);
      }
    else
      {
	// Otherwise choose a leaf node.
	int_dist dist{0, static_cast<int>(get_leaves()) - 1};
	target.leaves = dist(rg.engine);
      }
    return target;
  }

  /* Swap two random subtrees between Individuals "a" and "b",
     selecting an internal node with chance probability. */
  void
  crossover(const float& chance, Individual& a, Individual& b)
  {
    real_dist probability{0, 1};

    Individual::Type type_a = (probability(rg.engine) < chance)
      ? Individual::Type::internal : Individual::Type::leaf;

    Individual::Type type_b = (probability(rg.engine) < chance)
      ? Individual::Type::internal : Individual::Type::leaf;

    std::swap(a[a.get_node(type_a)], b[b.get_node(type_b)]);
  }

  // Read-only "getters" for private data

  unsigned int
  Individual::get_internals() const
  {
    return size.internals;
  }

  unsigned int
  Individual::get_leaves() const
  {
    return size.leaves;
  }

  unsigned int
  Individual::get_total() const
  {
    return size.internals + size.leaves;
  }

  unsigned int
  Individual::get_depth() const
  {
    return size.depth;
  }

  unsigned int
  Individual::get_score() const
  {
    return score;
  }

  float
  Individual::get_fitness() const
  {
    return fitness;
  }

  float
  Individual::get_adjusted() const
  {
    return adjusted;
  }
}
<commit_msg>Fixing patches (revert into feature branch)<commit_after>/* indivudal.cpp - CS 472 Project #2: Genetic Programming
 * Copyright 2014 Andrew Schwartzmeyer
 *
 * Source file for individual namespace
 */

#include <algorithm>
#include <cassert>
#include <cmath>
#include <iostream>
#include <memory>
#include <tuple>
#include <vector>

#include "individual.hpp"
#include "../options/options.hpp"
#include "../random_generator/random_generator.hpp"

namespace individual
{
  using std::vector;
  using std::string;
  using namespace random_generator;

  // Default Size struct constructor.
  Size::Size(): internals{0}, leaves{0}, depth{0} {}

  using F = Function;
  // Vectors of same-arity function enums.
  vector<F> nullaries {F::left, F::right, F::forward};
  vector<F> binaries {F::prog2, F::iffoodahead};
  vector<F> trinaries {F::prog3};

  /* Vectors of available function enums.  Should be moved into
     Options struct. */
  vector<F> leaves = nullaries;
  vector<F> internals {F::prog2, F::prog3, F::iffoodahead};

  // Returns a random function from a given set of functions.
  Function
  get_function(const vector<Function>& functions)
  {
    int_dist dist{0, static_cast<int>(functions.size()) - 1}; // closed interval
    return functions.at(dist(rg.engine));
  }

  // Returns bool of whether or not the item is in the set.
  template<typename I, typename S> bool
  contains(const I& item, const S& set)
  {
    return std::find(set.begin(), set.end(), item) != set.end();
  }

  // Returns the appropriate arity for a given function.
  unsigned int
  get_arity(const Function& function)
  {
    if (contains(function, nullaries))
      return 0;
    else if (contains(function, binaries))
      return 2;
    else if (contains(function, trinaries))
      return 3;
    assert(false);
  }

  // Default constructor for "empty" node
  Node::Node(): function{Function::nil}, arity{0} {}

  /* Recursively constructs a parse tree using the given method
     (either GROW or FULL). */
  Node::Node(const Method& method, const unsigned int& max_depth)
  {
    // Create leaf node if at the max depth or randomly (if growing).
    real_dist dist{0, 1};
    float grow_chance =
      static_cast<float>(leaves.size()) / (leaves.size() + internals.size());
    if (max_depth == 0
	or (method == Method::grow and dist(rg.engine) < grow_chance))
      {
	function = get_function(leaves);
	arity = 0; // leaves are always zero
      }
    // Otherwise choose an internal node.
    else
      {
	function = get_function(internals);
	arity = get_arity(function);
	// Recursively create subtrees.
	children.reserve(arity);
	for (unsigned int i = 0; i < arity; ++i)
	  children.emplace_back(Node{method, max_depth - 1});
      }
    assert(function != Function::nil); // do not create null types
    assert(children.size() == arity); // ensure arity
  }

  Node create(const unsigned int& max_depth = 0, const float& chance = 0.5)
  {
    real_dist method_dist{0, 1};
    Method method = (method_dist(rg.engine) < chance)
      ? Method::grow : Method::full;

    int_dist depth_dist{0, static_cast<int>(max_depth)};
    unsigned int depth = depth_dist(rg.engine);

    return Node{method, depth};
  }

  // Returns a string visually representing a particular node.
  string
  Node::represent() const
  {
    switch (function)
      {
      case F::nil:
	assert(false); // Never represent empty node.
      case F::prog2:
	return "prog-2";
      case F::prog3:
	return "prog-3";
      case F::iffoodahead:
	return "if-food-ahead";
      case F::left:
	return "left";
      case F::right:
	return "right";
      case F::forward:
	return "forward";
      }
    assert(false); // Every node should have been matched.
  }

  /* Returns string representation of expression in Polish/prefix
     notation using a pre-order traversal. */
  string
  Node::print() const
  {
    if (children.empty())
      return represent();

    string formula = "(" + represent();

    for (const Node& child : children)
      formula += " " + child.print();

    return formula + ")";
  }

  /* Evaluates an ant over a given map using a depth-first post-order
     recursive continuous evaluation of a decision tree. */
  void
  Node::evaluate(options::Map& map) const
  {
    if (not map.active()) return;

    switch (function)
      {
      case F::left:
	map.left(); // Terminal case
	break;
      case F::right:
	map.right(); // Terminal case
	break;
      case F::forward:
	map.forward(); // Terminal case
	break;
      case F::iffoodahead:
	if (map.look()) // Do left or right depending on if food ahead
	  children.at(0).evaluate(map);
	else
	  children.at(1).evaluate(map);
	break;
      case F::prog2: // Falls through
      case F::prog3:
	for (const Node& child : children)
	  child.evaluate(map);
	break;
      case F::nil:
	assert(false); // Never evaluate empty node
      }
  }

  /* Recursively count children and find maximum depth of tree via
     post-order traversal.  Keep track of internals, leaves, and depth
     via Size struct */
  const Size
  Node::size() const
  {
    Size size;

    if (children.empty())
      ++size.leaves;
    else
      {
	vector<unsigned int> depths;
	depths.reserve(arity);
	for (const Node& child : children)
	  {
	    Size temp = child.size();
	    size.internals += temp.internals;
	    size.leaves += temp.leaves;
	    depths.emplace_back(temp.depth);
	  }
	size.depth = 1 + *std::max_element(depths.begin(), depths.end());
	++size.internals;
      }
    return size;
  }

  // Used to represent "not-found" (similar to a NULL pointer).
  Node empty;

  /* Depth-first search for taget node.  Must be seeking either
     internal or leaf, cannot be both. */
  Node&
  Node::visit(const Size& i, Size& visiting)
  {
    for (Node& child : children)
      {
	// Increase relevant count.
	if (child.children.empty())
	  ++visiting.leaves;
	else
	  ++visiting.internals;

	// Return node reference if found.
	if (visiting.internals == i.internals or visiting.leaves == i.leaves)
	  return child;

	Node& temp = child.visit(i, visiting); // Recursive search.
	if (temp.function != Function::nil)
	  return temp;
      }
    return empty;
  }

  void
  Node::mutate()
  {
    if (arity == 0)
      {
	const Function old = function;
	while (function == old)
	  function = get_function(leaves);
      }
    else if (arity == 2 or arity == 3)
      {
	const Function old = function;
	while (function == old)
	  function = get_function(internals);
	arity = get_arity(function);
      }
    else
      {
	std::cerr << "Arity shouldn't be: " << arity << std::endl;
	assert(false);
      }

    // Fix arity mismatches caused by mutation
    while (children.size() > arity)
      children.pop_back();

    while (children.size() < arity)
      children.emplace_back(create(4));

    if (arity != children.size())
      {
	std::cerr << "Arity was: " << arity
		  << ", with children: " << children.size()
		  << std::endl;
      }
    assert(arity == children.size());
    assert(function != Function::nil);
  }

  // Default constructor for Individual
  Individual::Individual() {}

  /* Create an Individual tree by having a root node (to which the
     actual construction is delegated). The depth is passed by value
     as its creation elsewhere is temporary. */
  Individual::Individual(const unsigned int depth, const float& chance,
			 options::Map map): fitness{0}, adjusted{0}
  {
    root = create(depth, chance);
    /* The evaluate method updates the size and both raw and adjusted
       fitnesses. */
    evaluate(map);
  }

  // Return string representation of a tree's size and fitness.
  string
  Individual::print() const
  {
    using std::to_string;

    string info = "# Size " + to_string(get_total()) + ", with "
      + to_string(get_internals()) + " internals, "
      + to_string(get_leaves()) + " leaves, and depth of "
      + to_string(get_depth()) + ".\n"
      + "# Score: " + to_string(score)
      + ", and adjusted fitness: " + to_string(adjusted) + ".\n";

    return info;
  }

  // Return string represenation of tree's expression (delegated).
  string
  Individual::print_formula() const
  {
    return "# Formula: " + root.print() + "\n";
  }

  /* Evaluate Individual for given values and calculate size.  Update
     Individual's size and fitness accordingly. Return non-empty
     string if printing. */
  string
  Individual::evaluate(options::Map map, const float& penalty,
		       const bool& print)
  {
    // Update size on evaluation because it's incredibly convenient.
    size = root.size();

    while (map.active())
      root.evaluate(map);

    score = map.fitness();
    // Adjusted fitness does not have size penalty.
    adjusted = static_cast<float>(score) / map.max();
    // Apply size penalty if not printing.
    fitness = score - penalty * get_total();

    string evaluation;
    if (print) evaluation = map.print();

    return evaluation;
  }

  using O = Operator;
  // Vectors of same-arity function enums.
  vector<O> operators {O::shrink, O::hoist, O::subtree, O::replacement};

  // Mutate each node with given probability.
  void
  Individual::mutate()
  {
    int_dist op_dist{0, static_cast<int>(operators.size()) - 1}; // closed interval
    const Operator op = operators.at(op_dist(rg.engine));
    /* Accessing the array operator of Individual to get the target
       node (p) is a little wonky syntax-wise, the shortest version is
       (*this)[p]. */
    Size p = get_node(Type::internal);
    if ((*this)[p].children.empty()) return; // p may have been root
    int_dist c_dist{0, static_cast<int>((*this)[p].children.size()) - 1};
    const unsigned int c = c_dist(rg.engine);
    assert(c <= 3);
    switch (op)
      {
      case O::shrink:
	{
	  (*this)[p].children.at(c) = std::move(create());
	  break;
	}
      case O::hoist:
	{
	  root = std::move((*this)[p].children.at(c));
	  break;
	}
      case O::subtree:
	{
	  // (*this)[p].children.pop_back();
	  (*this)[p].children.at(c) = std::move(create(4));
	  break;
	}
      case O::replacement:
	{
	  (*this)[p].children.at(c).mutate();
	  break;
	}
      }
  }

  // Safely return reference to desired node.
  Node&
  Individual::operator[](const Size& i)
  {
    assert(i.internals <= get_internals());
    assert(i.leaves <= get_leaves());

    Size visiting;
    // Return root node if that's what we're seeking.
    if (i.internals == 0 and i.leaves == 0)
      return root;
    else
      return root.visit(i, visiting);
  }

  Size
  Individual::get_node(const Type& type)
  {
    Size target;
    // Guaranteed to have at least 1 leaf, but may have 0 internals.
    if (type == Type::internal and get_internals() != 0)
      {
	// Choose an internal node.
	int_dist dist{0, static_cast<int>(get_internals()) - 1};
	target.internals = dist(rg.engine);
      }
    else
      {
	// Otherwise choose a leaf node.
	int_dist dist{0, static_cast<int>(get_leaves()) - 1};
	target.leaves = dist(rg.engine);
      }
    return target;
  }

  /* Swap two random subtrees between Individuals "a" and "b",
     selecting an internal node with chance probability. */
  void
  crossover(const float& chance, Individual& a, Individual& b)
  {
    real_dist probability{0, 1};

    Individual::Type type_a = (probability(rg.engine) < chance)
      ? Individual::Type::internal : Individual::Type::leaf;

    Individual::Type type_b = (probability(rg.engine) < chance)
      ? Individual::Type::internal : Individual::Type::leaf;

    std::swap(a[a.get_node(type_a)], b[b.get_node(type_b)]);
  }

  // Read-only "getters" for private data

  unsigned int
  Individual::get_internals() const
  {
    return size.internals;
  }

  unsigned int
  Individual::get_leaves() const
  {
    return size.leaves;
  }

  unsigned int
  Individual::get_total() const
  {
    return size.internals + size.leaves;
  }

  unsigned int
  Individual::get_depth() const
  {
    return size.depth;
  }

  unsigned int
  Individual::get_score() const
  {
    return score;
  }

  float
  Individual::get_fitness() const
  {
    return fitness;
  }

  float
  Individual::get_adjusted() const
  {
    return adjusted;
  }
}
<|endoftext|>


<commit_before>//Copyright (c) 2021 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "LightningLayer.h"

#include "LightningTree.h"

#include "../sliceDataStorage.h"
#include "../utils/linearAlg2D.h"
#include "../utils/SVG.h"
#include "../utils/SparsePointGridInclusive.h"

using namespace cura;

coord_t LightningLayer::getWeightedDistance(const Point& boundary_loc, const Point& unsupported_loc)
{
    return vSize(boundary_loc - unsupported_loc);
}

PolygonLightningDistanceField::PolygonLightningDistanceField
(
    const coord_t& radius,
 const Polygons& current_outline,
 const Polygons& current_overhang,
 const std::vector<std::shared_ptr<LightningTreeNode>>& initial_trees
)
{
    supporting_radius = radius;
    Polygons supporting_polylines = current_outline;
    for (PolygonRef poly : supporting_polylines)
    {
        if (!poly.empty())
        {
            poly.add(poly[0]); // add start so that the polyline is closed
        }
    }

    const LightningTreeNode::branch_visitor_func_t add_offset_branch_func =
    [&](const Point& parent, const Point& child)
    {
        supporting_polylines.addLine(parent, child);
    };
    for (const auto& tree : initial_trees)
    {
        tree->visitBranches(add_offset_branch_func);
    }
    supported = supporting_polylines.offsetPolyLine(supporting_radius);
    unsupported = current_overhang.difference(supported);
}

bool PolygonLightningDistanceField::tryGetNextPoint(Point* p, coord_t supporting_radius) const
{
    if (unsupported.area() < 25)
    {
        return false;
    }
    coord_t total_length = unsupported[0].polygonLength();
    coord_t dist_to_point_on_boundary = std::rand() % total_length;
    ClosestPolygonPoint cpp = PolygonUtils::walk(ClosestPolygonPoint(unsupported[0][0], 0, unsupported[0]), dist_to_point_on_boundary);
    *p = PolygonUtils::moveInside(cpp, supporting_radius);
    if (!unsupported.inside(*p))
    {
        PolygonUtils::moveInside(unsupported, *p, supporting_radius / 2);
    }
    // NOTE: it's okay for the rare case where a point ends up outside; it's just an inefficient tree branch.
    return true;
}

void PolygonLightningDistanceField::update(const Point& to_node, const Point& added_leaf)
{
    Polygons line;
    line.addLine(to_node, added_leaf);
    Polygons offsetted = line.offsetPolyLine(supporting_radius, ClipperLib::jtRound);
    supported = supported.unionPolygons(offsetted);
    unsupported = unsupported.difference(supported);
}

// -- -- -- -- -- --
// -- -- -- -- -- --

LightningDistanceField::LightningDistanceField
(
    const coord_t& radius,
 const Polygons& current_outline,
 const Polygons& current_overhang,
 const std::vector<std::shared_ptr<LightningTreeNode>>& initial_trees
)
: grid(cell_size)
, supporting_radius(radius)
, current_outline(current_outline)
, current_overhang(current_overhang)
{
    std::vector<Point> regular_dots = PolygonUtils::spreadDotsArea(current_overhang, cell_size);
    for (Point p : regular_dots)
    {
        const ClosestPolygonPoint cpp = PolygonUtils::findClosest(p, current_outline);
        const coord_t dist_to_boundary = vSize(p - cpp.p());
        unsupported_points.emplace_back(p, dist_to_boundary);
    }
    unsupported_points.sort([](const UnsupCell& a, const UnsupCell& b) { return a.dist_to_boundary < b.dist_to_boundary; });
    for (auto it = unsupported_points.begin(); it != unsupported_points.end(); ++it)
    {
        UnsupCell& cell = *it;
        unsupported_points_grid.emplace(grid.toGridPoint(cell.loc), it);
    }
}

bool LightningDistanceField::tryGetNextPoint(Point* p, coord_t supporting_radius) const
{
    if (unsupported_points.empty()) return false;
    *p = unsupported_points.front().loc;
    return true;
}

void LightningDistanceField::update(const Point& to_node, const Point& added_leaf)
{
    grid.processNearby(added_leaf, supporting_radius,
                       [added_leaf, this](const SquareGrid::GridPoint& grid_loc)
                       {
                           auto it = unsupported_points_grid.find(grid_loc);
                           if (it != unsupported_points_grid.end())
                           {
                               std::list<UnsupCell>::iterator& list_it = it->second;
                               UnsupCell& cell = *list_it;
                               if (shorterThen(cell.loc - added_leaf, supporting_radius))
                               {
                                   unsupported_points.erase(list_it);
                                   unsupported_points_grid.erase(it);
                               }
                           }
                           return true;
                       });
}

// -- -- -- -- -- --
// -- -- -- -- -- --

Point GroundingLocation::p() const
{
    if (tree_node != nullptr)
    {
        return tree_node->getLocation();
    }
    else
    {
        assert(boundary_location);
        return boundary_location->p();
    }
}

void LightningLayer::fillLocator(SparsePointGridInclusive<std::weak_ptr<LightningTreeNode>>& tree_node_locator)
{
    const LightningTreeNode::node_visitor_func_t add_node_to_locator_func =
        [&tree_node_locator](std::shared_ptr<LightningTreeNode> node)
        {
            tree_node_locator.insert(node->getLocation(), node);
        };
    for (auto& tree : tree_roots)
    {
        tree->visitNodes(add_node_to_locator_func);
    }
}

void LightningLayer::generateNewTrees(const Polygons& current_overhang, Polygons& current_outlines, coord_t supporting_radius)
{
    LightningDistanceField distance_field(supporting_radius, current_outlines, current_overhang, tree_roots);

    constexpr coord_t locator_cell_size = 2000;
    SparsePointGridInclusive<std::weak_ptr<LightningTreeNode>> tree_node_locator(locator_cell_size);
    fillLocator(tree_node_locator);

    constexpr size_t debug_max_iterations = 9999; // TODO: remove
    size_t i_debug = 0;

    // Until no more points need to be added to support all:
    // Determine next point from tree/outline areas via distance-field
    Point unsupported_location;
    while (distance_field.tryGetNextPoint(&unsupported_location, supporting_radius) && i_debug < debug_max_iterations)
    {
        ++i_debug;

        GroundingLocation grounding_loc = getBestGroundingLocation(unsupported_location, current_outlines, supporting_radius, tree_node_locator);

        // TODO: update unsupported_location to lie closer to grounding_loc

        auto tree_node = attach(unsupported_location, grounding_loc);
        tree_node_locator.insert(tree_node->getLocation(), tree_node);

        // update distance field
        distance_field.update(grounding_loc.p(), unsupported_location);
    }
}

GroundingLocation LightningLayer::getBestGroundingLocation(const Point& unsupported_location, const Polygons& current_outlines, const coord_t supporting_radius, const SparsePointGridInclusive<std::weak_ptr<LightningTreeNode>>& tree_node_locator, const std::shared_ptr<LightningTreeNode>& exclude_tree)
{
    ClosestPolygonPoint cpp = PolygonUtils::findClosest(unsupported_location, current_outlines);
    Point node_location = cpp.p();

    std::shared_ptr<LightningTreeNode> sub_tree(nullptr);
    coord_t current_dist = getWeightedDistance(node_location, unsupported_location);
    auto candidate_trees = tree_node_locator.getNearbyVals(unsupported_location, std::min(current_dist, supporting_radius));
    for (auto& candidate_wptr : candidate_trees)
    {
        auto candidate_sub_tree = candidate_wptr.lock();
        if (candidate_sub_tree && candidate_sub_tree != exclude_tree && !(exclude_tree && exclude_tree->hasOffspring(candidate_sub_tree)))
        {
            const coord_t candidate_dist = candidate_sub_tree->getWeightedDistance(unsupported_location, supporting_radius);
            if (candidate_dist < current_dist)
            {
                current_dist = candidate_dist;
                sub_tree = candidate_sub_tree;
            }
        }
    }

    if (!sub_tree)
    {
        return GroundingLocation{ nullptr, cpp };
    }
    else
    {
        return GroundingLocation{ sub_tree, std::optional<ClosestPolygonPoint>() };
    }
}

std::shared_ptr<LightningTreeNode> LightningLayer::attach(const Point& unsupported_location, const GroundingLocation& grounding_loc)
{
    // Update trees & distance fields.
    if (grounding_loc.boundary_location)
    {
        tree_roots.push_back(LightningTreeNode::create(grounding_loc.p(), unsupported_location));
        return tree_roots.back();
    }
    else
    {
        return grounding_loc.tree_node->addChild(unsupported_location);
    }
}

void LightningLayer::reconnectRoots(std::vector<std::shared_ptr<LightningTreeNode>>& to_be_reconnected_tree_roots, const Polygons& current_outlines, const coord_t supporting_radius)
{
    constexpr coord_t locator_cell_size = 2000;
    SparsePointGridInclusive<std::weak_ptr<LightningTreeNode>> tree_node_locator(locator_cell_size);
    fillLocator(tree_node_locator);

    for (auto root_ptr : to_be_reconnected_tree_roots)
    {
        auto old_root_it = std::find(tree_roots.begin(), tree_roots.end(), root_ptr);
        GroundingLocation ground = getBestGroundingLocation(root_ptr->getLocation(), current_outlines, supporting_radius, tree_node_locator, root_ptr);
        if (ground.boundary_location)
        {
            if (ground.boundary_location.value().p() == root_ptr->getLocation())
            {
                continue; // Already on the boundary.
            }

            auto new_root = LightningTreeNode::create(ground.p());
            new_root->addChild(root_ptr);
            tree_node_locator.insert(new_root->getLocation(), new_root);

            *old_root_it = std::move(new_root); // replace old root with new root
        }
        else
        {
            assert(ground.tree_node);
            assert(ground.tree_node != root_ptr);
            assert(!root_ptr->hasOffspring(ground.tree_node));
            assert(!ground.tree_node->hasOffspring(root_ptr));

            ground.tree_node->addChild(root_ptr);

            // remove old root
            *old_root_it = std::move(tree_roots.back());
            tree_roots.pop_back();
        }
    }
}

// Returns 'added someting'.
Polygons LightningLayer::convertToLines() const
{
    Polygons result_lines;
    if (tree_roots.empty())
    {
        return result_lines;
    }

    // TODO: The convert trees to lines 'algorithm' is way too simple right now (unless they're already going to be connected later).
    LightningTreeNode::branch_visitor_func_t convert_trees_to_lines =
        [&result_lines](const Point& node, const Point& leaf)
    {
        result_lines.addLine(node, leaf);
    };
    for (const auto& tree : tree_roots)
    {
        tree->visitBranches(convert_trees_to_lines);
    }
    return result_lines;
}
<commit_msg>get random next point with equal distance<commit_after>//Copyright (c) 2021 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "LightningLayer.h"

#include <iterator> // advance

#include "LightningTree.h"

#include "../sliceDataStorage.h"
#include "../utils/linearAlg2D.h"
#include "../utils/SVG.h"
#include "../utils/SparsePointGridInclusive.h"

using namespace cura;

coord_t LightningLayer::getWeightedDistance(const Point& boundary_loc, const Point& unsupported_loc)
{
    return vSize(boundary_loc - unsupported_loc);
}

PolygonLightningDistanceField::PolygonLightningDistanceField
(
    const coord_t& radius,
 const Polygons& current_outline,
 const Polygons& current_overhang,
 const std::vector<std::shared_ptr<LightningTreeNode>>& initial_trees
)
{
    supporting_radius = radius;
    Polygons supporting_polylines = current_outline;
    for (PolygonRef poly : supporting_polylines)
    {
        if (!poly.empty())
        {
            poly.add(poly[0]); // add start so that the polyline is closed
        }
    }

    const LightningTreeNode::branch_visitor_func_t add_offset_branch_func =
    [&](const Point& parent, const Point& child)
    {
        supporting_polylines.addLine(parent, child);
    };
    for (const auto& tree : initial_trees)
    {
        tree->visitBranches(add_offset_branch_func);
    }
    supported = supporting_polylines.offsetPolyLine(supporting_radius);
    unsupported = current_overhang.difference(supported);
}

bool PolygonLightningDistanceField::tryGetNextPoint(Point* p, coord_t supporting_radius) const
{
    if (unsupported.area() < 25)
    {
        return false;
    }
    coord_t total_length = unsupported[0].polygonLength();
    coord_t dist_to_point_on_boundary = std::rand() % total_length;
    ClosestPolygonPoint cpp = PolygonUtils::walk(ClosestPolygonPoint(unsupported[0][0], 0, unsupported[0]), dist_to_point_on_boundary);
    *p = PolygonUtils::moveInside(cpp, supporting_radius);
    if (!unsupported.inside(*p))
    {
        PolygonUtils::moveInside(unsupported, *p, supporting_radius / 2);
    }
    // NOTE: it's okay for the rare case where a point ends up outside; it's just an inefficient tree branch.
    return true;
}

void PolygonLightningDistanceField::update(const Point& to_node, const Point& added_leaf)
{
    Polygons line;
    line.addLine(to_node, added_leaf);
    Polygons offsetted = line.offsetPolyLine(supporting_radius, ClipperLib::jtRound);
    supported = supported.unionPolygons(offsetted);
    unsupported = unsupported.difference(supported);
}

// -- -- -- -- -- --
// -- -- -- -- -- --

LightningDistanceField::LightningDistanceField
(
    const coord_t& radius,
 const Polygons& current_outline,
 const Polygons& current_overhang,
 const std::vector<std::shared_ptr<LightningTreeNode>>& initial_trees
)
: grid(cell_size)
, supporting_radius(radius)
, current_outline(current_outline)
, current_overhang(current_overhang)
{
    std::vector<Point> regular_dots = PolygonUtils::spreadDotsArea(current_overhang, cell_size);
    for (Point p : regular_dots)
    {
        const ClosestPolygonPoint cpp = PolygonUtils::findClosest(p, current_outline);
        const coord_t dist_to_boundary = vSize(p - cpp.p());
        unsupported_points.emplace_back(p, dist_to_boundary);
    }
    unsupported_points.sort(
        [](const UnsupCell& a, const UnsupCell& b)
        {
            coord_t da = a.dist_to_boundary;
            coord_t db = b.dist_to_boundary;
            if (da == db) return std::hash<Point>{}(a.loc) % 17 < std::hash<Point>{}(b.loc) % 17;
            return da < db;
        });
    for (auto it = unsupported_points.begin(); it != unsupported_points.end(); ++it)
    {
        UnsupCell& cell = *it;
        unsupported_points_grid.emplace(grid.toGridPoint(cell.loc), it);
    }
}

bool LightningDistanceField::tryGetNextPoint(Point* p, coord_t supporting_radius) const
{
    if (unsupported_points.empty()) return false;
    *p = unsupported_points.front().loc;
    return true;
}

void LightningDistanceField::update(const Point& to_node, const Point& added_leaf)
{
    grid.processNearby(added_leaf, supporting_radius,
                       [added_leaf, this](const SquareGrid::GridPoint& grid_loc)
                       {
                           auto it = unsupported_points_grid.find(grid_loc);
                           if (it != unsupported_points_grid.end())
                           {
                               std::list<UnsupCell>::iterator& list_it = it->second;
                               UnsupCell& cell = *list_it;
                               if (shorterThen(cell.loc - added_leaf, supporting_radius))
                               {
                                   unsupported_points.erase(list_it);
                                   unsupported_points_grid.erase(it);
                               }
                           }
                           return true;
                       });
}

// -- -- -- -- -- --
// -- -- -- -- -- --

Point GroundingLocation::p() const
{
    if (tree_node != nullptr)
    {
        return tree_node->getLocation();
    }
    else
    {
        assert(boundary_location);
        return boundary_location->p();
    }
}

void LightningLayer::fillLocator(SparsePointGridInclusive<std::weak_ptr<LightningTreeNode>>& tree_node_locator)
{
    const LightningTreeNode::node_visitor_func_t add_node_to_locator_func =
        [&tree_node_locator](std::shared_ptr<LightningTreeNode> node)
        {
            tree_node_locator.insert(node->getLocation(), node);
        };
    for (auto& tree : tree_roots)
    {
        tree->visitNodes(add_node_to_locator_func);
    }
}

void LightningLayer::generateNewTrees(const Polygons& current_overhang, Polygons& current_outlines, coord_t supporting_radius)
{
    LightningDistanceField distance_field(supporting_radius, current_outlines, current_overhang, tree_roots);

    constexpr coord_t locator_cell_size = 2000;
    SparsePointGridInclusive<std::weak_ptr<LightningTreeNode>> tree_node_locator(locator_cell_size);
    fillLocator(tree_node_locator);

    constexpr size_t debug_max_iterations = 9999; // TODO: remove
    size_t i_debug = 0;

    // Until no more points need to be added to support all:
    // Determine next point from tree/outline areas via distance-field
    Point unsupported_location;
    while (distance_field.tryGetNextPoint(&unsupported_location, supporting_radius) && i_debug < debug_max_iterations)
    {
        ++i_debug;

        GroundingLocation grounding_loc = getBestGroundingLocation(unsupported_location, current_outlines, supporting_radius, tree_node_locator);

        // TODO: update unsupported_location to lie closer to grounding_loc

        auto tree_node = attach(unsupported_location, grounding_loc);
        tree_node_locator.insert(tree_node->getLocation(), tree_node);

        // update distance field
        distance_field.update(grounding_loc.p(), unsupported_location);
    }
}

GroundingLocation LightningLayer::getBestGroundingLocation(const Point& unsupported_location, const Polygons& current_outlines, const coord_t supporting_radius, const SparsePointGridInclusive<std::weak_ptr<LightningTreeNode>>& tree_node_locator, const std::shared_ptr<LightningTreeNode>& exclude_tree)
{
    ClosestPolygonPoint cpp = PolygonUtils::findClosest(unsupported_location, current_outlines);
    Point node_location = cpp.p();

    std::shared_ptr<LightningTreeNode> sub_tree(nullptr);
    coord_t current_dist = getWeightedDistance(node_location, unsupported_location);
    auto candidate_trees = tree_node_locator.getNearbyVals(unsupported_location, std::min(current_dist, supporting_radius));
    for (auto& candidate_wptr : candidate_trees)
    {
        auto candidate_sub_tree = candidate_wptr.lock();
        if (candidate_sub_tree && candidate_sub_tree != exclude_tree && !(exclude_tree && exclude_tree->hasOffspring(candidate_sub_tree)))
        {
            const coord_t candidate_dist = candidate_sub_tree->getWeightedDistance(unsupported_location, supporting_radius);
            if (candidate_dist < current_dist)
            {
                current_dist = candidate_dist;
                sub_tree = candidate_sub_tree;
            }
        }
    }

    if (!sub_tree)
    {
        return GroundingLocation{ nullptr, cpp };
    }
    else
    {
        return GroundingLocation{ sub_tree, std::optional<ClosestPolygonPoint>() };
    }
}

std::shared_ptr<LightningTreeNode> LightningLayer::attach(const Point& unsupported_location, const GroundingLocation& grounding_loc)
{
    // Update trees & distance fields.
    if (grounding_loc.boundary_location)
    {
        tree_roots.push_back(LightningTreeNode::create(grounding_loc.p(), unsupported_location));
        return tree_roots.back();
    }
    else
    {
        return grounding_loc.tree_node->addChild(unsupported_location);
    }
}

void LightningLayer::reconnectRoots(std::vector<std::shared_ptr<LightningTreeNode>>& to_be_reconnected_tree_roots, const Polygons& current_outlines, const coord_t supporting_radius)
{
    constexpr coord_t locator_cell_size = 2000;
    SparsePointGridInclusive<std::weak_ptr<LightningTreeNode>> tree_node_locator(locator_cell_size);
    fillLocator(tree_node_locator);

    for (auto root_ptr : to_be_reconnected_tree_roots)
    {
        auto old_root_it = std::find(tree_roots.begin(), tree_roots.end(), root_ptr);
        GroundingLocation ground = getBestGroundingLocation(root_ptr->getLocation(), current_outlines, supporting_radius, tree_node_locator, root_ptr);
        if (ground.boundary_location)
        {
            if (ground.boundary_location.value().p() == root_ptr->getLocation())
            {
                continue; // Already on the boundary.
            }

            auto new_root = LightningTreeNode::create(ground.p());
            new_root->addChild(root_ptr);
            tree_node_locator.insert(new_root->getLocation(), new_root);

            *old_root_it = std::move(new_root); // replace old root with new root
        }
        else
        {
            assert(ground.tree_node);
            assert(ground.tree_node != root_ptr);
            assert(!root_ptr->hasOffspring(ground.tree_node));
            assert(!ground.tree_node->hasOffspring(root_ptr));

            ground.tree_node->addChild(root_ptr);

            // remove old root
            *old_root_it = std::move(tree_roots.back());
            tree_roots.pop_back();
        }
    }
}

// Returns 'added someting'.
Polygons LightningLayer::convertToLines() const
{
    Polygons result_lines;
    if (tree_roots.empty())
    {
        return result_lines;
    }

    // TODO: The convert trees to lines 'algorithm' is way too simple right now (unless they're already going to be connected later).
    LightningTreeNode::branch_visitor_func_t convert_trees_to_lines =
        [&result_lines](const Point& node, const Point& leaf)
    {
        result_lines.addLine(node, leaf);
    };
    for (const auto& tree : tree_roots)
    {
        tree->visitBranches(convert_trees_to_lines);
    }
    return result_lines;
}
<|endoftext|>