<commit_before>#include <algorithm>

#include <QFileDialog>
#include <QMessageBox>

#include "species_dialog.h"
#include "metadata_dialog.h"
#include "annotatedregion.h"
#include "annotated_line.h"
#include "annotated_dot.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"

namespace fish_annotator { namespace image_annotator {

namespace fs = boost::filesystem;

namespace { //anonymous

static const std::vector<std::string> kDirExtensions = {
  ".jpg", ".png", ".bmp", ".tif", ".jpeg",
  ".JPG", ".PNG", ".BMP", ".TIF", ".JPEG"};

} // anonymous namespace

MainWindow::MainWindow(QWidget *parent)
  : annotations_(new ImageAnnotationList)
  , view_(new AnnotationView)
  , scene_(new AnnotationScene)
  , ui_(new Ui::MainWindow)
  , species_controls_(new SpeciesControls(this))
  , annotation_widget_(new AnnotationWidget(this))
  , global_state_widget_(new GlobalStateWidget(this))
  , image_files_()
  , metadata_()
  , species_()
  , subspecies_()
  , current_annotations_()
  , color_map_() {
  ui_->setupUi(this);
  setWindowTitle("Image Annotator");
#ifdef _WIN32
  setWindowIcon(QIcon(":/icons/cvision/cvision_no_text.ico"));
#endif
  ui_->next->setIcon(QIcon(":/icons/image_controls/next.svg"));
  ui_->prev->setIcon(QIcon(":/icons/image_controls/prev.svg"));
  ui_->videoWindowLayout->addWidget(view_.get());
  ui_->speciesLayout->addWidget(annotation_widget_.get());
  ui_->speciesLayout->addWidget(species_controls_.get());
  ui_->globalStateLayout->addWidget(global_state_widget_.get());
  tabifyDockWidget(
    ui_->globalStateDockWidget,
    ui_->navigationDockWidget);
  tabifyDockWidget(
    ui_->navigationDockWidget,
    ui_->speciesDockWidget);
  QObject::connect(species_controls_.get(), &SpeciesControls::individualAdded,
      this, &MainWindow::addIndividual);
  QObject::connect(species_controls_.get(), &SpeciesControls::colorChanged,
      this, &MainWindow::colorChanged);
  scene_->setToolWidget(annotation_widget_.get());
  QObject::connect(scene_.get(), &AnnotationScene::boxFinished,
      this, &MainWindow::addBoxAnnotation);
  QObject::connect(scene_.get(), &AnnotationScene::lineFinished,
      this, &MainWindow::addLineAnnotation);
  QObject::connect(scene_.get(), &AnnotationScene::dotFinished,
      this, &MainWindow::addDotAnnotation);
  fs::path current_path(QDir::currentPath().toStdString());
  fs::path default_species = current_path / fs::path("default.species");
  if(fs::exists(default_species)) {
    species_controls_->loadSpeciesFile(
        QString(default_species.string().c_str()));
  }
}

void MainWindow::resizeEvent(QResizeEvent *event) {
  view_->fitInView();
}

void MainWindow::on_next_clicked() {
  int next_val = ui_->imageSlider->value() + 1;
  if(next_val <= ui_->imageSlider->maximum()) {
    ui_->imageSlider->setValue(next_val);
  }
}

void MainWindow::on_prev_clicked() {
  int prev_val = ui_->imageSlider->value() - 1;
  if(prev_val >= ui_->imageSlider->minimum()) {
    ui_->imageSlider->setValue(prev_val);
  }
}

void MainWindow::on_loadImageDir_triggered() {
  QString image_dir = QFileDialog::getExistingDirectory(this,
    "Select an image directory.");
  if(!image_dir.isEmpty()) {
    onLoadDirectorySuccess(image_dir);
  }
}

void MainWindow::on_saveAnnotations_triggered() {
  if(image_files_.size() > 0) {
    annotations_->write(image_files_);
  }
}

void MainWindow::on_setMetadata_triggered() {
  MetadataDialog *dlg = new MetadataDialog(this);
  dlg->setMetadata(metadata_);
  if(dlg->exec()) {
    metadata_ = dlg->getMetadata();
  }
}

void MainWindow::on_imageSlider_valueChanged() {
  scene_->clear();
  current_annotations_.clear();
  std::string filename = image_files_[ui_->imageSlider->value()].string();
  QImage current(filename.c_str());
  if(!current.isNull()) {
    scene_->addPixmap(QPixmap::fromImage(current));
    scene_->setSceneRect(current.rect());
    view_->setScene(scene_.get());
    view_->show();
    ui_->fileNameValue->setText(filename.c_str());
    fs::path img_path(filename);
    auto annotations =
      annotations_->getImageAnnotations(img_path.filename());
    for(auto annotation : annotations) {
    }
    drawAnnotations();
    updateSpeciesCounts();
    updateTypeMenus();
  }
  else {
    QMessageBox err;
    err.critical(0, "Error", std::string(
        std::string("Error loading image ")
      + filename
      + std::string(".")).c_str());
  }
}

void MainWindow::on_showAnnotations_stateChanged() {
  on_imageSlider_valueChanged();
}

void MainWindow::on_idSelection_activated(const QString &id) {
  updateTypeMenus();
}

void MainWindow::on_typeMenu_activated(const QString &text) {
  auto ann = currentAnnotation();
  if(ann != nullptr) {
    ann->species_ = text.toStdString();
  }
  updateSpeciesCounts();
  updateTypeMenus();
}

void MainWindow::on_subTypeMenu_activated(const QString &text) {
  auto ann = currentAnnotation();
  if(ann != nullptr) {
    ann->subspecies_ = text.toStdString();
  }
}

void MainWindow::on_removeAnnotation_clicked() {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto current_image = image_files_[ui_->imageSlider->value()];
    int id = ui_->idSelection->currentText().toInt();
    annotations_->remove(current_image, id);
    on_imageSlider_valueChanged();
  }
}

void MainWindow::addIndividual(std::string species, std::string subspecies) {
  species_ = species;
  subspecies_ = subspecies;
  scene_->setMode(kSelect);
  QApplication::restoreOverrideCursor();
  scene_->setMode(kDraw);
}

void MainWindow::colorChanged(QMap<QString, QColor> color_map) {
  color_map_ = color_map;
  drawAnnotations();
}

void MainWindow::addBoxAnnotation(const QRectF &rect) {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto current_image = image_files_[ui_->imageSlider->value()];
    uint64_t id = annotations_->nextId(current_image);
    auto annotation = std::make_shared<ImageAnnotation>(
      current_image.filename().string(), species_, subspecies_, id,
      Rect(rect.x(), rect.y(), rect.width(), rect.height()), kBox);
    annotations_->insert(annotation);
    ui_->idSelection->setCurrentText(QString::number(id));
    drawAnnotations();
    updateTypeMenus();
    updateSpeciesCounts();
  }
}

void MainWindow::addLineAnnotation(const QLineF &line) {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto current_image = image_files_[ui_->imageSlider->value()];
    uint64_t id = annotations_->nextId(current_image);
    auto annotation = std::make_shared<ImageAnnotation>(
      current_image.filename().string(), species_, subspecies_, id,
      Rect(line.x1(), line.y1(), line.x2(), line.y2()), kLine);
    annotations_->insert(annotation);
    ui_->idSelection->setCurrentText(QString::number(id));
    drawAnnotations();
    updateTypeMenus();
    updateSpeciesCounts();
  }
}

void MainWindow::addDotAnnotation(const QPointF &dot) {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto current_image = image_files_[ui_->imageSlider->value()];
    uint64_t id = annotations_->nextId(current_image);
    auto annotation = std::make_shared<ImageAnnotation>(
      current_image.filename().string(), species_, subspecies_, id,
      Rect(dot.x(), dot.y(), 0, 0), kDot);
    annotations_->insert(annotation);
    ui_->idSelection->setCurrentText(QString::number(id));
    drawAnnotations();
    updateTypeMenus();
    updateSpeciesCounts();
  }
}

void MainWindow::onLoadDirectorySuccess(const QString &image_dir) {
  image_files_.clear();
  fs::directory_iterator dir_it(image_dir.toStdString());
  fs::directory_iterator dir_end;
  fs::path current_path(QDir::currentPath().toStdString());
  fs::path default_global_state = current_path / fs::path("default.global");
  for(; dir_it != dir_end; ++dir_it) {
    fs::path ext(dir_it->path().extension());
    for(auto &ok_ext : kDirExtensions) {
      if(ext == ok_ext) {
        auto val = std::make_shared<GlobalStateAnnotation>();
        if(fs::exists(default_global_state)) {
          deserialize(*val, default_global_state.string());
        }
        annotations_->insertGlobalStateAnnotation(
          dir_it->path().filename().string(),
          val);
        image_files_.push_back(dir_it->path());
      }
    }
  }
  std::sort(image_files_.begin(), image_files_.end());
  if(image_files_.size() > 0) {
    ui_->idLabel->setEnabled(true);
    ui_->speciesLabel->setEnabled(true);
    ui_->subspeciesLabel->setEnabled(true);
    ui_->idSelection->setEnabled(true);
    ui_->typeMenu->setEnabled(true);
    ui_->subTypeMenu->setEnabled(true);
    ui_->removeAnnotation->setEnabled(true);
    ui_->showAnnotations->setEnabled(true);
    ui_->setMetadata->setEnabled(true);
    ui_->next->setEnabled(true);
    ui_->prev->setEnabled(true);
    ui_->saveAnnotations->setEnabled(true);
    ui_->imageSlider->setEnabled(true);
    ui_->imageSlider->setMinimum(0);
    ui_->imageSlider->setMaximum(static_cast<int>(image_files_.size() - 1));
    ui_->imageSlider->setSingleStep(1);
    ui_->imageSlider->setValue(0);
    annotations_->read(image_files_);
    species_controls_->loadFromVector(annotations_->getAllSpecies());
    on_imageSlider_valueChanged();
    view_->setBoundingRect(scene_->sceneRect());
    view_->fitInView();
  }
  else {
    QMessageBox err;
    err.critical(0, "Error", "No images found in this directory.");
  }
}

std::shared_ptr<ImageAnnotation> MainWindow::currentAnnotation() {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto id = ui_->idSelection->currentText();
    auto current_image = image_files_[ui_->imageSlider->value()];
    auto annotations =
      annotations_->getImageAnnotations(current_image);
    for(auto annotation : annotations) {
      if(annotation->id_ == id.toInt()) {
        return annotation;
      }
    }
  }
  return nullptr;
}

void MainWindow::updateTypeMenus() {
  auto ann = currentAnnotation();
  ui_->typeMenu->clear();
  ui_->subTypeMenu->clear();
  if(ann != nullptr) {
    auto species = species_controls_->getSpecies();
    for(auto &s : species) {
      ui_->typeMenu->addItem(s.getName().c_str());
      if(s.getName() == ann->species_) {
        ui_->typeMenu->setCurrentText(s.getName().c_str());
        auto subspecies = s.getSubspecies();
        for(auto &sub : subspecies) {
          ui_->subTypeMenu->addItem(sub.c_str());
          if(sub == ann->subspecies_) {
            ui_->subTypeMenu->setCurrentText(sub.c_str());
          }
        }
      }
    }
  }
}

void MainWindow::drawAnnotations() {
  for(auto ann : current_annotations_) {
    scene_->removeItem(ann);
  }
  current_annotations_.clear();
  ui_->idSelection->clear();
  if(image_files_.size() > 0) {
    std::string filename = image_files_[ui_->imageSlider->value()].string();
    QImage current(filename.c_str());
    if(!current.isNull()) {
      fs::path img_path(filename);
      global_state_widget_->setStates(
        annotations_->getGlobalStateAnnotation(img_path.filename().string()));
      auto annotations =
        annotations_->getImageAnnotations(img_path.filename());
      for(auto annotation : annotations) {
        if(ui_->showAnnotations->isChecked()) {
          AnnotatedRegion<ImageAnnotation> *box = nullptr;
          AnnotatedLine<ImageAnnotation> *line = nullptr;
          AnnotatedDot<ImageAnnotation> *dot = nullptr;
          QString name = annotation->species_.c_str();
          QColor color = color_map_[name.toLower()];
          switch(annotation->type_) {
            case kBox:
              box = new AnnotatedRegion<ImageAnnotation>(
                  annotation->id_, annotation, current.rect(), color);
              scene_->addItem(box);
              current_annotations_.push_back(box);
              break;
            case kLine:
              line = new AnnotatedLine<ImageAnnotation>(
                  annotation->id_, annotation, current.rect(), color);
              scene_->addItem(line);
              current_annotations_.push_back(line);
              break;
            case kDot:
              dot = new AnnotatedDot<ImageAnnotation>(
                  annotation->id_, annotation, current.rect(), color);
              scene_->addItem(dot);
              current_annotations_.push_back(dot);
              break;
          }
        }
        ui_->idSelection->addItem(QString::number(annotation->id_));
      }
    }
  }
}

