<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/gtk/menu_gtk.h"

#include "app/gfx/gtk_util.h"
#include "app/l10n_util.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/stl_util-inl.h"
#include "base/string_util.h"
#include "chrome/browser/gtk/standard_menus.h"
#include "chrome/common/gtk_util.h"
#include "third_party/skia/include/core/SkBitmap.h"

using gtk_util::ConvertAcceleratorsFromWindowsStyle;

bool MenuGtk::block_activation_ = false;

MenuGtk::MenuGtk(MenuGtk::Delegate* delegate,
                 const MenuCreateMaterial* menu_data,
                 GtkAccelGroup* accel_group)
    : delegate_(delegate),
      dummy_accel_group_(gtk_accel_group_new()),
      menu_(gtk_menu_new()),
      factory_(this) {
  ConnectSignalHandlers();
  BuildMenuIn(menu_.get(), menu_data, accel_group);
}

MenuGtk::MenuGtk(MenuGtk::Delegate* delegate, bool load)
    : delegate_(delegate),
      dummy_accel_group_(NULL),
      menu_(gtk_menu_new()),
      factory_(this) {
  ConnectSignalHandlers();
  if (load)
    BuildMenuFromDelegate();
}

MenuGtk::~MenuGtk() {
  menu_.Destroy();
  STLDeleteContainerPointers(submenus_we_own_.begin(), submenus_we_own_.end());
  if (dummy_accel_group_)
    g_object_unref(dummy_accel_group_);
}

void MenuGtk::ConnectSignalHandlers() {
  // We connect afterwards because OnMenuShow calls SetMenuItemInfo, which may
  // take a long time or even start a nested message loop.
  g_signal_connect(menu_.get(), "show", G_CALLBACK(OnMenuShow), this);
  g_signal_connect(menu_.get(), "hide", G_CALLBACK(OnMenuHidden), this);
}

void MenuGtk::AppendMenuItemWithLabel(int command_id,
                                      const std::string& label) {
  std::string converted_label = ConvertAcceleratorsFromWindowsStyle(label);
  GtkWidget* menu_item =
      gtk_menu_item_new_with_mnemonic(converted_label.c_str());
  AppendMenuItem(command_id, menu_item);
}

void MenuGtk::AppendMenuItemWithIcon(int command_id,
                                     const std::string& label,
                                     const SkBitmap& icon) {
  GtkWidget* menu_item = BuildMenuItemWithImage(label, icon);
  AppendMenuItem(command_id, menu_item);
}

void MenuGtk::AppendCheckMenuItemWithLabel(int command_id,
                                           const std::string& label) {
  std::string converted_label = ConvertAcceleratorsFromWindowsStyle(label);
  GtkWidget* menu_item =
      gtk_check_menu_item_new_with_mnemonic(converted_label.c_str());
  AppendMenuItem(command_id, menu_item);
}

void MenuGtk::AppendSeparator() {
  GtkWidget* menu_item = gtk_separator_menu_item_new();
  gtk_widget_show(menu_item);
  gtk_menu_shell_append(GTK_MENU_SHELL(menu_.get()), menu_item);
}

void MenuGtk::AppendMenuItem(int command_id, GtkWidget* menu_item) {
  g_object_set_data(G_OBJECT(menu_item), "menu-id",
                    reinterpret_cast<void*>(command_id));

  g_signal_connect(G_OBJECT(menu_item), "activate",
                   G_CALLBACK(OnMenuItemActivated), this);

  gtk_widget_show(menu_item);
  gtk_menu_shell_append(GTK_MENU_SHELL(menu_.get()), menu_item);
}

void MenuGtk::Popup(GtkWidget* widget, GdkEvent* event) {
  DCHECK(event->type == GDK_BUTTON_PRESS)
      << "Non-button press event sent to RunMenuAt";

  GdkEventButton* event_button = reinterpret_cast<GdkEventButton*>(event);
  Popup(widget, event_button->button, event_button->time);
}

void MenuGtk::Popup(GtkWidget* widget, gint button_type, guint32 timestamp) {
  gtk_menu_popup(GTK_MENU(menu_.get()), NULL, NULL,
                 WidgetMenuPositionFunc,
                 widget,
                 button_type, timestamp);
}

void MenuGtk::PopupAsContext(guint32 event_time) {
  // TODO(estade): |button| value of 3 (6th argument) is not strictly true,
  // but does it matter?
  gtk_menu_popup(GTK_MENU(menu_.get()), NULL, NULL, NULL, NULL, 3, event_time);
}

void MenuGtk::PopupAsContextAt(guint32 event_time, gfx::Point point) {
  gtk_menu_popup(GTK_MENU(menu_.get()), NULL, NULL,
                 PointMenuPositionFunc, &point, 3, event_time);
}

void MenuGtk::PopupAsFromKeyEvent(GtkWidget* widget) {
  Popup(widget, 0, gtk_get_current_event_time());
  gtk_menu_shell_select_first(GTK_MENU_SHELL(menu_.get()), FALSE);
}

void MenuGtk::Cancel() {
  gtk_menu_popdown(GTK_MENU(menu_.get()));
}

void MenuGtk::BuildMenuIn(GtkWidget* menu,
                          const MenuCreateMaterial* menu_data,
                          GtkAccelGroup* accel_group) {
  // We keep track of the last menu item in order to group radio items.
  GtkWidget* last_menu_item = NULL;
  for (; menu_data->type != MENU_END; ++menu_data) {
    GtkWidget* menu_item = NULL;

    std::string label;
    if (menu_data->label_argument) {
      label = l10n_util::GetStringFUTF8(
          menu_data->label_id,
          l10n_util::GetStringUTF16(menu_data->label_argument));
    } else if (menu_data->label_id) {
      label = l10n_util::GetStringUTF8(menu_data->label_id);
    } else if (menu_data->type != MENU_SEPARATOR) {
      label = delegate_->GetLabel(menu_data->id);
      DCHECK(!label.empty());
    }

    label = ConvertAcceleratorsFromWindowsStyle(label);

    switch (menu_data->type) {
      case MENU_RADIO:
        if (GTK_IS_RADIO_MENU_ITEM(last_menu_item)) {
          menu_item = gtk_radio_menu_item_new_with_mnemonic_from_widget(
              GTK_RADIO_MENU_ITEM(last_menu_item), label.c_str());
        } else {
          menu_item = gtk_radio_menu_item_new_with_mnemonic(
              NULL, label.c_str());
        }
        break;
      case MENU_CHECKBOX:
        menu_item = gtk_check_menu_item_new_with_mnemonic(label.c_str());
        break;
      case MENU_SEPARATOR:
        menu_item = gtk_separator_menu_item_new();
        break;
      case MENU_NORMAL:
      default:
        menu_item = gtk_menu_item_new_with_mnemonic(label.c_str());
        break;
    }

    if (menu_data->submenu) {
      GtkWidget* submenu = gtk_menu_new();
      BuildMenuIn(submenu, menu_data->submenu, accel_group);
      gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu);
    } else if (menu_data->custom_submenu) {
      gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item),
                                menu_data->custom_submenu->menu_.get());
      submenus_we_own_.push_back(menu_data->custom_submenu);
    }

    if (menu_data->accel_key) {
      // If we ever want to let the user do any key remaping, we'll need to
      // change the following so we make a gtk_accel_map which keeps the actual
      // keys.
      gtk_widget_add_accelerator(menu_item,
                                 "activate",
                                 menu_data->only_show || !accel_group ?
                                     dummy_accel_group_ : accel_group,
                                 menu_data->accel_key,
                                 GdkModifierType(menu_data->accel_modifiers),
                                 GTK_ACCEL_VISIBLE);
    }

    g_object_set_data(G_OBJECT(menu_item), "menu-data",
                      const_cast<MenuCreateMaterial*>(menu_data));

    g_signal_connect(G_OBJECT(menu_item), "activate",
                     G_CALLBACK(OnMenuItemActivated), this);

    gtk_widget_show(menu_item);
    gtk_menu_append(menu, menu_item);
    last_menu_item = menu_item;
  }
}

GtkWidget* MenuGtk::BuildMenuItemWithImage(const std::string& label,
                                           const SkBitmap& icon) {
  std::string converted_label = ConvertAcceleratorsFromWindowsStyle(label);
  GtkWidget* menu_item =
      gtk_image_menu_item_new_with_mnemonic(converted_label.c_str());

  GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&icon);
  gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),
                                gtk_image_new_from_pixbuf(pixbuf));
  g_object_unref(pixbuf);
  if (delegate_->AlwaysShowImages())
    gtk_util::SetAlwaysShowImage(menu_item);

  return menu_item;
}

void MenuGtk::BuildMenuFromDelegate() {
  // Note that the menu IDs start at 1, not 0.
  for (int i = 1; i <= delegate_->GetItemCount(); ++i) {
    GtkWidget* menu_item = NULL;

    if (delegate_->IsItemSeparator(i)) {
      menu_item = gtk_separator_menu_item_new();
    } else if (delegate_->HasIcon(i)) {
      const SkBitmap* icon = delegate_->GetIcon(i);
      menu_item = BuildMenuItemWithImage(delegate_->GetLabel(i), *icon);
    } else {
      menu_item = gtk_menu_item_new_with_label(delegate_->GetLabel(i).c_str());
    }

    AppendMenuItem(i, menu_item);
  }
}

// static
void MenuGtk::OnMenuItemActivated(GtkMenuItem* menuitem, MenuGtk* menu) {
  if (block_activation_)
    return;

  // We receive activation messages when highlighting a menu that has a
  // submenu. Ignore them.
  if (gtk_menu_item_get_submenu(menuitem))
    return;

  const MenuCreateMaterial* data =
      reinterpret_cast<const MenuCreateMaterial*>(
          g_object_get_data(G_OBJECT(menuitem), "menu-data"));

  int id;
  if (data) {
    id = data->id;
  } else {
    id = reinterpret_cast<intptr_t>(g_object_get_data(G_OBJECT(menuitem),
                                                      "menu-id"));
  }

  // The menu item can still be activated by hotkeys even if it is disabled.
  if (menu->delegate_->IsCommandEnabled(id))
    menu->delegate_->ExecuteCommand(id);
}

