<commit_before>#include <nan.h>
#include <iostream>
#include <string>
#include <sstream>
#include <map>
#include <stdlib.h>
#include <stdio.h>

#include <poppler/qt5/poppler-form.h>
#include <poppler/qt5/poppler-qt5.h>

#include <cairo/cairo.h>
#include <cairo/cairo-pdf.h>

#include <QtCore/QBuffer>
#include <QtCore/QFile>
#include <QtGui/QImage>

#include "NodePoppler.h"    // NOLINT(build/include)

using namespace std;
using v8::Number;
using v8::String;
using v8::Local;
using v8::Object;
using v8::Array;
using v8::Value;
using v8::Boolean;

inline bool fileExists (const std::string& name) {
  if (FILE *file = fopen(name.c_str(), "r")) {
    fclose(file);
    return true;
  } else {
    return false;
  }
}

// Cairo write and read functions (to QBuffer)
static cairo_status_t readPngFromBuffer(void *closure, unsigned char *data, unsigned int length) {
  QBuffer *myBuffer = (QBuffer *)closure;
  size_t bytes_read;
  bytes_read = myBuffer->read((char *)data, length);
  if (bytes_read != length)
    return CAIRO_STATUS_READ_ERROR;

  return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t writePngToBuffer(void *closure, const unsigned char *data, unsigned int length) {
  QBuffer *myBuffer = (QBuffer *)closure;
  size_t bytes_wrote;
  bytes_wrote = myBuffer->write((char *)data, length);
  if (bytes_wrote != length)
    return CAIRO_STATUS_READ_ERROR;

  return CAIRO_STATUS_SUCCESS;
}

void createPdf(QBuffer *buffer, Poppler::Document *document) {
  ostringstream ss;

  Poppler::PDFConverter *converter = document->pdfConverter();
  converter->setPDFOptions(converter->pdfOptions() | Poppler::PDFConverter::WithChanges);
  converter->setOutputDevice(buffer);
  if (!converter->convert()) {
    // enum Error
    // {
    //     NoError,
    //     FileLockedError,
    //     OpenOutputError,
    //     NotSupportedInputFileError
    // };
    ss << "Error occurred when converting PDF: " << converter->lastError();
    delete converter;
    throw ss.str();
  }
  delete converter;
}

void createImgPdf(QBuffer *buffer, Poppler::Document *document) {
  // Calculate max page sizes. The output PDF will have its size set to max width/height.
  double maxWidth = 0;
  double maxHeight = 0;

  int n = document->numPages();

  for (int i = 0; i < n; i += 1) {
    Poppler::Page *page = document->page(i);

    QSizeF size = page->pageSizeF();

    if (size.height() > maxHeight)
      maxHeight = size.height();

    if (size.width() > maxWidth)
      maxWidth = size.width();
  }

  // Initialize Cairo writing surface, and since we have images at 360 DPI, we'll set scaling.
  cairo_surface_t *surface = cairo_pdf_surface_create_for_stream(writePngToBuffer, buffer, maxWidth, maxHeight);
  cairo_t *cr = cairo_create(surface);
  cairo_scale(cr, 0.2, 0.2);

  for (int i = 0; i < n; i += 1) {
    Poppler::Page *page = document->page(i);

    // Save the page as PNG image to buffer. (We could use QFile if we want to save images to files)
    QBuffer *pageImage = new QBuffer();
    pageImage->open(QIODevice::ReadWrite);
    QImage img = page->renderToImage(360, 360);
    img.save(pageImage, "png");
    pageImage->seek(0);  // Reading does not work if we don't reset the pointer

    // Ookay, let's try to make new page out of the image
    cairo_surface_t *drawImageSurface = cairo_image_surface_create_from_png_stream(readPngFromBuffer, pageImage);
    cairo_set_source_surface(cr, drawImageSurface, 0, 0);
    cairo_paint(cr);
    cairo_surface_destroy(drawImageSurface);

    // Create new page if multipage document
    if (n > 0) {
      cairo_surface_show_page(surface);
      if (cr != NULL) {
        cairo_destroy(cr);
      }
      cr = cairo_create(surface);
      cairo_scale(cr, 0.2, 0.2);
    }

    pageImage->close();
    delete pageImage;

    delete page;
  }

  // Close Cairo
  cairo_destroy(cr);
  cairo_surface_finish(surface);
  cairo_surface_destroy (surface);
}

WriteFieldsParams v8ParamsToCpp(const Nan::FunctionCallbackInfo<v8::Value>& args) {
  Local<Object> parameters;
  string saveFormat = "imgpdf";
  map<string, string> fields;

  String::Utf8Value sourcePdfFileNameParam(args[0]->ToString());
  string sourcePdfFileName = string(*sourcePdfFileNameParam);

  Local<Object> changeFields = args[1]->ToObject();
  Local<String> saveStr = Nan::New("save").ToLocalChecked();

  // Check if any configuration parameters
  if (args.Length() > 2) {
    parameters = args[2]->ToObject();
    Local<Value> saveParam = Nan::Get(parameters, saveStr).ToLocalChecked();
    if (!saveParam->IsUndefined()) {
      String::Utf8Value saveFormatParam(Nan::Get(parameters, saveStr).ToLocalChecked());
      saveFormat = string(*saveFormatParam);
    }
  }

  // Convert form fields to c++ map
  Local<Array> fieldArray = changeFields->GetPropertyNames();
  for (uint32_t i = 0; i < fieldArray->Length(); i += 1) {
    Local<Value> name = fieldArray->Get(i);
    Local<Value> value = changeFields->Get(name);
    fields[std::string(*String::Utf8Value(name))] = std::string(*String::Utf8Value(value));
  }

  // Create and return PDF
  struct WriteFieldsParams params(sourcePdfFileName, saveFormat, fields);
  return params;
}

// Pdf creator that is not dependent on V8 internals (safe at async?)
QBuffer *writePdfFields(struct WriteFieldsParams params) {

  ostringstream ss;
  // If source file does not exist, throw error and return false
  if (!fileExists(params.sourcePdfFileName)) {
    ss << "File \"" << params.sourcePdfFileName << "\" does not exist";
    throw ss.str();
  }

  // Open document and return false and throw error if something goes wrong
  Poppler::Document *document = Poppler::Document::load(QString::fromStdString(params.sourcePdfFileName));
  if (document == NULL) {
    ss << "Error occurred when reading \"" << params.sourcePdfFileName << "\"";
    throw ss.str();
  }

  // Fill form
  int n = document->numPages();
  stringstream idSS;
  for (int i = 0; i < n; i += 1) {
    Poppler::Page *page = document->page(i);
    QList<Poppler::FormField *> formFields = page->formFields();

    foreach (Poppler::FormField *field, formFields) {
      string fieldName = field->fullyQualifiedName().toStdString();
      // Support writing fields by both fieldName and id.
      // If fieldName is not present in params, try id.
      if (params.fields.count(fieldName) == 0) {
        idSS << field->id();
        fieldName = idSS.str();
        idSS.str("");
        idSS.clear();
      }
      if (!field->isReadOnly() && field->isVisible() && params.fields.count(fieldName) ) {
        // Text
        if (field->type() == Poppler::FormField::FormText) {
          Poppler::FormFieldText *textField = (Poppler::FormFieldText *) field;
          textField->setText(QString::fromUtf8(params.fields[fieldName].c_str()));
        }

        // Choice
        if (field->type() == Poppler::FormField::FormChoice) {
          Poppler::FormFieldChoice *choiceField = (Poppler::FormFieldChoice *) field;
          if (choiceField->isEditable()) {
            choiceField->setEditChoice(QString::fromUtf8(params.fields[fieldName].c_str()));
          }
          else {
            QStringList possibleChoices = choiceField->choices();
            QString proposedChoice = QString::fromUtf8(params.fields[fieldName].c_str());
            int index = possibleChoices.indexOf(proposedChoice);
            if (index >= 0) {
              QList<int> choiceList;
              choiceList << index;
              choiceField->setCurrentChoices(choiceList);
            }
          }
        }

        // Button
        // ! TODO ! Note. Poppler doesn't support checkboxes with hashtag names (aka using exportValue).
        if (field->type() == Poppler::FormField::FormButton) {
          Poppler::FormFieldButton* myButton = (Poppler::FormFieldButton *)field;
    	  switch (myButton->buttonType()) {
    		// Push
    		case Poppler::FormFieldButton::Push:     break;
    		case Poppler::FormFieldButton::CheckBox:
    		  if (params.fields[fieldName].compare("true") == 0) {
    		    myButton->setState(true);
    		  }
    		  else {
    			myButton->setState(false);
    		  }
    		  break;
    		case Poppler::FormFieldButton::Radio:
    		  if (params.fields[fieldName].compare(myButton->caption().toUtf8().constData()) == 0) {
    			myButton->setState(true);
    		  }
    		  else {
    		    myButton->setState(false);
    		  }
    		  break;
    		}
          }
        }
      }
    }

    qDeleteAll(formFields);
    delete page;
  }

  // Now save and return the document
  QBuffer *bufferDevice = new QBuffer();
  bufferDevice->open(QIODevice::ReadWrite);

  // Get save parameters
  if (params.saveFormat == "imgpdf") {
    createImgPdf(bufferDevice, document);
  }
  else {
    createPdf(bufferDevice, document);
  }

  delete document;

  return bufferDevice;
}

//***********************************
//
// Node.js methods
//
//***********************************

// Read PDF form fields
NAN_METHOD(ReadSync) {

  // expect a number as the first argument
  Nan::Utf8String *fileName = new Nan::Utf8String(info[0]);
  ostringstream ss;
  int n = 0;

  // If file does not exist, throw error and return false
  if (!fileExists(**fileName)) {
    ss << "File \"" << **fileName << "\" does not exist";
    Nan::ThrowError(Nan::New<String>(ss.str()).ToLocalChecked());
    info.GetReturnValue().Set(Nan::False());
  }

  // Open document and return false and throw error if something goes wrong
  Poppler::Document *document = Poppler::Document::load(**fileName);
  if (document != NULL) {
    // Get field list
    n = document->numPages();
  } else {
    ss << "Error occurred when reading \"" << **fileName << "\"";
    Nan::ThrowError(Nan::New<String>(ss.str()).ToLocalChecked());
    info.GetReturnValue().Set(Nan::False());
  }

  delete fileName;

  // Store field value objects to v8 array
  Local<Array> fieldArray = Nan::New<Array>();
  int fieldNum = 0;

  for (int i = 0; i < n; i += 1) {
    Poppler::Page *page = document->page(i);
    QList<Poppler::FormField *> formFields = page->formFields();

    foreach (Poppler::FormField *field, formFields) {

      if (!field->isReadOnly() && field->isVisible()) {

        // Make JavaScript object out of the fieldnames
        Local<Object> obj = Nan::New<Object>();
        Nan::Set(obj, Nan::New<String>("name").ToLocalChecked(), Nan::New<String>(field->fullyQualifiedName().toStdString()).ToLocalChecked());
        Nan::Set(obj, Nan::New<String>("page").ToLocalChecked(), Nan::New<Number>(i));

        // ! TODO ! Note. Poppler doesn't support checkboxes with hashtag names (aka using exportValue).
        string fieldType;
        // Set default value undefined
        Nan::Set(obj, Nan::New<String>("value").ToLocalChecked(), Nan::Undefined());
        Poppler::FormFieldButton *myButton;
        Poppler::FormFieldChoice *myChoice;

        Nan::Set(obj, Nan::New<String>("id").ToLocalChecked(), Nan::New<Number>(field->id()));
        switch (field->type()) {

          // FormButton
          case Poppler::FormField::FormButton:
            myButton = (Poppler::FormFieldButton *)field;
            switch (myButton->buttonType()) {
              // Push
              case Poppler::FormFieldButton::Push:        fieldType = "push_button";     break;
              case Poppler::FormFieldButton::CheckBox:
                fieldType = "checkbox";
                Nan::Set(obj, Nan::New<String>("value").ToLocalChecked(), Nan::New<Boolean>(myButton->state()));
                break;
              case Poppler::FormFieldButton::Radio:       fieldType = "radio";           break;
            }
            break;

          // FormText
          case Poppler::FormField::FormText:
            Nan::Set(obj, Nan::New<String>("value").ToLocalChecked(), Nan::New<String>(((Poppler::FormFieldText *)field)->text().toStdString()).ToLocalChecked());
            fieldType = "text";
            break;

          // FormChoice
          case Poppler::FormField::FormChoice: {
            Local<Array> choiceArray = Nan::New<Array>();
            myChoice = (Poppler::FormFieldChoice *)field;
            QStringList possibleChoices = myChoice->choices();
            for (int i = 0; i < possibleChoices.size(); i++) {
              Nan::Set(choiceArray, i, Nan::New<String>(possibleChoices.at(i).toStdString()).ToLocalChecked());
            }
            Nan::Set(obj, Nan::New<String>("choices").ToLocalChecked(), choiceArray);
            switch (myChoice->choiceType()) {
              case Poppler::FormFieldChoice::ComboBox:    fieldType = "combobox";       break;
              case Poppler::FormFieldChoice::ListBox:     fieldType = "listbox";        break;
            }
            break;
          }

          // FormSignature
          case Poppler::FormField::FormSignature:         fieldType = "formsignature";  break;

          default:
            fieldType = "undefined";
            break;
        }

        Nan::Set(obj, Nan::New<String>("type").ToLocalChecked(), Nan::New<String>(fieldType.c_str()).ToLocalChecked());

        fieldArray->Set(fieldNum, obj);
        fieldNum++;

      }
    }

    qDeleteAll(formFields);
    delete page;
  }

  info.GetReturnValue().Set(fieldArray);

  delete document;
}