void MainWindow::updateSpeciesCounts() {
  species_controls_->resetCounts();
  std::string filename = image_files_[ui_->imageSlider->value()].string();
  fs::path img_path(filename);
  auto counts = annotations_->getCounts(img_path.filename().string());
  for(const auto &species : species_controls_->getSpecies()) {
    auto it = counts.find(species.getName());
    if(it != counts.end()) {
      species_controls_->setCount(it->second, it->first);
    }
    else {
      species_controls_->setCount(0, species.getName());
    }
  }
}

#include "moc_mainwindow.cpp"

}} // namespace fish_annotator::image_annotator
<commit_msg>Make items selectable upon loading them<commit_after>#include <algorithm>

#include <QFileDialog>
#include <QMessageBox>

#include "species_dialog.h"
#include "metadata_dialog.h"
#include "annotatedregion.h"
#include "annotated_line.h"
#include "annotated_dot.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"

namespace fish_annotator { namespace image_annotator {

namespace fs = boost::filesystem;

namespace { //anonymous

static const std::vector<std::string> kDirExtensions = {
  ".jpg", ".png", ".bmp", ".tif", ".jpeg",
  ".JPG", ".PNG", ".BMP", ".TIF", ".JPEG"};

} // anonymous namespace

MainWindow::MainWindow(QWidget *parent)
  : annotations_(new ImageAnnotationList)
  , view_(new AnnotationView)
  , scene_(new AnnotationScene)
  , ui_(new Ui::MainWindow)
  , species_controls_(new SpeciesControls(this))
  , annotation_widget_(new AnnotationWidget(this))
  , global_state_widget_(new GlobalStateWidget(this))
  , image_files_()
  , metadata_()
  , species_()
  , subspecies_()
  , current_annotations_()
  , color_map_() {
  ui_->setupUi(this);
  setWindowTitle("Image Annotator");
#ifdef _WIN32
  setWindowIcon(QIcon(":/icons/cvision/cvision_no_text.ico"));
#endif
  ui_->next->setIcon(QIcon(":/icons/image_controls/next.svg"));
  ui_->prev->setIcon(QIcon(":/icons/image_controls/prev.svg"));
  ui_->videoWindowLayout->addWidget(view_.get());
  ui_->speciesLayout->addWidget(annotation_widget_.get());
  ui_->speciesLayout->addWidget(species_controls_.get());
  ui_->globalStateLayout->addWidget(global_state_widget_.get());
  tabifyDockWidget(
    ui_->globalStateDockWidget,
    ui_->navigationDockWidget);
  tabifyDockWidget(
    ui_->navigationDockWidget,
    ui_->speciesDockWidget);
  QObject::connect(species_controls_.get(), &SpeciesControls::individualAdded,
      this, &MainWindow::addIndividual);
  QObject::connect(species_controls_.get(), &SpeciesControls::colorChanged,
      this, &MainWindow::colorChanged);
  scene_->setToolWidget(annotation_widget_.get());
  QObject::connect(scene_.get(), &AnnotationScene::boxFinished,
      this, &MainWindow::addBoxAnnotation);
  QObject::connect(scene_.get(), &AnnotationScene::lineFinished,
      this, &MainWindow::addLineAnnotation);
  QObject::connect(scene_.get(), &AnnotationScene::dotFinished,
      this, &MainWindow::addDotAnnotation);
  fs::path current_path(QDir::currentPath().toStdString());
  fs::path default_species = current_path / fs::path("default.species");
  if(fs::exists(default_species)) {
    species_controls_->loadSpeciesFile(
        QString(default_species.string().c_str()));
  }
}

void MainWindow::resizeEvent(QResizeEvent *event) {
  view_->fitInView();
}

void MainWindow::on_next_clicked() {
  int next_val = ui_->imageSlider->value() + 1;
  if(next_val <= ui_->imageSlider->maximum()) {
    ui_->imageSlider->setValue(next_val);
  }
}

void MainWindow::on_prev_clicked() {
  int prev_val = ui_->imageSlider->value() - 1;
  if(prev_val >= ui_->imageSlider->minimum()) {
    ui_->imageSlider->setValue(prev_val);
  }
}

void MainWindow::on_loadImageDir_triggered() {
  QString image_dir = QFileDialog::getExistingDirectory(this,
    "Select an image directory.");
  if(!image_dir.isEmpty()) {
    onLoadDirectorySuccess(image_dir);
  }
}

void MainWindow::on_saveAnnotations_triggered() {
  if(image_files_.size() > 0) {
    annotations_->write(image_files_);
  }
}

void MainWindow::on_setMetadata_triggered() {
  MetadataDialog *dlg = new MetadataDialog(this);
  dlg->setMetadata(metadata_);
  if(dlg->exec()) {
    metadata_ = dlg->getMetadata();
  }
}

void MainWindow::on_imageSlider_valueChanged() {
  scene_->clear();
  current_annotations_.clear();
  std::string filename = image_files_[ui_->imageSlider->value()].string();
  QImage current(filename.c_str());
  if(!current.isNull()) {
    scene_->addPixmap(QPixmap::fromImage(current));
    scene_->setSceneRect(current.rect());
    view_->setScene(scene_.get());
    view_->show();
    ui_->fileNameValue->setText(filename.c_str());
    fs::path img_path(filename);
    auto annotations =
      annotations_->getImageAnnotations(img_path.filename());
    for(auto annotation : annotations) {
    }
    drawAnnotations();
    updateSpeciesCounts();
    updateTypeMenus();
  }
  else {
    QMessageBox err;
    err.critical(0, "Error", std::string(
        std::string("Error loading image ")
      + filename
      + std::string(".")).c_str());
  }
}

void MainWindow::on_showAnnotations_stateChanged() {
  on_imageSlider_valueChanged();
}

void MainWindow::on_idSelection_activated(const QString &id) {
  updateTypeMenus();
}

void MainWindow::on_typeMenu_activated(const QString &text) {
  auto ann = currentAnnotation();
  if(ann != nullptr) {
    ann->species_ = text.toStdString();
  }
  updateSpeciesCounts();
  updateTypeMenus();
}

void MainWindow::on_subTypeMenu_activated(const QString &text) {
  auto ann = currentAnnotation();
  if(ann != nullptr) {
    ann->subspecies_ = text.toStdString();
  }
}

void MainWindow::on_removeAnnotation_clicked() {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto current_image = image_files_[ui_->imageSlider->value()];
    int id = ui_->idSelection->currentText().toInt();
    annotations_->remove(current_image, id);
    on_imageSlider_valueChanged();
  }
}

void MainWindow::addIndividual(std::string species, std::string subspecies) {
  species_ = species;
  subspecies_ = subspecies;
  scene_->setMode(kSelect);
  QApplication::restoreOverrideCursor();
  scene_->setMode(kDraw);
}

void MainWindow::colorChanged(QMap<QString, QColor> color_map) {
  color_map_ = color_map;
  drawAnnotations();
}

void MainWindow::addBoxAnnotation(const QRectF &rect) {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto current_image = image_files_[ui_->imageSlider->value()];
    uint64_t id = annotations_->nextId(current_image);
    auto annotation = std::make_shared<ImageAnnotation>(
      current_image.filename().string(), species_, subspecies_, id,
      Rect(rect.x(), rect.y(), rect.width(), rect.height()), kBox);
    annotations_->insert(annotation);
    ui_->idSelection->setCurrentText(QString::number(id));
    drawAnnotations();
    updateTypeMenus();
    updateSpeciesCounts();
  }
}

void MainWindow::addLineAnnotation(const QLineF &line) {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto current_image = image_files_[ui_->imageSlider->value()];
    uint64_t id = annotations_->nextId(current_image);
    auto annotation = std::make_shared<ImageAnnotation>(
      current_image.filename().string(), species_, subspecies_, id,
      Rect(line.x1(), line.y1(), line.x2(), line.y2()), kLine);
    annotations_->insert(annotation);
    ui_->idSelection->setCurrentText(QString::number(id));
    drawAnnotations();
    updateTypeMenus();
    updateSpeciesCounts();
  }
}

void MainWindow::addDotAnnotation(const QPointF &dot) {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto current_image = image_files_[ui_->imageSlider->value()];
    uint64_t id = annotations_->nextId(current_image);
    auto annotation = std::make_shared<ImageAnnotation>(
      current_image.filename().string(), species_, subspecies_, id,
      Rect(dot.x(), dot.y(), 0, 0), kDot);
    annotations_->insert(annotation);
    ui_->idSelection->setCurrentText(QString::number(id));
    drawAnnotations();
    updateTypeMenus();
    updateSpeciesCounts();
  }
}

void MainWindow::onLoadDirectorySuccess(const QString &image_dir) {
  image_files_.clear();
  fs::directory_iterator dir_it(image_dir.toStdString());
  fs::directory_iterator dir_end;
  fs::path current_path(QDir::currentPath().toStdString());
  fs::path default_global_state = current_path / fs::path("default.global");
  for(; dir_it != dir_end; ++dir_it) {
    fs::path ext(dir_it->path().extension());
    for(auto &ok_ext : kDirExtensions) {
      if(ext == ok_ext) {
        auto val = std::make_shared<GlobalStateAnnotation>();
        if(fs::exists(default_global_state)) {
          deserialize(*val, default_global_state.string());
        }
        annotations_->insertGlobalStateAnnotation(
          dir_it->path().filename().string(),
          val);
        image_files_.push_back(dir_it->path());
      }
    }
  }
  std::sort(image_files_.begin(), image_files_.end());
  if(image_files_.size() > 0) {
    ui_->idLabel->setEnabled(true);
    ui_->speciesLabel->setEnabled(true);
    ui_->subspeciesLabel->setEnabled(true);
    ui_->idSelection->setEnabled(true);
    ui_->typeMenu->setEnabled(true);
    ui_->subTypeMenu->setEnabled(true);
    ui_->removeAnnotation->setEnabled(true);
    ui_->showAnnotations->setEnabled(true);
    ui_->setMetadata->setEnabled(true);
    ui_->next->setEnabled(true);
    ui_->prev->setEnabled(true);
    ui_->saveAnnotations->setEnabled(true);
    ui_->imageSlider->setEnabled(true);
    ui_->imageSlider->setMinimum(0);
    ui_->imageSlider->setMaximum(static_cast<int>(image_files_.size() - 1));
    ui_->imageSlider->setSingleStep(1);
    ui_->imageSlider->setValue(0);
    annotations_->read(image_files_);
    species_controls_->loadFromVector(annotations_->getAllSpecies());
    on_imageSlider_valueChanged();
    view_->setBoundingRect(scene_->sceneRect());
    view_->fitInView();
    scene_->setMode(kSelect);
  }
  else {
    QMessageBox err;
    err.critical(0, "Error", "No images found in this directory.");
  }
}

std::shared_ptr<ImageAnnotation> MainWindow::currentAnnotation() {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto id = ui_->idSelection->currentText();
    auto current_image = image_files_[ui_->imageSlider->value()];
    auto annotations =
      annotations_->getImageAnnotations(current_image);
    for(auto annotation : annotations) {
      if(annotation->id_ == id.toInt()) {
        return annotation;
      }
    }
  }
  return nullptr;
}

void MainWindow::updateTypeMenus() {
  auto ann = currentAnnotation();
  ui_->typeMenu->clear();
  ui_->subTypeMenu->clear();
  if(ann != nullptr) {
    auto species = species_controls_->getSpecies();
    for(auto &s : species) {
      ui_->typeMenu->addItem(s.getName().c_str());
      if(s.getName() == ann->species_) {
        ui_->typeMenu->setCurrentText(s.getName().c_str());
        auto subspecies = s.getSubspecies();
        for(auto &sub : subspecies) {
          ui_->subTypeMenu->addItem(sub.c_str());
          if(sub == ann->subspecies_) {
            ui_->subTypeMenu->setCurrentText(sub.c_str());
          }
        }
      }
    }
  }
}

void MainWindow::drawAnnotations() {
  for(auto ann : current_annotations_) {
    scene_->removeItem(ann);
  }
  current_annotations_.clear();
  ui_->idSelection->clear();
  if(image_files_.size() > 0) {
    std::string filename = image_files_[ui_->imageSlider->value()].string();
    QImage current(filename.c_str());
    if(!current.isNull()) {
      fs::path img_path(filename);
      global_state_widget_->setStates(
        annotations_->getGlobalStateAnnotation(img_path.filename().string()));
      auto annotations =
        annotations_->getImageAnnotations(img_path.filename());
      for(auto annotation : annotations) {
        if(ui_->showAnnotations->isChecked()) {
          AnnotatedRegion<ImageAnnotation> *box = nullptr;
          AnnotatedLine<ImageAnnotation> *line = nullptr;
          AnnotatedDot<ImageAnnotation> *dot = nullptr;
          QString name = annotation->species_.c_str();
          QColor color = color_map_[name.toLower()];
          switch(annotation->type_) {
            case kBox:
              box = new AnnotatedRegion<ImageAnnotation>(
                  annotation->id_, annotation, current.rect(), color);
              scene_->addItem(box);
              current_annotations_.push_back(box);
              break;
            case kLine:
              line = new AnnotatedLine<ImageAnnotation>(
                  annotation->id_, annotation, current.rect(), color);
              scene_->addItem(line);
              current_annotations_.push_back(line);
              break;
            case kDot:
              dot = new AnnotatedDot<ImageAnnotation>(
                  annotation->id_, annotation, current.rect(), color);
              scene_->addItem(dot);
              current_annotations_.push_back(dot);
              break;
          }
        }
        ui_->idSelection->addItem(QString::number(annotation->id_));
      }
    }
  }
}