// static
void MenuGtk::WidgetMenuPositionFunc(GtkMenu* menu,
                                     int* x,
                                     int* y,
                                     gboolean* push_in,
                                     void* void_widget) {
  GtkWidget* widget = GTK_WIDGET(void_widget);
  GtkRequisition menu_req;

  gtk_widget_size_request(GTK_WIDGET(menu), &menu_req);

  gdk_window_get_origin(widget->window, x, y);
  GdkScreen *screen = gtk_widget_get_screen(widget);
  gint monitor = gdk_screen_get_monitor_at_point(screen, *x, *y);

  GdkRectangle screen_rect;
  gdk_screen_get_monitor_geometry(screen, monitor,
                                  &screen_rect);

  if (GTK_WIDGET_NO_WINDOW(widget)) {
    *x += widget->allocation.x;
    *y += widget->allocation.y;
  }
  *y += widget->allocation.height;

  bool start_align =
    !!g_object_get_data(G_OBJECT(widget), "left-align-popup");
  if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT)
    start_align = !start_align;

  if (!start_align)
    *x += widget->allocation.width - menu_req.width;

  // If the menu would run off the bottom of the screen, and there is more
  // screen space up than down, then pop upwards.
  if (*y + menu_req.height >= screen_rect.height &&
      *y > screen_rect.height / 2) {
    *y -= menu_req.height;
  }

  *push_in = FALSE;
}

// static
void MenuGtk::PointMenuPositionFunc(GtkMenu* menu,
                                    int* x,
                                    int* y,
                                    gboolean* push_in,
                                    gpointer userdata) {
  *push_in = TRUE;

  gfx::Point* point = reinterpret_cast<gfx::Point*>(userdata);
  *x = point->x();
  *y = point->y();

  GtkRequisition menu_req;
  gtk_widget_size_request(GTK_WIDGET(menu), &menu_req);
  GdkScreen* screen = gdk_screen_get_default();
  gint screen_height = gdk_screen_get_height(screen);

  if (*y + menu_req.height >= screen_height)
    *y -= menu_req.height;
}

void MenuGtk::UpdateMenu() {
  gtk_container_foreach(GTK_CONTAINER(menu_.get()), SetMenuItemInfo, this);
}

// static
void MenuGtk::OnMenuShow(GtkWidget* widget, MenuGtk* menu) {
  MessageLoop::current()->PostTask(FROM_HERE,
      menu->factory_.NewRunnableMethod(&MenuGtk::UpdateMenu));
}

// static
void MenuGtk::OnMenuHidden(GtkWidget* widget, MenuGtk* menu) {
  menu->delegate_->StoppedShowing();
}

// static
void MenuGtk::SetMenuItemInfo(GtkWidget* widget, gpointer userdata) {
  if (GTK_IS_SEPARATOR_MENU_ITEM(widget)) {
    // We need to explicitly handle this case because otherwise we'll ask the
    // menu delegate about something with an invalid id.
    return;
  }

  MenuGtk* menu = reinterpret_cast<MenuGtk*>(userdata);
  int id;
  const MenuCreateMaterial* data =
      reinterpret_cast<const MenuCreateMaterial*>(
          g_object_get_data(G_OBJECT(widget), "menu-data"));
  if (data) {
    id = data->id;
  } else {
    id = reinterpret_cast<intptr_t>(g_object_get_data(G_OBJECT(widget),
                                    "menu-id"));
  }

  if (GTK_IS_CHECK_MENU_ITEM(widget)) {
    GtkCheckMenuItem* item = GTK_CHECK_MENU_ITEM(widget);

    // gtk_check_menu_item_set_active() will send the activate signal. Touching
    // the underlying "active" property will also call the "activate" handler
    // for this menu item. So we prevent the "activate" handler from
    // being called while we set the checkbox.
    // Why not use one of the glib signal-blocking functions?  Because when we
    // toggle a radio button, it will deactivate one of the other radio buttons,
    // which we don't have a pointer to.
    // Wny not make this a member variable?  Because "menu" is a pointer to the
    // root of the MenuGtk and we want to disable *all* MenuGtks, including
    // submenus.
    block_activation_ = true;
    gtk_check_menu_item_set_active(item, menu->delegate_->IsItemChecked(id));
    block_activation_ = false;
  }

  if (GTK_IS_MENU_ITEM(widget)) {
    gtk_widget_set_sensitive(
        widget, menu->delegate_->IsCommandEnabled(id));

    GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(widget));
    if (submenu) {
      gtk_container_foreach(GTK_CONTAINER(submenu), &SetMenuItemInfo,
                            userdata);
    }
  }
}
<commit_msg>Fix radio items in MenuGtk.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/gtk/menu_gtk.h"

#include "app/gfx/gtk_util.h"
#include "app/l10n_util.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/stl_util-inl.h"
#include "base/string_util.h"
#include "chrome/browser/gtk/standard_menus.h"
#include "chrome/common/gtk_util.h"
#include "third_party/skia/include/core/SkBitmap.h"

using gtk_util::ConvertAcceleratorsFromWindowsStyle;

bool MenuGtk::block_activation_ = false;

MenuGtk::MenuGtk(MenuGtk::Delegate* delegate,
                 const MenuCreateMaterial* menu_data,
                 GtkAccelGroup* accel_group)
    : delegate_(delegate),
      dummy_accel_group_(gtk_accel_group_new()),
      menu_(gtk_menu_new()),
      factory_(this) {
  ConnectSignalHandlers();
  BuildMenuIn(menu_.get(), menu_data, accel_group);
}

MenuGtk::MenuGtk(MenuGtk::Delegate* delegate, bool load)
    : delegate_(delegate),
      dummy_accel_group_(NULL),
      menu_(gtk_menu_new()),
      factory_(this) {
  ConnectSignalHandlers();
  if (load)
    BuildMenuFromDelegate();
}

MenuGtk::~MenuGtk() {
  menu_.Destroy();
  STLDeleteContainerPointers(submenus_we_own_.begin(), submenus_we_own_.end());
  if (dummy_accel_group_)
    g_object_unref(dummy_accel_group_);
}

void MenuGtk::ConnectSignalHandlers() {
  // We connect afterwards because OnMenuShow calls SetMenuItemInfo, which may
  // take a long time or even start a nested message loop.
  g_signal_connect(menu_.get(), "show", G_CALLBACK(OnMenuShow), this);
  g_signal_connect(menu_.get(), "hide", G_CALLBACK(OnMenuHidden), this);
}

void MenuGtk::AppendMenuItemWithLabel(int command_id,
                                      const std::string& label) {
  std::string converted_label = ConvertAcceleratorsFromWindowsStyle(label);
  GtkWidget* menu_item =
      gtk_menu_item_new_with_mnemonic(converted_label.c_str());
  AppendMenuItem(command_id, menu_item);
}

void MenuGtk::AppendMenuItemWithIcon(int command_id,
                                     const std::string& label,
                                     const SkBitmap& icon) {
  GtkWidget* menu_item = BuildMenuItemWithImage(label, icon);
  AppendMenuItem(command_id, menu_item);
}

void MenuGtk::AppendCheckMenuItemWithLabel(int command_id,
                                           const std::string& label) {
  std::string converted_label = ConvertAcceleratorsFromWindowsStyle(label);
  GtkWidget* menu_item =
      gtk_check_menu_item_new_with_mnemonic(converted_label.c_str());
  AppendMenuItem(command_id, menu_item);
}

void MenuGtk::AppendSeparator() {
  GtkWidget* menu_item = gtk_separator_menu_item_new();
  gtk_widget_show(menu_item);
  gtk_menu_shell_append(GTK_MENU_SHELL(menu_.get()), menu_item);
}

void MenuGtk::AppendMenuItem(int command_id, GtkWidget* menu_item) {
  g_object_set_data(G_OBJECT(menu_item), "menu-id",
                    reinterpret_cast<void*>(command_id));

  g_signal_connect(G_OBJECT(menu_item), "activate",
                   G_CALLBACK(OnMenuItemActivated), this);

  gtk_widget_show(menu_item);
  gtk_menu_shell_append(GTK_MENU_SHELL(menu_.get()), menu_item);
}

void MenuGtk::Popup(GtkWidget* widget, GdkEvent* event) {
  DCHECK(event->type == GDK_BUTTON_PRESS)
      << "Non-button press event sent to RunMenuAt";

  GdkEventButton* event_button = reinterpret_cast<GdkEventButton*>(event);
  Popup(widget, event_button->button, event_button->time);
}

void MenuGtk::Popup(GtkWidget* widget, gint button_type, guint32 timestamp) {
  gtk_menu_popup(GTK_MENU(menu_.get()), NULL, NULL,
                 WidgetMenuPositionFunc,
                 widget,
                 button_type, timestamp);
}

void MenuGtk::PopupAsContext(guint32 event_time) {
  // TODO(estade): |button| value of 3 (6th argument) is not strictly true,
  // but does it matter?
  gtk_menu_popup(GTK_MENU(menu_.get()), NULL, NULL, NULL, NULL, 3, event_time);
}

void MenuGtk::PopupAsContextAt(guint32 event_time, gfx::Point point) {
  gtk_menu_popup(GTK_MENU(menu_.get()), NULL, NULL,
                 PointMenuPositionFunc, &point, 3, event_time);
}

void MenuGtk::PopupAsFromKeyEvent(GtkWidget* widget) {
  Popup(widget, 0, gtk_get_current_event_time());
  gtk_menu_shell_select_first(GTK_MENU_SHELL(menu_.get()), FALSE);
}

void MenuGtk::Cancel() {
  gtk_menu_popdown(GTK_MENU(menu_.get()));
}