// Write PDF form fields
NAN_METHOD(WriteSync) {

  // Check and return parameters given at JavaScript function call
  WriteFieldsParams params = v8ParamsToCpp(info);

  // Create and return pdf
  try
  {
    QBuffer *buffer = writePdfFields(params);
    Local<Object> returnPdf = Nan::CopyBuffer((char *)buffer->data().data(), buffer->size()).ToLocalChecked();
    buffer->close();
    delete buffer;
    info.GetReturnValue().Set(returnPdf);
  }
  catch (string error)
  {
    Nan::ThrowError(Nan::New<String>(error).ToLocalChecked());
    info.GetReturnValue().Set(Nan::Null());
  }
}
<commit_msg>Adding support for Radio Button fill<commit_after>#include <nan.h>
#include <iostream>
#include <string>
#include <sstream>
#include <map>
#include <stdlib.h>
#include <stdio.h>

#include <poppler/qt5/poppler-form.h>
#include <poppler/qt5/poppler-qt5.h>

#include <cairo/cairo.h>
#include <cairo/cairo-pdf.h>

#include <QtCore/QBuffer>
#include <QtCore/QFile>
#include <QtGui/QImage>

#include "NodePoppler.h"    // NOLINT(build/include)

using namespace std;
using v8::Number;
using v8::String;
using v8::Local;
using v8::Object;
using v8::Array;
using v8::Value;
using v8::Boolean;

inline bool fileExists (const std::string& name) {
  if (FILE *file = fopen(name.c_str(), "r")) {
    fclose(file);
    return true;
  } else {
    return false;
  }
}

// Cairo write and read functions (to QBuffer)
static cairo_status_t readPngFromBuffer(void *closure, unsigned char *data, unsigned int length) {
  QBuffer *myBuffer = (QBuffer *)closure;
  size_t bytes_read;
  bytes_read = myBuffer->read((char *)data, length);
  if (bytes_read != length)
    return CAIRO_STATUS_READ_ERROR;

  return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t writePngToBuffer(void *closure, const unsigned char *data, unsigned int length) {
  QBuffer *myBuffer = (QBuffer *)closure;
  size_t bytes_wrote;
  bytes_wrote = myBuffer->write((char *)data, length);
  if (bytes_wrote != length)
    return CAIRO_STATUS_READ_ERROR;

  return CAIRO_STATUS_SUCCESS;
}

void createPdf(QBuffer *buffer, Poppler::Document *document) {
  ostringstream ss;

  Poppler::PDFConverter *converter = document->pdfConverter();
  converter->setPDFOptions(converter->pdfOptions() | Poppler::PDFConverter::WithChanges);
  converter->setOutputDevice(buffer);
  if (!converter->convert()) {
    // enum Error
    // {
    //     NoError,
    //     FileLockedError,
    //     OpenOutputError,
    //     NotSupportedInputFileError
    // };
    ss << "Error occurred when converting PDF: " << converter->lastError();
    delete converter;
    throw ss.str();
  }
  delete converter;
}

void createImgPdf(QBuffer *buffer, Poppler::Document *document) {
  // Calculate max page sizes. The output PDF will have its size set to max width/height.
  double maxWidth = 0;
  double maxHeight = 0;

  int n = document->numPages();

  for (int i = 0; i < n; i += 1) {
    Poppler::Page *page = document->page(i);

    QSizeF size = page->pageSizeF();

    if (size.height() > maxHeight)
      maxHeight = size.height();

    if (size.width() > maxWidth)
      maxWidth = size.width();
  }

  // Initialize Cairo writing surface, and since we have images at 360 DPI, we'll set scaling.
  cairo_surface_t *surface = cairo_pdf_surface_create_for_stream(writePngToBuffer, buffer, maxWidth, maxHeight);
  cairo_t *cr = cairo_create(surface);
  cairo_scale(cr, 0.2, 0.2);

  for (int i = 0; i < n; i += 1) {
    Poppler::Page *page = document->page(i);

    // Save the page as PNG image to buffer. (We could use QFile if we want to save images to files)
    QBuffer *pageImage = new QBuffer();
    pageImage->open(QIODevice::ReadWrite);
    QImage img = page->renderToImage(360, 360);
    img.save(pageImage, "png");
    pageImage->seek(0);  // Reading does not work if we don't reset the pointer

    // Ookay, let's try to make new page out of the image
    cairo_surface_t *drawImageSurface = cairo_image_surface_create_from_png_stream(readPngFromBuffer, pageImage);
    cairo_set_source_surface(cr, drawImageSurface, 0, 0);
    cairo_paint(cr);
    cairo_surface_destroy(drawImageSurface);

    // Create new page if multipage document
    if (n > 0) {
      cairo_surface_show_page(surface);
      if (cr != NULL) {
        cairo_destroy(cr);
      }
      cr = cairo_create(surface);
      cairo_scale(cr, 0.2, 0.2);
    }

    pageImage->close();
    delete pageImage;

    delete page;
  }

  // Close Cairo
  cairo_destroy(cr);
  cairo_surface_finish(surface);
  cairo_surface_destroy (surface);
}

WriteFieldsParams v8ParamsToCpp(const Nan::FunctionCallbackInfo<v8::Value>& args) {
  Local<Object> parameters;
  string saveFormat = "imgpdf";
  map<string, string> fields;

  String::Utf8Value sourcePdfFileNameParam(args[0]->ToString());
  string sourcePdfFileName = string(*sourcePdfFileNameParam);

  Local<Object> changeFields = args[1]->ToObject();
  Local<String> saveStr = Nan::New("save").ToLocalChecked();

  // Check if any configuration parameters
  if (args.Length() > 2) {
    parameters = args[2]->ToObject();
    Local<Value> saveParam = Nan::Get(parameters, saveStr).ToLocalChecked();
    if (!saveParam->IsUndefined()) {
      String::Utf8Value saveFormatParam(Nan::Get(parameters, saveStr).ToLocalChecked());
      saveFormat = string(*saveFormatParam);
    }
  }

  // Convert form fields to c++ map
  Local<Array> fieldArray = changeFields->GetPropertyNames();
  for (uint32_t i = 0; i < fieldArray->Length(); i += 1) {
    Local<Value> name = fieldArray->Get(i);
    Local<Value> value = changeFields->Get(name);
    fields[std::string(*String::Utf8Value(name))] = std::string(*String::Utf8Value(value));
  }

  // Create and return PDF
  struct WriteFieldsParams params(sourcePdfFileName, saveFormat, fields);
  return params;
}

// Pdf creator that is not dependent on V8 internals (safe at async?)
QBuffer *writePdfFields(struct WriteFieldsParams params) {

  ostringstream ss;
  // If source file does not exist, throw error and return false
  if (!fileExists(params.sourcePdfFileName)) {
    ss << "File \"" << params.sourcePdfFileName << "\" does not exist";
    throw ss.str();
  }

  // Open document and return false and throw error if something goes wrong
  Poppler::Document *document = Poppler::Document::load(QString::fromStdString(params.sourcePdfFileName));
  if (document == NULL) {
    ss << "Error occurred when reading \"" << params.sourcePdfFileName << "\"";
    throw ss.str();
  }

  // Fill form
  int n = document->numPages();
  stringstream idSS;
  for (int i = 0; i < n; i += 1) {
    Poppler::Page *page = document->page(i);
    QList<Poppler::FormField *> formFields = page->formFields();

    foreach (Poppler::FormField *field, formFields) {
      string fieldName = field->fullyQualifiedName().toStdString();
      // Support writing fields by both fieldName and id.
      // If fieldName is not present in params, try id.
      if (params.fields.count(fieldName) == 0) {
        idSS << field->id();
        fieldName = idSS.str();
        idSS.str("");
        idSS.clear();
      }
      if (!field->isReadOnly() && field->isVisible() && params.fields.count(fieldName) ) {
        // Text
        if (field->type() == Poppler::FormField::FormText) {
          Poppler::FormFieldText *textField = (Poppler::FormFieldText *) field;
          textField->setText(QString::fromUtf8(params.fields[fieldName].c_str()));
        }

        // Choice
        if (field->type() == Poppler::FormField::FormChoice) {
          Poppler::FormFieldChoice *choiceField = (Poppler::FormFieldChoice *) field;
          if (choiceField->isEditable()) {
            choiceField->setEditChoice(QString::fromUtf8(params.fields[fieldName].c_str()));
          }
          else {
            QStringList possibleChoices = choiceField->choices();
            QString proposedChoice = QString::fromUtf8(params.fields[fieldName].c_str());
            int index = possibleChoices.indexOf(proposedChoice);
            if (index >= 0) {
              QList<int> choiceList;
              choiceList << index;
              choiceField->setCurrentChoices(choiceList);
            }
          }
        }

        // Button
        // ! TODO ! Note. Poppler doesn't support checkboxes with hashtag names (aka using exportValue).
        if (field->type() == Poppler::FormField::FormButton) {
          Poppler::FormFieldButton* myButton = (Poppler::FormFieldButton *)field;
          switch (myButton->buttonType()) {
            // Push
            case Poppler::FormFieldButton::Push:     break;
            case Poppler::FormFieldButton::CheckBox:
              if (params.fields[fieldName].compare("true") == 0) {
                myButton->setState(true);
              }
              else {
                myButton->setState(false);
              }
              break;
            case Poppler::FormFieldButton::Radio:
              if (params.fields[fieldName].compare(myButton->caption().toUtf8().constData()) == 0) {
                myButton->setState(true);
              }
              else {
                myButton->setState(false);
              }
              break;
            }
          }
        }
      }
    }

    qDeleteAll(formFields);
    delete page;
  }

  // Now save and return the document
  QBuffer *bufferDevice = new QBuffer();
  bufferDevice->open(QIODevice::ReadWrite);

  // Get save parameters
  if (params.saveFormat == "imgpdf") {
    createImgPdf(bufferDevice, document);
  }
  else {
    createPdf(bufferDevice, document);
  }

  delete document;

  return bufferDevice;
}