void MainWindow::updateSpeciesCounts() {
  species_controls_->resetCounts();
  std::string filename = image_files_[ui_->imageSlider->value()].string();
  fs::path img_path(filename);
  auto counts = annotations_->getCounts(img_path.filename().string());
  for(const auto &species : species_controls_->getSpecies()) {
    auto it = counts.find(species.getName());
    if(it != counts.end()) {
      species_controls_->setCount(it->second, it->first);
    }
    else {
      species_controls_->setCount(0, species.getName());
    }
  }
}

#include "moc_mainwindow.cpp"

}} // namespace fish_annotator::image_annotator
<|endoftext|>


<commit_before>/***************************************************************************
*   Copyright © 2003 Jason Kivlighn <jkivlighn@gmail.com>                 *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
***************************************************************************/

#include "rezkonvimporter.h"

#include <kapplication.h>
#include <klocale.h>
#include <kdebug.h>

#include <QFile>
#include <QRegExp>
#include <QTextStream>

#include "datablocks/mixednumber.h"

RezkonvImporter::RezkonvImporter() : BaseImporter()
{}

RezkonvImporter::~RezkonvImporter()
{}

void RezkonvImporter::parseFile( const QString &filename )
{
	QFile input( filename );

	if ( input.open( QIODevice::ReadOnly ) ) {
		QTextStream stream( &input );
		stream.skipWhiteSpace();

		QString line;

		while ( !stream.atEnd() ) {
			line = stream.readLine();

			if ( line.contains( QRegExp( "^=====.*REZKONV.*", Qt::CaseInsensitive ) ) ) {
				QStringList raw_recipe;
				while ( !( line = stream.readLine() ).contains( QRegExp( "^=====\\s*$" ) ) && !stream.atEnd() )
					raw_recipe << line;

				readRecipe( raw_recipe );
			}
		}

		if ( fileRecipeCount() == 0 )
			setErrorMsg( i18n( "No recipes found in this file." ) );
	}
	else
		setErrorMsg( i18n( "Unable to open file." ) );
}

void RezkonvImporter::readRecipe( const QStringList &raw_recipe )
{
	kapp->processEvents(); //don't want the user to think its frozen... especially for files with thousands of recipes

	Recipe recipe;

	QStringList::const_iterator text_it = raw_recipe.begin();
	m_end_it = raw_recipe.end();

	//title (Titel)
	text_it++;
	recipe.title = ( *text_it ).mid( ( *text_it ).indexOf( ":" ) + 1, ( *text_it ).length() ).trimmed();
	kDebug() << "Found title: " << recipe.title ;

	//categories (Kategorien):
	text_it++;
	QStringList categories;
   if ( ( *text_it ).mid( ( *text_it ).indexOf( ":" ) + 1, ( *text_it ).length() ).isEmpty() )
      categories = QStringList();
   else
      categories = ( *text_it ).mid( ( *text_it ).indexOf( ":" ) + 1, ( *text_it ).length() ).split( ',', QString::SkipEmptyParts );

	for ( QStringList::const_iterator it = categories.constBegin(); it != categories.constEnd(); ++it ) {
		Element new_cat;
		new_cat.name = QString( *it ).trimmed();
		kDebug() << "Found category: " << new_cat.name ;
		recipe.categoryList.append( new_cat );
	}

	//yield (Menge)
	text_it++;
	//get the number between the ":" and the next space after it
	QString yield_str = ( *text_it ).trimmed();
	yield_str.remove( QRegExp( "^Menge:\\s*" ) );
	int sep_index = yield_str.indexOf( ' ' );
	if ( sep_index != -1 )
		recipe.yield.type = yield_str.mid( sep_index+1 );
	readRange( yield_str.mid( 0, sep_index ), recipe.yield.amount, recipe.yield.amount_offset );
	kDebug() << "Found yield: " << recipe.yield.amount ;

	bool is_sub = false;
	bool last_line_empty = false;
	text_it++;
	while ( text_it != raw_recipe.end() ) {
		if ( ( *text_it ).isEmpty() ) {
			last_line_empty = true;
			text_it++;
			continue;
		}

		if ( ( *text_it ).contains( QRegExp( "^=====.*=$" ) ) )  //is a header
		{
			if ( ( *text_it ).contains( "quelle", Qt::CaseInsensitive ) )
			{
				loadReferences( text_it, recipe );
				break; //reference lines are the last before the instructions
			}
			else
				loadIngredientHeader( *text_it, recipe );
		}

		//if it has no more than two spaces followed by a non-digit
		//then we'll assume it is a direction line
		else if ( last_line_empty && ( *text_it ).contains( QRegExp( "^\\s{0,2}[^\\d\\s=]" ) ) )
			break;
		else
			loadIngredient( *text_it, recipe, is_sub );

		last_line_empty = false;
		text_it++;
	}

	loadInstructions( text_it, recipe );

	add
		( recipe );

	current_header = QString::null;
}

void RezkonvImporter::loadIngredient( const QString &string, Recipe &recipe, bool &is_sub )
{
	Ingredient new_ingredient;
	new_ingredient.amount = 0; //amount not required, so give default of 0

	QRegExp cont_test( "^-{1,2}" );
	if ( string.trimmed().contains( cont_test ) ) {
		QString name = string.trimmed();
		name.remove( cont_test );
		kDebug() << "Appending to last ingredient: " << name ;
		if ( !recipe.ingList.isEmpty() )  //so it doesn't crash when the first ingredient appears to be a continuation of another
			( *recipe.ingList.at( recipe.ingList.count() - 1 ) ).name += " " + name;

		return ;
	}

	//amount
	if ( !string.mid( 0, 7 ).trimmed().isEmpty() )
		readRange( string.mid( 0, 7 ), new_ingredient.amount, new_ingredient.amount_offset );

	//unit
	QString unit_str = string.mid( 8, 9 ).trimmed();
	new_ingredient.units = Unit( unit_str, new_ingredient.amount );

	//name and preparation method
	new_ingredient.name = string.mid( 18, string.length() - 18 ).trimmed();

	//separate out the preparation method
	QString name_and_prep = new_ingredient.name;
	int separator_index = name_and_prep.indexOf( "," );
	if ( separator_index != -1 ) {
		new_ingredient.name = name_and_prep.mid( 0, separator_index ).trimmed();
		new_ingredient.prepMethodList = ElementList::split(",",name_and_prep.mid( separator_index + 1, name_and_prep.length() ).trimmed() );
	}

	//header (if present)
	new_ingredient.group = current_header;

	bool last_is_sub = is_sub;
	if ( !new_ingredient.prepMethodList.isEmpty() &&
	new_ingredient.prepMethodList.last().name == "or" ) {
		new_ingredient.prepMethodList.pop_back();
		is_sub = true;
	}
	else
		is_sub = false;

	if ( last_is_sub )
		( *recipe.ingList.at( recipe.ingList.count() - 1 ) ).substitutes.append(new_ingredient);
	else
		recipe.ingList.append( new_ingredient );
}

void RezkonvImporter::loadIngredientHeader( const QString &string, Recipe &/*recipe*/ )
{
	QString header = string;
	header.remove( QRegExp( "^=*" ) ).remove( QRegExp( "=*$" ) );
	header = header.trimmed();

	kDebug() << "found ingredient header: " << header ;

	current_header = header;
}

void RezkonvImporter::loadInstructions( QStringList::const_iterator &text_it, Recipe &recipe )
{
	QString instr;
	QRegExp rx_title( "^:{0,1}\\s*O-Titel\\s*:" );
	QString line;
	text_it++;
	while ( text_it != m_end_it ) {
		line = *text_it;

		//titles longer than the line width are rewritten here
		if ( line.contains( rx_title ) ) {
			line.remove( rx_title );
			recipe.title = line.trimmed();

			QRegExp rx_line_cont( ":\\s*>{0,1}\\s*:" );
			while ( ( line = *text_it ).contains( rx_line_cont ) ) {
				line.remove( rx_line_cont );
				recipe.title += line;

				text_it++;
			}
			kDebug() << "Found long title: " << recipe.title ;
		}
		else {
			if ( line.isEmpty() && ( (text_it+1) != m_end_it ) )
				instr += "\n\n";

			instr += line.trimmed();
		}

		text_it++;
	}

	recipe.instructions = instr;
}

void RezkonvImporter::loadReferences( QStringList::const_iterator &text_it, Recipe &recipe )
{
	kDebug() << "Found source header" ;

	text_it++;
	while ( text_it != m_end_it && !text_it->trimmed().isEmpty() ) {
		QRegExp rx_line_begin( "^\\s*-{0,2}\\s*" );

		QRegExp rx_creation_date = QRegExp( "^\\s*-{0,2}\\s*Erfasst \\*RK\\*", Qt::CaseInsensitive );
		if ( ( *text_it ).contains( rx_creation_date ) )  // date followed by typist
		{
			QString date = *text_it;
			date.remove( rx_creation_date ).remove( QRegExp( " von\\s*$" ) );

			// Date is given as DD.MM.YY
			QString s = date.section( ".", 0, 0 );
			int day = s.toInt();

			s = date.section( ".", 1, 1 );
			int month = s.toInt();

			s = date.section( ".", 2, 2 );
			int year = s.toInt();
			year += 1900;
			if ( year < 1970 )
				year += 100; //we'll assume nothing has been created before 1970 (y2k issues :p)

			recipe.ctime = QDateTime(QDate(year,month,day));

			#if 0
			//typist
			text_it++;
			QString typist = = *text_it;
			typist.remove( rx_line_begin );
			#endif

		}
		else //everything else is an author
		{
			if ( ( *text_it ).contains( rx_line_begin ) ) {
				QString author = *text_it;
				author.remove( rx_line_begin );

				recipe.authorList.append( Element( author ) );
			}
			else
				break;
		}
		text_it++;
	}
}

void RezkonvImporter::readRange( const QString &range_str, double &amount, double &amount_offset )
{
	QString from = range_str.section( '-', 0, 0 );
	QString to   = range_str.section( '-', 1, 1 );

	amount = MixedNumber::fromString( from ).toDouble();

	if ( !to.trimmed().isEmpty() )
		amount_offset = MixedNumber::fromString( to ).toDouble() - amount;
}
<commit_msg>We should use the trimmed version of the line here too.<commit_after>/***************************************************************************
*   Copyright © 2003 Jason Kivlighn <jkivlighn@gmail.com>                 *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
***************************************************************************/

#include "rezkonvimporter.h"

#include <kapplication.h>
#include <klocale.h>
#include <kdebug.h>

#include <QFile>
#include <QRegExp>
#include <QTextStream>

#include "datablocks/mixednumber.h"

RezkonvImporter::RezkonvImporter() : BaseImporter()
{}

RezkonvImporter::~RezkonvImporter()
{}

void RezkonvImporter::parseFile( const QString &filename )
{
	QFile input( filename );

	if ( input.open( QIODevice::ReadOnly ) ) {
		QTextStream stream( &input );
		stream.skipWhiteSpace();

		QString line;

		while ( !stream.atEnd() ) {
			line = stream.readLine();

			if ( line.contains( QRegExp( "^=====.*REZKONV.*", Qt::CaseInsensitive ) ) ) {
				QStringList raw_recipe;
				while ( !( line = stream.readLine() ).contains( QRegExp( "^=====\\s*$" ) ) && !stream.atEnd() )
					raw_recipe << line;

				readRecipe( raw_recipe );
			}
		}

		if ( fileRecipeCount() == 0 )
			setErrorMsg( i18n( "No recipes found in this file." ) );
	}
	else
		setErrorMsg( i18n( "Unable to open file." ) );
}