void MenuGtk::BuildMenuIn(GtkWidget* menu,
                          const MenuCreateMaterial* menu_data,
                          GtkAccelGroup* accel_group) {
  // We keep track of the last menu item in order to group radio items.
  GtkWidget* last_menu_item = NULL;
  for (; menu_data->type != MENU_END; ++menu_data) {
    GtkWidget* menu_item = NULL;

    std::string label;
    if (menu_data->label_argument) {
      label = l10n_util::GetStringFUTF8(
          menu_data->label_id,
          l10n_util::GetStringUTF16(menu_data->label_argument));
    } else if (menu_data->label_id) {
      label = l10n_util::GetStringUTF8(menu_data->label_id);
    } else if (menu_data->type != MENU_SEPARATOR) {
      label = delegate_->GetLabel(menu_data->id);
      DCHECK(!label.empty());
    }

    label = ConvertAcceleratorsFromWindowsStyle(label);

    switch (menu_data->type) {
      case MENU_RADIO:
        if (GTK_IS_RADIO_MENU_ITEM(last_menu_item)) {
          menu_item = gtk_radio_menu_item_new_with_mnemonic_from_widget(
              GTK_RADIO_MENU_ITEM(last_menu_item), label.c_str());
        } else {
          menu_item = gtk_radio_menu_item_new_with_mnemonic(
              NULL, label.c_str());
        }
        break;
      case MENU_CHECKBOX:
        menu_item = gtk_check_menu_item_new_with_mnemonic(label.c_str());
        break;
      case MENU_SEPARATOR:
        menu_item = gtk_separator_menu_item_new();
        break;
      case MENU_NORMAL:
      default:
        menu_item = gtk_menu_item_new_with_mnemonic(label.c_str());
        break;
    }

    if (menu_data->submenu) {
      GtkWidget* submenu = gtk_menu_new();
      BuildMenuIn(submenu, menu_data->submenu, accel_group);
      gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu);
    } else if (menu_data->custom_submenu) {
      gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item),
                                menu_data->custom_submenu->menu_.get());
      submenus_we_own_.push_back(menu_data->custom_submenu);
    }

    if (menu_data->accel_key) {
      // If we ever want to let the user do any key remaping, we'll need to
      // change the following so we make a gtk_accel_map which keeps the actual
      // keys.
      gtk_widget_add_accelerator(menu_item,
                                 "activate",
                                 menu_data->only_show || !accel_group ?
                                     dummy_accel_group_ : accel_group,
                                 menu_data->accel_key,
                                 GdkModifierType(menu_data->accel_modifiers),
                                 GTK_ACCEL_VISIBLE);
    }

    g_object_set_data(G_OBJECT(menu_item), "menu-data",
                      const_cast<MenuCreateMaterial*>(menu_data));

    g_signal_connect(G_OBJECT(menu_item), "activate",
                     G_CALLBACK(OnMenuItemActivated), this);

    gtk_widget_show(menu_item);
    gtk_menu_append(menu, menu_item);
    last_menu_item = menu_item;
  }
}

GtkWidget* MenuGtk::BuildMenuItemWithImage(const std::string& label,
                                           const SkBitmap& icon) {
  std::string converted_label = ConvertAcceleratorsFromWindowsStyle(label);
  GtkWidget* menu_item =
      gtk_image_menu_item_new_with_mnemonic(converted_label.c_str());

  GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&icon);
  gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),
                                gtk_image_new_from_pixbuf(pixbuf));
  g_object_unref(pixbuf);
  if (delegate_->AlwaysShowImages())
    gtk_util::SetAlwaysShowImage(menu_item);

  return menu_item;
}

void MenuGtk::BuildMenuFromDelegate() {
  // Note that the menu IDs start at 1, not 0.
  for (int i = 1; i <= delegate_->GetItemCount(); ++i) {
    GtkWidget* menu_item = NULL;

    if (delegate_->IsItemSeparator(i)) {
      menu_item = gtk_separator_menu_item_new();
    } else if (delegate_->HasIcon(i)) {
      const SkBitmap* icon = delegate_->GetIcon(i);
      menu_item = BuildMenuItemWithImage(delegate_->GetLabel(i), *icon);
    } else {
      menu_item = gtk_menu_item_new_with_label(delegate_->GetLabel(i).c_str());
    }

    AppendMenuItem(i, menu_item);
  }
}

// static
void MenuGtk::OnMenuItemActivated(GtkMenuItem* menuitem, MenuGtk* menu) {
  if (block_activation_)
    return;

  // We receive activation messages when highlighting a menu that has a
  // submenu. Ignore them.
  if (gtk_menu_item_get_submenu(menuitem))
    return;

  // The activate signal is sent to radio items as they get deselected;
  // ignore it in this case.
  if (GTK_IS_RADIO_MENU_ITEM(menuitem) &&
      !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) {
    return;
  }

  const MenuCreateMaterial* data =
      reinterpret_cast<const MenuCreateMaterial*>(
          g_object_get_data(G_OBJECT(menuitem), "menu-data"));

  int id;
  if (data) {
    id = data->id;
  } else {
    id = reinterpret_cast<intptr_t>(g_object_get_data(G_OBJECT(menuitem),
                                                      "menu-id"));
  }

  // The menu item can still be activated by hotkeys even if it is disabled.
  if (menu->delegate_->IsCommandEnabled(id))
    menu->delegate_->ExecuteCommand(id);
}

// static
void MenuGtk::WidgetMenuPositionFunc(GtkMenu* menu,
                                     int* x,
                                     int* y,
                                     gboolean* push_in,
                                     void* void_widget) {
  GtkWidget* widget = GTK_WIDGET(void_widget);
  GtkRequisition menu_req;

  gtk_widget_size_request(GTK_WIDGET(menu), &menu_req);

  gdk_window_get_origin(widget->window, x, y);
  GdkScreen *screen = gtk_widget_get_screen(widget);
  gint monitor = gdk_screen_get_monitor_at_point(screen, *x, *y);

  GdkRectangle screen_rect;
  gdk_screen_get_monitor_geometry(screen, monitor,
                                  &screen_rect);

  if (GTK_WIDGET_NO_WINDOW(widget)) {
    *x += widget->allocation.x;
    *y += widget->allocation.y;
  }
  *y += widget->allocation.height;

  bool start_align =
    !!g_object_get_data(G_OBJECT(widget), "left-align-popup");
  if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT)
    start_align = !start_align;

  if (!start_align)
    *x += widget->allocation.width - menu_req.width;

  // If the menu would run off the bottom of the screen, and there is more
  // screen space up than down, then pop upwards.
  if (*y + menu_req.height >= screen_rect.height &&
      *y > screen_rect.height / 2) {
    *y -= menu_req.height;
  }

  *push_in = FALSE;
}

// static
void MenuGtk::PointMenuPositionFunc(GtkMenu* menu,
                                    int* x,
                                    int* y,
                                    gboolean* push_in,
                                    gpointer userdata) {
  *push_in = TRUE;

  gfx::Point* point = reinterpret_cast<gfx::Point*>(userdata);
  *x = point->x();
  *y = point->y();

  GtkRequisition menu_req;
  gtk_widget_size_request(GTK_WIDGET(menu), &menu_req);
  GdkScreen* screen = gdk_screen_get_default();
  gint screen_height = gdk_screen_get_height(screen);

  if (*y + menu_req.height >= screen_height)
    *y -= menu_req.height;
}

void MenuGtk::UpdateMenu() {
  gtk_container_foreach(GTK_CONTAINER(menu_.get()), SetMenuItemInfo, this);
}

// static
void MenuGtk::OnMenuShow(GtkWidget* widget, MenuGtk* menu) {
  MessageLoop::current()->PostTask(FROM_HERE,
      menu->factory_.NewRunnableMethod(&MenuGtk::UpdateMenu));
}

// static
void MenuGtk::OnMenuHidden(GtkWidget* widget, MenuGtk* menu) {
  menu->delegate_->StoppedShowing();
}