//***********************************
//
// Node.js methods
//
//***********************************

// Read PDF form fields
NAN_METHOD(ReadSync) {

  // expect a number as the first argument
  Nan::Utf8String *fileName = new Nan::Utf8String(info[0]);
  ostringstream ss;
  int n = 0;

  // If file does not exist, throw error and return false
  if (!fileExists(**fileName)) {
    ss << "File \"" << **fileName << "\" does not exist";
    Nan::ThrowError(Nan::New<String>(ss.str()).ToLocalChecked());
    info.GetReturnValue().Set(Nan::False());
  }

  // Open document and return false and throw error if something goes wrong
  Poppler::Document *document = Poppler::Document::load(**fileName);
  if (document != NULL) {
    // Get field list
    n = document->numPages();
  } else {
    ss << "Error occurred when reading \"" << **fileName << "\"";
    Nan::ThrowError(Nan::New<String>(ss.str()).ToLocalChecked());
    info.GetReturnValue().Set(Nan::False());
  }

  delete fileName;

  // Store field value objects to v8 array
  Local<Array> fieldArray = Nan::New<Array>();
  int fieldNum = 0;

  for (int i = 0; i < n; i += 1) {
    Poppler::Page *page = document->page(i);
    QList<Poppler::FormField *> formFields = page->formFields();

    foreach (Poppler::FormField *field, formFields) {

      if (!field->isReadOnly() && field->isVisible()) {

        // Make JavaScript object out of the fieldnames
        Local<Object> obj = Nan::New<Object>();
        Nan::Set(obj, Nan::New<String>("name").ToLocalChecked(), Nan::New<String>(field->fullyQualifiedName().toStdString()).ToLocalChecked());
        Nan::Set(obj, Nan::New<String>("page").ToLocalChecked(), Nan::New<Number>(i));

        // ! TODO ! Note. Poppler doesn't support checkboxes with hashtag names (aka using exportValue).
        string fieldType;
        // Set default value undefined
        Nan::Set(obj, Nan::New<String>("value").ToLocalChecked(), Nan::Undefined());
        Poppler::FormFieldButton *myButton;
        Poppler::FormFieldChoice *myChoice;

        Nan::Set(obj, Nan::New<String>("id").ToLocalChecked(), Nan::New<Number>(field->id()));
        switch (field->type()) {

          // FormButton
          case Poppler::FormField::FormButton:
            myButton = (Poppler::FormFieldButton *)field;
            switch (myButton->buttonType()) {
              // Push
              case Poppler::FormFieldButton::Push:        fieldType = "push_button";     break;
              case Poppler::FormFieldButton::CheckBox:
                fieldType = "checkbox";
                Nan::Set(obj, Nan::New<String>("value").ToLocalChecked(), Nan::New<Boolean>(myButton->state()));
                break;
              case Poppler::FormFieldButton::Radio:       fieldType = "radio";           break;
            }
            break;

          // FormText
          case Poppler::FormField::FormText:
            Nan::Set(obj, Nan::New<String>("value").ToLocalChecked(), Nan::New<String>(((Poppler::FormFieldText *)field)->text().toStdString()).ToLocalChecked());
            fieldType = "text";
            break;

          // FormChoice
          case Poppler::FormField::FormChoice: {
            Local<Array> choiceArray = Nan::New<Array>();
            myChoice = (Poppler::FormFieldChoice *)field;
            QStringList possibleChoices = myChoice->choices();
            for (int i = 0; i < possibleChoices.size(); i++) {
              Nan::Set(choiceArray, i, Nan::New<String>(possibleChoices.at(i).toStdString()).ToLocalChecked());
            }
            Nan::Set(obj, Nan::New<String>("choices").ToLocalChecked(), choiceArray);
            switch (myChoice->choiceType()) {
              case Poppler::FormFieldChoice::ComboBox:    fieldType = "combobox";       break;
              case Poppler::FormFieldChoice::ListBox:     fieldType = "listbox";        break;
            }
            break;
          }

          // FormSignature
          case Poppler::FormField::FormSignature:         fieldType = "formsignature";  break;

          default:
            fieldType = "undefined";
            break;
        }

        Nan::Set(obj, Nan::New<String>("type").ToLocalChecked(), Nan::New<String>(fieldType.c_str()).ToLocalChecked());

        fieldArray->Set(fieldNum, obj);
        fieldNum++;

      }
    }

    qDeleteAll(formFields);
    delete page;
  }

  info.GetReturnValue().Set(fieldArray);

  delete document;
}

// Write PDF form fields
NAN_METHOD(WriteSync) {

  // Check and return parameters given at JavaScript function call
  WriteFieldsParams params = v8ParamsToCpp(info);

  // Create and return pdf
  try
  {
    QBuffer *buffer = writePdfFields(params);
    Local<Object> returnPdf = Nan::CopyBuffer((char *)buffer->data().data(), buffer->size()).ToLocalChecked();
    buffer->close();
    delete buffer;
    info.GetReturnValue().Set(returnPdf);
  }
  catch (string error)
  {
    Nan::ThrowError(Nan::New<String>(error).ToLocalChecked());
    info.GetReturnValue().Set(Nan::Null());
  }
}
<|endoftext|>


<commit_before>///
/// @file   PrimeSieve.cpp
/// @brief  PrimeSieve is a high level class that manages prime
///         sieving. It is used for printing and counting primes
///         and for computing the nth prime.
///
/// Copyright (C) 2019 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primesieve/PrimeSieve.hpp>
#include <primesieve/ParallelSieve.hpp>
#include <primesieve/pmath.hpp>
#include <primesieve/PrintPrimes.hpp>
#include <primesieve/PreSieve.hpp>
#include <primesieve/types.hpp>

#include <stdint.h>
#include <algorithm>
#include <array>
#include <chrono>
#include <iostream>
#include <string>

using namespace std;

namespace {

struct SmallPrime
{
  uint64_t first;
  uint64_t last;
  int index;
  string str;
};

const array<SmallPrime, 8> smallPrimes
{{
  { 2,  2, 0, "2" },
  { 3,  3, 0, "3" },
  { 5,  5, 0, "5" },
  { 3,  5, 1, "(3, 5)" },
  { 5,  7, 1, "(5, 7)" },
  { 5, 11, 2, "(5, 7, 11)" },
  { 5, 13, 3, "(5, 7, 11, 13)" },
  { 5, 17, 4, "(5, 7, 11, 13, 17)" }
}};

} // namespace

namespace primesieve {

PrimeSieve::PrimeSieve()
{
  int sieveSize = get_sieve_size();
  setSieveSize(sieveSize);
}

/// Used for multi-threading
PrimeSieve::PrimeSieve(ParallelSieve* parent) :
  flags_(parent->flags_),
  sieveSize_(parent->sieveSize_),
  parent_(parent)
{ }

PrimeSieve::~PrimeSieve() = default;

void PrimeSieve::reset()
{
  counts_.fill(0);
  percent_ = -1.0;
  seconds_ = 0.0;
  sievedDistance_ = 0;
}

bool PrimeSieve::isFlag(int flag) const
{
  return (flags_ & flag) == flag;
}

bool PrimeSieve::isFlag(int first, int last) const
{
  int mask = (last * 2) - first;
  return (flags_ & mask) != 0;
}

bool PrimeSieve::isCountPrimes() const
{
  return isFlag(COUNT_PRIMES);
}

bool PrimeSieve::isPrintPrimes() const
{
  return isFlag(PRINT_PRIMES);
}

bool PrimeSieve::isPrint() const
{
  return isFlag(PRINT_PRIMES, PRINT_SEXTUPLETS);
}

bool PrimeSieve::isCountkTuplets() const
{
  return isFlag(COUNT_TWINS, COUNT_SEXTUPLETS);
}

bool PrimeSieve::isPrintkTuplets() const
{
  return isFlag(PRINT_TWINS, PRINT_SEXTUPLETS);
}

bool PrimeSieve::isStatus() const
{
  return isFlag(PRINT_STATUS, CALCULATE_STATUS);
}

bool PrimeSieve::isCount(int i) const
{
  return isFlag(COUNT_PRIMES << i);
}

bool PrimeSieve::isPrint(int i) const
{
  return isFlag(PRINT_PRIMES << i);
}

uint64_t PrimeSieve::getStart() const
{
  return start_;
}

uint64_t PrimeSieve::getStop() const
{
  return stop_;
}

uint64_t PrimeSieve::getDistance() const
{
  if (start_ <= stop_)
    return stop_ - start_;
  else
    return 0;
}

uint64_t PrimeSieve::getCount(int i) const
{
  return counts_.at(i);
}

counts_t& PrimeSieve::getCounts()
{
  return counts_;
}

int PrimeSieve::getSieveSize() const
{
  return sieveSize_;
}

double PrimeSieve::getSeconds() const
{
  return seconds_;
}

PreSieve& PrimeSieve::getPreSieve()
{
  return preSieve_;
}

void PrimeSieve::setPercentGui(double* percentGui)
{
  percentGui_ = percentGui;
}

void PrimeSieve::setFlags(int flags)
{
  flags_ = flags;
}

void PrimeSieve::addFlags(int flags)
{
  flags_ |= flags;
}

void PrimeSieve::setStart(uint64_t start)
{
  start_ = start;
}

void PrimeSieve::setStop(uint64_t stop)
{
  stop_ = stop;
}

/// Set the size of the sieve array in KiB (kibibyte)
void PrimeSieve::setSieveSize(int sieveSize)
{
  sieveSize_ = inBetween(8, sieveSize, 4096);
  sieveSize_ = floorPow2(sieveSize_);
}

void PrimeSieve::setStatus(double percent)
{
  if (!parent_)
  {
    auto old = percent_;
    percent_ = percent;
    if (percentGui_)
      *percentGui_ = percent_;
    if (isFlag(PRINT_STATUS))
      printStatus(old, percent_);
  }
}

void PrimeSieve::updateStatus(uint64_t dist)
{
  if (parent_)
  {
    updateDistance_ += dist;
    if (parent_->tryUpdateStatus(updateDistance_))
      updateDistance_ = 0;
  }
  else
  {
    sievedDistance_ += dist;
    double percent = 100;
    if (getDistance() > 0)
      percent = sievedDistance_ * 100.0 / getDistance();
    auto old = percent_;
    percent_ = min(percent, 100.0);
    if (percentGui_)
      *percentGui_ = percent_;
    if (isFlag(PRINT_STATUS))
      printStatus(old, percent_);
  }
}

void PrimeSieve::printStatus(double old, double current)
{
  int percent = (int) current;
  if (percent > (int) old)
  {
    cout << '\r' << percent << '%' << flush;
    if (percent == 100)
      cout << '\n';
  }
}

/// Process small primes <= 5 and small k-tuplets <= 17
void PrimeSieve::processSmallPrimes()
{
  for (auto& p : smallPrimes)
  {
    if (p.first >= start_ && p.last <= stop_)
    {
      if (isCount(p.index))
        counts_[p.index]++;
      if (isPrint(p.index))
        cout << p.str << '\n';
    }
  }
}

uint64_t PrimeSieve::countPrimes(uint64_t start, uint64_t stop)
{
  sieve(start, stop, COUNT_PRIMES);
  return getCount(0);
}

void PrimeSieve::sieve(uint64_t start, uint64_t stop)
{
  setStart(start);
  setStop(stop);
  sieve();
}

void PrimeSieve::sieve(uint64_t start, uint64_t stop, int flags)
{
  setStart(start);
  setStop(stop);
  setFlags(flags);
  sieve();
}

/// Sieve the primes and prime k-tuplets (twin primes,
/// prime triplets, ...) in [start, stop]
///
void PrimeSieve::sieve()
{
  reset();

  if (start_ > stop_)
    return;

  setStatus(0);
  auto t1 = chrono::system_clock::now();

  if (start_ <= 5)
    processSmallPrimes();

  if (stop_ >= 7)
  {
    PrintPrimes printPrimes(*this);
    printPrimes.sieve();
  }

  auto t2 = chrono::system_clock::now();
  chrono::duration<double> seconds = t2 - t1;
  seconds_ = seconds.count();
  setStatus(100);
}

} // namespace
<commit_msg>Update comment<commit_after>///
/// @file   PrimeSieve.cpp
/// @brief  PrimeSieve is a high level class that manages prime
///         sieving. It is used for printing and counting primes
///         and for computing the nth prime.
///
/// Copyright (C) 2019 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primesieve/PrimeSieve.hpp>
#include <primesieve/ParallelSieve.hpp>
#include <primesieve/pmath.hpp>
#include <primesieve/PrintPrimes.hpp>
#include <primesieve/PreSieve.hpp>
#include <primesieve/types.hpp>