void RezkonvImporter::readRecipe( const QStringList &raw_recipe )
{
	kapp->processEvents(); //don't want the user to think its frozen... especially for files with thousands of recipes

	Recipe recipe;

	QStringList::const_iterator text_it = raw_recipe.begin();
	m_end_it = raw_recipe.end();

	//title (Titel)
	text_it++;
	recipe.title = ( *text_it ).mid( ( *text_it ).indexOf( ":" ) + 1, ( *text_it ).length() ).trimmed();
	kDebug() << "Found title: " << recipe.title ;

	//categories (Kategorien):
	text_it++;
	QStringList categories;
   if ( ( *text_it ).mid( ( *text_it ).indexOf( ":" ) + 1, ( *text_it ).length() ).isEmpty() )
      categories = QStringList();
   else
      categories = ( *text_it ).mid( ( *text_it ).indexOf( ":" ) + 1, ( *text_it ).length() ).split( ',', QString::SkipEmptyParts );

	for ( QStringList::const_iterator it = categories.constBegin(); it != categories.constEnd(); ++it ) {
		Element new_cat;
		new_cat.name = QString( *it ).trimmed();
		kDebug() << "Found category: " << new_cat.name ;
		recipe.categoryList.append( new_cat );
	}

	//yield (Menge)
	text_it++;
	//get the number between the ":" and the next space after it
	QString yield_str = ( *text_it ).trimmed();
	yield_str.remove( QRegExp( "^Menge:\\s*" ) );
	int sep_index = yield_str.indexOf( ' ' );
	if ( sep_index != -1 )
		recipe.yield.type = yield_str.mid( sep_index+1 );
	readRange( yield_str.mid( 0, sep_index ), recipe.yield.amount, recipe.yield.amount_offset );
	kDebug() << "Found yield: " << recipe.yield.amount ;

	bool is_sub = false;
	bool last_line_empty = false;
	text_it++;
	while ( text_it != raw_recipe.end() ) {
		if ( ( *text_it ).isEmpty() ) {
			last_line_empty = true;
			text_it++;
			continue;
		}

		if ( ( *text_it ).contains( QRegExp( "^=====.*=$" ) ) )  //is a header
		{
			if ( ( *text_it ).contains( "quelle", Qt::CaseInsensitive ) )
			{
				loadReferences( text_it, recipe );
				break; //reference lines are the last before the instructions
			}
			else
				loadIngredientHeader( *text_it, recipe );
		}

		//if it has no more than two spaces followed by a non-digit
		//then we'll assume it is a direction line
		else if ( last_line_empty && ( *text_it ).contains( QRegExp( "^\\s{0,2}[^\\d\\s=]" ) ) )
			break;
		else
			loadIngredient( *text_it, recipe, is_sub );

		last_line_empty = false;
		text_it++;
	}

	loadInstructions( text_it, recipe );

	add
		( recipe );

	current_header = QString::null;
}

void RezkonvImporter::loadIngredient( const QString &string, Recipe &recipe, bool &is_sub )
{
	Ingredient new_ingredient;
	new_ingredient.amount = 0; //amount not required, so give default of 0

	QRegExp cont_test( "^-{1,2}" );
	if ( string.trimmed().contains( cont_test ) ) {
		QString name = string.trimmed();
		name.remove( cont_test );
		kDebug() << "Appending to last ingredient: " << name ;
		if ( !recipe.ingList.isEmpty() )  //so it doesn't crash when the first ingredient appears to be a continuation of another
			( *recipe.ingList.at( recipe.ingList.count() - 1 ) ).name += " " + name;

		return ;
	}

	//amount
	if ( !string.mid( 0, 7 ).trimmed().isEmpty() )
		readRange( string.mid( 0, 7 ), new_ingredient.amount, new_ingredient.amount_offset );

	//unit
	QString unit_str = string.mid( 8, 9 ).trimmed();
	new_ingredient.units = Unit( unit_str, new_ingredient.amount );

	//name and preparation method
	new_ingredient.name = string.mid( 18, string.length() - 18 ).trimmed();

	//separate out the preparation method
	QString name_and_prep = new_ingredient.name;
	int separator_index = name_and_prep.indexOf( "," );
	if ( separator_index != -1 ) {
		new_ingredient.name = name_and_prep.mid( 0, separator_index ).trimmed();
		new_ingredient.prepMethodList = ElementList::split(",",name_and_prep.mid( separator_index + 1, name_and_prep.length() ).trimmed() );
	}

	//header (if present)
	new_ingredient.group = current_header;

	bool last_is_sub = is_sub;
	if ( !new_ingredient.prepMethodList.isEmpty() &&
	new_ingredient.prepMethodList.last().name == "or" ) {
		new_ingredient.prepMethodList.pop_back();
		is_sub = true;
	}
	else
		is_sub = false;

	if ( last_is_sub )
		( *recipe.ingList.at( recipe.ingList.count() - 1 ) ).substitutes.append(new_ingredient);
	else
		recipe.ingList.append( new_ingredient );
}

void RezkonvImporter::loadIngredientHeader( const QString &string, Recipe &/*recipe*/ )
{
	QString header = string;
	header.remove( QRegExp( "^=*" ) ).remove( QRegExp( "=*$" ) );
	header = header.trimmed();

	kDebug() << "found ingredient header: " << header ;

	current_header = header;
}

void RezkonvImporter::loadInstructions( QStringList::const_iterator &text_it, Recipe &recipe )
{
	QString instr;
	QRegExp rx_title( "^:{0,1}\\s*O-Titel\\s*:" );
	QString line;
	text_it++;
	while ( text_it != m_end_it ) {
		line = *text_it;

		//titles longer than the line width are rewritten here
		if ( line.contains( rx_title ) ) {
			line.remove( rx_title );
			recipe.title = line.trimmed();

			QRegExp rx_line_cont( ":\\s*>{0,1}\\s*:" );
			while ( ( line = *text_it ).contains( rx_line_cont ) ) {
				line.remove( rx_line_cont );
				recipe.title += line;

				text_it++;
			}
			kDebug() << "Found long title: " << recipe.title ;
		}
		else {
			if ( line.trimmed().isEmpty() && ( (text_it+1) != m_end_it ) )
				instr += "\n\n";

			instr += line.trimmed();
		}

		text_it++;
	}

	recipe.instructions = instr;
}

void RezkonvImporter::loadReferences( QStringList::const_iterator &text_it, Recipe &recipe )
{
	kDebug() << "Found source header" ;

	text_it++;
	while ( text_it != m_end_it && !text_it->trimmed().isEmpty() ) {
		QRegExp rx_line_begin( "^\\s*-{0,2}\\s*" );

		QRegExp rx_creation_date = QRegExp( "^\\s*-{0,2}\\s*Erfasst \\*RK\\*", Qt::CaseInsensitive );
		if ( ( *text_it ).contains( rx_creation_date ) )  // date followed by typist
		{
			QString date = *text_it;
			date.remove( rx_creation_date ).remove( QRegExp( " von\\s*$" ) );

			// Date is given as DD.MM.YY
			QString s = date.section( ".", 0, 0 );
			int day = s.toInt();

			s = date.section( ".", 1, 1 );
			int month = s.toInt();

			s = date.section( ".", 2, 2 );
			int year = s.toInt();
			year += 1900;
			if ( year < 1970 )
				year += 100; //we'll assume nothing has been created before 1970 (y2k issues :p)

			recipe.ctime = QDateTime(QDate(year,month,day));

			#if 0
			//typist
			text_it++;
			QString typist = = *text_it;
			typist.remove( rx_line_begin );
			#endif

		}
		else //everything else is an author
		{
			if ( ( *text_it ).contains( rx_line_begin ) ) {
				QString author = *text_it;
				author.remove( rx_line_begin );

				recipe.authorList.append( Element( author ) );
			}
			else
				break;
		}
		text_it++;
	}
}

void RezkonvImporter::readRange( const QString &range_str, double &amount, double &amount_offset )
{
	QString from = range_str.section( '-', 0, 0 );
	QString to   = range_str.section( '-', 1, 1 );

	amount = MixedNumber::fromString( from ).toDouble();

	if ( !to.trimmed().isEmpty() )
		amount_offset = MixedNumber::fromString( to ).toDouble() - amount;
}
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ResponseCorrelator.h"

using namespace activemq;
using namespace activemq::transport;
using namespace activemq::transport::filters;
using namespace activemq::exceptions;
using namespace decaf::lang;
using namespace decaf::lang::exceptions;

////////////////////////////////////////////////////////////////////////////////
ResponseCorrelator::ResponseCorrelator( Transport* next, bool own )
:
    TransportFilter( next, own ) {

    //nextCommandId = 0;
    nextCommandId.set(1);
    // Start in the closed state.
    closed = true;
}

////////////////////////////////////////////////////////////////////////////////
ResponseCorrelator::~ResponseCorrelator(){

    // Close the transport and destroy it.
    close();

    // Don't do anything with the future responses -
    // they should be cleaned up by each requester.
}

////////////////////////////////////////////////////////////////////////////////
void ResponseCorrelator::oneway( Command* command )
    throw( CommandIOException, decaf::lang::exceptions::UnsupportedOperationException ) {

    try{
        command->setCommandId( nextCommandId.getAndIncrement() );
        command->setResponseRequired( false );

        if( closed || next == NULL ){
            throw CommandIOException( __FILE__, __LINE__,
                "transport already closed" );
        }

        next->oneway( command );
    }
    AMQ_CATCH_RETHROW( UnsupportedOperationException )
    AMQ_CATCH_RETHROW( CommandIOException )
    AMQ_CATCH_EXCEPTION_CONVERT( ActiveMQException, CommandIOException )
    AMQ_CATCH_EXCEPTION_CONVERT( Exception, CommandIOException )
    AMQ_CATCHALL_THROW( CommandIOException )
}

////////////////////////////////////////////////////////////////////////////////
Response* ResponseCorrelator::request( Command* command )
    throw( CommandIOException, decaf::lang::exceptions::UnsupportedOperationException ) {

    try{
        command->setCommandId( nextCommandId.getAndIncrement() );
        command->setResponseRequired( true );

        // Add a future response object to the map indexed by this
        // command id.
        FutureResponse* futureResponse = new FutureResponse();

        synchronized( &mapMutex ){
            requestMap[command->getCommandId()] = futureResponse;
        }

        // Wait to be notified of the response via the futureResponse
        // object.
        Response* response = NULL;

        // Send the request.
        next->oneway( command );

        // Get the response.
        response = futureResponse->getResponse();

        // Perform cleanup on the map.
        synchronized( &mapMutex ){

            // We've done our waiting - get this thing out
            // of the map.
            requestMap.erase( command->getCommandId() );

            // Destroy the futureResponse.  It is safe to
            // do this now because the other thread only
            // accesses the futureResponse within a lock on
            // the map.
            delete futureResponse;
            futureResponse = NULL;
        }

        if( response == NULL ){

            throw CommandIOException( __FILE__, __LINE__,
                "No valid response received for command: %s, check broker.",
                command->toString().c_str() );
        }

        return response;
    }
    AMQ_CATCH_RETHROW( UnsupportedOperationException )
    AMQ_CATCH_RETHROW( CommandIOException )
    AMQ_CATCH_EXCEPTION_CONVERT( ActiveMQException, CommandIOException )
    AMQ_CATCH_EXCEPTION_CONVERT( Exception, CommandIOException )
    AMQ_CATCHALL_THROW( CommandIOException )
}

////////////////////////////////////////////////////////////////////////////////
Response* ResponseCorrelator::request( Command* command, unsigned int timeout )
    throw( CommandIOException, decaf::lang::exceptions::UnsupportedOperationException ) {

    try{
        command->setCommandId( nextCommandId.getAndIncrement() );
        command->setResponseRequired( true );

        // Add a future response object to the map indexed by this
        // command id.
        FutureResponse* futureResponse = new FutureResponse();

        synchronized( &mapMutex ){
            requestMap[command->getCommandId()] = futureResponse;
        }

        // Wait to be notified of the response via the futureResponse
        // object.
        Response* response = NULL;

        // Send the request.
        next->oneway( command );

        // Get the response.
        response = futureResponse->getResponse( timeout );

        // Perform cleanup on the map.
        synchronized( &mapMutex ){

            // We've done our waiting - get this thing out
            // of the map.
            requestMap.erase( command->getCommandId() );

            // Destroy the futureResponse.  It is safe to
            // do this now because the other thread only
            // accesses the futureResponse within a lock on
            // the map.
            delete futureResponse;
            futureResponse = NULL;
        }

        if( response == NULL ){

            throw CommandIOException( __FILE__, __LINE__,
                "No valid response received for command: %s, check broker.",
                command->toString().c_str() );
        }

        return response;
    }
    AMQ_CATCH_RETHROW( UnsupportedOperationException )
    AMQ_CATCH_RETHROW( CommandIOException )
    AMQ_CATCH_EXCEPTION_CONVERT( ActiveMQException, CommandIOException )
    AMQ_CATCH_EXCEPTION_CONVERT( Exception, CommandIOException )
    AMQ_CATCHALL_THROW( CommandIOException )
}