// static
void MenuGtk::SetMenuItemInfo(GtkWidget* widget, gpointer userdata) {
  if (GTK_IS_SEPARATOR_MENU_ITEM(widget)) {
    // We need to explicitly handle this case because otherwise we'll ask the
    // menu delegate about something with an invalid id.
    return;
  }

  MenuGtk* menu = reinterpret_cast<MenuGtk*>(userdata);
  int id;
  const MenuCreateMaterial* data =
      reinterpret_cast<const MenuCreateMaterial*>(
          g_object_get_data(G_OBJECT(widget), "menu-data"));
  if (data) {
    id = data->id;
  } else {
    id = reinterpret_cast<intptr_t>(g_object_get_data(G_OBJECT(widget),
                                    "menu-id"));
  }

  if (GTK_IS_CHECK_MENU_ITEM(widget)) {
    GtkCheckMenuItem* item = GTK_CHECK_MENU_ITEM(widget);

    // gtk_check_menu_item_set_active() will send the activate signal. Touching
    // the underlying "active" property will also call the "activate" handler
    // for this menu item. So we prevent the "activate" handler from
    // being called while we set the checkbox.
    // Why not use one of the glib signal-blocking functions?  Because when we
    // toggle a radio button, it will deactivate one of the other radio buttons,
    // which we don't have a pointer to.
    // Wny not make this a member variable?  Because "menu" is a pointer to the
    // root of the MenuGtk and we want to disable *all* MenuGtks, including
    // submenus.
    block_activation_ = true;
    gtk_check_menu_item_set_active(item, menu->delegate_->IsItemChecked(id));
    block_activation_ = false;
  }

  if (GTK_IS_MENU_ITEM(widget)) {
    gtk_widget_set_sensitive(
        widget, menu->delegate_->IsCommandEnabled(id));

    GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(widget));
    if (submenu) {
      gtk_container_foreach(GTK_CONTAINER(submenu), &SetMenuItemInfo,
                            userdata);
    }
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/options_util.h"

#include "chrome/browser/browser_process.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/metrics/metrics_service.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
#include "chrome/installer/util/google_update_settings.h"

// static
void OptionsUtil::ResetToDefaults(Profile* profile) {
  // TODO(tc): It would be nice if we could generate this list automatically so
  // changes to any of the options pages doesn't require updating this list
  // manually.
  PrefService* prefs = profile->GetPrefs();
  const wchar_t* kUserPrefs[] = {
    prefs::kAcceptLanguages,
    prefs::kAlternateErrorPagesEnabled,
    prefs::kCookieBehavior,
    prefs::kDefaultCharset,
    prefs::kDnsPrefetchingEnabled,
#if defined(OS_LINUX)
    prefs::kCertRevocationCheckingEnabled,
    prefs::kSSL2Enabled,
    prefs::kSSL3Enabled,
    prefs::kTLS1Enabled,
#endif
    prefs::kDownloadDefaultDirectory,
    prefs::kDownloadExtensionsToOpen,
    prefs::kEnableSpellCheck,
    prefs::kFormAutofillEnabled,
    prefs::kHomePage,
    prefs::kHomePageIsNewTabPage,
    prefs::kMixedContentFiltering,
    prefs::kPromptForDownload,
    prefs::kPasswordManagerEnabled,
    prefs::kRestoreOnStartup,
    prefs::kSafeBrowsingEnabled,
    prefs::kSearchSuggestEnabled,
    prefs::kShowHomeButton,
    prefs::kSpellCheckDictionary,
    prefs::kURLsToRestoreOnStartup,
    prefs::kWebKitDefaultFixedFontSize,
    prefs::kWebKitDefaultFontSize,
    prefs::kWebKitFixedFontFamily,
    prefs::kWebKitJavaEnabled,
    prefs::kWebKitJavascriptEnabled,
    prefs::kWebKitLoadsImagesAutomatically,
    prefs::kWebKitPluginsEnabled,
    prefs::kWebKitSansSerifFontFamily,
    prefs::kWebKitSerifFontFamily,
  };
  for (size_t i = 0; i < arraysize(kUserPrefs); ++i)
    prefs->ClearPref(kUserPrefs[i]);

  PrefService* local_state = g_browser_process->local_state();
  // Note that we don't reset the kMetricsReportingEnabled preference here
  // because the reset will reset it to the default setting specified in Chrome
  // source, not the default setting selected by the user on the web page where
  // they downloaded Chrome. This means that if the user ever resets their
  // settings they'll either inadvertedly enable this logging or disable it.
  // One is undesirable for them, one is undesirable for us. For now, we just
  // don't reset it.
  const wchar_t* kLocalStatePrefs[] = {
    prefs::kApplicationLocale,
    prefs::kOptionsWindowLastTabIndex,
  };
  for (size_t i = 0; i < arraysize(kLocalStatePrefs); ++i)
    local_state->ClearPref(kLocalStatePrefs[i]);
}

// static
bool OptionsUtil::ResolveMetricsReportingEnabled(bool enabled) {
  GoogleUpdateSettings::SetCollectStatsConsent(enabled);
  bool update_pref = GoogleUpdateSettings::GetCollectStatsConsent();

  if (enabled != update_pref) {
    DLOG(INFO) <<
        "OptionsUtil: Unable to set crash report status to " <<
        enabled;
  }

  // Only change the pref if GoogleUpdateSettings::GetCollectStatsConsent
  // succeeds.
  enabled = update_pref;

  MetricsService* metrics = g_browser_process->metrics_service();
  DCHECK(metrics);
  if (metrics) {
    metrics->SetUserPermitsUpload(enabled);
    if (enabled)
      metrics->Start();
    else
      metrics->Stop();
  }

  return enabled;
}
<commit_msg>Clears the "Clear auto-opening settings" button (put it in the default state, ie, disabled), when the user  clicks in "Reset to defaults" button.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/options_util.h"

#include "chrome/browser/browser_process.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/download/download_manager.h"
#include "chrome/browser/metrics/metrics_service.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
#include "chrome/installer/util/google_update_settings.h"

// static
void OptionsUtil::ResetToDefaults(Profile* profile) {
  // TODO(tc): It would be nice if we could generate this list automatically so
  // changes to any of the options pages doesn't require updating this list
  // manually.
  PrefService* prefs = profile->GetPrefs();
  const wchar_t* kUserPrefs[] = {
    prefs::kAcceptLanguages,
    prefs::kAlternateErrorPagesEnabled,
    prefs::kCookieBehavior,
    prefs::kDefaultCharset,
    prefs::kDnsPrefetchingEnabled,
#if defined(OS_LINUX)
    prefs::kCertRevocationCheckingEnabled,
    prefs::kSSL2Enabled,
    prefs::kSSL3Enabled,
    prefs::kTLS1Enabled,
#endif
    prefs::kDownloadDefaultDirectory,
    prefs::kDownloadExtensionsToOpen,
    prefs::kEnableSpellCheck,
    prefs::kFormAutofillEnabled,
    prefs::kHomePage,
    prefs::kHomePageIsNewTabPage,
    prefs::kMixedContentFiltering,
    prefs::kPromptForDownload,
    prefs::kPasswordManagerEnabled,
    prefs::kRestoreOnStartup,
    prefs::kSafeBrowsingEnabled,
    prefs::kSearchSuggestEnabled,
    prefs::kShowHomeButton,
    prefs::kSpellCheckDictionary,
    prefs::kURLsToRestoreOnStartup,
    prefs::kWebKitDefaultFixedFontSize,
    prefs::kWebKitDefaultFontSize,
    prefs::kWebKitFixedFontFamily,
    prefs::kWebKitJavaEnabled,
    prefs::kWebKitJavascriptEnabled,
    prefs::kWebKitLoadsImagesAutomatically,
    prefs::kWebKitPluginsEnabled,
    prefs::kWebKitSansSerifFontFamily,
    prefs::kWebKitSerifFontFamily,
  };
  profile->GetDownloadManager()->ResetAutoOpenFiles();
  for (size_t i = 0; i < arraysize(kUserPrefs); ++i)
    prefs->ClearPref(kUserPrefs[i]);

  PrefService* local_state = g_browser_process->local_state();
  // Note that we don't reset the kMetricsReportingEnabled preference here
  // because the reset will reset it to the default setting specified in Chrome
  // source, not the default setting selected by the user on the web page where
  // they downloaded Chrome. This means that if the user ever resets their
  // settings they'll either inadvertedly enable this logging or disable it.
  // One is undesirable for them, one is undesirable for us. For now, we just
  // don't reset it.
  const wchar_t* kLocalStatePrefs[] = {
    prefs::kApplicationLocale,
    prefs::kOptionsWindowLastTabIndex,
  };
  for (size_t i = 0; i < arraysize(kLocalStatePrefs); ++i)
    local_state->ClearPref(kLocalStatePrefs[i]);
}

// static
bool OptionsUtil::ResolveMetricsReportingEnabled(bool enabled) {
  GoogleUpdateSettings::SetCollectStatsConsent(enabled);
  bool update_pref = GoogleUpdateSettings::GetCollectStatsConsent();

  if (enabled != update_pref) {
    DLOG(INFO) <<
        "OptionsUtil: Unable to set crash report status to " <<
        enabled;
  }

  // Only change the pref if GoogleUpdateSettings::GetCollectStatsConsent
  // succeeds.
  enabled = update_pref;

  MetricsService* metrics = g_browser_process->metrics_service();
  DCHECK(metrics);
  if (metrics) {
    metrics->SetUserPermitsUpload(enabled);
    if (enabled)
      metrics->Start();
    else
      metrics->Stop();
  }

  return enabled;
}
<|endoftext|>


<commit_before> //=============================================================================================================
/**
* @file     connectivitymeasures.cpp
* @author   Lorenz Esch <Lorenz.Esch@tu-ilmenau.de>;
*           Matti Hamalainen <msh@nmr.mgh.harvard.edu>
* @version  1.0
* @date     July, 2016
*
* @section  LICENSE
*
* Copyright (C) 2016, Your name and Matti Hamalainen. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that
* the following conditions are met:
*     * Redistributions of source code must retain the above copyright notice, this list of conditions and the
*       following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
*       the following disclaimer in the documentation and/or other materials provided with the distribution.
*     * Neither the name of MNE-CPP authors nor the names of its contributors may be used
*       to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @brief    ConnectivityMeasures class definition.
*
*/

//*************************************************************************************************************
//=============================================================================================================
// INCLUDES
//=============================================================================================================

#include "connectivitymeasures.h"
#include "network/networknode.h"
#include "network/networkedge.h"

#include <iostream>

//*************************************************************************************************************
//=============================================================================================================
// INCLUDES
//=============================================================================================================

//*************************************************************************************************************
//=============================================================================================================
// QT INCLUDES
//=============================================================================================================

//*************************************************************************************************************
//=============================================================================================================
// Eigen INCLUDES
//=============================================================================================================

#include <unsupported/Eigen/FFT>

//*************************************************************************************************************
//=============================================================================================================
// USED NAMESPACES
//=============================================================================================================

using namespace CONNECTIVITYLIB;
using namespace Eigen;

//*************************************************************************************************************
//=============================================================================================================
// DEFINE GLOBAL METHODS
//=============================================================================================================

//*************************************************************************************************************
//=============================================================================================================
// DEFINE MEMBER METHODS
//=============================================================================================================

ConnectivityMeasures::ConnectivityMeasures()
{
}

//*************************************************************************************************************

Network::SPtr ConnectivityMeasures::crossCorrelation(const MatrixXd& matData, const MatrixX3f& matVert)
{
    Network::SPtr finalNetwork  = Network::SPtr(new Network());

    //Create nodes
    for(int i = 0; i < matData.rows(); ++i)
    {
        RowVectorXf rowVert = RowVectorXf::Zero(3);
        if(matVert.rows() != 0) {
            rowVert(0) = matVert.row(i)(0);
            rowVert(1) = matVert.row(i)(1);
            rowVert(2) = matVert.row(i)(2);
        }

        *finalNetwork << NetworkNode::SPtr(new NetworkNode(i, rowVert));
    }

    //Create edges
    for(int i = 0; i < matData.rows(); ++i)
    {
        for(int j = i; j < matData.rows(); ++j)
        {
            QPair<int,double> crossCorrPair = eigenCrossCorrelation(matData.row(i), matData.row(j));

            QSharedPointer<NetworkEdge> pEdge = QSharedPointer<NetworkEdge>(new NetworkEdge(finalNetwork->getNodes()[i], finalNetwork->getNodes()[j], crossCorrPair.second));

            *finalNetwork->getNodes()[i] << pEdge;
            *finalNetwork << pEdge;
        }
    }

//    finalNetwork.scale();
//    matDist /= matDist.maxCoeff();

    return finalNetwork;
}

//*************************************************************************************************************

Eigen::MatrixXd ConnectivityMeasures::crossCorrelation(const MatrixXd& matData)
{
    MatrixXd matDist(matData.rows(), matData.rows());
    matDist.setZero();

    for(int i = 0; i < matDist.rows(); ++i)
    {
        QPair<int,double> crossCorrPair = eigenCrossCorrelation(matData.row(i), matData.row(i));

        double dScaling = crossCorrPair.second;

        for(int j = i; j < matDist.rows(); ++j)
        {
            QPair<int,double> crossCorrPair = eigenCrossCorrelation(matData.row(i), matData.row(j));
            matDist(i,j) = crossCorrPair.second/* / dScaling*/;
        }
    }

    matDist /= matDist.maxCoeff();

    return matDist;
}

//*************************************************************************************************************

QPair<int,double> ConnectivityMeasures::eigenCrossCorrelation(const RowVectorXd& xCorrInputVecFirstIn, const RowVectorXd& xCorrInputVecSecondIn)
//std::pair<double, double> ConnectivityMeasures::eigenCrossCorrelation(std::vector<double>& xCorrInputVecFirst, std::vector<double>& xCorrInputVecSecond)
{
//    Eigen::FFT<double> fft;
//    int N = std::max(xCorrInputVecFirst.size(), xCorrInputVecSecond.size());

//    //Compute the FFT size as the "next power of 2" of the input vector's length (max)
//    int b = ceil(log2(2.0 * N - 1));
//    int fftsize = pow(2,b);
//    int end = fftsize - 1;
//    int maxlag = N - 1;
//    size_t firstSize = xCorrInputVecFirst.size();
//    size_t secondSize = xCorrInputVecSecond.size();

//    //Zero Padd
//    for (int i = xCorrInputVecFirst.size(); i < fftsize; i++)
//    {
//        xCorrInputVecFirst.push_back(0);
//    }

//    for (int i = xCorrInputVecSecond.size(); i < fftsize; i++)
//    {
//        xCorrInputVecSecond.push_back(0);
//    }

//    std::vector<std::complex<double> > freqvec;
//    std::vector<std::complex<double> > freqvec2;

//    //FFT for freq domain to both vectors
//    fft.fwd( freqvec,xCorrInputVecFirst);
//    fft.fwd( freqvec2,xCorrInputVecSecond);

//    //Main step of cross corr
//    for (int i = 0; i < fftsize; i++)
//    {
//        freqvec[i] = freqvec[i] * std::conj(freqvec2[i]);
//    }

//    std::vector<double> result;
//    fft.inv(result, freqvec);

//    //Will get rid of extra zero padding and move minus lags to beginning without copy
//    std::vector<double> result2(std::make_move_iterator(result.begin() + end - maxlag + 1),
//                                std::make_move_iterator(result.end()));

//    result2.insert(result2.end(), make_move_iterator(result.begin())
//                   , make_move_iterator(result.begin()+maxlag));

//    auto minMaxRange = std::minmax_element(result2.begin(),
//                        result2.end());

//    //Will take back the changes which made in input vector
//    if (xCorrInputVecFirst.size() != firstSize)
//        xCorrInputVecFirst.resize(firstSize);
//    if (xCorrInputVecSecond.size() != secondSize)
//        xCorrInputVecSecond.resize(secondSize);

//    //Return val
//    auto resultIndex = ((minMaxRange.second - result2.begin()) - N + 1);

//    auto maxValue = result[minMaxRange.second - result.begin()];
//    return std::make_pair (resultIndex, maxValue);

    Eigen::FFT<double> fft;

    int N = std::max(xCorrInputVecFirstIn.cols(), xCorrInputVecSecondIn.cols());

    //Compute the FFT size as the "next power of 2" of the input vector's length (max)
    int b = ceil(log2(2.0 * N - 1));
    int fftsize = pow(2,b);
    int end = fftsize - 1;
    int maxlag = N - 1;

    //Zero Padd
    RowVectorXd xCorrInputVecFirst(fftsize);
    xCorrInputVecFirst.setZero();
    xCorrInputVecFirst.head(xCorrInputVecFirstIn.cols()) = xCorrInputVecFirstIn;

    RowVectorXd xCorrInputVecSecond(fftsize);
    xCorrInputVecSecond.setZero();
    xCorrInputVecSecond.head(xCorrInputVecSecondIn.cols()) = xCorrInputVecSecondIn;

    //FFT for freq domain to both vectors
    RowVectorXcd freqvec;
    RowVectorXcd freqvec2;

    fft.fwd(freqvec, xCorrInputVecFirst);
    fft.fwd(freqvec2, xCorrInputVecSecond);

    //Create conjugate complex
    freqvec2.conjugate();

    //Main step of cross corr
    for (int i = 0; i < fftsize; i++)
    {
        freqvec[i] = freqvec[i] * freqvec2[i];
    }

    RowVectorXd result;
    fft.inv(result, freqvec);

    //Will get rid of extra zero padding
    RowVectorXd result2 = result;//.segment(maxlag, N);

    QPair<int,int> minMaxRange;
    int idx = 0;
    result2.minCoeff(&idx);
    minMaxRange.first = idx;
    result2.maxCoeff(&idx);
    minMaxRange.second = idx;

//    std::cout<<"result2(minMaxRange.first)"<<result2(minMaxRange.first)<<std::endl;
//    std::cout<<"result2(minMaxRange.second)"<<result2(minMaxRange.second)<<std::endl;
//    std::cout<<"b"<<b<<std::endl;
//    std::cout<<"fftsize"<<fftsize<<std::endl;
//    std::cout<<"end"<<end<<std::endl;
//    std::cout<<"maxlag"<<maxlag<<std::endl;

    //Return val
    int resultIndex = minMaxRange.second;
    double maxValue = result2(resultIndex);

    return QPair<int,double>(resultIndex, maxValue);
}
<commit_msg>[LIB-117] Add safety check to crossCorrelation function<commit_after> //=============================================================================================================
/**
* @file     connectivitymeasures.cpp
* @author   Lorenz Esch <Lorenz.Esch@tu-ilmenau.de>;
*           Matti Hamalainen <msh@nmr.mgh.harvard.edu>
* @version  1.0
* @date     July, 2016
*
* @section  LICENSE
*
* Copyright (C) 2016, Your name and Matti Hamalainen. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that
* the following conditions are met:
*     * Redistributions of source code must retain the above copyright notice, this list of conditions and the
*       following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
*       the following disclaimer in the documentation and/or other materials provided with the distribution.
*     * Neither the name of MNE-CPP authors nor the names of its contributors may be used
*       to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @brief    ConnectivityMeasures class definition.
*
*/

//*************************************************************************************************************
//=============================================================================================================
// INCLUDES
//=============================================================================================================

#include "connectivitymeasures.h"
#include "network/networknode.h"
#include "network/networkedge.h"

#include <iostream>

//*************************************************************************************************************
//=============================================================================================================
// INCLUDES
//=============================================================================================================

//*************************************************************************************************************
//=============================================================================================================
// QT INCLUDES
//=============================================================================================================

//*************************************************************************************************************
//=============================================================================================================
// Eigen INCLUDES
//=============================================================================================================

#include <unsupported/Eigen/FFT>

//*************************************************************************************************************
//=============================================================================================================
// USED NAMESPACES
//=============================================================================================================

using namespace CONNECTIVITYLIB;
using namespace Eigen;

//*************************************************************************************************************
//=============================================================================================================
// DEFINE GLOBAL METHODS
//=============================================================================================================

//*************************************************************************************************************
//=============================================================================================================
// DEFINE MEMBER METHODS
//=============================================================================================================

ConnectivityMeasures::ConnectivityMeasures()
{
}

//*************************************************************************************************************

Network::SPtr ConnectivityMeasures::crossCorrelation(const MatrixXd& matData, const MatrixX3f& matVert)
{
    Network::SPtr finalNetwork  = Network::SPtr(new Network());

    //Create nodes
    for(int i = 0; i < matData.rows(); ++i) {
        RowVectorXf rowVert(3);
        rowVert.setZero();

        if(matVert.rows() != 0 && i < matVert.rows()) {
            rowVert(0) = matVert.row(i)(0);
            rowVert(1) = matVert.row(i)(1);
            rowVert(2) = matVert.row(i)(2);
        }

        *finalNetwork << NetworkNode::SPtr(new NetworkNode(i, rowVert));
    }

    //Create edges
    for(int i = 0; i < matData.rows(); ++i) {
        for(int j = i; j < matData.rows(); ++j) {
            QPair<int,double> crossCorrPair = eigenCrossCorrelation(matData.row(i), matData.row(j));

            QSharedPointer<NetworkEdge> pEdge = QSharedPointer<NetworkEdge>(new NetworkEdge(finalNetwork->getNodes()[i], finalNetwork->getNodes()[j], crossCorrPair.second));

            *finalNetwork->getNodes()[i] << pEdge;
            *finalNetwork << pEdge;
        }
    }

//    finalNetwork.scale();
//    matDist /= matDist.maxCoeff();

    return finalNetwork;
}

//*************************************************************************************************************

Eigen::MatrixXd ConnectivityMeasures::crossCorrelation(const MatrixXd& matData)
{
    MatrixXd matDist(matData.rows(), matData.rows());
    matDist.setZero();

    for(int i = 0; i < matDist.rows(); ++i) {
        QPair<int,double> crossCorrPair = eigenCrossCorrelation(matData.row(i), matData.row(i));

        double dScaling = crossCorrPair.second;

        for(int j = i; j < matDist.rows(); ++j) {
            QPair<int,double> crossCorrPair = eigenCrossCorrelation(matData.row(i), matData.row(j));
            matDist(i,j) = crossCorrPair.second/* / dScaling*/;
        }
    }

    matDist /= matDist.maxCoeff();

    return matDist;
}

//*************************************************************************************************************

QPair<int,double> ConnectivityMeasures::eigenCrossCorrelation(const RowVectorXd& xCorrInputVecFirstIn, const RowVectorXd& xCorrInputVecSecondIn)
//std::pair<double, double> ConnectivityMeasures::eigenCrossCorrelation(std::vector<double>& xCorrInputVecFirst, std::vector<double>& xCorrInputVecSecond)
{
//    Eigen::FFT<double> fft;
//    int N = std::max(xCorrInputVecFirst.size(), xCorrInputVecSecond.size());

//    //Compute the FFT size as the "next power of 2" of the input vector's length (max)
//    int b = ceil(log2(2.0 * N - 1));
//    int fftsize = pow(2,b);
//    int end = fftsize - 1;
//    int maxlag = N - 1;
//    size_t firstSize = xCorrInputVecFirst.size();
//    size_t secondSize = xCorrInputVecSecond.size();

//    //Zero Padd
//    for (int i = xCorrInputVecFirst.size(); i < fftsize; i++)
//    {
//        xCorrInputVecFirst.push_back(0);
//    }

//    for (int i = xCorrInputVecSecond.size(); i < fftsize; i++)
//    {
//        xCorrInputVecSecond.push_back(0);
//    }

//    std::vector<std::complex<double> > freqvec;
//    std::vector<std::complex<double> > freqvec2;

//    //FFT for freq domain to both vectors
//    fft.fwd( freqvec,xCorrInputVecFirst);
//    fft.fwd( freqvec2,xCorrInputVecSecond);

//    //Main step of cross corr
//    for (int i = 0; i < fftsize; i++)
//    {
//        freqvec[i] = freqvec[i] * std::conj(freqvec2[i]);
//    }

//    std::vector<double> result;
//    fft.inv(result, freqvec);

//    //Will get rid of extra zero padding and move minus lags to beginning without copy
//    std::vector<double> result2(std::make_move_iterator(result.begin() + end - maxlag + 1),
//                                std::make_move_iterator(result.end()));

//    result2.insert(result2.end(), make_move_iterator(result.begin())
//                   , make_move_iterator(result.begin()+maxlag));

//    auto minMaxRange = std::minmax_element(result2.begin(),
//                        result2.end());

//    //Will take back the changes which made in input vector
//    if (xCorrInputVecFirst.size() != firstSize)
//        xCorrInputVecFirst.resize(firstSize);
//    if (xCorrInputVecSecond.size() != secondSize)
//        xCorrInputVecSecond.resize(secondSize);

//    //Return val
//    auto resultIndex = ((minMaxRange.second - result2.begin()) - N + 1);

//    auto maxValue = result[minMaxRange.second - result.begin()];
//    return std::make_pair (resultIndex, maxValue);

    Eigen::FFT<double> fft;

    int N = std::max(xCorrInputVecFirstIn.cols(), xCorrInputVecSecondIn.cols());

    //Compute the FFT size as the "next power of 2" of the input vector's length (max)
    int b = ceil(log2(2.0 * N - 1));
    int fftsize = pow(2,b);
    int end = fftsize - 1;
    int maxlag = N - 1;

    //Zero Padd
    RowVectorXd xCorrInputVecFirst(fftsize);
    xCorrInputVecFirst.setZero();
    xCorrInputVecFirst.head(xCorrInputVecFirstIn.cols()) = xCorrInputVecFirstIn;

    RowVectorXd xCorrInputVecSecond(fftsize);
    xCorrInputVecSecond.setZero();
    xCorrInputVecSecond.head(xCorrInputVecSecondIn.cols()) = xCorrInputVecSecondIn;

    //FFT for freq domain to both vectors
    RowVectorXcd freqvec;
    RowVectorXcd freqvec2;

    fft.fwd(freqvec, xCorrInputVecFirst);
    fft.fwd(freqvec2, xCorrInputVecSecond);

    //Create conjugate complex
    freqvec2.conjugate();

    //Main step of cross corr
    for (int i = 0; i < fftsize; i++) {
        freqvec[i] = freqvec[i] * freqvec2[i];
    }

    RowVectorXd result;
    fft.inv(result, freqvec);

    //Will get rid of extra zero padding
    RowVectorXd result2 = result;//.segment(maxlag, N);

    QPair<int,int> minMaxRange;
    int idx = 0;
    result2.minCoeff(&idx);
    minMaxRange.first = idx;
    result2.maxCoeff(&idx);
    minMaxRange.second = idx;

//    std::cout<<"result2(minMaxRange.first)"<<result2(minMaxRange.first)<<std::endl;
//    std::cout<<"result2(minMaxRange.second)"<<result2(minMaxRange.second)<<std::endl;
//    std::cout<<"b"<<b<<std::endl;
//    std::cout<<"fftsize"<<fftsize<<std::endl;
//    std::cout<<"end"<<end<<std::endl;
//    std::cout<<"maxlag"<<maxlag<<std::endl;

    //Return val
    int resultIndex = minMaxRange.second;
    double maxValue = result2(resultIndex);

    return QPair<int,double>(resultIndex, maxValue);
}
<|endoftext|>


<commit_before>// testContactGeometry.cpp
// Author:  Peter Eastman, Ajay Seth
/*
* Copyright (c) 2005-2010, Stanford University. All rights reserved.
* Use of the OpenSim software in source form is permitted provided that the following
* conditions are met:
* 	1. The software is used only for non-commercial research and education. It may not
*     be used in relation to any commercial activity.
* 	2. The software is not distributed or redistributed.  Software distribution is allowed
*     only through https://simtk.org/home/opensim.
* 	3. Use of the OpenSim software or derivatives must be acknowledged in all publications,
*      presentations, or documents describing work in which OpenSim or derivatives are used.
* 	4. Credits to developers may not be removed from executables
*     created from modifications of the source.
* 	5. Modifications of source code must retain the above copyright notice, this list of
*     conditions and the following disclaimer.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
*  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
*  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
*  SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
*  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
*  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
*  OR BUSINESS INTERRUPTION) OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
*  WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//==========================================================================================================
//	testJoints builds OpenSim models using the OpenSim API and builds an equivalent
//  Simbody system using the Simbody API for each test case. A test fails if the
//  OpenSim and Simbody final states of the simulation are not equivelent (norm-err
//  less than 10x integration error tolerance)
//
//	Tests Include:
//      1. Analytical contact sphere-plane geometry
//      2. Mesh-based sphere on analytical plane geometry
//		3.
//
//==========================================================================================================
#include <iostream>
#include <OpenSim/Common/IO.h>
#include <OpenSim/Common/Exception.h>

#include <OpenSim/Simulation/Model/BodySet.h>
#include <OpenSim/Simulation/Manager/Manager.h>
#include <OpenSim/Analyses/Kinematics.h>
#include <OpenSim/Analyses/ForceReporter.h>

#include <OpenSim/Simulation/Model/ContactGeometrySet.h>
#include <OpenSim/Simulation/Model/ContactHalfSpace.h>
#include <OpenSim/Simulation/Model/ContactMesh.h>
#include <OpenSim/Simulation/Model/ContactSphere.h>
#include <OpenSim/Simulation/Model/ElasticFoundationForce.h>
#include <OpenSim/Simulation/Model/Model.h>
#include <OpenSim/Simulation/Model/HuntCrossleyForce.h>
#include <OpenSim/Simulation/SimbodyEngine/FreeJoint.h>
#include <OpenSim/Auxiliary/auxiliaryTestFunctions.h>
#include "SimTKsimbody.h"

using namespace OpenSim;
using namespace SimTK;
using namespace std;

//==========================================================================================================
// Common Parameters for the simulations are just global.
const static double integ_accuracy = 1.0e-5;
const static double duration = 1.0;
const static double interval = 0.01;
const static SimTK::Vec3 gravity_vec = SimTK::Vec3(0, -9.8065, 0);
const static double mass = 1.0;
const static double radius = 0.10;
const static double height = 1.0;
const static string mesh_file = "sphere_10cm_radius.obj"; //"10_5_cm_sphere_47700.obj";

//==========================================================================================================

int testBouncingBall(bool useMesh);
int testBallToBallContact(bool useElasticFoundation, bool useMesh1, bool useMesh2);

int main()
{
    try
    {
    	testBouncingBall(false);
    	testBouncingBall(true);
		testBallToBallContact(false, false, false);
		testBallToBallContact(true, true, false);
		testBallToBallContact(true, false, true);
		testBallToBallContact(true, true, true);
    }
    catch (const OpenSim::Exception& e) {
        e.print(cerr);
        return 1;
    }
    cout << "Done" << endl;
    return 0;
}

//==========================================================================================================
// Test Cases
//==========================================================================================================
int testBouncingBall(bool useMesh)
{
	// Setup OpenSim model
	Model *osimModel = new Model;

	//OpenSim bodies
    OpenSim::Body& ground = osimModel->getGroundBody();
	OpenSim::Body ball;
	ball.setName("ball");
	ball.setMass(mass);
	ball.setMassCenter(Vec3(0));
	ball.setInertia(Inertia(1.0));

	// Add joints
	FreeJoint free("free", ground, Vec3(0), Vec3(0), ball, Vec3(0), Vec3(0));
	osimModel->addBody(&ball);
	// BAD: have to set memoryOwner to false or model will try to delete
	osimModel->updBodySet().setMemoryOwner(false);

    // Create ContactGeometry.
    ContactHalfSpace *floor = new ContactHalfSpace(Vec3(0), Vec3(0, 0, -0.5*SimTK_PI), ground, "ground");
    osimModel->addContactGeometry(floor);
    OpenSim::ContactGeometry* geometry;
    if (useMesh)
        geometry = new ContactMesh(mesh_file, Vec3(0), Vec3(0), ball, "ball");
    else
        geometry = new ContactSphere(radius, Vec3(0), ball, "ball");
    osimModel->addContactGeometry(geometry);

    OpenSim::Force* force;
    if (useMesh)
    {
	    // Add an ElasticFoundationForce.
        OpenSim::ElasticFoundationForce::ContactParameters* contactParams = new OpenSim::ElasticFoundationForce::ContactParameters(1.0e6/radius, 1e-5, 0.0, 0.0, 0.0);
        contactParams->addGeometry("ball");
        contactParams->addGeometry("ground");
        force = new OpenSim::ElasticFoundationForce(contactParams);
	    osimModel->addForce(force);
    }
    else
    {
	    // Add a HuntCrossleyForce.
        OpenSim::HuntCrossleyForce::ContactParameters* contactParams = new OpenSim::HuntCrossleyForce::ContactParameters(1.0e6, 1e-5, 0.0, 0.0, 0.0);
        contactParams->addGeometry("ball");
        contactParams->addGeometry("ground");
        force = new OpenSim::HuntCrossleyForce(contactParams);
	    osimModel->addForce(force);
    }

	osimModel->setGravity(gravity_vec);

	osimModel->setName("TestContactGeomtery_Ball");
	osimModel->copy()->print("TestContactGeomtery_Ball.osim");

	Kinematics* kin = new Kinematics(osimModel);
	osimModel->addAnalysis(kin);

    SimTK::State osim_state = osimModel->initSystem();
	osim_state.updQ()[4] = height;
    osimModel->getMultibodySystem().realize(osim_state, Stage::Position );

	//Initial system energy is all potential
	double Etot_orig = mass*(-gravity_vec[1])*height;

	//==========================================================================================================
	// Simulate it and see if it bounces correctly.
 	cout << "stateY=" << osim_state.getY() << std::endl;

    RungeKuttaMersonIntegrator integrator(osimModel->getMultibodySystem() );
	integrator.setAccuracy(integ_accuracy);
    Manager manager(*osimModel, integrator);

    for (unsigned int i = 0; i < duration/interval; ++i)
    {
        manager.setInitialTime(i*interval);
		manager.setFinalTime((i+1)*interval);
        manager.integrate(osim_state);
		double time = osim_state.getTime();

        osimModel->getMultibodySystem().realize(osim_state, Stage::Acceleration);
        Vec3 pos, vel;

		osimModel->updSimbodyEngine().getPosition(osim_state, osimModel->getBodySet().get("ball"), Vec3(0), pos);
		osimModel->updSimbodyEngine().getVelocity(osim_state, osimModel->getBodySet().get("ball"), Vec3(0), vel);

		double Etot = mass*((-gravity_vec[1])*pos[1] + 0.5*vel[1]*vel[1]);

		//cout << "starting system energy = " << Etot_orig << " versus current energy = " << Etot << endl;
		// contact absorbs and returns energy so make sure not in contact
        if (pos[1] > 2*radius)
        {
            ASSERT_EQUAL(Etot_orig, Etot, 1e-2, __FILE__, __LINE__, "Bouncing ball on plane Failed: energy was not conserved.");
        }
        else
        {
			cout << "In contact at time = " << time << endl;
            ASSERT(pos[1] < 5.0 && pos[1] > 0);
        }
        ASSERT_EQUAL(0.0, pos[0], 1e-4);
        ASSERT_EQUAL(0.0, pos[2], 1e-4);
		ASSERT_EQUAL(0.0, vel[0], 1e-3);
        ASSERT_EQUAL(0.0, vel[2], 1e-3);
    }

	std::string prefix = useMesh?"Kinematics_Mesh":"Kinematics_NoMesh";
	kin->printResults(prefix);

	// model takes ownership of components unless container set is told otherwise
	delete osimModel;

	return 0;
}

// test sphere to sphere contact using elastic foundation with and without
// meshes and their combination
int testBallToBallContact(bool useElasticFoundation, bool useMesh1, bool useMesh2)
{
	// Setup OpenSim model
	Model *osimModel = new Model;

	//OpenSim bodies
    OpenSim::Body& ground = osimModel->getGroundBody();
	OpenSim::Body ball;
	ball.setName("ball");
	ball.setMass(mass);
	ball.setMassCenter(Vec3(0));
	ball.setInertia(Inertia(1.0));

	// Add joints
	FreeJoint free("free", ground, Vec3(0), Vec3(0), ball, Vec3(0), Vec3(0));

	osimModel->addBody(&ball);
	// BAD: have to set memoryOwner to false or model will try to delete
	osimModel->updBodySet().setMemoryOwner(false);

    // Create ContactGeometry.
    OpenSim::ContactGeometry *ball1, *ball2;

	if (useElasticFoundation && useMesh1)
        ball1 = new ContactMesh(mesh_file, Vec3(0), Vec3(0), ground, "ball1");
    else
        ball1 = new ContactSphere(radius, Vec3(0), ground, "ball1");

    if (useElasticFoundation && useMesh2)
        ball2 = new ContactMesh(mesh_file, Vec3(0), Vec3(0), ball, "ball2");
    else
        ball2 = new ContactSphere(radius, Vec3(0), ball, "ball2");

	osimModel->addContactGeometry(ball1);
	osimModel->addContactGeometry(ball2);

    OpenSim::Force* force;

	std::string prefix;
	if (useElasticFoundation){

	}
	else{

	}
    if (useElasticFoundation)
    {
	    // Add an ElasticFoundationForce.
        OpenSim::ElasticFoundationForce::ContactParameters* contactParams = new OpenSim::ElasticFoundationForce::ContactParameters(1.0e6/(2*radius), 0.001, 0.0, 0.0, 0.0);
        contactParams->addGeometry("ball1");
        contactParams->addGeometry("ball2");
        force = new OpenSim::ElasticFoundationForce(contactParams);
		prefix = "EF_";
		prefix += useMesh1 ?"Mesh":"noMesh";
		prefix += useMesh2 ? "_to_Mesh":"_to_noMesh";

    }
    else
    {
	    // Add a Hertz HuntCrossleyForce.
        OpenSim::HuntCrossleyForce::ContactParameters* contactParams = new OpenSim::HuntCrossleyForce::ContactParameters(1.0e6, 0.001, 0.0, 0.0, 0.0);
        contactParams->addGeometry("ball1");
        contactParams->addGeometry("ball2");
        force = new OpenSim::HuntCrossleyForce(contactParams);
		prefix = "Hertz";

    }

	force->setName(prefix);
	osimModel->addForce(force);
	osimModel->setGravity(gravity_vec);

	osimModel->setName(prefix);
	osimModel->copy()->print(prefix+".osim");

	Kinematics* kin = new Kinematics(osimModel);
	osimModel->addAnalysis(kin);

	ForceReporter* reporter = new ForceReporter(osimModel);
	osimModel->addAnalysis(reporter);

    SimTK::State osim_state = osimModel->initSystem();
	osim_state.updQ()[4] = height;
    osimModel->getMultibodySystem().realize(osim_state, Stage::Position );

	//==========================================================================================================
	// Simulate it and see if it bounces correctly.
 	cout << "stateY=" << osim_state.getY() << std::endl;

    RungeKuttaMersonIntegrator integrator(osimModel->getMultibodySystem() );
	integrator.setAccuracy(integ_accuracy);
	integrator.setMaximumStepSize(100*integ_accuracy);
    Manager manager(*osimModel, integrator);
    manager.setInitialTime(0.0);
    manager.setFinalTime(duration);
    manager.integrate(osim_state);

	kin->printResults(prefix);
	reporter->printResults(prefix);

	// model takes ownership of components unless container set is told otherwise
	delete osimModel;

	return 0;
}<commit_msg>Automatically compare Hertz and Elastic Foundation contact results for two colliding spheres. Also compare results for Mesg-to-Mesh and Mesh-to-noMesh interaction with EF and make sure these results are the same. This test verifies a fix to mesh-mesh handling in Simbody 2.2.<commit_after>// testContactGeometry.cpp
// Author:  Peter Eastman, Ajay Seth
/*
* Copyright (c) 2005-2010, Stanford University. All rights reserved.
* Use of the OpenSim software in source form is permitted provided that the following
* conditions are met:
* 	1. The software is used only for non-commercial research and education. It may not
*     be used in relation to any commercial activity.
* 	2. The software is not distributed or redistributed.  Software distribution is allowed
*     only through https://simtk.org/home/opensim.
* 	3. Use of the OpenSim software or derivatives must be acknowledged in all publications,
*      presentations, or documents describing work in which OpenSim or derivatives are used.
* 	4. Credits to developers may not be removed from executables
*     created from modifications of the source.
* 	5. Modifications of source code must retain the above copyright notice, this list of
*     conditions and the following disclaimer.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
*  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
*  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
*  SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
*  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
*  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
*  OR BUSINESS INTERRUPTION) OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
*  WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//==========================================================================================================
//	testJoints builds OpenSim models using the OpenSim API and builds an equivalent
//  Simbody system using the Simbody API for each test case. A test fails if the
//  OpenSim and Simbody final states of the simulation are not equivelent (norm-err
//  less than 10x integration error tolerance)
//
//	Tests Include:
//      1. Analytical contact sphere-plane geometry
//      2. Mesh-based sphere on analytical plane geometry
//		3.
//
//==========================================================================================================
#include <iostream>
#include <OpenSim/Common/IO.h>
#include <OpenSim/Common/Exception.h>

#include <OpenSim/Simulation/Model/BodySet.h>
#include <OpenSim/Simulation/Manager/Manager.h>
#include <OpenSim/Analyses/Kinematics.h>
#include <OpenSim/Analyses/ForceReporter.h>

#include <OpenSim/Simulation/Model/ContactGeometrySet.h>
#include <OpenSim/Simulation/Model/ContactHalfSpace.h>
#include <OpenSim/Simulation/Model/ContactMesh.h>
#include <OpenSim/Simulation/Model/ContactSphere.h>
#include <OpenSim/Simulation/Model/ElasticFoundationForce.h>
#include <OpenSim/Simulation/Model/Model.h>
#include <OpenSim/Simulation/Model/HuntCrossleyForce.h>
#include <OpenSim/Simulation/SimbodyEngine/FreeJoint.h>
#include <OpenSim/Auxiliary/auxiliaryTestFunctions.h>
#include "SimTKsimbody.h"

using namespace OpenSim;
using namespace SimTK;
using namespace std;

//==========================================================================================================
// Common Parameters for the simulations are just global.
const static double integ_accuracy = 1.0e-5;
const static double duration = 1.0;
const static double interval = 0.01;
const static SimTK::Vec3 gravity_vec = SimTK::Vec3(0, -9.8065, 0);
const static double mass = 1.0;
const static double radius = 0.10;
const static double height = 1.0;
const static string mesh_file = "sphere_10cm_radius.obj"; //"10_5_cm_sphere_47700.obj";

//==========================================================================================================

int testBouncingBall(bool useMesh);
int testBallToBallContact(bool useElasticFoundation, bool useMesh1, bool useMesh2);
void compareHertzAndMeshContactResults();

int main()
{
    try
    {
    	testBouncingBall(false);
    	testBouncingBall(true);
		testBallToBallContact(false, false, false);
		testBallToBallContact(true, true, false);
		testBallToBallContact(true, false, true);
		testBallToBallContact(true, true, true);
		compareHertzAndMeshContactResults();
    }
    catch (const OpenSim::Exception& e) {
        e.print(cerr);
        return 1;
    }
    cout << "Done" << endl;
    return 0;
}

//==========================================================================================================
// Test Cases
//==========================================================================================================
int testBouncingBall(bool useMesh)
{
	// Setup OpenSim model
	Model *osimModel = new Model;

	//OpenSim bodies
    OpenSim::Body& ground = osimModel->getGroundBody();
	OpenSim::Body ball;
	ball.setName("ball");
	ball.setMass(mass);
	ball.setMassCenter(Vec3(0));
	ball.setInertia(Inertia(1.0));

	// Add joints
	FreeJoint free("free", ground, Vec3(0), Vec3(0), ball, Vec3(0), Vec3(0));
	osimModel->addBody(&ball);
	// BAD: have to set memoryOwner to false or model will try to delete
	osimModel->updBodySet().setMemoryOwner(false);

    // Create ContactGeometry.
    ContactHalfSpace *floor = new ContactHalfSpace(Vec3(0), Vec3(0, 0, -0.5*SimTK_PI), ground, "ground");
    osimModel->addContactGeometry(floor);
    OpenSim::ContactGeometry* geometry;
    if (useMesh)
        geometry = new ContactMesh(mesh_file, Vec3(0), Vec3(0), ball, "ball");
    else
        geometry = new ContactSphere(radius, Vec3(0), ball, "ball");
    osimModel->addContactGeometry(geometry);

    OpenSim::Force* force;
    if (useMesh)
    {
	    // Add an ElasticFoundationForce.
        OpenSim::ElasticFoundationForce::ContactParameters* contactParams = new OpenSim::ElasticFoundationForce::ContactParameters(1.0e6/radius, 1e-5, 0.0, 0.0, 0.0);
        contactParams->addGeometry("ball");
        contactParams->addGeometry("ground");
        force = new OpenSim::ElasticFoundationForce(contactParams);
	    osimModel->addForce(force);
    }
    else
    {
	    // Add a HuntCrossleyForce.
        OpenSim::HuntCrossleyForce::ContactParameters* contactParams = new OpenSim::HuntCrossleyForce::ContactParameters(1.0e6, 1e-5, 0.0, 0.0, 0.0);
        contactParams->addGeometry("ball");
        contactParams->addGeometry("ground");
        force = new OpenSim::HuntCrossleyForce(contactParams);
	    osimModel->addForce(force);
    }

	osimModel->setGravity(gravity_vec);

	osimModel->setName("TestContactGeomtery_Ball");
	osimModel->copy()->print("TestContactGeomtery_Ball.osim");

	Kinematics* kin = new Kinematics(osimModel);
	osimModel->addAnalysis(kin);

    SimTK::State osim_state = osimModel->initSystem();
	osim_state.updQ()[4] = height;
    osimModel->getMultibodySystem().realize(osim_state, Stage::Position );

	//Initial system energy is all potential
	double Etot_orig = mass*(-gravity_vec[1])*height;

	//==========================================================================================================
	// Simulate it and see if it bounces correctly.
 	cout << "stateY=" << osim_state.getY() << std::endl;

    RungeKuttaMersonIntegrator integrator(osimModel->getMultibodySystem() );
	integrator.setAccuracy(integ_accuracy);
    Manager manager(*osimModel, integrator);

    for (unsigned int i = 0; i < duration/interval; ++i)
    {
        manager.setInitialTime(i*interval);
		manager.setFinalTime((i+1)*interval);
        manager.integrate(osim_state);
		double time = osim_state.getTime();

        osimModel->getMultibodySystem().realize(osim_state, Stage::Acceleration);
        Vec3 pos, vel;

		osimModel->updSimbodyEngine().getPosition(osim_state, osimModel->getBodySet().get("ball"), Vec3(0), pos);
		osimModel->updSimbodyEngine().getVelocity(osim_state, osimModel->getBodySet().get("ball"), Vec3(0), vel);

		double Etot = mass*((-gravity_vec[1])*pos[1] + 0.5*vel[1]*vel[1]);

		//cout << "starting system energy = " << Etot_orig << " versus current energy = " << Etot << endl;
		// contact absorbs and returns energy so make sure not in contact
        if (pos[1] > 2*radius)
        {
            ASSERT_EQUAL(Etot_orig, Etot, 1e-2, __FILE__, __LINE__, "Bouncing ball on plane Failed: energy was not conserved.");
        }
        else
        {
			cout << "In contact at time = " << time << endl;
            ASSERT(pos[1] < 5.0 && pos[1] > 0);
        }
        ASSERT_EQUAL(0.0, pos[0], 1e-4);
        ASSERT_EQUAL(0.0, pos[2], 1e-4);
		ASSERT_EQUAL(0.0, vel[0], 1e-3);
        ASSERT_EQUAL(0.0, vel[2], 1e-3);
    }

	std::string prefix = useMesh?"Kinematics_Mesh":"Kinematics_NoMesh";
	kin->printResults(prefix);

	// model takes ownership of components unless container set is told otherwise
	delete osimModel;

	return 0;
}

// test sphere to sphere contact using elastic foundation with and without
// meshes and their combination
int testBallToBallContact(bool useElasticFoundation, bool useMesh1, bool useMesh2)
{
	// Setup OpenSim model
	Model *osimModel = new Model;

	//OpenSim bodies
    OpenSim::Body& ground = osimModel->getGroundBody();
	OpenSim::Body ball;
	ball.setName("ball");
	ball.setMass(mass);
	ball.setMassCenter(Vec3(0));
	ball.setInertia(Inertia(1.0));

	// Add joints
	FreeJoint free("free", ground, Vec3(0), Vec3(0), ball, Vec3(0), Vec3(0));

	osimModel->addBody(&ball);
	// BAD: have to set memoryOwner to false or model will try to delete
	osimModel->updBodySet().setMemoryOwner(false);

    // Create ContactGeometry.
    OpenSim::ContactGeometry *ball1, *ball2;

	if (useElasticFoundation && useMesh1)
        ball1 = new ContactMesh(mesh_file, Vec3(0), Vec3(0), ground, "ball1");
    else
        ball1 = new ContactSphere(radius, Vec3(0), ground, "ball1");

    if (useElasticFoundation && useMesh2)
        ball2 = new ContactMesh(mesh_file, Vec3(0), Vec3(0), ball, "ball2");
    else
        ball2 = new ContactSphere(radius, Vec3(0), ball, "ball2");

	osimModel->addContactGeometry(ball1);
	osimModel->addContactGeometry(ball2);

    OpenSim::Force* force;

	std::string prefix;
	if (useElasticFoundation){

	}
	else{

	}
    if (useElasticFoundation)
    {
	    // Add an ElasticFoundationForce.
        OpenSim::ElasticFoundationForce::ContactParameters* contactParams = new OpenSim::ElasticFoundationForce::ContactParameters(1.0e6/(2*radius), 0.001, 0.0, 0.0, 0.0);
        contactParams->addGeometry("ball1");
        contactParams->addGeometry("ball2");
        force = new OpenSim::ElasticFoundationForce(contactParams);
		prefix = "EF_";
		prefix += useMesh1 ?"Mesh":"noMesh";
		prefix += useMesh2 ? "_to_Mesh":"_to_noMesh";

    }
    else
    {
	    // Add a Hertz HuntCrossleyForce.
        OpenSim::HuntCrossleyForce::ContactParameters* contactParams = new OpenSim::HuntCrossleyForce::ContactParameters(1.0e6, 0.001, 0.0, 0.0, 0.0);
        contactParams->addGeometry("ball1");
        contactParams->addGeometry("ball2");
        force = new OpenSim::HuntCrossleyForce(contactParams);
		prefix = "Hertz";

    }

	force->setName("contact");
	osimModel->addForce(force);
	osimModel->setGravity(gravity_vec);

	osimModel->setName(prefix);
	osimModel->copy()->print(prefix+".osim");

	Kinematics* kin = new Kinematics(osimModel);
	osimModel->addAnalysis(kin);

	ForceReporter* reporter = new ForceReporter(osimModel);
	osimModel->addAnalysis(reporter);

    SimTK::State osim_state = osimModel->initSystem();
	osim_state.updQ()[4] = height;
    osimModel->getMultibodySystem().realize(osim_state, Stage::Position );

	//==========================================================================================================
	// Simulate it and see if it bounces correctly.
 	cout << "stateY=" << osim_state.getY() << std::endl;

    RungeKuttaMersonIntegrator integrator(osimModel->getMultibodySystem() );
	integrator.setAccuracy(integ_accuracy);
	integrator.setMaximumStepSize(100*integ_accuracy);
    Manager manager(*osimModel, integrator);
    manager.setInitialTime(0.0);
    manager.setFinalTime(duration);
    manager.integrate(osim_state);

	kin->printResults(prefix);
	reporter->printResults(prefix);

	// model takes ownership of components unless container set is told otherwise
	delete osimModel;

	return 0;
}

void compareHertzAndMeshContactResults()
{
	Storage hertz("Hertz_ForceReporter_forces.sto");
	Storage meshToMesh("EF_Mesh_to_Mesh_ForceReporter_forces.sto");
	Storage noMeshToMesh("EF_noMesh_to_Mesh_ForceReporter_forces.sto");
	Storage meshToNoMesh("EF_Mesh_to_noMesh_ForceReporter_forces.sto");

	int nforces = hertz.getColumnLabels().getSize()-1;

	// Hertz theory and ElasticFoundation will not be the same, but they should yield similar results, to withing
	Array<double> rms_tols_1(12, nforces);
	CHECK_STORAGE_AGAINST_STANDARD(meshToMesh, hertz, rms_tols_1, __FILE__, __LINE__, "ElasticFoundation FAILED to Match Hertz Contact ");

	// ElasticFoundation on mesh to mesh and mesh to non-mesh should be virtually identical
	Array<double> rms_tols_2(0.5, nforces);
	CHECK_STORAGE_AGAINST_STANDARD(meshToMesh, meshToNoMesh, rms_tols_2, __FILE__, __LINE__, "ElasticFoundation Mesh-Mesh FAILED to match Mesh-noMesh Case ");

	// ElasticFoundation on non-mesh to mesh and mesh to non-mesh should be identical
	Array<double> rms_tols_3(integ_accuracy, nforces);
	CHECK_STORAGE_AGAINST_STANDARD(noMeshToMesh, meshToNoMesh, rms_tols_3, __FILE__, __LINE__, "ElasticFoundation noMesh-Mesh FAILED to match Mesh-noMesh Case ");

}
<|endoftext|>