#include <stdint.h>
#include <algorithm>
#include <array>
#include <chrono>
#include <iostream>
#include <string>

using namespace std;

namespace {

struct SmallPrime
{
  uint64_t first;
  uint64_t last;
  int index;
  string str;
};

const array<SmallPrime, 8> smallPrimes
{{
  { 2,  2, 0, "2" },
  { 3,  3, 0, "3" },
  { 5,  5, 0, "5" },
  { 3,  5, 1, "(3, 5)" },
  { 5,  7, 1, "(5, 7)" },
  { 5, 11, 2, "(5, 7, 11)" },
  { 5, 13, 3, "(5, 7, 11, 13)" },
  { 5, 17, 4, "(5, 7, 11, 13, 17)" }
}};

} // namespace

namespace primesieve {

PrimeSieve::PrimeSieve()
{
  int sieveSize = get_sieve_size();
  setSieveSize(sieveSize);
}

/// Used for multi-threading
PrimeSieve::PrimeSieve(ParallelSieve* parent) :
  flags_(parent->flags_),
  sieveSize_(parent->sieveSize_),
  parent_(parent)
{ }

PrimeSieve::~PrimeSieve() = default;

void PrimeSieve::reset()
{
  counts_.fill(0);
  percent_ = -1.0;
  seconds_ = 0.0;
  sievedDistance_ = 0;
}

bool PrimeSieve::isFlag(int flag) const
{
  return (flags_ & flag) == flag;
}

bool PrimeSieve::isFlag(int first, int last) const
{
  int mask = (last * 2) - first;
  return (flags_ & mask) != 0;
}

bool PrimeSieve::isCountPrimes() const
{
  return isFlag(COUNT_PRIMES);
}

bool PrimeSieve::isPrintPrimes() const
{
  return isFlag(PRINT_PRIMES);
}

bool PrimeSieve::isPrint() const
{
  return isFlag(PRINT_PRIMES, PRINT_SEXTUPLETS);
}

bool PrimeSieve::isCountkTuplets() const
{
  return isFlag(COUNT_TWINS, COUNT_SEXTUPLETS);
}

bool PrimeSieve::isPrintkTuplets() const
{
  return isFlag(PRINT_TWINS, PRINT_SEXTUPLETS);
}

bool PrimeSieve::isStatus() const
{
  return isFlag(PRINT_STATUS, CALCULATE_STATUS);
}

bool PrimeSieve::isCount(int i) const
{
  return isFlag(COUNT_PRIMES << i);
}

bool PrimeSieve::isPrint(int i) const
{
  return isFlag(PRINT_PRIMES << i);
}

uint64_t PrimeSieve::getStart() const
{
  return start_;
}

uint64_t PrimeSieve::getStop() const
{
  return stop_;
}

uint64_t PrimeSieve::getDistance() const
{
  if (start_ <= stop_)
    return stop_ - start_;
  else
    return 0;
}

uint64_t PrimeSieve::getCount(int i) const
{
  return counts_.at(i);
}

counts_t& PrimeSieve::getCounts()
{
  return counts_;
}

int PrimeSieve::getSieveSize() const
{
  return sieveSize_;
}

double PrimeSieve::getSeconds() const
{
  return seconds_;
}

PreSieve& PrimeSieve::getPreSieve()
{
  return preSieve_;
}

void PrimeSieve::setPercentGui(double* percentGui)
{
  percentGui_ = percentGui;
}

void PrimeSieve::setFlags(int flags)
{
  flags_ = flags;
}

void PrimeSieve::addFlags(int flags)
{
  flags_ |= flags;
}

void PrimeSieve::setStart(uint64_t start)
{
  start_ = start;
}

void PrimeSieve::setStop(uint64_t stop)
{
  stop_ = stop;
}

/// Set the size of the sieve array in KiB (kibibyte)
void PrimeSieve::setSieveSize(int sieveSize)
{
  sieveSize_ = inBetween(8, sieveSize, 4096);
  sieveSize_ = floorPow2(sieveSize_);
}

void PrimeSieve::setStatus(double percent)
{
  if (!parent_)
  {
    auto old = percent_;
    percent_ = percent;
    if (percentGui_)
      *percentGui_ = percent_;
    if (isFlag(PRINT_STATUS))
      printStatus(old, percent_);
  }
}

void PrimeSieve::updateStatus(uint64_t dist)
{
  if (parent_)
  {
    updateDistance_ += dist;
    if (parent_->tryUpdateStatus(updateDistance_))
      updateDistance_ = 0;
  }
  else
  {
    sievedDistance_ += dist;
    double percent = 100;
    if (getDistance() > 0)
      percent = sievedDistance_ * 100.0 / getDistance();
    auto old = percent_;
    percent_ = min(percent, 100.0);
    if (percentGui_)
      *percentGui_ = percent_;
    if (isFlag(PRINT_STATUS))
      printStatus(old, percent_);
  }
}

void PrimeSieve::printStatus(double old, double current)
{
  int percent = (int) current;
  if (percent > (int) old)
  {
    cout << '\r' << percent << '%' << flush;
    if (percent == 100)
      cout << '\n';
  }
}

/// Process small primes <= 5 and small k-tuplets <= 17
void PrimeSieve::processSmallPrimes()
{
  for (auto& p : smallPrimes)
  {
    if (p.first >= start_ && p.last <= stop_)
    {
      if (isCount(p.index))
        counts_[p.index]++;
      if (isPrint(p.index))
        cout << p.str << '\n';
    }
  }
}

uint64_t PrimeSieve::countPrimes(uint64_t start, uint64_t stop)
{
  sieve(start, stop, COUNT_PRIMES);
  return getCount(0);
}

void PrimeSieve::sieve(uint64_t start, uint64_t stop)
{
  setStart(start);
  setStop(stop);
  sieve();
}

void PrimeSieve::sieve(uint64_t start, uint64_t stop, int flags)
{
  setStart(start);
  setStop(stop);
  setFlags(flags);
  sieve();
}

/// Sieve the primes and prime k-tuplets (twin primes,
/// prime triplets, ...) in [start, stop].
///
void PrimeSieve::sieve()
{
  reset();

  if (start_ > stop_)
    return;

  setStatus(0);
  auto t1 = chrono::system_clock::now();

  if (start_ <= 5)
    processSmallPrimes();

  if (stop_ >= 7)
  {
    PrintPrimes printPrimes(*this);
    printPrimes.sieve();
  }

  auto t2 = chrono::system_clock::now();
  chrono::duration<double> seconds = t2 - t1;
  seconds_ = seconds.count();
  setStatus(100);
}

} // namespace
<|endoftext|>


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

#include "PrimeTower.h"

#include <algorithm>
#include <limits>

#include "ExtruderTrain.h"
#include "sliceDataStorage.h"
#include "gcodeExport.h"
#include "LayerPlan.h"
#include "infill.h"
#include "PrintFeature.h"
#include "raft.h"

#define CIRCLE_RESOLUTION 32 //The number of vertices in each circle.