////////////////////////////////////////////////////////////////////////////////
void ResponseCorrelator::onCommand( Command* command ) {

    // Let's see if the incoming command is a response.
    Response* response = dynamic_cast<Response*>( command );

    if( response == NULL ){

        // It's a non-response - just notify the listener.
        fire( command );
        return;
    }

    // It is a response - let's correlate ...
    synchronized( &mapMutex ){

        // Look the future request up based on the correlation id.
        std::map<unsigned int, FutureResponse*>::iterator iter =
            requestMap.find( response->getCorrelationId() );
        if( iter == requestMap.end() ){

            // This is not terrible - just log it.
            //printf("ResponseCorrelator::onCommand() - received unknown response for request: %d\n",
            //    response->getCorrelationId() );
            return;
        }

        // Get the future response (if it's in the map, it's not NULL).
        FutureResponse* futureResponse = NULL;
        futureResponse = iter->second;

        // Set the response property in the future response.
        futureResponse->setResponse( response );
    }
}

////////////////////////////////////////////////////////////////////////////////
void ResponseCorrelator::start() throw( cms::CMSException ) {

    try{

        /**
         * We're already started.
         */
        if( !closed ){
            return;
        }

        if( commandlistener == NULL ){
            throw exceptions::ActiveMQException( __FILE__, __LINE__,
                "commandListener is invalid" );
        }

        if( exceptionListener == NULL ){
            throw exceptions::ActiveMQException( __FILE__, __LINE__,
                "exceptionListener is invalid" );
        }

        if( next == NULL ){
            throw exceptions::ActiveMQException( __FILE__, __LINE__,
                "next transport is NULL" );
        }

        // Start the delegate transport object.
        next->start();

        // Mark it as open.
        closed = false;
    }
    AMQ_CATCH_RETHROW( ActiveMQException )
    AMQ_CATCH_EXCEPTION_CONVERT( Exception, ActiveMQException )
    AMQ_CATCHALL_THROW( ActiveMQException )
}

////////////////////////////////////////////////////////////////////////////////
void ResponseCorrelator::close() throw( cms::CMSException ){

    try{

        // Wake-up any outstanding requests.
        synchronized( &mapMutex ){
            std::map<unsigned int, FutureResponse*>::iterator iter = requestMap.begin();
            for( ; iter != requestMap.end(); ++iter ){
                iter->second->setResponse( NULL );
            }
        }

        if( !closed && next != NULL ){
            next->close();
        }

        closed = true;
    }
    AMQ_CATCH_RETHROW( ActiveMQException )
    AMQ_CATCH_EXCEPTION_CONVERT( Exception, ActiveMQException )
    AMQ_CATCHALL_THROW( ActiveMQException )
}

////////////////////////////////////////////////////////////////////////////////
void ResponseCorrelator::onTransportException(
    Transport* source AMQCPP_UNUSED,
    const decaf::lang::Exception& ex ) {

    // Trigger each outstanding request to complete so that we don't hang
    // forever waiting for one that has been sent without timeout.
    synchronized( &mapMutex ){
        std::map<unsigned int, FutureResponse*>::iterator iter = requestMap.begin();
        for( ; iter != requestMap.end(); ++iter ){
            iter->second->setResponse( NULL );
        }
    }

    fire( ex );
}
<commit_msg>https://issues.apache.org/activemq/browse/AMQCPP-200<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ResponseCorrelator.h"

using namespace activemq;
using namespace activemq::transport;
using namespace activemq::transport::filters;
using namespace activemq::exceptions;
using namespace decaf::lang;
using namespace decaf::lang::exceptions;

////////////////////////////////////////////////////////////////////////////////
ResponseCorrelator::ResponseCorrelator( Transport* next, bool own )
:
    TransportFilter( next, own ) {

    //nextCommandId = 0;
    nextCommandId.set(1);
    // Start in the closed state.
    closed = true;
}

////////////////////////////////////////////////////////////////////////////////
ResponseCorrelator::~ResponseCorrelator(){

    // Close the transport and destroy it.
    close();

    // Don't do anything with the future responses -
    // they should be cleaned up by each requester.
}

////////////////////////////////////////////////////////////////////////////////
void ResponseCorrelator::oneway( Command* command )
    throw( CommandIOException, decaf::lang::exceptions::UnsupportedOperationException ) {

    try{
        command->setCommandId( nextCommandId.getAndIncrement() );
        command->setResponseRequired( false );

        if( closed || next == NULL ){
            throw CommandIOException( __FILE__, __LINE__,
                "transport already closed" );
        }

        next->oneway( command );
    }
    AMQ_CATCH_RETHROW( UnsupportedOperationException )
    AMQ_CATCH_RETHROW( CommandIOException )
    AMQ_CATCH_EXCEPTION_CONVERT( ActiveMQException, CommandIOException )
    AMQ_CATCH_EXCEPTION_CONVERT( Exception, CommandIOException )
    AMQ_CATCHALL_THROW( CommandIOException )
}

////////////////////////////////////////////////////////////////////////////////
Response* ResponseCorrelator::request( Command* command )
    throw( CommandIOException, decaf::lang::exceptions::UnsupportedOperationException ) {

    try{
        command->setCommandId( nextCommandId.getAndIncrement() );
        command->setResponseRequired( true );

        // Add a future response object to the map indexed by this
        // command id.
        // TODO = This might not get deleted if an exception is thrown.
        FutureResponse* futureResponse = new FutureResponse();

        synchronized( &mapMutex ){
            requestMap[command->getCommandId()] = futureResponse;
        }

        // Wait to be notified of the response via the futureResponse
        // object.
        Response* response = NULL;

        // Send the request.
        next->oneway( command );

        // Get the response.
        response = futureResponse->getResponse();

        // Perform cleanup on the map.
        synchronized( &mapMutex ){

            // We've done our waiting - get this thing out
            // of the map.
            requestMap.erase( command->getCommandId() );

            // Destroy the futureResponse.  It is safe to
            // do this now because the other thread only
            // accesses the futureResponse within a lock on
            // the map.
            delete futureResponse;
            futureResponse = NULL;
        }

        if( response == NULL ){

            throw CommandIOException( __FILE__, __LINE__,
                "No valid response received for command: %s, check broker.",
                command->toString().c_str() );
        }

        return response;
    }
    AMQ_CATCH_RETHROW( UnsupportedOperationException )
    AMQ_CATCH_RETHROW( CommandIOException )
    AMQ_CATCH_EXCEPTION_CONVERT( ActiveMQException, CommandIOException )
    AMQ_CATCH_EXCEPTION_CONVERT( Exception, CommandIOException )
    AMQ_CATCHALL_THROW( CommandIOException )
}

////////////////////////////////////////////////////////////////////////////////
Response* ResponseCorrelator::request( Command* command, unsigned int timeout )
    throw( CommandIOException, decaf::lang::exceptions::UnsupportedOperationException ) {

    try{
        command->setCommandId( nextCommandId.getAndIncrement() );
        command->setResponseRequired( true );

        // Add a future response object to the map indexed by this
        // command id.
        // TODO = This might not get deleted if an exception is thrown.
        FutureResponse* futureResponse = new FutureResponse();

        synchronized( &mapMutex ){
            requestMap[command->getCommandId()] = futureResponse;
        }

        // Wait to be notified of the response via the futureResponse
        // object.
        Response* response = NULL;

        // Send the request.
        next->oneway( command );

        // Get the response.
        response = futureResponse->getResponse( timeout );

        // Perform cleanup on the map.
        synchronized( &mapMutex ){

            // We've done our waiting - get this thing out
            // of the map.
            requestMap.erase( command->getCommandId() );

            // Destroy the futureResponse.  It is safe to
            // do this now because the other thread only
            // accesses the futureResponse within a lock on
            // the map.
            delete futureResponse;
            futureResponse = NULL;
        }

        if( response == NULL ){

            throw CommandIOException( __FILE__, __LINE__,
                "No valid response received for command: %s, check broker.",
                command->toString().c_str() );
        }

        return response;
    }
    AMQ_CATCH_RETHROW( UnsupportedOperationException )
    AMQ_CATCH_RETHROW( CommandIOException )
    AMQ_CATCH_EXCEPTION_CONVERT( ActiveMQException, CommandIOException )
    AMQ_CATCH_EXCEPTION_CONVERT( Exception, CommandIOException )
    AMQ_CATCHALL_THROW( CommandIOException )
}

////////////////////////////////////////////////////////////////////////////////
void ResponseCorrelator::onCommand( Command* command ) {

    // Let's see if the incoming command is a response.
    Response* response = dynamic_cast<Response*>( command );

    if( response == NULL ){

        // It's a non-response - just notify the listener.
        fire( command );
        return;
    }

    // It is a response - let's correlate ...
    synchronized( &mapMutex ){

        // Look the future request up based on the correlation id.
        std::map<unsigned int, FutureResponse*>::iterator iter =
            requestMap.find( response->getCorrelationId() );
        if( iter == requestMap.end() ){

            // This is not terrible - just log it.
            //printf("ResponseCorrelator::onCommand() - received unknown response for request: %d\n",
            //    response->getCorrelationId() );
            return;
        }

        // Get the future response (if it's in the map, it's not NULL).
        FutureResponse* futureResponse = NULL;
        futureResponse = iter->second;

        // Set the response property in the future response.
        futureResponse->setResponse( response );
    }
}

////////////////////////////////////////////////////////////////////////////////
void ResponseCorrelator::start() throw( cms::CMSException ) {

    try{

        /**
         * We're already started.
         */
        if( !closed ){
            return;
        }

        if( commandlistener == NULL ){
            throw exceptions::ActiveMQException( __FILE__, __LINE__,
                "commandListener is invalid" );
        }

        if( exceptionListener == NULL ){
            throw exceptions::ActiveMQException( __FILE__, __LINE__,
                "exceptionListener is invalid" );
        }

        if( next == NULL ){
            throw exceptions::ActiveMQException( __FILE__, __LINE__,
                "next transport is NULL" );
        }

        // Start the delegate transport object.
        next->start();

        // Mark it as open.
        closed = false;
    }
    AMQ_CATCH_RETHROW( ActiveMQException )
    AMQ_CATCH_EXCEPTION_CONVERT( Exception, ActiveMQException )
    AMQ_CATCHALL_THROW( ActiveMQException )
}

////////////////////////////////////////////////////////////////////////////////
void ResponseCorrelator::close() throw( cms::CMSException ){

    try{

        // Wake-up any outstanding requests.
        synchronized( &mapMutex ){
            std::map<unsigned int, FutureResponse*>::iterator iter = requestMap.begin();
            for( ; iter != requestMap.end(); ++iter ){
                iter->second->setResponse( NULL );
            }
        }

        if( !closed && next != NULL ){
            next->close();
        }

        closed = true;
    }
    AMQ_CATCH_RETHROW( ActiveMQException )
    AMQ_CATCH_EXCEPTION_CONVERT( Exception, ActiveMQException )
    AMQ_CATCHALL_THROW( ActiveMQException )
}

////////////////////////////////////////////////////////////////////////////////
void ResponseCorrelator::onTransportException(
    Transport* source AMQCPP_UNUSED,
    const decaf::lang::Exception& ex ) {

    // Trigger each outstanding request to complete so that we don't hang
    // forever waiting for one that has been sent without timeout.
    synchronized( &mapMutex ){
        std::map<unsigned int, FutureResponse*>::iterator iter = requestMap.begin();
        for( ; iter != requestMap.end(); ++iter ){
            iter->second->setResponse( NULL );
        }
    }

    fire( ex );
}
<|endoftext|>


<commit_before>#include "sys/clock.hpp"
#include "sys/rand.hpp"
#include "client/opengl.hpp"
#include "client/ui/event.hpp"
#include "client/ui/window.hpp"
#include "client/ui/menu.hpp"
#include "client/keyboard/keycode.h"
#include "client/keyboard/keytable.h"
#include "impl/lfile.h"
#include <gtk/gtk.h>
#include <gtk/gtkgl.h>
#include <stdlib.h>
#include <unistd.h>

class GTKWindow : public UI::Window {
public:
    GTKWindow()
        : updateSize(false)
    { }

    virtual void close();

    bool updateSize;
};

void GTKWindow::close()
{
    gtk_main_quit();
}

static const unsigned int MAX_FPS = 100;
static const unsigned int MIN_FRAMETIME = 1000 / MAX_FPS;

static gboolean handle_expose(GtkWidget *area, GTKWindow *w)
{
    GdkGLContext *context = gtk_widget_get_gl_context(area);
    GdkGLDrawable *drawable = gtk_widget_get_gl_drawable(area);
    if (!gdk_gl_drawable_gl_begin(drawable, context)) {
        fputs("Could not begin GL drawing\n", stderr);
        exit(1);
    }

    if (w->updateSize) {
        GtkAllocation a;
        gtk_widget_get_allocation(area, &a);
        glViewport(0, 0, a.width, a.height);
        w->setSize(a.width, a.height);
    }
    w->draw();

    if (gdk_gl_drawable_is_double_buffered(drawable))
        gdk_gl_drawable_swap_buffers(drawable);
    else
        glFlush();
    gdk_gl_drawable_gl_end(drawable);
    return TRUE;
}

static gboolean handle_configure(GTKWindow *w)
{
    w->updateSize = true;
    return TRUE;
}

static gboolean update(gpointer user_data)
{
    GtkWidget *area = GTK_WIDGET(user_data);
    gdk_window_invalidate_rect(area->window, &area->allocation, FALSE);
    gdk_window_process_updates(area->window, FALSE);
    return TRUE;
}

static gboolean handle_key(GTKWindow *w, GdkEventKey *e, UI::EventType t)
{
    int code = e->hardware_keycode, hcode;
    if (code < 0 || code > 255)
        return TRUE;
    hcode = EVDEV_NATIVE_TO_HID[code];
    if (hcode == 255)
        return TRUE;
    UI::KeyEvent uevt(t, hcode);
    w->handleEvent(uevt);
    return TRUE;
}

static gboolean handle_motion(GTKWindow *w, GdkEventMotion *e)
{
    int x = e->x, y = w->height() - 1 - e->y;
    UI::MouseEvent uevt(UI::MouseMove, -1, x, y);
    w->handleEvent(uevt);
    return TRUE;
}

static gboolean handle_button(GTKWindow *w, GdkEventButton *e,
                              UI::EventType t)
{
    int x = e->x, y = w->height() - 1 - e->y, button;
    switch (e->button) {
    case 1: button = UI::ButtonLeft; break;
    case 2: button = UI::ButtonMiddle; break;
    case 3: button = UI::ButtonRight; break;
    default: button = UI::ButtonOther + e->button - 4; break;
    }
    UI::MouseEvent uevt(t, button, x, y);
    w->handleEvent(uevt);
    return TRUE;
}

static gboolean handle_event(GtkWidget *widget, GdkEvent *event,
                             gpointer user_data)
{
    GTKWindow *w = reinterpret_cast<GTKWindow *>(user_data);
    switch (event->type) {
    case GDK_EXPOSE:
        return handle_expose(widget, w);
    case GDK_MOTION_NOTIFY:
        return handle_motion(w, &event->motion);
    case GDK_BUTTON_PRESS:
        return handle_button(w, &event->button, UI::MouseDown);
    case GDK_BUTTON_RELEASE:
        return handle_button(w, &event->button, UI::MouseUp);
    case GDK_KEY_PRESS:
        return handle_key(w, &event->key, UI::KeyDown);
    case GDK_KEY_RELEASE:
        return handle_key(w, &event->key, UI::KeyUp);
    case GDK_CONFIGURE:
        return handle_configure(w);
    default:
        return FALSE;
    }
}

// static bool gEditor = false;

static void init(int argc, char *argv[])
{
    int opt;
    const char *altpath = NULL;

    gtk_init(&argc, &argv);
    gtk_gl_init(&argc, &argv);

    while ((opt = getopt(argc, argv, "ei:")) != -1) {
        switch (opt) {
        case 'e':
            // gEditor = true;
            break;

        case 'i':
            altpath = optarg;
            break;

        default:
            fputs("Invalid usage\n", stderr);
            exit(1);
        }
    }

    sg_path_init();
    // pathInit(altpath);
    initTime();
    Rand::global.seed();
}

int main(int argc, char *argv[])
{
    gboolean r;
    GTKWindow w;

    init(argc, argv);
    int width = 768 /* + (gEditor ? 64 : 0) */, height = 480;

    w.setSize(width, height);
    /*
    if (gEditor)
        w.setScreen(new LD22::Editor);
    else
        w.setScreen(new LD22::Screen);
    */
    w.setScreen(new UI::Menu);

    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Game");
    gtk_window_set_default_size(GTK_WINDOW(window), width, height);
    g_signal_connect_swapped(G_OBJECT(window), "destroy",
                             G_CALLBACK(gtk_main_quit), NULL);

    GtkWidget *area = gtk_drawing_area_new();
    gtk_container_add(GTK_CONTAINER(window), area);
    gtk_widget_set_can_focus(area, TRUE);
    unsigned mask = GDK_EXPOSURE_MASK |
        GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
        GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
        GDK_POINTER_MOTION_MASK;
    gtk_widget_set_events(area, mask);

    gtk_widget_show(window);
    gtk_widget_grab_focus(area);
    GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(window));

    static const int ATTRIB_1[] = {
        GDK_GL_RGBA,
        GDK_GL_DOUBLEBUFFER,
        GDK_GL_RED_SIZE, 8,
        GDK_GL_BLUE_SIZE, 8,
        GDK_GL_GREEN_SIZE, 8,
        GDK_GL_ALPHA_SIZE, 8,
        GDK_GL_ATTRIB_LIST_NONE
    };
    static const int ATTRIB_2[] = {
        GDK_GL_RGBA,
        GDK_GL_DOUBLEBUFFER,
        GDK_GL_RED_SIZE, 1,
        GDK_GL_BLUE_SIZE, 1,
        GDK_GL_GREEN_SIZE, 1,
        GDK_GL_ALPHA_SIZE, 1,
        GDK_GL_ATTRIB_LIST_NONE
    };
    const int *attrib[] = { ATTRIB_1, ATTRIB_2, 0 };
    GdkGLConfig *config = 0;
    for (unsigned int i = 0; !config && attrib[i]; ++i)
        config = gdk_gl_config_new_for_screen(screen, attrib[i]);
    if (!config) {
        fputs("Could not initialize GdkGL\n", stderr);
        return 1;
    }
    r = gtk_widget_set_gl_capability(
        area, config, NULL, TRUE, GDK_GL_RGBA_TYPE);
    if (!r) {
        fputs("Could not assign GL capability\n", stderr);
        return 1;
    }

    g_signal_connect(area, "event", G_CALLBACK(handle_event), &w);

    gtk_widget_show_all(window);

    g_timeout_add(10, update, area);

    gtk_main();

    return 0;
}
<commit_msg>Fix dangling pointer in Gtk port<commit_after>#include "sys/clock.hpp"
#include "sys/rand.hpp"
#include "client/opengl.hpp"
#include "client/ui/event.hpp"
#include "client/ui/window.hpp"
#include "client/ui/menu.hpp"
#include "client/keyboard/keycode.h"
#include "client/keyboard/keytable.h"
#include "impl/lfile.h"
#include <gtk/gtk.h>
#include <gtk/gtkgl.h>
#include <stdlib.h>
#include <unistd.h>

static gint timer;

class GTKWindow : public UI::Window {
public:
    GTKWindow()
        : updateSize(false)
    { }

    virtual void close();

    bool updateSize;
};

static void quit()
{
    if (timer > 0) {
        g_source_remove(timer);
        timer = 0;
    }
    gtk_main_quit();
}

void GTKWindow::close()
{
    quit();
}

static const unsigned int MAX_FPS = 100;
static const unsigned int MIN_FRAMETIME = 1000 / MAX_FPS;

static gboolean handle_destroy(GtkWidget *widget, GdkEvent *event,
                               gpointer user_data)
{
    quit();
    return FALSE;
}

static gboolean handle_expose(GtkWidget *area, GTKWindow *w)
{
    GdkGLContext *context = gtk_widget_get_gl_context(area);
    GdkGLDrawable *drawable = gtk_widget_get_gl_drawable(area);
    if (!gdk_gl_drawable_gl_begin(drawable, context)) {
        fputs("Could not begin GL drawing\n", stderr);
        exit(1);
    }

    if (w->updateSize) {
        GtkAllocation a;
        gtk_widget_get_allocation(area, &a);
        glViewport(0, 0, a.width, a.height);
        w->setSize(a.width, a.height);
    }
    w->draw();

    if (gdk_gl_drawable_is_double_buffered(drawable))
        gdk_gl_drawable_swap_buffers(drawable);
    else
        glFlush();
    gdk_gl_drawable_gl_end(drawable);
    return TRUE;
}

static gboolean handle_configure(GTKWindow *w)
{
    w->updateSize = true;
    return TRUE;
}

static gboolean update(gpointer user_data)
{
    GtkWidget *area = GTK_WIDGET(user_data);
    gdk_window_invalidate_rect(area->window, &area->allocation, FALSE);
    gdk_window_process_updates(area->window, FALSE);
    return TRUE;
}

static gboolean handle_key(GTKWindow *w, GdkEventKey *e, UI::EventType t)
{
    int code = e->hardware_keycode, hcode;
    if (code < 0 || code > 255)
        return TRUE;
    hcode = EVDEV_NATIVE_TO_HID[code];
    if (hcode == 255)
        return TRUE;
    UI::KeyEvent uevt(t, hcode);
    w->handleEvent(uevt);
    return TRUE;
}

static gboolean handle_motion(GTKWindow *w, GdkEventMotion *e)
{
    int x = e->x, y = w->height() - 1 - e->y;
    UI::MouseEvent uevt(UI::MouseMove, -1, x, y);
    w->handleEvent(uevt);
    return TRUE;
}

static gboolean handle_button(GTKWindow *w, GdkEventButton *e,
                              UI::EventType t)
{
    int x = e->x, y = w->height() - 1 - e->y, button;
    switch (e->button) {
    case 1: button = UI::ButtonLeft; break;
    case 2: button = UI::ButtonMiddle; break;
    case 3: button = UI::ButtonRight; break;
    default: button = UI::ButtonOther + e->button - 4; break;
    }
    UI::MouseEvent uevt(t, button, x, y);
    w->handleEvent(uevt);
    return TRUE;
}

static gboolean handle_event(GtkWidget *widget, GdkEvent *event,
                             gpointer user_data)
{
    GTKWindow *w = reinterpret_cast<GTKWindow *>(user_data);
    switch (event->type) {
    case GDK_EXPOSE:
        return handle_expose(widget, w);
    case GDK_MOTION_NOTIFY:
        return handle_motion(w, &event->motion);
    case GDK_BUTTON_PRESS:
        return handle_button(w, &event->button, UI::MouseDown);
    case GDK_BUTTON_RELEASE:
        return handle_button(w, &event->button, UI::MouseUp);
    case GDK_KEY_PRESS:
        return handle_key(w, &event->key, UI::KeyDown);
    case GDK_KEY_RELEASE:
        return handle_key(w, &event->key, UI::KeyUp);
    case GDK_CONFIGURE:
        return handle_configure(w);
    default:
        return FALSE;
    }
}

// static bool gEditor = false;

static void init(int argc, char *argv[])
{
    int opt;
    const char *altpath = NULL;

    gtk_init(&argc, &argv);
    gtk_gl_init(&argc, &argv);

    while ((opt = getopt(argc, argv, "ei:")) != -1) {
        switch (opt) {
        case 'e':
            // gEditor = true;
            break;

        case 'i':
            altpath = optarg;
            break;

        default:
            fputs("Invalid usage\n", stderr);
            exit(1);
        }
    }

    sg_path_init();
    // pathInit(altpath);
    initTime();
    Rand::global.seed();
}

int main(int argc, char *argv[])
{
    gboolean r;
    GTKWindow w;

    init(argc, argv);
    int width = 768 /* + (gEditor ? 64 : 0) */, height = 480;

    w.setSize(width, height);
    /*
    if (gEditor)
        w.setScreen(new LD22::Editor);
    else
        w.setScreen(new LD22::Screen);
    */
    w.setScreen(new UI::Menu);

    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Game");
    gtk_window_set_default_size(GTK_WINDOW(window), width, height);
    g_signal_connect_swapped(G_OBJECT(window), "destroy",
                             G_CALLBACK(handle_destroy), NULL);

    GtkWidget *area = gtk_drawing_area_new();
    gtk_container_add(GTK_CONTAINER(window), area);
    gtk_widget_set_can_focus(area, TRUE);
    unsigned mask = GDK_EXPOSURE_MASK |
        GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
        GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
        GDK_POINTER_MOTION_MASK;
    gtk_widget_set_events(area, mask);

    gtk_widget_show(window);
    gtk_widget_grab_focus(area);
    GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(window));

    static const int ATTRIB_1[] = {
        GDK_GL_RGBA,
        GDK_GL_DOUBLEBUFFER,
        GDK_GL_RED_SIZE, 8,
        GDK_GL_BLUE_SIZE, 8,
        GDK_GL_GREEN_SIZE, 8,
        GDK_GL_ALPHA_SIZE, 8,
        GDK_GL_ATTRIB_LIST_NONE
    };
    static const int ATTRIB_2[] = {
        GDK_GL_RGBA,
        GDK_GL_DOUBLEBUFFER,
        GDK_GL_RED_SIZE, 1,
        GDK_GL_BLUE_SIZE, 1,
        GDK_GL_GREEN_SIZE, 1,
        GDK_GL_ALPHA_SIZE, 1,
        GDK_GL_ATTRIB_LIST_NONE
    };
    const int *attrib[] = { ATTRIB_1, ATTRIB_2, 0 };
    GdkGLConfig *config = 0;
    for (unsigned int i = 0; !config && attrib[i]; ++i)
        config = gdk_gl_config_new_for_screen(screen, attrib[i]);
    if (!config) {
        fputs("Could not initialize GdkGL\n", stderr);
        return 1;
    }
    r = gtk_widget_set_gl_capability(
        area, config, NULL, TRUE, GDK_GL_RGBA_TYPE);
    if (!r) {
        fputs("Could not assign GL capability\n", stderr);
        return 1;
    }

    g_signal_connect(area, "event", G_CALLBACK(handle_event), &w);

    gtk_widget_show_all(window);

    timer = g_timeout_add(10, update, area);

    gtk_main();

    return 0;
}
<|endoftext|>