namespace cura
{

PrimeTower::PrimeTower(const SliceDataStorage& storage)
: wipe_from_middle(false)
{
    enabled = storage.getSettingBoolean("prime_tower_enable")
           && storage.getSettingInMicrons("prime_tower_min_volume") > 10
           && storage.getSettingInMicrons("prime_tower_size") > 10;

    extruder_count = storage.meshgroup->getExtruderCount();
    extruder_order.resize(extruder_count);
    for (unsigned int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
    {
        extruder_order[extruder_nr] = extruder_nr; //Start with default order, then sort.
    }
    //Sort from high adhesion to low adhesion.
    const SliceDataStorage* storage_ptr = &storage; //Communicate to lambda via pointer to prevent copy.
    std::sort(extruder_order.begin(), extruder_order.end(), [storage_ptr](const unsigned int& extruder_nr_a, const unsigned int& extruder_nr_b) -> bool
    {
        const double adhesion_a = storage_ptr->meshgroup->getExtruderTrain(extruder_nr_a)->getSettingAsRatio("material_adhesion_tendency");
        const double adhesion_b = storage_ptr->meshgroup->getExtruderTrain(extruder_nr_b)->getSettingAsRatio("material_adhesion_tendency");
        return adhesion_a < adhesion_b;
    });
}

void PrimeTower::generateGroundpoly(const SliceDataStorage& storage)
{
    if (!enabled)
    {
        return;
    }

    coord_t tower_size = storage.getSettingInMicrons("prime_tower_size");
    bool circular_prime_tower = storage.getSettingBoolean("prime_tower_circular");

    PolygonRef p = outer_poly.newPoly();
    int tower_distance = 0;
    int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
    int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
    if (circular_prime_tower)
    {
        double_t tower_radius = tower_size / 2;
        for (unsigned int i = 0; i < CIRCLE_RESOLUTION; i++)
        {
            const double angle = (double) i / CIRCLE_RESOLUTION * 2 * M_PI; //In radians.
            p.add(Point(x - tower_radius + tower_distance + cos(angle) * tower_radius,
                        y + tower_radius + tower_distance + sin(angle) * tower_radius));
        }
    }
    else
    {
        p.add(Point(x + tower_distance, y + tower_distance));
        p.add(Point(x + tower_distance, y + tower_distance + tower_size));
        p.add(Point(x + tower_distance - tower_size, y + tower_distance + tower_size));
        p.add(Point(x + tower_distance - tower_size, y + tower_distance));
    }
    middle = Point(x - tower_size / 2, y + tower_size / 2);

    post_wipe_point = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
}

void PrimeTower::generatePaths(const SliceDataStorage& storage)
{
    enabled &= storage.max_print_height_second_to_last_extruder >= 0; //Maybe it turns out that we don't need a prime tower after all because there are no layer switches.
    if (enabled)
    {
        generatePaths_denseInfill(storage);
        generateStartLocations();
    }
}

void PrimeTower::generatePaths_denseInfill(const SliceDataStorage& storage)
{
    const coord_t layer_height = storage.getSettingInMicrons("layer_height");
    pattern_per_extruder.resize(extruder_count);

    coord_t cumulative_inset = 0; //Each tower shape is going to be printed inside the other. This is the inset we're doing for each extruder.
    for (unsigned int extruder : extruder_order)
    {
        const coord_t line_width = storage.meshgroup->getExtruderTrain(extruder)->getSettingInMicrons("prime_tower_line_width");
        const coord_t required_volume = storage.meshgroup->getExtruderTrain(extruder)->getSettingInCubicMillimeters("prime_tower_min_volume") * 1000000000; //To cubic microns.
        const double flow = storage.meshgroup->getExtruderTrain(extruder)->getSettingAsRatio("prime_tower_flow");
        coord_t current_volume = 0;
        ExtrusionMoves& pattern = pattern_per_extruder[extruder];

        //Create the walls of the prime tower.
        unsigned int wall_nr = 0;
        for (; current_volume < required_volume; wall_nr++)
        {
            //Create a new polygon with an offset from the outer polygon.
            Polygons polygons = outer_poly.offset(-cumulative_inset - wall_nr * line_width - line_width / 2);
            pattern.polygons.add(polygons);
            current_volume += polygons.polygonLength() * line_width * layer_height * flow;
            if (polygons.empty()) //Don't continue. We won't ever reach the required volume because it doesn't fit.
            {
                break;
            }
        }
        cumulative_inset += wall_nr * line_width;

        //Generate the pattern for the first layer.
        coord_t line_width_layer0 = line_width;
        if (storage.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::RAFT)
        {
            line_width_layer0 *= storage.meshgroup->getExtruderTrain(extruder)->getSettingAsRatio("initial_layer_line_width_factor");
        }
        pattern_per_extruder_layer0.emplace_back();

        ExtrusionMoves& pattern_layer0 = pattern_per_extruder_layer0.back();

        // Generate a concentric infill pattern in the form insets for the prime tower's first layer instead of using
        // the infill pattern because the infill pattern tries to connect polygons in different insets which causes the
        // first layer of the prime tower to not stick well.
        Polygons inset = outer_poly.offset(-line_width_layer0 / 2);
        while (!inset.empty())
        {
            pattern_layer0.polygons.add(inset);
            inset = inset.offset(-line_width_layer0);
        }
    }
}

void PrimeTower::generateStartLocations()
{
    // Evenly spread out a number of dots along the prime tower's outline. This is done for the complete outline,
    // so use the same start and end segments for this.
    PolygonsPointIndex segment_start = PolygonsPointIndex(&outer_poly, 0, 0);
    PolygonsPointIndex segment_end = segment_start;

    PolygonUtils::spreadDots(segment_start, segment_end, number_of_prime_tower_start_locations, prime_tower_start_locations);
}

void PrimeTower::addToGcode(const SliceDataStorage& storage, LayerPlan& gcode_layer, const GCodeExport& gcode, const int prev_extruder, const int new_extruder) const
{
    if (!enabled)
    {
        return;
    }
    if (gcode_layer.getPrimeTowerIsPlanned(new_extruder))
    { // don't print the prime tower if it has been printed already with this extruder.
        return;
    }

    if (gcode_layer.getLayerNr() > storage.max_print_height_second_to_last_extruder + 1)
    {
        return;
    }

    bool post_wipe = storage.meshgroup->getExtruderTrain(prev_extruder)->getSettingBoolean("prime_tower_wipe_enabled");

    // Do not wipe on the first layer, we will generate non-hollow prime tower there for better bed adhesion.
    const int layer_nr = gcode_layer.getLayerNr();
    if (prev_extruder == new_extruder || layer_nr == 0)
    {
        post_wipe = false;
    }

    // Go to the start location if it's not the first layer
    if (layer_nr != 0)
    {
        gotoStartLocation(storage, gcode_layer, new_extruder);
    }

    addToGcode_denseInfill(storage, gcode_layer, new_extruder);

    // post-wipe:
    if (post_wipe)
    { //Make sure we wipe the old extruder on the prime tower.
        gcode_layer.addTravel(post_wipe_point - gcode.getExtruderOffset(prev_extruder) + gcode.getExtruderOffset(new_extruder));
    }

    gcode_layer.setPrimeTowerIsPlanned(new_extruder);
}

void PrimeTower::addToGcode_denseInfill(const SliceDataStorage& storage, LayerPlan& gcode_layer, const int extruder_nr) const
{
    const ExtrusionMoves& pattern = (gcode_layer.getLayerNr() == -Raft::getFillerLayerCount(storage))
        ? pattern_per_extruder_layer0[extruder_nr]
        : pattern_per_extruder[extruder_nr];

    const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr];

    gcode_layer.addPolygonsByOptimizer(pattern.polygons, config);
    gcode_layer.addLinesByOptimizer(pattern.lines, config, SpaceFillType::Lines);
}

Point PrimeTower::getLocationBeforePrimeTower(const SliceDataStorage& storage) const
{
    Point ret(0, 0);
    int absolute_starting_points = 0;
    for (unsigned int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); extruder_nr++)
    {
        ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(0);
        if (train.getSettingBoolean("machine_extruder_start_pos_abs"))
        {
            ret += Point(train.getSettingInMicrons("machine_extruder_start_pos_x"), train.getSettingInMicrons("machine_extruder_start_pos_y"));
            absolute_starting_points++;
        }
    }
    if (absolute_starting_points > 0)
    { // take the average over all absolute starting positions
        ret /= absolute_starting_points;
    }
    else
    { // use the middle of the bed
        ret = storage.machine_size.flatten().getMiddle();
    }
    return ret;
}

void PrimeTower::subtractFromSupport(SliceDataStorage& storage)
{
    const Polygons outside_polygon = outer_poly.getOutsidePolygons();
    AABB outside_polygon_boundary_box(outside_polygon);
    for(size_t layer = 0; layer <= (size_t)storage.max_print_height_second_to_last_extruder + 1 && layer < storage.support.supportLayers.size(); layer++)
    {
        SupportLayer& support_layer = storage.support.supportLayers[layer];
        // take the differences of the support infill parts and the prime tower area
        support_layer.excludeAreasFromSupportInfillAreas(outside_polygon, outside_polygon_boundary_box);
    }
}

void PrimeTower::gotoStartLocation(const SliceDataStorage& storage, LayerPlan& gcode_layer, const int extruder_nr) const
{
    // start location index = ([Ei + 1] * abs([LR]) ) mod [DOT_COUNT]
    // where:
    //  - [Ei] is the current extruder index starting from 0
    //  - [LR] is the current layer number (converted)
    //  - [DOT_COUNT] is the total number of dots around the prime tower's perimeter.

    // Because layer number can be negative, it needs to be converted to a non-negative number.
    int lr = gcode_layer.getLayerNr();
    int ei = extruder_nr + 1;
    if (lr < 0)
    {
        // Treat extruder index on an negative layer in the reversed order, i.e.
        //    [DOT_COUNT] - (index mod [DOT_COUNT])
        // to above having the same starting location for layers such as -1 and 1, -2 and 2, etc.
        ei = number_of_prime_tower_start_locations - (ei % number_of_prime_tower_start_locations);
        lr = -lr;
    }
    int current_start_location_idx = ((ei) * lr) % number_of_prime_tower_start_locations;

    const ClosestPolygonPoint wipe_location = prime_tower_start_locations[current_start_location_idx];

    const ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder_nr);
    const coord_t inward_dist = train->getSettingInMicrons("machine_nozzle_size") * 3 / 2 ;
    const coord_t start_dist = train->getSettingInMicrons("machine_nozzle_size") * 2;
    const Point prime_end = PolygonUtils::moveInsideDiagonally(wipe_location, inward_dist);
    const Point outward_dir = wipe_location.location - prime_end;
    const Point prime_start = wipe_location.location + normal(outward_dir, start_dist);

    gcode_layer.addTravel(prime_start);
}

}//namespace cura
<commit_msg>Simplify calculation of start position when layers are negative.<commit_after>//Copyright (c) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "PrimeTower.h"

#include <algorithm>
#include <limits>

#include "ExtruderTrain.h"
#include "sliceDataStorage.h"
#include "gcodeExport.h"
#include "LayerPlan.h"
#include "infill.h"
#include "PrintFeature.h"
#include "raft.h"

#define CIRCLE_RESOLUTION 32 //The number of vertices in each circle.

namespace cura
{

PrimeTower::PrimeTower(const SliceDataStorage& storage)
: wipe_from_middle(false)
{
    enabled = storage.getSettingBoolean("prime_tower_enable")
           && storage.getSettingInMicrons("prime_tower_min_volume") > 10
           && storage.getSettingInMicrons("prime_tower_size") > 10;

    extruder_count = storage.meshgroup->getExtruderCount();
    extruder_order.resize(extruder_count);
    for (unsigned int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
    {
        extruder_order[extruder_nr] = extruder_nr; //Start with default order, then sort.
    }
    //Sort from high adhesion to low adhesion.
    const SliceDataStorage* storage_ptr = &storage; //Communicate to lambda via pointer to prevent copy.
    std::sort(extruder_order.begin(), extruder_order.end(), [storage_ptr](const unsigned int& extruder_nr_a, const unsigned int& extruder_nr_b) -> bool
    {
        const double adhesion_a = storage_ptr->meshgroup->getExtruderTrain(extruder_nr_a)->getSettingAsRatio("material_adhesion_tendency");
        const double adhesion_b = storage_ptr->meshgroup->getExtruderTrain(extruder_nr_b)->getSettingAsRatio("material_adhesion_tendency");
        return adhesion_a < adhesion_b;
    });
}

void PrimeTower::generateGroundpoly(const SliceDataStorage& storage)
{
    if (!enabled)
    {
        return;
    }

    coord_t tower_size = storage.getSettingInMicrons("prime_tower_size");
    bool circular_prime_tower = storage.getSettingBoolean("prime_tower_circular");

    PolygonRef p = outer_poly.newPoly();
    int tower_distance = 0;
    int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
    int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
    if (circular_prime_tower)
    {
        double_t tower_radius = tower_size / 2;
        for (unsigned int i = 0; i < CIRCLE_RESOLUTION; i++)
        {
            const double angle = (double) i / CIRCLE_RESOLUTION * 2 * M_PI; //In radians.
            p.add(Point(x - tower_radius + tower_distance + cos(angle) * tower_radius,
                        y + tower_radius + tower_distance + sin(angle) * tower_radius));
        }
    }
    else
    {
        p.add(Point(x + tower_distance, y + tower_distance));
        p.add(Point(x + tower_distance, y + tower_distance + tower_size));
        p.add(Point(x + tower_distance - tower_size, y + tower_distance + tower_size));
        p.add(Point(x + tower_distance - tower_size, y + tower_distance));
    }
    middle = Point(x - tower_size / 2, y + tower_size / 2);

    post_wipe_point = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
}

void PrimeTower::generatePaths(const SliceDataStorage& storage)
{
    enabled &= storage.max_print_height_second_to_last_extruder >= 0; //Maybe it turns out that we don't need a prime tower after all because there are no layer switches.
    if (enabled)
    {
        generatePaths_denseInfill(storage);
        generateStartLocations();
    }
}

void PrimeTower::generatePaths_denseInfill(const SliceDataStorage& storage)
{
    const coord_t layer_height = storage.getSettingInMicrons("layer_height");
    pattern_per_extruder.resize(extruder_count);

    coord_t cumulative_inset = 0; //Each tower shape is going to be printed inside the other. This is the inset we're doing for each extruder.
    for (unsigned int extruder : extruder_order)
    {
        const coord_t line_width = storage.meshgroup->getExtruderTrain(extruder)->getSettingInMicrons("prime_tower_line_width");
        const coord_t required_volume = storage.meshgroup->getExtruderTrain(extruder)->getSettingInCubicMillimeters("prime_tower_min_volume") * 1000000000; //To cubic microns.
        const double flow = storage.meshgroup->getExtruderTrain(extruder)->getSettingAsRatio("prime_tower_flow");
        coord_t current_volume = 0;
        ExtrusionMoves& pattern = pattern_per_extruder[extruder];

        //Create the walls of the prime tower.
        unsigned int wall_nr = 0;
        for (; current_volume < required_volume; wall_nr++)
        {
            //Create a new polygon with an offset from the outer polygon.
            Polygons polygons = outer_poly.offset(-cumulative_inset - wall_nr * line_width - line_width / 2);
            pattern.polygons.add(polygons);
            current_volume += polygons.polygonLength() * line_width * layer_height * flow;
            if (polygons.empty()) //Don't continue. We won't ever reach the required volume because it doesn't fit.
            {
                break;
            }
        }
        cumulative_inset += wall_nr * line_width;

        //Generate the pattern for the first layer.
        coord_t line_width_layer0 = line_width;
        if (storage.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::RAFT)
        {
            line_width_layer0 *= storage.meshgroup->getExtruderTrain(extruder)->getSettingAsRatio("initial_layer_line_width_factor");
        }
        pattern_per_extruder_layer0.emplace_back();

        ExtrusionMoves& pattern_layer0 = pattern_per_extruder_layer0.back();

        // Generate a concentric infill pattern in the form insets for the prime tower's first layer instead of using
        // the infill pattern because the infill pattern tries to connect polygons in different insets which causes the
        // first layer of the prime tower to not stick well.
        Polygons inset = outer_poly.offset(-line_width_layer0 / 2);
        while (!inset.empty())
        {
            pattern_layer0.polygons.add(inset);
            inset = inset.offset(-line_width_layer0);
        }
    }
}

void PrimeTower::generateStartLocations()
{
    // Evenly spread out a number of dots along the prime tower's outline. This is done for the complete outline,
    // so use the same start and end segments for this.
    PolygonsPointIndex segment_start = PolygonsPointIndex(&outer_poly, 0, 0);
    PolygonsPointIndex segment_end = segment_start;

    PolygonUtils::spreadDots(segment_start, segment_end, number_of_prime_tower_start_locations, prime_tower_start_locations);
}

void PrimeTower::addToGcode(const SliceDataStorage& storage, LayerPlan& gcode_layer, const GCodeExport& gcode, const int prev_extruder, const int new_extruder) const
{
    if (!enabled)
    {
        return;
    }
    if (gcode_layer.getPrimeTowerIsPlanned(new_extruder))
    { // don't print the prime tower if it has been printed already with this extruder.
        return;
    }

    if (gcode_layer.getLayerNr() > storage.max_print_height_second_to_last_extruder + 1)
    {
        return;
    }

    bool post_wipe = storage.meshgroup->getExtruderTrain(prev_extruder)->getSettingBoolean("prime_tower_wipe_enabled");

    // Do not wipe on the first layer, we will generate non-hollow prime tower there for better bed adhesion.
    const int layer_nr = gcode_layer.getLayerNr();
    if (prev_extruder == new_extruder || layer_nr == 0)
    {
        post_wipe = false;
    }

    // Go to the start location if it's not the first layer
    if (layer_nr != 0)
    {
        gotoStartLocation(storage, gcode_layer, new_extruder);
    }

    addToGcode_denseInfill(storage, gcode_layer, new_extruder);

    // post-wipe:
    if (post_wipe)
    { //Make sure we wipe the old extruder on the prime tower.
        gcode_layer.addTravel(post_wipe_point - gcode.getExtruderOffset(prev_extruder) + gcode.getExtruderOffset(new_extruder));
    }

    gcode_layer.setPrimeTowerIsPlanned(new_extruder);
}

void PrimeTower::addToGcode_denseInfill(const SliceDataStorage& storage, LayerPlan& gcode_layer, const int extruder_nr) const
{
    const ExtrusionMoves& pattern = (gcode_layer.getLayerNr() == -Raft::getFillerLayerCount(storage))
        ? pattern_per_extruder_layer0[extruder_nr]
        : pattern_per_extruder[extruder_nr];

    const GCodePathConfig& config = gcode_layer.configs_storage.prime_tower_config_per_extruder[extruder_nr];

    gcode_layer.addPolygonsByOptimizer(pattern.polygons, config);
    gcode_layer.addLinesByOptimizer(pattern.lines, config, SpaceFillType::Lines);
}

Point PrimeTower::getLocationBeforePrimeTower(const SliceDataStorage& storage) const
{
    Point ret(0, 0);
    int absolute_starting_points = 0;
    for (unsigned int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); extruder_nr++)
    {
        ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(0);
        if (train.getSettingBoolean("machine_extruder_start_pos_abs"))
        {
            ret += Point(train.getSettingInMicrons("machine_extruder_start_pos_x"), train.getSettingInMicrons("machine_extruder_start_pos_y"));
            absolute_starting_points++;
        }
    }
    if (absolute_starting_points > 0)
    { // take the average over all absolute starting positions
        ret /= absolute_starting_points;
    }
    else
    { // use the middle of the bed
        ret = storage.machine_size.flatten().getMiddle();
    }
    return ret;
}

void PrimeTower::subtractFromSupport(SliceDataStorage& storage)
{
    const Polygons outside_polygon = outer_poly.getOutsidePolygons();
    AABB outside_polygon_boundary_box(outside_polygon);
    for(size_t layer = 0; layer <= (size_t)storage.max_print_height_second_to_last_extruder + 1 && layer < storage.support.supportLayers.size(); layer++)
    {
        SupportLayer& support_layer = storage.support.supportLayers[layer];
        // take the differences of the support infill parts and the prime tower area
        support_layer.excludeAreasFromSupportInfillAreas(outside_polygon, outside_polygon_boundary_box);
    }
}

void PrimeTower::gotoStartLocation(const SliceDataStorage& storage, LayerPlan& gcode_layer, const int extruder_nr) const
{
    int current_start_location_idx = ((((extruder_nr + 1) * gcode_layer.getLayerNr()) % number_of_prime_tower_start_locations)
            + number_of_prime_tower_start_locations) % number_of_prime_tower_start_locations;

    const ClosestPolygonPoint wipe_location = prime_tower_start_locations[current_start_location_idx];

    const ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder_nr);
    const coord_t inward_dist = train->getSettingInMicrons("machine_nozzle_size") * 3 / 2 ;
    const coord_t start_dist = train->getSettingInMicrons("machine_nozzle_size") * 2;
    const Point prime_end = PolygonUtils::moveInsideDiagonally(wipe_location, inward_dist);
    const Point outward_dir = wipe_location.location - prime_end;
    const Point prime_start = wipe_location.location + normal(outward_dir, start_dist);

    gcode_layer.addTravel(prime_start);
}

}//namespace cura
<|endoftext|>


<commit_before>/* dlvhex -- Answer-Set Programming with external interfaces.
 * Copyright (C) 2005, 2006, 2007 Roman Schindlauer
 * Copyright (C) 2006, 2007, 2008, 2009, 2010 Thomas Krennwallner
 * Copyright (C) 2009, 2010 Peter Schüller
 *
 * This file is part of dlvhex.
 *
 * dlvhex 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 License, or (at your option) any later version.
 *
 * dlvhex 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 dlvhex; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 */