<commit_before>/*
  This file is part of Ingen.
  Copyright 2007-2016 David Robillard <http://drobilla.net/>

  Ingen is free software: you can redistribute it and/or modify it under the
  terms of the GNU Affero General Public License as published by the Free
  Software Foundation, either version 3 of the License, or any later version.

  Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for details.

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

#ifndef INGEN_GUI_RGBA_HPP
#define INGEN_GUI_RGBA_HPP

#include <cmath>
#include <cstdint>

namespace ingen {
namespace gui {

static inline uint32_t
rgba_to_uint(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
	return ((static_cast<uint32_t>(r) << 24) |
	        (static_cast<uint32_t>(g) << 16) |
	        (static_cast<uint32_t>(b) << 8) |
	        (static_cast<uint32_t>(a)));
}

static inline uint8_t
mono_interpolate(uint8_t v1, uint8_t v2, float f)
{
	return static_cast<int>(rintf((v2) * (f) + (v1) * (1 - (f))));
}

#define RGBA_R(x) (static_cast<uint32_t>(x) >> 24)
#define RGBA_G(x) ((static_cast<uint32_t>(x) >> 16) & 0xFF)
#define RGBA_B(x) ((static_cast<uint32_t>(x) >> 8) & 0xFF)
#define RGBA_A(x) (static_cast<uint32_t>(x) & 0xFF)

static inline uint32_t
rgba_interpolate(uint32_t c1, uint32_t c2, float f)
{
	return rgba_to_uint(
		mono_interpolate(RGBA_R(c1), RGBA_R(c2), f),
		mono_interpolate(RGBA_G(c1), RGBA_G(c2), f),
		mono_interpolate(RGBA_B(c1), RGBA_B(c2), f),
		mono_interpolate(RGBA_A(c1), RGBA_A(c2), f));
}

} // namespace gui
} // namespace ingen

#endif // INGEN_GUI_RGBA_HPP
<commit_msg>Avoid using "static inline" for functions in headers<commit_after>/*
  This file is part of Ingen.
  Copyright 2007-2016 David Robillard <http://drobilla.net/>

  Ingen is free software: you can redistribute it and/or modify it under the
  terms of the GNU Affero General Public License as published by the Free
  Software Foundation, either version 3 of the License, or any later version.

  Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for details.

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

#ifndef INGEN_GUI_RGBA_HPP
#define INGEN_GUI_RGBA_HPP

#include <cmath>
#include <cstdint>

namespace ingen {
namespace gui {

inline uint32_t
rgba_to_uint(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
	return ((static_cast<uint32_t>(r) << 24) |
	        (static_cast<uint32_t>(g) << 16) |
	        (static_cast<uint32_t>(b) << 8) |
	        (static_cast<uint32_t>(a)));
}

inline uint8_t
mono_interpolate(uint8_t v1, uint8_t v2, float f)
{
	return static_cast<int>(rintf((v2) * (f) + (v1) * (1 - (f))));
}

#define RGBA_R(x) (static_cast<uint32_t>(x) >> 24)
#define RGBA_G(x) ((static_cast<uint32_t>(x) >> 16) & 0xFF)
#define RGBA_B(x) ((static_cast<uint32_t>(x) >> 8) & 0xFF)
#define RGBA_A(x) (static_cast<uint32_t>(x) & 0xFF)

inline uint32_t
rgba_interpolate(uint32_t c1, uint32_t c2, float f)
{
	return rgba_to_uint(
		mono_interpolate(RGBA_R(c1), RGBA_R(c2), f),
		mono_interpolate(RGBA_G(c1), RGBA_G(c2), f),
		mono_interpolate(RGBA_B(c1), RGBA_B(c2), f),
		mono_interpolate(RGBA_A(c1), RGBA_A(c2), f));
}

} // namespace gui
} // namespace ingen

#endif // INGEN_GUI_RGBA_HPP
<|endoftext|>


<commit_before>#include "CLoadContext.h"

#include "CMainState.h"
#include "CMainMenuState.h"
#include "CGwenEventForwarder.h"

void CLoadContext::addLabel(std::wstring const & Label, Gwen::Color const & Color)
{
	Gwen::Controls::Label * MediumLabel = new Gwen::Controls::Label(Canvas);
	MediumLabel->SetFont(GUIManager->getMediumFont());
	MediumLabel->SetText(Label);
	MediumLabel->SetBounds(20 + Indent, LabelHeight, 1024, 300);
	MediumLabel->SetTextColor(Color);

	GUIManager->draw(true);
	CApplication::get().swapBuffers();

	LabelHeight += 40;
}

CLoadContext::CLoadContext()
	: LabelHeight(0), Indent(0)
{}

void CLoadContext::init()
{
	loadContext();

	LabelHeight = 70;
	Indent = 0;
}

void CLoadContext::run()
{
	// Load References
	CApplication & Application = CApplication::get();
	CMainState & MainState = CMainState::get();
	CMainMenuState & MenuState = CMainMenuState::get();

	GUIManager = Context->GUIContext;
	Canvas = GUIManager->getCanvas();

	// Init Canvas
	Canvas->SetBackgroundColor(Gwen::Color(32, 48, 48));
	Canvas->SetDrawBackground(true);

	// Top Label
	Gwen::Controls::Label * BigLabel = new Gwen::Controls::Label(Canvas);
	BigLabel->SetFont(GUIManager->getLargeFont());
	BigLabel->SetText(L"Loading...");
	BigLabel->SetBounds(10, 10, 1590, 300);
	BigLabel->SetTextColor(Gwen::Color(255, 255, 255, 84));

	GUIManager->draw(true);
	CApplication::get().swapBuffers();

	addLabel(L"Initializing System...");
	Application.loadEngines();
	MainState.load();
	MenuState.load();
	CGwenEventForwarder * Forwarder = new CGwenEventForwarder(GUIManager->getCanvas());

	addLabel(L"Loading Scene Shaders...");
	Application.getSceneManager().init(false, false);
	loadShaders();

	addLabel(L"Loading Scene Objects...");
	loadScene();

	Context->DataManager = new SciDataManager();
	addLabel(L"Menu is Starting...");

	// Cleanup GUI
	Canvas->RemoveAllChildren();
}

void CLoadContext::loadShaders()
{
	Indent = 60;
	bool Failed = false;

	if (! (Context->Shaders.Diffuse = CShaderLoader::loadShader("Diffuse")))
		addLabel(L"Failed to load Diffuse Shader - Glyphs will not draw.", Gwen::Color(255, 32, 32, 192)), Failed = true;
	if (! (Context->Shaders.DiffuseTexture = CShaderLoader::loadShader("DiffuseTexture")))
		addLabel(L"Failed to load Diffuse/Texture Shader - Backdrop will not draw.", Gwen::Color(255, 64, 64, 192)), Failed = true;
	if (! (Context->Shaders.Volume = CShaderLoader::loadShader("Volume2")))
		addLabel(L"Failed to load Volume Shader - Volume will not draw.", Gwen::Color(255, 64, 64, 192)), Failed = true;
	if (! (Context->Shaders.Terrain = CShaderLoader::loadShader("Terrain")))
		addLabel(L"Failed to load Terrain Shader - Terrain will not draw.", Gwen::Color(255, 64, 64, 192)), Failed = true;

	if (! Failed)
		addLabel(L"All shaders compiled successfully.", Gwen::Color(64, 255, 64, 192));

	Indent = 0;
}

void CLoadContext::loadScene()
{
	// References
	CProgramContext::SScene & Scene = Context->Scene;
	CSceneManager * SceneManager = & CApplication::get().getSceneManager();

	// OpenGL Parameters
	glClearColor(0.3f, 0.5f, 0.5f, 1.0f);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);

	// Global Light Position
	Scene.LightPosition = SVector3f(0.2f, 0.4f, 0.2f);

	// Cameras
	Scene.Camera = new CCameraControl(SVector3f(1.f, 0.3f, 4.5f));
	Scene.Camera->setProjection(60.f, CApplication::get().getAspectRatio(), 0.01f, 100.f);
	Scene.Camera->setVelocity(1.9f);
	SceneManager->setActiveCamera(Scene.Camera);

	Scene.OrbitCamera = new CPerspectiveCameraSceneObject();

	// Basic Shader/Mesh
	Scene.Cube = CMeshLoader::createCubeMesh();
	Scene.Shader = Context->Shaders.Diffuse;

	// Backdrop/SkyCube
	Scene.SkyBox = SceneManager->addMeshSceneObject(Scene.Cube, Context->Shaders.DiffuseTexture, 0);
	Scene.SkyBox->setScale(SVector3f(28.f));
	Scene.SkyBox->setTexture(0, "Space.bmp");
	Scene.SkyBox->setCullingEnabled(false);
	Scene.SkyBox->setVisible(false);

	// Light Tracker
	//Scene.LightObject = SceneManager->addMeshSceneObject(Scene.Cube, CShaderLoader::loadShader("Simple"), 0);
	//Scene.LightObject->setScale(SVector3f(0.09f));

	// Container Objects
	Scene.PointCloudObject = new ISceneObject();
	Scene.PointCloudObject->setVisible(false);
	SceneManager->addSceneObject(Scene.PointCloudObject);
	Scene.PointCloudObject->setCullingEnabled(false);

	Scene.GridObject = new ISceneObject();
	Scene.GridObject->setVisible(false);
	SceneManager->addSceneObject(Scene.GridObject);
	Scene.GridObject->setCullingEnabled(false);

	// Terrain
	Scene.Terrain = new CTerrainSceneObject();
	SceneManager->addSceneObject(Scene.Terrain);
	Scene.Terrain->setCullingEnabled(false);
	Scene.Terrain->setScale(SVector3f(0.0186f) * 2.f);
	SVector3f Scale = Scene.Terrain->getScale();
	Scale.X *= -1;
	Scene.Terrain->setScale(Scale);
	Scene.Terrain->setTranslation(SVector3f(2.026057f, -0.5f, -4.140311f));
	Scene.Terrain->setRotation(SVector3f(0.f, 10.f, 0.f));
	Scene.Terrain->setVisible(false);

	// Volume
	Scene.VolumeSceneObject = new CVolumeSceneObject();
	SceneManager->addSceneObject(Scene.VolumeSceneObject);
	Scene.VolumeSceneObject->ShowVolume = 2;
}
<commit_msg>+ Added delay upon shader failure.<commit_after>#include "CLoadContext.h"

#include "CMainState.h"
#include "CMainMenuState.h"
#include "CGwenEventForwarder.h"

void CLoadContext::addLabel(std::wstring const & Label, Gwen::Color const & Color)
{
	Gwen::Controls::Label * MediumLabel = new Gwen::Controls::Label(Canvas);
	MediumLabel->SetFont(GUIManager->getMediumFont());
	MediumLabel->SetText(Label);
	MediumLabel->SetBounds(20 + Indent, LabelHeight, 1024, 300);
	MediumLabel->SetTextColor(Color);

	GUIManager->draw(true);
	CApplication::get().swapBuffers();

	LabelHeight += 40;
}

CLoadContext::CLoadContext()
	: LabelHeight(0), Indent(0)
{}

void CLoadContext::init()
{
	loadContext();

	LabelHeight = 70;
	Indent = 0;
}

void CLoadContext::run()
{
	// Load References
	CApplication & Application = CApplication::get();
	CMainState & MainState = CMainState::get();
	CMainMenuState & MenuState = CMainMenuState::get();

	GUIManager = Context->GUIContext;
	Canvas = GUIManager->getCanvas();

	// Init Canvas
	Canvas->SetBackgroundColor(Gwen::Color(32, 48, 48));
	Canvas->SetDrawBackground(true);

	// Top Label
	Gwen::Controls::Label * BigLabel = new Gwen::Controls::Label(Canvas);
	BigLabel->SetFont(GUIManager->getLargeFont());
	BigLabel->SetText(L"Loading...");
	BigLabel->SetBounds(10, 10, 1590, 300);
	BigLabel->SetTextColor(Gwen::Color(255, 255, 255, 84));

	GUIManager->draw(true);
	CApplication::get().swapBuffers();

	addLabel(L"Initializing System...");
	Application.loadEngines();
	MainState.load();
	MenuState.load();
	CGwenEventForwarder * Forwarder = new CGwenEventForwarder(GUIManager->getCanvas());

	addLabel(L"Loading Scene Shaders...");
	Application.getSceneManager().init(false, false);
	loadShaders();

	addLabel(L"Loading Scene Objects...");
	loadScene();

	Context->DataManager = new SciDataManager();
	addLabel(L"Menu is Starting...");

	// Cleanup GUI
	Canvas->RemoveAllChildren();
}

void CLoadContext::loadShaders()
{
	Indent = 60;
	bool Failed = false;

	if (! (Context->Shaders.Diffuse = CShaderLoader::loadShader("Diffuse")))
		addLabel(L"Failed to load Diffuse Shader - Glyphs will not draw.", Gwen::Color(255, 32, 32, 192)), Failed = true;
	if (! (Context->Shaders.DiffuseTexture = CShaderLoader::loadShader("DiffuseTexture")))
		addLabel(L"Failed to load Diffuse/Texture Shader - Backdrop will not draw.", Gwen::Color(255, 64, 64, 192)), Failed = true;
	if (! (Context->Shaders.Volume = CShaderLoader::loadShader("Volume2")))
		addLabel(L"Failed to load Volume Shader - Volume will not draw.", Gwen::Color(255, 64, 64, 192)), Failed = true;
	if (! (Context->Shaders.Terrain = CShaderLoader::loadShader("Terrain")))
		addLabel(L"Failed to load Terrain Shader - Terrain will not draw.", Gwen::Color(255, 64, 64, 192)), Failed = true;

	if (! Failed)
		addLabel(L"All shaders compiled successfully.", Gwen::Color(64, 255, 64, 192));
	else
		sf::Sleep(1.5f);

	Indent = 0;
}

void CLoadContext::loadScene()
{
	// References
	CProgramContext::SScene & Scene = Context->Scene;
	CSceneManager * SceneManager = & CApplication::get().getSceneManager();

	// OpenGL Parameters
	glClearColor(0.3f, 0.5f, 0.5f, 1.0f);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);

	// Global Light Position
	Scene.LightPosition = SVector3f(0.2f, 0.4f, 0.2f);

	// Cameras
	Scene.Camera = new CCameraControl(SVector3f(1.f, 0.3f, 4.5f));
	Scene.Camera->setProjection(60.f, CApplication::get().getAspectRatio(), 0.01f, 100.f);
	Scene.Camera->setVelocity(1.9f);
	SceneManager->setActiveCamera(Scene.Camera);

	Scene.OrbitCamera = new CPerspectiveCameraSceneObject();

	// Basic Shader/Mesh
	Scene.Cube = CMeshLoader::createCubeMesh();
	Scene.Shader = Context->Shaders.Diffuse;

	// Backdrop/SkyCube
	Scene.SkyBox = SceneManager->addMeshSceneObject(Scene.Cube, Context->Shaders.DiffuseTexture, 0);
	Scene.SkyBox->setScale(SVector3f(28.f));
	Scene.SkyBox->setTexture(0, "Space.bmp");
	Scene.SkyBox->setCullingEnabled(false);
	Scene.SkyBox->setVisible(false);

	// Light Tracker
	//Scene.LightObject = SceneManager->addMeshSceneObject(Scene.Cube, CShaderLoader::loadShader("Simple"), 0);
	//Scene.LightObject->setScale(SVector3f(0.09f));

	// Container Objects
	Scene.PointCloudObject = new ISceneObject();
	Scene.PointCloudObject->setVisible(false);
	SceneManager->addSceneObject(Scene.PointCloudObject);
	Scene.PointCloudObject->setCullingEnabled(false);

	Scene.GridObject = new ISceneObject();
	Scene.GridObject->setVisible(false);
	SceneManager->addSceneObject(Scene.GridObject);
	Scene.GridObject->setCullingEnabled(false);

	// Terrain
	Scene.Terrain = new CTerrainSceneObject();
	SceneManager->addSceneObject(Scene.Terrain);
	Scene.Terrain->setCullingEnabled(false);
	Scene.Terrain->setScale(SVector3f(0.0186f) * 2.f);
	SVector3f Scale = Scene.Terrain->getScale();
	Scale.X *= -1;
	Scene.Terrain->setScale(Scale);
	Scene.Terrain->setTranslation(SVector3f(2.026057f, -0.5f, -4.140311f));
	Scene.Terrain->setRotation(SVector3f(0.f, 10.f, 0.f));
	Scene.Terrain->setVisible(false);

	// Volume
	Scene.VolumeSceneObject = new CVolumeSceneObject();
	SceneManager->addSceneObject(Scene.VolumeSceneObject);
	Scene.VolumeSceneObject->ShowVolume = 2;
}
<|endoftext|>


<commit_before>#include "actionspace/ObjectOrientedActionSpace.hpp"
#include <algorithm>
#include <iostream>
#include <iterator>

namespace libcozmo {
namespace actionspace {

bool ObjectOrientedActionSpace::action_similarity(
    const int& action_id1,
    const int& action_id2,
    double* similarity) const
{
    if (!(is_valid_action_id(action_id1) &&
          is_valid_action_id(action_id2))) {
        return false;
    }

    *similarity = sqrt(
        pow((actions[action_id1]->speed -
            actions[action_id2]->speed), 2) +
        pow((actions[action_id1]->duration -
            actions[action_id2]->duration), 2) +
        pow((actions[action_id1]->start_pos(0) -
            actions[action_id2]->start_pos(0)), 2) +
        pow((actions[action_id1]->start_pos(1) -
            actions[action_id2]->start_pos(1)), 2) +
        pow((actions[action_id1]->theta -
            actions[action_id2]->theta), 2));
    return true;
}

void ObjectOrientedActionSpace::find_headings(
    const double& theta,
    std::vector<double>* headings) const
{
    double angle = theta;
    // Force angle to be between [0, 2pi]
    if (angle > 2 * M_PI || angle < 0) {
        int cycles = angle / (2 * M_PI);
        angle -= 2 * M_PI * cycles;
        if (angle < 0) {
            angle += 2 * M_PI;
        }
    }
    headings->push_back(angle);
    for (size_t i = 0; i < 3; ++i) {
        angle -= M_PI / 2;
        if (angle < 0) {
            angle = 2 * M_PI + angle;
        }
        headings->push_back(angle);
    }
}

void ObjectOrientedActionSpace::find_start_pos(
    const Eigen::Vector2d& obj_pos,
    const double& cube_offset,
    const double& theta,
    Eigen::Vector2d* start_pos) const
{
    start_pos->x() = obj_pos.x() - v_offset * cos(theta) +
                                cube_offset * sin(theta);
    start_pos->y() = obj_pos.y() - v_offset * sin(theta) -
                                cube_offset * cos(theta);
}

void ObjectOrientedActionSpace::generate_actions(
    const Eigen::Vector2d& obj_pos,
    const double& theta,
    const double& h_offset)
{
    std::vector<double> cube_offsets;
    if (num_offset == 1) { // if only one offset, use center of side of object
        cube_offsets = {0};
    } else {
        cube_offsets = utils::linspace(-h_offset, h_offset, num_offset);
    }

    std::vector<double> headings;
    find_headings(theta, &headings);

    int action_id = 0;
    for (size_t i = 0; i < headings.size(); ++i) {
        double heading = headings[i];
        for (const auto& cube_offset : cube_offsets) {
            for (const auto& speed : speeds) {
                for (const auto& duration : durations) {
                    Eigen::Vector2d start_pos;
                    find_start_pos(obj_pos, cube_offset, heading, &start_pos);
                    if (is_valid_action_id(action_id)) {
                        ObjectOrientedActionSpace::Action* action =
                            static_cast<ObjectOrientedActionSpace::Action*>(
                                get_action(action_id));
                        action->update_action(
                            speed,
                            duration,
                            start_pos,
                            heading);
                    } else {
                        actions.push_back(new Action(
                            speed,
                            duration,
                            start_pos,
                            heading));
                    }
                    action_id++;
                }
            }
        }
    }
}

ActionSpace::Action* ObjectOrientedActionSpace::get_action(
    const int& action_id) const
{
    if (!is_valid_action_id(action_id)) {
        throw std::out_of_range("Action ID invalid");
    }
    return actions[action_id];
}

bool ObjectOrientedActionSpace::is_valid_action_id(
    const int& action_id) const
{
    return action_id < actions.size() && action_id >= 0;
}

void ObjectOrientedActionSpace::publish_action(
    const int& action_id,
    const ros::Publisher& publisher) const
{
    if (!is_valid_action_id(action_id)) {
        throw std::out_of_range("Action ID invalid");
    }
    libcozmo::ObjectOrientedAction msg;
    const Action* action = static_cast<Action*>(get_action(action_id));
    msg.speed = action->speed;
    msg.duration = action->duration;
    msg.x = action->start_pos.x();
    msg.y = action->start_pos.y();
    msg.theta = action->theta;

    publisher.publish(msg);
}

int ObjectOrientedActionSpace::size() const {
    return actions.size();
}

} // namespace actionspace
} // namespace libcozmo
<commit_msg>Added ternary operator<commit_after>#include "actionspace/ObjectOrientedActionSpace.hpp"
#include <algorithm>
#include <iostream>
#include <iterator>

namespace libcozmo {
namespace actionspace {

bool ObjectOrientedActionSpace::action_similarity(
    const int& action_id1,
    const int& action_id2,
    double* similarity) const
{
    if (!(is_valid_action_id(action_id1) &&
          is_valid_action_id(action_id2))) {
        return false;
    }

    *similarity = sqrt(
        pow((actions[action_id1]->speed -
            actions[action_id2]->speed), 2) +
        pow((actions[action_id1]->duration -
            actions[action_id2]->duration), 2) +
        pow((actions[action_id1]->start_pos(0) -
            actions[action_id2]->start_pos(0)), 2) +
        pow((actions[action_id1]->start_pos(1) -
            actions[action_id2]->start_pos(1)), 2) +
        pow((actions[action_id1]->theta -
            actions[action_id2]->theta), 2));
    return true;
}

void ObjectOrientedActionSpace::find_headings(
    const double& theta,
    std::vector<double>* headings) const
{
    double angle = theta;
    // Force angle to be between [0, 2pi]
    if (angle > 2 * M_PI || angle < 0) {
        int cycles = angle / (2 * M_PI);
        angle -= 2 * M_PI * cycles;
        if (angle < 0) {
            angle += 2 * M_PI;
        }
    }
    headings->push_back(angle);
    for (size_t i = 0; i < 3; ++i) {
        angle -= M_PI / 2;
        if (angle < 0) {
            angle = 2 * M_PI + angle;
        }
        headings->push_back(angle);
    }
}

void ObjectOrientedActionSpace::find_start_pos(
    const Eigen::Vector2d& obj_pos,
    const double& cube_offset,
    const double& theta,
    Eigen::Vector2d* start_pos) const
{
    start_pos->x() = obj_pos.x() - v_offset * cos(theta) +
                                cube_offset * sin(theta);
    start_pos->y() = obj_pos.y() - v_offset * sin(theta) -
                                cube_offset * cos(theta);
}

void ObjectOrientedActionSpace::generate_actions(
    const Eigen::Vector2d& obj_pos,
    const double& theta,
    const double& h_offset)
{
    std::vector<double> cube_offsets = num_offset == 1 ?
        std::vector<double>{0} :
        utils::linspace(-h_offset, h_offset, num_offset);

    std::vector<double> headings;
    find_headings(theta, &headings);

    int action_id = 0;
    for (size_t i = 0; i < headings.size(); ++i) {
        double heading = headings[i];
        for (const auto& cube_offset : cube_offsets) {
            for (const auto& speed : speeds) {
                for (const auto& duration : durations) {
                    Eigen::Vector2d start_pos;
                    find_start_pos(obj_pos, cube_offset, heading, &start_pos);
                    if (is_valid_action_id(action_id)) {
                        ObjectOrientedActionSpace::Action* action =
                            static_cast<ObjectOrientedActionSpace::Action*>(
                                get_action(action_id));
                        action->update_action(
                            speed,
                            duration,
                            start_pos,
                            heading);
                    } else {
                        actions.push_back(new Action(
                            speed,
                            duration,
                            start_pos,
                            heading));
                    }
                    action_id++;
                }
            }
        }
    }
}

ActionSpace::Action* ObjectOrientedActionSpace::get_action(
    const int& action_id) const
{
    if (!is_valid_action_id(action_id)) {
        throw std::out_of_range("Action ID invalid");
    }
    return actions[action_id];
}

bool ObjectOrientedActionSpace::is_valid_action_id(
    const int& action_id) const
{
    return action_id < actions.size() && action_id >= 0;
}

void ObjectOrientedActionSpace::publish_action(
    const int& action_id,
    const ros::Publisher& publisher) const
{
    if (!is_valid_action_id(action_id)) {
        throw std::out_of_range("Action ID invalid");
    }
    libcozmo::ObjectOrientedAction msg;
    const Action* action = static_cast<Action*>(get_action(action_id));
    msg.speed = action->speed;
    msg.duration = action->duration;
    msg.x = action->start_pos.x();
    msg.y = action->start_pos.y();
    msg.theta = action->theta;

    publisher.publish(msg);
}

int ObjectOrientedActionSpace::size() const {
    return actions.size();
}

} // namespace actionspace
} // namespace libcozmo
<|endoftext|>