/**
 * @file ProgramCtx.cpp
 * @author Thomas Krennwallner
 * @date
 *
 * @brief Program context.
 *
 *
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H

#include "dlvhex2/ProgramCtx.h"
#include "dlvhex2/Registry.h"
#include "dlvhex2/PluginContainer.h"
#include "dlvhex2/State.h"
//#include "dlvhex2/DLVProcess.h"
#include "dlvhex2/EvalHeuristicEasy.h"

#include <boost/shared_ptr.hpp>

#include <sstream>
#include <iostream>

DLVHEX_NAMESPACE_BEGIN

ProgramCtx::ProgramCtx():
		maxint(0), onlyBestModels(false)
{
}

ProgramCtx::~ProgramCtx()
{
  DBGLOG(DBG,"resetting state");
  state.reset();

  DBGLOG(DBG,"resetting callbacks");
  modelCallbacks.clear();
  finalCallbacks.clear();

  DBGLOG(DBG,"resetting modelBuilder");
  modelBuilder.reset();

  DBGLOG(DBG,"resetting parser");
  parser.reset();

  DBGLOG(DBG,"resetting evalgraph");
  evalgraph.reset();

  DBGLOG(DBG,"resetting compgraph");
  compgraph.reset();

  DBGLOG(DBG,"resetting depgraph");
  depgraph.reset();

  DBGLOG(DBG,"resetting edbList");
  std::vector<InterpretationPtr>::iterator it = edbList.begin();
  while ( it != edbList.end() )
    {
      it->reset();
      it++;
    }
  edb.reset();

  DBGLOG(DBG,"resetting inputProvider");
  inputProvider.reset();

  DBGLOG(DBG,"resetting aspsoftware");
  aspsoftware.reset();

  DBGLOG(DBG,"resetting pluginData");
  pluginData.clear();

  DBGLOG(DBG,"resetting pluginEnvironment");
  pluginEnvironment.clear();

  DBGLOG(DBG,"resetting registry, usage count was " << _registry.use_count() << " (it should be 2)");
  // not printing, it creates too much clutter
	//if( Logger::Instance().shallPrint(Logger::DBG) )
	//	_registry->print(Logger::Instance().stream()) << std::endl;
  _registry.reset();

  DBGLOG(DBG,"resetting pluginAtoms");
  pluginAtoms.clear();

  DBGLOG(DBG,"resetting pluginContainer, usage count was " << _pluginContainer.use_count() << " (it should be 1)");
  _pluginContainer.reset();
}

void
ProgramCtx::changeState(const boost::shared_ptr<State>& s)
{
  state = s;
}

// cannot change registry if something is already stored here
void ProgramCtx::setupRegistry(
    RegistryPtr registry)
{
  assert(
      (
        !_registry || // allow to set from nothing
        (idb.empty() && !edb && idbList.size()==0 && edbList.size()==0 && pluginAtoms.empty()) // allow to change if empty
      )
      &&
      "cannot change registry once idb or edb or pluginAtoms contains data");
  _registry = registry;
  _registry->setupAuxiliaryGroundAtomMask();
}

void ProgramCtx::changeRegistry(RegistryPtr registry)
{
  // clear everything that depends on IDs of registry
  idb.clear();
  edb.reset();
  idbList.clear();
  edbList.clear();
  pluginAtoms.clear();

  // setup new registry
  setupRegistry(registry);

  // re-add plugin atoms (using new registry)
  addPluginAtomsFromPluginContainer();
}

void ProgramCtx::setupPluginContainer(
    PluginContainerPtr pluginContainer)
{
  assert(
      (
        !_pluginContainer || // allow to set if unset
        pluginAtoms.empty() // allow to change if no atoms stored
      )
      &&
      "cannot change pluginContainer once pluginAtoms are used");
  _pluginContainer = pluginContainer;
  #warning here we could reset the pointers in all ExternalAtoms if we unset the pluginContainer
}

ASPSolverManager::SoftwareConfigurationPtr
ProgramCtx::getASPSoftware() const
{
  assert(aspsoftware != 0);
  return aspsoftware;
}

void ProgramCtx::setASPSoftware(ASPSolverManager::SoftwareConfigurationPtr software)
{
  aspsoftware = software;
}

void ProgramCtx::showPlugins() { state->showPlugins(this); }
void ProgramCtx::convert() { state->convert(this); }
void ProgramCtx::parse() { state->parse(this); }
void ProgramCtx::moduleSyntaxCheck() { state->moduleSyntaxCheck(this); }
void ProgramCtx::mlpSolver() { state->mlpSolver(this); }
void ProgramCtx::rewriteEDBIDB() { state->rewriteEDBIDB(this); }
void ProgramCtx::safetyCheck() { state->safetyCheck(this); }
void ProgramCtx::createDependencyGraph() { state->createDependencyGraph(this); }
void ProgramCtx::createAttributeGraph() { state->createAttributeGraph(this); }
void ProgramCtx::optimizeEDBDependencyGraph() { state->optimizeEDBDependencyGraph(this); }
void ProgramCtx::createComponentGraph() { state->createComponentGraph(this); }
void ProgramCtx::strongSafetyCheck() { state->strongSafetyCheck(this); }
void ProgramCtx::createEvalGraph() { state->createEvalGraph(this); }
void ProgramCtx::setupProgramCtx() { state->setupProgramCtx(this); }
void ProgramCtx::evaluate() { state->evaluate(this); }
void ProgramCtx::postProcess() { state->postProcess(this); }

// ============================== subprogram handling ==============================

bool ProgramCtx::SubprogramAnswerSetCallback::operator()(AnswerSetPtr model){
	answersets.push_back(model->interpretation);
	return true;
}

ProgramCtx::SubprogramAnswerSetCallback::~SubprogramAnswerSetCallback(){}

std::vector<InterpretationPtr> ProgramCtx::evaluateSubprogram(InterpretationConstPtr edb, std::vector<ID>& idb){

	ProgramCtx pc = *this;
//	pc.changeRegistry(this->registry());
	pc.idb = idb;
	pc.edb = InterpretationPtr(new Interpretation(*edb));
	pc.currentOptimum.clear();

	return evaluateSubprogram(pc, false);
}

std::vector<InterpretationPtr> ProgramCtx::evaluateSubprogram(InputProviderPtr& ip, InterpretationConstPtr addFacts){

	ProgramCtx pc = *this;
//	pc.changeRegistry(this->registry());
	pc.idb.clear();
	pc.edb = InterpretationPtr(new Interpretation(this->registry()));
	pc.currentOptimum.clear();
	pc.config.setOption("NumberOfModels",0);
	if( !!addFacts )
		pc.edb->getStorage() |= addFacts->getStorage();
	pc.inputProvider = ip;
	ip.reset();

	return evaluateSubprogram(pc, true);
}

std::vector<InterpretationPtr> ProgramCtx::evaluateSubprogram(ProgramCtx& pc, bool parse){

	DBGLOG(DBG, "Resetting context");
	pc.state.reset();
	pc.modelBuilder.reset();
	pc.parser.reset();
	pc.evalgraph.reset();
	pc.compgraph.reset();
	pc.depgraph.reset();
//	pc.modelBuilderFactory = ctx->modelBuilderFactory;

  if( !pc.evalHeuristic )
  {
    DBGLOG(DBG, "Setting eval heuristics");
    pc.evalHeuristic.reset(new EvalHeuristicEasy);
  }

	DBGLOG(DBG, "Starting state pipeline " << (parse ? "with" : "without") << " parsing");
	if (parse){
		pc.changeState(StatePtr(new ConvertState));
	}else{
		pc.changeState(StatePtr(new SafetyCheckState));
	}

	if (parse){
		pc.convert();
		pc.parse();
	}

	DBGLOG(DBG, "Associate PluginAtom instances with ExternalAtom instances");
	pc.associateExtAtomsWithPluginAtoms(pc.idb, true);

	pc.safetyCheck();
	pc.createAttributeGraph();
	pc.createDependencyGraph();
	pc.optimizeEDBDependencyGraph();
	pc.createComponentGraph();
	pc.createEvalGraph();
	pc.setupProgramCtx();

	DBGLOG(DBG, "Setting AnswerSetCallback");
	pc.modelCallbacks.clear();
	pc.finalCallbacks.clear();
	SubprogramAnswerSetCallback* spasc = new SubprogramAnswerSetCallback();
	ModelCallbackPtr spascp = ModelCallbackPtr(spasc);
	pc.modelCallbacks.push_back(spascp);

	DBGLOG(DBG, "Evaluate subprogram");
	pc.evaluate();
	std::vector<InterpretationPtr> result;
	BOOST_FOREACH (InterpretationPtr intr, spasc->answersets){
		result.push_back(intr);
	}

	return result;
}

// ============================== end subprogram handling ==============================

#if 0
PluginAtomPtr
PluginContainer::getAtom(const std::string& name) const
{
  PluginAtomMap::const_iterator pa = pluginAtoms.find(name);

  if (pa == pluginAtoms.end())
    return PluginAtomPtr();

  return pa->second;
}

#endif

void ProgramCtx::addPluginAtom(PluginAtomPtr atom)
{
  assert(!!atom);
  assert(!!_registry);
  const std::string& predicate = atom->getPredicate();
  LOG(PLUGIN,"adding PluginAtom '" << predicate << "'");
  if( pluginAtoms.find(predicate) == pluginAtoms.end() )
  {
    atom->setRegistry(_registry);
    pluginAtoms[predicate] = atom;
  }
  else
  {
    LOG(WARNING,"External atom " << predicate << " is already loaded (skipping)");
  }
}

// call processOptions for each loaded plugin
// (this is supposed to remove "recognized" options from pluginOptions)
void ProgramCtx::processPluginOptions(
    std::list<const char*>& pluginOptions)
{
  BOOST_FOREACH(PluginInterfacePtr plugin, pluginContainer()->getPlugins())
  {
    LOG(DBG,"processing options for plugin " << plugin->getPluginName());
    LOG(DBG,"currently have " << printrange(pluginOptions));
	  plugin->processOptions(pluginOptions, *this);
  }
}

void ProgramCtx::addPluginAtomsFromPluginContainer()
{
  assert(!!pluginContainer());
  assert(!!registry());

  BOOST_FOREACH(PluginInterfacePtr plugin, pluginContainer()->getPlugins())
  {
    LOG(DBG,"adding plugin atoms from plugin " << plugin->getPluginName());
    // always freshly create! (pluginatoms are linked to a registry,
    // so when using multiple registries, you have to create multiple pluginatoms)
    BOOST_FOREACH(PluginAtomPtr pap, plugin->createAtoms(*this))
    {
      assert(!!pap);
      const std::string& pred = pap->getPredicate();
      LOG(DBG,"  got plugin atom " << pred);
      if( pluginAtoms.count(pred) != 0 )
      {
        LOG(WARNING,"warning: predicate '" << pred << "' already present in PluginAtomMap (skipping)");
      }
      else
      {
        pap->setRegistry(registry());
        pluginAtoms[pred] = pap;
      }
    }
  }
}

// associate plugins in container to external atoms in registry
void ProgramCtx::associateExtAtomsWithPluginAtoms(
    const Tuple& idb, bool failOnUnknownAtom)
{
  assert(!!_registry);
  DBGLOG_SCOPE(DBG,"aEAwPA",false);
  DBGLOG(DBG,"= associateExtAtomsWithPluginAtoms");

  Tuple eatoms;

  // associate all rules
  for(Tuple::const_iterator it = idb.begin();
      it != idb.end(); ++it)
  {
    assert(it->isRule());
    // skip those without external atoms
    if( !it->doesRuleContainExtatoms() )
      continue;

    // associate all literals in rule body
    const Rule& rule = _registry->rules.getByID(*it);

    // get external atoms (recursively)
    _registry->getExternalAtomsInTuple(rule.body, eatoms);
  }

  // now associate
  for(Tuple::const_iterator it = eatoms.begin();
      it != eatoms.end(); ++it)
  {
    assert(it->isExternalAtom());

    const ExternalAtom& eatom = _registry->eatoms.getByID(*it);
    const std::string& predicate = _registry->getTermStringByID(eatom.predicate);
    // lookup pluginAtom to this eatom predicate
    PluginAtomMap::iterator itpa = pluginAtoms.find(predicate);
    if( itpa != pluginAtoms.end() )
    {
      assert(!!itpa->second);
      // we store this as a POD pointer!
      eatom.pluginAtom = itpa->second.get();
      eatom.prop |= itpa->second->getExtSourceProperties();
      eatom.pluginAtom->setupProperties(eatom);
    }
    else
    {
      DBGLOG(DBG,"did not find plugin atom for predicate '" << predicate << "'");
      if( failOnUnknownAtom )
      {
        throw FatalError("did not find plugin atom "
            " for predicate '" + predicate + "'");
      }
    }
  }
}

// setup this ProgramCtx (using setupProgramCtx() for of all plugins)
void ProgramCtx::setupByPlugins()
{
  BOOST_FOREACH(PluginInterfacePtr plugin, pluginContainer()->getPlugins())
  {
    LOG(DBG,"setting up program ctx for plugin " << plugin->getPluginName());
	  plugin->setupProgramCtx(*this);
  }
}

// reset the cache of Plugins that use Environment
void ProgramCtx::resetCacheOfPlugins()
{
	typedef std::pair<std::string, PluginAtomPtr> pairPluginAtomMap;
	BOOST_FOREACH(pairPluginAtomMap p, pluginAtoms)
		if(p.second->getExtSourceProperties().doesItUseEnvironment())
			p.second->resetCache();
}

DLVHEX_NAMESPACE_END

// Local Variables:
// mode: C++
// End:
<commit_msg>forgot to initialize terminationRequest<commit_after>/* dlvhex -- Answer-Set Programming with external interfaces.
 * Copyright (C) 2005, 2006, 2007 Roman Schindlauer
 * Copyright (C) 2006, 2007, 2008, 2009, 2010 Thomas Krennwallner
 * Copyright (C) 2009, 2010 Peter Schüller
 *
 * This file is part of dlvhex.
 *
 * dlvhex 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 License, or (at your option) any later version.
 *
 * dlvhex 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 dlvhex; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 */

/**
 * @file ProgramCtx.cpp
 * @author Thomas Krennwallner
 * @date
 *
 * @brief Program context.
 *
 *
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H

#include "dlvhex2/ProgramCtx.h"
#include "dlvhex2/Registry.h"
#include "dlvhex2/PluginContainer.h"
#include "dlvhex2/State.h"
//#include "dlvhex2/DLVProcess.h"
#include "dlvhex2/EvalHeuristicEasy.h"

#include <boost/shared_ptr.hpp>

#include <sstream>
#include <iostream>

DLVHEX_NAMESPACE_BEGIN

ProgramCtx::ProgramCtx():
		maxint(0), onlyBestModels(false), terminationRequest(false)
{
}

ProgramCtx::~ProgramCtx()
{
  DBGLOG(DBG,"resetting state");
  state.reset();

  DBGLOG(DBG,"resetting callbacks");
  modelCallbacks.clear();
  finalCallbacks.clear();

  DBGLOG(DBG,"resetting modelBuilder");
  modelBuilder.reset();

  DBGLOG(DBG,"resetting parser");
  parser.reset();

  DBGLOG(DBG,"resetting evalgraph");
  evalgraph.reset();

  DBGLOG(DBG,"resetting compgraph");
  compgraph.reset();

  DBGLOG(DBG,"resetting depgraph");
  depgraph.reset();

  DBGLOG(DBG,"resetting edbList");
  std::vector<InterpretationPtr>::iterator it = edbList.begin();
  while ( it != edbList.end() )
    {
      it->reset();
      it++;
    }
  edb.reset();

  DBGLOG(DBG,"resetting inputProvider");
  inputProvider.reset();

  DBGLOG(DBG,"resetting aspsoftware");
  aspsoftware.reset();

  DBGLOG(DBG,"resetting pluginData");
  pluginData.clear();

  DBGLOG(DBG,"resetting pluginEnvironment");
  pluginEnvironment.clear();

  DBGLOG(DBG,"resetting registry, usage count was " << _registry.use_count() << " (it should be 2)");
  // not printing, it creates too much clutter
	//if( Logger::Instance().shallPrint(Logger::DBG) )
	//	_registry->print(Logger::Instance().stream()) << std::endl;
  _registry.reset();

  DBGLOG(DBG,"resetting pluginAtoms");
  pluginAtoms.clear();

  DBGLOG(DBG,"resetting pluginContainer, usage count was " << _pluginContainer.use_count() << " (it should be 1)");
  _pluginContainer.reset();
}

void
ProgramCtx::changeState(const boost::shared_ptr<State>& s)
{
  state = s;
}

// cannot change registry if something is already stored here
void ProgramCtx::setupRegistry(
    RegistryPtr registry)
{
  assert(
      (
        !_registry || // allow to set from nothing
        (idb.empty() && !edb && idbList.size()==0 && edbList.size()==0 && pluginAtoms.empty()) // allow to change if empty
      )
      &&
      "cannot change registry once idb or edb or pluginAtoms contains data");
  _registry = registry;
  _registry->setupAuxiliaryGroundAtomMask();
}

void ProgramCtx::changeRegistry(RegistryPtr registry)
{
  // clear everything that depends on IDs of registry
  idb.clear();
  edb.reset();
  idbList.clear();
  edbList.clear();
  pluginAtoms.clear();

  // setup new registry
  setupRegistry(registry);

  // re-add plugin atoms (using new registry)
  addPluginAtomsFromPluginContainer();
}

void ProgramCtx::setupPluginContainer(
    PluginContainerPtr pluginContainer)
{
  assert(
      (
        !_pluginContainer || // allow to set if unset
        pluginAtoms.empty() // allow to change if no atoms stored
      )
      &&
      "cannot change pluginContainer once pluginAtoms are used");
  _pluginContainer = pluginContainer;
  #warning here we could reset the pointers in all ExternalAtoms if we unset the pluginContainer
}

ASPSolverManager::SoftwareConfigurationPtr
ProgramCtx::getASPSoftware() const
{
  assert(aspsoftware != 0);
  return aspsoftware;
}

void ProgramCtx::setASPSoftware(ASPSolverManager::SoftwareConfigurationPtr software)
{
  aspsoftware = software;
}

void ProgramCtx::showPlugins() { state->showPlugins(this); }
void ProgramCtx::convert() { state->convert(this); }
void ProgramCtx::parse() { state->parse(this); }
void ProgramCtx::moduleSyntaxCheck() { state->moduleSyntaxCheck(this); }
void ProgramCtx::mlpSolver() { state->mlpSolver(this); }
void ProgramCtx::rewriteEDBIDB() { state->rewriteEDBIDB(this); }
void ProgramCtx::safetyCheck() { state->safetyCheck(this); }
void ProgramCtx::createDependencyGraph() { state->createDependencyGraph(this); }
void ProgramCtx::createAttributeGraph() { state->createAttributeGraph(this); }
void ProgramCtx::optimizeEDBDependencyGraph() { state->optimizeEDBDependencyGraph(this); }
void ProgramCtx::createComponentGraph() { state->createComponentGraph(this); }
void ProgramCtx::strongSafetyCheck() { state->strongSafetyCheck(this); }
void ProgramCtx::createEvalGraph() { state->createEvalGraph(this); }
void ProgramCtx::setupProgramCtx() { state->setupProgramCtx(this); }
void ProgramCtx::evaluate() { state->evaluate(this); }
void ProgramCtx::postProcess() { state->postProcess(this); }

// ============================== subprogram handling ==============================

bool ProgramCtx::SubprogramAnswerSetCallback::operator()(AnswerSetPtr model){
	answersets.push_back(model->interpretation);
	return true;
}

ProgramCtx::SubprogramAnswerSetCallback::~SubprogramAnswerSetCallback(){}

std::vector<InterpretationPtr> ProgramCtx::evaluateSubprogram(InterpretationConstPtr edb, std::vector<ID>& idb){

	ProgramCtx pc = *this;
//	pc.changeRegistry(this->registry());
	pc.idb = idb;
	pc.edb = InterpretationPtr(new Interpretation(*edb));
	pc.currentOptimum.clear();

	return evaluateSubprogram(pc, false);
}

std::vector<InterpretationPtr> ProgramCtx::evaluateSubprogram(InputProviderPtr& ip, InterpretationConstPtr addFacts){

	ProgramCtx pc = *this;
//	pc.changeRegistry(this->registry());
	pc.idb.clear();
	pc.edb = InterpretationPtr(new Interpretation(this->registry()));
	pc.currentOptimum.clear();
	pc.config.setOption("NumberOfModels",0);
	if( !!addFacts )
		pc.edb->getStorage() |= addFacts->getStorage();
	pc.inputProvider = ip;
	ip.reset();

	return evaluateSubprogram(pc, true);
}

std::vector<InterpretationPtr> ProgramCtx::evaluateSubprogram(ProgramCtx& pc, bool parse){

	DBGLOG(DBG, "Resetting context");
	pc.state.reset();
	pc.modelBuilder.reset();
	pc.parser.reset();
	pc.evalgraph.reset();
	pc.compgraph.reset();
	pc.depgraph.reset();
//	pc.modelBuilderFactory = ctx->modelBuilderFactory;

  if( !pc.evalHeuristic )
  {
    DBGLOG(DBG, "Setting eval heuristics");
    pc.evalHeuristic.reset(new EvalHeuristicEasy);
  }

	DBGLOG(DBG, "Starting state pipeline " << (parse ? "with" : "without") << " parsing");
	if (parse){
		pc.changeState(StatePtr(new ConvertState));
	}else{
		pc.changeState(StatePtr(new SafetyCheckState));
	}

	if (parse){
		pc.convert();
		pc.parse();
	}

	DBGLOG(DBG, "Associate PluginAtom instances with ExternalAtom instances");
	pc.associateExtAtomsWithPluginAtoms(pc.idb, true);

	pc.safetyCheck();
	pc.createAttributeGraph();
	pc.createDependencyGraph();
	pc.optimizeEDBDependencyGraph();
	pc.createComponentGraph();
	pc.createEvalGraph();
	pc.setupProgramCtx();

	DBGLOG(DBG, "Setting AnswerSetCallback");
	pc.modelCallbacks.clear();
	pc.finalCallbacks.clear();
	SubprogramAnswerSetCallback* spasc = new SubprogramAnswerSetCallback();
	ModelCallbackPtr spascp = ModelCallbackPtr(spasc);
	pc.modelCallbacks.push_back(spascp);

	DBGLOG(DBG, "Evaluate subprogram");
	pc.evaluate();
	std::vector<InterpretationPtr> result;
	BOOST_FOREACH (InterpretationPtr intr, spasc->answersets){
		result.push_back(intr);
	}

	return result;
}

// ============================== end subprogram handling ==============================

#if 0
PluginAtomPtr
PluginContainer::getAtom(const std::string& name) const
{
  PluginAtomMap::const_iterator pa = pluginAtoms.find(name);

  if (pa == pluginAtoms.end())
    return PluginAtomPtr();

  return pa->second;
}

#endif

void ProgramCtx::addPluginAtom(PluginAtomPtr atom)
{
  assert(!!atom);
  assert(!!_registry);
  const std::string& predicate = atom->getPredicate();
  LOG(PLUGIN,"adding PluginAtom '" << predicate << "'");
  if( pluginAtoms.find(predicate) == pluginAtoms.end() )
  {
    atom->setRegistry(_registry);
    pluginAtoms[predicate] = atom;
  }
  else
  {
    LOG(WARNING,"External atom " << predicate << " is already loaded (skipping)");
  }
}

// call processOptions for each loaded plugin
// (this is supposed to remove "recognized" options from pluginOptions)
void ProgramCtx::processPluginOptions(
    std::list<const char*>& pluginOptions)
{
  BOOST_FOREACH(PluginInterfacePtr plugin, pluginContainer()->getPlugins())
  {
    LOG(DBG,"processing options for plugin " << plugin->getPluginName());
    LOG(DBG,"currently have " << printrange(pluginOptions));
	  plugin->processOptions(pluginOptions, *this);
  }
}

void ProgramCtx::addPluginAtomsFromPluginContainer()
{
  assert(!!pluginContainer());
  assert(!!registry());

  BOOST_FOREACH(PluginInterfacePtr plugin, pluginContainer()->getPlugins())
  {
    LOG(DBG,"adding plugin atoms from plugin " << plugin->getPluginName());
    // always freshly create! (pluginatoms are linked to a registry,
    // so when using multiple registries, you have to create multiple pluginatoms)
    BOOST_FOREACH(PluginAtomPtr pap, plugin->createAtoms(*this))
    {
      assert(!!pap);
      const std::string& pred = pap->getPredicate();
      LOG(DBG,"  got plugin atom " << pred);
      if( pluginAtoms.count(pred) != 0 )
      {
        LOG(WARNING,"warning: predicate '" << pred << "' already present in PluginAtomMap (skipping)");
      }
      else
      {
        pap->setRegistry(registry());
        pluginAtoms[pred] = pap;
      }
    }
  }
}

// associate plugins in container to external atoms in registry
void ProgramCtx::associateExtAtomsWithPluginAtoms(
    const Tuple& idb, bool failOnUnknownAtom)
{
  assert(!!_registry);
  DBGLOG_SCOPE(DBG,"aEAwPA",false);
  DBGLOG(DBG,"= associateExtAtomsWithPluginAtoms");

  Tuple eatoms;

  // associate all rules
  for(Tuple::const_iterator it = idb.begin();
      it != idb.end(); ++it)
  {
    assert(it->isRule());
    // skip those without external atoms
    if( !it->doesRuleContainExtatoms() )
      continue;

    // associate all literals in rule body
    const Rule& rule = _registry->rules.getByID(*it);

    // get external atoms (recursively)
    _registry->getExternalAtomsInTuple(rule.body, eatoms);
  }

  // now associate
  for(Tuple::const_iterator it = eatoms.begin();
      it != eatoms.end(); ++it)
  {
    assert(it->isExternalAtom());

    const ExternalAtom& eatom = _registry->eatoms.getByID(*it);
    const std::string& predicate = _registry->getTermStringByID(eatom.predicate);
    // lookup pluginAtom to this eatom predicate
    PluginAtomMap::iterator itpa = pluginAtoms.find(predicate);
    if( itpa != pluginAtoms.end() )
    {
      assert(!!itpa->second);
      // we store this as a POD pointer!
      eatom.pluginAtom = itpa->second.get();
      eatom.prop |= itpa->second->getExtSourceProperties();
      eatom.pluginAtom->setupProperties(eatom);
    }
    else
    {
      DBGLOG(DBG,"did not find plugin atom for predicate '" << predicate << "'");
      if( failOnUnknownAtom )
      {
        throw FatalError("did not find plugin atom "
            " for predicate '" + predicate + "'");
      }
    }
  }
}

// setup this ProgramCtx (using setupProgramCtx() for of all plugins)
void ProgramCtx::setupByPlugins()
{
  BOOST_FOREACH(PluginInterfacePtr plugin, pluginContainer()->getPlugins())
  {
    LOG(DBG,"setting up program ctx for plugin " << plugin->getPluginName());
	  plugin->setupProgramCtx(*this);
  }
}

// reset the cache of Plugins that use Environment
void ProgramCtx::resetCacheOfPlugins()
{
	typedef std::pair<std::string, PluginAtomPtr> pairPluginAtomMap;
	BOOST_FOREACH(pairPluginAtomMap p, pluginAtoms)
		if(p.second->getExtSourceProperties().doesItUseEnvironment())
			p.second->resetCache();
}

DLVHEX_NAMESPACE_END

// Local Variables:
// mode: C++
// End:
<|endoftext|>